1. 程式人生 > >機器學習筆記(2)——使用樸素貝葉斯演算法過濾(中英文)垃圾郵件

機器學習筆記(2)——使用樸素貝葉斯演算法過濾(中英文)垃圾郵件

在上一篇文章《使用樸素貝葉斯演算法對文件分類詳解》中,我們實現了用樸素貝葉斯演算法對簡單文件的分類,今天我們將利用此分類器來過濾垃圾郵件。

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下執行,並添加了詳細的中文註釋。