【Keras入門(2)】訓練モデル保存(KerasモデルとSavedModel)

入門者に向けてKerasの初歩を解説します。
[Google Colaboratory](https://colab.research.google.com/notebooks/welcome.ipynb?hl=ja)を使っているのでローカルでの環境準備すらしていません。Google Colaboratoryについては[「Google Colaboratory概要と使用手順(TensorFlowもGPUも使える)」](https://qiita.com/FukuharaYohei/items/18bc4465923bdda81826)の記事を参照ください。

以下のシリーズにしています。

- [【Keras入門(1)】単純なディープラーニングモデル定義](https://qiita.com/FukuharaYohei/items/579cc0184885b4ba75df)
- 【Keras入門(2)】訓練モデル保存(KerasモデルとSavedModel) <- 本記事
- [【Keras入門(3)】TensorBoardで見える化](https://qiita.com/FukuharaYohei/items/fe73cdb63ee82a7ea318)
- [【Keras入門(4)】Kerasの評価関数(Metrics)](https://qiita.com/FukuharaYohei/items/f7df70b984a4c7a53d58)
- [【Keras入門(5)】単純なRNNモデル定義](https://qiita.com/FukuharaYohei/items/25de4a0faf634ad34efc)
- [【Keras入門(6)】単純なRNNモデル定義(最終出力のみ使用)](https://qiita.com/FukuharaYohei/items/39f865bb53cdd5052179)
- [【Keras入門(7)】単純なSeq2Seqモデル定義](https://qiita.com/FukuharaYohei/items/27cd247342a0f7006511)

#使ったPythonライブラリ
[Google Colaboratory](https://colab.research.google.com/notebooks/welcome.ipynb?hl=ja)でインストール済の以下のライブラリとバージョンを使っています。KerasはTensorFlowに統合されているものを使っているので、ピュアなKerasは使っていません。Pythonは3.6です。

- tensorflow: 1.13.1
- Numpy: 1.16.3

# コールバックとモデル保存
Kerasでモデルを保存する場合には、[コールバック](https://keras.io/ja/callbacks/)という機能を使用します。これは、訓練中に呼び出すことができる仕組みです。
前回の記事[「【Keras入門(1)】単純なディープラーニングモデル定義」](https://qiita.com/FukuharaYohei/items/579cc0184885b4ba75df)で使用した[fit関数](https://keras.io/ja/models/model/#fit)に渡すことで使用できます。

```python
model.fit(data, labels, epochs=300, validation_split=0.2, callbacks=li_cb)
```

経験ないですが、[fit_generator関数](https://keras.io/ja/models/model/#fit_generator)にも使えるようです。

fit関数に渡しているli_cbという変数は以下のように定義しています。配列にすることで複数の機能をコールバックで使用できます。今回は、[ModelCheckpoint](https://keras.io/ja/callbacks/#modelcheckpoint)を使ったモデルの保存です。save_best_onlyをTrueにすることで、ベストな指標が出たエポックのモデルを保存します。

```python
from tensorflow.keras.callbacks import ModelCheckpoint, Callback
# Callbackを定義し、モデル保存の追加
li_cb = []
li_cb.append(ModelCheckpoint('./model.hdf5', save_best_only=True))
```

#SavedModelフォーマットでの保存(訓練時)
TensorFlow Servingなどで使用するためにSavedModelフォーマットにしたい場合もあります。その場合は、Kerasのモデルを使って[save_keras_model関数](https://www.tensorflow.org/api_docs/python/tf/contrib/saved_model/save_keras_model)を呼び出します。

```python
tf.contrib.saved_model.save_keras_model(model, './models/keras_export')
```

ただし、[tf.contribはTensorFlow2.0でなくなる](https://github.com/tensorflow/community/blob/master/rfcs/20180907-contrib-sunset.md)ため、TensorFlow2.0では[export_saved_model](https://www.tensorflow.org/versions/r2.0/api_docs/python/tf/keras/experimental/export_saved_model)を使う必要がありそうです。
※結構、探しましたが[GitHub上](https://github.com/tensorflow/tensorflow/blob/553acd6389ddf824bf8723c8e39c4321a9a6d99b/tensorflow/tools/compatibility/all_renames_v2.py)で見つけました。


パラメータserving_onlyをTrueにして、モデルを軽くしたかったのですが

```python
tf.contrib.saved_model.save_keras_model(model, './models/keras_export', serving_only=True)
```

Eager Executionをしろというエラーが出ました。

```
AssertionError: tf.saved_model.save is not supported when graph building. tf.enable_eager_execution() must run first when calling it from TensorFlow 1.x.
```

逆にtf.enable_eager_execution()を最初に呼び出して実行すると、fit関数実行時にエラーが起きました。調べていませんが、共存できないのでしょうか。

```
RuntimeError: Unable to create link (name already exists)
```

#SavedModelフォーマットでの保存(訓練後にモデル変換)
訓練後にKerasモデルからSavedModelに変換する方法です。モデルをロードして保存するだけの非常に簡単な方法です。

```python
from tensorflow.keras.models import load_model

# モデルを初期化してロード
model = None
model = load_model('./model.hdf5')

tf.contrib.saved_model.save_keras_model(model, './models/keras_export')
```

これならEager Executionしてserving_onlyをTrueにしても大丈夫です。

```python
from tensorflow.keras.models import load_model
tf.enable_eager_execution()

# モデルを初期化してロード
model = None
model = load_model('./model.hdf5')

tf.contrib.saved_model.save_keras_model(model, './models/keras_export', serving_only=True)
```

saved_model_cliを使って見てみます。signature_defの"__saved_model_init_op"の部分がおかしい？
ただ、serving_defaultは問題なさそうなので、取り急ぎ無視します。
※saved_model_cliについては、[CLI to inspect and execute SavedModel](https://www.tensorflow.org/guide/saved_model#cli_to_inspect_and_execute_savedmodel)参照

```bash
$ saved_model_cli show --all --dir ./models/keras_export/1558534626
MetaGraphDef with tag-set: 'serve' contains the following SignatureDefs:

signature_def['__saved_model_init_op']:
  The given SavedModel SignatureDef contains the following input(s):
  The given SavedModel SignatureDef contains the following output(s):
    outputs['__saved_model_init_op'] tensor_info:
        dtype: DT_INVALID
        shape: unknown_rank
        name: init_1
  Method name is: 

signature_def['serving_default']:
  The given SavedModel SignatureDef contains the following input(s):
    inputs['dense_input'] tensor_info:
        dtype: DT_FLOAT
        shape: (-1, 2)
        name: dense_input:0
  The given SavedModel SignatureDef contains the following output(s):
    outputs['dense_1'] tensor_info:
        dtype: DT_FLOAT
        shape: (-1, 1)
        name: dense_1/Sigmoid:0
  Method name is: tensorflow/serving/predict
```


ちなみに[simple_save](https://www.tensorflow.org/api_docs/python/tf/saved_model/simple_save)は、今後なくなるようなので、使わない方が無難です。
>Warning: THIS FUNCTION IS DEPRECATED. It will be removed in a future version. Instructions for updating: This function will only be available through the v1 compatibility library as tf.compat.v1.saved_model.simple_save.

# プログラム全体
プログラム全体を[GitHub](https://github.com/YoheiFukuhara/keras-for-beginner/blob/master/Keras02_SaveModel.ipynb)に置いています。訓練、保存、読み込んでSavedModel保存という順序です。
[公式チュートリアル](https://www.tensorflow.org/tutorials/keras/save_and_restore_models)を参考にしました。