こしあん
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

Chainerで画像の前処理やDataAugmentationをしたいときはDatasetMixin... Chainerにはデフォルトでランダムクロップや標準化といった、画像の前処理やDataAugmentation用の関数が用意されていません。別途のChainer CVというライブラリを使う方法もありますが、chainer.dataset.DatasetMixinを継承させて独自のデータ・セットを定...
Numpyの配列に対して「最も多く存在する値」を求める方法... アンサンブル学習などで、Numpyの配列のある軸に対して「最も多く存在する値」を求めたい、つまり「多数決」をしたいことがあります。その方法を見ていきます。 最も大きい値がmax, 最も大きい値が存在するインデックスがargmax, では「最も多く存在する値」は? 配列のある軸に対して、「最も大...
Kerasでランドマーク検出用の損失関数を作る上でのポイント... ランドマーク検出やオブジェクト検出では、yに最初に物体やランドマークが存在する確率をおいて、それ以降に座標を配置するというようなデータ構造を取ります。その場合、カスタム損失関数を定義する必要が出てきますが、どのように定義するれば良いでしょうか。それを見ていきます。 Kerasの損失関数 分類問...
Numpyの配列をN個飛ばしで列挙する簡単な方法... Numpyの配列から奇数番目、偶数番目の要素を取り出したいときが稀によくあります。インデックスの配列を定義する必要があるのかなと思いますが、とても簡単な方法があります。それを見ていきましょう。 基本は「::スキップしたい間隔」 例として、0~9までの配列をとります。 >>>...
Kerasで転移学習用にレイヤー名とそのインデックスを調べる方法... Kerasで転移学習をするときに、学習済みモデルのレイヤーの名前と、そのインデックス(何番目にあるかということ)の対応を知りたいことがあります。その方法を解説します。 転移学習とは 転移学習とは、ImageNetなど何百万もの大量の画像で事前学習させたモデルを使い、それを「特徴量検出器」として...

Add a Comment

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