1. 程式人生 > >樸素貝葉斯實戰篇之新浪新聞分類

樸素貝葉斯實戰篇之新浪新聞分類

Python版本: Python3.x              作者:崔家華

執行平臺: Windows                    編輯:黃俊嘉

IDE: Sublime text3

一、前言

上篇文章Python3《機器學習實戰》學習筆記(四):樸素貝葉斯基礎篇之言論過濾器講解了樸素貝葉斯的基礎知識。本篇文章將在此基礎上進行擴充套件,你將看到以下內容:

1.拉普拉斯平滑

2.垃圾郵件過濾(Python3)

3.新浪新聞分類(sklearn)

二、樸素貝葉斯改進之拉普拉斯平滑

上篇文章提到過,演算法存在一定的問題,需要進行改進。那麼需要改進的地方在哪裡呢?利用貝葉斯分類器對文件進行分類時,要計算多個概率的乘積以獲得文件屬於某個類別的概率,即計算  。如果其中有一個概率值為0,那麼最後的成績也為0。我們拿出上一篇文章的截圖。

0?wx_fmt=png

從上圖可以看出,在計算的時候已經出現了概率為0的情況。如果新例項文字,包含這種概率為0的分詞,那麼最終的文字屬於某個類別的概率也就是0了。顯然,這樣是不合理的,為了降低這種影響,可以將所有詞的出現數初始化為1,並將分母初始化為2。這種做法就叫做拉普拉斯平滑(Laplace Smoothing)又被稱為加1平滑,是比較常用的平滑方法,它就是為了解決0概率問題。

除此之外,另外一個遇到的問題就是下溢位,這是由於太多很小的數相乘造成的。學過數學的人都知道,兩個小數相乘,越乘越小,這樣就造成了下溢位。在程式中,在相應小數位置進行四捨五入,計算結果可能就變成0了。為了解決這個問題,對乘積結果取自然對數。通過求對數可以避免下溢位或者浮點數舍入導致的錯誤。同時,採用自然對數進行處理不會有任何損失。下圖給出函式f(x)和ln(f(x))的曲線。

0?wx_fmt=png

檢查這兩條曲線,就會發現它們在相同區域內同時增加或者減少,並且在相同點上取到極值。它們的取值雖然不同,但不影響最終結果。因此我們可以對上篇文章的trainNB0(trainMatrix, trainCategory)函式進行更改,修改如下:

0?wx_fmt=png0?wx_fmt=png

執行程式碼,就可以得到如下結果:

0?wx_fmt=png

瞧,這樣我們得到的結果就沒有問題了,不存在0概率。當然除此之外,我們還需要對程式碼進行修改classifyNB(vec2Classify, p0Vec, p1Vec, pClass1)函式,修改如下:

0?wx_fmt=png

為啥這麼改?因為取自然對數了。

這樣,我們的樸素貝葉斯分類器就改進完畢了。

三、樸素貝葉斯之過濾垃圾郵件

在上篇文章那個簡單的例子中,我們引入了字串列表。使用樸素貝葉斯解決一些現實生活中的問題時,需要先從文字內容得到字串列表,然後生成詞向量。下面這個例子中,我們將瞭解樸素貝葉斯的一個最著名的應用:電子郵件垃圾過濾。首先看一下使用樸素貝葉斯對電子郵件進行分類的步驟:

收集資料:提供文字檔案。

準備資料:將文字檔案解析成詞條向量。

分析資料:檢查詞條確保解析的正確性。

訓練演算法:使用我們之前建立的trainNB0()函式。

測試演算法:使用classifyNB(),並構建一個新的測試函式來計算文件集的錯誤率。

使用演算法:構建一個完整的程式對一組文件進行分類,將錯分的文件輸出到螢幕上。

1、收集資料

資料我已經為大家準備好了,可以在我的Github上下載:

https://github.com/Jack-Cherish/Machine-Learning/tree/master/Naive%20Bayes/email

有兩個資料夾ham和spam,spam檔案下的txt檔案為垃圾郵件。

2、準備資料

對於英文文字,我們可以以非字母、非數字作為符號進行切分,使用split函式即可。編寫程式碼如下:

0?wx_fmt=png0?wx_fmt=png

這樣我們就得到了詞彙表,結果如下圖所示:

0?wx_fmt=png

根據詞彙表,我們就可以將每個文字向量化。我們將資料集分為訓練集和測試集,使用交叉驗證的方式測試樸素貝葉斯分類器的準確性。編寫程式碼如下:

0?wx_fmt=png0?wx_fmt=png0?wx_fmt=png0?wx_fmt=png0?wx_fmt=png0?wx_fmt=png0?wx_fmt=png

執行結果如下:

0?wx_fmt=png

函式spamTest()會輸出在10封隨機選擇的電子郵件上的分類錯誤概率。既然這些電子郵件是隨機選擇的,所以每次的輸出結果可能有些差別。如果發現錯誤的話,函式會輸出錯誤的文件的此表,這樣就可以瞭解到底是哪篇文件發生了錯誤。如果想要更好地估計錯誤率,那麼就應該將上述過程重複多次,比如說10次,然後求平均值。相比之下,將垃圾郵件誤判為正常郵件要比將正常郵件歸為垃圾郵件好。為了避免錯誤,有多種方式可以用來修正分類器,這些內容會在後續文章中進行討論。

這部分程式碼獲取:

https://github.com/Jack-Cherish/Machine-Learning/blob/master/Naive%20Bayes/bayes-modify.py

四、樸素貝葉斯之新浪新聞分類(Sklearn)

1、中文語句切分

考慮一個問題,英文的語句可以通過非字母和非數字進行切分,但是漢語句子呢?就比如我打的這一堆字,該如何進行切分呢?我們自己寫個規則?

幸運地是,這部分的工作不需要我們自己做了,可以直接使用第三方分片語件,即jieba,沒錯就是"結巴"。

jieba已經相容Python2和Python3,使用如下指令直接安裝即可:

0?wx_fmt=png

Python中文分片語件使用簡單:

民間教程:https://www.oschina.net/p/jieba

官方教程:https://github.com/fxsjy/jieba

新聞分類資料集我也已經準備好,可以到我的Github進行下載:https://github.com/Jack-Cherish/Machine-Learning/tree/master/Naive%20Bayes/SogouC

資料集已經做好分類,分資料夾儲存,分類結果如下:

0?wx_fmt=png

資料集已經準備好,接下來,讓我們直接進入正題。切分中文語句,編寫如下程式碼:

0?wx_fmt=png

程式碼執行結果如下所示,可以看到,我們已經順利將每個文字進行切分,並進行了類別標記。

0?wx_fmt=png

2、文字特徵選擇

我們將所有文字分成訓練集和測試集,並對訓練集中的所有單詞進行詞頻統計,並按降序排序。也就是將出現次數多的詞語在前,出現次數少的詞語在後進行排序。編寫程式碼如下:

0?wx_fmt=png0?wx_fmt=png0?wx_fmt=png

all_words_list就是將所有訓練集的切分結果通過詞頻降序排列構成的單詞合集。觀察一下列印結果,不難發現,這裡包含了很多標點符號,很顯然,這些標點符號是不能作為新聞分類的特徵的。總不能說,應為這個文章逗號多,所以它是xx類新聞吧?為了降低這些高頻的符號對分類結果的影響,我們應該怎麼做呢?答曰:拋棄他們! 除了這些,還有"在","了"這樣對新聞分類無關痛癢的詞。並且還有一些數字,數字顯然也不能作為分類新聞的特徵。所以要消除它們對分類結果的影響,我們可以定製一個規則。

0?wx_fmt=png

一個簡單的規則可以這樣制定:首先去掉高頻詞,至於去掉多少個高頻詞,我們可以通過觀察去掉高頻詞個數和最終檢測準確率的關係來確定。除此之外,去除數字,不把數字作為分類特徵。同時,去除一些特定的詞語,比如:"的","一","在","不","當然","怎麼"這類的對新聞分類無影響的介詞、代詞、連詞。怎麼去除這些詞呢?可以使用已經整理好的stopwords_cn.txt文字。下載地址:https://github.com/Jack-Cherish/Machine-Learning/blob/master/Naive%20Bayes/stopwords_cn.txt

這個檔案是這個樣子的:

0?wx_fmt=png

所以我們可以根據這個文件,將這些單詞去除,不作為分類的特徵。我們先去除前100個高頻詞彙,然後編寫程式碼如下:

0?wx_fmt=png0?wx_fmt=png0?wx_fmt=png0?wx_fmt=png

執行結果如下:

0?wx_fmt=png

可以看到,我們已經濾除了那些沒有用的片語,這個feature_words就是我們最終選出的用於新聞分類的特徵。隨後,我們就可以根據feature_words,將文字向量化,然後用於訓練樸素貝葉斯分類器。這個向量化的思想和第三章的思想一致,因此不再累述。

3、使用Sklearn構建樸素貝葉斯分類器

資料已經處理好了,接下來就可以使用sklearn構建樸素貝葉斯分類器了。

官方英文文件地址:

http://scikit-learn.org/dev/modules/generated/sklearn.naive_bayes.MultinomialNB.html

樸素貝葉斯是一類比較簡單的演算法,scikit-learn中樸素貝葉斯類庫的使用也比較簡單。相對於決策樹,KNN之類的演算法,樸素貝葉斯需要關注的引數是比較少的,這樣也比較容易掌握。在scikit-learn中,一共有3個樸素貝葉斯的分類演算法類。分別是GaussianNB,MultinomialNB和BernoulliNB。其中GaussianNB就是先驗為高斯分佈的樸素貝葉斯,MultinomialNB就是先驗為多項式分佈的樸素貝葉斯,而BernoulliNB就是先驗為伯努利分佈的樸素貝葉斯。上篇文章講解的先驗概率模型就是先驗概率為多項式分佈的樸素貝葉斯。

0?wx_fmt=png

對於新聞分類,屬於多分類問題。我們可以使用MultinamialNB()完成我們的新聞分類問題。另外兩個函式的使用暫且不再進行擴充套件,可以自行學習。MultinomialNB假設特徵的先驗概率為多項式分佈,即如下式:

0?wx_fmt=png

接下來,我們看下MultinamialNB這個函式,只有3個引數:

0?wx_fmt=png

引數說明如下:

1.alpha:浮點型可選引數,預設為1.0,其實就是新增拉普拉斯平滑,即為上述公式中的λ ,如果這個引數設定為0,就是不新增平滑;

2.fit_prior:布林型可選引數,預設為True。布林引數fit_prior表示是否要考慮先驗概率,如果是false,則所有的樣本類別輸出都有相同的類別先驗概率。否則可以自己用第三個引數class_prior輸入先驗概率,或者不輸入第三個引數class_prior讓MultinomialNB自己從訓練集樣本來計算先驗概率,此時的先驗概率為P(Y=Ck)=mk/m。其中m為訓練集樣本總數量,mk為輸出為第k類別的訓練集樣本數。

3.class_prior:可選引數,預設為None。

總結如下:

0?wx_fmt=png

除此之外,MultinamialNB也有一些方法供我們使用:

0?wx_fmt=png

MultinomialNB一個重要的功能是有partial_fit方法,這個方法的一般用在如果訓練集資料量非常大,一次不能全部載入記憶體的時候。這時我們可以把訓練集分成若干等分,重複呼叫partial_fit來一步步的學習訓練集,非常方便。GaussianNB和BernoulliNB也有類似的功能。 在使用MultinomialNB的fit方法或者partial_fit方法擬合數據後,我們可以進行預測。此時預測有三種方法,包括predict,predict_log_proba和predict_proba。predict方法就是我們最常用的預測方法,直接給出測試集的預測類別輸出。predict_proba則不同,它會給出測試集樣本在各個類別上預測的概率。容易理解,predict_proba預測出的各個類別概率裡的最大值對應的類別,也就是predict方法得到類別。predict_log_proba和predict_proba類似,它會給出測試集樣本在各個類別上預測的概率的一個對數轉化。轉化後predict_log_proba預測出的各個類別對數概率裡的最大值對應的類別,也就是predict方法得到類別。具體細節不再講解,可參照官網手冊。

瞭解了這些,我們就可以編寫程式碼,通過觀察取不同的去掉前deleteN個高頻詞的個數與最終檢測準確率的關係,確定deleteN的取值:

0?wx_fmt=png0?wx_fmt=png0?wx_fmt=png0?wx_fmt=png0?wx_fmt=png0?wx_fmt=png0?wx_fmt=png

執行結果如下:

0?wx_fmt=png

我們繪製出了deleteNs和test_accuracy的關係,這樣我們就可以大致確定去掉前多少的高頻詞彙了。每次執行程式,繪製的圖形可能不盡相同,我們可以通過多次測試,來決定這個deleteN的取值,然後確定這個引數,這樣就可以順利構建出用於新聞分類的樸素貝葉斯分類器了。我測試感覺450還不錯,最差的分類準確率也可以達到百分之50以上。將if __name__ == '__main__'下的程式碼修改如下:

0?wx_fmt=png

執行結果:

0?wx_fmt=png

五、總結

1.在訓練樸素貝葉斯分類器之前,要處理好訓練集,文字的清洗還是有很多需要學習的東西。

2.根據提取的分類特徵將文字向量化,然後訓練樸素貝葉斯分類器。

3.去高頻詞彙數量的不同,對結果也是有影響的的。

4.拉普拉斯平滑對於改善樸素貝葉斯分類器的分類效果有著積極的作用。

5.如有問題,請留言。如有錯誤,還望指正,謝謝!

本文出現的所有程式碼和資料集,均可在我的github上下載,歡迎Follow、Star:

https://github.com/Jack-Cherish/Machine-Learning