毎日テキストマイニング

180日間、毎日テキストマイニングをするブログです

2018/09/13【82日目】chatbotで使ったコードを見直していく(後半)

後半の最初はピックルの読み込みから。

dataset = load_clean_sentences('both.pkl')
dataset1 = dataset.reshape(-1,1)
train = load_clean_sentences('train.pkl')
test = load_clean_sentences('test.pkl')

それから定義したモデルの読み込み。

model = define_model(all_vocab_size, all_length, 256)
model.compile(optimizer='adam', loss='categorical_crossentropy')

昨日、しれっとシカトしましたがモデルを定義した時に、def define_model(vocab, timesteps, n_units):←n_unitsが何なのか調べていなかったです。実際に使う時は 256を指定していますね。n_unitsというのは正確にはnumber_unitのことで、モデルの隠れ層のことらしいです。値が高すぎるとオーバーフィットイングを起こす可能性がありますのd、128か256がいいそうです。

2行目のcompileには最適化関数と損失関数を引数として渡せるそうです。最適化関数がadam、損失関数がcategorical_crossentropy。いちいち初めてみるものが出してきますね。ググってみましたが、それほど詳しく書いてあるドキュメントがなかったので、ひとまずそういうものだと覚えておきます。

次です。

def word_for_id(integer, tokenizer):
    for word, index in tokenizer.word_index.items():
        if index == integer:
            return word
    return None

intの数字を単語に戻す関数ですね。どうでもいいですが、関数を定義する時に、for文のネストがあると2番目のreturnがNoneになるんですね。ちょっとへー、となりました。

次に行きます。

def predict_sequence(model, tokenizer, source):
    prediction = model.predict(source, verbose=1)[0]
    integers = [argmax(vector) for vector in prediction]
    target = list()
    for i in integers:
        word = word_for_id(i, tokenizer)
        if word is None:
            break
        target.append(word)
    return ' '.join(target)

Outputする関数ですね。verboseというのはログを出力するものらしいです。

verbose: 0, 1または2.詳細表示モード.0とすると標準出力にログを出力しません. 1の場合はログをプログレスバーで標準出力,2の場合はエポックごとに1行のログを出力します.

試しに出力してみます。

prediction = model.predict(source, verbose=1)[0]
YOU: あ
1/1 [==============================] - 0s 365ms/step
ANSWER: から から から は は です が が が 了承 ます
YOU: い
1/1 [==============================] - 0s 14ms/step
ANSWER: 今日 から です です です も も どうぞ よろしく お願い お願い ください
YOU: う
1/1 [==============================] - 0s 15ms/step
ANSWER: 手 から は は です です が かか ので 了承 ます
YOU: え
1/1 [==============================] - 0s 16ms/step
ANSWER: から から から は は です が が が 了承 ます
YOU: お
1/1 [==============================] - 0s 15ms/step
ANSWER: 手 手 は は は は が が が が

こんな風になりました。

integers = [argmax(vector) for vector in prediction]の箇所はリスト内包表記で、integersリストに直接要素をappendするようです。普通のfor文でappendするより速度が速くなるらしいですね。それで追加している要素というのが、NumPyのargmax関数というのものです。argmax関数は多次元配列の中の最大値の要素を持つインデックスを返す関数だそうです。おそらく確率的にたかいvectorを返しているのだと思います。

あと、ややこしそうなのは次だけですね。

def translate(model, tokenizer, sources):
    predicted = list()
    for i, source in enumerate(sources):
        source = source.reshape((1, source.shape[0]))
        translation = predict_sequence(model, all_tokenizer, source)
        print('ANSWER: %s' % (translation))
        predicted.append(translation.split())

for文の中のin enumerate()のところが初めてみましたが、リストの要素とインデックス(カウンタ)を同時に取得できるそうです。

test = ["青","赤","黄色"]
for i, source in enumerate(test):
    print(i, source)

実行結果。

0 青
1 赤
2 黄色

なるほど。ただ、コード中にiを使用している箇所がないので、なぜここでenumerateを使ったのか謎です。試しに enumerateを取っても変わりませんでした。

ef translate(model, tokenizer, sources):
    predicted = list()
    for source in sources:
        # translate encoded source text
        source = source.reshape((1, source.shape[0]))
        translation = predict_sequence(model, all_tokenizer, source)
        print('ANSWER: %s' % (translation))
        predicted.append(translation.split())
YOU: あ
ANSWER: から から から は は です が が が 了承 ます
YOU: い
ANSWER: 今日 から です です です も も どうぞ よろしく お願い お願い ください
YOU: う
ANSWER: 手 から は は です です が かか ので 了承 ます
YOU: え
ANSWER: から から から は は です が が が 了承 ます
YOU: お
ANSWER: 手 手 は は は は が が が が

一通りコードを見てきましたが、あまりいじれそうな個所はなさそうですね。 やはり会話データを増やしていくしかないですね。

今日の結果

今日のAKBの呟きは51件でした。 要約するとこんな感じです。

ままぽんとぱぱぽんからの手紙に両親の愛に私も号泣。大森美優ちゃんは本当に素敵な人☺️チーム4にいるときはたくさん甘えてワガママも言ってくださいね元気に生きてくれてることに…""思ったよりみんな写真撮ってくれてて選べないから言葉だけで、、、、見てくれた皆さんありがとう?
生誕祭の時ぽんさんの両親からのお手紙を聞いた時感動しすぎてちょっと泣いちゃいました?
みゆぽんさんはいつも仲良くなってくれて心から大好…""センター試験追試配信ありがとうございました!先生役楽しかったしみんなと仲良くなれてよかった?✨#AKBグループセンター試験""?‍♀️?‍♀️""公演終わりました!美優ちゃんの生誕祭皆さんの声援が大きくて美優ちゃんへの愛情がすごく伝わってきました?❤️生誕祭の仕切りお手紙をちゃんと読めるか緊張してたけどなにより美優ちゃんの生誕祭の仕切りをやれて嬉しかっ…""今日は久しぶりに『手をつなぎながら』公演に出演させていただきましたー?!そしてみゆぽんさんの生誕祭!!お手紙やスピーチとっても感動しました?
'嬉しい': 6, '楽しい': 3, 'いい': 3, '美味しい': 2, 'すごい': 2, '可愛い': 1, 'やすい': 1, '仲良い': 1, '暖かい': 1, '優しい': 1, 'おいしい': 1, 'よい': 1, '大きい': 1}
'さん': 10, '生誕': 10, '祭': 10, 'ちゃん': 9, '今日': 8, 'ゆ': 8, '時': 7, '嬉しい': 6, '公演': 6, 'たくさん': 6, '手紙': 6, 'みんな': 5, '私': 5, '美優': 5, 'こと': 4, '写真': 4, '皆さん': 4, '是非': 4,
'する': 16, 'さん': 10, '生誕': 10, '祭': 10, 'ちゃん': 9, '今日': 8, 'ゆ': 8, 'なる': 8, 'くださる': 8, '時': 7, 'いる': 7, '嬉しい': 6, '公演': 6, 'たくさん': 6, '手紙': 6, 'くれる': 6,

f:id:rimt:20180918164825p:plain

f:id:rimt:20180918164801p:plain