前言
朴素貝葉斯算法最為廣泛而經典的應用毫無疑問是文檔分類,更具體的情形是郵件過濾系統。
本文詳細地講解一個基於朴素貝葉斯分類算法的郵件過濾系統的具體實現。
本文側重於工程實現,至於其中很多算法的細節請參考之前的一篇文章:朴素貝葉斯分類算法原理分析與代碼實現。
准備數據:切分文本
獲取到文本文件之后,首先要做的是兩件事情:
1. 將文本文件轉換為詞匯列表
2. 將上一步的結果進一步轉換為詞向量
對於 1,具體來說,就是將文本文件以非字母或數字之外的字符為界進行切割。
僅僅使用字符串的 split 函數實現起來很麻煩,而Python中真正處理文本的利器是正則表達式,使用正則表達式可輕易實現這個任務。
如下函數可用於實現1:
1 #============================================= 2 # 輸入: 3 # bigString: 待轉換文檔字符串 4 # 輸出: 5 # 待轉換文檔的列表格式 6 #============================================= 7 def textParse(bigString): 8 import re 9 listOfTokens = re.split(r'\W*', bigString) 10 return [tok.lower() for tok in listOfTokens if len(tok) > 2]
注意,由於切分后的結果有可能出現空格符,因此在返回時再加了一層過濾。
關於正則表達式的具體用法不屬於本文講解范圍,有興趣的讀者請自行查閱相關資料。
對於2,在上一篇文章:朴素貝葉斯分類算法原理分析與代碼實現中已經有過實現的范例,這里就不再累述。
訓練並測試
1. 從代碼中指定路徑的目錄中查找郵件(不同分類的兩個目錄),搜集所有郵件信息並將其轉換為詞向量格式。
2. 將這部分數據再分為訓練集部分和測試集部分。
3. 調用朴素貝葉斯分類函數對數據集進行訓練,得到貝葉斯公式中的各個概率子項。
4. 求出待分類文檔的詞向量並繼續完成此貝葉斯公式來計算該詞向量的屬於各分類的概率。取其中最大概率為分類結果。
5. 將上一步得到的分類結果和實際結果對照,打印最終測試信息。
如下代碼用於訓練及測試:
1 #============================================= 2 # 輸入: 3 # vocabList: 詞匯列表 4 # inputSet: 待轉換文檔的列表格式 5 # 輸出: 6 # returnVec: 轉換后的詞向量(詞袋模型) 7 #============================================= 8 def bagOfWords2VecMN(vocabList, inputSet): 9 '文檔(列表格式) -> 詞向量(詞袋模型)' 10 11 returnVec = [0]*len(vocabList) 12 for word in inputSet: 13 if word in vocabList: 14 returnVec[vocabList.index(word)] += 1 15 16 return returnVec 17 18 #============================================= 19 # 輸入: 20 # 代碼內指定路徑的兩種分類郵件 21 # 輸出: 22 # 空 (打印貝葉斯分類測試的結果) 23 #============================================= 24 def spamTest(): 25 '測試貝葉斯分類並打印結果' 26 27 # 文檔字符串集合 28 docList=[] 29 # 文檔分類集合 30 classList = [] 31 # 所有字符串 32 fullText =[] 33 34 # 從兩種分類郵件中各取出25封郵件,獲得文檔字符串集合,文檔分類集合,所有字符串。 35 for i in range(1,26): 36 wordList = textParse(open('/home/fangmeng/email/spam/%d.txt' % i).read()) 37 docList.append(wordList) 38 fullText.extend(wordList) 39 classList.append(1) 40 wordList = textParse(open('/home/fangmeng/email/ham/%d.txt' % i).read()) 41 docList.append(wordList) 42 fullText.extend(wordList) 43 classList.append(0) 44 45 # 詞匯表 46 vocabList = createVocabList(docList) 47 # 訓練集范圍 48 trainingSet = range(50) 49 # 測試集范圍 50 testSet=[] 51 52 # 這總共50封郵件里,取10封用來做分類測試。 53 # 同時將這10封測試郵件從訓練集范圍內移除。 54 for i in range(10): 55 randIndex = int(random.uniform(0,len(trainingSet))) 56 testSet.append(trainingSet[randIndex]) 57 del(trainingSet[randIndex]) 58 59 # 訓練集詞向量矩陣 60 trainMat=[] 61 # 訓練集分類列表 62 trainClasses = [] 63 64 # 構建訓練集詞向量矩陣和訓練集分類列表 65 for docIndex in trainingSet: 66 trainMat.append(bagOfWords2VecMN(vocabList, docList[docIndex])) 67 trainClasses.append(classList[docIndex]) 68 69 # 對訓練集中郵件進行分類測試並打印測試結果 70 p0V,p1V,pSpam = trainNB0(numpy.array(trainMat),numpy.array(trainClasses)) 71 errorCount = 0 72 for docIndex in testSet: 73 wordVector = bagOfWords2VecMN(vocabList, docList[docIndex]) 74 if classifyNB(numpy.array(wordVector),p0V,p1V,pSpam) != classList[docIndex]: 75 errorCount += 1 76 print "錯誤分類:\n",docList[docIndex] 77 print '錯誤率: \n',float(errorCount)/len(testSet)
打印結果大致如下(測試集為隨機選取,故每次執行可能不同):

小結
1. 文檔解析在具體的文檔分類項目中占有很大比重。完美的文檔解析可通過正則表達式達到。
2. 在項目實現時,盡可能使用Python的一些工具(如正則表達式),或者第三方庫(如numpy),能很大程度提高開發效率。
