こしあん
2018-11-14

KerasのModelCheckpointのsave_best_onlyは何を表すのか?


Kerasには「モデルの精度が良くなったときだけ係数を保存する」のに便利なModelCheckpointというクラスがあります。ただこのsave_best_onlyがいまいち公式の解説だとピンとこないので調べてみました。

ModelCheckpointとは?

公式ドキュメントより

keras.callbacks.ModelCheckpoint(filepath, monitor=’val_loss’, verbose=0, save_best_only=False, save_weights_only=False, mode=’auto’, period=1)

各エポック終了後にモデルを保存します.

filepath、monitorとかはわかりやすいんでいいんですが、この「save_best_onlyって何?なんでしかもデフォルトでFalseになってるの?」というのが気になって仕方がありませんでした。この公式解説が紛らわしくて

save_best_only: save_best_only=Trueの場合,監視しているデータによって最新の最良モデルが上書きされません.

これを読むと、「じゃあsave_best_only=Falseなら、より精度なりが良くなったときにファイルを上書きしないでどんどん新しいファイルを作るの?」と思ってしまいます。自分は一時期そう思っていました。ただこれは勘違いです。

もしsave_best_only=False(デフォルト)で実行すると

試しにModelCheckpointの「save_best_only=False」で実行してみます。データはMNISTとします。

from keras.layers import Dense, Input, Flatten
from keras.models import Model
from keras.callbacks import ModelCheckpoint

from keras.datasets import mnist
from keras.utils import to_categorical

(X_train, y_train), (X_test, y_test) = mnist.load_data()
X_train, X_test = X_train/255.0, X_test/255.0
y_train, y_test = to_categorical(y_train), to_categorical(y_test)

input = Input((28, 28))
x = Flatten()(input)
x = Dense(128, activation="relu")(x)
x = Dense(10, activation="softmax")(x)

model = Model(input, x)
model.compile("adam", "categorical_crossentropy", ["acc"])

cp = ModelCheckpoint("weights.hdf5", monitor="val_loss", verbose=1,
                     save_best_only=False, save_weights_only=True)
model.fit(X_train, y_train, batch_size=128, epochs=20, callbacks=[cp],
        validation_data=(X_test, y_test))

ここではverbose=1としてログに書き出すようにしています。結果は次のようになります。

Epoch 00011: saving model to weights.hdf5
Epoch 12/20
60000/60000 [==============================] - 3s 49us/step - loss: 0.0208 - acc: 0.9950 - val_loss: 0.0717 - val_acc: 0.9780

Epoch 00012: saving model to weights.hdf5
Epoch 13/20
60000/60000 [==============================] - 3s 49us/step - loss: 0.0178 - acc: 0.9956 - val_loss: 0.0748 - val_acc: 0.9781

Epoch 00013: saving model to weights.hdf5
Epoch 14/20
60000/60000 [==============================] - 3s 49us/step - loss: 0.0146 - acc: 0.9968 - val_loss: 0.0745 - val_acc: 0.979

ここのval_lossを注意深く見てください。val_lossが0.0717→0.0748→0.0745と上がっているにもかかわらず、モデルが上書きされていますよね?。これがsave_best_only=Falseの正体です。

つまり、「save_best_only=False」なら、モニターしている評価値(この場合はval_loss)悪くなったときに上書きしないということではなくて、periodの引数で指定したのエポックごとに必ず保存するという意味なのです。試しにperiod=2としてみましょう。

Epoch 17/20
60000/60000 [==============================] - 3s 49us/step - loss: 0.0101 - acc: 0.9981 - val_loss: 0.0806 - val_acc: 0.9768
Epoch 18/20
60000/60000 [==============================] - 3s 48us/step - loss: 0.0088 - acc: 0.9984 - val_loss: 0.0842 - val_acc: 0.9761

Epoch 00018: saving model to weights.hdf5
Epoch 19/20
60000/60000 [==============================] - 3s 48us/step - loss: 0.0070 - acc: 0.9990 - val_loss: 0.0790 - val_acc: 0.9795
Epoch 20/20
60000/60000 [==============================] - 3s 50us/step - loss: 0.0077 - acc: 0.9984 - val_loss: 0.0877 - val_acc: 0.9771

Epoch 00020: saving model to weights.hdf5

「save_best_only=False, period=2」なら、モニターしている評価値が上がろうが下がろうが、2エポックごとに自動的に保存しているというのが確認できました。

一番いい精度を頼みたい場合(save_best_only=Trueなら)

GANとかモデルが崩壊するおそれのあるケースでもない限り、かなりの場合で「一番いい精度(少ない損失)の係数だけ保存していればいい」と思います。その場合は、save_best_only=Trueにしましょう。これをすることで想定された挙動になります。

ModelCheckpointの設定を以下のように変えます。デフォルトでsave_best_only=Falseなので、設定変更は必須です。

cp = ModelCheckpoint("weights.hdf5", monitor="val_loss", verbose=1,
                     save_best_only=True, save_weights_only=True)
Epoch 13/20
60000/60000 [==============================] - 3s 50us/step - loss: 0.0189 - acc: 0.9954 - val_loss: 0.0675 - val_acc: 0.9798

Epoch 00013: val_loss improved from 0.06868 to 0.06753, saving model to weights.hdf5
Epoch 14/20
60000/60000 [==============================] - 3s 50us/step - loss: 0.0167 - acc: 0.9960 - val_loss: 0.0744 - val_acc: 0.9773

Epoch 00014: val_loss did not improve from 0.06753

このように悪化した場合は「val_loss did not improve from 0.06753」と表示され、改善したときだけ保存されます。おそらくこれが大多数の人がやりたかったことではないでしょうか。

ちなみにモニターする評価値には訓練損失(loss)や訓練精度(acc)を入れることもできます。試しに訓練精度を入れてみましょう。もし訓練損失でいいときは、「monitor=”loss”」でOKです。

cp = ModelCheckpoint("weights.hdf5", monitor="acc", verbose=1,
                     save_best_only=True, save_weights_only=True)
Epoch 1/20
60000/60000 [==============================] - 4s 66us/step - loss: 0.3603 - acc: 0.9002 - val_loss: 0.1877 - val_acc: 0.9450

Epoch 00001: acc improved from -inf to 0.90017, saving model to weights.hdf5

このようにOKですね。デフォルトで組み込みの損失関数や評価関数では、大小の変化で改善か悪化かを自動的に判断してくれるようです。自作の関数だと「mode」の引数で設定必須だと思います

まとめ

  • ModelCheckpointを使うとモデルの係数を自動的に保存してくれる
  • しかし、デフォルトでsave_best_only=Falseなので、改善しようが悪化しようがなんでもかんでも上書きしようとする。save_best_only=Trueへの変更を忘れずに

Related Posts

条件に応じた配列の要素の抽出をTensorFlowで行う... Numpyで条件を与えて、インデックスのスライスによって配列の要素を抽出する、というようなケースはよくあります。これをTensorFlowのテンソルでやるのにはどうすればいいのでしょうか?それを見ていきます。 Numpyではこんな例 例えば、5×5のランダムな行列をデータとします。この配列を左...
TensorFlowでコサイン類似度を計算する方法... TensorFlowで損失関数や距離関数に「コサイン類似度」を使うことを考えます。Scikit-learnでは簡単に計算できますが、同様にTensorFlowでの行列演算でも計算できます。それを見ていきます。 コサイン類似度 コサイン類似度は、ユークリッド距離と同様に距離関数として使われる評価...
転移学習でネットワーク内でアップサンプリングする方法(Keras)... 転移学習でインプットのサイズを揃えなければいけないことがありますが、これをRAM(CPU)上でやるとメモリが不足することがあります。転移学習の重みをそのまま使い、事前にアップサンプリングレイヤーを差し込む方法を紹介します。 関連記事とバックグラウンド まず前提知識としてCPU側でアップサンプリ...
Numpyの配列に対して「最も多く存在する値」を求める方法... アンサンブル学習などで、Numpyの配列のある軸に対して「最も多く存在する値」を求めたい、つまり「多数決」をしたいことがあります。その方法を見ていきます。 最も大きい値がmax, 最も大きい値が存在するインデックスがargmax, では「最も多く存在する値」は? 配列のある軸に対して、「最も大...
KerasのCallbackを使って継承したImageDataGeneratorに値が渡せるか確かめ... Kerasで前処理の内容をエポックごとに変えたいというケースがたまにあります。これを実装するとなると、CallbackからGeneratorに値を渡すというコードになりますが、これが本当にできるかどうか確かめてみました。 想定する状況 例えば、前処理で正則化に関係するData Augmenta...

Add a Comment

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