gensimによるword2vec
まずはjanomeなどを使って、各sentenceがtokenizeされた以下のようなリストsentencesを用意する。
1 2 3 4 5 6 7 8 9 10 11 |
# 5chからてきとうに拾ってきた文章 sentences = [ ['何', 'も', '見', 'に', '行っ', 'た', 'こと', 'ない'], ['日野', 'に', 'い', 'た', '時', '東京ドーム', '回', 'くらい', '見', 'に', '行っ', 'た', 'わ'], ['ツイート', 'み', 'て', '知っ', 'た', 'けど', 'ホーム', 'ボタン', 'も', 'ない', 'なんて', 'アンド', 'ロ', 'の', 'ほう', 'が', 'よ', 'さ', 'そう'], ['の', '操作', 'は', '全て', '画面', 'ジェスチャー', 'だ', 'から', '違和感', 'が'], ['あと', '豪邸', '売っ', 'た', '金', 'で', '財産', '分与', '嫁', 'は', '再婚', '高橋', 'ジョージ', '涙', '目', 'に', 'なり', 'ながら', '話し', 'て', 'た', 'わ'], ['米', '企業', 'も', 'あんまり', '好き', 'じゃ', 'ない', 'ウィンドウ', 'ず', 'とか', 'だんだん', '使い', 'づらく', 'なっ', 'て', 'いる', '気', 'が', 'する'], ['客観', '的', 'に', 'みる', 'と', '日本', 'って', '子ども', 'を', '奪っ', 'て', '財産', 'を', '強奪', 'する', '強盗', 'みたい', 'な', 'やつ', 'を', '裁判所', 'が', '追認', 'し', 'て', '味方', 'し', 'て', 'いる', 'よう', 'な', 'ところ', 'が', 'ある', 'ん', 'だ', 'よ', 'な', '、', 'で', '一方', '的', 'に', '本当は', '被害', '者', 'かも', 'しれ', 'ない', '人間', 'を', '悪人', '扱い', 'でしょ', '俺', 'は', '神様', 'じゃ', 'ない', 'けど', '、', 'そりゃあ', '天', 'も', '日本', 'を', '見限っ', 'て', '地獄', 'を', '具現', '化', 'する', 'の', 'も', 'なる', 'ほど', 'だ', 'わ', 'と', '思っ', 'た', 'わ'], ['子供', 'が', '成人', 'し', 'て', '自立', 'し', 'たら', '父親', 'によって', '来', 'て', 'くれる', 'と', '思い', 'たい'] ] |
あるていどの分量を用意しないと「RuntimeError: you must first build vocabulary before training the model」というエラーになる。
sentencesをgensimに与えて単語をベクトル化し、ファイルに保存する。
1 2 3 4 5 |
from gensim.models import word2vec # sg=1:skip-gram, size=5:5次元ベクトルに変換, window=3:単語3つ分の文脈を考慮 vec = word2vec.Word2Vec(sentences, sg=1, size=5, window=3) vec.save('5ch.word2vec') |
まずはどんなベクトルになったか確認。
1 2 3 4 5 6 7 8 9 10 |
vec = word2vec.Word2Vec.load('5ch.word2vec') vec.wv.vectors # [[-0.09309922 -0.07330111 -0.04078407 0.00363464 -0.05838574] # [-0.00841863 -0.04764092 0.02599181 -0.02519906 0.04207696] # [-0.09396616 -0.08528031 -0.0113731 -0.04975578 -0.03806607] # [-0.02181842 0.05379159 -0.04360372 0.08373219 0.09710913] # [-0.01054219 0.08616821 -0.05808588 0.08249895 0.0770894 ] # [ 0.07515892 -0.02192332 -0.08187028 0.02956331 -0.01100636] # [-0.04056986 -0.05961309 -0.08684193 -0.04180455 0.08563547]] |
今回のデータ量では7つの単語しかベクトル化されない。
ベクトル化された単語は以下。
1 2 |
vec.wv.index2word # ['て', 'た', 'に', 'が', 'を', 'も', 'ない'] |
単語をベクトルに変換。
1 2 |
vec.wv.get_vector('た') # [-0.00841863 -0.04764092 0.02599181 -0.02519906 0.04207696] |
vec.wv.vectorsとvec.wv.index2wordの順序は対応している。
1 2 |
vec.wv.get_vector(vec.wv.index2word[1]) == vec.wv.vectors[1] # [ True True True True True] |
ベクトルを単語に変換するには、vec.wv.most_similarメソッドを使ってcos類似度がいちばん高いものを選ぶ。
1 2 3 |
word = vec.wv.most_similar([vec.wv.get_vector('た')], [], 1)[0][0] word # た |
ググったりコード補完で調べたが、単語からIDに変換するメソッドやフィールドはもってないっぽい。自分で関数をつくった。
1 2 3 4 5 6 7 8 |
def word_to_index(word, index2word): for i, w in enumerate(index2word): if word == w: return i return None # ベクトル化されていない単語の場合はNoneを返す word_to_index('た', vec.wv.index2word) # 1 |
モデルをある状態に持っていってから文章生成したい場合
stateful=Trueにしてからpredictすると状態が更新されていくっぽい(参考)。ただ、ほんとうに更新されているかどうかどうやって確認していいかがわからない。たぶんFunctional APIを使わないと無理。あるいはtensorflowまで降りていくか。いやだなー。
「ゼロから作るDeep Learning2」は↓のようなコードが7章にあったので、predictで状態が更新されていくのはまちがいない。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
model = BetterRnnlmGen() model.load_params('../ch06/BetterRnnlm.pkl') #---中略--- model.reset_state() start_words = 'the meaning of life is' start_ids = [word_to_id[w] for w in start_words.split(' ')] for x in start_ids[:-1]: x = np.array(x).reshape(1, 1) model.predict(x) word_ids = model.generate(start_ids[-1], skip_ids) word_ids = start_ids[:-1] + word_ids txt = ' '.join([id_to_word[i] for i in word_ids]) txt = txt.replace(' <eos>', '.\n') print('-' * 50) print(txt) |
参考リンクに貼られていた公式FAQに明言されていた。
predict, fit, train_on_batch, predict_classesなどの関数はいずれもstatefulレイヤーの状態を更新することに注意してください.そのため,statefulな訓練だけでなく,statefulな予測も可能となります.
predictで状態が更新されるということでまちがいない。
都度状態をとっておいてバックする方法
stateful=Trueにする以外にも、出力と一緒に状態もとっておいて、それらをデコーダーにフィードバックする方法もあるみたい。predict時にreturn_state=Trueで状態も出力するようにしておいて、それをinitial_stateをとおしてLSTMに与えるんだね(最後のソースの8-9行目と37-38行目と56行目)。冗長な書き方な気もするけど、直観的に理解しやすい。この書き方とstateful=Trueにするほうとで同じ結果になれば動作確認になる。