源代碼下載:NaviveBayesClassify.rar html
Prefacejava
文本的分類和聚類是一個比較有意思的話題,我之前也寫過一篇blog《基於K-Means的文本聚類算法》,加上最近讀了幾本數據挖掘和機器學習的書籍,所以很想寫點東西來記錄下學習的所得。算法
在本文的上半部分《基於樸素貝葉斯分類器的文本分類算法(上)》一文中簡單介紹了貝葉斯學習的基本理論,這一篇將展現如何將該理論運用到中文文本分類中來,具體的文本分類原理就再也不介紹了,在上半部分有,也能夠參見代碼的註釋。app
文本特徵向量機器學習
文本特徵向量能夠描述爲文本中的字/詞構成的屬性。例如給出文本:ide
Good good study,Day day up.性能
能夠得到該文本的特徵向量集:{ Good, good, study, Day, day , up.}學習
樸素貝葉斯模型是文本分類模型中的一種簡單但性能優越的的分類模型。爲了簡化計算過程,假定各待分類文本特徵變量是相互獨立的,即「樸素貝葉斯模型的假設」。相互獨立代表了全部特徵變量之間的表述是沒有關聯的。如上例中,[good]和[study]這兩個特徵變量就是沒有任何關聯的。測試
在上例中,文本是英文,但因爲中文自己是沒有天然分割符(如空格之類符號),因此要得到中文文本的特徵變量向量首先須要對文本進行中文分詞網站
中文分詞
這裏採用極易中文分詞組件,這個中文分詞組件能夠無償使用,提供Lucene接口,跨平臺,性能可靠。
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;
/**
* 先驗機率計算
* <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;
}
}
分類條件機率
這是另外一個影響因子,和先驗機率一塊兒來決定最終結果
package
com.vista;
/**
* <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;
}
}
分類結果
用來保存各個分類及其計算出的機率值,
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中的類條件機率
* <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
使用的測試文本:
微軟公司提出以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]