我的第一篇學習筆記——使用樸素貝葉斯演算法對文件分類詳解
樸素貝葉斯演算法可以實現對文件的分類,其中最著名的應用之一就是過濾垃圾郵件。先做一個簡單的分類,以論壇的留言為例,構建一個快速的過濾器,來區分哪些留言是負面言論,哪些是正面言論。
我對演算法思路的理解:首先計算訓練集中每個詞語分別在正面(負面)文件中出現的概率以及正面(負面)文件的概率,再計算待分類樣本中的每個詞語屬於正面(負面)文件的概率和正面(負面)文件概率的乘積,即為該樣本屬於正面(負面)樣本的概率,樣本屬於哪一類文件的概率較大就歸為哪類文件(讀著有點繞),下面詳細介紹分類的過程。
1. 條件概率
首先來學習一下基於條件概率的分類思想。對於樣本,它屬於類別的概率為,屬於樣本的概率為
- 如果,那麼樣本屬於類別
- 如果,那麼樣本屬於類別
完整的貝葉斯公式如下:
在此分類演算法中,我們用它的簡化形式:
用分類的思想可以這樣理解這個公式:是待分類樣本的特徵集合,那麼要求得屬於類別的概率,就轉化為求訓練集中,類別的樣本集中特徵集的概率、類別的概率、特徵集的概率,這3個值通過訓練集資料可以更容易計算得出。
2. 準備資料——構建詞袋模型
對每個文字,構建一個向量,向量的維度與詞條列表的維度相同,向量的值是詞條列表中每個詞條在該文字中出現的次數,這種模型叫做詞袋模型。使用Python程式設計實現,程式碼來源《機器學習實戰》,建立bayes.py檔案,加入如下程式碼:
# 載入資料集 def loadDataSet(): postingList = [['my', 'dog', 'has', 'flea', 'problems', 'help', 'please'], ['maybe', 'not', 'take', 'him', 'to', 'dog', 'park','stupid'], ['my', 'dalmation', 'is', 'so', 'cute', 'I', 'love', 'him'], ['stop', 'posting', 'stupid', 'worthless', 'garbage'], ['mr', 'licks', 'ate', 'my', 'steak', 'how', 'to', 'stop','him'], ['quit', 'buying', 'worthless', 'dog', 'food', 'stupid']] classVec = [0, 1, 0, 1, 0, 1] # 1 為負面言論, 0 為正常言論 return postingList, classVec # 建立不重複的詞條列表 def createVocabList(dataSet): vocabSet = set([]) for document in dataSet: vocabSet = vocabSet | set(document) return list(vocabSet) # 樸素貝葉斯詞袋模型 def bagOfWords2Vec(vocabList, inputSet): returnVec = [0] * len(vocabList) for word in inputSet: if word in vocabList: return Vec[vocabList.index(word)] += 1 else: print("the word: %s is not in my Vocabulary!" % word) return returnVec
看一下執行效果:
>>> import bayes
>>> listOPosts, listClasses = bayes.loadDataSet()
>>> myVocabList = bayes.createVocabList(listOPosts)
>>> myVocabList
['steak', 'dalmation', 'garbage', 'love', 'cute', 'please', 'help', 'stop', 'buying', 'worthless',
'ate', 'dog', 'to', 'I', 'food', 'is', 'how', 'not', 'mr', 'quit', 'flea', 'licks', 'problems',
'take', 'so', 'park', 'has', 'my', 'posting', 'stupid', 'maybe', 'him']
>>> bayes.bagOfWords2Vec(myVocabList, listOPosts[0])
[0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0]
>>> bayes.bagOfWords2Vec(myVocabList, listOPosts[1])
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1]
詞條列表中索引為5的詞語是please,它在第一個文件中出現過1次,最後一個詞語是him,它在第二個文件中出現過1次。
3. 訓練演算法——從詞向量計算概率
現在我們知道一個詞是否出現在一篇文件中,也知道文件所屬的類別。為了方便理解,把條件概率公式中的替換為(代表詞向量)
之所以稱為“樸素”貝葉斯,是因為假設樣本中的所有特徵是相互獨立的,那麼就有如下簡化的計算方法:
其中,表示在第類樣本中,第個詞語出現的概率;
表示第類樣本所佔的概率,如示例中有6個樣本,正負樣本各3個,因此和均為0.5;
同樣可以按的方法求得,但是因為我們只需比較和的大小來進行分類,作為相同的分母,在程式碼中沒有計算。
因為要使用數值計算類庫Numpy的一些函式,需要在檔案的最上面新增引用語句:from numpy import *
# 樸素貝葉斯分類器訓練函式,引數為樣本矩陣和類別矩陣
def trainNB0(trainMatrix, trainCategory):
numTrainDocs = len(trainMatrix) # 樣本數
numWords = len(trainMatrix[0]) # 詞條數
pAbusive = sum(trainCategory) / float(numTrainDocs) # 計算類別為1 的概率
p0Num = zeros(numWords)
p1Num = zeros(numWords)
p0Denom = 0.0
p1Denom = 0.0
for i in range(numTrainDocs):
if trainCategory[i] == 1: # 類別為1 的文件
p1Num += trainMatrix[i] # 類別為1 的詞條矩陣
p1Denom += sum(trainMatrix[i]) # 類別為1 的文件的總詞數
else:
p0Num += trainMatrix[i]
p0Denom += sum(trainMatrix[i])
p1Vect = p1Num / p1Denom # 類別為1 的文件中,每個詞出現的概率
p0Vect = p0Num / p0Denom
return p0Vect, p1Vect, pAbusive
測試一下效果:
>>> import bayes
>>> listOPosts, listClasses = bayes.loadDataSet()
>>> myVocabList = bayes.createVocabList(listOPosts)
>>> trainMat = []
>>> for postinDoc in listOPosts:
... trainMat.append(bayes.bagOfWords2Vec(myVocabList, postinDoc))
...
>>> p0V, p1V, pAb = bayes.trainNB0(trainMat, listClasses)
>>> p0V
array([0.04166667, 0.04166667, 0. , 0.04166667, 0.04166667,
0.04166667, 0.04166667, 0.04166667, 0. , 0. ,
0.04166667, 0.04166667, 0.04166667, 0.04166667, 0. ,
0.04166667, 0.04166667, 0. , 0.04166667, 0. ,
0.04166667, 0.04166667, 0.04166667, 0. , 0.04166667,
0. , 0.04166667, 0.125 , 0. , 0. ,
0. , 0.08333333])
>>> p1V
array([0. , 0. , 0.05263158, 0. , 0. ,
0. , 0. , 0.05263158, 0.05263158, 0.10526316,
0. , 0.10526316, 0.05263158, 0. , 0.05263158,
0. , 0. , 0.05263158, 0. , 0.05263158,
0. , 0. , 0. , 0.05263158, 0. ,
0.05263158, 0. , 0. , 0.05263158, 0.15789474,
0.05263158, 0.05263158])
>>> pAb
0.5
負面樣本的概率pAb為0.5,與前面計算的相同。正面樣本中共有23個詞語,負面樣本中共有19個詞語,詞條列表中的第一個詞為steak,它在正面樣本中出現一次,在負面樣本中沒有出現,該詞的條件概率為分別為0.04166667和0,計算正確;倒數第3個詞語是stupid,它在正面樣本中沒有出現,在負面樣本中出現3次,該詞的條件概率分別是0和0.15789474,計算正確。
至此,我們已經求得每個詞語在某類文件中的條件概率:
,
,
在分類之前,有兩個地方需要優化:
(1)根據公式,如果其中有一個詞語的條件概率為0,那麼計算結果就是0,為了避免這種情況,將所有詞出現的次數初始化為1,分母初始化為2。
(2) 多個概率非常小的值相乘,最後四捨五入可能會得到0,可以採用求對數的方法來避免這種現象。
修改trainNB0()方法的第6到9行,和return前的2行,修改後的程式碼:
# 樸素貝葉斯分類器訓練函式,引數為樣本矩陣和類別矩陣
def trainNB0(trainMatrix, trainCategory):
numTrainDocs = len(trainMatrix) # 樣本數
numWords = len(trainMatrix[0]) # 詞條數
pAbusive = sum(trainCategory) / float(numTrainDocs) # 計算類別為1 的概率
p0Num = ones(numWords)
p1Num = ones(numWords)
p0Denom = 2.0
p1Denom = 2.0
for i in range(numTrainDocs):
if trainCategory[i] == 1: # 類別為1 的文件
p1Num += trainMatrix[i] # 類別為1 的詞條矩陣
p1Denom += sum(trainMatrix[i]) # 類別為1 的文件的總詞數
else:
p0Num += trainMatrix[i]
p0Denom += sum(trainMatrix[i])
p1Vect = log(p1Num / p1Denom) # 類別為1 的文件中,每個詞出現的概率
p0Vect = log(p0Num / p0Denom)
return p0Vect, p1Vect, pAbusive
4. 測試演算法——利用分類器對樣本分類
有了前面的準備工作,現在可以構建分類器了,分類器classifyNB有4個引數,依次是待分類的向量,以及trainNB0計算的3個概率值。再寫一個分類的測試函式testingNB,避免每次測試都要輸入測試程式碼。
# 樸素貝葉斯分類函式
def classifyNB(vec2Classify, p0Vec, p1Vec, pClass1):
p1 = sum(vec2Classify * p1Vec) + log(pClass1)
p0 = sum(vec2Classify * p0Vec) + log(1.0 - pClass1)
if p1 > p0:
return 1
else:
return 0
# 測試樸素貝葉斯
def testingNB():
listOPosts, listClasses = loadDataSet()
myVocabList = createVocabList(listOPosts)
trainMat = []
for postinDoc in listOPosts:
trainMat.append(setOfWords2Vec(myVocabList, postinDoc))
p0v, p1v, pAb = trainNB0(trainMat, listClasses)
testEntry = ['love', 'my', 'dalmation']
thisDoc = array(setOfWords2Vec(myVocabList, testEntry))
print(testEntry, 'classified as:', classifyNB(thisDoc, p0v, p1v, pAb))
testEntry = ['stupid', 'garbage']
thisDoc = array(setOfWords2Vec(myVocabList, testEntry))
print(testEntry, 'classified as:', classifyNB(thisDoc, p0v, p1v, pAb))
看看實際效果,很明顯‘love’一類的文字被歸為正面言論,‘stupid’、‘garbage’被歸為負面言論。
>>> import bayes
>>> bayes.testingNB()
['love', 'my', 'dalmation'] classified as: 0
['stupid', 'garbage'] classified as: 1
以上就是我對樸素貝葉斯演算法的學習和理解,理論和程式碼主要來自《機器學習實戰》,並嘗試用更容易理解的表述和公式加以說明,不足之處歡迎探討。