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

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

gensimのword2vecを使えるようにする

ネット上にあちこち記事がある。山内のメモは こんな感じ

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

日本語wikipedediaのデータをコーパスとして学習させる。

ウィキペディアの記事をコーパスとするために、形態素解析(MeCabなど)を 使って名詞だけ抜き出すことを試したが、結果があまり面白くないので、 他の人がやっているように →参照 分ち書きにするだけで済ませる。

word2vecのAPIを試す

gensimのword2vecモジュールの使い方は、チュートリアルとしては radimrehurekのページ  に書いてあるが、使い方としては学習部分と、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'剣道']

のようなものにできる。

語のベクトル空間を取り出して、色々と処理を試す

チュートリアル等では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に樹形図が表示される。

結果は、

test-capitals.png test-drinksandplates.png

左の図では、日本の都市(東京、大阪、京都)がうまくクラスタに入っていないが、 海外の都市は、都市の下に、首都のクラスタとそれ以外があり、首都にはロンドン、パリ、モスクワが入り、それ以外にはリバプール、ニューヨーク、ロサンゼルスが入っている。つまり

{都市, {首都, {ロンドン, パリ, モスクワ}},
       {リバプール, ニューヨーク, ロサンゼルス}}

という感じになっているように見える。偶然かもしれないが、かなりうまくできていると思う。

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


更にもう少し考えてみる >>> gensim-word2vecでベクトル空間を試す(2)


添付ファイル: filetest-drinksandplates.png 682件 [詳細] filetest-capitals.png 680件 [詳細]

トップ   編集 凍結 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS
Last-modified: 2015-01-13 (火) 07:19:19 (1046d)