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

上次我們講到樸素貝葉斯分類,忘記的同學(xué)參考一文搞懂樸素貝葉斯分類逮光,今天就通過(guò)樸素貝葉斯分來(lái)來(lái)實(shí)現(xiàn)一個(gè)簡(jiǎn)單的垃圾短信分類器卒落。

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

實(shí)現(xiàn)這個(gè)分類器我們使用的數(shù)據(jù)集來(lái)自倫敦大學(xué)學(xué)院的機(jī)器學(xué)習(xí)數(shù)據(jù)集(UCL machine learning)话瞧,圖中所示為該數(shù)據(jù)集的部分?jǐn)?shù)據(jù):


一般用 pandas 模塊來(lái)處理數(shù)據(jù), 在這里需要用到 pandas 的read_table()方法嫩与,原因是文檔集兩列之間用了tab鍵來(lái)分割.如果對(duì)于read_table()該選擇哪些參數(shù)不明確的話,需要先閱讀 pandas 的官方 API 文檔交排,這里就不詳細(xì)介紹了划滋,很容易就搜索到。提一點(diǎn)埃篓,read_table()默認(rèn)分隔符就是以 tab 鍵來(lái)分割數(shù)據(jù)处坪。

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

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


接下來(lái),進(jìn)行數(shù)據(jù)預(yù)處理架专,將 ham 和 spam 分別用 1 和 0 表示同窘,為什么這么做呢?原因是機(jī)器只能識(shí)別機(jī)器語(yǔ)言胶征,不能識(shí)別自然語(yǔ)言塞椎,如果把 label 用 string 類型來(lái)表示的話,scikit-learn 最后會(huì)自動(dòng)轉(zhuǎn)換成不識(shí)別的 float 型的值睛低。
如果 label 按照 string 類型來(lái)表示,模型也照樣有能力預(yù)測(cè),不過(guò)通常在計(jì)算性能指標(biāo)的時(shí)候會(huì)出現(xiàn)問(wèn)題钱雷,比如召回率骂铁,精度等。代碼如下:

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

詞袋方法

在處理文本信息中,選用的是詞袋方法套蒂,詞袋方法的思路就是每當(dāng)輸入一個(gè)文檔片段钞支,就會(huì)來(lái)計(jì)算這個(gè)片段中單詞出現(xiàn)的次數(shù)。就像將詞語(yǔ)放入一個(gè)袋子中操刀,然后統(tǒng)計(jì)看看袋子中不同的詞語(yǔ)出現(xiàn)的次數(shù)烁挟,而不會(huì)考慮單詞出現(xiàn)的順序,在文本處理中會(huì)把單詞出現(xiàn)次數(shù)當(dāng)做訓(xùn)練分類器的特征骨坑。
比如說(shuō):現(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)成一個(gè)頻率分布的矩陣撼嗓,一句話代表一行,列表示每個(gè)單詞欢唾。如下圖所示:


要實(shí)現(xiàn)這個(gè) 有兩種辦法且警,第一種自己實(shí)現(xiàn)一個(gè)詞袋模型,第二中是用 sklearn 的 CountVectorizer()方法礁遣,該方法會(huì)將文檔集標(biāo)記化(將每個(gè)文檔片段分割成單個(gè)單詞)并為每個(gè)標(biāo)記提供一個(gè)整數(shù) ID斑芜,同時(shí)會(huì)記錄每個(gè)標(biāo)記出現(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)換小寫(xiě)祟霍,移除標(biāo)點(diǎn)
    remove_punc = i.lower().translate(str.maketrans('','', string.punctuation))
    # 將句子分割成單個(gè)單詞
    result = remove_punc.split(' ')
    # 計(jì)算每句中每個(gè)單詞的頻率
    word = Counter(result)
    frequency_list.append(word)
pprint.pprint(frequency_list)

count_vector = CountVectorizer()
count_vector.fit()

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

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

運(yùn)行結(jié)果如圖:


用 sklearn 庫(kù)解決

先放 code:

from sklearn.feature_extraction.text import CountVectorizer
count_vector = CountVectorizer()
# 訓(xùn)練文檔集
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() 方法有幾個(gè)參數(shù)需要注意浅碾。

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

訓(xùn)練模型并測(cè)試

訓(xùn)練和測(cè)試模型直接使用 sklearn 提供的 api 即可。先分割訓(xùn)練和測(cè)試集:

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 實(shí)現(xiàn)樸素貝葉斯定理

分割完訓(xùn)練集和數(shù)據(jù)集以后伍伤,我們利用 sklearn 中的貝葉斯分類器來(lái)訓(xùn)練模型并進(jìn)行預(yù)測(cè)。

from sklearn.naive_bayes import MultinomialNB
# 初始化實(shí)例
count_vector = CountVectorizer()
# 訓(xùn)練數(shù)據(jù)轉(zhuǎn)成矩陣
training_data = count_vector.fit_transform(X_train)
# 轉(zhuǎn)化測(cè)試集為矩陣
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ù)單詞計(jì)數(shù)實(shí)現(xiàn)的)百侧。如果是連續(xù)數(shù)據(jù),則需要用其他方法辛润,例如高斯樸素貝葉斯方法,不過(guò)數(shù)據(jù)也需要假設(shè)符合高斯分布才可以。

評(píng)估模型

評(píng)估模型主要用到四種指標(biāo):準(zhǔn)確率(Accuracy)置济、精度(Precision)挟纱、召回率(Recall)和 F1 分?jǐn)?shù)檀轨。后面有專門的文章來(lái)詳細(xì)介紹評(píng)估指標(biāo)撤师。
上文已經(jīng)預(yù)測(cè)數(shù)據(jù)腺占,現(xiàn)在根據(jù)評(píng)估指標(biāo)來(lái)看一下模型是否滿足預(yù)測(cè)需求。將上面四種指標(biāo)分別打印出來(lái):

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ù)測(cè)的測(cè)試集的結(jié)果打印出來(lái)怎顾。

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)完成。可能有人會(huì)疑問(wèn)淑翼,如果新來(lái)數(shù)據(jù)該如何處理呢肉瓦?上文中的
predictions = naive_bayes.predict(testing_data)這行代碼是用來(lái)預(yù)測(cè)數(shù)據(jù)結(jié)果船殉,當(dāng)新來(lái)一個(gè)文檔集(本例中是短信)時(shí),只需要將文檔集轉(zhuǎn)成詞袋模型钉疫,例如前面在自己造輪子環(huán)節(jié)中的例句城菊,我們將變量doc_array代入赚爵,即可得到結(jié)果為[0,0,0,0], 表示這 4 個(gè)文檔集均為非垃圾郵件。

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

原創(chuàng)文章霎挟,歡迎轉(zhuǎn)載窝剖,轉(zhuǎn)載請(qǐng)聲明作者和出處,謝謝酥夭!

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

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末疙描,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子讶隐,更是在濱河造成了極大的恐慌起胰,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,284評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件巫延,死亡現(xiàn)場(chǎng)離奇詭異效五,居然都是意外死亡地消,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,115評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門畏妖,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)脉执,“玉大人,你說(shuō)我怎么就攤上這事戒劫“胍模” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,614評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵谱仪,是天一觀的道長(zhǎng)玻熙。 經(jīng)常有香客問(wèn)我否彩,道長(zhǎng)疯攒,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,671評(píng)論 1 293
  • 正文 為了忘掉前任列荔,我火速辦了婚禮敬尺,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘贴浙。我一直安慰自己砂吞,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,699評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布崎溃。 她就那樣靜靜地躺著蜻直,像睡著了一般。 火紅的嫁衣襯著肌膚如雪袁串。 梳的紋絲不亂的頭發(fā)上概而,一...
    開(kāi)封第一講書(shū)人閱讀 51,562評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音囱修,去河邊找鬼赎瑰。 笑死,一個(gè)胖子當(dāng)著我的面吹牛破镰,可吹牛的內(nèi)容都是我干的餐曼。 我是一名探鬼主播,決...
    沈念sama閱讀 40,309評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼鲜漩,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼源譬!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起孕似,我...
    開(kāi)封第一講書(shū)人閱讀 39,223評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤踩娘,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后鳞青,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體霸饲,經(jīng)...
    沈念sama閱讀 45,668評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡为朋,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,859評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了厚脉。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片习寸。...
    茶點(diǎn)故事閱讀 39,981評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖傻工,靈堂內(nèi)的尸體忽然破棺而出霞溪,到底是詐尸還是另有隱情,我是刑警寧澤中捆,帶...
    沈念sama閱讀 35,705評(píng)論 5 347
  • 正文 年R本政府宣布鸯匹,位于F島的核電站,受9級(jí)特大地震影響泄伪,放射性物質(zhì)發(fā)生泄漏殴蓬。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,310評(píng)論 3 330
  • 文/蒙蒙 一蟋滴、第九天 我趴在偏房一處隱蔽的房頂上張望染厅。 院中可真熱鬧,春花似錦津函、人聲如沸肖粮。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,904評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)涩馆。三九已至,卻和暖如春允坚,著一層夾襖步出監(jiān)牢的瞬間魂那,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,023評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工屋讶, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留冰寻,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,146評(píng)論 3 370
  • 正文 我出身青樓皿渗,卻偏偏與公主長(zhǎng)得像斩芭,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子乐疆,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,933評(píng)論 2 355

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