言語処理100本ノック 2015の88本目「類似度の高い単語10件」の記録です。
すべての単語から似たやつを抜き出します。これまた、自分のメールボックスや議事録等からやってみたい処理ですね。
技術的には前回の内容とほぼ同じです。
参考リンク
リンク | 備考 |
---|---|
088.類似度の高い単語10件.ipynb | 回答プログラムのGitHubリンク |
素人の言語処理100本ノック:88 | 言語処理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」*を使っています。
88. 類似度の高い単語10件
85で得た単語の意味ベクトルを読み込み,"England"とコサイン類似度が高い10語と,その類似度を出力せよ.
回答
回答プログラム 088.類似度の高い単語10件.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)
# 'Englandの単語ベクトルを読み込み、ノルムも計算
v1 = matrix_x300[group_t.index.get_loc('England')]
v1_norm = np.linalg.norm(v1)
# コサイン類似度計算
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番最後から-11(-12)までを1件ずつ出力(トップはEngland自身)
for index in cos_sim_sorted[:-12:-1]:
print('{}\t{}'.format(group_t.index[index], cos_sim[index]))
回答解説
コサイン類似度計算部分を関数にしました。count_nonzero
関数で判定して、ベクトルがすべてゼロの場合は-1を返すようにしています。
# コサイン類似度計算
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))]
上記の計算に対して、numpyだとこっちのapply_along_axis
を使った方が速いかとも思いましたが、むしろ遅かったので非採用です。
cos_sim = np.apply_along_axis(get_cos_similarity, 1, matrix_x300)
最後の出力結果です。ScotlandやItalyなどが上位にいます。
Japanもいるのが意外です。島国だからなのでしょうか。
England 1.0000000000000002
Scotland 0.6364961613062289
Italy 0.6033905306935802
Wales 0.5961887337227456
Australia 0.5953277272306978
Spain 0.5752511915429617
Japan 0.5611603300967408
France 0.5547284075334182
Germany 0.5539239745925412
United_Kingdom 0.5225684232409136
Cheshire 0.5125286144779688