ギークなエンジニアを目指す男

機械学習系の知識を蓄えようとするブログ

【初学者向け】TFIDFについて簡単にまとめてみた

本日は、TF-IDFについて簡単に勉強してみたので、それのまとめです。

TF-IDFとは

Term Frequency - Inverse Document Frequencyの略で自然言語をベクトルで表現する方法のひとつであり、ある文書を特徴づける重要な単語を抽出したいときに有効な手法です。

TF-IDFの求め方は次のとおりです。

 tfidf = tf × idf

tfとidfを掛けることによりtfidfを求められます。まとめると、下記のように表すことができます。

f:id:taxa_program:20190114140515p:plain:w400
TF-IDFの計算式

このtf(Term Frequency)idf(Inverse Document Frequency)について説明します。

Term Frequency

tfはある単語のある文書における出現頻度のことです。tfでは、出現頻度が多い単語ほど重要になります。

tfの求め方は下記の通りです。

 tf = 単語i の 文書dにおける出現回数 / 文書dにおける全単語の出現回数の和

例えば下記のような文書があった場合、

A:私はラーメンを愛している。中でも味噌ラーメンが一番好きだ。

B:私は焼きそばが好きだ。しかしラーメンはもっと好きだ。

これを形態素解析してみると

A:['私', 'ラーメン', '愛', '中', '味噌', 'ラーメン', '一番', '好き']

B:['私', '焼きそば', '好き', 'しかし', 'ラーメン', 'もっと', '好き']

というようになります。この文書Aで、ラーメン味噌のtfを計算してみます。

 tf(ラーメン, 文書A) = 2 / 6 = 0.33
 tf(味噌, 文書A) = 1/6 = 0.17

上記から、この文書では味噌よりもラーメンの方が重要な単語ということが分かります。

Inverse Document Frequency

idfは、ある単語がいくつの文書で使用されているかを表しています。例えば、「私」や「これ」、「しかし」などの単語はいろんな文書で使用される可能性があり、出現頻度が高いのは当然です(=あまり重要な単語ではない)。そのような普遍的に出現する単語ではなく、特定の文書にしか登場しないレア単語であればあるほど、idf値は高くなります。

idfの求め方は下記の通りです。

 idf = log(総文書数 / ある単語iを含む文書の数) + 1

tfと同じ例でidfを求めてみると、

 idf(ラーメン) = log(2/2) + 1 = 1
 idf(味噌) = log(2/1) + 1 = 1.3

ラーメンより味噌の方が全文書からの比率として出現頻度が低い(=レア)なので、idfの値が高くなっています。

TF-IDF(Term Frequency - Inverse Document Frequency)を求める

上記の例より、tfとidfについて求めることができたので、最後のTF-IDFを求めてみます。

 TFIDF = tf(ラーメン, 文書A) × idf(ラーメン) = 0.33 × 1 = 0.33
 TFIDF = tf(味噌, 文書A) × idf(味噌) = 0.17 × 1.3 = 0.22

簡単に表にまとめてみます。

A:私はラーメンを愛している。中でも味噌ラーメンが一番好きだ。

B:私は焼きそばが好きだ。しかしラーメンはもっと好きだ。

- tf idf TF-IDF
ラーメン 0.33 1 0.33
味噌 0.17 1.3 0.22

tf値とTF-IDF値を比較したときに、「ラーメン」に関しては同じ値ですが、「味噌」に関してはTF-IDFの方が値が大きくなっています。これは、文書Aにおいて「味噌」という単語のレア度が高い(=文書を特徴付ける単語になっている)ためだと考えられます。

scikit-learnで計算してみる

上記の例で、実際にコーディングしてみます。(都合上、上記と若干単語表現を変えています)

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

df = pd.DataFrame({'id': ['A', 'B'],
                   'text': [ '私は ラーメン 愛する 中でも 味噌 ラーメン 一番 好き', '私は 焼きそば 好き しかし ラーメン もっと 好き']})


# TF-IDFの計算
tfidf_vectorizer = TfidfVectorizer(use_idf=True,lowercase=False)

# 文章内の全単語のTfidf値を取得
tfidf_matrix = tfidf_vectorizer.fit_transform(df['text'])

# index 順の単語リスト
terms = tfidf_vectorizer.get_feature_names()

# 単語毎のtfidf値配列:TF-IDF 行列 (numpy の ndarray 形式で取得される)
# 1つ目の文書に対する、各単語のベクトル値
# 2つ目の文書に対する、各単語のベクトル値
# ・・・
# が取得できる(文書の数 * 全単語数)の配列になる。(toarray()で密行列に変換)
tfidfs = tfidf_matrix.toarray()

各値を確認してみます。

print(terms)

-> ['しかし', 'もっと', 'ラーメン', '一番', '中でも', '味噌', '好き', '愛する', '焼きそば', '私は']
print(tfidfs)

->[0 0 0.5364 0.3769 0.3769 0.3769 0.2682 0.3769 0 0.2682 ]
->[0.4069 0.4069 0.2895 0 0 0 0.5791 0 0.4069 0.2895 ]

これを表にまとめると下記のようになります。(小数第3位で切り捨てています)

- しかし もっと ラーメン 一番 中でも 味噌 好き 愛する 焼きそば 私は
A 0 0 0.53 0.37 0.37 0.37 0.26 0.37 0 0.26
B 0.40 0.40 0.28 0 0 0 0.57 0 0.40 0.28

この値は、各単語をTF-IDFによりベクトル値として数値変換した結果になっています。(TfidfVectorizerは正規化までしてくれるので、前半で述べた計算結果と若干異なっています)

この数値が大きければ大きいほど、その文書における重要単語ということになります。

TfidfVectorizerの使い方については、公式ドキュメントをご参照ください。 正規化についてはここに記載があります。