1. 程式人生 > 其它 >sklearn 《Text feature extraction》筆記

sklearn 《Text feature extraction》筆記

技術標籤:Pythonsklearn

官方教程見:
https://scikit-learn.org/stable/modules/feature_extraction.html#text-feature-extraction
6.2.3節

1 The Bag of Words representation(詞帶表示法)

文字分析是機器學習演算法的一個主要應用領域。然而原始資料、符號序列不能直接反饋給演算法本身,因為大多數演算法希望得到的是固定大小的數字特徵向量,而不是長度可變的原始文字文件。

為了解決這個問題,scikit-learn提供了最常見的從文字內容中提取數字特徵的實用工具,分別是:

  • 把字串轉為標記(token),併為每個可能的標記提供一個整數id,例如使用空格和標點符號作為標記分隔符。
  • 計算每個文件中標記的出現次數。
  • 把出現在樣本或文件的主要內容中的標記進行標準化,並按照重要性遞減的次序賦權值。

因此,一個文件的語料庫可以用一個矩陣來表示,每個文件有一行,語料庫中出現的每個標記(如單詞)有一列。

我們稱向量化為將文字文件集合轉化為數字特徵向量的一般過程。這種特定的策略(標記化、計數和歸一化)被稱為 “詞袋”(Bag of Words)或 "n-grams袋 "表示法。文件由詞的出現次數來描述,而完全忽略詞在文件中的相對位置資訊。

2 Sparsity(稀疏化)

由於大多數文件通常會使用語料庫中使用的單詞的一個很小的子集,因此產生的矩陣將有許多特徵值為零(通常超過99%)。

例如,一個由10000個短文文件(如電子郵件)組成的集合,這個集合使用的詞彙量總大小在10萬個不同的單詞左右,而每個文件將單獨使用100到1000個不同的單詞。

為了能夠在記憶體中儲存這樣一個矩陣,同時也為了加快代數運算矩陣/向量的速度,實現通常會使用稀疏表示法,比如scipy.sparse包中的實現。

3 Common Vectorizer usage(常見的向量器用法)

CountVectorizer這個類在一個類中同時實現了文件的標記化(tokenization)和標記的出現次數計數(occurrence counting):

from sklearn.feature_extraction.text import
CountVectorizer

這個模組有很多引數,但是預設值是相當合理的(詳情請看參考文件):

vectorizer = CountVectorizer()
print(vectorizer)

# output:
CountVectorizer(analyzer='word', binary=False, decode_error='strict',
        dtype=<class 'numpy.int64'>, encoding='utf-8', input='content',
        lowercase=True, max_df=1.0, max_features=None, min_df=1,
        ngram_range=(1, 1), preprocessor=None, stop_words=None,
        strip_accents=None, token_pattern='(?u)\\b\\w\\w+\\b',
        tokenizer=None, vocabulary=None)

接下來我們嘗試用它對一個小規模的文件語料庫進行標記化,並對詞彙的出現次數進行計數:

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)
# X 是一個以三元組形式儲存的稀疏矩陣,表示的是一個4x9的稀疏矩陣
print(X)
print(X.shape)
print(type(X))
print(vectorizer.get_feature_names())
print(X.toarray())
print(vectorizer.vocabulary_.get('document'))

# output:
  (0, 1)	1
  (0, 2)	1
  (0, 6)	1
  (0, 3)	1
  (0, 8)	1
  (1, 5)	2
  (1, 1)	1
  (1, 6)	1
  (1, 3)	1
  (1, 8)	1
  (2, 4)	1
  (2, 7)	1
  (2, 0)	1
  (2, 6)	1
  (3, 1)	1
  (3, 2)	1
  (3, 6)	1
  (3, 3)	1
  (3, 8)	1
(4, 9)
<class 'scipy.sparse.csr.csr_matrix'>
['and', 'document', 'first', 'is', 'one', 'second', 'the', 'third', 'this']
[[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]]
 8

預設的配置是通過提取至少兩個字母的單詞來標記字串。這一步的具體功能可以明確要求:

analyze = vectorizer.build_analyzer()
print(analyze("This is a text document to analyze.") == (
    ['this', 'is', 'text', 'document', 'to', 'analyze']))

# output:
True

在訓練語料中沒有看到的詞,在未來呼叫轉換方法時將完全被忽略:

>>> vectorizer.transform(['Something completely new.']).toarray()
array([[0, 0, 0, 0, 0, 0, 0, 0, 0]]...)

注意,在上一個語料庫中,第一個文件和最後一個文件的詞彙是完全相同的,只有is和this的順序不同,因此這兩個文件被編碼成了完全相等的向量。這樣一來,我們失去了最後一個文件是一個疑問句的這一資訊。為了保留一些區域性的排序資訊,我們可以在1-grams(單個單詞為提取物件)的基礎上額外的提取2-grams:

bigram_vectorizer = CountVectorizer(ngram_range=(1, 2), token_pattern=r'\b\w+\b', min_df=1)
analyze = bigram_vectorizer.build_analyzer()
print(analyze('Bi-grams are cool!'))

# output:
['bi', 'grams', 'are', 'cool', 'bi grams', 'grams are', 'are cool']

這樣一來,2-grams向量器提取出的詞彙矩陣比之前的更大,但是解決了在 local positioning模式下編碼二義性的問題:

>>> X_2 = bigram_vectorizer.fit_transform(corpus).toarray()
>>> X_2
array([[0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0],
       [0, 0, 1, 0, 0, 1, 1, 0, 0, 2, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0],
       [1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 0],
       [0, 0, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1]]...)

3.1 Using stop words(停用詞)

停頓詞是指 “而”、“該”、"他 "等詞,這些詞被認為在表示文字內容方面沒有資訊,為了避免被理解為預測的訊號,可以將其刪除。但有時,類似的詞對預測是有用的,比如在對寫作風格或個性進行分類時。

在模組提供的“英語”停用詞列表不是通用的、一刀切的解決方案,有些任務可能需要一個更針對性的解決方案,更多資訊見[NQY18]。

請謹慎選擇停用詞表。流行的停用詞表可能包括對某些任務來說資訊量很大的詞。

4 Tf–idf term weighting(Tf-IDF術語加權)

在一個龐大的文字語料庫中,有些詞會非常頻繁地出現(如英語中的 “the”、“a”、"is "等),因此關於文件的實際內容的有意義資訊很少。如果我們將直接計數的資料直接輸入到分類器中,那麼這些非常頻繁的詞彙就會掩蓋掉比較罕見但更有趣的詞彙的頻率。

為了將計數特徵重新加權為適合分類器使用的浮點值,通常會使用 tf-idf 變換。

其中,Tf 的意思是term-frequency(詞頻),即在一個文件內某個詞條總共出現的次數,tf-idf 的意思是 term-frequency 倍的 inverse document-frequency(兩個量相乘):
在這裡插入圖片描述
使用 TfidfTransformer 的預設設定:
TfidfTransformer(norm=‘l2’, use_idf=True, smooth_idf=True, sublinear_tf=False)
tf 將與 idf 相乘,其中,idf 的運算公式是:
在這裡插入圖片描述
其中 n 是語料庫中的文件數量,df(t) 是含有詞條 t 的文件的數量。

輸出出來的 tf-idf 向量接著會使用歐幾里得範數來標準化:
在這裡插入圖片描述
這原本是一種為資訊檢索而開發的術語加權方案(作為搜尋引擎結果的排名功能),在文件分類和聚類中也得到了很好的應用。

下面的章節包含了進一步的解釋和例子,說明了tf-idfs的具體計算方法,以及scikit-learn的TfidfTransformer和TfidfVectorizer中計算出的tf-idfs與標準教科書中定義idf的符號略有不同,其中教科書的定義:
在這裡插入圖片描述
在TfidfTransformer和TfidfVectorizer中,在smooth_idf=False的情況下,"1 "計數會加到idf中,而不是idf的分母:
在這裡插入圖片描述
這種標準化由 TfidfTransformer 類實現:

from sklearn.feature_extraction.text import TfidfTransformer

counts = [[3, 0, 1],
          [2, 0, 0],
          [3, 0, 0],
          [4, 0, 0],
          [3, 2, 0],
          [3, 0, 2]]

transformer = TfidfTransformer()
transformer.fit_transform(counts).toarray()

通過呼叫 fit 方法計算得到的每個特徵的權重儲存在模組的一個屬性裡(?這仨特徵的權重的計算公式是什麼?):

transformer.idf_

由於tf-idf非常常用於文字特徵,所以還有一個叫做TfidfVectorizer的類,它將CountVectorizer和TfidfTransformer的所有選項結合在一個模型中:

from sklearn.feature_extraction.text import TfidfVectorizer
vectorizer = TfidfVectorizer()
vectorizer.fit_transform(corpus).toarray()