當我們嘗試使用統計機器學習方法解決文本的有關問題時,第一個需要的解決的問題是,如果在計算機中表示出一個文本樣本。一種經典而且被廣泛運用的文本表示方法,即向量空間模型(VSM),俗稱“詞袋模型”。
我們首先看一下向量空間模型如何表示一個文本:
空間向量模型需要一個“字典”:文本的樣本集中特征詞集合,這個字典可以在樣本集中產生,也可以從外部導入,上圖中的字典是[baseball, specs, graphics,..., space, quicktime, computer]。
有了字典后便可以表示出某個文本。先定義一個與字典長度相同的向量,向量中的每個位置對應字典中的相應位置的單詞,比如字典中的第一個單詞baseball,對應向量中的第一個位置。然后遍歷這個文本,對應文本中的出現某個單詞,在向量中的對應位置,填入“某個值”。
實際上填入的“某個值”,就是當前特征詞的權重(Term Weight),目前特征詞的權重主要有以下四種:
- Bool(presence)
表示某個單詞是否在某個文檔中出現,如果出現則記為1,否定則記為0。
- Term frequency(TF)
表示某個單詞在文本中出現的次數(上圖中使用的權重),一個文本中,某個特征詞出現的愈多,可能其在樣本中的貢獻越大。
- Inverse document frequency(IDF)
document frequency表示特征詞在數據集中出現的文檔頻率。某個詞文檔頻率越低,相應的這些文檔,越容易被捕獲。
- TF-IDF
TF-IDF則綜合了上面兩種特征權重的性質。
有關於“教育”的文檔中,“高校”、“學生”等詞出現的頻率很高,而在“體育”類的文檔中,“比賽”, “選手”出現的頻率比很高。采用TF權重,這些特征詞有着較高權重是合理的(Term frequency)。但是,某些詞如“這些”,“是”, “的”,也有着較高的詞頻,但是重要度顯然沒有,“高校”、“學生”、“比賽”, “選手”來得重要。但“這些”,“是”, “的”這些詞IDF往往比較低,很好的彌補了TF的缺陷。因此TF-IDF權重,在傳統的文本分類,信息檢索領域有着非常廣泛的運用。
盡管TF-IDF權重有着非常廣泛的應用,並不是所有的文本權重采用TF-IDF都會有較好的性能。比如,情感分類(Sentiment Classification)問題上,采用BOOL型的權重往往有較好的性能(Sentiment Classification的很多論文都采用BOOL型權重)。
現在,我們回到文章開頭提高的向量空間模型。基於向量空間模型表示方法,每個特征詞之間相互獨立。由於這種表示簡單的特點,在開始之初,推動了文本分類相關研究工作,但是隨着時間的推移,傳統的向量空間模型由於丟棄了詞序、句法和部分語義信息,往往限制了某些領域的發展(如Sentiment Classification),成為影響性能的瓶頸。目前的解決思路有:
- 使用N-Gram語法特征
- 將語法語義信息考慮到分類任務中
- 模型上改進...
最后,介紹一下sklearn中的文本的表示方法,並以此實現一個簡單的文本分類。
我們使用的數據集是 movie_reviews語料(情感分類器任務)。數據集的組織方式是,一個文本存放在文件下,標簽相同的文件放在同一個文件夾下。其數據集的結構如下:
movie_reviews\
pos\
cv000_29590.txt, cv001_18431.txt...cv999_13106.txt
neg\
cv000_29416.txt, cv001_19502.txt...cv999_14636.txt
在sklearn中,sklearn.datasets.load_files,可以很好的加載這種結構的數據集,數據加載完成后,就可以利用前面介紹的VSM,將文本樣本表示出來。
sklearn專門提供了文本特征的提取模塊:sklearn.feature_extraction.text,完成將一個文本樣本變成一個詞袋。CountVectorizer對應詞頻權重或是BOOL型權重(通過參數binary調節)向量空間模型, TfidfVectorizer提供了Tfidf權重下的向量空間模型。sklearn為他們提供了大量的參數(所有參數也都提供了默認參數),具有很高的靈活性和實用性。
在movie_reviews語料上,基於 sklearn 文本表示方法,並使用Multinomial Naive Bayes分類器進行情感分類的代碼如下:
#!/usr/bin/env python # coding=gbk import os import sys import numpy as np from sklearn.datasets import load_files from sklearn.cross_validation import train_test_split from sklearn.feature_extraction.text import CountVectorizer from sklearn.naive_bayes import MultinomialNB def text_classifly(dataset_dir_name): #加載數據集,切分數據集80%訓練,20%測試 movie_reviews = load_files(dataset_dir_name) doc_terms_train, doc_terms_test, doc_class_train, doc_class_test = train_test_split(movie_reviews.data, movie_reviews.target, test_size = 0.2) #BOOL型特征下的向量空間模型,注意,測試樣本調用的是transform接口 count_vec = CountVectorizer(binary = True) doc_train_bool = count_vec.fit_transform(doc_terms_train) doc_test_bool = count_vec.transform(doc_terms_test) #調用MultinomialNB分類器 clf = MultinomialNB().fit(doc_train_bool, doc_class_train) doc_class_predicted = clf.predict(doc_test_bool) print 'Accuracy: ', np.mean(doc_class_predicted == doc_class_test) if __name__ == '__main__': dataset_dir_name = sys.argv[1] text_classifly(dataset_dir_name)