読者です 読者をやめる 読者になる 読者になる

中年engineerの独り言 - crumbjp

LinuxとApacheの憂鬱

自然言語解析 in MONMO(中編)

一連の自然言語処理MONMOちゃん上で実現する試みの第2弾
前回は形態素解析まで行った。

今回は、形態素解析結果から、そのドキュメントの特徴を表す『ベクトル』を算出する、ベクタライズを行う。

TF-IDF

自然言語処理における代表的なベクタライズ手法。


考え方
  1. ドキュメント中、何回も出現する単語はそのドキュメントを表す重要な単語である。
  2. 多くのドキュメント中に出現する単語は普遍的な単語なので重要ではない。

シンプルだ。


TF-IDFの要素
N
総ドキュメント数
TF[a]
ある単語(a)がその1ドキュメント中に現れた回数
DF[a]
ある単語が現れたドキュメント数
IDF[a]
log( N / DF[a] )
TF-IDF[a]
TF[a] x IDF[a]
TF-IDFの例

私はそれを読みたい。
私はそれを書きたい。

"私はそれを読みたい。"={
  "私"   : 0,
  "は"   : 0,
  "それ" : 0,
 "を"   : 0,
  "読み" : 0.3,
  "たい" : 0
}
"私はそれを書きたい。"={
  "私"   : 0,
  "は"   : 0,
  "それ" : 0,
 "を"   : 0,
  "書き" : 0.3,
  "たい" : 0
}
  • N=2
  • 『私』『は』『それ』『を』『たい』のIDFはlog(2/2) = log(1) = 0; よってTF-IDFも0となる。
  • 『読み』『書き』のIDFはlog(2/1) = log(2) = 約0.3;
  • 『読み』『書き』のTFは、それぞれ1

これはあくまで例。
実際はDF=1 の単語は他のドキュメントと結び付けようが無く、無視して良い。
(処理量を減らすために積極的に削るべき)

そんな訳で

今回も、これをMONMOちゃんのMAPジョブを使って大量(?)のドキュメントと品詞を並列に処理する。


monmo-NLProcessing/vectorize

vectorize
https://github.com/monmo/monmo-NLProcessing/blob/master/vectorize/README
処理順
  1. tokenize結果からTFを算出
  2. TFからDFを算出
  3. DFからIDFを算出
  4. TF & IDF からTIF-IDFを算出

MongoDBの特性を考慮すると、この順が一番効率が良いだろう。


ベクタライズ準備

前回の手順で完了している。
ディレクトリを移動するだけ

cd monmo-NLProcessing/vectorize
ベクタライズ(簡易版)
./vectorize.sh -s test.token.sampledoc
文書検索
TF結果を使うと高度な検索が出来る。
./fulltext_search.sh -s test.vector.tf.token.sampledoc -w 'はさみや糊など' -V
= META =
{
        "dic" : "analysis.dictionary",
        "doc" : "test.sampledoc",
        "doc_field" : "body",
        "docs" : 73,
        "normalize" : true,
        "tf" : "test.vector.tf.token.sampledoc",
        "token" : "test.token.sampledoc",
        "type" : "TF"
}
= DIC =
5212ed32b399b667b8567608 => はさみ
5212ed33b399b667b85685b3 => や
5212ed55b399b667b858df1f => 糊
5212ed32b399b667b85671f3 => など
= QUERY =
{
        "value.w" : {
                "$all" : [
                        "5212ed32b399b667b8567608",
                        "5212ed33b399b667b85685b3",
                        "5212ed55b399b667b858df1f",
                        "5212ed32b399b667b85671f3"
                ]
        }
}
= DOCS =
[ ObjectId("51e64d60c507ed1f43d21400") ]
= VERBOSE =
 * 51e64d60c507ed1f43d21400 : 折り紙出典:フリー百科事典『ウィキペディア(Wikipedia)』移動:案内、検索この項目では、紙を折る遊びについて記述しています。"折紙"、"折り紙"の他の用
ベクタライズ(正規手順)

  1.TF

./tf.sh    -s test.token.sampledoc            -o test.vector.tf.token.sampledoc

  2.DF

./df.sh    -s test.vector.tf.token.sampledoc  -o test.vector.df.token.sampledoc

  3.IDF ※要チューニング

./idf.sh   -s test.vector.df.token.sampledoc  -o test.vector.idf.token.sampledoc

  4.TF-IDF

./tfidf.sh -s test.vector.idf.token.sampledoc -o test.vector.tfidf.token.sampledoc
IDFチューニング

このフェーズで色々なチューニングをする。

  1.DFとIDFを確認する。

./view_df.sh -s test.vector.df.token.sampledoc
./view_df.sh -s test.vector.idf.token.sampledoc

  2.上を確認しながら、limit,threshold,verb-onlyの値を調整する。

limit
(DF / 総ドキュメント数)の最大値
threshold
DFの最小値
verb-only
名詞だけ抽出
切り捨て過ぎの場合はlimit値を下げる
./idf.sh --limit 0.3   -s test.vector.df.token.sampledoc  -o test.vector.idf.token.sampledoc
もっと切り捨てたい場合はlimit値を上げる
./idf.sh --limit 0.5   -s test.vector.df.token.sampledoc  -o test.vector.idf.token.sampledoc
名詞だけを評価する(大抵の場合、条件を緩くした方が良い)
./idf.sh --limit 0.3 --verb-only -s test.vector.df.token.sampledoc  -o test.vector.idf.token.sampledoc

  3.結果が良くなるまで繰り返す

  4.TF-IDFを再産出

./tfidf.sh -s test.vector.idf.token.sampledoc -o test.vector.tfidf.token.sampledoc

まとめ

これで、ドキュメントをベクトル化する所まで出来たMONMOちゃん
ベクトルはドキュメントの特徴を数学的にパラメータ化したもので、後は数学的手法によって色々な使い方が出来る。

次回はベクトルのクラスタリングを行う予定。
ドキュメントの関連度や、グループ化などが出来る!!

修正

TF=1 => DF=1 の単語は・・・

ご指摘ありがとうございます!!