如何對文字提取特徵
問題來源
做文字分類這樣的問題,需要從大量語料中提取特徵,並將這些文字特徵變換為數值特徵。
假設我們有下面3個樣本,怎麼將他們轉換為數值特徵呢?
樣本 | 文字 |
---|---|
1 | God is love |
2 | OpenGL on the GPU is fast |
3 | Doctor David is PHD |
方法1:Bags of words
這種所謂Bags of words
的特徵,是將訓練集中所有出現過的單詞做成一個字典,統計每個單詞出現的次數,作為特徵。本文例子中有3個樣本,共出現了13個單詞,出去3個is是重複出現的,可獲得一個由11個單片語成的字典。統計這11個單詞在每個樣本中的出現次數,可得:
Bags of words特徵
樣本 | david | doctor | fast | god | gpu | is | love | on | opengl | phd | the |
---|---|---|---|---|---|---|---|---|---|---|---|
1 | 0 | 0 | 0 | 1 | 0 | 1 | 1 | 0 | 0 | 0 | 0 |
2 | 0 | 0 | 1 | 0 | 1 | 1 | 0 | 1 | 1 | 0 | 1 |
3 | 1 | 1 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 1 | 0 |
我們也可以用sklearn
中的sklearn.feature_extraction.text.CountVectorizer
來獲取Bags of words特徵
基於sklearn獲取Bags of words特徵
的python程式碼
# 原始語料,3個文字
strs_train =[
'God is love',
'OpenGL on the GPU is fast',
'Doctor David is PHD']
# 提取特徵
from sklearn.feature_extraction.text import CountVectorizer
count_vect = CountVectorizer()
X_train_counts = count_vect.fit_transform(strs_train)
X_train_counts.shape
# 檢視數值特徵
X_train_counts.todense()#轉換成數值特徵Bags of words
# 檢視特徵名
count_vect.vocabulary_#特徵名
用程式碼計算得到的特徵與理論統計得到的特徵完全相同
方法2:TF
TF
即詞頻
(Term Frequency)。Bags of words特徵
統計的是單詞的出現次數,這樣在長文字中,單詞的出現次數會大於短文字,可能造成不公平問題。所以TF特徵
就被提出,用於統計詞頻。
TF=(某個單詞在文字中的出現次數)/(文字中的總單詞數)。
根據TF
的計算公式,可得TF特徵
如下。
TF特徵
樣本 | david | doctor | fast | god | gpu | is | love | on | opengl | phd | the |
---|---|---|---|---|---|---|---|---|---|---|---|
1 | 0 | 0 | 0 | 0.33 | 0 | 0.33 | 0.33 | 0 | 0 | 0 | 0 |
2 | 0 | 0 | 0.17 | 0 | 0.17 | 0.17 | 0 | 0.17 | 0.17 | 0 | 0.17 |
3 | 0.25 | 0.25 | 0 | 0 | 0 | 0.25 | 0 | 0 | 0 | 0.25 | 0 |
我們也可以用sklearn
中的sklearn.feature_extraction.text.TfidfTransformer
來獲取TF特徵
。
from sklearn.feature_extraction.text import TfidfTransformer
from sklearn.feature_extraction.text import CountVectorizer
# 先提取 Bags of words特徵
count_vect = CountVectorizer()
X_train_counts = count_vect.fit_transform(strs_train)
# 再基於Bags of words特徵,變換為TF特徵
tf_transformer = TfidfTransformer(use_idf=False).fit(X_train_counts)
X_train_tf = tf_transformer.transform(X_train_counts)
X_train_tf.shape
# 檢視數值特徵
X_train_tf.todense()#轉換成數值特徵Bags of words
# 檢視特徵名
count_vect.vocabulary_#特徵名
經計算,我們發現,程式碼算得到的特徵與理論公式計算得到的特徵數值不相同!!
這是什麼情況呢?經檢視sklearn
的原始碼。發現sklearn
計算TF的方式與理論公式不一致,它只對Bags of words特徵
矩陣做了Normalization
,就將結果作為TF特徵
。如果在計算TF特徵
時,去掉Normalization
(程式碼TfidfTransformer(norm=None, use_idf=False).fit(X_train_counts)
),如下,就得到了與Bags of words特徵
完全相同的結果。
from sklearn.feature_extraction.text import TfidfTransformer
from sklearn.feature_extraction.text import CountVectorizer
# 先提取 Bags of words特徵
count_vect = CountVectorizer()
X_train_counts = count_vect.fit_transform(strs_train)
# 再基於Bags of words特徵,變換為TF特徵
tf_transformer = TfidfTransformer(norm=None, use_idf=False).fit(X_train_counts)
X_train_tf = tf_transformer.transform(X_train_counts)
X_train_tf.shape
# 檢視數值特徵
X_train_tf.todense()#轉換成數值特徵Bags of words
# 檢視特徵名
count_vect.vocabulary_#特徵名
仔細想想sklearn
的做法也沒有問題,TF特徵
的提出,不就是為了去除文件大小對結果的影響嗎。Normalization
不也能達到同樣效果麼。
並且我們這裡也要明白,TF的是可以有不同的定義的,本文給出的是理論上用的最多的計算公式。但sklearn
計算TF就用了其他的方法。一些常用的TF定義見這裡。
方法3:TF-IDF
IDF
是逆文件頻率
。逆文件頻率 = log(語料中的文件總數/含有該詞的文件數加1)。得到逆文件頻率
後,TF-IDF = TF*IDF。
TF-IDF特徵
樣本 | david | doctor | fast | god | gpu | is | love | on | opengl | phd | the |
---|---|---|---|---|---|---|---|---|---|---|---|
1 | 0 | 0 | 0 | 0.33*log(1.5) | 0 | 0.33*log(0.75) | 0.33*log(1.5) | 0 | 0 | 0 | 0 |
2 | 0 | 0 | 0.17*log(1.5) | 0 | 0.17*log(1.5) | 0.17*log(1.5) | 0 | 0.17*log(1.5) | 0.17*log(0.75) | 0 | 0.17*log(1.5) |
3 | 0.25*log(1.5) | 0.25*log(1.5) | 0 | 0 | 0 | 0.25*log(0.75) | 0 | 0 | 0 | 0.25*log(1.5) | 0 |
我們也可以用sklearn
中的sklearn.feature_extraction.text.TfidfTransformer
來獲取TF-IDF特徵
。
from sklearn.feature_extraction.text import TfidfTransformer
from sklearn.feature_extraction.text import CountVectorizer
# 先提取 Bags of words特徵
count_vect = CountVectorizer()
X_train_counts = count_vect.fit_transform(strs_train)
# 再基於Bags of words特徵,變換為TF-IDF特徵
tfidf_transformer = TfidfTransformer()
X_train_tfidf = tfidf_transformer.fit_transform(X_train_counts)
X_train_tfidf.todense()
同理,sklearn
計算TF-IDF特徵
的方法也和本文的理論公式有所區別,並且最終還對計算結果做了Normalization
。具體細節見原始碼註釋。
方法4:FeatureHasher
FeatureHasher
能將string通過Hash演算法Murmurhash3
,變為數值化的矩陣。
在使用FeatureHasher
前,需要將原始語料的字元格式變為如下格式。
[{'God': 1, 'is': 1, 'love': 1},
{'GPU': 1, 'OpenGL': 1, 'fast': 1, 'is': 1, 'on': 1, 'the': 1},
{'David': 1, 'Doctor': 1, 'PHD': 1, 'is': 1}]
完整程式碼如下:
from sklearn.feature_extraction import FeatureHasher
# 將原始資料Hash到10維Feature
h = FeatureHasher(n_features=10, non_negative=True)
# 將資料組成D格式
D=[]
for s in strs_train:
tmp = {}
for w in s.split(' '):
tmp[w] = 1
D.append(tmp)
f = h.transform(D)
f.toarray()
FeatureHasher
特徵
樣本 | x1 | x2 | x3 | x4 | x5 | x6 | x7 | x8 | x9 | x10 |
---|---|---|---|---|---|---|---|---|---|---|
1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 |
2 | 0 | 1 | 0 | 1 | 0 | 0 | 0 | 0 | 1 | 1 |
3 | 0 | 0 | 0 | 0 | 1 | 0 | 1 | 1 | 0 | 1 |
結論
對於文字、字串資料,我們有4種常用方法(Bags of words
,TF
,TF-IDF
,FeatureHasher
),將原始資料變為數值型資料。
本文例子所有原始碼參見Github。