PV-DMは、文と単語にユニークな固定次元のベクトルを割り当て、文の単語列をウィンドウサイズ幅ごとに抽出し、文のベクトルを追加して、それらの平均(または連結)をとったベクトルから、次に現れる単語を多クラス分類により予測するように学習する。
その際、単語ベクトルも同時に学習される。
学習済みモデルを使用して文の類似度を測る
以前の日記でTF-IDFで試した気象庁のFAQを使用して、入力した文に意味が近い質問文を予想する。
import gensim
import MeCab
from sklearn.metrics.pairwise import cosine_similarity
import numpy as np
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('faq', type=str)
parser.add_argument('model', type=str)
parser.add_argument("--dictionary", "-d", type=str, help="mecab dictionary")
args = parser.parse_args()
mecab = MeCab.Tagger("-Owakati" + ("" if not args.dictionary else " -d " + args.dictionary))
model = gensim.models.Doc2Vec.load(args.model)
questions = []
answers = []
for line in open(args.faq, "r", encoding="utf-8"):
cols = line.strip().split('\t')
questions.append(gensim.utils.simple_preprocess(mecab.parse(cols[0]).strip(), min_len=1))
answers.append(cols[1])
doc_vecs = []
for question in questions:
doc_vecs.append(model.infer_vector(question))
while True:
line = input("> ")
if not line:
break
vec = model.infer_vector(gensim.utils.simple_preprocess(mecab.parse(line), min_len=1))
sims = cosine_similarity([vec], doc_vecs)
index = np.argsort(sims[0])
print(questions[index[-1]])
print()
print(answers[index[-1]])
print()
print(questions[index[-2]])
print(questions[index[-3]])
print(questions[index[-4]])
print()
以下のような文を入力すると、それぞれ予測した質問文(上位4つ)は以下のようになった。
> みぞれとは何ですか?
['フェーン現象', 'と', 'は', '何', 'です', 'か']
['大雨', 'の', '回数', 'は', '増え', 'て', 'いる', 'の', 'です', 'か']
['プレート', 'と', 'は', '何', 'です', 'か']
['エルニーニョ現象', 'ラニーニャ現象', 'が', '発生', 'する', 'と', '日本', '近海', 'の', '海面', '水温', 'は', 'どの', 'よう', 'に', 'なる', 'の', 'です', 'か']
> 天気予報が外れる理由は?
['週間', '天気予報', 'が', '外れる', 'こと', 'が', 'あり', 'ます', 'が', 'なぜ', 'です', 'か']
['週間', '天気予報', 'は', 'よく', '外れる', 'ので', '日', '先', 'くらい', 'の', '予報', 'だけ', 'で', '良い', 'の', 'で', 'は', 'ない', 'です', 'か']
['噴火警報', '火口', '周辺', '警報', '噴火', '予報', 'について', '教え', 'て', 'ください']
['海上', 'の', '台風', 'の', '中心', '気圧', 'は', 'どの', 'よう', 'に', '測っ', 'て', 'い', 'ます', 'か']
> 地震は予知できますか?
['花粉情報', 'は', '気象庁', 'で', '発表', 'し', 'て', 'い', 'ます', 'か']
['空', 'は', 'どうして', '青い', 'の', 'です', 'か', '夕焼け', 'は', 'どうして', '赤い', 'の', 'です', 'か']
['東海', '地域', 'に', 'は', 'どの', 'よう', 'な', '監視', '体制', 'が', 'とら', 'れ', 'て', 'い', 'ます', 'か']
['地震', 'の', '予知', 'は', 'でき', 'ます', 'か']
> 津波の規模によってどんな被害が起きるのですか?
['津波', 'の', '高さ', 'によって', 'どの', 'よう', 'な', '被害', 'が', '発生', 'する', 'の', 'です', 'か']
['特別警報', 'と', '既存', 'の', '記録的短時間大雨情報', 'の', '違い', 'は', '何', 'です', 'か', '廃止', 'さ', 'れ', 'たり', 'は', 'し', 'ない', 'の', 'です', 'か']
['テレビ局', 'によって', '天気予報', 'の', '内容', 'が', '違う', 'こと', 'が', 'ある', 'の', 'は', 'なぜ', 'です', 'か']
['検定', 'が', '必要', 'と', 'なる', '気象', '測', '器', 'に', 'は', 'どんな', 'もの', 'が', 'あり', 'ます', 'か']
> 高潮の発生の仕組みは?
['副振動', 'と', 'は', '何', 'です', 'か']
['花粉症記念日', '月', '日', 'と', 'は', 'なん', 'です', 'か']
['雪', 'は', 'どうして', 'できる', 'の', 'です', 'か']
['竜巻', 'は', 'どうして', '起きる', 'の', 'です', 'か']
> 高潮の発生の仕組みは?
['高潮', 'の', '発生', 'の', '仕組み', 'は']
['雲', 'が', '七', '色', 'に', '見える', '彩雲', 'の', '仕組み', 'は', '何', 'です', 'か']
['津波', 'は', 'どの', 'よう', 'な', '仕組み', 'で', '発生', 'する', 'の', 'です', 'か']
['竜巻', 'は', 'どうして', '起きる', 'の', 'です', 'か']
みぞれについて聞いているのに、「みぞれ」が現れない文を予測している。
「天気予報」、「外れる」と2つ単語を含む場合は、近い文を予測している。
「地震」、「予知」では単語が2つでも4番目になっている。
「津波の規模によってどんな被害が起きるのですか?」と長い文を入力すると、それなりの精度になるようだ。
また、「高潮の発生の仕組みは?」を2回予測すると、異なる結果が返っている。予測のたびにランダムな初期化から学習するため起きる。
以上のように、短い文については、あまり精度が高くないという結果になった。
ウィンドウサイズはデフォルトの5を使用しているので、キーとなる単語は少なくとも5個はあった方が良いかもしれない。
ベクトルの次元やウィンドウサイズやイテレーション回数を変えてみてどうなるかさらに検証が必要そうだ。
短い文での類似を測るには、word2vecのベクトルの平均を使用するなどdoc2vecとは別の方法が有効かもしれない。