kerasのモデルの定義方法メモ

投稿者: | 2018年8月5日

最近、kerasで遊ぶ機会が増えてきた。モデルの作り方が複数あってちょっと紛らわしいのでメモがてらに整理する。
なお、自分はtensorflowの上のkerasを利用している。

モデルの定義方法の種類と制限

kerasには主に3種類のモデルの定義方法がある。

  • Sequenceを利用するもの
  • Functional APIを利用するもの
  • Modelクラスを継承するもの

Modelを継承する場合の注意点

Modelを継承する場合には以下の注意が必要である。

  • model.saveが動作しない、save_weightsは動作するのでそちらを使う

eager_executionと組み合わせるときの注意点

eager_executionを利用するときに以下の制限がある。

  • optimizerがkeras側のものは動作しないのでtensorflow側のoptimizerを利用する
  • tensorboardのcallbackが上手く動作しないので自前でやる必要がある

Sequenceを利用するモデル定義

簡単なものだと以下の様なSequenceを使った定義ができる。
とりあえずの動作の確認には便利だが、分岐するネットワークなどの定義は出来ない。

from keras.models import Sequential
from keras.layers import Dense, Activation

model = Sequential([
    Dense(32, input_shape=(784,)),
    Activation('relu'),
    Dense(10),
    Activation('softmax'),
])

Functional APIを利用するモデル定義

Functional APIを利用して以下のようにも定義できる。

from keras.layers import Input, Dense
from keras.models import Model

# This returns a tensor
inputs = Input(shape=(784,))

# a layer instance is callable on a tensor, and returns a tensor
x = Dense(64, activation='relu')(inputs)
x = Dense(64, activation='relu')(x)
predictions = Dense(10, activation='softmax')(x)

# This creates a model that includes
# the Input layer and three Dense layers
model = Model(inputs=inputs, outputs=predictions)
model.compile(optimizer='rmsprop',
              loss='categorical_crossentropy',
              metrics=['accuracy'])
model.fit(data, labels)  # starts training

Modelクラスを継承する定義

Modelクラスを継承して独自のモデルクラスを作成することもできる。
eager_executionと組み合わせると強力。

keras開発者のFrançois Chollet さんの以下のtweetのように、簡潔にRNNなどの記述ができて、デバッグなどもcallに適当にbreak入れたり、printしてデバッグすることも出来ます。

もっと単純な例だと、以下のような感じです。

import keras

class SimpleMLP(keras.Model):

    def __init__(self, use_bn=False, use_dp=False, num_classes=10):
        super(SimpleMLP, self).__init__(name='mlp')
        self.use_bn = use_bn
        self.use_dp = use_dp
        self.num_classes = num_classes

        self.dense1 = keras.layers.Dense(32, activation='relu')
        self.dense2 = keras.layers.Dense(num_classes, activation='softmax')
        if self.use_dp:
            self.dp = keras.layers.Dropout(0.5)
        if self.use_bn:
            self.bn = keras.layers.BatchNormalization(axis=-1)

    def call(self, inputs):
        x = self.dense1(inputs)
        if self.use_dp:
            x = self.dp(x)
        if self.use_bn:
            x = self.bn(x)
        return self.dense2(x)

model = SimpleMLP()
model.compile(...)
model.fit(...)

注意点として、Modelクラスは__setattr__とかで各レイヤの管理を行っており、直接 self.layer_name = SomeLayer()  というコードを書かないとoptimizerから変数が見えなくて全く学習がされていないなどの罠がある。

複数のレイヤをfor文などを利用して上手く定義できたと思ったら上手く動いていないということが起こり得るので注意が必要。

また、現在の実装だとfitの際にモデルが利用するメモリの計算にバグがあるようで、以下の様にサイズを明示してからfitしないとGPUのメモリが枯渇して動作しない。

# batch sizeが1相当のデータを指定
dummy_x = np.zeros((1, 28, 28, 1))
model._set_inputs(dummy_x)

関連情報: https://github.com/tensorflow/tensorflow/issues/19241

まとめ

とりあえず記法的にはModelを継承してeager_executionを利用しているのが幸せそう。ただし、冒頭に書いた制限やバグもちらほらあるようなので注意は必要。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です