2016年4月20日水曜日

文書の類似度

前回、tf-idfを使って、文書に含まれる語句の出現頻度から重み付けを行いました。これは、文書の特徴を抽出するためということでした。「風の又三郎」と「セロ弾きのゴーシュ」で名詞を抜き出し、tf-idfの値を導出しましたが、まあ、両者で違いがあることがわかったという程度で、両者が明示的に比較されたわけではありませんでした。両者の違いというのは、両者がどれだけ似ていないかということにもなります。そこで、両者の類似度を求めれば、明示的に比較したことになります。
類似度を計算するときによく用いられる指標は相関です。でも、テキスト処理の分野だと、コサイン類似度という指標の方がよく用いられます。で、コサイン類似度というのは、文書をベクトルで表しておいて、そのベクトルどうしの関係を表現します。以下の式で表されます。
cos(x, y) = x・y / |x||y| = {x / |x|}・{y / |y|}
x, yはそれぞれ、文書ベクトルx = (x_1, x_2, ..., x_n), y = (y_1, y_2, ..., y_n)を表します。ベクトルの要素は共にn個あります。で、前回まで求めてきたtf-idfの結果を、上式に代入します。文書ベクトルの要素は、各文書の単語に対するtf-idfの値です。
文書の類似度を、Rを使って実際に求めてみましょう。前回、「セロ弾きのゴーシュ」を読み込みました。そこまではできているとします。で、新たに「注文の多い料理店」を読み込みます。
> menu <- Aozora(url = "https://www.aozora.gr.jp/cards/000081/files/
43754_ruby_17594.zip")
> library(RMeCab)
> listTFIDF <- docMatrix("NORUBY", pos=c("名詞"), weight = "tf*idf*norm" )
> head(listTFIDF, 20)
              docs
terms          chumonno_oi_ryoriten2.txt kazeno_matasaburo2.txt serohikino_goshu2.txt
  HOUSE                      0.023302018            0.000000000           0.000000000
  RESTAURANT                 0.023302018            0.000000000           0.000000000
  WILDCAT                    0.023302018            0.000000000           0.000000000
  あいつ                     0.014287567            0.004440664           0.000000000
  あすこ                     0.023302018            0.000000000           0.000000000
  あたりまえ                 0.014287567            0.002220332           0.000000000
  あっち                     0.014287567            0.006660996           0.000000000
  あと                       0.009014451            0.023814849           0.021455069
  あなた                     0.018028902            0.001400873           0.006130020
  イギリス                   0.023302018            0.000000000           0.000000000
  いっしょ                   0.009014451            0.014008735           0.009195030
  いも                       0.023302018            0.000000000           0.000000000
  いらっしゃい               0.139812109            0.000000000           0.000000000
  うし                       0.027043354            0.030819216           0.003065010
  うち                       0.009014451            0.015409608           0.024520079
  オーバー                   0.023302018            0.000000000           0.000000000
  おなか                     0.023302018            0.000000000           0.000000000
  おまけ                     0.014287567            0.002220332           0.000000000
  お客                       0.069906055            0.000000000           0.000000000
  お見舞                     0.023302018            0.000000000           0.000000000
>
今回、docMatrixの引数weightをtf*idf*normとしています。normというのは正規化を意味していまして、各文書(列)の要素を2乗して合計すると1になります。これは、類似度を計算するとき、都合がよいです。先に示した類似度の式では|x|および|y|が1になるということですから、文書ベクトルの内積を求めれば済むことになります。
> v1 <- as.vector(listTFIDF[,1])
> v2 <- as.vector(listTFIDF[,2])
> v3 <- as.vector(listTFIDF[,3])
>
一列目を取り出してベクトルv1、二列目をv2、三列目をv3とします。そして、それぞれのベクトルの内積を取ります。Rでは、ベクトルの内積は %*% を使います。単なる * だけだと、要素毎のかけ算になることに注意します。
> s12 <- v1 %*% v2
> s12
          [,1]
[1,] 0.1672723
> s23 <- v2 %*% v3
> s23
          [,1]
[1,] 0.1438562
> s31 <- v3 %*% v1
> s31
          [,1]
[1,] 0.1510847
>
この結果からは、どの文書も同等程度に似ていないということのようです。

2016年4月9日土曜日

tf-idf

前回は、文書を分かち書きして形容詞を取り出し、その出現頻度を調べました。結果を見てわかると思いますが、案外、形容詞ってその文書の特徴を捉えているんですね。ということは、文書に出現する語句を調べると、その文書の特徴がわかるのではないか、という推測が成り立つわけです。これは直感的にも納得できます。例えば、あるの文書があったとして、その中に含まれる単語として「企業」とか「株価」とか「雇用」とかが含まれれば、その話題は経済かな、と推定されます。でも、単語の出現回数を数えただけだと、都合の悪いことがあります。どの文書にも出てくる単語があった場合、それがその文書を特徴付けるとは思われません。例えば、「彼」とか「それ」とかの語句は、文章を構成するとき頻出します。どの文書にも出てくるので、その文書を特徴付けるものにはなりません。
したがって、文書を特徴付けるのは、他の文書にあまり出てこない単語が何回も出てくる場合ということになります。そこで、そうした語句に重みを付けるようにします。このような手法の一つにtf-idfというのがあります。文書中の単語への重みづけを行い、文書の特徴を抽出するのに使われます。
tfはterm frequencyの頭文字をとったもので、単語の出現頻度を表します。定義は、tf =(文書dに含まれる単語tの出現頻度)/(文書dに含まれる全単語の出現頻度の和)です。idfはinverse document frequencyの頭文字をとったもので、多くの文書に出現する単語の重みを下げます。定義は、idf = log{(全文書数)/(単語tが出現する文書の数)}です。logを使っているのは、文書数に対する影響を少なくするためです。あと、log 0を避けるため、1を加える措置を施すことがあります。で、両者をかけ合わせたものが、tf-idfになります。
では、Rで試してみましょう。まず、以下のコマンドを打ち込んでみて下さい。ただし、前回やった青空文庫の「風の又三郎」が、ディレクトリ名NORUBYの中に「ルビなしファイル」として置いてあるものとします(前回のkazeまでやってある状態)。で、新たに「セロ弾きのゴーシュ」を読み込んで、両者を比較してみます。
> celo <- Aozora(url = "https://www.aozora.gr.jp/cards/000081/files/470_ruby_3987.zip")
> library(RMeCab)
> listTFIDF <- docMatrix("NORUBY", pos=c("名詞"), weight = "tf*idf" )
> head(listTFIDF, 20)
              docs
terms          kazeno_matasaburo2.txt serohikino_goshu2.txt
  アアハハハ                        2                     0
  アアハハハハ                      2                     0
  あい                             16                     0
  あいだ                            2                     0
  あいつ                            4                     0
  あかし                            2                     1
  あぐら                            2                     0
  あざみ                            4                     0
  あざらし                          2                     0
  あし                              2                     0
  あした                            4                     0
  あたり                            7                     2
  あたりまえ                        2                     0
  あちこち                          8                     0
  あっち                            6                     0
  あと                             17                     7
  あなた                            1                     2
  アルプス                          2                     0
  あん                              2                     0
  い                               12                     0
>

名詞だけを抽出して、各単語の重みを求めました。それぞれの作品では、どんな単語が重みづけられているでしょうか。注意点は、docMatrixはリスト形式で返されるということです。