1、引言
關於文本的提取有很多方法,本文主要探索下sklearn官方的文本特征提取功能。
2、文本特征提取
文本分析是機器學習算法的主要應用領域。 然而,原始數據,符號文字序列不能直接傳遞給算法,因為它們大多數要求具有固定長度的數字矩陣特征向量,而不是具有可變長度的原始文本文檔。
sklearn提供三種方法:
令牌化, 對每個可能的詞令牌分成字符串並賦予整數形的id,例如通過使用空格和標點符號作為令牌分隔符。
統計,每個詞令牌在文檔中的出現次數。
標准化,在大多數的文檔 / 樣本中,可以減少重要的次令牌的出現次數的權重。
總的來說,方法是把文本文檔集合轉化成特征向量,比如每一行是一個文檔,每一列是詞id。
由於詞的稀疏性,通常使用scipy.sparse
包中的稀疏實現。
3、使用方法
類CountVectorizer介紹
首先類 CountVectorizer 在單個類中實現了 tokenization (詞語切分)和 occurrence counting (出現頻數統計):
>>> from sklearn.feature_extraction.text import CountVectorizer >>> vectorizer = CountVectorizer() >>> corpus = [ ... 'This is the first document.', ... 'This is the second second document.', ... 'And the third one.', ... 'Is this the first document?', ... ] >>> X = vectorizer.fit_transform(corpus) #對每一列進行index >>> vectorizer.get_feature_names() == ( ... ['and', 'document', 'first', 'is', 'one', ... 'second', 'the', 'third', 'this']) True >>> X.toarray() array([[0, 1, 1, 1, 0, 0, 1, 0, 1], [0, 1, 0, 1, 0, 2, 1, 0, 1], [1, 0, 0, 0, 1, 0, 1, 1, 0], [0, 1, 1, 1, 0, 0, 1, 0, 1]]...) #還可以設置一個或兩個詞來進行分割,設置兩個詞的原因是部分語句要連讀才有區分度 >>> bigram_vectorizer = CountVectorizer(ngram_range=(1, 2), ... token_pattern=r'\b\w+\b', min_df=1) >>> analyze = bigram_vectorizer.build_analyzer() >>> analyze('Bi-grams are cool!') == ( ... ['bi', 'grams', 'are', 'cool', 'bi grams', 'grams are', 'are cool']) True
TF-idf項加權
在一個大的文本語料庫中,一些單詞將出現很多次(例如 “the”, “a”, “is” 是英文),因此對文檔的實際內容沒有什么有意義的信息。 如果我們將直接計數數據直接提供給分類器,那么這些頻繁詞組會掩蓋住那些我們關注但很少出現的詞。
為了重新計算特征權重,並將其轉化為適合分類器使用的浮點值,因此使用 tf-idf 變換是非常常見的。
Tf表示 術語頻率,而 tf-idf 表示術語頻率乘以轉制文檔頻率:
.
術語頻率,一個術語在給定文檔中出現的次數乘以 idf 組件, 計算為
,
其中 是文檔的總數,
是包含術語
的文檔數。 然后,所得到的 tf-idf 向量通過歐幾里得范數歸一化:
.
它源於一個詞權重的信息檢索方式(作為搜索引擎結果的評級函數),同時也在文檔分類和聚類中表現良好。
以下部分包含進一步說明和示例,說明如何精確計算 tf-idfs 以及如何在 scikit-learn 中計算 tf-idfs, TfidfTransformer
並 TfidfVectorizer
與定義 idf 的標准教科書符號略有不同
在 TfidfTransformer
和 TfidfVectorizer
中 smooth_idf=False
,將 “1” 計數添加到 idf 而不是 idf 的分母:
>>> from sklearn.feature_extraction.text import TfidfTransformer >>> transformer = TfidfTransformer(smooth_idf=False) >>> counts = [[3, 0, 1], ... [2, 0, 0], ... [3, 0, 0], ... [4, 0, 0], ... [3, 2, 0], ... [3, 0, 2]] ... >>> tfidf = transformer.fit_transform(counts) >>> tfidf.toarray() array([[ 0.81940995, 0. , 0.57320793], [ 1. , 0. , 0. ], [ 1. , 0. , 0. ], [ 1. , 0. , 0. ], [ 0.47330339, 0.88089948, 0. ], [ 0.58149261, 0. , 0.81355169]])
每行都被正則化,使其適應歐幾里得標准:
例如,我們可以計算`計數`數組中第一個文檔中第一個項的 tf-idf ,如下所示:
現在,如果我們對文檔中剩下的2個術語重復這個計算,我們得到:
和原始 tf-idfs 的向量:
然后,應用歐幾里德(L2)規范,我們獲得文檔1的以下 tf-idfs:
通過 擬合
方法調用計算的每個特征的權重存儲在模型屬性中:
>>> transformer.idf_
array([ 1. ..., 2.25..., 1.84...])
雖然tf-idf標准化通常非常有用,但是可能有一種情況是二元變量顯示會提供更好的特征。 這可以使用類 CountVectorizer
的 二進制
參數來實現。 特別地,一些估計器,諸如 伯努利朴素貝葉斯 顯式的使用離散的布爾隨機變量。 而且,非常短的文本很可能影響 tf-idf 值,而二進制出現信息更穩定。
通常情況下,調整特征提取參數的最佳方法是使用基於網格搜索的交叉驗證,例如通過將特征提取器與分類器進行流水線化。
詞語表示的限制
直接看例子
>>> ngram_vectorizer = CountVectorizer(analyzer='char_wb', ngram_range=(2, 2)) >>> counts = ngram_vectorizer.fit_transform(['words', 'wprds']) >>> ngram_vectorizer.get_feature_names() == ( ... [' w', 'ds', 'or', 'pr', 'rd', 's ', 'wo', 'wp']) True >>> counts.toarray().astype(int) array([[1, 1, 1, 0, 1, 1, 1, 0], [1, 1, 0, 1, 1, 1, 0, 1]]) >>> ngram_vectorizer = CountVectorizer(analyzer='char_wb', ngram_range=(5, 5)) >>> ngram_vectorizer.fit_transform(['jumpy fox']) ... <1x4 sparse matrix of type '<... 'numpy.int64'>' with 4 stored elements in Compressed Sparse ... format> >>> ngram_vectorizer.get_feature_names() == ( ... [' fox ', ' jump', 'jumpy', 'umpy ']) True >>> ngram_vectorizer = CountVectorizer(analyzer='char', ngram_range=(5, 5)) >>> ngram_vectorizer.fit_transform(['jumpy fox']) ... <1x5 sparse matrix of type '<... 'numpy.int64'>' with 5 stored elements in Compressed Sparse ... format> >>> ngram_vectorizer.get_feature_names() == ( ... ['jumpy', 'mpy f', 'py fo', 'umpy ', 'y fox']) True
對比以上幾種例子,我們可以知道:
對於使用白色空格進行單詞分離的語言,對於語言邊界感知變體 char_wb
尤其有趣,因為在這種情況下,它會產生比原始 char
變體顯着更少的噪音特征。 對於這樣的語言,它可以增加使用這些特征訓練的分類器的預測精度和收斂速度,同時保持關於拼寫錯誤和詞導出的穩健性。
雖然可以通過提取 n-gram 而不是單獨的單詞來保存一些本地定位信息,但是包含 n-gram 的單詞和袋子可以破壞文檔的大部分內部結構,因此破壞了該內部結構的大部分含義。
為了處理自然語言理解的更廣泛的任務,因此應考慮到句子和段落的地方結構。因此,許多這樣的模型將被稱為 “結構化輸出” 問題,這些問題目前不在 scikit-learn 的范圍之內。