文本分析初學者教程:用Python實現垃圾郵件過濾器

編程語言 Python 機器學習 Word 機器之心 2017-04-25

機器之心報道


本文介紹瞭如何通過 Python 和 scikit-learn 實現垃圾郵件過濾的一種方法。對比和分析了兩個分類器的結果:多項式樸素貝葉斯和支持向量機。

文本分析初學者教程:用Python實現垃圾郵件過濾器

文本挖掘(text mining,從文本中導出信息)是一個廣泛的領域,因為不斷產生的巨量文本數據而已經得到了普及。情緒分析、文檔分類、主題分類、文本概括、機器翻譯等許多任務的自動化都已經通過機器學習得到了實現。

垃圾郵件過濾(spam filtering)是文檔分類任務的入門級示例,其涉及了將電子郵件分為垃圾郵件或非垃圾郵件(也稱為 ham)。你的 Gmail 賬戶的垃圾郵箱就是最好的例子。那麼讓我們在公開的郵件語料庫上構建垃圾郵件過濾器吧。我已經從 Ling-spam 語料庫(http://www.aueb.gr/users/ion/data/lingspam_public.tar.gz )上提取了同等數量的垃圾郵件和非垃圾郵件。我們從這裡下載將要對其操作的提取出來的子集:https://www.dropbox.com/s/yjiplngoa430rid/

我們將通過以下步驟構建此應用程序:

  1. 準備文本數據

  2. 創建詞典

  3. 特徵提取過程

  4. 訓練分類器

此外,我們將在該子集中的測試集上測試我們的結果。


1、 準備文本數據

這裡使用的數據集被分為訓練集和測試集,分別包含了 702 封郵件和 260 封郵件,其中垃圾郵件和 ham 郵件的數量相等。垃圾郵件的文件名中包含了 *spmsg*,所以很容易識別。

在任何一個文本挖掘問題中,文本清理(text cleaning)是我們從文檔中刪除那些可能對我們想要提取的信息無用的文字的第一步。電子郵件可能包含了大量對垃圾郵件檢測無用的字符,如標點符號、停止詞、數字等。 Ling-spam 語料庫中的郵件已經通過以下方式進行了預處理。

a) 移除停止詞 — 像 “and”、“the”、“of” 之類的停止詞在所有的英語句子當中都非常常見,在判定是否為垃圾郵件時沒有多少作用,所以這些詞已經從電子郵件中刪除。

b) 詞形還原(lemmatization ) — 這是將一個單詞的不同變化形式分組在一起的過程,以便其可被視為單個項進行分析。 例如,“include”、“includes”和“included” 將全部用 “include” 表示。在詞形還原中,句子的語境也會得到保留,而詞幹提取(stemming)則不會。(詞幹提取是文本挖掘中的另一個術語,其不會考慮句意)。

我們還需要從郵件文檔中刪除非文字信息,比如標點符號或者特殊字符。有幾種方法可以做到這一點。這裡,我們將在創建詞典後刪除這樣的詞,這非常方便,因為當你有了一個詞典時你只需要刪除每個這樣的單詞一次。歡呼吧!!到現在為止,你不需要做任何事情。


2、創建詞典

數據集中的示例郵件如下所示:

Subject: postinghi , ' m work phonetics project modern irish ' m hard source . anyone recommend book article english ? ' , specifically interest palatal ( slender ) consonant , work helpful too . thank ! laurel sutton ( sutton @ garnet . berkeley . edu

可以看出,郵件第一行是主題(subject),第三行包含了郵件的正文。我們只會對其內容執行文本分析以檢測垃圾郵件。作為第一步,我們需要創建一個詞及其頻率的詞典。對於此任務,我們使用了 700 封郵件作為訓練集。這個 Python 函數可為你創建這個詞典。

def make_Dictionary(train_dir): emails = [os.path.join(train_dir,f) for f in os.listdir(train_dir)] all_words = [] for mail in emails: with open(mail) as m: for i,line in enumerate(m): if i == 2: #Body of email is only 3rd line of text file words = line.split() all_words += words dictionary = Counter(all_words) # Paste code for non-word removal here(code snippet is given below) return dictionary

.

一旦詞典被創建,我們可以添加下面幾行代碼到上面的函數以刪除移除非詞(如第 1 步所示)。我也刪除了詞典中不合理的單個字符,這些字符在這裡是不相關的。別忘了在函數def make_Dictionary(train_dir)中插入以下代碼。

list_to_remove = dictionary.keys()for item in list_to_remove: if item.isalpha() == False: del dictionary[item] elif len(item) == 1: del dictionary[item]dictionary = dictionary.most_common(3000)

可以通過命令print dictionary顯示詞典。你也許會發現一些不合理的單詞數很多,但是別擔心,這只是一個詞典並且稍後你可以改進它。如果你是按照這篇文章說的那樣操作的並且使用了我提供的數據集,那麼請確保你的詞典中包含以下最常用的單詞的條目。這裡,我已經選擇了 3000 個詞典中最常用的詞。

[('order', 1414), ('address', 1293), ('report', 1216), ('mail', 1127), ('send', 1079), ('language', 1072), ('email', 1051), ('program', 1001), ('our', 987), ('list', 935), ('one', 917), ('name', 878), ('receive', 826), ('money', 788), ('free', 762)

3、特徵提取過程

一旦詞典準備就緒,我們可以為訓練集的每封郵件提取 3000 維的詞計數向量(word count vector,這裡是我們的特徵)。每個詞計數向量包含了訓練文件中的 3000 個單詞的頻率。當然,你現在可能已經猜到了它們大部分是 0。讓我們舉個例子。假設我們的詞典中有 500 個詞。每個詞計數向量包含訓練文件中 500 個字典詞的頻率。 假設訓練文件中的文本是“Get the work done, work done”,那麼它將被編碼為[0,0,0,0,0,... .0,0,2,0,0,0,......, 0,0,1,0,0,... 0,0,1,0,0,... 2,0,0,0,0,0]。 在這個 500 長度的詞計數向量中,只有的第 296、359、415、495 位有詞的計數值,其餘位置都是零。

下面的 Python 代碼將生成一個特徵向量矩陣,其中行表示訓練集的 700 個文件,列表示詞典的 3000 個詞。索引“ij”處的值將是第 i 個文件中詞典的第 j 個詞的出現次數。

def extract_features(mail_dir): files = [os.path.join(mail_dir,fi) for fi in os.listdir(mail_dir)] features_matrix = np.zeros((len(files),3000)) docID = 0; for fil in files: with open(fil) as fi: for i,line in enumerate(fi): if i == 2: words = line.split() for word in words: wordID = 0 for i,d in enumerate(dictionary): if d[0] == word: wordID = i features_matrix[docID,wordID] = words.count(word) docID = docID + 1 return features_matrix

4、訓練分類器

這裡,我將使用 scikit-learn 機器學習庫(http://scikit-learn.org/stable/ )訓練分類器。這是一個開源 Python 機器學習庫,其被捆綁在第三方分佈 anaconda(https://www.continuum.io/downloads )中,也可以按這個教程單獨安裝使用:http://scikit-learn.org/stable/install.html 。一旦安裝,我們只需要將其導入到我們的程序中即可。

我已經訓練了兩個模型,即樸素貝葉斯分類器(Naive Bayes classifier)和支持向量機(SVM)。對於文檔分類問題,樸素貝葉斯分類器是一種常規的並且非常流行的方法。它是一個基於貝葉斯定理的監督概率分類器,其假設每對特徵之間是獨立的。支持向量機是監督式的二元分類器,在你擁有更多的特徵時它非常有效。支持向量機(SVM)的目標是將訓練數據中的一些子集從被稱為支持向量(support vector,分離超平面的邊界)的剩餘部分分離。預測測試數據類型的支持向量機模型的決策函數基於支持向量並且利用了核技巧( kernel trick)。

一旦分類器訓練完畢,我們可以在測試集上檢查模型的表現。我們提取了測試集中的每一封郵件的詞計數向量,並使用訓練後的樸素貝葉斯(NB)分類器和支持向量機模型預測其類別(ham 郵件或垃圾郵件)。以下是垃圾郵件過濾應用程序的完全代碼。你必須包含我們在之前的步驟 2 和步驟 3 定義的兩個函數。

import osimport numpy as npfrom collections import Counterfrom sklearn.naive_bayes import MultinomialNB, GaussianNB, BernoulliNBfrom sklearn.svm import SVC, NuSVC, LinearSVC# Create a dictionary of words with its frequencytrain_dir = 'train-mails'dictionary = make_Dictionary(train_dir)# Prepare feature vectors per training mail and its labelstrain_labels = np.zeros(702)train_labels[351:701] = 1train_matrix = extract_features(train_dir)# Training SVM and Naive bayes classifiermodel1 = MultinomialNB()model2 = LinearSVC()model1.fit(train_matrix,train_labels)model2.fit(train_matrix,train_labels)# Test the unseen mails for Spamtest_dir = 'test-mails'test_matrix = extract_features(test_dir)test_labels = np.zeros(260)test_labels[130:260] = 1result1 = model1.predict(test_matrix)result2 = model2.predict(test_matrix)print confusion_matrix(test_labels,result1)print confusion_matrix(test_labels,result2)

測試表現水平

測試集包含 130 封垃圾郵件 和 130 封非垃圾郵件。如果你已經走到了這一步,你將會發現以下結果。我已經展示出了對這兩個模型的訓練集的混淆矩陣(confusion matrix )。對角元素表示正確識別(也叫真識別)的郵件,其中非對角元素表示郵件的錯誤分類(假識別)。

多項式樸素貝葉斯HamSpam
Ham1291
Spam9121
支持向量機(線性)HamSpam
Ham1264
Spam6124


除了 SVM 具有稍微平衡的假識別之外,這兩個模型在測試集上具有相似的表現。我必須提醒你,測試數據既沒有在創建詞典使用,也沒有用在訓練集中。


你的任務

下載 Euron-spam 語料庫(http://www.aueb.gr/users/ion/data/enron-spam/ )的預處理表格。該語料庫在 6 個目錄中包含了 33716 封郵件,其中包含 “ham” 和“spam” 文件夾。非垃圾郵件和垃圾郵件的總數分別為 16545 和 17171。

遵循本文章中描述的相同步驟,並檢查它如何執行支持向量機和多項式樸素貝葉斯模型。由於該語料庫的目錄結構不同於博客文章中使用的 ling-spam 子集的目錄結構,因此你可能需要重新對其組織或在def make_Dictionary(dir)def extract_features(dir)函數中進行修改。

我將 Euron-spam 語料庫以 60:40 的比例分成訓練集和測試集。執行本博客的相同步驟後,我在 13487 封測試集郵件中得到以下結果。我們可以看到,在正確檢測垃圾電子郵件方面的表現,支持向量機(SVM)略優於樸素貝葉斯分類器。

多項式樸素貝葉斯HamSpam
Ham6445225
Spam1376680
支持向量機(線性)HamSpam
Ham6490180
Spam1096708

最後的感想

我試圖保持教程的簡潔性。希望對文本分析感興趣的初學者可以從這個應用程序開始。

文本分析初學者教程:用Python實現垃圾郵件過濾器

你可能會思考樸素貝葉斯和支持向量機(SVM)背後的數學技術。 支持向量機(SVM)在數學上是較為複雜的模型,但樸素貝葉斯相對容易理解。我鼓勵你從在線資源中學習這些模型。除此之外,你可以進行很多實驗以便發現各種參數的效果,比如

  • 訓練數據的數量

  • 詞典的大小

  • 不同的機器學習技術,比如 GaussianNB、BernoulliNB、SVC)

  • 對支持向量機模型參數進行調優

  • 通過消除不重要的詞(可以手動)以改進詞典

  • 一些其它的特徵(可搜索 td-idf 瞭解)

你可以從 GitHub 獲得這兩種語料庫的完整 Python 實現:https://github.com/abhijeet3922/Mail-Spam-Filtering

相關推薦

推薦中...