當前位置:
首頁 > 最新 > 基於多項式貝葉斯的增量學習的文本分類

基於多項式貝葉斯的增量學習的文本分類

宋天龍(TonySong)Webtrekk中國區技術和諮詢負責人(Webtrekk,德國最大的網站數據分析服務提供商), 數據常青藤和數據研究與商業應用博主,資深數據分析領域專家。

著有《Python數據分析與數據化運營》、《網站數據挖掘與分析:系統方法與商業實踐》 、《企業大數據系統構建實戰:技術、架構、實施與應用》

Python數據分析與數據化運營 已經正式上線,點擊閱讀全文可以購買!


1 案例背景

文本分類是對內容做類別劃分的常用場景。本案例是從一堆新聞文件中通過對文本內容建立分類模型,通過增量學習的方式實現對未知數據的預測。

案例數據源文件都在可以從《Python數據分析與數據化運營》附件中,共有兩部分,第一部分在 「附件-chapter8」中的news_data.tar.gz壓縮包中,該壓縮包包含10個文件,這些是用來做主題模型的訓練集;另外一個文件在相同目錄下,名為test_sets.txt,該文件是用來做每次增量學習後的測試和檢驗;最後一個是article.txt,用來做分類預測。案例的程序文件chapter8_code2.py也在數據源目錄之下。該附件可以在可從http://www.dataivy.cn/book/python_book.zip或https://pan.baidu.com/s/1kUUBWNX下載。

本案例用到的主要技術包括:

q 數據預處理:字元串全形轉半形、XML文件內容解析、文本轉稀疏矩陣

q 數據建模:基於增量學習的分類器

主要用到的庫包括:re、tarfile、os、numpy、bs4、sklearn,其中sklearn是核心。

本案例的重點技術有3個:

q 使用bs4的BeautifulSoup做XML文件內容解析。

q 文本轉稀疏矩陣,基於sklearn的HashingVectorizer庫實現,而非之前介紹過的分詞技術。

q 使用sklearn中的MultinomialNB(多項式樸素貝葉斯)做分類學習,並基於增量學習的策略實現文本分類訓練和預測。


本案例的數據與上個案例的數據格式相同,具體參照「8.7.3 案例數據」。


步驟1導入庫

本案例主要用到了以下庫:

re:正則表達式庫,用來在文本解析時提取標籤

tarfile:用來解析原始數據壓縮文件

os:用來判斷數據是否存在以及遍歷目錄文件

numpy:基礎數據處理模塊

bs4:這裡用到了其中的BeautifulSoup做XML文件內容解析

HashingVectorizer:文本轉稀疏矩陣

MultinomialNB:貝葉斯分類器

accuracy_score:分類評估指標

步驟2定義功能函數,包括全形轉半形、解析文件內容、交叉檢驗、word to vector、label to vecotr。

全形轉半形

該函數用來將文本內容中的全形字元串轉換為半形字元串,這樣會避免在自然語言處理過程中由於字元不統一而導致的信息混亂問題。其輸入參數如下:

q content:要轉換的字元串內容

函數返回:轉換後的半形字元串。

注意 由於該功能是針對每一個字元做識別轉換的,因此其過程比較慢。

該函數的具體定義如下:

定義一個空字元串對象new_str,用於存儲轉換後的結果。使用for循環讀取每個字元,然後使用ord方法讀取字元的ASCII值或Unicode值賦值給code_num,接下來根據值做判斷:當code_num值等於12288時,這是一個全形空格,此時直接將其值設置為32來轉換成半形空格;當code_num值在[65281,65374]時,將其直接減去65248來轉換為半形字元值。

數值轉換完成後,使用unichr方法將其轉換為字元,並通過直接相加的方法重新組合字元串。

最後返迴轉換後的新字元串。

相關知識點:全形字元串轉半形字元串

全形指的是一個字元佔2個標準字元的位置(例如中國漢字),半形指的是佔1個標準字元的位置(例如普通的字元a)。在文本相關的處理過程中,半形和全形字元通常是數據預處理的必要過程。全形字元包含兩類字元:

一類是特殊字元:空格,它的全形值十進位整數為12288,表示為十六進位結果是0x3000;而其半形十進位整數數值為 32,十六進位結果是0x20。

一類是有規律的字元,這類字元的全形十進位整數的範圍是[65281,65374],用十六進位表示的結果是[0xFF01,0xFF5E];其對應的半形十進位整數值為[33,126],用十六進位表示的結果是[0x21,0x7E]。

除了空格外,有規律的字元在半形和全形之間的差值是65248,因此我們可以直接在全形數值上減去65248即可得到半形數值。

在轉換過程中,我們用到了2個新的函數:ord和unichr,這兩

個是配對使用的函數,前者用來從字元串讀取對應的數值,後者用於將數值轉換為unicode字元串。

注意 並不是所有的全形字元都能被轉換為半形字元,例如漢字是全形字元,佔2個字元的位置,但無法被轉換;只有英文字母、數字鍵、符號鍵等才能可以做全形和半形之間的轉換。

解析文件內容

該函數主要用來從原始文件中解析出文本內容和標籤數據。函數輸入參數:

data:包含代碼的原始內容

函數返回:以列表形式返迴文本中的所有內容和對應標籤

該函數的具體定義如下:

使用BeautifulSoup庫建立處理對象raw_code,這裡指定的解析庫是lxml。

對raw_code使用find_all方法查找所有的doc標籤,將返回的列表結果賦值給doc_code。

分別新建空列表對象content_list和label_list,用來存儲每個content標籤的內容及其對應標籤。

使用for循環讀出每個doc標籤對象each_doc,先判斷其是否為空,如果不為空,則從each_doc中使用find方法查找content標籤,然後使用text屬性獲得標籤內的內容,再調用str_convert函數將其中的全形轉換為半形,最後將結果追加到列表content_list。使用相同的思路從each_doc中提取URL信息,然後使用re正則表達式庫根據/和.做字元串分割,從得到的結果中提取第3個字元(子域名)作為標籤加入到標籤列表。

最後返回內容列表和標籤列表

提示 由於在原始文本中,url的子域名代表該文本所屬的主題分類,因此直接從URL中提取子域名可以做該內容的分類標籤。

相關知識點:使用re.split對內容做分割操作

在Python中,對字元串做分割是常用操作。默認的字元串支持split方法做分割,並且可指定分割符號。例如通過如下操作可以將字元串s以逗號為分隔符分割為6個元素對象:

但是如果要分割的對象中包含多個分割字元規則,那麼可以使用正則表達式庫的split方法做分割。re.split的主要參數如下:

pattern:分割規則

string:要分割的字元串

pattern是正則表達式靈活處理字元的核心,在本案例中,我們使用了最簡單的並列字元的方法來表示,基於/或.都分割。除此以外,正則表達式還支持多種規則模式。

首先是正則表達式對於不同對象的表示方法,例如數字、字母等。如表8-1.

表8-1常用正則表達式的對象表示方法

但是如果要分割的對象中包含多個分割字元規則,那麼可以使用正則表達式庫的split方法做分割。re.split的主要參數如下:

pattern:分割規則

string:要分割的字元串

pattern是正則表達式靈活處理字元的核心,在本案例中,我們使用了最簡單的並列字元的方法來表示,基於/或.都分割。除此以外,正則表達式還支持多種規則模式。

首先是正則表達式對於不同對象的表示方法,例如數字、字母等。如表8-1.

表8-1常用正則表達式的對象表示方法

關於對象的位置

在對某個對象做匹配時,可能對象具有特定的位置屬性,例如字元的開頭、結尾等,此時可以利用其位置屬性做規則匹配,如表8-2.

表8-2常用正則表達式的位置表示方法

關於對象的次數

在匹配過程中,可能我們希望對其出現的次數做限制,例如出現5個數字,3個字母等,此時我們需要次數控制規則,如表8-3.

表8-3常用正則表達式的此時表示方法

關於特殊匹配模式

很多時候,我們會有一些特殊的匹配模式,例如:

匹配多個並列條件,此時使用|來表示

匹配字符集,使用[]表示,[]中可以使用表8-1中的規則來定義字元對象

對字符集的總體取非操作,使用[^]

定義一個字符集區間,例如使用[1-5]定義一個區間

交叉檢驗

本函數通過交叉檢驗計算每次增量學習後的模型得分。輸入參數:

model_object:每次增量學習後的模型對象

data:訓練數據集

label:訓練數據集對應的標籤

函數返回:交叉檢驗得分

函數功能具體實現如下:

直接調用增量學習對象model_object的predict方法對測試數據集data做測試,並對照其真實label做數據檢驗,最後返回每次得分。其中:

使用指定的accuracy_score方法獲得預測標籤與真實標籤的預測準確率

使用round()方法保留結果為4位小數

注意 這裡沒有使用普通的cross_val_score方法做多折交叉檢驗,原因是cross_val_score方法中默認使用函數對象的fit方法做訓練,而不支持增量學習的方法,因此無法對增量學習的效果做驗證。所以,這裡通過「手動」的方法做結果測試。

word to vector

本函數用來將訓練集文本數據轉換為稀疏矩陣。在之前的章節中,我們介紹過使用結巴分詞、sklearn中的TfidfVectorizer、gensim中的TfidfModel建立文本向量模型。這裡我們介紹另外一種方法HashingVectorizer。

HashingVectorizer用來將文本文檔集合轉化為token發生次數的集合(這點跟上個案例中gensim的doc2bow思路很像),其結果是用token發生次數(或者二進位信息)的scipy.sparse稀疏矩陣來表示文本文檔信息。這種方法的優勢在於:

無需在內存中存儲任何字典信息,因此非常適合大數據集的計算

沒有任何對象的「狀態」信息,因此可以很快的執行pickle或un-pickle操作

非常適合在增量學習或者管道方法中應用,因為其在fit期間沒有任何狀態變更

提示 如何理解HashingVectorizer的狀態?在之前的章節中,我們要做預測應用時,一般都需要針對訓練集和預測集分階段執行,原因是很多對象都有固定的轉換或計算模式,該模式在fit期間產生,在訓練或預測時都是用相同的fit對象的計算模式。例如使用PCA做降維,先對其做fit操作形成降維對象,然後分別對訓練集和預測集做降維。而HashingVectorizer則在fit方法下其對象不產生任何狀態的變更,這點決定了我們不需將其對象存儲下來,也不需要分階段針對不同數據集做區分應用。

該函數的實現過程如下:

通過HashingVectorizer(non_negative=True)建立HashingVectorizer對象,指定結果非負。

model_vector.fit_transform(data)將輸入文本轉化為稀疏矩陣,並返回其結果

label to vecotr

本函數用來將文本標籤轉換為向量標籤,例如將["sports", "house", "news"]轉換為[0,1,2]。輸入參數:

label:原始文本標籤列表

unique_list:標籤唯一值列表

函數返回:向量標籤列表

函數的實現過程如下:

先通過for循環結合enumerate()方法,從label中讀取每個索引及其對應文本標籤值

然後使用列表賦值方法,逐個將其文本值替換為索引值

最後返回新的向量標籤列表

步驟3解壓縮文件,從該步驟開始進入到應用過程。解壓縮文件步驟是將tar.gz中的壓縮文件提取出來。

該過程主要定義如下:

使用tarfile.open方法打開壓縮包並建立操作對象tar

使用tar.getnames()獲得壓縮包內的每個文件對象的名稱

通過for循環配合extract方法將每個文件提取到指定目錄下面(path定義的目錄)

關閉壓縮包對象

步驟4定義對象,該步驟用來定義在接下來的訓練過程中用到

的全局變數和常量。其中:

由於上述定義非常簡單,在注釋中都有解釋,在此不再贅述,僅說明幾個可能有疑問的定義:

pre_list:由於每次增量學習時,都會調用增量學習後的對象做預測,因此這裡可以分析每次預測與實際值是否相符。

unique_list:由於我們在做訓練之前已經獲知內容分類,因此這裡直接定義;如果沒有該先驗經驗信息,那麼需要單獨從列表中獲取。需要注意的是,該列表的運算只能全局性運算一次,否則每個文件中由於訓練集出現的順序不同,可能導致不同文件下唯一值列表的順序不同。例如文件1的唯一值列表是["sports","house", "news"],而文件2的唯一值列表可能是["house", "sports", "news"],雖然對應的索引都是[01,2],但對應的類別已經不同。

上述過程返回唯一值列表信息如下:("unique label:", ["sports", "house", "news"]),該列表將作為新數據集預測的索引結果參照。

步驟4交叉檢驗和預測數據集預處理,該步驟用來實現在增量學習過程中涉及到的交叉檢驗和預測數據集的預處理工作。由於在

訓練過程中會不斷調用該信息,因此這裡統一處理之後再做後續調用,避免重複計算浪費時間和資源。

上述實現過程比較簡單,基本思路是:

使用with open()方法打開文件並讀取文件中的數據,形成原始數據對象,交叉檢驗和預測數據集都是獨立於訓練集的數據

調用data_parse()解析出文本內容和對應標籤

調用word_to_vector()將文本內容向量化

調用label_to_vector()將標籤內容向量化(僅針對交叉檢驗集)

步驟5增量學習,該步驟是本節內容的主要環節。

增量訓練。使用with open()方法讀取每個文件的數據,然後依次調用data_parse、word_to_vector、label_to_vector函數實現解析文本內容和標籤並將內容和標籤做向量化轉換。使用貝葉斯分類器的partial_fit方法而非fit方法做增量訓練。

注意 在第一次增量訓練時,必須通過classes來指定分類的類別,後續過程的類別指定是可選的。

交叉檢驗。調用cross_val函數,並將在步驟4中處理好的數據以及增量學習的模型對象做檢驗檢查,將結果追加到score_list列表中。

增量預測。調用貝葉斯增量學習對象的predict方法做預測,將預測結果轉換為列表後追加到pre_list。

最後依次輸出每次交叉檢驗得分、預測標籤索引值、正確的標籤值。

上述代碼完成後返回如下信息:


從cross validation score得到的結果看,隨著每次數據量的增加,交叉檢驗的得分的趨勢不斷提高,這也證實了增量學習本身對於準確率的提升貢獻,但在第8次訓練時,總體得分從0.9147下降到0.9142,其中可能包含以下原因:

第8次的數據集本身是有誤的(或者不準確的),導致檢驗結果下降。

之前的數據中可能存在有誤信息,而第8次本身的信息是準確的,導致第8次的結果略有下降。

從10次的檢驗結果來看,整體趨勢的增長是良好的。

對新數據集的預測時,無論哪個階段都能準確的預測出其類別歸屬("sports"對應的索引值為0)。


在此類應用的部署中,有以下幾點需要經過改造才能部署到應用環境:

類別標籤的定義,應該從常量定義改為從固定標籤獲取。但考慮到增量學習時,一般情況下標籤值都是固定的,即不能出現第2次訓練時的預測標籤類別是[1,2,3],到後期變成[2,3,4]。因此這種一次性操作只需在首次執行時設置即可。同樣的,還有在第一次增量學習時,指定的classes值也需要做對應設置。

數據來源,需要改造為實際讀取數據的環境。一般情況下,

這種增量學習適合實時計算需求比較高的場景,例如個性化推薦,因此數據來源一般會基於實時產生數據的系統或機制,例如網站分析系統等。


在針對文本做分類時,需要讀者注意以下幾個問題:

針對文本的全形和半形轉換必不可少,實際上除此以外還包括大小寫轉換等操作,這些更多的用於英文處理。本案例中沒有任何對於大小寫轉換的操作,原因是在HashingVectorizer中默認通過lowercase=True來實現該功能。

在針對每個文檔(doc)做解析時,由於每個doc下面只含有一個url和content,因此使用的是find而不是find_all方法(當然find_all方法也能實現,但返回的是一個列表)。

不要直接使用不支持增量學習的交叉檢驗方法做模型做檢驗,否則將得到錯誤的得分信息。

增量學習的一般趨勢是良好的,但如果遇到隨著增量學習其準確率不斷下降或不穩定的情況,需要檢查檢驗方法是否準確或者分析是否存在數據集標籤標定或解析錯誤的問題。


關於增量學習的價值

增量學習的優點並不是通過演算法或模型本身來提供較高的準確率,而是通過不斷有新數據的加入來提高模型的準確率,因此在一定意義上,模型本身的選擇以及調參等動作都變得「不那麼重要」,因為只要數據足夠大,即使再差的模型也會由於掌握了足夠的多的數據規律而更加精準的預測新樣本,這是增量學習的關鍵所在。

當然,增量學習還能實現在物理硬體限制(尤其是內存)及其

他軟硬體不作任何優化的條件下,對於海量數據的訓練的支持,是一種非常好的解決大數據量計算問題的有效方法。

關於本案例中涉及到的方法

訓練集的文本跟預測集的文本不一致,會導致訓練時的中間過程或分類模型無法適用於預測過程,這點在文本分類時非常常見。案例中使用的HashingVectorizer能將詞語出現的頻率映射到固定維度空間,即使出現新的詞語也不會影響固定維度空間的模式,因此非常適合預測應用時新詞較多的場景。

HashingVectorizer本身能提供壓縮後的稀疏矩陣,其本身就能大量降低對於系統內存的佔用,非常適合大數據集下的計算和處理。

貝葉斯分類器廣泛應用於文本分類領域,其效果較好。除了本文提到的MultinomialNB外,還包括BernoulliNB和 GaussianNB兩種方法,他們各自有其適用場景。

有關數據分析與挖掘的更多內容,請查看《Python數據分析與數據化運營》。有關這本書的寫作感受、詳細內容介紹、附件(含數據和代碼源文件-源代碼可更改數據源直接使用)下載、關鍵知識和方法以及完整書稿目錄,請訪問《Python數據分析與數據化運營》新書上線,要購買此書請直接點擊閱讀全文購買。


喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 天善智能 的精彩文章:

企業級 BI plus 敏捷自助分析,IBM 分析產品新一代之 Cognos Analytics

TAG:天善智能 |