kerasのoptimizerの状態を保存する
背景
新規データに対して逐次的に毎日学習するkerasのモデルを考える。
シンプルなモデルであればmodel.save()
とload_model()
を使えばなんの問題もない。
参考: Kerasのノウハウ覚え書き
しかし、Lambda層が入るとmodel.save()
ができなくなる。
参考: KerasのLambda層でreshapeしたとき、保存に失敗する(場合がある)話
追加の学習はせず予測のためだけに使うモデルなら、save_weights()
とかで重みだけ保存しておけばよいが、毎日新規データを学習したいため微妙。
というのは、optimizerがmomentumなどの状態を持っている場合、それも保存しておかなければならない。
以下の記事を参考に、optimizerの状態の保存と読み込みを行って、各種実験をしてみる。
参考: python - Save and load model optimizer state - Stack Overflow
実験
準備
必要なライブラリのインポート
from sklearn import datasets from sklearn import preprocessing from keras.models import Model from keras.layers import Input, Dense from keras.utils import np_utils from keras.optimizers import SGD from keras import backend as K
データの準備
iris = datasets.load_iris() X = iris.data Y = iris.target X = preprocessing.scale(X) Y = np_utils.to_categorical(Y)
モデルを作ってコンパイルする関数
def make_model(momentum=0.0, decay=0.0, nesterov=False): inputs = Input(shape=(4,)) outputs = Dense(3, activation="softmax", kernel_initializer="ones", bias_initializer="ones", name="output_layer")(inputs) model = Model(inputs=inputs, outputs=outputs) model.compile(optimizer=SGD(lr=0.01, momentum=momentum, decay=decay, nesterov=nesterov), loss='categorical_crossentropy') return model
実験1
optimizerの状態がない場合の学習。
model = make_model() model.fit(X, Y, epochs=10, verbose=2, batch_size=200)
- 0s - loss: 1.0986 Epoch 2/10 - 0s - loss: 1.0890 Epoch 3/10 - 0s - loss: 1.0796 Epoch 4/10 - 0s - loss: 1.0704 Epoch 5/10 - 0s - loss: 1.0613 Epoch 6/10 - 0s - loss: 1.0524 Epoch 7/10 - 0s - loss: 1.0437 Epoch 8/10 - 0s - loss: 1.0351 Epoch 9/10 - 0s - loss: 1.0267 Epoch 10/10 - 0s - loss: 1.0185
実験2
optimizerの状態がない場合の学習。
epochs=1
の学習をfor文で回す。
実験1と同じ結果。
model = make_model() for i in range(10): model.fit(X, Y, epochs=1, verbose=2, batch_size=200)
Epoch 1/1 - 0s - loss: 1.0986 Epoch 1/1 - 0s - loss: 1.0890 Epoch 1/1 - 0s - loss: 1.0796 Epoch 1/1 - 0s - loss: 1.0704 Epoch 1/1 - 0s - loss: 1.0613 Epoch 1/1 - 0s - loss: 1.0524 Epoch 1/1 - 0s - loss: 1.0437 Epoch 1/1 - 0s - loss: 1.0351 Epoch 1/1 - 0s - loss: 1.0267 Epoch 1/1 - 0s - loss: 1.0185
実験3
optimizerの状態がある場合の学習。
model = make_model(momentum=1, decay=1, nesterov=True) model.fit(X, Y, epochs=10, verbose=2, batch_size=200)
Epoch 1/10 - 0s - loss: 1.0986 Epoch 2/10 - 0s - loss: 1.0795 Epoch 3/10 - 0s - loss: 1.0610 Epoch 4/10 - 0s - loss: 1.0414 Epoch 5/10 - 0s - loss: 1.0209 Epoch 6/10 - 0s - loss: 0.9997 Epoch 7/10 - 0s - loss: 0.9781 Epoch 8/10 - 0s - loss: 0.9563 Epoch 9/10 - 0s - loss: 0.9343 Epoch 10/10 - 0s - loss: 0.9125
実験4
optimizerの状態がある場合の学習。
epochs=1
の学習をfor文で回す。
実験3と同じ結果。
model
がoptimizerの状態を保持している。
model = make_model(momentum=1, decay=1, nesterov=True) for i in range(10): model.fit(X, Y, epochs=1, verbose=2, batch_size=200)
Epoch 1/1 - 0s - loss: 1.0986 Epoch 1/1 - 0s - loss: 1.0795 Epoch 1/1 - 0s - loss: 1.0610 Epoch 1/1 - 0s - loss: 1.0414 Epoch 1/1 - 0s - loss: 1.0209 Epoch 1/1 - 0s - loss: 0.9997 Epoch 1/1 - 0s - loss: 0.9781 Epoch 1/1 - 0s - loss: 0.9563 Epoch 1/1 - 0s - loss: 0.9343 Epoch 1/1 - 0s - loss: 0.9125
実験5
optimizerの状態がない場合の学習。
モデルの作成と学習を繰り返す。
lossが変化しない。
for i in range(10): model = make_model() model.fit(X, Y, epochs=1, verbose=2, batch_size=200)
Epoch 1/1 - 0s - loss: 1.0986 Epoch 1/1 - 0s - loss: 1.0986 Epoch 1/1 - 0s - loss: 1.0986 Epoch 1/1 - 0s - loss: 1.0986 Epoch 1/1 - 0s - loss: 1.0986 Epoch 1/1 - 0s - loss: 1.0986 Epoch 1/1 - 1s - loss: 1.0986 Epoch 1/1 - 1s - loss: 1.0986 Epoch 1/1 - 1s - loss: 1.0986 Epoch 1/1 - 1s - loss: 1.0986
実験6
optimizerの状態がある場合の学習。
モデルの作成と学習を繰り返す。
実験5と同様にlossが変化しない。
for i in range(10): model = make_model(momentum=1, decay=1, nesterov=True) model.fit(X, Y, epochs=1, verbose=2, batch_size=200)
Epoch 1/1 - 1s - loss: 1.0986 Epoch 1/1 - 1s - loss: 1.0986 Epoch 1/1 - 1s - loss: 1.0986 Epoch 1/1 - 1s - loss: 1.0986 Epoch 1/1 - 1s - loss: 1.0986 Epoch 1/1 - 1s - loss: 1.0986 Epoch 1/1 - 1s - loss: 1.0986 Epoch 1/1 - 1s - loss: 1.0986 Epoch 1/1 - 1s - loss: 1.0986 Epoch 1/1 - 1s - loss: 1.0986
実験7
optimizerの状態がない場合の学習。
モデルの作成と学習を繰り返す。
モデル作成後に重みを読み込む。
実験1や実験2と同じ結果。
for i in range(10): model = make_model() if i != 0: model.get_layer("output_layer").set_weights(weights_dense) model.fit(X, Y, epochs=1, verbose=2, batch_size=200) weights_dense = model.get_layer("output_layer").get_weights()
Epoch 1/1 - 1s - loss: 1.0986 Epoch 1/1 - 1s - loss: 1.0890 Epoch 1/1 - 1s - loss: 1.0796 Epoch 1/1 - 1s - loss: 1.0704 Epoch 1/1 - 1s - loss: 1.0613 Epoch 1/1 - 1s - loss: 1.0524 Epoch 1/1 - 1s - loss: 1.0437 Epoch 1/1 - 1s - loss: 1.0351 Epoch 1/1 - 2s - loss: 1.0267 Epoch 1/1 - 2s - loss: 1.0185
実験8
optimizerの状態がある場合の学習。
モデルの作成と学習を繰り返す。
モデル作成後に重みを読み込む。
optimizerの状態の違いのため、実験3や実験4と異なる結果。
for i in range(10): model = make_model(momentum=1, decay=1, nesterov=True) if i != 0: model.get_layer("output_layer").set_weights(weights_dense) model.fit(X, Y, epochs=1, verbose=2, batch_size=200) weights_dense = model.get_layer("output_layer").get_weights()
Epoch 1/1 - 2s - loss: 1.0986 Epoch 1/1 - 2s - loss: 1.0795 Epoch 1/1 - 2s - loss: 1.0703 Epoch 1/1 - 2s - loss: 1.0612 Epoch 1/1 - 2s - loss: 1.0523 Epoch 1/1 - 2s - loss: 1.0350 Epoch 1/1 - 2s - loss: 1.0266 Epoch 1/1 - 2s - loss: 1.0183 Epoch 1/1 - 2s - loss: 1.0102 Epoch 1/1 - 2s - loss: 1.0023
実験9
optimizerの状態がある場合の学習。
モデルの作成と学習を繰り返す。
モデル作成後に重みとoptimizerの状態を読み込む。
実験3や実験4と同じ結果。
for i in range(10): model = make_model(momentum=1, decay=1, nesterov=True) model._make_train_function() if i != 0: model.get_layer("output_layer").set_weights(weights_dense) model.optimizer.set_weights(weights_optimizer) model.fit(X, Y, epochs=1, verbose=2, batch_size=200) weights_dense = model.get_layer("output_layer").get_weights() weights_optimizer = model.optimizer.get_weights()
Epoch 1/1 - 2s - loss: 1.0986 Epoch 1/1 - 0s - loss: 1.0795 Epoch 1/1 - 0s - loss: 1.0610 Epoch 1/1 - 0s - loss: 1.0414 Epoch 1/1 - 0s - loss: 1.0209 Epoch 1/1 - 0s - loss: 0.9997 Epoch 1/1 - 0s - loss: 0.9781 Epoch 1/1 - 0s - loss: 0.9563 Epoch 1/1 - 0s - loss: 0.9343 Epoch 1/1 - 0s - loss: 0.9125
まとめ
model.save()
が使える場合
->model.save()
を使えば良い。model.save()
が使えない場合- 再学習しない場合
-> モデルの重みのみ保存すれば良い。 - 再学習する必要がある場合
-> モデルの重みとoptimizerの状態を保存すれば良い。
- 再学習しない場合
ちなみに、weights_dense
とweights_optimizer
の中身は以下。
weights_dense
[array([[0.9180371 , 1.00887 , 1.0730928 ], [1.0696087 , 0.94541067, 0.9849805 ], [0.8941715 , 1.0231149 , 1.0827136 ], [0.8982664 , 1.0132247 , 1.0885084 ]], dtype=float32), array([0.9999076, 1.000105 , 0.9999874], dtype=float32)]
weights_optimizer
[10, array([[-0.00934379, 0.00099744, 0.00834635], [ 0.00797938, -0.0062994 , -0.00167998], [-0.01209093, 0.00263835, 0.00945257], [-0.01162081, 0.00148986, 0.01013095]], dtype=float32), array([-2.1416694e-05, 2.4464378e-05, -3.0475137e-06], dtype=float32)]
なので、pickleとかで保存すれば良い。
model._make_train_function()
は何をしているかわからない。
けど、それがないとmodel.optimizer.set_weights(weights_optimizer)
でエラーになる。