ナード戦隊データマン

データサイエンスを用いて悪と戦うぞ

word2vecで未知語に対処する

Word2vecの問題点は、訓練後のWord2vecのボキャブラリーは固定で、OOV(Out of vocabulary)へ対処できないことです。ここでは、Word2vecのボキャブラリーをsentencepieceでサブワード分割し、それを訓練することで未知語への対処を試みます。

モデル

無題の図形描画 (2).jpg

事前に必要なもの

jawikiから、mecab neologdでトークナイズして訓練したgensimのWord2vecを用意してください。

コード

# coding: utf-8
from gensim.models import KeyedVectors
import sentencepiece as spm
from keras.layers import Input, Dense, Embedding, Flatten, GRU
from keras.layers import SpatialDropout1D
from keras.layers.convolutional import Conv1D, MaxPooling1D
from keras.models import Sequential
from keras.preprocessing.sequence import pad_sequences

def load_data(w2vfile="./jawiki_200.txt", spfile="jawiki.model"):
    model = KeyedVectors.load_word2vec_format(w2vfile, binary=False)
    vocab = model.wv.vocab
    sp = spm.SentencePieceProcessor()
    sp.Load(spfile)
    X = []
    for x in vocab:
        X.append(sp.EncodeAsIds(x))
    y = []
    for x in vocab:
        y.append(model.wv[x])
    X = pad_sequences(X, maxlen=47)
    return X,y,model,vocab,sp

def build_model(vocabsize):
    model = Sequential()
    model.add(Embedding(vocabsize, 200, input_length=47))
    model.add(SpatialDropout1D(0.2))
    model.add(Conv1D(32, kernel_size=3, padding='same', activation='relu'))
    model.add(MaxPooling1D(pool_size=2))
    model.add(GRU(200))
    model.add(Dense(200, activation="linear"))
    model.compile(loss='mse', optimizer='rmsprop', metrics=['mse'])
    return model

if __name__ == "__main__":
    X, y, wv, vocab, sp = load_data()
    model = build_model(sp.get_piece_size()+1)
    model.fit(X, y, epochs=10, batch_size=1000)
    model.save("model.h5")

試す

"11次元超重力理論"という未知語に対してどんな予測をするかを見ます。

# coding: utf-8
from keras.models import load_model
from keras.preprocessing.sequence import pad_sequences
import sentencepiece as spm
from gensim.models import KeyedVectors
import numpy as np

def cossim(x,y):
    return np.dot(x,y)/(np.linalg.norm(x)*np.linalg.norm(y))

model = load_model("./model.h5")
sp = spm.SentencePieceProcessor()
sp.Load("./jawiki_sp_model/jamodel/jawiki.model")
wv = KeyedVectors.load_word2vec_format("jawiki_200.txt", binary=False)

example = "11次元超重力理論"
ex = sp.EncodeAsIds(example)
ex = pad_sequences([ex], maxlen=47)
pred = model.predict(ex)
ks = ["ペット", "政治", "勉強", "学問"]
a = {k:wv[k] for k in ks}
print(wv.most_similar(pred))
print({k:cossim(x,pred[0]) for k,x in a.items()})

出力:

[
  ('地球流体力学', 0.5742046236991882), 
  ('散逸構造', 0.5524085760116577), 
  ('気体分子運動論', 0.5428990125656128), 
  ('カオス理論', 0.5426242351531982), 
  ('ワインバーグ=サラム理論', 0.5341025590896606), 
  ('連続体力学', 0.5340026617050171), 
  ('2状態系', 0.5339871644973755), 
  ('漸近的自由性', 0.5332730412483215), 
  ('ニュートン力学', 0.5317675471305847), 
  ('古典物理学', 0.531417965888977)
]

{'ペット': -0.015529865, '政治': 0.33418512, '勉強': -0.042449825, '学問': 0.3523173}