iT邦幫忙

第 11 屆 iT 邦幫忙鐵人賽

DAY 30
0
Google Developers Machine Learning

又LAG的ML學習筆記系列 第 30

簡單使用scikit-learn裡的TFIDF看看

什麼是TFIDF?

實際上TFIDF分成兩個部份,TF和IDF。分別表示詞頻term frequency,tf)和逆向檔案頻率inverse document frequency,idf)。和Word2Vec一樣,是種將文字轉換為向量的方式。

不過我只理解TFIDF而已。TextRank也是一種辦法,是由PageRank變形而來,但也只有一些概念而已。

接著簡單介紹TF和IDF這兩個部份,理解也有助於使用scikit-learn裡的TFIDF。

TFIDF最常被使用的一個目的是,找到文件當中的關鍵字。怎樣的關鍵字是重要的?一個直覺的想法是出現最多次的字。這可能可以,不過因為每個文件的字數不同,無法比較。所以在用文件內的字數作為分母,將所有文件得到數值加以規範化。這也就是TF,特定單字在文件出現次數/文件總次數

不過只是這樣,很有可能讓一些定冠詞,或是常用單字,像是a, an, the, and, or 等等,得到很高的分數。一個簡單的作法是先把這些字詞去掉(stopword),不過還有一種方式是將低這些單字的分數。IDF會去計算一個字出現在文件的逆向頻率,這表示出現頻率越高,出現在越多文件之中,但是得分會越低。透過TF和IDF相乘:TFxIDF,得到的綜合分數就是TFIDF。


一般stopword和idf會一起使用。


使用scikit-learn裡的TFIDF

引入相關套件

from sklearn.feature_extraction.text import CountVectorizer
from sklearn.feature_extraction.text import TfidfTransformer
from sklearn.feature_extraction.text import TfidfVectorizer
import pandas as pd

CountVectorizer會計算單字出現在文件的次數;再透過TfidfVectorizer轉換成TFIDF和IDF。也可以直接使用TfidfTransformer計算TFIDF。但先來建立幾個假文:

建立測試假文

d1 = 'a b d e d f a f e fa d s a b n'
d2 = 'a z a f e fa h'
d3 = 'a z a f e fa h'

這裡建立簡單的三個文本。

透過CountVectorizer和TfidfTransformer

可以很簡單的使用新增CountVectorizerTfidfVectorizer,並使用其方法fit()。來看看:

計算單字次數:

vectorizer = CountVectorizer(stop_words=None, token_pattern="(?u)\\b\\w+\\b")  
X = vectorizer.fit_transform([d1,d2,d3])
r = pd.DataFrame(X.toarray(),columns=vectorizer.get_feature_names())
print("CountVector")
r

vectorizer.get_feature_names()可以取得計算的單字。另外,原本的token_pattern(?u)\\b\\w\\w+\\b,會過濾掉兩個字母以下的內容,但測試文本使用單個字母來測試,所以要加以改寫。將stop_word設為None也是同樣道理,比免去除單字,因為只是範例,而想看看所有結果:

CountVector:
 	a 	b 	d 	e 	f 	fa 	h 	n 	s 	z
d1 	3 	2 	3 	2 	2 	1 	0 	1 	1 	0
d2 	2 	0 	0 	1 	1 	1 	1 	0 	0 	1
d3 	2 	0 	0 	1 	1 	1 	1 	0 	0 	1

轉換為IDF

轉換方式很類似:

transformer = TfidfTransformer(smooth_idf=True)
Z = transformer.fit_transform(X)
r = pd.DataFrame(Z.toarray(),columns=vectorizer.get_feature_names(), index=['d1', 'd2', 'd3'])
print("TFIDF")
r

然後結果:

TFIDF:

	a 	b 	d 	e 	f 	fa 	h 	n 	s 	z
d1 	0.384107 	0.433566 	0.650349 	0.256071 	0.256071 	0.128036 	0.000000 	0.216783 	0.216783 	0.000000
d2 	0.622686 	0.000000 	0.000000 	0.311343 	0.311343 	0.311343 	0.400911 	0.000000 	0.000000 	0.400911
d3 	0.622686 	0.000000 	0.000000 	0.311343 	0.311343 	0.311343 	0.400911 	0.000000 	0.000000 	0.400911

查看IDF

transformer有一個屬性idf_儲存IDF值。

print("IDF")
pd.DataFrame([transformer.idf_], columns=vectorizer.get_feature_names())

透過CountVectorizer和TfidfVectorizer

直接使用的方式也很類似,就不贅述。

vectorizer = TfidfVectorizer(sublinear_tf=False, stop_words=None, token_pattern="(?u)\\b\\w+\\b", smooth_idf=True)  
X = vectorizer.fit_transform([d1,d2,d3])
r = pd.DataFrame(X.toarray(),columns=vectorizer.get_feature_names(), index=['d1', 'd2', 'd3'])
print("TFIDF")
r

其接果也一模一樣,IDF值也可以透過vectorizer.idf_取得。


但是那裡怪怪的?

根據IDF的公式:

將字母a的結果帶入,應該是lg(3/3)=lg(1)=0,但得到結果卻是1?翻翻看原始碼,是加1了沒錯,但是其他結果似乎也不太對。只有CountVector很簡單是一樣的,其他不管是TFIDF的結果還是IDF的結果都與公式不符。在看過其他文章後,才知道TFIDF有其他變形。TfidfTransformerTfidfVectorizer有可多可選的參數可調整,這接參數也會影響結果。

這個結果以前沒去注意都沒發現/images/emoticon/emoticon13.gif。還想說是不是我算錯,但是也有人是有同樣發現,scikit-learn裡的TFIDF並不是經典的算法。

最後需要說明的是,由於函式 TfidfVectorizer() 有很多引數,我們這裡僅僅採用了預設的形式,所以輸出的結果可能與採用前面介紹的(最基本最原始的)演算法所得出之結果有所差異(但數量的大小關係並不會改變)。


上一篇
從開始到混亂的log、ln、lg理解之旅
下一篇
Mozilla Iodide 玩玩 - 能產生漂亮文件的另類的Jupyter Notebook替代品
系列文
又LAG的ML學習筆記32

尚未有邦友留言

立即登入留言