文本分类算法之--贝叶斯分类算法的实现Java版本

package com.vista;
import java.io.IOException;      
import jeasy.analysis.MMAnalyzer;

/* *
* 中文分词器
*/
public   class  ChineseSpliter 
{
    
/* *
    * 对给定的文本进行中文分词
    * @param text 给定的文本
    * @param splitToken 用于分割的标记,如"|"
    * @return 分词完毕的文本
    
*/
    
public   static  String split(String text,String splitToken)
    {
        String result 
=   null ;
        MMAnalyzer analyzer 
=   new  MMAnalyzer();      
        
try       
        {
            result 
=  analyzer.segment(text, splitToken);    
        }      
        
catch  (IOException e)      
        {     
            e.printStackTrace();     
        }     
        
return  result;
    }
}
复制代码

停用词处理

      去掉文档中无意思的词语也是必须的一项工作,这里简单的定义了一些常见的停用词,并根据这些常用停用词在分词时进行判断。

复制代码
package com.vista;

/* *
* 停用词处理器
* @author phinecos 

*/
public   class  StopWordsHandler 
{
    
private   static  String stopWordsList[]  = { " " " 我们 " , " " , " 自己 " , " " , " " , " " , " " , " " , " " , " " , " " , " " , " " , " " , " " , " " , " " , " " , " " , " " , " " , " " , " " , " " , " " , " " , " " , "" }; // 常用停用词
     public   static  boolean IsStopWord(String word)
    {
        
for ( int  i = 0 ;i < stopWordsList.length; ++ i)
        {
            
if (word.equalsIgnoreCase(stopWordsList[i]))
                
return   true ;
        }
        
return   false ;
    }
}
复制代码

训练集管理器

      我们的系统首先需要从训练样本集中得到假设的先验概率和给定假设下观察到不同数据的概率。

复制代码
package  com.vista;
import  java.io.BufferedReader;
import  java.io.File;
import  java.io.FileInputStream;
import  java.io.FileNotFoundException;
import  java.io.IOException;
import  java.io.InputStreamReader;
import  java.util.Properties;
import  java.util.logging.Level;
import  java.util.logging.Logger;
/**
* 训练集管理器
*/
public   class  TrainingDataManager 
{
    
private  String[] traningFileClassifications; // 训练语料分类集合
     private  File traningTextDir; // 训练语料存放目录
     private   static  String defaultPath  =   " D:\\TrainningSet " ;
    
    
public  TrainingDataManager() 
    {
        traningTextDir 
=   new  File(defaultPath);
        
if  ( ! traningTextDir.isDirectory()) 
        {
            
throw   new  IllegalArgumentException( " 训练语料库搜索失败! [ "   + defaultPath  +   " ] " );
        }
        
this .traningFileClassifications  =  traningTextDir.list();
    }
    
/**
    * 返回训练文本类别,这个类别就是目录名
    * 
@return  训练文本类别
    
*/
    
public  String[] getTraningClassifications() 
    {
        
return   this .traningFileClassifications;
    }
    
/**
    * 根据训练文本类别返回这个类别下的所有训练文本路径(full path)
    * 
@param  classification 给定的分类
    * 
@return  给定分类下所有文件的路径(full path)
    
*/
    
public  String[] getFilesPath(String classification) 
    {
        File classDir 
=   new  File(traningTextDir.getPath()  + File.separator  + classification);
        String[] ret 
=  classDir.list();
        
for  ( int  i  =   0 ; i  <  ret.length; i ++
        {
            ret[i] 
=  traningTextDir.getPath()  + File.separator  + classification  + File.separator  + ret[i];
        }
        
return  ret;
    }
    
/**
    * 返回给定路径的文本文件内容
    * 
@param  filePath 给定的文本文件路径
    * 
@return  文本内容
    * 
@throws  java.io.FileNotFoundException
    * 
@throws  java.io.IOException
    
*/
    
public   static  String getText(String filePath)  throws  FileNotFoundException,IOException 
    {
        InputStreamReader isReader 
= new  InputStreamReader( new  FileInputStream(filePath), " GBK " );
        BufferedReader reader 
=   new  BufferedReader(isReader);
        String aline;
        StringBuilder sb 
=   new  StringBuilder();
        
while  ((aline  =  reader.readLine())  !=   null )
        {
            sb.append(aline 
+   "   " );
        }
        isReader.close();
        reader.close();
        
return  sb.toString();
    }
    
/**
    * 返回训练文本集中所有的文本数目
    * 
@return  训练文本集中所有的文本数目
    
*/
    
public   int  getTrainingFileCount()
    {
        
int  ret  =   0 ;
        
for  ( int  i  =   0 ; i  <  traningFileClassifications.length; i ++ )
        {
            ret 
+= getTrainingFileCountOfClassification(traningFileClassifications[i]);
        }
        
return  ret;
    }
    
/**
    * 返回训练文本集中在给定分类下的训练文本数目
    * 
@param  classification 给定的分类
    * 
@return  训练文本集中在给定分类下的训练文本数目
    
*/
    
public   int  getTrainingFileCountOfClassification(String classification)
    {
        File classDir 
=   new  File(traningTextDir.getPath()  + File.separator  + classification);
        
return  classDir.list().length;
    }
    
/**
    * 返回给定分类中包含关键字/词的训练文本的数目
    * 
@param  classification 给定的分类
    * 
@param  key 给定的关键字/词
    * 
@return  给定分类中包含关键字/词的训练文本的数目
    
*/
    
public   int  getCountContainKeyOfClassification(String classification,String key) 
    {
        
int  ret  =   0 ;
        
try  
        {
            String[] filePath 
=  getFilesPath(classification);
            
for  ( int  j  =   0 ; j  <  filePath.length; j ++
            {
                String text 
=  getText(filePath[j]);
                
if  (text.contains(key)) 
                {
                    ret
++ ;
                }
            }
        }
        
catch  (FileNotFoundException ex) 
        {
        Logger.getLogger(TrainingDataManager.
class .getName()).log(Level.SEVERE,  null ,ex);
    
        } 
        
catch  (IOException ex)
        {
            Logger.getLogger(TrainingDataManager.
class .getName()).log(Level.SEVERE,  null ,ex);
        }
        
return  ret;
    }
}
复制代码

先验概率

      先验概率是我们需要计算的两大概率值之一

复制代码
package  com.vista;
/**
* 先验概率计算

先验概率计算


* P(cj)=N(C=cj)/

* 其中,N(C=cj)表示类别cj中的训练文本数量;
* N表示训练文本集总数量。
*/
public   class  PriorProbability 
{
    
private   static  TrainingDataManager tdm  = new  TrainingDataManager();
    
/**
    * 先验概率
    * 
@param  c 给定的分类
    * 
@return  给定条件下的先验概率
    
*/
    
public   static   float  calculatePc(String c)
    {
        
float  ret  =  0F;
        
float  Nc  =  tdm.getTrainingFileCountOfClassification(c);
        
float  N  =  tdm.getTrainingFileCount();
        ret 
=  Nc  /  N;
        
return  ret;
    }
}

复制代码

分类条件概率

      这是另一个影响因子,和先验概率一起来决定最终结果

复制代码
package  com.vista;

/**
条件概率计算
*

类条件概率


* P(xj|cj)=( N(X=xi, C=cj
)+1 ) / ( N(C=cj)+M+V ) 

* 其中,N(X=xi, C=cj)表示类别cj中包含属性x
* i
的训练文本数量;N(C=cj)表示类别cj中的训练文本数量;M值用于避免
* N(X=xi, C=cj)过小所引发的问题;V表示类别的总数。
*

条件概率


定义 设A, B是两个事件,且P(A)>0 称

P(B∣A)=P(AB)/P(A)

* 为在条件A下发生的条件事件B发生的条件概率。

*/

public   class  ClassConditionalProbability 
{
    
private   static  TrainingDataManager tdm  =   new  TrainingDataManager();
    
private   static   final   float  M  =  0F;
    
    
/**
    * 计算类条件概率
    * 
@param  x 给定的文本属性
    * 
@param  c 给定的分类
    * 
@return  给定条件下的类条件概率
    
*/
    
public   static   float  calculatePxc(String x, String c) 
    {
        
float  ret  =  0F;
        
float  Nxc  =  tdm.getCountContainKeyOfClassification(c, x);
        
float  Nc  =  tdm.getTrainingFileCountOfClassification(c);
        
float  V  =  tdm.getTraningClassifications().length;
        ret 
=  (Nxc  +   1 /  (Nc  +  M  +  V);  // 为了避免出现0这样极端情况,进行加权处理
         return  ret;
    }
}
复制代码

分类结果

      用来保存各个分类及其计算出的概率值,

复制代码
package  com.vista;
/**
* 分类结果
*/
public   class  ClassifyResult 
{
    
public   double  probility; // 分类的概率
     public  String classification; // 分类
     public  ClassifyResult()
    {
        
this .probility  =   0 ;
        
this .classification  =   null ;
    }
}
复制代码

朴素贝叶斯分类器

      利用样本数据集计算先验概率和各个文本向量属性在分类中的条件概率,从而计算出各个概率值,最后对各个概率值进行排序,选出最大的概率值,即为所属的分类。

复制代码
package  com.vista;
import  com.vista.ChineseSpliter;
import  com.vista.ClassConditionalProbability;
import  com.vista.PriorProbability;
import  com.vista.TrainingDataManager;
import  com.vista.StopWordsHandler;
import  java.util.ArrayList;
import  java.util.Comparator;
import  java.util.List;
import  java.util.Vector;

/**
* 朴素贝叶斯分类器
*/
public   class  BayesClassifier 
{
    
private  TrainingDataManager tdm; // 训练集管理器
     private  String trainnigDataPath; // 训练集路径
     private   static   double  zoomFactor  =   10.0f ;
    
/**
    * 默认的构造器,初始化训练集
    
*/
    
public  BayesClassifier() 
    {
        tdm 
= new  TrainingDataManager();
    }

    
/**
    * 计算给定的文本属性向量X在给定的分类Cj中的类条件概率
    * ClassConditionalProbability连乘值
    * 
@param  X 给定的文本属性向量
    * 
@param  Cj 给定的类别
    * 
@return  分类条件概率连乘值,即

    
*/
    
float  calcProd(String[] X, String Cj) 
    {
        
float  ret  =   1.0F ;
        
//  类条件概率连乘
         for  ( int  i  =   0 ; i  < X.length; i ++ )
        {
            String Xi 
=  X[i];
            
// 因为结果过小,因此在连乘之前放大10倍,这对最终结果并无影响,因为我们只是比较概率大小而已
            ret  *= ClassConditionalProbability.calculatePxc(Xi, Cj) * zoomFactor;
        }
        
//  再乘以先验概率
        ret  *=  PriorProbability.calculatePc(Cj);
        
return  ret;
    }
    
/**
    * 去掉停用词
    * 
@param  text 给定的文本
    * 
@return  去停用词后结果
    
*/
    
public  String[] DropStopWords(String[] oldWords)
    {
        Vector
< String >  v1  =   new  Vector < String > ();
        
for ( int  i = 0 ;i < oldWords.length; ++ i)
        {
            
if (StopWordsHandler.IsStopWord(oldWords[i]) == false )
            {
// 不是停用词
                v1.add(oldWords[i]);
            }
        }
        String[] newWords 
=   new  String[v1.size()];
        v1.toArray(newWords);
        
return  newWords;
    }
    
/**
    * 对给定的文本进行分类
    * 
@param  text 给定的文本
    * 
@return  分类结果
    
*/
    @SuppressWarnings(
" unchecked " )
    
public  String classify(String text) 
    {
        String[] terms 
=   null ;
        terms
=  ChineseSpliter.split(text,  "   " ).split( "   " ); // 中文分词处理(分词后结果可能还包含有停用词)
        terms  =  DropStopWords(terms); // 去掉停用词,以免影响分类
        
        String[] Classes 
=  tdm.getTraningClassifications(); // 分类
         float  probility  =   0.0F ;
        List
< ClassifyResult >  crs  =   new  ArrayList < ClassifyResult > (); // 分类结果
         for  ( int  i  =   0 ; i  < Classes.length; i ++
        {
            String Ci 
=  Classes[i]; // 第i个分类
            probility  =  calcProd(terms, Ci); // 计算给定的文本属性向量terms在给定的分类Ci中的分类条件概率
            
// 保存分类结果
            ClassifyResult cr  =   new  ClassifyResult();
            cr.classification 
=  Ci; // 分类
            cr.probility  =  probility; // 关键字在分类的条件概率
            System.out.println( " In process. " );
            System.out.println(Ci 
+   " "   +  probility);
            crs.add(cr);
        }
        
// 对最后概率结果进行排序
        java.util.Collections.sort(crs, new  Comparator() 
        {
            
public   int  compare( final  Object o1, final  Object o2) 
            {
                
final  ClassifyResult m1  =  (ClassifyResult) o1;
                
final  ClassifyResult m2  =  (ClassifyResult) o2;
                
final   double  ret  =  m1.probility  -  m2.probility;
                
if  (ret  <   0
                {
                    
return   1 ;
                } 
                
else  
                {
                    
return   - 1 ;
                }
            }
        });
        
// 返回概率最大的分类
         return  crs.get( 0 ).classification;
    }
    
    
public   static   void  main(String[] args)
    {
        String text 
=   " 微软公司提出以446亿美元的价格收购雅虎中国网2月1日报道 美联社消息,微软公司提出以446亿美元现金加股票的价格收购搜索网站雅虎公司。微软提出以每股31美元的价格收购雅虎。微软的收购报价较雅虎1月31日的收盘价19.18美元溢价62%。微软公司称雅虎公司的股东可以选择以现金或股票进行交易。微软和雅虎公司在2006年底和2007年初已在寻求双方合作。而近两年,雅虎一直处于困境:市场份额下滑、运营业绩不佳、股价大幅下跌。对于力图在互联网市场有所作为的微软来说,收购雅虎无疑是一条捷径,因为双方具有非常强的互补性。(小桥) " ;
        BayesClassifier classifier 
=   new  BayesClassifier(); // 构造Bayes分类器
        String result  =  classifier.classify(text); // 进行分类
        System.out.println( " 此项属于[ " + result + " ] " );
    }
}
复制代码

训练集与分类测试

作为测试,这里选用Sogou实验室的文本分类数据,我只使用了mini版本。迷你版本有10个类别 ,共计100篇文章,总大小244KB

使用的测试文本:

复制代码
微软公司提出以446亿美元的价格收购雅虎

中国网2月1日报道 美联社消息,微软公司提出以446亿美元现金加股票的价格收购搜索网站雅虎公司。

微软提出以每股31美元的价格收购雅虎。微软的收购报价较雅虎1月31日的收盘价19
. 18美元溢价62%。微软公司称雅虎公司的股东可以选择以现金或股票进行交易。

微软和雅虎公司在2006年底和2007年初已在寻求双方合作。而近两年,雅虎一直处于困境:市场份额下滑、运营业绩不佳、股价大幅下跌。对于力图在互联网市场有所作为的微软来说,收购雅虎无疑是一条捷径,因为双方具有非常强的互补性。
( 小桥 )
复制代码

使用mini版本的测试结果:

复制代码
In process .
IT:
2.8119528E-5
In process
.
体育:
2.791735E-21
In process
.
健康:
3.3188528E-12
In process
.
军事:
2.532662E-19
In process
.
招聘:
2.3753596E-17
In process
.
教育:
4.2023427E-19
In process
.
文化:
6.0595915E-23
In process
.
旅游:
5.1286412E-17
In process
.
汽车:
4.085446E-8
In process
.
财经:
3.7337095E-10
此项属于[IT]
复制代码

作者:洞庭散人

出处:http://phinecos.cnblogs.com/    

本博客遵从 Creative Commons Attribution 3.0 License,若用于非商业目的,您可以自由转载,但请保留原作者信息和文章链接URL。

你可能感兴趣的:(文本挖掘)