自然言語処理の深遠

Deep Dive Into Natural Language Processing

Keras の fit と fit_generator の速度を比較する

Kerasでモデルを学習させるときによく使われるのが、fitメソッドfit_generatorメソッドだ。 各メソッドについて簡単に説明すると、fitは訓練用データを一括で与えると内部でbatch_size分に分割して学習してくれる。 それに対し、fit_generatorではbatch_size分のデータを生成するgeneratorを自分で作成して与える必要がある。 ミニバッチごとに入力の前処理をしたい場合なんかはfit_generatorを使うことになる。

本記事では、これらfitメソッドとfit_generatorメソッドを使って同じモデルを学習させ、学習時間を比較してみる。 なぜ比較するのかというと、「ひょっとして学習時間に差があるのでは?」と気になったからだ。 検証してはっきりさせておくことで、fitとfit_generatorの使い分けに役立てられればいいと思う。

検証は以下のように進める:

  • LSTMによる文書分類モデルを構築
  • fitとfit_generatorそれぞれで学習時間を計測

検証に用いたコードはこちら

モデルの構築

まずは検証に使うモデルを構築する。 文書分類タスクとして、IMDBデータセットを使った評価分析を行うことにする。 また、モデルとしては単純なLSTMのネットワークを構築する。 モデルの構築について順をおって説明する。

まずは必要なモジュールをインポートする。 今回は単純なモデルなので、Functional APIではなくSequentialモデルでモデルを構築していく。 インポートのコードは以下の通り:

import numpy as np
from keras.preprocessing import sequence
from keras.models import Sequential
from keras.layers import Dense, Embedding
from keras.layers import LSTM
from keras.datasets import imdb

インポートの次はハイパーパラメータを設定する。 今回は語彙数を20000語、文長は80に制限し、バッチサイズは32とした。 ハイパーパラメータの設定は以下の通り:

max_features = 20000
maxlen = 80
batch_size = 32

次に、データのロードと前処理を行う。 ロードしたデータはミニバッチ時に効率よく計算するためにpaddingして長さを揃える。 本来はfit_generatorに与えるgenerator内で前処理すべきだと思うが、今回はfitとfit_generatorの速度を比較したいため、予め前処理して条件を揃える。 コードは以下の通り:

(x_train, y_train), (x_test, y_test) = 
imdb.load_data(num_words=max_features)

x_train = sequence.pad_sequences(x_train, maxlen=maxlen)
x_test = sequence.pad_sequences(x_test, maxlen=maxlen)

いよいよモデルを構築する。先にも書いたように、今回は単純なモデルなのでSequentialモデルで構築する。 モデルのアーキテクチャは、分散表現をLSTMに入力し、LSTMの出力結果を全結合層に与えてneg/posの確率を出力するというものだ。 モデルを定義するコードは以下の通り:

model = Sequential()
model.add(Embedding(max_features, 128))
model.add(LSTM(128, dropout=0.2, recurrent_dropout=0.2))
model.add(Dense(1, activation='sigmoid'))

model.compile(loss='binary_crossentropy',
              optimizer='adam',
              metrics=['accuracy'])

ここまででデータの準備とモデルの構築は完了した。 次は学習を行っていく。

fitメソッドによる学習

いよいよfitメソッドを使って構築したモデルを学習させる。 コードは以下のように書ける:

model.fit(x_train, y_train,
          batch_size=batch_size,
          epochs=1,
          validation_data=(x_test, y_test))

学習結果は以下の通り:

25000/25000 [==============================] - 329s - loss: 0.4559 - acc: 0.7876 - val_loss: 0.3681 - val_acc: 0.8416

学習は329秒で完了している。

fit_generatorメソッドによる学習

次にfit_generatorメソッドで学習を行う。 fit_generatorメソッドには、データを生成するジェネレータとステップ数を与える必要がある。 そのためのジェネレータとステップ数を生成するコードは以下の通り:

def batch_iter(data, labels, batch_size, shuffle=True):
    num_batches_per_epoch = int((len(data) - 1) / batch_size) + 1

    def data_generator():
        data_size = len(data)
        while True:
            # Shuffle the data at each epoch
            if shuffle:
                shuffle_indices = np.random.permutation(np.arange(data_size))
                shuffled_data = data[shuffle_indices]
                shuffled_labels = labels[shuffle_indices]
            else:
                shuffled_data = data
                shuffled_labels = labels

            for batch_num in range(num_batches_per_epoch):
                start_index = batch_num * batch_size
                end_index = min((batch_num + 1) * batch_size, data_size)
                X = shuffled_data[start_index: end_index]
                y = shuffled_labels[start_index: end_index]
                yield X, y

    return num_batches_per_epoch, data_generator()

train_steps, train_batches = batch_iter(x_train, y_train, batch_size)
valid_steps, valid_batches = batch_iter(x_test, y_test, batch_size)

いよいよfit_generatorメソッドを使って構築したモデルを学習させる。 コードは以下のように書ける:

model.fit_generator(train_batches, train_steps,
                    epochs=1, 
                    validation_data=valid_batches,
                    validation_steps=valid_steps)

学習結果は以下の通り:

782/782 [==============================] - 307s - loss: 0.4601 - acc: 0.7791 - val_loss: 0.3605 - val_acc: 0.8416

学習には307秒かかっている。

結論

fitの学習に329秒とfit_generatorの学習に307秒かかったので学習時間はほぼ同じという結果になった。 したがって、使い分けとしては、予め前処理しておける場合はfitを使い、ミニバッチごとに前処理したい場合はfit_generatorを使えば良さそう。