應用樸素貝葉斯分類器對文本簡單分類
樸素貝葉斯分類器
一,生成詞向量(詞集模型)
第一,假設這里有兩個參數vocabList
, inputSet
。vocabList
代表著包含很多無重復的詞,詞量足夠大,inputSet
代表著我們預轉換的詞列表。
第二,創建一個與vocabList
列表等長的全0列表returnVec
,用于保存我們后面inputSet
里中詞是否存在vocabList
的標記。如果存在,則在對應為置為1;如果不存在,這里簡單處理直接忽略。因此盡量使vocabList
里的詞足夠多。遍歷inputSet
詞列表,針對每一個元素檢測是否出現在vocabList
列表中,存在,則在與vocabList
同一索引處的returnVec
列表中置位置上中置1,代完成詞向量標記處理。
第三,返回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
這是詞集模型,即將詞的出現與否作為統計度量,如果一個詞多次出現與出現一次是一樣的;而詞袋,還包含了詞出現的次數。在程序中只需修改為returnVec[vocabList.index(word)] += 1
二,計算樸素貝葉斯先驗概率
第一,
貝葉斯概率.png
如果在貝葉斯定理中涉及多個屬性,我們需要假設這些屬性間中相互獨立的,也即一個屬性的出現與否不受其他屬性的影響,雖然假設是不一定成立的,但這也正是樸素二字的體現。相比硬規則而言,這已經使樸素貝葉斯分類器具有相當好的結果了。
圖貝葉斯公式
很容易由訓練集求得P(X1|Ci)、P(X2|Ci)、P(X3|Ci)...P(Xn|Ci)概率。
X k表示元組X在屬性A k的值。對于每個屬性需要考查屬性值是分類屬性還是連續值屬性。
(1)如果是分類屬性,則P(X1|Ci)的值由屬性值為X1的元組的屬于Ci類別的元組與所有屬于Ci的元組相比求得。
(2)如果是連續值,數個P(X1|Ci)、P(X2|Ci)、P(X3|Ci)...P(Xn|Ci)的概率值乘積也不難求,通常假定Xi服從均值為u,標準差為sigma的高斯分布
貝葉斯公式
樸素貝葉斯計算公式.png
Xk服從服從均值為u,標準差為sigma的高斯分布
高斯計算公式.png
也即我們只需求出Ci類訓練元組A k的均值及標準差即可。
第二,這里做的是樸素貝葉斯分類屬性的應用,兩個類別,1代表著垃圾郵件,0代表正常郵件。
(1)計算訓練文檔條數,該訓練文檔由詞向量組成。
(2)分別計算屬于各個分類的詞出現次數和該分類下總詞數。
需要計算多個概率的乘積以推測具有某些屬性的詞應歸屬于哪個類別下,如分類為1具有X1,X2,X3,Xn屬性值的概率:
P(X1|C=1)* P(X2|C=1)*P(X3|C=1)...P(Xn|C=1)
,如果其中一個概率為0,那么最終概率結果便成0了,正反例概率同時為0的概率很大,也就意味著分類無效。為降低概率為0影響,我們在開始為每個詞出現次數設置為1,文檔數為2。同時,為避免多個float小數相乘結果下溢問題,使用numpy模塊的log函數,注意在下面程序中,我使用python自帶的log函數運算失敗,該用numpy才算成功。
(3)計算訓練文檔的垃圾率,因為向量元素1代表著垃圾,故垃圾文檔率為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 為單詞出現次數數組 p0Denom = 2.0 p1Denom = 2.0 # p1Denom為數據集中總詞數 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
三, 樸素貝葉斯分類器各類別先驗概率
在前面我們為避免多個小數值相乘結果下溢問題,使用了log函數相加便得出正反例概率相對值大小。
P(C|W) = P(W|C) * P(C) / P(W)
在計算正反例(這里僅僅有兩個分類)后驗概率是,由于分母P(W)代表著在所有文檔中該屬性組合出現的概率,它是相等的,所以,我們可以僅僅比較分子大小便可以得出具有某些特征的文檔屬于哪個類別。
def classifyNB(vec2Classify, p0Vec, p1Vec, pClass1): # 不在預保留的詞匯表中的詞語默認都是好詞 # vec2Classify 用來確定測試單詞是否存在。 p1 = sum(vec2Classify * p1Vec) + log(pClass1) p0 = sum(vec2Classify * p0Vec) + log(1 - pClass1) if p1 > p0: return 1 else: return 0
有一點需要注意的是,sum(vec2Classify * p1Vec) + log(pClass1)
表示貝葉斯定理的分子部分。
vec2Classify在詞集模型中表示該屬性是否在訓練數據集中出現,如果出現,該屬性的概率值為p1Vec或p0Vec數組所對應位置處的值。最后比較兩個概率大小,返回大者。
而在詞袋模型中,vec2Classify具有體現該屬性出現的次數能力,在計算概率過程中,它相當于為該屬性附上權重。
四,對樸素貝葉斯分類器的分類的測試
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)
因為在訓練數據集沒有['beijing', 'HongKong']
,同時對訓練集中不存在的詞的處理默認是非垃圾的,故在這里可以看到['beijing', 'HongKong']
也被分到正常類別當中。
五 拓展技巧
(1),留存交叉驗證
是指從數據集中隨機選擇一部分數據作為訓練集,而余下的數據部分作為測試集,對減弱數據過擬合的有重要作用。
Python代碼實現:
思想很簡單,創建一個與數據集具有同樣長度的trainingIndexSet用來保存訓練集索引,之后從該索引列表中移除測試數據集的索引,并保存在一個新的索引中。在模型訓練過程中,通過索引加載所需數據集,同樣測試也是,由于沒有對分類類標進行操作,因此,不管是在訓練集的屬性還是測試集的屬性所對應的分類類號都還是一一對應的。
從10個數據集中隨機抽出3個作為測試集。
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 # 在模型訓練和測試時通過`alist[trainingIndexSet[i]]`、`alist[testIndex[i]]`獲取數據集
注意uniform函數按說能夠返回與末端相等的值,但我試驗循環1百萬次沒試出來。
還有一種對分類號操作的實現,這里不寫了。
六,總結
(1)詞集僅僅統計一個單詞是否出現;而詞袋還包含了單詞出現次數的統計,相當在計算貝葉斯先驗概率時為每個單詞賦予權重;TF-IDF是更高級的文本分類應用,它排除了輔助詞對文本分類的影響,如中文中的"的","是","啊"等詞,這些詞在對正確分類的貢獻小、價值低,因此給予它很小的權重,雖然出現次數很多,這也就是逆文檔率概念。
(2)貝葉斯先驗概率求解過程涉及多個概率相乘問題,最終結果可能下溢問題,故選用log,首選numpy,Python自帶的log函數可能不合適。