機器學習筆記(2)——使用樸素貝葉斯演算法過濾(中英文)垃圾郵件
阿新 • • 發佈:2019-02-06
在上一篇文章《使用樸素貝葉斯演算法對文件分類詳解》中,我們實現了用樸素貝葉斯演算法對簡單文件的分類,今天我們將利用此分類器來過濾垃圾郵件。
1. 準備資料——文字切分
之前演算法中輸入的文件格式為單詞向量,例如['my', 'dog', 'has', 'flea', 'problems', 'help', 'please'],而實際情況中通常要處理的是文字(例如郵件),那麼就要先將文字轉換為詞向量,在bayes.py中加入程式碼:
# 檔案解析為向量 def textParse(bigString): import re listOfTokens = re.split(r'\W*', bigString) return [tok.lower() for tok in listOfTokens if len(tok) > 2]
上面3行程式碼包含了很多內容,它的輸入引數是字串,返回字串列表。
- 因為要使用正則表示式切分文字,首先引用了re模組。
- 呼叫re.split方法切分文字,正則表示式‘\W*’代表以單詞和數字外的任意長度字元作為分隔符,前面的r表示原生字元,用於宣告\不作為轉義字元而是與W一起作為正則表示式。
- listOfTokens是被切分後的詞列表, 但其中包含了空字串,解析文字中的url產生的無含義的短字串等。程式碼最後一行負責生成並返回一個列表, 列表的內容為listOfTokens中長度大於2的單詞,並統一轉為小寫。
2. 測試演算法——過濾垃圾郵件
現在有50封電子郵件,垃圾郵件和正常郵件各25個,隨機選取10個作為測試集,剩餘的40個作為訓練集。測試過程可以分解為3部分:
- 讀取文字檔案,並將其轉換為詞向量列表
- 構建訓練集和測試集,利用訓練集構建分類器
- 對測試集樣本分類,輸出分類錯誤的文字和錯誤率
# 垃圾郵件測試函式 def spamTest(): docList = [] classList = [] fileList = [] for i in range(1, 26): wordList = textParse( open(r'email\spam\%d.txt' % i).read()) # 將垃圾郵件內容轉換為詞向量 docList.append(wordList) # 新增到郵件列表 classList.append(1) # 新增到類別列表 fileList.append(r'email\spam\%d.txt' % i) wordList = textParse( open(r'email\ham\%d.txt' % i).read()) # 將正常郵件內容轉換為詞向量 docList.append(wordList) classList.append(0) fileList.append(r'email\ham\%d.txt' % i) vocabList = createVocabList(docList) # 生成詞條列表 trainingSet = list(range(50)) # 建立一個長度為50的訓練集索引列表 testSet = [] for i in range(10): # 隨機選擇10個作為測試樣本,並將其從訓練集中刪除 randomIndex = int(random.uniform(0, len(trainingSet))) testSet.append(trainingSet[randomIndex]) del (trainingSet[randomIndex]) trainMat = [] trainClasses = [] for docIndex in trainingSet: # 生成訓練樣本的矩陣 trainMat.append(bagOfWords2Vec(vocabList, docList[docIndex])) trainClasses.append(classList[docIndex]) p0v, p1v, pSpam = trainNB0(array(trainMat), array(trainClasses)) # 構建分類器 errorCount = 0 for docIndex in testSet: # 對測試集分類 wordVector = bagOfWords2Vec(vocabList, docList[docIndex]) if classifyNB(array(wordVector), p0v, p1v, pSpam) != classList[ docIndex]: # 如果分類錯誤,列印錯誤資訊 errorCount += 1 print('classification error', fileList[docIndex]) print('the error rate is: ', float(errorCount) / len(testSet)) # 輸出錯誤率
下面測試一下分類的效果,因為測試集是隨機選取的,每次測試的結果可能不同,可以多次執行測試函式求平均錯誤率,以此來評估演算法。我運行了10次,平均錯誤率是4%。
3. 中文郵件怎麼處理
上面處理的郵件是全英文的,可是中文郵件怎麼辦呢?如果按上述方法按標點符號來切分的話,中文的一整句話會被當做一個詞條,這顯然不行,好在Python有個強大的中文處理模組 jieba(結巴),它不僅能對中文文字切詞,如果碰到英文單詞,也會以英文的預設形式切分。
# 檔案解析函式,可處理中文和英文
def textParse1(bigString):
import re
import jieba
listOfTokens = jieba.lcut(bigString) # 使用jieba切分文字
newList = [re.sub(r'\W*', '', s) for s in listOfTokens] # 去掉標點符號
return [tok.lower() for tok in newList if len(tok) > 0] # 刪除長度為0的空值
這裡需要注意的是,如果郵件內容包含中文,是否刪除短字串就要謹慎了,因為中文的一兩個字對於分類也是有意義的。我們來試一下效果:
不錯,現在對中文郵件也可以輕鬆過濾了。
說明:文中的測試樣本和部分程式碼來自《機器學習實戰》,但書中的程式碼適合Python 2.7版本, 我將程式碼做了調整使其能夠在Python 3.6下執行,並添加了詳細的中文註釋。