源代碼下載:NaviveBayesClassify.rarhtml
Prefacejava
文本的分類和聚類是一個比較有意思的話題,我之前也寫過一篇blog《基於K-Means的文本聚類算法》,加上最近讀了幾本數據挖掘和機器學習的書籍,所以很想寫點東西來記錄下學習的所得。算法
在本文的上半部分《基於樸素貝葉斯分類器的文本分類算法(上)》一文中簡單介紹了貝葉斯學習的基本理論,這一篇將展現如何將該理論運用到中文文本分類中來,具體的文本分類原理就再也不介紹了,在上半部分有,也能夠參見代碼的註釋。app
文本特徵向量機器學習
文本特徵向量能夠描述爲文本中的字/詞構成的屬性。例如給出文本:ide
Good good study,Day day up.post
能夠得到該文本的特徵向量集:{ Good, good, study, Day, day , up.}性能
樸素貝葉斯模型是文本分類模型中的一種簡單但性能優越的的分類模型。爲了簡化計算過程,假定各待分類文本特徵變量是相互獨立的,即「樸素貝葉斯模型的假設」。相互獨立代表了全部特徵變量之間的表述是沒有關聯的。如上例中,[good]和[study]這兩個特徵變量就是沒有任何關聯的。學習
在上例中,文本是英文,但因爲中文自己是沒有天然分割符(如空格之類符號),因此要得到中文文本的特徵變量向量首先須要對文本進行中文分詞測試
中文分詞
這裏採用極易中文分詞組件,這個中文分詞組件能夠無償使用,提供Lucene接口,跨平臺,性能可靠。
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;
}
}
停用詞處理<?xml:namespace prefix="O">?xml:namespace>
去掉文檔中無心思的詞語也是必須的一項工做,這裏簡單的定義了一些常見的停用詞,並根據這些經常使用停用詞在分詞時進行判斷。
/* *
* 停用詞處理器
* @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 ;
}
}
訓練集管理器
咱們的系統首先須要從訓練樣本集中獲得假設的先驗機率和給定假設下觀察到不一樣數據的機率。
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;
}
}
先驗機率
先驗機率是咱們須要計算的兩大機率值之一
/**
* 先驗機率計算
* <h3>先驗機率計算</h3>
* P(c<sub>j</sub>)=N(C=c<sub>j</sub>)<b>/</b>N <br>
* 其中,N(C=c<sub>j</sub>)表示類別c<sub>j</sub>中的訓練文本數量;
* 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;
}
}
分類條件機率
這是另外一個影響因子,和先驗機率一塊兒來決定最終結果
/**
* <b>類</b>條件機率計算
*
* <h3>類條件機率</h3>
* P(x<sub>j</sub>|c<sub>j</sub>)=( N(X=x<sub>i</sub>, C=c<sub>j
* </sub>)+1 ) <b>/</b> ( N(C=c<sub>j</sub>)+M+V ) <br>
* 其中,N(X=x<sub>i</sub>, C=c<sub>j</sub>)表示類別c<sub>j</sub>中包含屬性x<sub>
* i</sub>的訓練文本數量;N(C=c<sub>j</sub>)表示類別c<sub>j</sub>中的訓練文本數量;M值用於避免
* N(X=x<sub>i</sub>, C=c<sub>j</sub>)太小所引起的問題;V表示類別的總數。
*
* <h3>條件機率</h3>
* <b>定義</b> 設A, B是兩個事件,且P(A)>0 稱<br>
* <tt>P(B∣A)=P(AB)/P(A)</tt><br>
* 爲在條件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;
}
}
分類結果
用來保存各個分類及其計算出的機率值,
/**
* 分類結果
*/
public class ClassifyResult
{
public double probility; // 分類的機率
public String classification; // 分類
public ClassifyResult()
{
this .probility = 0 ;
this .classification = null ;
}
}
樸素貝葉斯分類器
利用樣本數據集計算先驗機率和各個文本向量屬性在分類中的條件機率,從而計算出各個機率值,最後對各個機率值進行排序,選出最大的機率值,即爲所屬的分類。
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中的類條件機率
* <code>ClassConditionalProbability</code>連乘值
* @param X 給定的文本屬性向量
* @param Cj 給定的類別
* @return 分類條件機率連乘值,即<br>
*/
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
使用的測試文本:
中國網2月1日報道 美聯社消息,微軟公司提出以446億美圓現金加股票的價格收購搜索網站雅虎公司。
微軟提出以每股31美圓的價格收購雅虎。微軟的收購報價較雅虎1月31日的收盤價19 . 18美圓溢價62%。微軟公司稱雅虎公司的股東能夠選擇以現金或股票進行交易。
微軟和雅虎公司在2006年末和2007年初已在尋求雙方合做。而近兩年,雅虎一直處於困境:市場份額下滑、運營業績不佳、股價大幅下跌。對於力圖在互聯網市場有所做爲的微軟來講,收購雅虎無疑是一條捷徑,由於雙方具備很是強的互補性。 ( 小橋 )
使用mini版本的測試結果:
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]