[[ノート/テキストマイニング]]

*gensimのword2vecで語のベクトル表現を作り、その空間を試してみたい (2015-01-08) [#fd1e9e1b]

**gensimのword2vecを使えるようにする [#dfce7bf8]
ネット上にあちこち記事がある。山内のメモは [[こんな感じ>ノート/テキストマイニング/gensimでLSI, LDAを試す]]

似たような話として [[Python - Perl + Java = ? はてなブログのデータとパソコン工房のPCを使って「word2vec」で遊んでみた:http://hatenanews.com/articles/201404/20050]] コーパスが圧倒的に強い。

**日本語wikipedediaのデータをコーパスとして学習させる。 [#g1c27459]
ウィキペディアの記事をコーパスとするために、形態素解析(MeCabなど)を
使って名詞だけ抜き出すことを試したが、結果があまり面白くないので、
他の人がやっているように [[→参照:http://moguranosenshi.hatenablog.com/entry/2014/04/01/word2vec%E3%81%A7%E6%84%8F%E5%91%B3%E3%81%AE%E8%B6%B3%E3%81%97%E5%BC%95%E3%81%8D]] 分ち書きにするだけで済ませる。

**word2vecのAPIを試す [#re6cc4be]
gensimのword2vecモジュールの使い方は、チュートリアルとしては [[radimrehurekのページ:http://radimrehurek.com/2014/02/word2vec-tutorial/]]  に書いてあるが、使い方としては学習部分と、similarityとかだけである。
もう一歩突っ込んだ記述は APIのマニュアルページ にある。

学習のためのプログラムは、チュートリアルにある通りのものを使う。
 #!/usr/bin/env python
 # -*- coding: utf-8 -*-
 import gensim, logging
 import sys
 import string, codecs
 sys.stdout = codecs.getwriter('utf_8')(sys.stdout)
 
 logging.basicConfig(format='%(asctime)s : %(levelname)s : %(message)s', level=logging.INFO)
 sentences = gensim.models.word2vec.Text8Corpus("wp2txt/wp2wakati.txt")
 
 # train word2vec on the two sentences
 model = gensim.models.word2vec.Word2Vec(sentences, workers=28, min_count=5)
 print "model gen complete"
 model.save("jpw-wakati-model")

チュートリアルに出てくる、テスト部分のプログラムは、
 #!/usr/bin/env python
 # -*- coding: utf-8 -*-
 import gensim, logging
 import sys
 import string, codecs
 sys.stdout = codecs.getwriter('utf_8')(sys.stdout)
 
 logging.basicConfig(format='%(asctime)s : %(levelname)s : %(message)s', level=logging.INFO)
 
 model = gensim.models.word2vec.Word2Vec.load("jpw-wakati-model")
 print "model load complete"
 
 out = model.most_similar(positive=[u'パリ', u'フランス'], negative=[u'ベルリン'], topn=5)
 out = model.most_similar(positive=[u'剣道'], negative=[u'剣'], topn=5)
 
 out = model.most_similar(positive=[u'日本', u'ロンドン'], negative=[u'東京'], topn=5)
 for x in out:
   for y in x:
     print y, '\t',
   print
 
 print model[u'剣道']
のようなものにできる。


**語のベクトル空間を取り出して、色々と処理を試す [#gc4dfbc2]
チュートリアル等ではsimilarityを中心にしているが、ベクトル空間を直接に見れば、もう少しいろいろがこと(たとえば距離を計算してクラスタ化してみるとか)が試せるのではないか。

2つの要素が欲しい。登録されている語(vocaburary)を取り出すことと、それに対応するベクトルを取り出すことである。もちろん、全データがタイトル付きの1つの配列(ExcelやRの表のように)になっていれば、それを使えばいいのだが、そのような記述は見当たらないので、自分で取り出してみる。(余分な手間をかけているかもしれない)

まず、登録されている語のリストを取り出すには、
 voc = model.vocab.keys()
のようにすればできる。

また、特定の語 x に対するベクトルを取り出すには、
 wvec = model[x]
とすればよい。但し、その語が登録されていないと、KeyErrorを返すので、例外対策をしておいたほうがいい。
 wv = []; vocnew = []
 for x in voc:
   try:
     wv.append(model[x])
   except KeyError:
     print x, u'を無視します'
   vocnew.append(x)

では、これらを使って語のクラスタ化を試してみる。まずは、特定の幾つかの語に対して、ベクトル間のユークリッド距離(必要に応じて距離関数は変えてもいい)に基づいて、クラスタ化してみる。

クラスタ化は、階層的クラスタ化と階層なしのクラスタ化があるが、まず興味があるのは、語の間の関係(たとえばis-a関係、包含関係)がベクトルクラスタの階層として現れるのかどうか、についてである。となると、階層的クラスタ化をして見ることになる。 (階層なしの場合、クラスタ数をあらかじめ指定しなければならず、これはちょっと頭が痛い。AICとかを用いる話があるが、それはさておくことにする。)

 #!/usr/bin/env python
 # -*- coding: utf-8 -*-
 ###############
 # jpwiki --> word2vec --> vectors --> hierarchical clustering
 ###############
 import gensim, logging
 import sys
 import string, codecs
 sys.stdout = codecs.getwriter('utf_8')(sys.stdout)
 
 #########################
 # python/scipyを使って階層的クラスタリング+系統樹を描く
 #   参照: http://openbook4.me/users/1/sections/783
 #   マニュアルは: http://docs.scipy.org/doc/scipy/reference/generated/scipy.cluster.hierarchy.linkage.html#scipy.cluster.hierarchy.linkage
 #########################
 import scipy.spatial.distance
 import scipy.cluster.hierarchy
 import matplotlib
 from matplotlib.pyplot import show
 font = {'family': 'VL Gothic',
         'size': '9' }
 matplotlib.rc('font', **font)
 
 logging.basicConfig(format='%(asctime)s : %(levelname)s : %(message)s', level=logging.INFO)
 
 model = gensim.models.word2vec.Word2Vec.load("jpw-wakati-model")
 print "model load complete"
 
 # Use a handmade vocaburary list (to get clustered)
 # In next step, use the entire registered vocaburary by 
 #voc = model.vocab.keys()
 wv = []
 vocnew = []
 #voc = [u'首都', u'東京', u'パリ', u'ロンドン', u'モスクワ', u'都市', u'大阪', 
 #  u'京都', u'ニューヨーク', u'ロサンゼルス', u'リバプール']
 #voc = [u'動物', u'イヌ', u'ネコ', u'ブタ', u'サル', 
 #  u'植物', u'サクラ', u'バラ', u'イネ']
 voc = [u'ビール', u'日本酒', u'焼酎', u'蕎麦', u'スパゲッティ', 
   u'ハンバーグ', u'カレー', u'バラ', u'桜']
 
 for x in voc:
   try:
     wv.append(model[x])
   except KeyError:
     print x, u'を無視します'
   vocnew.append(x)
 
 # linkage配列を作る
 #l = scipy.cluster.hierarchy.linkage(d)
 #l = scipy.cluster.hierarchy.linkage(wv, method='ward')
 l = scipy.cluster.hierarchy.linkage(wv, method='average')
 print l
 
 # dentrogramに表現する
 scipy.cluster.hierarchy.dendrogram(l, labels=vocnew)
 show()

matplotlibによりたとえばXWindowに樹形図が表示される。

結果は、

&ref(test-capitals.png,,400x400);
&ref(test-drinksandplates.png,,400x400);

左の図では、日本の都市(東京、大阪、京都)がうまくクラスタに入っていないが、
海外の都市は、都市の下に、首都のクラスタとそれ以外があり、首都にはロンドン、パリ、モスクワが入り、それ以外にはリバプール、ニューヨーク、ロサンゼルスが入っている。つまり
 {都市, {首都, {ロンドン, パリ, モスクワ}},
        {リバプール, ニューヨーク, ロサンゼルス}}
という感じになっているように見える。偶然かもしれないが、かなりうまくできていると思う。

それに反して右の図は、かなりうまくいっていない。ward法で分類している。
(ビール、日本酒、焼酎)と、(スパゲッティ、カレー、ハンバーグ、蕎麦)と(桜、バラ)がグループになることを期待したのだが、(蕎麦、焼酎)が近くにあったりして、何が起こっているのかよくわからない。「蕎麦焼酎」にでもひっかかったのだろうか?

------

更にもう少し考えてみる >>> [[ノート/テキストマイニング/gensim-word2vecでベクトル空間を試す(2)]]
更にもう少し考えてみる >>> [[gensim-word2vecでベクトル空間を試す(2)>ノート/テキストマイニング/gensim-word2vecでベクトル空間を試す(2)]]

トップ   編集 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS