實戰(zhàn):垃圾短信分類器

上次我們講到樸素貝葉斯分類,忘記的同學參考一文搞懂樸素貝葉斯分類,今天就通過樸素貝葉斯分來來實現(xiàn)一個簡單的垃圾短信分類器。

數(shù)據(jù)預(yù)處理

實現(xiàn)這個分類器我們使用的數(shù)據(jù)集來自倫敦大學學院的機器學習數(shù)據(jù)集(UCL machine learning),圖中所示為該數(shù)據(jù)集的部分數(shù)據(jù):


一般用 pandas 模塊來處理數(shù)據(jù), 在這里需要用到 pandas 的read_table()方法,原因是文檔集兩列之間用了tab鍵來分割.如果對于read_table()該選擇哪些參數(shù)不明確的話,需要先閱讀 pandas 的官方 API 文檔,這里就不詳細介紹了,很容易就搜索到。提一點,read_table()默認分隔符就是以 tab 鍵來分割數(shù)據(jù)。

import pandas as pd
#讀取數(shù)據(jù)
df = pd.read_table("yourfilepath", names=['label', 'sms_message'])
df.head()

通過上述代碼可以讀取數(shù)據(jù)并把前 5 行數(shù)據(jù)打印出來:


接下來,進行數(shù)據(jù)預(yù)處理,將 ham 和 spam 分別用 1 和 0 表示,為什么這么做呢?原因是機器只能識別機器語言,不能識別自然語言,如果把 label 用 string 類型來表示的話,scikit-learn 最后會自動轉(zhuǎn)換成不識別的 float 型的值。
如果 label 按照 string 類型來表示,模型也照樣有能力預(yù)測,不過通常在計算性能指標的時候會出現(xiàn)問題,比如召回率,精度等。代碼如下:

# 做一個 map 表,0 表示‘ham',1 表示’spam‘
df['label'] = df.label.map({'ham':0, 'spam':1})
df.head()

詞袋方法

在處理文本信息中,選用的是詞袋方法,詞袋方法的思路就是每當輸入一個文檔片段,就會來計算這個片段中單詞出現(xiàn)的次數(shù)。就像將詞語放入一個袋子中,然后統(tǒng)計看看袋子中不同的詞語出現(xiàn)的次數(shù),而不會考慮單詞出現(xiàn)的順序,在文本處理中會把單詞出現(xiàn)次數(shù)當做訓練分類器的特征。
比如說:現(xiàn)在有 4 句話:

1.Hello, how are you!!
2.Country road, takes me home..
3.Shall we have a dinner tonight?
4.Hi, how is going your homework?

我們用詞袋模型將上面四句話轉(zhuǎn)成一個頻率分布的矩陣,一句話代表一行,列表示每個單詞。如下圖所示:


要實現(xiàn)這個 有兩種辦法,第一種自己實現(xiàn)一個詞袋模型,第二中是用 sklearn 的 CountVectorizer()方法,該方法會將文檔集標記化(將每個文檔片段分割成單個單詞)并為每個標記提供一個整數(shù) ID,同時會記錄每個標記出現(xiàn)的次數(shù)。

自己造輪子

import string
import pprint
from collections import Counter
from sklearn.feature_extraction.text import CountVectorizer
documents = ['Hello, how are you!',
                'Country road, takes me home.',
                'Shall we have a dinner tonight?',
                'Hi, how is going your homework?']
frequency_list = []
for i in documents:
    # 轉(zhuǎn)換小寫,移除標點
    remove_punc = i.lower().translate(str.maketrans('','', string.punctuation))
    # 將句子分割成單個單詞
    result = remove_punc.split(' ')
    # 計算每句中每個單詞的頻率
    word = Counter(result)
    frequency_list.append(word)
pprint.pprint(frequency_list)

count_vector = CountVectorizer()
count_vector.fit()

上述代碼中有主要做了 3 件事:

  1. 將所有的單詞都轉(zhuǎn)換為小寫,為了便于統(tǒng)計
  2. 去除每一句中的標點符號,只需要關(guān)心單詞
  3. 計算單詞出現(xiàn)的次數(shù)

運行結(jié)果如圖:


用 sklearn 庫解決

先放 code:

from sklearn.feature_extraction.text import CountVectorizer
count_vector = CountVectorizer()
# 訓練文檔集
count_vector.fit(documents)
# 獲取文檔集中的不重復(fù)的單詞
count_vector.get_feature_names()
# 將文檔集向量化
doc_array = count_vector.transform(documents).toarray()
frequency_matrix = pd.DataFrame(doc_array,                      columns=count_vector.get_feature_names())

上面代碼提到的 CountVectorizer() 方法有幾個參數(shù)需要注意。

  • lowercase會將所有單詞轉(zhuǎn)化為小寫,默認為True
  • token_pattern可以去掉所有標點符號,默認值為正則表達式:‘(?u)\b\w\w+\b’
  • stop_words參數(shù)默認為‘None’,如果設(shè)置成‘english’,那么會按照sklearn定義的英語停用側(cè)列表來匹配我們數(shù)據(jù)集中的所有單詞,考慮到我們的數(shù)據(jù)集比較小,因此保留默認值即可。
    fit()方法用來訓練文檔集,get_feature_names()用來將文檔集歸類,也就是將不重復(fù)的單詞都統(tǒng)計下來。transform()方法會將文檔集轉(zhuǎn)成矩陣的形式,矩陣的行是文檔集的單詞,列表示單詞的次數(shù)。toarray()將結(jié)果轉(zhuǎn)成數(shù)組的形式。最后把得到的結(jié)果用 pandas 的 DataFrame 的形式顯示出來。
    這樣就成功用sklearn 實現(xiàn)了詞袋模型。最終得到剛開始的圖片效果:
    [圖片上傳失敗...(image-247b2e-1531659038717)]

訓練模型并測試

訓練和測試模型直接使用 sklearn 提供的 api 即可。先分割訓練和測試集:

from sklearn.model_selection import train_test_split

X_train, X_test, y_train, y_test = train_test_split(df['sms_message'], 
                                                    df['label'], 
                                                    random_state=1)

print('Number of rows in the total set: {}'.format(df.shape[0]))
print('Number of rows in the training set: {}'.format(X_train.shape[0]))
print('Number of rows in the test set: {}'.format(X_test.shape[0]))

使用 sklearn 實現(xiàn)樸素貝葉斯定理

分割完訓練集和數(shù)據(jù)集以后,我們利用 sklearn 中的貝葉斯分類器來訓練模型并進行預(yù)測。

from sklearn.naive_bayes import MultinomialNB
# 初始化實例
count_vector = CountVectorizer()
# 訓練數(shù)據(jù)轉(zhuǎn)成矩陣
training_data = count_vector.fit_transform(X_train)
# 轉(zhuǎn)化測試集為矩陣
testing_data = count_vector.transform(X_test)
naive_bayes = MultinomialNB()
naive_bayes.fit(training_data, y_train)
predictions = naive_bayes.predict(testing_data)

需要注意的是,上述代碼使用的MultinomialNB()只是適應(yīng)于離散數(shù)據(jù)(就像我們例子中文本分類是根據(jù)單詞計數(shù)實現(xiàn)的)。如果是連續(xù)數(shù)據(jù),則需要用其他方法,例如高斯樸素貝葉斯方法,不過數(shù)據(jù)也需要假設(shè)符合高斯分布才可以。

評估模型

評估模型主要用到四種指標:準確率(Accuracy)、精度(Precision)、召回率(Recall)和 F1 分數(shù)。后面有專門的文章來詳細介紹評估指標。
上文已經(jīng)預(yù)測數(shù)據(jù),現(xiàn)在根據(jù)評估指標來看一下模型是否滿足預(yù)測需求。將上面四種指標分別打印出來:

from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score
print('Accuracy score: ', format(accuracy_score(y_test, predictions)))
print('Precision score: ', format(precision_score(y_test, predictions)))
print('Recall score: ', format(recall_score(y_test, predictions)))
print('F1 score: ', format(f1_score(y_test, predictions)))

結(jié)果如下:


打印結(jié)果

最后 可以將模型預(yù)測的測試集的結(jié)果打印出來。

category_map = {0:'ham', 1:'spam'}
for message, category, real in zip(X_test[50:100], predictions[50:100], y_test[50:100]):
    print('\n recevie message:', message, '\n prediction:', category_map[category], 'true value:', category_map[real])

限于篇幅原因只顯示部分結(jié)果:


至此,垃圾短信分類已經(jīng)完成。可能有人會疑問,如果新來數(shù)據(jù)該如何處理呢?上文中的
predictions = naive_bayes.predict(testing_data)這行代碼是用來預(yù)測數(shù)據(jù)結(jié)果,當新來一個文檔集(本例中是短信)時,只需要將文檔集轉(zhuǎn)成詞袋模型,例如前面在自己造輪子環(huán)節(jié)中的例句,我們將變量doc_array代入,即可得到結(jié)果為[0,0,0,0], 表示這 4 個文檔集均為非垃圾郵件。

PS: 在本文中使用的是 python3, 以及截圖在 jupyter notebook 下面運行的,這樣便于數(shù)據(jù)分析。完整的代碼已提交 github, 點擊獲取源碼

原創(chuàng)文章,歡迎轉(zhuǎn)載,轉(zhuǎn)載請聲明作者和出處,謝謝!

歡迎關(guān)注機器學習club ,在這里我將持續(xù)輸出機器學習原創(chuàng)文章,盡量用樸實的語言帶你領(lǐng)略技術(shù)的美妙。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,825評論 6 546
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,814評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,980評論 0 384
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 64,064評論 1 319
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,779評論 6 414
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 56,109評論 1 330
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,099評論 3 450
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 43,287評論 0 291
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,799評論 1 338
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 41,515評論 3 361
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,750評論 1 375
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,221評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,933評論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,327評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,667評論 1 296
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,492評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,703評論 2 380

推薦閱讀更多精彩內(nèi)容