復現Entropy-based Term Weighting Schemes for Text Categorization in VSM小結
論文 ofollow,noindex">Entropy-based Term Weighting Schemes for Text Categorization in VSM 提出了新的基於熵的用於文字分類的詞權重計算方法tf·dc,tf·bdc,通過和目前流行的權重計算方法如tf·idf, tf·chi, tf·ig, tf·eccd, tf·rf and iqf·qf·icf進行實驗比較,證實其提出的計算方法的可行性和優越性。 筆者通過復現論文新提出的tf_dc,tf_bdc,以及用於實驗比較的tf·idf, tf·chi, tf·ig, tf·eccd, tf·rf and iqf·qf·icf,在使用和論文實驗一樣的語料庫 Reuters-R8 和 同樣的分類模型 :KNN和SVM後,發現確實如同論文說的outperform,起碼在Reuters上的分類結果優於tf-idf。
在此將整個復現的流程記錄和小結一下,從閱讀論文到實現計算方法再到使用分類模型到評估結果,整個過程雖然遇到了不少問題,但最終能夠逐個克服並最終完成復現。
復現基於python2.7,KNN使用 scikit-learn.org/" target="_blank" rel="nofollow,noindex">sklearn 包,SVM和原論文同樣使用 liblinear ,鑑於只是大致復現,因此除了和原論文同樣對KNN的鄰居數目引數進行實驗外,沒有細緻對knn和SVM做調參。
理論介紹
VSM向量空間模型
在自然語言處理過程中,第一步都是將要處理的字、詞或文字轉換成向量,畢竟計算機不懂文字,它只會處理數字。把詞轉換成向量我們有one hot, word embedding。到了文件層級,既然文件是由詞語組成的,那麼可以試著用詞語來表示文件。來看看一個用one hot表示文章的例子:
假設詞彙表有 ['one', 'apple','a','day','an'], 此時只使用one hot,即只判斷記錄詞是否出現,不記錄詞的頻率
文章a = "one day". 那麼 它的向量則是 [1,0,0,1,0]
文章b = "an apple", 則代表b的向量是[0,1,0,0,1]
one hot表示法雖然簡單,但也有很多缺點,比如只記錄詞出現與否,詞的區分能力被認為是一樣的等等,由此人們提出了很多計算方法,核心思想就是表示出 一個詞的辨別能力 。詞語的辨別能力是指:這個詞將一篇文件從其它文件中區分出來的能力(或者將一個類從其它類區分出來的能力),比如說一篇文章出現 "演算法" 這個詞較多,那麼它通常會是計算機等領域的文章,而不太可能會是體育、藝術類的文章。
就拿之前的例子而言,an和One這種詞明顯在很多地方都會出現,因此它們的辨別能力不強,而apple就比它們好一點,那麼在特徵權重計算中,它的權重就會比其它兩個高一些。
由此,每個詞語對應一個維度,每個詞語有一定的權重(由訓練語料訓練出來,代表這個詞區分各類文件或各個標籤的能力),再結合詞語在文字出現的次數,就能夠構成一個多維向量,將文件成功投射到多維空間中,這就是向量空間模型。投射之後,計算文章之間的相似度就可以有很多方法了,比如直接計算空間當中的距離啊,cosine啊等等,那麼我們就可以將文章歸到和它相似度高的那類中,由此完成文件分類的過程。
舊方法
為什麼需要提出新的權重計算方法呢?因為舊的不夠好,不夠好在哪裡?論文給出了理由:
大多數監督學習的計算方法基於詞在 PC(positive category) 和 NC(negative category) 中的出現次數,就會有以下問題:
- PC是單獨一個類, 而 NC包含多個類,而把它們統一成一個數字,那麼顯然NC的數目要遠遠大於PC,在權重計算中也會佔主導地位。
- NC包含多個類,僅歸為一個數字後,詞語在這些類中的分佈資訊就丟失了
- 計算權重得基於標籤,但測試文件本身就不具備標籤
對於非監督的計算方法,就拿tf-idf來說,其能力在於 將一篇文件從其它文件區分出來,而不是將一個類從其它類區分出來 。
文章列舉了其它較為流行的權重計算方法,並依照上面提出的問題一一舉出了例子。
tf·idf
作為最流行的權重計算方法,其計算方法分為兩個部分
一個是tf(i,j),即詞i在文章j中出現的頻率: , 用詞i出現的次數 / 這篇文章總長即可
另一個是 idf(i,j),稱為逆文件頻率,和這個詞出現的文件數相關: , 用文件總數 / 出現了 i 的文件數目,而後再取log,一般為了防止分母為0會在分母上加一
最終的 tf-idf就等於兩者相乘。
tf·chi
基於統計的卡方檢驗和下面一些計算方法都基於以下這個表格:

image
A:類別k中出現了詞j的文件數目
B:除類別k外的其它類出現了詞j的文件總數,用詞j出現的文件總數 - A 即可
C:負文件數目,即類別k中不包含詞j的數目,用 類別k的文章總數-A
D:其它類別不包含詞j的數目,用其它類文件總數 - B
卡方檢驗的原始公式和近似公式:

image
tf·ig
Information Gain 資訊增益:增加了這個資訊使得系統的熵降低了多少。
在特徵權重計算中,以詞語出現與否分別計算整個語料庫的熵,以熵的差值作為詞語的資訊增益,即詞的權重。

image
P(Ci):表示類別Ci出現的概率,用Ci包含的文件數除以文件總數
P(t):詞語T出現的概率,用出現過T的文件數除以總文件數
P(Ci|t):出現T的時候,類別Ci出現的概率,用出現了T並且屬於類別Ci的文件數除以出現了T的文件數
P(~t):詞語T不出現的概率,用 1 - P(t) 即可
P(Ci|~t)表示未出現T的時候,類別Ci出現的概率,用未出現了T並且屬於類別Ci的文件數除以未出現T的文件數
tf·eccd
論文 Entropy based feature selection for text categorization 同樣提出了一種基於熵的權重計算方法

image

image
tf·rf
由於表格中B、D的數目顯然很大,為了避免它們帶來的影響,人們提出了relevance frequency(rf),只是用 a和c的比值來表明一個詞的辨別能力。

image
iqf·qf·icf
這篇論文 Term weighting schemes for question categorization 面對短文字(使用者提出的問題)提出三種新的權重計算方式: iqf*qf*icf、qf*icf 和 vrf。
和rf相比,iqf*qf*icf額外考慮了一個詞出現了類數目,然而正如論文提出的那樣,只考慮了類的數目,卻沒有考慮到詞在這些類內部的分佈情況。

image

image
新的權重計算方法
tf·dc
論文論證了用熵來表示詞語辨別能力的可行性,由此提出了新的計算方法 dc:distribution concentration。
思想基於兩點:
- 辨別能力和詞語在所有類的集中程度有關,詞語集中程度越高,則它只出現在很少幾個類,那麼它的辨別能力就越高
- 集中程度越高的詞,具有的熵越小

image
H(t)即代表t的熵, f(t,ci)表明詞語t在類別 ci出現的文件數目
由於 ,在除了log(|C|)之後就能將熵化為[0,1]區間,這使得同一篇文章內的詞有了可比性。
tf·bdc
考慮到現實中語料庫類別包含的文件數目有差異,並非理想中所有類的文件數大致相等,那麼為了平衡類之間的規模差異,論文在dc的基礎上提出了bdc:balanced distribution concentration。
為了避免大類文件數目過多帶來的偏差,論文將絕對頻率換成了概率,由此平衡類之間的差異。

image
復現過程
用特徵權重進行文字分類的思路
文字分類的整個過程如下:
- 通過訓練語料庫計算得到詞語權重,並通過將語料庫文章投影成向量構成訓練特徵,標籤則為類標籤索引,以此訓練KNN或SVM模型。 此步主要得到三樣東西:
- 詞語權重表
- 詞彙表:計算詞語頻率後刪減頻率過高和過低的詞的產物,每個詞彙表裡面的詞將作為一維,每篇文章為 1*n 的向量,n為詞彙表大小。
- 模型引數
- 對於每篇測試文件:
- 根據詞彙表刪去無關詞彙
- 查詞語權重表,若使用tf則額外計算每個詞語在文字中出現的頻率。 得到每個詞語的詞語權重,由此得到文件的向量表示
- 將文件向量作為特徵輸入分類模型中,得到預測結果
資料處理
語料庫和論文中同樣選用路透社的語料 Reuters-21578 R8,鑑於Reuters的語料是有名的難處理再加上覆現的重點不在此,因此筆者直接使用處理好的語料: Reuters-21578 R8 , TrainingSet , TestSet .
獲得語料之後,一個比較重要的地方在於製作特徵向量和標籤。
我的特徵向量和標籤製作方法是:
- 對於文件,首先將所有詞轉換成one hot,轉換使用了sklearn.preprocessing中的Encoder,然而一件很重要的事情在於one hot的順序,因為Encoder會按照詞彙出現順序設定one hot的順序,因此我的做法是把文件的詞連線在詞彙表後面一起匯入Encoder,而後再對生成的one hot進行擷取,因為詞彙表的順序和大小是固定的,所以擷取也很方便。 需要注意的是要將詞彙表設定為全域性的,並且僅在訓練集中構造詞彙表,而測試集僅用詞彙表進行篩選。
- 對於類別標籤,同樣在訓練集中構造標籤表,而後串接起來匯入Encoder再擷取,然後用argmax獲得唯一的1所在的下標,由此將字串轉換成了單個數字。
維度壓縮
在復現的過程中,首先遇到的第一個小問題就是維度過大,雖然只使用了幾MB的語料庫,但要是把每個詞都作為一維的話,那就有將近兩萬維,剛開始直接執行的時候電腦就報出超出記憶體的錯誤了。
解決方法就是刪去頻率過高和過低的詞:
- 統計訓練語料中的詞語頻率得到詞頻表和詞彙表
- 使用Counter得到各個頻率的詞彙數目並使用matplotlib.pyplot將詞彙頻率繪製成直方圖,此外還將詞彙表的長度作為額外參考
- 根據長度、頻率分佈挑選閾值,根據上下界刪減詞彙表
- 根據詞彙表刪去訓練和測試語料的其它詞,僅保留在詞彙表中的詞語。
通過這麼個維度壓縮,使得維度從兩萬維降低到五千多維,不僅加快了執行速度,減少了執行需要的空間,同時也減少了停用詞和自造詞的干擾。

image
<center>刪減前</center>

image
<center>刪減後</center>
KNN中的cos近似
下一個比較重要的小問題在於sklearn中的KNN提供的計算距離的函式並沒有cos距離,而後在尋找解決方案時發現了這條 stackoverflow上的回答 :
回答分為兩個方面:
-
指出為什麼cosine相似度沒有在sklearn包中:
cosine相似度在兩個向量完全一樣時的輸出結果是1,在它們完全相反時結果是-1,而這嚴格上並不能算作衡量指標,其它如歐幾里得距離,向量相似度越高距離越小即越接近於0. (不過為什麼通過1-cos近似),因此不能使用knn的加速結構來加快運算。
-
給出瞭解決方法:
- 可以自行實現cos相似度並作為函式引數傳進去,代價就是不能使用knn中用於加速的結構,只能使用暴力計算。
- 第二個方案比較巧妙,通過深入到公式的轉換把計算cosine相似度轉換成用歸一化之後的歐幾里得即可。

image
看完之後筆者被那個公式轉換驚歎到,而後果斷地採用了這個方案,在計算出文件向量之後,額外做一次歸一化,之後只需要正常傳入knn,距離函式用預設的歐幾里得距離即可。
論文細節
論文中的實驗部分有這麼一句話:
To represent test documents with category-specific schemes, e.g., tf·chi, tf·ig, tf·eccd, tf·rf and iqf·qf·icf, we adopt a popular method in previous studies hat assigning the maximum value among |C| estimated weights to each term in test documents.
鑑於筆者缺乏大量閱讀英文文件的經驗,再加上當時沒有考慮到實現,所以閱讀論文的時候始終不明白這句話的意思,但後來在實現話中提到的 tf-chi、tf-ig等方法時程式頻繁報出詞典key error,而後想起這句似乎關鍵但並不太明白什麼意思的話,再結合實現時候的問題,終於明白了。
權重計算方法如 tf-idf 分為兩個部分,一個是 tf ,由一個詞在一篇文章內出現的頻率得到,訓練集和測試集均要計算,用python程式碼表示即是一個兩層的詞典
tf[document][word] = frequency[document][word] / doclength[document]
另一個是 idf ,由一個詞在語料庫中出現的文件數和文件總數計算得到,對於訓練集是需要計算得到的,對於測試集則相當於權重詞典,要用的時候直接查就行,而它的表現形式在tf-idf中是一個單層的詞典,idf只計算每個詞,和詞在哪個類中沒有關係。
tf_idf[document][word] = tf[document][word] * idf[word]
而對於如chi等詞,它們詞權重計算不僅和詞相關,還和類別相關。意思就是每個詞的權重在不同類是不一樣的,用程式碼表示出來即是:
tf_chi[document][word] = tf[document][word] * chi[label][word]
那麼就有一個比較重要的問題:要是測試集的詞在測試文件屬於的類中不存在怎麼辦?那句話就給出了答案:若是詞在所屬類中不存在權重,那麼就在其它類裡面選擇這個詞權重最大的那個作為權重,用程式碼表示就是:
if weights[labell].has_key(word): tf_chi[doc][word] *= weights[labell][word] else: tf_chi[doc][word] *= max([ weights[x][word]for x in weights if weights[x].has_key(word)])
由此,缺失的拼圖找到了,筆者最終成功實現了這些權重計算方法。
衡量標準
根據論文原文,實驗採用了兩種衡量標準:MicroF1和MacroF1:
-
MicroF1就是一般說的準確率:預測正確的數目 / 測試樣本總數
-
MacroF1 就是通常的F1的均值:
-
-
-
: 正確預測為C的數目 / 預測為C的總數
: 正確預測為C的數目 / 真實為C的數目
-
TP: true positive 屬於C被分到C(預測正確)
TN: true nagative 屬於C被分到其它類(預測錯誤)
FP: false positive 不屬於C被正確分類(預測正確)
FN: false nagative 不屬於C被分到C(預測錯誤)
-
復現結果
下面是復現之後的結果:
筆者在未調參的SVM、KNN上,使用Reuters R8語料庫的結果(KNN鄰居數在1-35內選擇結果最優的):

image

image
論文給出的最終結果:

image
筆者復現的在Reuters R8中KNN鄰居數和MicroF1的關係:

image
論文給出的關係圖:

image
可以看到,雖然資料上略微有差距,但經過在模型上的優化之後應該能夠接近或達到論文給出的結果,提出的新的權重計算方式tf_dc和tf_bdc在Reuters R8上的表現還是不錯的,不說能夠傲視所有權重計算方式,起碼錶現足夠優異,有一席之地。
小結
通過這次復現,筆者較為深入地學習了向量空間模型VSM,還了解了各種權重計算方法,談到權重計算也不再只有單一的tf_idf了。此外,這麼一個完整的,從資料到特徵(雖然語料庫預處理不是我做的),再到匯入模型進行訓練,再到預測,最後進行評估,這麼個流程走下來之後,筆者對於機器學習的理解也加深了。光是從調包上講也知道怎麼用KNN和SVM,怎麼做資料可視化了。
原始碼
原論文、筆者實現過程的完整程式碼(包括訓練模型、測試、評估、所有權重計算方法)、筆者實驗得到的資料(MicroF1和MacroF1,knn各個鄰居數上的MicroF1,可直接呼叫評估函式檢視結果)都可以在這裡看到: 我的github
我的個人部落格: Zedom1.top