いっきのblog

技術とか色々

word2vecの理論ついてざっくり理解しつつ試してみる

4、5年前くらいに自然言語処理コミュニティで流行ったword2vecというものがある。
「同じ文章にある単語同士は近しい」という仮定のもと、様々な文章を計算することによって100〜200次元(調整次第)の空間に各単語を「ベクトル」で表せるというものだ。
計算にはニューラルネットワークを用いている。僕には説明できるほど理解度が深まってないので、めちゃ分かりやすい記事を紹介する。

www.randpy.tokyo

こちらも幅広く説明しててわかりやすい。word2vecの何が衝撃だったかも書いてあるので興味がある方はぜ是非。

www.slideshare.net

Wikipediaからモデルを作ってみる

Wikipediaのデータの準備は以前の記事で紹介してある。

kzkohashi.hatenablog.com

比較用にipaの辞書とipa-NEologdの辞書を使う。

from gensim.models import word2vec
import logging

logging.basicConfig(format='%(asctime)s : %(levelname)s : %(message)s', level=logging.INFO)

# 普通のipa辞書
sentences = word2vec.Text8Corpus('../data/wiki_wakati.txt')
model = word2vec.Word2Vec(sentences, size=200, min_count=20, window=15)
model.save("../data/wiki.model")

# mecab-ipadic-NEologdの辞書
sentences_neo = word2vec.Text8Corpus('../data/wiki_wakati_neo.txt')
model_neo = word2vec.Word2Vec(sentences_neo, size=200, min_count=20, window=15)
model_neo.save("../data/wiki_neo.model")

RNNを利用して大量の単語を200(size)次元に圧縮する。window(文脈の最大単語数)はチューニングする上で重要らしく、ここをいじることで適切なモデルを構築が可能とのこと。

コサイン類似度から近しい単語の抽出

全単語をベクトルで表せるということは、その単語周辺にある単語も抽出できる。「エンジニア」に近しい単語を計算してみる。

from gensim.models import word2vec

model = word2vec.Word2Vec.load("../data/wiki.model")
results = model.wv.most_similar(positive=['エンジニア'])
for result in results:
    print(result)

納得できそうな単語が並んだと思う。

('プログラマー', 0.654212474822998)
('アーキテクト', 0.639656662940979)
('デザイナー', 0.6255037188529968)
('システムエンジニア', 0.6227017641067505)
('エンジニアリング', 0.6170016527175903)
('チーフ', 0.6166958212852478)
('ディレクター', 0.6148343086242676)
('技師', 0.610156774520874)
('コンサルタント', 0.6077147722244263)
('テクニカルディレクター', 0.5718519687652588)

ベクトルによる足し算引き算を行う

もちろん足し算と引き算もできる。実際に「エンジニア」から「アーキテクト」を引いてみるとどうなるかやってみる。イメージとしては、「コーダー」的なイメージだが・・。
まずはipa辞書でやると

from gensim.models import word2vec

model = word2vec.Word2Vec.load("../data/wiki.model")
results = model.wv.most_similar(positive=['エンジニア'], negative=['アーキテクト'])
for result in results:
    print(result)


('ジミ・ヘンドリックス', 0.4125749170780182)
('ミキサー', 0.39917922019958496)
('ドラム', 0.3988112211227417)
('ミックスダウン', 0.3903549015522003)
('フィーリング', 0.38688427209854126)
('ストリングス', 0.38426920771598816)
('フェンダー・ストラトキャスター', 0.3818718492984772)
('ハモンドオルガン', 0.38104212284088135)
('エントウィッスル', 0.3800022602081299)
('ソニー・ロリンズ', 0.37943196296691895)

音楽関連のワードがでてきた・・・。歌って踊れるエンジニアから、踊れるを抜いた状態なのだろうか。
次にNEologdの辞書で行ってみると


プログラマープログラマー
from gensim.models import word2vec
<200b>
model = word2vec.Word2Vec.load("../data/wiki_neo.model")
results = model.wv.most_similar(positive=['エンジニア'], negative=['アーキテクト'])
for result in results:
    print(result)

('ジャコ', 0.38521867990493774)
('苦労', 0.37028828263282776)
('ミキサー', 0.35078269243240356)
('クルー', 0.34380707144737244)
('高所恐怖症', 0.3329920172691345)
('テープレコーダー', 0.3325659930706024)
('運転手', 0.3293205499649048)
('パム', 0.32380378246307373)
('スタッフ', 0.32033008337020874)
('機材', 0.3194393217563629)

音楽関連のジャコ出会って欲しいが、2番目が「苦労」となると「雑魚」の可能性もありえるな・・・。アーキテクトができないエンジニアはダメということか。。

終わりに

僕のチューニング不足であまりよくない結果になってしまったが、word2vecがすごいことは理解できた。
次はもう少し意味のある実践したいなー。