東北大乾・岡崎研(現乾・鈴木研)で作成された、新人研修の一つであるプログラミング基礎勉強会の教材『言語処理100本ノック 2020年版』をPython(3.7)で解いた記事です。
今回は第7章:単語ベクトルの「64. アナロジーデータでの実験」を解説します。
独学でPythonを勉強してきたので、間違いやもっと効率の良い方法があるかもしれません。
改善点を見つけた際はご指摘いただけると幸いです。
ソースコードはGitHubにも公開しています。
第7章:単語ベクトル
単語の意味を実ベクトルで表現する単語ベクトル(単語埋め込み)に関して,以下の処理を行うプログラムを作成せよ.
第7章: 単語ベクトル – 言語処理100本ノック 2020 (Rev 1) – NLP100 2020
64. アナロジーデータでの実験
単語アナロジーの評価データをダウンロードし,vec(2列目の単語) – vec(1列目の単語) + vec(3列目の単語)を計算し,そのベクトルと類似度が最も高い単語と,その類似度を求めよ.求めた単語と類似度は,各事例の末尾に追記せよ.
第7章: 単語ベクトル – 言語処理100本ノック 2020 (Rev 1) – NLP100 2020
import pandas as pd
from gensim.models import KeyedVectors
from tqdm import tqdm
def culculate_similarity(row):
positive = [row["v2"], row["v3"]]
negative = [row["v1"]]
return pd.Series(list(model.most_similar(positive=positive, negative=negative))[0])
tqdm.pandas()
with open("./input/questions-words.txt") as rf:
lines = rf.readlines()
lines = [line.rstrip("\n").split() for line in lines]
columns = ["category", "v1", "v2", "v3", "v4"]
datas = [[] for _ in range(5)]
for line in lines:
if line[0] == ":":
category = line[1]
continue
datas[0].append(category)
for i in range(4):
datas[i + 1].append(line[i])
df = pd.DataFrame(
data={"category": datas[0], "v1": datas[1], "v2": datas[2], "v3": datas[3], "v4": datas[4]},
columns=columns
)
model = KeyedVectors.load_word2vec_format('./input/GoogleNews-vectors-negative300.bin', binary=True)
df[["SimWord", "Score"]] = df.progress_apply(culculate_similarity, axis=1)
df.to_csv("./output/64.txt", sep=" ", index=False, header=None)
このデータは先頭がコロン(:
)で始まる行はカテゴリを表しており、意味的アナロジーを表すカテゴリと、文法的アナロジーを表すカテゴリの2種類に別れています。
2種類の分類を把握していないと次の65.の問題が解けないので、データを維持することを忘れないようにしましょう。
ちなみに2種類のカテゴリ分けは下記の通りです。
意味的アナロジー | capital-common-countries, capital-world, currency, city-in-state, family |
文法的アナロジー | gram1-adjective-to-adverb, gram2-opposite, gram3-comparative, gram4-superlative, gram5-present-participle, gram6-nationality-adjective, gram7-past-tense, gram8-plural, gram9-plural-verbs |
プログラム本文はデータ量が多いので`tqdm`でプログレスバーを表示するようにしています。
指定されたデータをダウンロードし、with
を用いてファイル読み込みを行います。
読み込んだファイルは行ごとにリストの要素になっており、これには改行\n
も含まれているので、rstrip()
メソッドを用いて改行を除去。
後続の作業がしやすいように、半角スペースで分割して1行内をリスト化しておきましょう。
カラム名は「category」「v1」「v2」「v3」「v4」を用意。
それぞれのカラムの値を入れていくために、空のリスト5つで初期化したリストを準備します。
各行に対して、処理を行っていくわけですが、冒頭でお伝えしたようにカテゴリ情報を維持しなくてはいけません。
このために、行の要素のリストの1要素目がコロン(:
)の場合は、カテゴリ情報として記憶しておきます。
カテゴリを示した行はこれ以上の情報は不必要なので、continue
でループ先頭に戻ります。
カテゴリ情報以外の行については、categoryカラムに前段階で記憶したカテゴリ情報を入れた上で、「v1」「v2」「v3」「v4」のそれぞれのカラムに単語を入れていきます。
これで5列のリストが完成します。
完成したリストはpandasのDataFrameの形にしておきます。
その後、モデルを読み込み、モデルを利用してお題のベクトル演算を実行します。
positiveにプラスの列(2列目、3列目)を、negativeにマイナスの列(1列目)を指定してmost_similar()メソッドの引数で渡して0要素目をpandasのSeriesとして返却。
返却されたものをそのまま「SimWord」カラム・「Score」カラムとしてDataFrameに追加してデータは完成です。
最後にto_csv()
メソッドで出力して今回の課題は終了です。
まとめ:どのデータが必要か考えながらが大事
今回は言語処理100本ノック第7章:単語ベクトルの「64. アナロジーデータでの実験」を解いてみました。
だんだんpandasでのデータの扱いにも慣れてきた気がしていますが、独学なのでもっとよい書き方があるのではと心配にもなります。
以前購入したUdemyのデータ分析・機械学習の教材がまだ少ししか触っていないので、言語処理100本ノックが終わったら、一通り勉強してみようと思います。
必要性を感じてからの勉強の方が記憶に残りますし、Udemy教材の後にまた言語処理100本ノックに取り組んでも更に成長できそうな気がします。
「ここはもっとこういう書き方がいいですよ!」「これはアンチパターンですよ!」など、言いたいことがある方はぜひコメントいただければと思います。
引き続きよろしくお願いします!