1. 程式人生 > >用Python開始機器學習(6:樸素貝葉斯分類器)

用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、測試資料

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

使用這個比較大的資料集,可以做一點點資料預處理的優化來避免每次都去硬碟讀取檔案。第一次執行時,把讀入的資料儲存起來,以後就不用每次再去讀取了。

#儲存
movie_reviews = load_files('endata')
sp.save('movie_data.npy', movie_data)
sp.save('movie_target.npy', movie_target)

#讀取
movie_data   = sp.load('movie_data.npy')
movie_target = sp.load('movie_target.npy')

4、程式碼與分析

Python程式碼如下:

# -*- coding: utf-8 -*-
from matplotlib import pyplot
import scipy as sp
import numpy as np
from sklearn.datasets import load_files
from sklearn.cross_validation import train_test_split
from sklearn.feature_extraction.text import  CountVectorizer
from sklearn.feature_extraction.text import  TfidfVectorizer
from sklearn.naive_bayes import MultinomialNB
from sklearn.metrics import precision_recall_curve
from sklearn.metrics import classification_report

'''
movie_reviews = load_files('data')
#儲存
sp.save('movie_data.npy', movie_reviews.data)
sp.save('movie_target.npy', movie_reviews.target)
'''

#讀取
movie_data   = sp.load('movie_data.npy')
movie_target = sp.load('movie_target.npy')
x = movie_data
y = movie_target

#BOOL型特徵下的向量空間模型,注意,測試樣本呼叫的是transform介面
count_vec = TfidfVectorizer(binary = False, decode_error = 'ignore',\
                            stop_words = 'english')


#載入資料集,切分資料集80%訓練,20%測試
x_train, x_test, y_train, y_test\
    = train_test_split(movie_data, movie_target, test_size = 0.2)
x_train = count_vec.fit_transform(x_train)
x_test  = count_vec.transform(x_test)


#呼叫MultinomialNB分類器
clf = MultinomialNB().fit(x_train, y_train)
doc_class_predicted = clf.predict(x_test)
    
#print(doc_class_predicted)
#print(y)
print(np.mean(doc_class_predicted == y_test))

#準確率與召回率
precision, recall, thresholds = precision_recall_curve(y_test, clf.predict(x_test))
answer = clf.predict_proba(x_test)[:,1]
report = answer > 0.5
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適用於伯努利分佈(二值分佈)的特徵。