言語処理100本ノック 2015の87本目「単語の類似度」の記録です。
いよいよ単語ベクトルを使って単語間の類似度を求めます。ようやく前処理が終わって本題になった感じです。自分のメールや会議の議事録などを使って単語間の類似度を求めてみたいものです。
類似度にはコサイン類似度を使います。高校で三角関数を習っていたときには「何に役に立つのだろう?」と感じていたことが本当に役に立つとは。
プログラム的には対して難しくないです。
参考リンク
リンク | 備考 |
---|---|
087.単語の類似度.ipynb | 回答プログラムのGitHubリンク |
素人の言語処理100本ノック:87 | 言語処理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」*を使っています。
87. 単語の類似度
85で得た単語の意味ベクトルを読み込み,"United States"と"U.S."のコサイン類似度を計算せよ.ただし,"U.S."は内部的に"U.S"と表現されていることに注意せよ.
課題補足(コサイン類似度)
コサイン類似度はベクトル同士の内積をノルムの積で割った以下の式です。詳しくはググるとたくさん出てきます。
\frac{\boldsymbol{A}\cdot\boldsymbol{B}}{|\boldsymbol{A}|\,|\boldsymbol{B}|}
回答
回答プログラム 087.単語の類似度.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)
# 'United States'と'U.S'の単語ベクトル表示
v1 = matrix_x300[group_t.index.get_loc('United_States')]
v2 = matrix_x300[group_t.index.get_loc('U.S')]
print(np.dot(v1, v2) / (np.linalg.norm(v1) * np.linalg.norm(v2)))
回答解説
前半は前回ノックと同じプログラムです。
変数v1
とv2
として各単語ベクトルを取り出しています。
# 'United States'と'U.S'の単語ベクトル表示
v1 = matrix_x300[group_t.index.get_loc('United_States')]
v2 = matrix_x300[group_t.index.get_loc('U.S')]
あとは計算するだけです。dot
で内積を、norm
でノルムを計算しています。
print(np.dot(v1, v2) / (np.linalg.norm(v1) * np.linalg.norm(v2)))
最大値が1(1の場合は同一)なので、0.83ならだいぶ似ているということでしょう。
0.837516976284694