TF-IDF とは
複数の文章・コーパスが与えられた時、「各単語がその文書内でどれくらい重要・特徴的か」を表す尺度。 文章ごとのキーワード特定、文章の単語検索のスコアリング、文章のベクトル表現などに利用される。
定義と解釈
TF
TF の定義
TF = Term Frequency.
$TF(t, d)$ は、ある文章 $d$ 中で、単語が $t$ が出現する頻度を表す。
\[TF(t_i, d_j) = \cfrac{ | \{ t: \ t \in d_j, \ t = t_i \} | }{|d_j|}\]${ t: \ t \in d_j, \ t = t_i }$ は $d_j$ 内の $t_i$ に一致する単語の集合。
TF の解釈
- その文章の中でその単語が何度も出現するほど値が大きい
- つまり、その文章内での単語の重要度 を表す
- $TF$ が大きくなるケース:
- you, is, a, the など、どんな文にもよく出てくる 一般語
- その文章のメインテーマに関係が深い単語
IDF
IDF の定義
IDF = Inverse Document Frequency.
全文章 $D$ のうち、単語 $t$ を含む文章の割合($DF$, document frequency)の逆数。
\[\begin{eqnarray} IDF(t_i, D) &=& \left( \cfrac{|\{ d: \ d \in D, t_i \in d \}|}{|D|} \right)^{-1} \\ &=& \cfrac{|D|}{|\{ d: \ d \in D, t_i \in d \}|} \end{eqnarray}\]${ d: \ d \in D, t_i \in d }$ は単語 $t_i$ を含む文章の集合。
一般に、ゼロ除算を防ぐために分母に1を足し、対数スケールを取って
\(IDF(t_i, D) = \log \left( \cfrac{|D|}{|\{ d: \ d \in D, t_i \in d \}| + 1} \right)\) の形で使われることが多い。
IDF の解釈
- 全文章の中で、その単語を持つ文章が少ないほど値が大きい
- つまり、全ての文章中での単語の珍しさ を表す
- $IDF$ が大きくなるケース:
- 特定の文章にしか出現しない単語 = 専門性が高い単語
- $IDF$ が小さくなるケース:
- 多くの文章に共通して出現する単語 = 一般語
TF-IDF
TF-IDF の定義
\[\begin{eqnarray} TF \cdot IDF (t_i, d_j, D) &=& TF(t_i, d_j) IDF(t_i, D) \\ &=& \cfrac{ | \{ t: \ t \in d_j, \ t = t_i \} | }{|d_j|} \log \left( \cfrac{|D|}{|\{ d: \ d \in D, t_i \in d \}| + 1} \right) \end{eqnarray}\]TF-IDF の解釈
- $TF$ 単独で単語のスコアにすると、you, is, a, the など一般語のスコアも高くなってしまう
- そこに $IDF$ をかけることで、一般語のスコアが下がり、「専門性が高くその文章のテーマに関係が深い単語」 のスコアが高くなる
具体例
- $D={d_1,d_2,d_3,d_4,d_5}$
- $d_1 = { t_1, t_2, t_3, {\color{red}{t_4, t_5, t_5}} }$
- $d_2 = { t_1, t_2, t_3, {\color{red}{t_1, t_4, t_4, t_4, t_5, t_5}} }$
- $d_3 = { t_1, t_2, t_3, {\color{red}{t_1, t_2, t_5, t_6, t_6, t_6, t_7}} }$
- $d_4 = { t_1, t_2, t_3, {\color{red}{t_3, t_6, t_6, t_7, t_7}} }$
- $d_5 = { t_1, t_2, t_3, {\color{red}{t_2, t_2, t_3, t_6, t_7, t_7, t_7}} }$
全ての $t_i, d_j$ に対して $TF(t_i, d_j)$ を計算すると、
import numpy as np
word_count = np.array([
#t1 t2 t3 t4 t5 t6 t7
[1, 1, 1, 1, 2, 0, 0], # d1
[2, 1, 1, 3, 2, 0, 0], # d2
[2, 2, 1, 0, 1, 3, 1], # d3
[1, 1, 2, 0, 0, 2, 2], # d4
[1, 3, 2, 0, 0, 1, 3] # d6
])
tf = word_count.T / word_count.sum(axis=1)
print(tf)
"""
d1 d2 d3 d4 d5
t1 [[0.16666667 0.22222222 0.2 0.125 0.1 ]
t2 [0.16666667 0.11111111 0.2 0.125 0.3 ]
t3 [0.16666667 0.11111111 0.1 0.25 0.2 ]
t4 [0.16666667 0.33333333 0. 0. 0. ]
t5 [0.33333333 0.22222222 0.1 0. 0. ]
t6 [0. 0. 0.3 0.25 0.1 ]
t7 [0. 0. 0.1 0.25 0.3 ]]
"""
(計算例)$ | d_3 | = 10$ であり、$d_3$ に $t_6$ は3回出現するから、 |
次に、全ての $t_i$ に対して $IDF(t_i, D)$ を計算すると、
size_D = word_count.shape[0] # 5
num_docs_of_each_word = (word_count > 0).sum(axis=0)
print(num_docs_of_each_word)
# [5 5 5 2 3 3 3]
idf = np.log(size_D / (num_docs_of_each_word + 1))
print(idf)
"""
t1 t2 t3 t4 t5 t6 t7
[-0.18232156 -0.18232156 -0.18232156 0.51082562 0.22314355 0.22314355
0.22314355]
"""
(計算例)$t_6$ を含む文章は全5件中3件あるから、
\[IDF(t_6, D) = \log \left( \cfrac{5}{3 + 1} \right) = 0.22314355\]以上を用いて、全ての $t_i, d_j$ に対して $TF \cdot IDF (t_i, d_j, D)$ を計算すると、
tf_idf = (tf.T * idf).T
print(tf_idf)
"""
d1 d2 d3 d4 d5
t1 [[-0.03038693 -0.0405159 -0.03646431 -0.02279019 -0.01823216]
t2 [-0.03038693 -0.02025795 -0.03646431 -0.02279019 -0.05469647]
t3 [-0.03038693 -0.02025795 -0.01823216 -0.04558039 -0.03646431]
t4 [ 0.0851376 0.17027521 0. 0. 0. ]
t5 [ 0.07438118 0.04958746 0.02231436 0. 0. ]
t6 [ 0. 0. 0.06694307 0.05578589 0.02231436]
t7 [ 0. 0. 0.02231436 0.05578589 0.06694307]]
"""
- どの文章にも出現する一般語 $t_1, t_2, t_3$ の値は期待通りに低い
- 一部の文章にしか出現しない $t_4, t_5, t_6, t_7$ の値は期待通りに高い
- $d_4$ にしか登場しない $t_7$ の値は特に高い(= IDF によるブースト)
- $d_2$ において出現頻度が高い $t_4$ の値も高め(= TF によるブースト)
TF-IDF により各文章をベクトル表現することができたので、文書間のコサイン類似度を計算してみる。
# 内積
inner_product = np.dot(tf_idf.T, tf_idf)
# 各文章ベクトルの長さ
vector_length = np.sqrt((tf_idf ** 2).sum(axis=0))
# 文章ベクトルの長さの積
vector_length_product = np.array([vector_length]).T.dot([vector_length])
# ドキュメント同士のコサイン類似度
cos_simularity = inner_product / vector_length_product
print(cos_simularity)
"""
d1 d2 d3 d4 d5
d1 [[1. 0.89906767 0.38600755 0.22984227 0.27158994]
d2 [0.89906767 1. 0.21784343 0.12969812 0.14303893]
d3 [0.38600755 0.21784343 1. 0.84015669 0.69879445]
d4 [0.22984227 0.12969812 0.84015669 1. 0.87536651]
d5 [0.27158994 0.14303893 0.69879445 0.87536651 1. ]]
"""
- 同じ文章同士の類似度は、完全に同じベクトルなので当然1
- $d_1, d_2$ 同士、$d_3, d_4, d_5$ 同士はかなり類似度が高い
- $d_1, d_2$ と $d_3, d_4, d_5$ はあまり類似していない
- その中でも、$d_3$ は $d_1, d_2$ と共通の単語 $t_5$ を持つので、$d_1, d_2$ への類似度が比較的高い
色々な重み付け
(ToDo)