文本分類需要CNN?No!fastText完美解決你的需求(後篇)
http://blog.csdn.net/weixin_36604953/article/details/78324834
想必通過前一篇的介紹,各位小主已經對word2vec以及CBOW和Skip-gram有了比較清晰的了解。在這一篇中,小編帶大家走進業內最新潮的文本分類算法,也就是fastText分類器。fastText與word2vec的提出者之所以會想到用fastText取代CNN(卷積神經網絡)等深度學習模型,目的是為了在大數據情況下提高運算速度。
其實,文本的學習與圖像的學習是不同的。學習不同,一般情況下,文本學習並不需要過多隱藏層、過復雜的神經網絡,為什麽這麽說呢?打個比方,有這樣一句話:“柯南是個聰明的帥哥”,如果讓你去學習理解這句話,聽上二三十次次與聽上兩三次相比,對於這句話的理解可能也沒有什麽提升,就模型而言,不需要過復雜的神經網絡就可以很好的對這句話進行學習,然而圖像不同,如果讓你臨摹一個大師的化作,你第一次臨摹可能是個四不像,但第一萬次臨摹,可能就會以假亂真了。所以,用簡單的網絡對自然語言進行學習,可以快速、高質量的得到結果。正因如此,才有了fastText算法。
言歸正傳,我們開始fastText算法的深入了解,這篇文章主要從三個方面來介紹算法:模型架構、分層softmax、n-gram特征。在算法原理介紹完畢後,同上一篇文章一樣,會帶領各位小主實現fastText算法的應用,迫不及待了吧?
預備知識
為了更好的理解fastText,我們先來了解一些預備知識。第一個是BoW模型,也叫做詞袋模型。BoW模型(Bag of words)應用於自然語言處理、信息檢索和圖像分類等方面。它忽略掉文本的語法和語序等要素(舉個例子,Bow模型認為“我愛你”和“你愛我”是相同的,如果我們的世界是一個BoW模型該有多好,再也不會出現對女神單相思的情況了),將其僅僅看作是若幹詞匯的集合。BoW 使用一組無序的單詞(word)來表達一段文字或一個文檔,並且文檔中每個單詞的出現都是獨立的。
例如:首先給出兩個簡單的文本文檔如下:
John likes to watch movies. Mary likes too.
John also likes to watch football games.
基於上述兩個文檔中出現的單詞,構建如下一個詞典 (dictionary):
上面的詞典中包含10個單詞, 每個單詞有唯一的索引, 那麽每個文本我們可以使用一個10維的向量來表示,向量中的元素是詞典中對應的詞語出現的頻數。如下所示:
[1, 1,1, 1, 0, 1, 1, 1, 0, 0]
該向量與原來文本中單詞出現的順序沒有關系,而是詞典中每個單詞在文本中出現的頻數。
第二個要介紹的是名為“霍夫曼樹”的數據結構,fastText的速度快,且不會受到不平衡分類問題影響的關鍵因素就是這個數據結構。
霍夫曼編碼樹,又稱最優二叉樹。是一類帶權路徑長度最短的樹。假設有n個權值{w1,w2,…,wn},如果構造一棵有n個葉子節點的二叉樹,而這n個葉子節點的權值是{w1,w2,…,wn},則所構造出的帶權路徑長度最小的二叉樹就被稱為霍夫曼樹。
帶權路徑長度:如果在一棵二叉樹中共有n個葉子節點,用Wi表示第i個葉子節點的權值,Li表示第i個葉子節點到根節點的路徑長度,則該二叉樹的帶權路徑長度:
霍夫曼樹的構建步驟如下:
(1)將給定的n個權值看做n棵只有根節點(無左右孩子)的二叉樹,組成一個集合HT,每棵樹的權值為該節點的權值。
(2)從集合HT中選出2棵權值最小的二叉樹,組成一棵新的二叉樹,其權值為這2棵二叉樹的權值之和。
(3)將步驟2中選出的2棵二叉樹從集合HT中刪去,同時將步驟2中新得到的二叉樹加入到集合HT中。
(4)重復步驟2和步驟3,直到集合HT中只含一棵樹,這棵樹便是霍夫曼樹。
感覺還雲裏霧裏?沒關系,小編來舉個簡單的例子你就明白了。假設給定如圖1的四個帶權重的節點A—D,結點下方為其對應的權重。
感覺還雲裏霧裏?沒關系,小編來舉個簡單的例子你就明白了。假設給定如圖1的四個帶權重的節點A—D,結點下方為其對應的權重。
首先選出權重最小的兩個節點作為圖2中新樹的左右子樹(新樹即右側由6、C、A組成的樹),新樹根節點權重為其左右子樹權重之和(即1+5=6)。
然後重復前一操作,從剩余結點中選出最小的,與前一個樹的根節點組成新樹的左右結點,如圖3所示,新的根節點權重為其兩個子結點之和(10+6=16)。
在重復前面的操作,得到圖4的最終樹。
fastText的霍夫曼樹葉子結點對應為最終的label,可以看到,權重最大的,也就是權重最大的label,其深度最小,fastText 充分利用了這個性質,使得其速度得以提升。
Huffman編碼(霍夫曼編碼):
對每個字符設計長度不等的編碼,讓出現較多的字符采用盡可能短的編碼。利用霍夫曼樹求得的用於通信的二進制編碼稱為霍夫曼編碼。
樹中從根結點到每個葉子節點都有一條路徑,對路徑上的各分支約定指向左子樹的分支表示“0”碼,指向右子樹的分支表示“1”碼,取每條路徑上的“0”或“1”的序列作為各個葉子節點對應的字符編碼,即是霍夫曼編碼。以前面的例子為例,各路徑上的編碼如圖5所示。故A結點的編碼應當為111,B為10,C為110,D為0。D是權重最大的結點,換言之也就是出現最多的結點,其編碼最短,只有一個0,而權重最小的結點是A,其編碼為111,這樣的編碼方式完成了我們對節點編碼的需求。
fastText模型架構
fastText算法是一種有監督的模型,與《前篇》中的CBOW架構很相似,其結構如圖6所示。《前篇》中的CBOW,通過上下文預測中間詞,而fastText則是通過上下文預測標簽(這個標簽就是文本的類別,是訓練模型之前通過人工標註等方法事先確定下來的)。如果小主在上一篇文章中已經對CBOW了解透徹了,可能到這裏你已經覺得fastText並沒有什麽新鮮玩意兒。事實確實如此,從模型架構上來說,沿用了CBOW的單層神經網絡的模式,不過fastText的處理速度才是這個算法的創新之處。
fastText模型的輸入是一個詞的序列(一段文本或者一句話),輸出是這個詞序列屬於不同類別的概率。在序列中的詞和詞組構成特征向量,特征向量通過線性變換映射到中間層,再由中間層映射到標簽。fastText在預測標簽時使用了非線性激活函數,但在中間層不使用非線性激活函數。
圖 6 展示了一個有一個隱藏層的簡單模型。第一個權重矩陣w_1可以被視作某個句子的詞查找表(詞表查找是啥來著?去看看《前篇》中的那個矩陣相乘你就想起來了)。詞表征被平均成一個文本表征,然後其會被饋送入一個線性分類器。這個構架和《前篇》介紹的CBOW模型相似,只是中間詞(middle word)被替換成了標簽(label)。該模型將一系列單詞作為輸入並產生一個預定義類的概率分布。我們使用一個softmax方程來計算這些概率。當數據量巨大時,線性分類器的計算十分昂貴,所以fastText使用了一個基於霍夫曼編碼樹的分層softmax方法。常用的文本特征表示方法是詞袋模型,然而詞袋(BoW)中的詞順序是不變的,但是明確考慮該順序的計算成本通常十分高昂。作為替代,fastText使用n-gram獲取額外特征來得到關於局部詞順序的部分信息,後文將詳細介紹。
層次softmax
Facebook聲稱fastText比其他學習方法要快得多,能夠訓練模型“在使用標準多核CPU的情況下10分鐘內處理超過10億個詞匯”,特別是與深度模型對比,fastText能將訓練時間由數天縮短到幾秒鐘。這樣的速度取決於什麽呢?模型架構上並沒有什麽本質革新,接下來就帶你“上高速”~
softmax函數
上一篇,我們已經介紹了這個函數。softmax函數實際是一個歸一化的指數函數:
而softmax把一個k維的real value向量(a1,a2,a3,a4….)映射成一個(b1,b2,b3,b4….)其中bi是一個0-1的常數,然後可以根據bi的大小來進行多分類的任務,如取權重最大的一維。
分層softmax(Hierarchical softmax)
分層softmax的目的是降低softmax層的計算復雜度。
二叉樹。Hierarchical softmax本質上是用層級關系替代了扁平化的softmax層,如圖1所示,每個葉子節點表示一個詞語(即霍夫曼樹的結構)。
我們可以把原來的softmax看做深度為1的樹,詞表V中的每一個詞語表示一個葉子節點。如果把softmax改為二叉樹結構,每個word表示葉子節點,那麽只需要沿著通向該詞語的葉子節點的路徑搜索,而不需要考慮其它的節點。這就是為什麽fastText可以解決不平衡分類問題,因為在對某個節點進行計算時,完全不依賴於它的上一層的葉子節點(即權重大於它的葉結點),也就是數目較大的label不能影響數目較小的label(即圖5中B無法影響A和C)。
平衡二叉樹的深度是log2(|V|),因此,最多只需要計算log2(|V|)個節點就能得到目標詞語的概率值。hierarchical softmax定義了詞表V中所有詞語的標準化概率分布。
具體說來,當遍歷樹的時候,我們需要能夠計算左側分枝或是右側分枝的概率值。為此,給每個節點分配一個向量表示。與常規的softmax做法不同,這裏不是給每個輸出詞語w生成詞向量v_w^’,而是給每個節點n計算一個向量〖v?n?’〗。總共有|V|-1個節點,每個節點都有自己獨一無二的向量表示,H-Softmax方法用到的參數與常規的softmax幾乎一樣。於是,在給定上下文c時,就能夠計算節點n左右兩個分枝的概率:
上式與常規的softmax大致相同。現在需要計算h與樹的每個節點的向量v_n^’的內積,而不是與每個輸出詞語的向量計算。而且,只需要計算一個概率值,這裏就是偏向n節點右枝的概率值。相反的,偏向左枝的概率值是1- p(right│n,c)。
如圖8所示,為了方便理解,這裏的葉子節點給出的依然是詞語,也就是CBOW的架構,fastText是相同的道理,只是將葉子節點替換為label即可。假設已知出現了詞語“the”、“dog”、“and”、“the”,則出現詞語“cat”的概率值就是在節點1向左偏的概率值、在節點2向右偏的概率以及在節點5向右偏的概率值的乘積。
值得註意的是,此方法只是加速了訓練過程,因為我們可以提前知道將要預測的詞語(以及其搜索路徑)。在測試過程中,被預測詞語是未知的,仍然無法避免計算所有詞語的概率值。
向量表示該節點的搜索路徑,詞語“cat”的向量就是011。上文中提到平衡二叉樹的深度不超過log2(|V|)。若詞表的大小是|V|=10000,那麽搜索路徑的平均長度就是13.3。
N-gram
常用的特征是詞袋模型,在第一部分小編已經介紹過詞袋模型了。詞袋模型不考慮詞之間的順序,因此 fastText 還加入了 N-gram 特征。“我愛你”的特征應當是“我”、“愛”、“你”。那麽“你愛我”這句話的特征和“我愛你”是一樣的,因為“我愛你”的bag(詞袋)中也是只包含“你”、“愛”、“我”。
還是那句話——“我愛你”:如果使用2-gram,這句話的特征還有 “我-愛”和“愛-你”,這兩句話“我愛你”和“你愛我”就能區別開來了,因為“你愛我”的2-gram的特征還包括“你-愛”和“愛-我”,這樣就可以區分“你愛我”和“我愛你”了。為了提高效率,實務中會過濾掉低頻的 N-gram。否則將會嚴重影響速度。
在fastText 中一個低維度向量與每個單詞都相關。隱藏表征在不同類別所有分類器中進行共享,使得文本信息在不同類別中能夠共同使用。這類表征被稱為詞袋(bag of words)(此處忽視詞序)。在 fastText中也使用向量表征單詞 n-gram來將局部詞序考慮在內,這對很多文本分類問題來說十分重要。
fastText算法實現
這裏提醒讀者,fastText在Python2和Python3中都可以使用,已有了現成的包,但只支持Linux和mac系統,windows暫時還不支持fastText。本例使用的環境是linux-ubuntu16.04+Python3+JupyterNotebook的組合,讀者當然可以使用其他IDE或是使用Python2,都是完全沒有問題的。只需要在終端使用語句pip install fasttext(Python2)或是pip3 install fasttext進行安裝即可。
我們使用的語料是清華大學的新聞文本(數據比較大,可以在http://thuctc.thunlp.org/message進行下載),這裏不在詳細介紹分詞過程,與《前篇》中的過程基本是一致的。可以直接到https://pan.baidu.com/s/1jH7wyOY和https://pan.baidu.com/s/1slGlPgx下載處理好的訓練集和測試集(處理後的數據形式為詞與詞之間用空格分開,詞語與標簽默認用label分隔),如圖9所示。
使用logging可以查看程序運行日誌。fasttext.supervised()的第一個參數為訓練集,即用來擬合模型的數據,第二個參數為模型存儲的絕對路徑,第三個為文本與標簽的分隔符;訓練好模型後通過load_model加載模型,對訓練集使用test方法則可以在測試集上使用模型,則會得到模型在測試集上的準確率和召回率,可以看到模型的準確率和召回率是非常高的,而且對於這個近400Mb的訓練集,擬合模型只花費了60秒左右(小編的電腦為8GB內存),速度是非常可觀的。
fastText算法通過了兩篇的講解,相比聰明的你已經掌握了算法原理,關於fastText算法到這裏就要告一段落了,熟悉了原理和思想,接下來小主可以去找些有趣的文本做一些有趣的事情了,與fastText一起飈車吧!
文本分類需要CNN?No!fastText完美解決你的需求(後篇)