言語処理100本ノック 2015の89本目「加法構成性によるアナロジー」の記録です。
「加法構成性」ということでベクトル演算をして結果を求めます。有名な「王 + 女性 - 男性 = 王女」の計算ですね。「上司 - 有能 = ?」のような計算で、世の中のいろいろなことで試してみたい演算です。
参考リンク
リンク | 備考 |
---|---|
089.加法構成性によるアナロジー.ipynb | 回答プログラムのGitHubリンク |
素人の言語処理100本ノック:89 | 言語処理100本ノックで常にお世話になっています |
環境
種類 | バージョン | 内容 |
---|---|---|
OS | Ubuntu18.04.01 LTS | 仮想で動かしています |
pyenv | 1.2.15 | 複数Python環境を使うことがあるのでpyenv使っています |
Python | 3.6.9 | pyenv上でpython3.6.9を使っています 3.7や3.8系を使っていないことに深い理由はありません パッケージはvenvを使って管理しています |
上記環境で、以下のPython追加パッケージを使っています。通常のpipでインストールするだけです。
種類 | バージョン |
---|---|
numpy | 1.17.4 |
pandas | 0.25.3 |
課題
第9章: ベクトル空間法 (I)
enwiki-20150112-400-r10-105752.txt.bz2は,2015年1月12日時点の英語のWikipedia記事のうち,約400語以上で構成される記事の中から,ランダムに1/10サンプリングした105,752記事のテキストをbzip2形式で圧縮したものである.このテキストをコーパスとして,単語の意味を表すベクトル(分散表現)を学習したい.第9章の前半では,コーパスから作成した単語文脈共起行列に主成分分析を適用し,単語ベクトルを学習する過程を,いくつかの処理に分けて実装する.第9章の後半では,学習で得られた単語ベクトル(300次元)を用い,単語の類似度計算やアナロジー(類推)を行う.
なお,問題83を素直に実装すると,大量(約7GB)の主記憶が必要になる. メモリが不足する場合は,処理を工夫するか,1/100サンプリングのコーパスenwiki-20150112-400-r100-10576.txt.bz2を用いよ.
今回は*「1/100サンプリングのコーパスenwiki-20150112-400-r100-10576.txt.bz2」*を使っています。
89. 加法構成性によるアナロジー
85で得た単語の意味ベクトルを読み込み,vec("Spain") - vec("Madrid") + vec("Athens")を計算し,そのベクトルと類似度の高い10語とその類似度を出力せよ.
回答
回答プログラム 089.加法構成性によるアナロジー.ipynb
import numpy as np
import pandas as pd
# 保存時に引数を指定しなかったので'arr_0'に格納されている
matrix_x300 = np.load('085.matrix_x300.npz')['arr_0']
print('matrix_x300 Shape:', matrix_x300.shape)
group_t = pd.read_pickle('./083_group_t.zip')
# 'vec("Spain") - vec("Madrid") + vec("Athens") のベクトル計算
vec = matrix_x300[group_t.index.get_loc('Spain')] \
- matrix_x300[group_t.index.get_loc('Madrid')] \
+ matrix_x300[group_t.index.get_loc('Athens')]
vec_norm = np.linalg.norm(vec)
# コサイン類似度計算
def get_cos_similarity(v2):
# ベクトルが全てゼロの場合は-1を返す
if np.count_nonzero(v2) == 0:
return -1
else:
return np.dot(v1, v2) / (v1_norm * np.linalg.norm(v2))
cos_sim = [get_cos_similarity(matrix_x300[i]) for i in range(len(group_t))]
print('Cosign Similarity result length:', len(cos_sim))
# インデックスを残してソート
cos_sim_sorted = np.argsort(cos_sim)
# 昇順でソートされた配列の1番最後から-10(-11)までを1件ずつ出力
for index in cos_sim_sorted[:-11:-1]:
print('{}\t{}'.format(group_t.index[index], cos_sim[index]))
回答解説
今回のメインの部分です。
ただ足し算、引き算をしているだけです。
# 'vec("Spain") - vec("Madrid") + vec("Athens") のベクトル計算
vec = matrix_x300[group_t.index.get_loc('Spain')] \
- matrix_x300[group_t.index.get_loc('Madrid')] \
+ matrix_x300[group_t.index.get_loc('Athens')]
最後の出力結果です。
Spainから首都のマドリッドを引いて、アテネを足しているので、意味としてはギリシャが正解なのでしょうか。
ギリシャは12位でコサイン類似度は0.686でした。
Spain 0.8178213952646727
Sweden 0.8071582503798717
Austria 0.7795030693787409
Italy 0.7466099164394225
Germany 0.7429125848677439
Belgium 0.729240312232219
Netherlands 0.7193045612969573
Télévisions 0.7067876635156688
Denmark 0.7062857691945504
France 0.7014078181006329