リムナンテスは愉快な気分

徒然なるままに、言語、数学、音楽、プログラミング、時々人生についての記事を書きます

GensimでWord2Vec→単語ベクトル可視化

学習済みのやつとかではなくて、自作のコーパスを使って単語ベクトル生成したいんだ…



探しても見つからず、かといって車輪の再発明をしたいわけでもないので、

  • 文章を準備 (1)
  • 文章を単語ごとに分割、調整 (1)
  • 適当にモデルを作成 (2)
  • 単語ベクトル抽出 (2)
  • 単語ベクトル可視化 (3,4)

の手順で実装していこうかなと思います


参考文献

  1. gensim入門 - Qiita
  2. word2vecでベクトルから単語を出力する - Qiita
  3. [Python]PythonでPCAを行う方法 - Qiita
  4. https://yoshipc.net/python-word2vec/

本題:Word2Vec

とりあえずGensimインストールします
公式サイトgensim: Topic modelling for humansの通りにやればOK

pip install --upgrade gensim


解析用の文章群を用意します。今回は適当なヒュムノス文を10本用意

documents = ["Was yea ra chs hymmnos mea",
		"Rrha ki ga wis hymmnos mea endia",
		"Was ki ga endia van jenhah van ieeya van gyaje van parce van dsier van fane",
		"Was yea erra chs ieeya en sol anw yeal",
		"Ah whai rre jenhah mea en hartes",
		"Rrha yea ra rre gyaje alroen dralee wassa",
		"Was yea ra yor hartes mea",
		"Was yea erra melenas yor",
		"Was yea ra irs dsier",
		"Rrha yea ra irs an ar ciel ee",
		"Was yea ra enne ar ciel"]

真面目にベクトル表現を得るんだったらもっとデータがないと話にならんのですが、
とりあえず動作確認がしたいので妥協(この文章群、偏ってるけど)


文を単語に分割した配列にし、1回しか出現しない単語を除去します

# 単語ごとに分割
texts = [[word for word in document.lower().split()] for document in documents]

# 単語の出現回数を格納する変数を定義
from collections import defaultdict
freq = defaultdict(int)

# 出現回数カウント
for text in texts:
	for token in text:
		freq[token] += 1
		
# 1回しか出てこない単語を除去
texts = [[token for token in text if freq[token]>1] for text in texts]


textsの中身はこんな感じ。うまくいってる

[['was', 'yea', 'ra', 'chs', 'hymmnos', 'mea'], 
['rrha', 'ki', 'ga', 'hymmnos', 'mea', 'endia'], 
['was', 'ki', 'ga', 'endia', 'van', 'jenhah', 'van', 'ieeya', 'van', 'gyaje', 'van', 'van', 'dsier', 'van'], 
['was', 'yea', 'erra', 'chs', 'ieeya', 'en'], 
['rre', 'jenhah', 'mea', 'en', 'hartes'], 
['rrha', 'yea', 'ra', 'rre', 'gyaje'], 
['was', 'yea', 'ra', 'yor', 'hartes', 'mea'], 
['was', 'yea', 'erra', 'yor'], 
['was', 'yea', 'ra', 'irs', 'dsier'], 
['rrha', 'yea', 'ra', 'irs', 'ar', 'ciel'], 
['was', 'yea', 'ra', 'ar', 'ciel']]


今回は単語のベクトル表現が欲しいので、適当にモデル作って学習させます。

from gensim.models import Word2Vec
model = Word2Vec(texts, size=5, window=2, min_count=1, workers=4)
model.save("hymmnos-trial.model")

総単語数が23なので、sqrt(23)≒5次元くらいにしておきますか
モデルの保存も忘れずに

単語ベクトル図示

さて、単語ベクトルの分布を可視化していきましょう。


学習モデルはWord2Vec.loadでインポートできます

from gensim.models import Word2Vec
model = Word2Vec.load("hymmnos-trial.model")


適当に単語リストを用意して全単語の単語ベクトルを出力
model.wv["単語"]でベクトル表現が手に入ります

wordlist = ["ar", "chs", "ciel", "dsier", "en", "endia", "erra", "ga", "gyaje", "hartes", "hymmnos", "ieeya", "irs", "jenhah", "ki", "mea", "ra", "rre", "rrha", "van", "was", "yea", "yor"]

for word in wordlist:
	print(word, model.wv[word])


今回のWord2Vecのモデルは中間層を5次元で構築しているので、
各単語は5次元のベクトルに変換されます

ar [ 0.02454851 -0.00128271 -0.055499    0.08232712 -0.09648794]
chs [ 0.05779681 -0.0439393  -0.07342346  0.0246091  -0.02231402]
ciel [-0.07806355  0.05745706 -0.05647837 -0.03801439 -0.03791884]
dsier [-0.09228735 -0.09905402 -0.05883621 -0.01603309 -0.01012423]
en [ 0.00060984 -0.02540413 -0.04015537  0.03778287 -0.05832371]
endia [ 0.00751746 -0.00378113 -0.08876722 -0.09292331  0.08464006]
erra [ 0.06206524 -0.09285549  0.0396496   0.04882227 -0.07579137]
ga [ 0.00809174 -0.08935694  0.07457921  0.09508897  0.09761995]
gyaje [-0.08156616  0.08354305 -0.04328651  0.05435437 -0.01419817]
hartes [-0.09996795 -0.02508386 -0.09044719  0.09598456  0.03360585]
hymmnos [ 0.09589075 -0.05700874 -0.08678991  0.08558236 -0.01223231]
ieeya [ 0.03692123  0.06536245 -0.05062268 -0.0394774  -0.013169  ]
irs [-0.0865057  -0.02802126 -0.03512951  0.01488029  0.04170462]
jenhah [-0.07503235 -0.08040676  0.00842145  0.08396688  0.07737341]
ki [-0.0629393   0.06792223 -0.02068239 -0.01287407 -0.06384669]
mea [0.02421383 0.03830793 0.02390544 0.01545416 0.08571503]
ra [-0.08417609  0.04572209  0.03236094  0.02229685 -0.06588703]
rre [ 0.07176235 -0.09135775  0.06975248  0.0280516   0.09277304]
rrha [ 0.01479494 -0.06536575 -0.00520885 -0.00092281  0.0964323 ]
van [-0.06531225 -0.04369221 -0.01532807  0.05799453  0.08299494]
was [-0.07311312 -0.00192154  0.01082229 -0.03476359 -0.02584441]
yea [-0.03307789 -0.02687487 -0.08275346 -0.08865387 -0.02860719]
yor [ 0.04845942  0.063347    0.0518582  -0.05352921 -0.06058975]

うーんこれうまく行ってるのかなぁ…wasとrrhaとかは近くなって欲しかったんだけど
(教師データが少ないのでそれはそう)


可視化するにあたり、主成分分析(PCA)で2次元空間に射影しましょう

# 主成分分析
from sklearn.decomposition import PCA
pca = PCA(n_components=2)
pca.fit(wordvecs)

# 射影
X_2d = pca.transform(wordvecs)
print(X_2d)


というわけで各単語ベクトルが2次元平面に射影されます

[[-0.04121369  0.07317596]
 [ 0.0106073   0.06933402]
 [-0.1155983  -0.03813237]
 [-0.00155561 -0.06463629]
 [-0.02345363  0.03930333]
 [-0.0125354  -0.0196008 ]
 [ 0.04904095  0.1125973 ]
 [ 0.16184334 -0.00886189]
 [-0.07956163 -0.05379281]
 [ 0.01188891 -0.10033859]
 [ 0.05431194  0.09376851]
 [-0.07331329  0.05327452]
 [ 0.00625386 -0.08024172]
 [ 0.09980441 -0.08258564]
 [-0.11392453 -0.00900584]
 [ 0.03886772  0.00681035]
 [-0.08000455 -0.02049518]
 [ 0.15081129  0.05168203]
 [ 0.09070683 -0.01008471]
 [ 0.06754438 -0.07884793]
 [-0.05217438 -0.02932637]
 [-0.07357465 -0.00444022]
 [-0.07477127  0.10044434]]


実際に図にしてみましょう

# 可視化
import matplotlib.pyplot as plt

wordlen = len(wordlist)
for i in range(wordlen):
	plt.plot(X_2d[i][0], X_2d[i][1], ms=5.0, zorder=2, marker='o')
	plt.annotate(wordlist[i], (X_2d[i][0], X_2d[i][1]))
	
plt.show()

f:id:frecafloros:20190522160651p:plain

一応これで単語ベクトルの学習と可視化ができるようになりました