毎日テキストマイニング

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

2018/09/12【81日目】chatbotで使ったコードを見直していく(前編)

ちょっと前から作り続けているchatbotをこのまま続けるにしろ、この辺で終わるにしろ、取り敢えずコードを見直していきたいと思います(自分で書いた訳ではないので)。

コードを見直していく

まず、真っ先に見る必要があるのはここのデータの保存というですね。

def save_clean_data(sentences, filename):
    dump(sentences, open(filename, 'wb'))
    print('Saved: %s' % filename)

関数名が綺麗なデータの保存と書いてあるだけあって、やっていることはデータの保存らしいです。

プログラムのオブジェクトは役目を終えたら消えてしまうので、後で使う際には保存する必要があるみたいです。これを漬け物という意味でピックルと呼ぶらしい。本来は次のようにしてpickle.dump()としているようです。

import pickle

pickle.dump(sentences, open(filename, 'wb'))

今回のコードだと、from pickle import load、from pickle import dumpとしていますので、pickleを省けるようです。

ちなみにpickleの呼び出しはこれですね。

def load_clean_sentences(filename):
    return load(open(filename, 'rb'))

次です。

def create_tokenizer(lines):
    tokenizer = Tokenizer()
    tokenizer.fit_on_texts(lines)
    return tokenizer

ここは文字のベクトル化ですね。.fit_on_texts()メソッドが初めて出てきたような気がしますが、kerasのドキュメントによると、学習に使う文章のリストを渡すだけのようです。同様に、テキストを時系列データに変換するメソッドもあります。次の箇所ですね。

def encode_sequences(tokenizer, length, lines):
    X = tokenizer.texts_to_sequences(lines)
    X = pad_sequences(X, maxlen=length, padding='post')
    return X

次の箇所に行きます。

def encode_output(sequences, vocab_size):
    ylist = list()
    for sequence in sequences:
        encoded = to_categorical(sequence, num_classes=vocab_size)
        ylist.append(encoded)
    y = array(ylist)
    y = y.reshape(sequences.shape[0], sequences.shape[1], vocab_size)
    return y

ここが何をやっているのかいまいち分かりませんが、yを0と1に変換しているのかと。encoded = to_categoricalのところのto_categorical関数はone-hotエンコーディングをしているそうです。この関数が使われているtrainYを出力してみますと、確かにone-hotエンコーディングをしているようです。

print(trainY)

実行結果。

[[[ 0.  0.  0. ...,  0.  0.  0.]
  [ 0.  0.  0. ...,  0.  0.  0.]
  [ 0.  0.  0. ...,  0.  0.  0.]
  ..., 
  [ 1.  0.  0. ...,  0.  0.  0.]
  [ 1.  0.  0. ...,  0.  0.  0.]
  [ 1.  0.  0. ...,  0.  0.  0.]]
(以下、省略)

ちなみに、テストの方を出力してみたら、リストに何も入っていませんでした。datasetが29個しかないのに、29個後を分割してもそりゃあ何も入らないですね。後で修正しようと思います。

次はLSTM モデルの構築。

def define_model(vocab, timesteps, n_units):
    model = Sequential()
    model.add(Embedding(vocab, n_units, input_length=timesteps, mask_zero=True))
    model.add(LSTM(n_units))
    model.add(RepeatVector(timesteps))
    model.add(LSTM(n_units, return_sequences=True))
    model.add(TimeDistributed(Dense(vocab, activation='softmax')))
    return model

まずはmodel = Sequential()で系列モデルを与えて、後は.add() メソッドでレイヤーを追加していくようです。 一番最初のEmbedding層は、ドキュメントによると、正の整数(インデックス)を固定次元の密ベクトルに変換します.とのことです(何言ってんだ)。まぁ、よく分かりませんが、優れたニューラルネットワークを作るのには必要だそうです。ちなみに一番目にしか置けないようです。

RepeatVector(n)というのはn回だけ繰り返す層のことだそうです。今回はRepeatVector(timesteps)としているので、timestepsの分だけ繰り返すらしいです。ちなみに今回のtimestepはおそらくinputした回数だと思われます。

LSTM(n_units, return_sequences=True)というのは、76日目で見ましたが、kerasで層を積み上げるには、完全な出力シーケンス(3次元テンソル)を返す必要があるので、return_sequences=Trueを指定する必要があるみたいです。

最後の箇所はsoftmax関数を導入しているようです。 長くなったので、後半はまた明日。

今日の結果

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

楽しみにしててね〜?‍♀️?
#AiKaBu…""アイドル修業中♡公演ありがとうございました〜?
"21時からSHOWROOMやりまーす☆""いとこと一緒にお友達に会いに行ってきました( ̄▽ ̄)初ミーアキャット、、♡思った以上に積極的でびっくり(°_°)""ちゃんと全部の欄埋めて、ガチな住所も書いて私のLINEに送ってくださいました??"
嬉しい': 5, 'すごい': 4, '良い': 4, '切ない': 3, '楽しい': 2, 'うれしい': 2, 'よい': 2, '可愛い': 2, '寂しい': 1, '強い': 1, '凄い': 1, '短い': 1, 'いい': 1, '大きい': 1, '重い': 1, '珍しい': 1, '長い': 1
'公演': 9, '配信': 7, '私': 6, '明日': 6, '今日': 6, '嬉しい': 5, 'たくさん': 5, 'すごい': 4, '良い': 4, 'の': 4, '久しぶり': 4, 'こと': 4, 'タイ': 4, 
'する': 23, 'くださる': 11, '公演': 9, '配信': 7, 'なる': 7, '私': 6, '明日': 6, '今日': 6, 'くる': 6, 'てる': 6, '見る': 6, '嬉しい': 5, 'たくさん': 5, 'ある': 5, '頑張る': 5, 'すごい': 4, '良い': 4, 'の': 4, '久しぶり': 4, 'こと': 4, 'タイ': 4, 'れる': 4, '楽しむ': 4, 'みる': 4, '来る': 4, 'がんばる': 4,

f:id:rimt:20180916234810p:plain

f:id:rimt:20180916234831p:plain