上次我們講到樸素貝葉斯分類,忘記的同學(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 件事:
- 將所有的單詞都轉(zhuǎn)換為小寫(xiě)押搪,為了便于統(tǒng)計(jì)
- 去除每一句中的標(biāo)點(diǎn)符號(hào),只需要關(guān)心單詞
- 計(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ù)的美妙熬北。