ChainerでLSTM言語モデルとミニバッチ学習の実装

2016年04月10日

概要

  • LSTMに日本語の文章を学習させた
  • LSTMでミニバッチ学習をするための実装の紹介

はじめに

ChainerでLSTMを実装するサンプルはいくつかありますが、英語データが対象だったり情報が古かったりしてそのままでは使えないケースがあります。

そこでChainer 1.7でLSTMを実装してみました。

コードはGitHubにあります。

使い方

適当なフォルダを作成し、そこに訓練用データを入れてください。

データはtxtファイルに文章を改行区切りで入力します。

Twitterなどから取得したツイートのテキストデータをそのまま使えます。

ファイル名やファイル数に制限はありません。フォルダ内のすべてのファイルが読み込まれます。

学習にはtrain.pyを使い、文章生成にはvalidate.pyを使います。

Windows 7 + GTX 970Mで動作確認済みです。

日本語を扱う関係で他の環境では問題が生じるかもしれません。

モデルを保存する時、ファイルサイズは100MBを超えることもあるため速いディスクかSSD上で動かすことをおすすめします。

ミニバッチ学習

RNN系のニューラルネットの学習では、入力や出力の系列の長さがデータごとに異なっているため、そのままではミニバッチ学習を行えません。

そこで、バッチ中で最も長い系列のデータに合わせて、他の短い系列の末尾を-1で埋めます。

参考:可変長データのミニバッチをchainerのwhereでやる

たとえば以下の2つのデータからなるミニバッチを考えます。(私の実装では0は終端記号になります)

A: [11, 12, 13,  0]
B: [23, 24, 25, 26, 27,  0]

各データは長さが異なるので、一番長いBに合わせてAの末尾を-1で埋めて拡張します。

A: [11, 12, 13,  0, -1, -1]
B: [23, 24, 25, 26, 27,  0]

これらを合わせたミニバッチCは以下のようになります。

C: [[11, 12, 13, -1, -1], [23, 24, 25, 26, 27]]

これは転置すると以下のようになり、インデックス0から系列ごとに取り出せるようになります。

C.T: [[11, 23], [12, 24], [13, 25], [0, 26], [-1, 27], [-1, 0]]

ただし、Chainer 1.7の時点で、EmbedIDに-1を入力するとnanが返ってきておかしなことになるため、コードに工夫をする必要があります。

以下のように前状態$c0$の-1の要素をすべて終端記号の0に置き換えます。(次状態$c1$は-1のままにしておきます)

for c0, c1 in zip(seq_batch[:-1], seq_batch[1:]):
	c0[c0 == -1] = 0
	c0 = Variable(xp.asarray(c0, dtype=np.int32))
	c1 = Variable(xp.asarray(c1, dtype=np.int32))

なぜ-1で埋めるかというと、softmax_cross_entropyの正解ラベルに-1を指定すると、対応する入力は無視され、誤差も$0$になってくれるからです。

終端記号から-1への遷移を学習する必要はありませんので。

loss = F.softmax_cross_entropy(output, c1)

追記

Chainer 1.8に対応させました。

1.8からはEmbedIDにignore_label=-1を指定することで-1を無視できるようになっています。

ただし1.7含む旧バージョンでは動作しませんのでご注意ください。