樸素貝葉斯分類器
一媚狰,生成詞向量(詞集模型)
第一型檀,假設(shè)這里有兩個(gè)參數(shù)vocabList
, inputSet
憎妙。vocabList
代表著包含很多無重復(fù)的詞甚纲,詞量足夠大口锭,inputSet
代表著我們預(yù)轉(zhuǎn)換的詞列表。
第二介杆,創(chuàng)建一個(gè)與vocabList
列表等長的全0列表returnVec
鹃操,用于保存我們后面inputSet
里中詞是否存在vocabList
的標(biāo)記。如果存在春哨,則在對應(yīng)為置為1荆隘;如果不存在,這里簡單處理直接忽略悲靴。因此盡量使vocabList
里的詞足夠多臭胜。遍歷inputSet
詞列表,針對每一個(gè)元素檢測是否出現(xiàn)在vocabList
列表中癞尚,存在,則在與vocabList
同一索引處的returnVec
列表中置位置上中置1乱陡,代完成詞向量標(biāo)記處理浇揩。
第三,返回returnVec
列表即可憨颠。
def setOfWords2Vec(vocabList, inputSet):
returnVec = [0] * len(vocabList)
for word in inputSet:
if word in vocabList:
returnVec[vocabList.index(word)] = 1
else:
print "the %s word not in vocabList" % word
return returnVec
這是詞集模型胳徽,即將詞的出現(xiàn)與否作為統(tǒng)計(jì)度量,如果一個(gè)詞多次出現(xiàn)與出現(xiàn)一次是一樣的爽彤;而詞袋养盗,還包含了詞出現(xiàn)的次數(shù)。在程序中只需修改為returnVec[vocabList.index(word)] += 1
二适篙,計(jì)算樸素貝葉斯先驗(yàn)概率
第一往核,
如果在貝葉斯定理中涉及多個(gè)屬性,我們需要假設(shè)這些屬性間中相互獨(dú)立的嚷节,也即一個(gè)屬性的出現(xiàn)與否不受其他屬性的影響聂儒,雖然假設(shè)是不一定成立的,但這也正是樸素二字的體現(xiàn)硫痰。相比硬規(guī)則而言衩婚,這已經(jīng)使樸素貝葉斯分類器具有相當(dāng)好的結(jié)果了。
圖貝葉斯公式
很容易由訓(xùn)練集求得P(X1|Ci)效斑、P(X2|Ci)非春、P(X3|Ci)...P(Xn|Ci)概率。
X k表示元組X在屬性A k的值。對于每個(gè)屬性需要考查屬性值是分類屬性還是連續(xù)值屬性奇昙。
(1)如果是分類屬性护侮,則P(X1|Ci)的值由屬性值為X1的元組的屬于Ci類別的元組與所有屬于Ci的元組相比求得。
(2)如果是連續(xù)值敬矩,數(shù)個(gè)P(X1|Ci)概行、P(X2|Ci)、P(X3|Ci)...P(Xn|Ci)的概率值乘積也不難求弧岳,通常假定Xi服從均值為u凳忙,標(biāo)準(zhǔn)差為sigma的高斯分布
貝葉斯公式
Xk服從服從均值為u,標(biāo)準(zhǔn)差為sigma的高斯分布
也即我們只需求出Ci類訓(xùn)練元組A k的均值及標(biāo)準(zhǔn)差即可禽炬。
第二涧卵,這里做的是樸素貝葉斯分類屬性的應(yīng)用,兩個(gè)類別腹尖,1代表著垃圾郵件柳恐,0代表正常郵件。
(1)計(jì)算訓(xùn)練文檔條數(shù)热幔,該訓(xùn)練文檔由詞向量組成乐设。
(2)分別計(jì)算屬于各個(gè)分類的詞出現(xiàn)次數(shù)和該分類下總詞數(shù)。
需要計(jì)算多個(gè)概率的乘積以推測具有某些屬性的詞應(yīng)歸屬于哪個(gè)類別下绎巨,如分類為1具有X1近尚,X2,X3,Xn屬性值的概率:
P(X1|C=1)* P(X2|C=1)*P(X3|C=1)...P(Xn|C=1)
场勤,如果其中一個(gè)概率為0戈锻,那么最終概率結(jié)果便成0了,正反例概率同時(shí)為0的概率很大和媳,也就意味著分類無效格遭。為降低概率為0影響,我們在開始為每個(gè)詞出現(xiàn)次數(shù)設(shè)置為1留瞳,文檔數(shù)為2拒迅。同時(shí),為避免多個(gè)float小數(shù)相乘結(jié)果下溢問題撼港,使用numpy模塊的log函數(shù)坪它,注意在下面程序中,我使用python自帶的log函數(shù)運(yùn)算失敗帝牡,該用numpy才算成功往毡。(3)計(jì)算訓(xùn)練文檔的垃圾率,因?yàn)橄蛄吭?代表著垃圾靶溜,故垃圾文檔率為P(C1)开瞭。
def trainNB0(trainMatrix, trainCategory):
numTrainDocs = len(trainMatrix)
numWords = len(trainMatrix[0])
pAbusive = sum(trainCategory) / float(numTrainDocs)
p0Num = ones(numWords)
p1Num = ones(numWords) # p1Num 為單詞出現(xiàn)次數(shù)數(shù)組
p0Denom = 2.0
p1Denom = 2.0 # p1Denom為數(shù)據(jù)集中總詞數(shù)
for i in range(numTrainDocs):
if trainCategory[i] == 1:
p1Num += trainMatrix[i]
p1Denom += sum(trainMatrix[i])
else:
p0Num += trainMatrix[i]
p0Denom += sum(trainMatrix[i])
p1Vect = log(p1Num / p1Denom)
p0Vect = log(p0Num / p0Denom)
return p0Vect, p1Vect, pAbusive
三懒震, 樸素貝葉斯分類器各類別先驗(yàn)概率
在前面我們?yōu)楸苊舛鄠€(gè)小數(shù)值相乘結(jié)果下溢問題,使用了log函數(shù)相加便得出正反例概率相對值大小嗤详。
P(C|W) = P(W|C) * P(C) / P(W)
在計(jì)算正反例(這里僅僅有兩個(gè)分類)后驗(yàn)概率是个扰,由于分母P(W)代表著在所有文檔中該屬性組合出現(xiàn)的概率,它是相等的葱色,所以递宅,我們可以僅僅比較分子大小便可以得出具有某些特征的文檔屬于哪個(gè)類別。
def classifyNB(vec2Classify, p0Vec, p1Vec, pClass1):
# 不在預(yù)保留的詞匯表中的詞語默認(rèn)都是好詞
# vec2Classify 用來確定測試單詞是否存在苍狰。
p1 = sum(vec2Classify * p1Vec) + log(pClass1)
p0 = sum(vec2Classify * p0Vec) + log(1 - pClass1)
if p1 > p0:
return 1
else:
return 0
有一點(diǎn)需要注意的是办龄,sum(vec2Classify * p1Vec) + log(pClass1)
表示貝葉斯定理的分子部分。
vec2Classify在詞集模型中表示該屬性是否在訓(xùn)練數(shù)據(jù)集中出現(xiàn)淋昭,如果出現(xiàn)俐填,該屬性的概率值為p1Vec或p0Vec數(shù)組所對應(yīng)位置處的值。最后比較兩個(gè)概率大小翔忽,返回大者英融。
而在詞袋模型中,vec2Classify具有體現(xiàn)該屬性出現(xiàn)的次數(shù)能力歇式,在計(jì)算概率過程中驶悟,它相當(dāng)于為該屬性附上權(quán)重。
四材失,對樸素貝葉斯分類器的分類的測試
def testingNB():
listOfPosts, listClasses = loadDataSet()
myVocabList = createVocabList(listOfPosts)
trainMat = []
for postinDoc in listOfPosts:
trainMat.append(setOfWords2Vec(myVocabList, postinDoc))
p0V, p1V, pAb = trainNB0(array(trainMat), array(listClasses))
testEntry = ['love', 'my', 'dalmation']
thisDoc = array(setOfWords2Vec(myVocabList, testEntry))
print testEntry,"classified is:", classifyNB(thisDoc, p0V, p1V, pAb)
testEntry =['beijing', 'HongKong']
thisDoc = array(setOfWords2Vec(myVocabList, testEntry))
print testEntry, "classified is:", classifyNB(thisDoc, p0V, p1V, pAb)
因?yàn)樵谟?xùn)練數(shù)據(jù)集沒有['beijing', 'HongKong']
撩银,同時(shí)對訓(xùn)練集中不存在的詞的處理默認(rèn)是非垃圾的,故在這里可以看到['beijing', 'HongKong']
也被分到正常類別當(dāng)中豺憔。
五 拓展技巧
(1),留存交叉驗(yàn)證
是指從數(shù)據(jù)集中隨機(jī)選擇一部分?jǐn)?shù)據(jù)作為訓(xùn)練集够庙,而余下的數(shù)據(jù)部分作為測試集恭应,對減弱數(shù)據(jù)過擬合的有重要作用。
Python代碼實(shí)現(xiàn):
思想很簡單耘眨,創(chuàng)建一個(gè)與數(shù)據(jù)集具有同樣長度的trainingIndexSet用來保存訓(xùn)練集索引昼榛,之后從該索引列表中移除測試數(shù)據(jù)集的索引,并保存在一個(gè)新的索引中剔难。在模型訓(xùn)練過程中胆屿,通過索引加載所需數(shù)據(jù)集,同樣測試也是偶宫,由于沒有對分類類標(biāo)進(jìn)行操作非迹,因此,不管是在訓(xùn)練集的屬性還是測試集的屬性所對應(yīng)的分類類號都還是一一對應(yīng)的纯趋。
從10個(gè)數(shù)據(jù)集中隨機(jī)抽出3個(gè)作為測試集憎兽。
alist = [[0, 1, 2, 3, 4],
[5, 6, 7, 8, 9],
[10, 11, 12, 13, 14],
[15, 16, 17, 18, 19],
[20, 21, 22, 23, 24],
[25, 26, 27, 28, 29],
[30, 31, 32, 33, 34],
[35, 36, 37, 38, 39],
[40, 41, 42, 43, 44],
[45, 46, 47, 48, 49]]
classes = [1, 0, 1, 1, 1, 0, 0, 1, 0, 0]
trainingIndexSet = range(len(alist))
testIndexSet = []
for i in range(3):
index = int(random.uniform(0, len(trainingIndexSet)))
print 'index: ', index
testIndexSet.append(trainingIndexSet[index])
del trainingIndexSet[index]
print testIndexSet
# 在模型訓(xùn)練和測試時(shí)通過`alist[trainingIndexSet[i]]`冷离、`alist[testIndex[i]]`獲取數(shù)據(jù)集
注意uniform函數(shù)按說能夠返回與末端相等的值,但我試驗(yàn)循環(huán)1百萬次沒試出來纯命。
還有一種對分類號操作的實(shí)現(xiàn)西剥,這里不寫了。
六亿汞,總結(jié)
(1)詞集僅僅統(tǒng)計(jì)一個(gè)單詞是否出現(xiàn)瞭空;而詞袋還包含了單詞出現(xiàn)次數(shù)的統(tǒng)計(jì),相當(dāng)在計(jì)算貝葉斯先驗(yàn)概率時(shí)為每個(gè)單詞賦予權(quán)重疗我;TF-IDF是更高級的文本分類應(yīng)用咆畏,它排除了輔助詞對文本分類的影響,如中文中的"的"碍粥,"是"鳖眼,"啊"等詞,這些詞在對正確分類的貢獻(xiàn)小嚼摩、價(jià)值低钦讳,因此給予它很小的權(quán)重,雖然出現(xiàn)次數(shù)很多枕面,這也就是逆文檔率概念愿卒。
(2)貝葉斯先驗(yàn)概率求解過程涉及多個(gè)概率相乘問題,最終結(jié)果可能下溢問題潮秘,故選用log琼开,首選numpy,Python自帶的log函數(shù)可能不合適枕荞。