前言
樸素貝葉斯演算法最為廣泛而經典的應用毫無疑問是文件分類,更具體的情形是郵件過濾系統。
本文詳細地講解一個基於樸素貝葉斯分類演算法的郵件過濾系統的具體實現。
本文側重於工程實現,至於其中很多演算法的細節請參考之前的一篇文章:樸素貝葉斯分類演算法原理分析與程式碼實現。
準備資料:切分文字
獲取到文字檔案之後,首先要做的是兩件事情:
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),能很大程度提高開發效率。