基於朴素貝葉斯的文本分類算法
摘要:常用的文本分類方法有支持向量機、K-近鄰算法和朴素貝葉斯。其中朴素貝葉斯具有容易實現,運行速度快的特點,被廣泛使用。本文詳細介紹了朴素貝葉斯的基本原理,討論多項式模型(MM),實現了可運行的代碼,並進行了一些數據測試。
關鍵字:朴素貝葉斯;文本分類
第1章 貝葉斯原理
1.1 貝葉斯公式[1]
已知某條件概率,如何得到兩個事件交換后的概率,也就是在已知P(A|B)的情況下如何求得P(B|A)。這里先解釋什么是條件概率:
表示事件B已經發生的前提下,事件A發生的概率,叫做事件B發生下事件A的條件概率。其基本求解公式為:
。
貝葉斯定理之所以有用,是因為我們在生活中經常遇到這種情況:我們可以很容易直接得出P(A|B),P(B|A)則很難直接得出,但我們更關心P(B|A),貝葉斯定理就為我們打通從P(A|B)獲得P(B|A)的道路。
貝葉斯定理:
1.2 貝葉斯定理在分類中的應用[2]
在分類(classification)問題中,常常需要把一個事物分到某個類別。一個事物具有很多屬性,把它的眾多屬性看做一個向量,即x=(x1,x2,x3,…,xn),用x這個向量來代表這個事物。類別也是有很多種,用集合Y={y1,y2,…ym}表示。如果x屬於y1類別,就可以給x打上y1標簽,意思是說x屬於y1類別。這就是所謂的分類(Classification)。
x的集合記為X,稱為屬性集。一般X和Y的關系是不確定的,你只能在某種程度上說x有多大可能性屬於類y1,比如說x有80%的可能性屬於類y1,這時可以把X和Y看做是隨機變量,P(Y|X)稱為Y的后驗概率(posterior probability),與之相對的,P(Y)稱為Y的先驗概率(prior probability)[2]。
在訓練階段,我們要根據從訓練數據中收集的信息,對X和Y的每一種組合學習后驗概率P(Y|X)。分類時,來了一個實例x,在剛才訓練得到的一堆后驗概率中找出所有的P(Y|x),其中最大的那個y,即為x所屬分類。根據貝葉斯公式,后驗概率為
在比較不同Y值的后驗概率時,分母P(X)總是常數,因此可以忽略。先驗概率P(Y)可以通過計算訓練集中屬於每一個類的訓練樣本所占的比例容易地估計。
1.3朴素貝葉斯分類器
朴素貝葉斯分類是一種十分簡單的分類算法,叫它朴素貝葉斯分類是因為這種方法的思想真的很朴素,朴素貝葉斯的思想基礎是 這樣的:對於給出的待分類項,求解在此項出現的條件下各個類別出現的概率,哪個最大,就認為此待分類項屬於哪個類別。通俗來說,就好比這么個道理,你在街 上看到一個黑人,我問你你猜這哥們哪里來的,你十有八九猜非洲。為什么呢?因為黑人中非洲人的比率最高,當然人家也可能是美洲人或亞洲人,但在沒有其它可 用信息下,我們會選擇條件概率最大的類別,這就是朴素貝葉斯的思想基礎。
1、條件獨立性[3]
給定類標號y,朴素貝葉斯分類器在估計類條件概率時假設屬性之間條件獨立。條件獨立假設可以形式化的表達如下:
其中每個訓練樣本可用一個屬性向量X=(x1,x2,x3,…,xn)表示,各個屬性之間條件獨立。
比如,對於一篇文章,
Good good study,Day day up.
可以用一個文本特征向量來表示,x=(Good, good, study, Day, day , up)。一般各個詞語之間肯定不是相互獨立的,有一定的上下文聯系。但在朴素貝葉斯文本分類時,我們假設個單詞之間沒有聯系,可以用一個文本特征向量來表示這篇文章,這就是"朴素"的來歷。
2、朴素貝葉斯如何工作
有了條件獨立假設,就不必計算X和Y的每一種組合的類條件概率,只需對給定的Y,計算每個xi的條件概率。后一種方法更實用,因為它不需要很大的訓練集就能獲得較好的概率估計。
3、估計分類屬性的條件概率
P(xi|Y=y)怎么計算呢?它一般根據類別y下包含屬性xi的實例的比例來估計。以文本分類為例,xi表示一個單詞,P(xi|Y=y)=包含該類別下包含單詞的xi的文章總數/ 該類別下的文章總數。
4、條件概率的m估計
假設有來了一個新樣本 x1= (Outlook = Cloudy,Temprature = Cool,Humidity = High,Wind = Strong),要求對其分類。我們來開始計算,
P(Outlook = Cloudy|Yes)=0/9=0 P(Outlook = Cloudy |No)=0/5=0
計算到這里,大家就會意識到,這里出現了一個新的屬性值,在訓練樣本中所沒有的。如果有一個屬性的類條件概率為0,則整個類的后驗概率就等於0,我們可以直接得到后驗概率P(Yes | x1)= P(No | x1)=0,這時二者相等,無法分類。
當訓練樣本不能覆蓋那么多的屬性值時,都會出現上述的窘境。簡單的使用樣本比例來估計類條件概率的方法太脆弱了,尤其是當訓練樣本少而屬性數目又很大時。
解決方法是使用m估計(m-estimate)方法來估計條件概率:
n是類yj中的樣本總數,nc是類yj中取值xi的樣本數,m是稱為等價樣本大小的參數,而p是用戶指定的參數。如果沒有訓練集(即n=0),則P(xi|yj)=p, 因此p可以看作是在類yj的樣本中觀察屬性值xi的先驗概率。等價樣本大小決定先驗概率和觀測概率nc/n之間的平衡。
第一階段——准備工作階段,這個階段的任務是為朴素貝葉斯分類做必要的准備,主要工作是根據具體情況確定特征屬性,並對每個特征屬性進行適當划分, 然后由人工對一部分待分類項進行分類,形成訓練樣本集合。這一階段的輸入是所有待分類數據,輸出是特征屬性和訓練樣本。這一階段是整個朴素貝葉斯分類中唯 一需要人工完成的階段,其質量對整個過程將有重要影響,分類器的質量很大程度上由特征屬性、特征屬性划分及訓練樣本質量決定。
第二階段——分類器訓練階段,這個階段的任務就是生成分類器,主要工作是計算每個類別在訓練樣本中的出現頻率及每個特征屬性划分對每個類別的條件概率估 計,並將結果記錄。其輸入是特征屬性和訓練樣本,輸出是分類器。這一階段是機械性階段,根據前面討論的公式可以由程序自動計算完成。
第三階段——應用階段。這個階段的任務是使用分類器對待分類項進行分類,其輸入是分類器和待分類項,輸出是待分類項與類別的映射關系。這一階段也是機械性階段,由程序完成。
第2章 朴素貝葉斯文本分類算法
現在開始進入本文的主旨部分:如何將貝葉斯分類器應用到文本分類上來。
2.1文本分類問題
在文本分類中,假設我們有一個文檔d∈X,X是文檔向量空間(document space),和一個固定的類集合C={c1,c2,…,cj},類別又稱為標簽。顯然,文檔向量空間是一個高維度空間。我們把一堆打了標簽的文檔集合<d,c>作為訓練樣本,<d,c>∈X×C。例如:
<d,c>={Beijing joins the World Trade Organization, China}
對於這個只有一句話的文檔,我們把它歸類到 China,即打上china標簽。
我們期望用某種訓練算法,訓練出一個函數γ,能夠將文檔映射到某一個類別:
γ:X→C
這種類型的學習方法叫做有監督學習,因為事先有一個監督者(我們事先給出了一堆打好標簽的文檔)像個老師一樣監督着整個學習過程。
2.2多項式模型[4]
1、基本原理
在多項式模型中,設某文檔d=(t1,t2,…,tk),tk是該文檔中出現過的單詞,允許重復,則
先驗概率P(c)= 類c下單詞總數/整個訓練樣本的單詞總數
類條件概率P(tk|c)=(類c下單詞tk在各個文檔中出現過的次數之和+1)/(類c下單詞總數+|V|)
V是訓練樣本的單詞表(即抽取單詞,單詞出現多次,只算一個),|V|則表示訓練樣本包含多少種單詞。在這里,m=|V|, p=1/|V|。
P(tk|c)可以看作是單詞tk在證明d屬於類c上提供了多大的證據,而P(c)則可以認為是類別c在整體上占多大比例(有多大可能性)。
2、偽代碼
//C,類別集合,D,用於訓練的文本文件集合
TrainMultiNomialNB(C,D) {
// 單詞出現多次,只算一個
V←ExtractVocabulary(D)
// 單詞可重復計算
N←CountTokens(D)
for each c∈C
// 計算類別c下的單詞總數
// N和Nc的計算方法和Introduction to Information Retrieval上的不同,個人認為
//該書是錯誤的,先驗概率和類條件概率的計算方法應當保持一致
Nc←CountTokensInClass(D,c)
prior[c]←Nc/N
// 將類別c下的文檔連接成一個大字符串
textc←ConcatenateTextOfAllDocsInClass(D,c)
for each t∈V
// 計算類c下單詞t的出現次數
Tct←CountTokensOfTerm(textc,t)
for each t∈V
//計算P(t|c)
return V,prior,condprob
}
ApplyMultiNomialNB(C,V,prior,condprob,d) {
// 將文檔d中的單詞抽取出來,允許重復,如果單詞是全新的,在全局單詞表V中都
// 沒出現過,則忽略掉
W←ExtractTokensFromDoc(V,d)
for each c∈C
score[c]←prior[c]
for each t∈W
if t∈Vd
score[c] *= condprob[t][c]
return max(score[c])
}
3、舉例[5]
給定一組分類好了的文本訓練數據,如下:
docId |
doc |
類別 In c=China? |
1 |
Chinese Beijing Chinese |
yes |
2 |
Chinese Chinese Shanghai |
yes |
3 |
Chinese Macao |
yes |
4 |
Tokyo Japan Chinese |
no |
給定一個新樣本Chinese Chinese Chinese Tokyo Japan,對其進行分類。
該文本用屬性向量表示為d=(Chinese, Chinese, Chinese, Tokyo, Japan),類別集合為Y={yes, no}。
類yes下總共有8個單詞,類no下總共有3個單詞,訓練樣本單詞總數為11,因此P(yes)=8/11, P(no)=3/11。類條件概率計算如下:
P(Chinese | yes)=(5+1)/(8+6)=6/14=3/7
P(Japan | yes)=P(Tokyo | yes)= (0+1)/(8+6)=1/14
P(Chinese|no)=(1+1)/(3+6)=2/9
P(Japan|no)=P(Tokyo| no) =(1+1)/(3+6)=2/9
分母中的8,是指yes類別下textc的長度,也即訓練樣本的單詞總數,6是指訓練樣本有Chinese,Beijing,Shanghai, Macao, Tokyo, Japan 共6個單詞,3是指no類下共有3個單詞。
有了以上類條件概率,開始計算后驗概率,
P(yes | d)=(3/7)3×1/14×1/14×8/11=108/184877≈0.00058417
P(no | d)= (2/9)3×2/9×2/9×3/11=32/216513≈0.00014780
因此,這個文檔屬於類別china。
第3章代碼詳解
本文附帶了一個eclipse工程,有完整的源代碼,以及一個微型文本訓練庫。
項目的類圖如圖所示
文本目錄如下圖所示:注詞庫的來源是搜狗的實驗室,所以具有較高的實驗性
3.1中文分詞
中文分詞不是本文的重點,這里我們直接使用第三方工具,本源碼使用的是MMAnalyzer極易中文分詞組件
應該導入
public class ChineseSpliter
{
/**
* 對給定的文本進行中文分詞
* @param text 給定的文本
* @param splitToken 用於分割的標記,如"|"
* @return分詞完畢的文本
*/
public static String split(String text,String splitToken)
{
}
}
3.2停止詞處理
停止詞(Stop Word)是指那些無意義的字或詞,如"的"、"在"等。去掉文檔中的停止詞也是必須的一項工作,這里簡單的定義了一些常見的停止詞,並根據這些常用停止詞在分詞時進行判斷。
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;
}
}
3.3預處理數據
把目錄放在D://TraninningSet
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)
{
}
/**
* 返回給定路徑的文本文件內容
* @param filePath 給定的文本文件路徑
* @return文本內容
* @throws java.io.FileNotFoundException
* @throws java.io.IOException
*/
public static String getText(String filePath) throws FileNotFoundException,IOException
{
}
/**
* 返回訓練文本集中所有的文本數目
* @return訓練文本集中所有的文本數目
*/
public int getTrainingFileCount()
{
}
/**
* 返回訓練文本集中在給定分類下的訓練文本數目
* @param classification 給定的分類
* @return訓練文本集中在給定分類下的訓練文本數目
*/
public int getTrainingFileCountOfClassification(String classification)
{
}
/**
* 返回給定分類中包含關鍵字/詞的訓練文本的數目
* @param classification 給定的分類
* @param key 給定的關鍵字/詞
* @return給定分類中包含關鍵字/詞的訓練文本的數目
*/
public int getCountContainKeyOfClassification(String classification,String key)
{
}
3.3訓練
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;
}
}
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);
return ret;
}
}
3.4分類
/**
* 分類結果
*/
public class ClassifyResult
{
public double probility;//分類的概率
public String classification;//分類
public ClassifyResult()
{
this.probility = 0;
this.classification = null;
}
}
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)
{
}
/**
* 去掉停用詞
* @param text 給定的文本
* @return去停用詞后結果
*/
public String[] DropStopWords(String[] oldWords)
{
}
/**
* 對給定的文本進行分類
* @param text 給定的文本
* @return分類結果
*/
@SuppressWarnings("unchecked")
public String classify(String text)
{
//返回概率最大的分類
return crs.get(0).classification;
}
}
測試數據
"新華網天津7月7日體育專電(記者張澤偉)7日,權健集團與天津松江俱樂部聯合召開新聞發布會,宣布權健集團正式全資收購松江俱樂部。權健集團董事長束昱輝表示,投資足球將不留余力、不留私心,要做就做最好。未來俱樂部的發展目標將分四步走:保級、沖超、參加亞冠、參加世俱杯。"
測試結果
更多細節請讀者閱讀源代碼。
References:
[1]. 李維傑與徐勇, 簡體中文垃圾郵件分類的實驗設計及對比研究. 計算機工程與應用, 2007. 43(25): 第128-132頁.
[2]. 黃志剛, 基於貝葉斯的中文垃圾郵件過濾系統的設計與實現, 2007, 電子科技大學.
[3]. 馬世軍, 姚建與喬文, 基於貝葉斯理論的垃圾郵件過濾技術. 硅谷, 2009(13): 第58頁.
[4]. 陸青梅與尹四清, 基於貝葉斯定理的垃圾郵件分類技術研究. 信息技術, 2008(2): 第118-120頁.
[5]. 王科, 基於貝葉斯的中文郵件分類關鍵技術研究, 2008, 南京郵電大學.