用Python開始機器學習(6:樸素貝葉斯分類器)

樸素貝葉斯分類器是一個以貝葉斯定理爲基礎,廣泛應用於情感分類領域的優美分類器。本文我們嘗試使用該分類器來解決上一篇文章中影評態度分類。

1、貝葉斯定理

假設對於某個數據集,隨機變量C表示樣本爲C類的概率,F1表示測試樣本某特徵出現的概率,套用基本貝葉斯公式,則如下所示:


上式表示對於某個樣本,特徵F1出現時,該樣本被分爲C類的條件概率。那麼如何用上式來對測試樣本分類呢?

舉例來說,有個測試樣本,其特徵F1出現了(F1=1),那麼就計算P(C=0|F1=1)和P(C=1|F1=1)的概率值。前者大,則該樣本被認爲是0類;後者大,則分爲1類。

對該公示,有幾個概念需要熟知:

先驗概率(Prior)。P(C)是C的先驗概率,可以從已有的訓練集中計算分爲C類的樣本佔所有樣本的比重得出。

證據(Evidence)。即上式P(F1),表示對於某測試樣本,特徵F1出現的概率。同樣可以從訓練集中F1特徵對應樣本所佔總樣本的比例得出。

似然(likelihood)。即上式P(F1|C),表示如果知道一個樣本分爲C類,那麼他的特徵爲F1的概率是多少。

對於多個特徵而言,貝葉斯公式可以擴展如下:


分子中存在一大串似然值。當特徵很多的時候,這些似然值的計算是極其痛苦的。現在該怎麼辦?

2、樸素的概念

爲了簡化計算,樸素貝葉斯算法做了一假設:“樸素的認爲各個特徵相互獨立”。這麼一來,上式的分子就簡化成了:

P(C)*P(F1|C)*P(F2|C)...P(Fn|C)。

這樣簡化過後,計算起來就方便多了。

這個假設是認爲各個特徵之間是獨立的,看上去確實是個很不科學的假設。因爲很多情況下,各個特徵之間是緊密聯繫的。然而在樸素貝葉斯的大量應用實踐實際表明其工作的相當好。

其次,由於樸素貝葉斯的工作原理是計算P(C=0|F1...Fn)和P(C=1|F1...Fn),並取最大值的那個作爲其分類。而二者的分母是一模一樣的。因此,我們又可以省略分母計算,從而進一步簡化計算過程。

另外,貝葉斯公式推導能夠成立有個重要前期,就是各個證據(evidence)不能爲0。也即對於任意特徵Fx,P(Fx)不能爲0。而顯示某些特徵未出現在測試集中的情況是可以發生的。因此實現上通常要做一些小的處理,例如把所有計數進行+1(加法平滑(additive smoothing,又叫拉普拉斯平滑(Laplace smothing))。而如果通過增加一個大於0的可調參數alpha進行平滑,就叫Lidstone平滑

例如,在所有6個分爲C=1的影評樣本中,某個特徵F1=1不存在,則P(F1=1|C=1)  = 0/6,P(F1=0|C=1)  = 6/6。

經過加法平滑後,P(F1=1|C=1)  = (0+1)/(6+2)=1/8,P(F1=0|C=1)  = (6+1)/(6+2)=7/8。

注意分母的+2,這種特殊處理使得2個互斥事件的概率和恆爲1。

最後,我們知道,當特徵很多的時候,大量小數值的小數乘法會有溢出風險。因此,通常的實現都是將其轉換爲log

log[P(C)*P(F1|C)*P(F2|C)...P(Fn|C)] = log[P(C)]+log[P(F1|C)] + ... +log[P(Fn|C)]

將乘法轉換爲加法,就徹底避免了乘法溢出風險。

爲確保掌握樸素貝葉斯分類原理,我們先使用上一篇文章最後的文本向量化結果做一個例子:


上述訓練集中共8個樣本,其中C=0的3個,C=1的5個。現在,假設給你一個測試樣本"nb movie",使用加一平滑進行樸素貝葉斯的分類過程如下:

 P(C=0)=3/8, P(C=1)=5/8。特徵F1="nb", F2="movie"。

分爲C=0的概率:P(F1=1, F2=1|C=0) = P(C=0)*P(F1=1|C=0)*P(F2=1|C=0) = 3/8 * (0+1)/(3+2) * (1+1)/(3+2) = 3/8 * 1/5 * 2/5 = 0.03。

分爲C=1的概率:P(F1=1, F2=1|C=1) = P(C=1)*P(F1=1|C=1)*P(F2=1|C=1) = 5/8 * (3+1)/(5+2) * (3+1)/(5+2) = 5/8 * 4/7 * 4/7 = 0.20。

分爲C=1的概率更大。因此將該樣本分爲C=1類。

(注意:實際計算中還要考慮上表中各個值的TF-IDF,具體計算方式取決於使用哪一類貝葉斯分類器。分類器種類見本文最後說明)

3、測試數據

本文使用上一篇博客中提到的康奈爾大學網站的2M影評數據集。下載地址http://download.csdn.net/detail/lsldd/9346233

每一個特徵值就是一個單詞的TF-IDF。當然,也可以簡單的使用單詞出現的次數。

使用這個比較大的數據集,可以做一點點數據預處理的優化來避免每次都去硬盤讀取文件。第一次運行時,把讀入的數據保存起來,以後就不用每次再去讀取了。

[python] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. #保存  
  2. movie_reviews = load_files('endata')  
  3. sp.save('movie_data.npy', movie_data)  
  4. sp.save('movie_target.npy', movie_target)  
  5.   
  6. #讀取  
  7. movie_data   = sp.load('movie_data.npy')  
  8. movie_target = sp.load('movie_target.npy')  

4、代碼與分析

Python代碼如下:

[python] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. # -*- coding: utf-8 -*-  
  2. from matplotlib import pyplot  
  3. import scipy as sp  
  4. import numpy as np  
  5. from sklearn.datasets import load_files  
  6. from sklearn.cross_validation import train_test_split  
  7. from sklearn.feature_extraction.text import  CountVectorizer  
  8. from sklearn.feature_extraction.text import  TfidfVectorizer  
  9. from sklearn.naive_bayes import MultinomialNB  
  10. from sklearn.metrics import precision_recall_curve  
  11. from sklearn.metrics import classification_report  
  12.   
  13. ''''' 
  14. movie_reviews = load_files('data') 
  15. #保存 
  16. sp.save('movie_data.npy', movie_reviews.data) 
  17. sp.save('movie_target.npy', movie_reviews.target) 
  18. '''  
  19.   
  20. #讀取  
  21. movie_data   = sp.load('movie_data.npy')  
  22. movie_target = sp.load('movie_target.npy')  
  23. x = movie_data  
  24. y = movie_target  
  25.   
  26. #BOOL型特徵下的向量空間模型,注意,測試樣本調用的是transform接口  
  27. count_vec = TfidfVectorizer(binary = False, decode_error = 'ignore',\  
  28.                             stop_words = 'english')  
  29.   
  30.   
  31. #加載數據集,切分數據集80%訓練,20%測試  
  32. x_train, x_test, y_train, y_test\  
  33.     = train_test_split(movie_data, movie_target, test_size = 0.2)  
  34. x_train = count_vec.fit_transform(x_train)  
  35. x_test  = count_vec.transform(x_test)  
  36.   
  37.   
  38. #調用MultinomialNB分類器  
  39. clf = MultinomialNB().fit(x_train, y_train)  
  40. doc_class_predicted = clf.predict(x_test)  
  41.       
  42. #print(doc_class_predicted)  
  43. #print(y)  
  44. print(np.mean(doc_class_predicted == y_test))  
  45.   
  46. #準確率與召回率  
  47. precision, recall, thresholds = precision_recall_curve(y_test, clf.predict(x_test))  
  48. answer = clf.predict_proba(x_test)[:,1]  
  49. report = answer > 0.5  
  50. print(classification_report(y_test, report, target_names = ['neg''pos']))  
輸出結果如下所示:

0.821428571429
             precision    recall  f1-score   support
        neg       0.78      0.87      0.83       135
        pos       0.87      0.77      0.82       145
avg / total     0.83      0.82      0.82       280
如果進行多次交叉檢驗,可以發現樸素貝葉斯分類器在這個數據集上能夠達到80%以上的準確率。如果你親自測試一下,會發現KNN分類器在該數據集上只能達到60%的準確率,相信你對樸素貝葉斯分類器應該能夠刮目相看了。而且要知道,情感分類這種帶有主觀色彩的分類準則,連人類都無法達到100%準確。

要注意的是,我們選用的樸素貝葉斯分類器類別:MultinomialNB,這個分類器以出現次數作爲特徵值,我們使用的TF-IDF也能符合這類分佈。

其他的樸素貝葉斯分類器如GaussianNB適用於高斯分佈(正態分佈)的特徵,而BernoulliNB適用於伯努利分佈(二值分佈)的特徵。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章