言語処理100本ノック 2015の挑戦記録です。環境はUbuntu 16.04 LTS + Python 3.5.2 :: Anaconda 4.1.1 (64-bit)です。過去のノックの一覧はこちらからどうぞ。
第10章: ベクトル空間法 (II)
第10章では,前章に引き続き単語ベクトルの学習に取り組む.
###92. アナロジーデータへの適用
91で作成した評価データの各事例に対して,vec(2列目の単語) - vec(1列目の単語) + vec(3列目の単語)を計算し,そのベクトルと類似度が最も高い単語と,その類似度を求めよ.求めた単語と類似度は,各事例の末尾に追記せよ.このプログラムを85で作成した単語ベクトル,90で作成した単語ベクトルに対して適用せよ.
####出来上がったコード:
# coding: utf-8
import pickle
from collections import OrderedDict
from scipy import io
import numpy as np
fname_dict_index_t = 'dict_index_t'
fname_matrix_x300 = 'matrix_x300'
fname_input = 'family.txt'
fname_output = 'family_out.txt'
def cos_sim(vec_a, vec_b):
'''コサイン類似度の計算
ベクトルvec_a、vec_bのコサイン類似度を求める
戻り値:
コサイン類似度
'''
norm_ab = np.linalg.norm(vec_a) * np.linalg.norm(vec_b)
if norm_ab != 0:
return np.dot(vec_a, vec_b) / norm_ab
else:
# ベクトルのノルムが0だと似ているかどうかの判断すらできないので最低値
return -1
# 辞書読み込み
with open(fname_dict_index_t, 'rb') as data_file:
dict_index_t = pickle.load(data_file)
keys = list(dict_index_t.keys())
# 行列読み込み
matrix_x300 = io.loadmat(fname_matrix_x300)['matrix_x300']
# 評価データ読み込み
with open(fname_input, 'rt') as data_file, \
open(fname_output, 'wt') as out_file:
for line in data_file:
cols = line.split(' ')
try:
# ベクトル計算
vec = matrix_x300[dict_index_t[cols[1]]] \
- matrix_x300[dict_index_t[cols[0]]] \
+ matrix_x300[dict_index_t[cols[2]]]
# コサイン類似度の一番高い単語を抽出
dist_max = -1
index_max = 0
result = ''
for i in range(len(dict_index_t)):
dist = cos_sim(vec, matrix_x300[i])
if dist > dist_max:
index_max = i
dist_max = dist
result = keys[index_max]
except KeyError:
# 単語がなければ0文字をコサイン類似度-1で出力
result = ''
dist_max = -1
# 出力
print('{} {} {}'.format(line.strip(), result, dist_max), file=out_file)
print('{} {} {}'.format(line.strip(), result, dist_max))
####実行結果:
結果は「family_out.txt」に保存します。処理に時間がかかるので、ファイルだけでなく画面にも同じ内容を出力するようにしました。実行時間は手元のパソコンで問題85の単語ベクトルが1時間50分、問題90の単語ベクトルが25分くらいでした。もう少し早くする方法はないものだろうか...
以下、問題90に対する結果の先頭部分です。
boy girl brother sister brother 0.9401630421547305
boy girl brothers sisters brothers 0.8945072765275828
boy girl dad mom girl 0.7280971994658558
boy girl father mother father 0.9436608943376003
boy girl grandfather grandmother grandfather 0.8252139862667345
boy girl grandpa grandma -1
boy girl grandson granddaughter granddaughter 0.8146889309237173
boy girl groom bride girl 0.7017715459762993
boy girl he she he 0.9651317504873835
boy girl his her his 0.9587287668802774
boy girl husband wife husband 0.9468113068648676
boy girl king queen king 0.9286736850752637
boy girl man woman man 0.9452997293828569
boy girl nephew niece niece 0.8271499425140075
boy girl policeman policewoman girl 0.7420750545104479
boy girl prince princess prince 0.7707574165422014
boy girl son daughter son 0.9564752654538731
boy girl sons daughters sons 0.9366514358470139
boy girl stepbrother stepsister -1
boy girl stepfather stepmother girl 0.680540253333323
(以下略)
###アナロジーデータへの適用
この問題でやることは問題89とほぼ同じです。問題91で作った評価用の単語に対してこれを繰り返します。
なお、問題91で作った評価用の単語が単語ベクトルに含まれていない場合があります。wikipediaのサンプリングデータで使われていない単語は含まれていませんし、word2vecでは低頻出語を除去する指定にしているので、そのような単語も含まれません。含まれない単語は計算できないため、結果の単語は空文字列を、類似度は-1を出力するようにしています。
上の実行結果の先頭部分は問題90で作ったword2vecの単語ベクトルなのですが、この先頭部分だけ見るとうまくいってるのは「boy girl grandson granddaughter」と「boy girl nephew niece」くらいで、他はほとんど間違ってますね...
正解率は次の問題93で算出します。
93本目のノックは以上です。誤りなどありましたら、ご指摘いただけますと幸いです。
実行結果には、100本ノックで用いるコーパス・データで配布されているデータの一部が含まれます。この第10章で用いているコーパス・データのライセンスはクリエイティブ・コモンズ 表示-継承 3.0 非移植(日本語訳)です。また、単語アナロジーの評価データはApache License 2.0で配布されているword2vecの一部です。