自然言語処理の問題集として有名な言語処理100本ノックの2020年版の解答例です。 この記事では、以下の第1章から第10章のうち、「第7章: 単語ベクトル」を解いてみた結果をまとめています。
- 第1章: 準備運動
- 第2章: UNIXコマンド
- 第3章: 正規表現
- 第4章: 形態素解析
- 第5章: 係り受け解析
- 第6章: 機械学習
- 第7章: 単語ベクトル
- 第8章: ニューラルネット
- 第9章: RNNとCNN
- 第10章: 機械翻訳
環境設定
コードの実行はGoogle Colaboratoryで行います。 以降の解答の実行結果をすべて含むノートブックは、以下のリンクから直接参照することができます。
第7章: 単語ベクトル
単語の意味を実ベクトルで表現する単語ベクトル(単語埋め込み)に関して,以下の処理を行うプログラムを作成せよ.
60. 単語ベクトルの読み込みと表示
Google Newsデータセット(約1,000億単語)での学習済み単語ベクトル(300万単語・フレーズ,300次元)をダウンロードし,”United States”の単語ベクトルを表示せよ.ただし,”United States”は内部的には”United_States”と表現されていることに注意せよ.
まずは、指定の学習済み単語ベクトルをダウンロードします。
import gdown url = 'https://drive.google.com/uc?id=0B7XkCwpI5KDYNlNUTTlSS21pQmM' output = 'GoogleNews-vectors-negative300.bin.gz' gdown.download(url, output, quiet=False)
続いて、自然言語処理のさまざまなタスクで利用されるGensimを用いて、単語ベクトルを読み込みます。
from gensim.models import KeyedVectors model = KeyedVectors.load_word2vec_format('./GoogleNews-vectors-negative300.bin.gz', binary=True)
読み込んだ後は、ベクトル化したい単語を指定するだけで簡単に単語ベクトルを得ることができます。
model['United_States']
--- 出力 --- array([-3.61328125e-02, -4.83398438e-02, 2.35351562e-01, 1.74804688e-01, -1.46484375e-01, -7.42187500e-02, -1.01562500e-01, -7.71484375e-02, 1.09375000e-01, -5.71289062e-02, -1.48437500e-01, -6.00585938e-02, 1.74804688e-01, -7.71484375e-02, 2.58789062e-02, -7.66601562e-02, -3.80859375e-02, 1.35742188e-01, 3.75976562e-02, -4.19921875e-02, ・・・
61. 単語の類似度
“United States”と”U.S.”のコサイン類似度を計算せよ.
ここではsimilarity
メソッドを利用します。2単語を指定すると、単語間のコサイン類似度を計算することができます。
model.similarity('United_States', 'U.S.')
--- 出力 --- 0.73107743
62. 類似度の高い単語10件
“United States”とコサイン類似度が高い10語と,その類似度を出力せよ.
ここではmost_similar
メソッドを利用します。単語を指定すると、topn
までの類似度上位単語とその類似度を取得することができます。
model.most_similar('United_States', topn=10)
--- 出力 --- [('Unites_States', 0.7877248525619507), ('Untied_States', 0.7541370391845703), ('United_Sates', 0.74007248878479), ('U.S.', 0.7310774326324463), ('theUnited_States', 0.6404393911361694), ('America', 0.6178410053253174), ('UnitedStates', 0.6167312264442444), ('Europe', 0.6132988929748535), ('countries', 0.6044804453849792), ('Canada', 0.6019070148468018)]
63. 加法構成性によるアナロジー
“Spain”の単語ベクトルから”Madrid”のベクトルを引き,”Athens”のベクトルを足したベクトルを計算し,そのベクトルと類似度の高い10語とその類似度を出力せよ.
前問でも利用したmost_similar
メソッドは、足すベクトルと引くベクトルをそれぞれ指定した上で、計算後のベクトルと類似度が高い単語を取得することができます。
ここでは、問題文の指示に従い、Spain
- Madrid
+ Athens
のベクトルと類似度の高い単語を表示していますが、期待通りGreece
が1位に登場しています。
vec = model['Spain'] - model['madrid'] + model['Athens'] model.most_similar(positive=['Spain', 'Athens'], negative=['Madrid'], topn=10)
--- 出力 --- [('Greece', 0.6898481249809265), ('Aristeidis_Grigoriadis', 0.5606848001480103), ('Ioannis_Drymonakos', 0.5552908778190613), ('Greeks', 0.545068621635437), ('Ioannis_Christou', 0.5400862693786621), ('Hrysopiyi_Devetzi', 0.5248444676399231), ('Heraklio', 0.5207759737968445), ('Athens_Greece', 0.516880989074707), ('Lithuania', 0.5166866183280945), ('Iraklion', 0.5146791934967041)]
64. アナロジーデータでの実験
単語アナロジーの評価データをダウンロードし,vec(2列目の単語) - vec(1列目の単語) + vec(3列目の単語)を計算し,そのベクトルと類似度が最も高い単語と,その類似度を求めよ.求めた単語と類似度は,各事例の末尾に追記せよ.
指定のデータをダウンロードします。
!wget http://download.tensorflow.org/data/questions-words.txt
# 先頭10行の確認 !head -10 questions-words.txt
--- 出力 --- : capital-common-countries Athens Greece Baghdad Iraq Athens Greece Bangkok Thailand Athens Greece Beijing China Athens Greece Berlin Germany Athens Greece Bern Switzerland Athens Greece Cairo Egypt Athens Greece Canberra Australia Athens Greece Hanoi Vietnam Athens Greece Havana Cuba
このデータは、(Athens-Greece, Tokyo-Japan)のように、意味的アナロジーを評価するための組と、(walk-walks, write-writes)のように文法的アナロジーを評価する組が含まれます。 全部で以下の14のカテゴリからなり、上の5つが前者、それ以外が後者に対応しています。
No. | カテゴリ |
---|---|
1 | capital-common-countries |
2 | capital-world |
3 | currency |
4 | city-in-state |
5 | family |
6 | gram1-adjective-to-adverb |
7 | gram2-opposite |
8 | gram3-comparative |
9 | gram4-superlative |
10 | gram5-present-participle |
11 | gram6-nationality-adjective |
12 | gram7-past-tense |
13 | gram8-plural |
14 | gram9-plural-verbs |
1行ずつ読込み、指定の単語と類似度を計算した上で整形したデータを出力します。
with open('./questions-words.txt', 'r') as f1, open('./questions-words-add.txt', 'w') as f2: for line in f1: # f1から1行ずつ読込み、求めた単語と類似度を追加してf2に書込む line = line.split() if line[0] == ':': category = line[1] else: word, cos = model.most_similar(positive=[line[1], line[2]], negative=[line[0]], topn=1)[0] f2.write(' '.join([category] + line + [word, str(cos) + '\n']))
!head -10 questions-words-add.txt
--- 出力 --- capital-common-countries Athens Greece Baghdad Iraq Iraqi 0.6351870894432068 capital-common-countries Athens Greece Bangkok Thailand Thailand 0.7137669324874878 capital-common-countries Athens Greece Beijing China China 0.7235777974128723 capital-common-countries Athens Greece Berlin Germany Germany 0.6734622120857239 capital-common-countries Athens Greece Bern Switzerland Switzerland 0.4919748306274414 capital-common-countries Athens Greece Cairo Egypt Egypt 0.7527809739112854 capital-common-countries Athens Greece Canberra Australia Australia 0.583732545375824 capital-common-countries Athens Greece Hanoi Vietnam Viet_Nam 0.6276341676712036 capital-common-countries Athens Greece Havana Cuba Cuba 0.6460992097854614 capital-common-countries Athens Greece Helsinki Finland Finland 0.6899983882904053
65. アナロジータスクでの正解率
64の実行結果を用い,意味的アナロジー(semantic analogy)と文法的アナロジー(syntactic analogy)の正解率を測定せよ.
対応するカテゴリごとにそれぞれ計算します。
with open('./questions-words-add.txt', 'r') as f: sem_cnt = 0 sem_cor = 0 syn_cnt = 0 syn_cor = 0 for line in f: line = line.split() if not line[0].startswith('gram'): sem_cnt += 1 if line[4] == line[5]: sem_cor += 1 else: syn_cnt += 1 if line[4] == line[5]: syn_cor += 1 print(f'意味的アナロジー正解率: {sem_cor/sem_cnt:.3f}') print(f'文法的アナロジー正解率: {syn_cor/syn_cnt:.3f}')
--- 出力 --- 意味的アナロジー正解率: 0.731 文法的アナロジー正解率: 0.740
66. WordSimilarity-353での評価
The WordSimilarity-353 Test Collectionの評価データをダウンロードし,単語ベクトルにより計算される類似度のランキングと,人間の類似度判定のランキングの間のスピアマン相関係数を計算せよ.
このデータは、単語のペアに対して人間が評価した類似度が付与されています。 それぞれのペアに対して単語ベクトルの類似度を計算し、両者のスピアマン順位相関係数を計算します。
!wget http://www.gabrilovich.com/resources/data/wordsim353/wordsim353.zip !unzip wordsim353.zip
--- 出力 --- Archive: wordsim353.zip inflating: combined.csv inflating: set1.csv inflating: set2.csv inflating: combined.tab inflating: set1.tab inflating: set2.tab inflating: instructions.txt
!head -10 './combined.csv'
--- 出力 --- Word 1,Word 2,Human (mean) love,sex,6.77 tiger,cat,7.35 tiger,tiger,10.00 book,paper,7.46 computer,keyboard,7.62 computer,internet,7.58 plane,car,5.77 train,car,6.31 telephone,communication,7.50
ws353 = [] with open('./combined.csv', 'r') as f: next(f) for line in f: # 1行ずつ読込み、単語ベクトルと類似度を計算 line = [s.strip() for s in line.split(',')] line.append(model.similarity(line[0], line[1])) ws353.append(line) # 確認 for i in range(5): print(ws353[i])
--- 出力 --- ['love', 'sex', '6.77', 0.2639377] ['tiger', 'cat', '7.35', 0.5172962] ['tiger', 'tiger', '10.00', 0.99999994] ['book', 'paper', '7.46', 0.3634626] ['computer', 'keyboard', '7.62', 0.39639163]
import numpy as np from scipy.stats import spearmanr # スピアマン相関係数の計算 human = np.array(ws353).T[2] w2v = np.array(ws353).T[3] correlation, pvalue = spearmanr(human, w2v) print(f'スピアマン相関係数: {correlation:.3f}')
--- 出力 --- スピアマン相関係数: 0.685
67. k-meansクラスタリング
国名に関する単語ベクトルを抽出し,k-meansクラスタリングをクラスタ数k=5として実行せよ.
適当な国名リストの取得元が見つからなかったため、単語アナロジーの評価データから収集しています。
# 国名の取得 countries = set() with open('./questions-words-add.txt') as f: for line in f: line = line.split() if line[0] in ['capital-common-countries', 'capital-world']: countries.add(line[2]) elif line[0] in ['currency', 'gram6-nationality-adjective']: countries.add(line[1]) countries = list(countries) # 単語ベクトルの取得 countries_vec = [model[country] for country in countries]
from sklearn.cluster import KMeans # k-meansクラスタリング kmeans = KMeans(n_clusters=5) kmeans.fit(countries_vec) for i in range(5): cluster = np.where(kmeans.labels_ == i)[0] print('cluster', i) print(', '.join([countries[k] for k in cluster]))
--- 出力 --- cluster 0 Taiwan, Afghanistan, Iraq, Lebanon, Indonesia, Turkey, Egypt, Libya, Syria, Korea, China, Nepal, Cambodia, India, Bhutan, Qatar, Laos, Malaysia, Iran, Vietnam, Oman, Bahrain, Pakistan, Thailand, Bangladesh, Morocco, Jordan, Israel cluster 1 Madagascar, Uganda, Botswana, Guinea, Malawi, Tunisia, Nigeria, Mauritania, Kenya, Zambia, Algeria, Mozambique, Ghana, Niger, Somalia, Angola, Mali, Senegal, Sudan, Zimbabwe, Gambia, Eritrea, Liberia, Burundi, Gabon, Rwanda, Namibia cluster 2 Suriname, Uruguay, Tuvalu, Nicaragua, Colombia, Belize, Venezuela, Ecuador, Fiji, Peru, Guyana, Jamaica, Brazil, Honduras, Samoa, Bahamas, Dominica, Philippines, Cuba, Chile, Mexico, Argentina cluster 3 Netherlands, Sweden, USA, Ireland, Canada, Spain, Malta, Greenland, Europe, Greece, France, Austria, Norway, Finland, Australia, Japan, Iceland, England, Italy, Denmark, Belgium, Switzerland, Germany, Portugal, Liechtenstein cluster 4 Croatia, Belarus, Uzbekistan, Latvia, Tajikistan, Slovakia, Ukraine, Hungary, Albania, Poland, Montenegro, Georgia, Russia, Kyrgyzstan, Armenia, Romania, Cyprus, Lithuania, Azerbaijan, Serbia, Slovenia, Turkmenistan, Moldova, Bulgaria, Estonia, Kazakhstan, Macedonia
68. Ward法によるクラスタリング
国名に関する単語ベクトルに対し,Ward法による階層型クラスタリングを実行せよ.さらに,クラスタリング結果をデンドログラムとして可視化せよ.
from matplotlib import pyplot as plt from scipy.cluster.hierarchy import dendrogram, linkage plt.figure(figsize=(15, 5)) Z = linkage(countries_vec, method='ward') dendrogram(Z, labels=countries) plt.show()
69. t-SNEによる可視化
国名に関する単語ベクトルのベクトル空間をt-SNEで可視化せよ.
t-SNEで単語ベクトルを2次元に圧縮し、散布図で可視化します。
!pip install bhtsne
import bhtsne embedded = bhtsne.tsne(np.array(countries_vec).astype(np.float64), dimensions=2, rand_seed=123) plt.figure(figsize=(10, 10)) plt.scatter(np.array(embedded).T[0], np.array(embedded).T[1]) for (x, y), name in zip(embedded, countries): plt.annotate(name, (x, y)) plt.show()
理解を深めるためのオススメ教材
全100問の解答はこちら