TensorFlow 2.0 : ガイド : Keras :- Keras でモデルをセーブしてシリアライズする (翻訳/解説)
翻訳 : (株)クラスキャット セールスインフォメーション
作成日時 : 12/04/2019
* 本ページは、TensorFlow org サイトの Guide – Keras の以下のページを翻訳した上で
適宜、補足説明したものです:
* サンプルコードの動作確認はしておりますが、必要な場合には適宜、追加改変しています。
* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。

- お住まいの地域に関係なく Web ブラウザからご参加頂けます。事前登録 が必要ですのでご注意ください。
 - Windows PC のブラウザからご参加が可能です。スマートデバイスもご利用可能です。
 
◆ お問合せ : 本件に関するお問い合わせ先は下記までお願いいたします。
| 株式会社クラスキャット セールス・マーケティング本部 セールス・インフォメーション | 
| E-Mail:sales-info@classcat.com ; WebSite: https://www.classcat.com/ | 
| Facebook: https://www.facebook.com/ClassCatJP/ | 
ガイド : Keras :- Keras でモデルをセーブしてシリアライズする
このガイドの最初のパートは Sequential モデルと Functional API を使用して構築されたモデルのためのセーブとシリアライゼーションをカバーします。セーブとシリアライゼーション API はモデルのこれらのタイプの両者について正確に同じです。
モデルのカスタム・サブクラスのためのセーブはセクション「サブクラス化されたモデルをセーブする」でカバーされます。この場合の API は Sequential や Functional モデルのためのものとは僅かに異なります。
セットアップ
from __future__ import absolute_import, division, print_function, unicode_literals import tensorflow as tf tf.keras.backend.clear_session() # For easy reset of notebook state.
Part I: Sequential モデルまたは Functional モデルをセーブする
次のモデルを考えましょう :
from tensorflow import keras from tensorflow.keras import layers inputs = keras.Input(shape=(784,), name='digits') x = layers.Dense(64, activation='relu', name='dense_1')(inputs) x = layers.Dense(64, activation='relu', name='dense_2')(x) outputs = layers.Dense(10, activation='softmax', name='predictions')(x) model = keras.Model(inputs=inputs, outputs=outputs, name='3_layer_mlp') model.summary()
Model: "3_layer_mlp" _________________________________________________________________ Layer (type) Output Shape Param # ================================================================= digits (InputLayer) [(None, 784)] 0 _________________________________________________________________ dense_1 (Dense) (None, 64) 50240 _________________________________________________________________ dense_2 (Dense) (None, 64) 4160 _________________________________________________________________ predictions (Dense) (None, 10) 650 ================================================================= Total params: 55,050 Trainable params: 55,050 Non-trainable params: 0 _________________________________________________________________
オプションとして、このモデルを訓練してみましょう、そしてそれはセーブするための重み値と optimizer 状態を持ちます。
もちろん、まだ訓練していないモデルもセーブできますが、明らかにそれは面白くないでしょう。
(x_train, y_train), (x_test, y_test) = keras.datasets.mnist.load_data()
x_train = x_train.reshape(60000, 784).astype('float32') / 255
x_test = x_test.reshape(10000, 784).astype('float32') / 255
model.compile(loss='sparse_categorical_crossentropy',
              optimizer=keras.optimizers.RMSprop())
history = model.fit(x_train, y_train,
                    batch_size=64,
                    epochs=1)
Train on 60000 samples 60000/60000 [==============================] - 3s 48us/sample - loss: 0.3080
# Save predictions for future checks predictions = model.predict(x_test)
モデル全体のセーブ
Functional API で構築されたモデルを単一のファイルにセーブできます。このファイルから同じモデルを後で再作成できます、モデルを作成したコードへのアクセスをもはや持たない場合でさえも。
このファイルは以下を含みます :
- モデルのアーキテクチャ
 - モデルの重み値 (それは訓練の間に学習されました)
 - モデルの訓練 config (それは compile に渡したものです)、もしあれば
 - optimizer とその状態、もしあれば (これは貴方がやめたところで訓練を再開することを可能にします)
 
# Save the model
model.save('path_to_my_model.h5')
# Recreate the exact same model purely from the file
new_model = keras.models.load_model('path_to_my_model.h5')
import numpy as np # Check that the state is preserved new_predictions = new_model.predict(x_test) np.testing.assert_allclose(predictions, new_predictions, rtol=1e-6, atol=1e-6) # Note that the optimizer state is preserved as well: # you can resume training where you left off.
SavedModel にエクスポートする
TensorFlow SavedModel 形式にモデル全体をエクスポートすることも可能です。SavedModel は TensorFlow オブジェクトのためのスタンドアロン・シリアライゼーション形式で、TensorFlow serving と Python 以外の TensorFlow 実装によりサポートされます。
# Export the model to a SavedModel
model.save('path_to_saved_model', save_format='tf')
# Recreate the exact same model
new_model = keras.models.load_model('path_to_saved_model')
# Check that the state is preserved
new_predictions = new_model.predict(x_test)
np.testing.assert_allclose(predictions, new_predictions, rtol=1e-6, atol=1e-6)
# Note that the optimizer state is preserved as well:
# you can resume training where you left off.
WARNING:tensorflow:From /tmpfs/src/tf_docs_env/lib/python3.6/site-packages/tensorflow_core/python/ops/resource_variable_ops.py:1781: calling BaseResourceVariable.__init__ (from tensorflow.python.ops.resource_variable_ops) with constraint is deprecated and will be removed in a future version. Instructions for updating: If using Keras pass *_constraint arguments to layers. INFO:tensorflow:Assets written to: path_to_saved_model/assets
作成された SavedModel は以下を含みます :
- モデル重みを含む TensorFlow チェックポイント。
 - 基礎となる TensorFlow グラフを含む SavedModel proto。
 
アーキテクチャ-only セービング
時に、貴方はモデルのアーキテクチャだけに興味があり、そして重み値や optimizer をセーブする必要がありません。この場合、get_config() メソッドを通してモデルの “config” を取得できます。config は Python 辞書で同じモデルを再作成することを可能にします — スクラッチから初期化され、訓練の間に以前に学習されたどのような情報も持ちません。
config = model.get_config() reinitialized_model = keras.Model.from_config(config) # Note that the model state is not preserved! We only saved the architecture. new_predictions = reinitialized_model.predict(x_test) assert abs(np.sum(predictions - new_predictions)) > 0.
代わりに from_json() から to_json() を使用することができます、これは config をストアするために Python 辞書の代わりに JSON 文字列を使用します。これは config をディスクにセーブするために有用です。
json_config = model.to_json() reinitialized_model = keras.models.model_from_json(json_config)
重み-only セービング
時に、貴方はアーキテクチャではなくモデルの状態 — その重み値 — にだけ興味があります。この場合、get_weights() を通して重み値を Numpy 配列のリストとして取得できて、set_weights を通してモデルの状態を設定できます :
weights = model.get_weights() # Retrieves the state of the model. model.set_weights(weights) # Sets the state of the model.
貴方のモデルを同じ状態で再作成するために get_config()/from_config() と get_weights()/set_weights() を組み合わせることができます。けれども、model.save() とは違い、これは訓練 config と optimizer を含みません。モデルを訓練のために使用する前に compile() を再度呼び出さなければならないでしょう。
config = model.get_config() weights = model.get_weights() new_model = keras.Model.from_config(config) new_model.set_weights(weights) # Check that the state is preserved new_predictions = new_model.predict(x_test) np.testing.assert_allclose(predictions, new_predictions, rtol=1e-6, atol=1e-6) # Note that the optimizer was not preserved, # so the model should be compiled anew before training # (and the optimizer will start from a blank state).
get_weights() と set_weights(weights) に対する save-to-disk の代替は save_weights(fpath) と load_weights(fpath) です。
ここにディスクにセーブするサンプルがあります :
# Save JSON config to disk
json_config = model.to_json()
with open('model_config.json', 'w') as json_file:
    json_file.write(json_config)
# Save weights to disk
model.save_weights('path_to_my_weights.h5')
# Reload the model from the 2 files we saved
with open('model_config.json') as json_file:
    json_config = json_file.read()
new_model = keras.models.model_from_json(json_config)
new_model.load_weights('path_to_my_weights.h5')
# Check that the state is preserved
new_predictions = new_model.predict(x_test)
np.testing.assert_allclose(predictions, new_predictions, rtol=1e-6, atol=1e-6)
# Note that the optimizer was not preserved.
しかし覚えておいてください、最も単純な、推奨される方法は単にこれです :
model.save('path_to_my_model.h5')
del model
model = keras.models.load_model('path_to_my_model.h5')
TensorFlow チェックポイントを使用して重み-only セーブ
save_weights は Keras HDF5 形式か、TensorFlow SavedModel 形式でファイルを作成できることに注意してください。
このフォーマットは貴方が提供するファイル拡張子から推論されます : それが”.h5″ か “.keras” であれば、フレームワークは Keras HDF5 形式を使用します。他の任意のものはチェックポイントをデフォルトとします。
model.save_weights('path_to_my_tf_checkpoint')
総合的な明瞭さのために、フォーマットは save_format 引数を通して明示的に渡すことができます、これは値 “tf” か “h5” を取ることができます :
model.save_weights('path_to_my_tf_checkpoint', save_format='tf')
サブクラス化されたモデルをセーブする
Sequential モデルと Functional モデルは層の DAG を表わすデータ構造です。そのようなものとして、それらは安全にシリアライズとデシリアライズされます。
サブクラス化されたモデルはそれがデータ構造ではないという点で異なります、それはコードのピースです。モデルのアーキテクチャは call メソッド本体を通して定義されます。これはモデルのアーキテクチャは安全にシリアライズ化できないことを意味します。モデルをロードするためには、それを作成したコード (モデル・サブクラスのコード) へのアクセスを持つ必要があるでしょう。代わりに、このコードをバイトコードとしてシリアライズすることもできるでしょうが (e.g. pickling を通して)、それは安全ではなく一般に可搬ではありません。
これらの違いについてのより多くの情報は、記事 “What are Symbolic and Imperative APIs in TensorFlow 2.0?” を見てください。
次のサブクラス化されたモデルを考えましょう、これは最初のセクションからのモデルと同じ構造に従います :
class ThreeLayerMLP(keras.Model):
  def __init__(self, name=None):
    super(ThreeLayerMLP, self).__init__(name=name)
    self.dense_1 = layers.Dense(64, activation='relu', name='dense_1')
    self.dense_2 = layers.Dense(64, activation='relu', name='dense_2')
    self.pred_layer = layers.Dense(10, activation='softmax', name='predictions')
  def call(self, inputs):
    x = self.dense_1(inputs)
    x = self.dense_2(x)
    return self.pred_layer(x)
def get_model():
  return ThreeLayerMLP(name='3_layer_mlp')
model = get_model()
最初に、決して使用されていないサブクラス化されたモデルはセーブできません。
それはサブクラス化されたモデルはその重みを作成するために何某かのデータの上で呼び出される必要があるからです。
モデルが作成されるまで、それはそれが期待しているはずの入力データの shape と dtype を知りません、そしてそれ故にその重み変数を作成できません。最初のセクションからの Functional モデルでは、入力の shape と dtype が (keras.Input(…)を通して) 前もって指定されたことを覚えているかもしれません — それが Functional モデルがインスタンス化されてすぐに状態を持つ理由です。
モデルを、それに状態を与えるために、訓練しましょう :
(x_train, y_train), (x_test, y_test) = keras.datasets.mnist.load_data()
x_train = x_train.reshape(60000, 784).astype('float32') / 255
x_test = x_test.reshape(10000, 784).astype('float32') / 255
model.compile(loss='sparse_categorical_crossentropy',
              optimizer=keras.optimizers.RMSprop())
history = model.fit(x_train, y_train,
                    batch_size=64,
                    epochs=1)
Train on 60000 samples 60000/60000 [==============================] - 3s 42us/sample - loss: 0.3062
サブクラス化されたモデルをセーブする推奨される方法は TensorFlow SavedModel チェックポイントを作成するために save_weights を使用することです、これはモデルに関連する総ての変数の値を含みます :- 層の重み – optimizer の状態 – ステートフル・モデル・メトリクスに関連する任意の変数 (もしあれば)
model.save_weights('path_to_my_weights', save_format='tf')
# Save predictions for future checks predictions = model.predict(x_test) # Also save the loss on the first batch # to later assert that the optimizer state was preserved first_batch_loss = model.train_on_batch(x_train[:64], y_train[:64])
貴方のモデルをリストアするためには、モデル・オブジェクトを作成したコードへのアクセスが必要です。
optimizer 状態と任意のステートフル・メトリックの状態をリストアするためには、モデルを (前と正確に同じ引数で) compile して load_weights を呼び出す前にそれをあるデータ上で呼び出すべきです :
# Recreate the model
new_model = get_model()
new_model.compile(loss='sparse_categorical_crossentropy',
                  optimizer=keras.optimizers.RMSprop())
# This initializes the variables used by the optimizers,
# as well as any stateful metric variables
new_model.train_on_batch(x_train[:1], y_train[:1])
# Load the state of the old model
new_model.load_weights('path_to_my_weights')
# Check that the model state has been preserved
new_predictions = new_model.predict(x_test)
np.testing.assert_allclose(predictions, new_predictions, rtol=1e-6, atol=1e-6)
# The optimizer state is preserved as well,
# so you can resume training where you left off
new_first_batch_loss = new_model.train_on_batch(x_train[:64], y_train[:64])
assert first_batch_loss == new_first_batch_loss
以上