言語処理100本ノック 2015の90本目「word2vecによる学習」の記録です。
今まで第9章でやってきた内容をパッケージ使って簡単にやってしまおう、という設問です。メモリ不足などを気にしながら必死にやってきた内容が3行程度のコードでできてしまうのは、拍子抜けな反面素晴らしさを痛感します。
今回は設問で指定されているGoogle社のword2vecを使わずにオープンソースのGeinsimを使っています。パッケージの更新頻度も多く、よく使われていると聞いたことがあるからです(にわか知識で、しっかりと調べていません)。
参考リンク
リンク | 備考 |
---|---|
090.word2vecによる学習.ipynb | 回答プログラムのGitHubリンク |
素人の言語処理100本ノック:90 | 言語処理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でインストールするだけです。
種類 | バージョン |
---|---|
gensim | 3.8.1 |
numpy | 1.17.4 |
課題
第10章: ベクトル空間法 (II)
第10章では,前章に引き続き単語ベクトルの学習に取り組む.
90. word2vecによる学習
81で作成したコーパスに対してword2vecを適用し,単語ベクトルを学習せよ.さらに,学習した単語ベクトルの形式を変換し,86-89のプログラムを動かせ.
回答
回答プログラム 090.word2vecによる学習.ipynb
from pprint import pprint
from gensim.models import word2vec
corpus = word2vec.Text8Corpus('./../09.ベクトル空間法 (I)/081.corpus.txt')
model = word2vec.Word2Vec(corpus, size=300)
model.save('090.word2vec.model')
# 86. 単語ベクトルの表示
pprint(model.wv['United_States'])
# 87. 単語の類似度
print(np.dot(model.wv['United_States'], model.wv['U.S']) / (np.linalg.norm(model.wv['United_States']) * np.linalg.norm(model.wv['U.S'])))
# 88. 類似度の高い単語10件
pprint(model.wv.most_similar('England'))
# 89. 加法構成性によるアナロジー
# vec("Spain") - vec("Madrid") + vec("Athens")
pprint(model.wv.most_similar(positive=['Spain', 'Athens'], negative=['Madrid']))
回答解説
単語ベクトル生成
まずはファイル読込です。Text8Corpus
関数を使っている例が多いなと思い、そもそもText8Corpus
とは何と思って調べました。
記事「日本語版text8コーパスを作って分散表現を学習する」によるとtext8とは以下の処理をしたWikipediaのデータらしい。
- テキストと画像キャプションは保持
- テーブルや外国語バージョンへのリンクを除去
- 引用、脚注、マークアップを除去
- ハイパーテキストはアンカーテキストだけ保持。それ以外は除去
- 数字はつづりを変換。たとえば、"20"は"two zero"に変換
- 大文字を小文字に変換
- a-zの範囲に入らない文字はスペースに変換
大文字はあった気もするが、だいたい条件に合致している気がしたのでText8Corpus
を使いました。
corpus = word2vec.Text8Corpus('./../09.ベクトル空間法 (I)/081.corpus.txt')
あとはWord2Vec
関数を使うだけで300次元の単語ベクトル完成です。4分弱で生成できました。すごい・・・
オプションは使わなかったですが、gemsimのword2vecのオプション一覧がわかりやすかったです。
model = word2vec.Word2Vec(corpus, size=300)
そして、後続のノックのためにファイル保存しておきます。
model.save('090.word2vec.model')
そうすると以下の3ファイルができるようです。1つでないのが気持ち悪いです。
ファイル | サイズ |
---|---|
090.word2vec.model | 5MB |
090.word2vec.model.trainables.syn1neg.npy | 103MB |
090.word2vec.model.wv.vectors.npy | 103MB |
86. 単語ベクトルの表示
85で得た単語の意味ベクトルを読み込み,"United States"のベクトルを表示せよ.ただし,"United States"は内部的には"United_States"と表現されていることに注意せよ.
model.wv
にベクトルが入っているので指定してあげるだけです。
pprint(model.wv['United_States'])
array([ 2.3478289 , -0.61461514, 0.0478639 , 0.6709404 , 1.1090833 ,
-1.0814637 , -0.78162867, -1.2584596 , -0.04286158, 1.2928476 ,
結果略
87. 単語の類似度
85で得た単語の意味ベクトルを読み込み,"United States"と"U.S."のコサイン類似度を計算せよ.ただし,"U.S."は内部的に"U.S"と表現されていることに注意せよ.
model
を使って第9章と同じベクトル同士のコサイン類似度計算をします。
第9章では0.837516976284694だったので、より類似度が高い数値が出ています。
print(np.dot(model.wv['United_States'], model.wv['U.S']) / (np.linalg.norm(model.wv['United_States']) * np.linalg.norm(model.wv['U.S'])))
0.8601596
88. 類似度の高い単語10件
85で得た単語の意味ベクトルを読み込み,"England"とコサイン類似度が高い10語と,その類似度を出力せよ.
modst_similar
関数を使うだけで出力できます。
pprint(model.wv.most_similar('England'))
[('Scotland', 0.7884809970855713),
('Wales', 0.7721374034881592),
('Ireland', 0.6838206052780151),
('Britain', 0.6335258483886719),
('Hampshire', 0.6147407293319702),
('London', 0.6021863222122192),
('Cork', 0.5809425115585327),
('Manchester', 0.5767091512680054),
('Liverpool', 0.5765234231948853),
('Orleans', 0.5624016523361206)]
ちなみに第9章での結果は以下の通りでしたが、今回はイギリスに関連する単語がより上位に出てきていて、より正しいデータが出力されていることがわかります。
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
89. 加法構成性によるアナロジー
85で得た単語の意味ベクトルを読み込み,vec("Spain") - vec("Madrid") + vec("Athens")を計算し,そのベクトルと類似度の高い10語とその類似度を出力せよ.
modst_similar
関数にpositive
とnegative
を渡すと計算して、類似度の高い10語を出力してくれます。
pprint(model.wv.most_similar(positive=['Spain', 'Athens'], negative=['Madrid']))
[('Denmark', 0.7606724500656128),
('Italy', 0.7585107088088989),
('Austria', 0.7528095841407776),
('Greece', 0.7401891350746155),
('Egypt', 0.7314825057983398),
('Russia', 0.7225484848022461),
('Great_Britain', 0.7184625864028931),
('Norway', 0.7148114442825317),
('Rome', 0.7076312303543091),
('kingdom', 0.6994863748550415)]
ちなみに第9章での結果は以下の通りでしたが、今回はギリシャも4位に出てきていてより正しいデータが出力されていることがわかります。
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