ITの隊長のブログ

ITの隊長のブログです。Rubyを使って仕事しています。最近も色々やっているお(^ω^ = ^ω^)

TF-IDFを理解しようと頑張った

スポンサードリンク

先月ぐらいから自然言語処理頑張ろうとして入門っぽいTF-IDFをPythonから理解しようとしたけど使いどころわからんくて無事死亡。

今日は式から理解しようとしてやってみた。

対数がわからん

ぐぐってもいいけど、この本もっていたので軽く理解。

式がわからん

tfidf(t, d) = tf(t, d) \times idf(t, d)

※tf-idfって書きたいのにハイフンが引き算になるのでつらい

  • tfはあるドキュメントdの単語の出現頻度
  • idfは逆文章頻度

idf(t,d) = \log \frac{n_{d}}{1 + df(t, d)}

  • n_{d}はドキュメントの総数
  • df(t, d)は単語tを含んでいるドキュメントdの個数を表す。分母の1dfが0の場合ゼロ除算を防ぐため

scikit-learnに実装されている式は下記らしい

idf(t, d) = \log \frac{1 + n_{d}}{1 +df(t, d)}

tfidfはこちら

tfidf(t, d) = tf(t, d) \times (idf(t, d) + 1)

  • ある文章dで出現頻度が大きいtは重要である可能性が高い
  • しかし、とある文章を感情分析したい場合、肯定的、否定的な文章どちらにも同じ単語が出現することはよくある
  • そこで多くの文章中dに存在する単語tは、1つの文章の特徴としてはなりづらくしよう -> idf

ってな感じかな

idfは分母と分子が同じだと1になり、対数で計算すると0になるので、値は小さくなる(はず

なるほど。

scikit-learnで試す

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

# 形態素解析した文章毎の半角スペース区切りの単語
# surfaces = [['映画 エイリアン レビュー あらすじ'], ...]

vectorizer = TfidfVectorizer()
x = vectorizer.fit_transform(surfaces)

# ベクトル器から単語リストを取得
terms = vectorizer.get_feature_names()

# 全単語数が確認できる
print(len(terms))

# 文章毎に単語のベクトルを取得
vec_matrix = x.toarray()

# (全文章数, 全単語数)のベクトル
# vec_matrix.shape

# tfidf値が0より大きいものだけ取得する
words = []
for doc in range(len(surfaces)):
    feature_index = x.toarray()[doc, :].nonzero()[0]
    tfidf_scores = zip(feature_index, [vec_matrix[doc, x] for x in feature_index])
    for w, s in [(terms[i], s) for (i, s) in tfidf_scores]:
        words.append(w)

# 単語のカウント
word_dict = {}
for w in words:
    if w in word_dict.keys():
        word_dict[w] += 1
    else:
        word_dict[w] = 1

word_df = pd.DataFrame([[k, v] for k, v in word_dict.items()], columns=['word', 'count'])
word_df.head()

これで重要そうな単語を確認することができる?

(ちなみにこれを実装したときに確認した単語リストは前処理が甘くjs,css,htmlのタグ名が多く上がっていた。。。orz)