朴素貝葉斯分類器是一個以貝葉斯定理為基礎,廣泛應用於情感分類領域的優美分類器。本文我們嘗試使用該分類器來解決上一篇文章中影評態度分類。
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。當然,也可以簡單的使用單詞出現的次數。
使用這個比較大的數據集,可以做一點點數據預處理的優化來避免每次都去硬盤讀取文件。第一次運行時,把讀入的數據保存起來,以后就不用每次再去讀取了。
#保存 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適用於伯努利分布(二值分布)的特征。
附:
使用python訓練貝葉斯模型預測貸款逾期
准備工作
首先是開始前的准備工作,導入所需的庫文件。依次為數值計算庫numpy,科學計算庫pandas,交叉驗證庫cross_validation和朴素貝葉斯算法庫GaussianNB。
#導入數值計算庫 import numpy as np #導入科學計算庫 import pandas as pd #導入交叉驗證庫 from sklearn import cross_validation #導入GaussianNB庫 from sklearn.naive_bayes import GaussianNB
讀取並查看數據表
讀取並創建名為loan_status的貸款歷史數據。這里我們只包含了兩個特征和極少的數據用於說明計算的過程。在真實的環節中要預測貸款的逾期情況所需數據量要大得多。按宜人貸公布的信息,他們的風控系統包含了250個特征和超過100萬條的歷史貸款數據,並且還有一個黑名單系統。
#讀取歷史貸款狀態數據並創建loan_status數據表 loan_status=pd.DataFrame(pd.read_excel('loan_status.xlsx'))
以下是貸款數據表中具體的內容,兩個特征分布為借款人的年齡,借款天數。結果顯示了這筆貸款最終的狀態。
#查看數據表內容 loan_status.head()
使用columns獲得數據表的列名稱。
#查看數據表列標題 loan_status.columns Index(['age', 'day', 'status'], dtype='object')
設置模型特征X及目標Y
將借款人的年齡和借款天數設置為特征X,將貸款的最終還款狀態設置為目標Y。
#設置特征X X=np.array(loan_status[['age','day']]) #設置目標Y Y=np.array(loan_status['status'])
分布查看特征和目標數據的維度。
#查看數據集的維度 X.shape,Y.shape ((823, 2), (823,))
將數據分割為訓練集和測試集
使用交叉檢驗庫通過隨機方式將特征數據和目標數據分為測試集和訓練集,其中訓練集為原數據集的60%,測試集為40%。
#將數據集拆分為訓練集和測試集 X_train,X_test,y_train,y_test=cross_validation.train_test_split(X,Y,test_size=0.4,random_state=0)
再次查看訓練集和測試集數據的維度。
#查看訓練集維度 X_train.shape,y_train.shape ((493, 2), (493,)) #查看測試集維度 X_test.shape,y_test.shape ((330, 2), (330,))
建立高斯朴素貝葉斯模型。
#建立模型 clf=GaussianNB()
使用訓練集數據對模型進行訓練。
#使用訓練集對模型進行訓練 clf.fit(X_train,y_train) GaussianNB(priors=None)
使用測試集數據對訓練后的模型進行測試,模型預測的准確率為69%。
#使用測試集數據檢驗模型准確率 clf.score(X_test,y_test) 0.68787878787878787
使用模型進行分類預測
使用訓練后的高斯朴素貝葉斯模型對新貸款用戶的數據進行分類預測。首先查看分類的結果。與之前的目標Y一樣,結果為Charged Off和Fully Paid。
clf.classes_array([‘Charged Off’, ‘Fully Paid’], dtype='<U11′)
輸入一組貸款用戶特征,年齡為25歲,借款天數30天。模型預測這筆貸款的結果為Fully Paid。
clf.predict([[25,30]])