Python
自然言語処理
ニューラルネットワーク
Keras
seq2seq

ニューラルネットワークをつかって推理小説家をつくる

はじめに

これまで(前回前々回)いろいろやってきましたが、ようやく本題に入れそうです。
Kerasを使ってSeq2Seqを組み、推理小説のデータを学習させます。
これで、文章生成すれば、すばらしい推理小説家が作れるはずです!

学習データ

学習に使ったデータは、青空文庫から以下の作家の
江戸川乱歩、夢野久作、大阪圭吉、小栗虫太郎、海野十三
の全小説を用います。

用いた学習データの詳細は、次のようになっています。

ファイル容量: 36M
文章数: 255737
非ユニーク単語数: 6685052
ユニーク単語数: 83660

これらのデータを、mecabを用いて単語ごとに区切り、word2vecを用いて、128次元のベクトルにします。ベクトル化された単語を元の文章と同じようになぎ合わせ文章をベクトルで表現します。文章ベクトルを学習データとして用います。

モデル

モデルは、前回作ったもの利用します。
input_dim、output_dimはそれぞれ128次元、隠れ層は256次元です。
入力は単語ベクトルを利用しているので、出力層の出力はベクトルになるようにsoftmaxではなく線形結合を用いています。

モデルのコードと図はそれぞれ以下のようになります。

input_dim = self.word_feat_len
output_dim = self.word_feat_len

encoder_inputs = Input(shape=(None, input_dim))
encoder_outputs, state_h, state_c = LSTM(latent_dim, return_state=True)(encoder_inputs)
encoder_states = [state_h, state_c]

decoder_inputs = Input(shape=(None, input_dim))
decoder_lstm = LSTM(self.latent_dim, return_sequences=True, return_state=True)
decoder_outputs, _, _ = self.decoder_lstm(decoder_inputs, initial_state=encoder_states)
decoder_dense = Dense(output_dim, activation='linear')
decoder_outputs = decoder_dense(decoder_outputs)

self.sequence_autoencoder = Model([encoder_inputs, decoder_inputs], decoder_outputs)

seq2seq_fig5.png

学習

今回は、bucket_sizeを[5, 10]に固定しました。つまり、全文章から、入力文章長と出力文章長がそれぞれ5、10となる文章のみを学習させます。

バッチサイズは70、epoch数は1、これを1stepとし、17500stepの学習を行いました。

最適化関数、損失関数はそれぞれ以下を用いました。

最適化関数 : RMSprop(lr=0.001, rho=0.9, epsilon=1e-08, decay=0.0)
損失関数 : mean_squared_error

 結果

次のグラフは。lossとval_lossのstepごとの推移を表しています。(横軸はepochと書いてますが、step数だと思ってください....。)
graph_loss.png

グラフを見ると、lossとval_lossともに、2500stepほどで完全に収束しているように見えます。lossとval_lossの最終的な値は、それぞれ、loss: 2.8645e-04 - val_loss: 1.2736となりました。lossは十分小さくなっていますが、val_lossは小さくなっていません。lossとval_lossの差が大きいため、過学習の可能性が考えられます。

次に、accuracyとval_accuracyのstepごとの推移を表したグラフです。lossのグラフと同様に横軸はstep数です。

graph_acc.png

accuracyとval_accuracyの最終的な値は、acc: 0.9750 - val_acc: 0.2238となりました。早い段階で、accuracyとval_accuracyも大きく変化しなくなっています。accuracyは、十分に1に小さい値になってますが、val_accuracyはいまいちです。こちらもlossと同様に、過学習している可能性があります。

学習データに対して、モデルが複雑なときは過学習が起きやすいです。今回のモデルのパラメタ総数は2,691,200でした。学習に用いたデータは、ユニークな単語数が83,660、文章数が255,737で、これらを単語毎に128次元のベクトルに圧縮したものです。約2百万のパラメタを決定するためには、学習データが少なすぎることはないと思います(この辺は直感的な感覚ですが....)。とすれば、学習に使用したデータにまだ、ノイズが混ざっていた可能性があります。また、batch_sizeが小さい場合にも過学習は起きる可能性がありますが、今回のbatch_sizeは70としています。他のうまく文章生成が出来ている論文やコードと見比べても小さすぎるようには思えません。今回用いた、最適化関数のパラメタはkerasのデフォルトのものを利用しましたが、これらを変更する必要があるかもしれません。もしくは、別の最適化関数を使うか、Dropoutを使う必要があるかもしれません。

まとめ

小説家のデータを使って、Seq2Seqを学習させ、文章生成を行いました。過学習している感じがありました。次は、メタパラメタ(ニューロンの数や隠れ層の層数、batch_size、epoch数)を決定する方法をもう少し詳しく調べて見たいと思います。(というか、他のブログなんかを漁ると、学習には何日もかかるらしいですが、今回の学習ではlossやaccが収束する2500stepまで1、2時間ほどしかかかってません....なんか間違ってるんですかね.....)