1. 程式人生 > >如何對文字提取特徵

如何對文字提取特徵

問題來源

做文字分類這樣的問題,需要從大量語料中提取特徵,並將這些文字特徵變換為數值特徵。
假設我們有下面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 wordsTFTF-IDFFeatureHasher),將原始資料變為數值型資料。

本文例子所有原始碼參見Github

參考