このページは Cloud Translation API によって翻訳されました。
Switch to English

Kerasモデルの保存と読み込み

TensorFlow.orgで見る Google Colabで実行 GitHubでソースを表示する ノートブックをダウンロード

前書き

Kerasモデルは複数のコンポーネントで構成されています:

  • モデルに含まれるレイヤーとそれらの接続方法を指定するアーキテクチャーまたは構成。
  • ウェイト値のセット(「モデルの状態」)。
  • オプティマイザー(モデルのコンパイルにより定義)。
  • 損失とメトリックのセット(モデルのコンパイル、またはadd_loss()またはadd_metric()呼び出しによって定義)。

Keras APIを使用すると、これらの断片を一度にディスクに保存したり、一部のみを選択的に保存したりできます。

  • すべてをTensorFlow SavedModel形式(または古いKeras H5形式)​​で単一のアーカイブに保存します。これは標準的な方法です。
  • アーキテクチャ/構成のみを保存します。通常はJSONファイルとして保存します。
  • 重み値のみを保存します。これは通常、モデルのトレーニング時に使用されます。

これらの各オプションを見てみましょう。どちらを使用するのですか。それらはどのように機能しますか?

保存と読み込みの簡単な答え

このガイドを読む時間が10秒しかない場合は、次のことを知っておく必要があります。

Kerasモデルの保存:

 model = ...  # Get model (Sequential, Functional Model, or Model subclass)
model.save('path/to/location')
 

モデルをロードし直す:

 from tensorflow import keras
model = keras.models.load_model('path/to/location')
 

それでは、詳細を見てみましょう。

セットアップ

 import numpy as np
import tensorflow as tf
from tensorflow import keras
 

モデル全体の保存と読み込み

モデル全体を単一の成果物に保存できます。以下が含まれます:

  • モデルのアーキテクチャ/構成
  • モデルの重み値(トレーニング中に学習された)
  • モデルのコンパイル情報( compile()場合)が呼び出されました
  • オプティマイザーとその状態(ある場合)(これにより、トレーニングを再開したところから再開できます)

API

モデル全体をディスクに保存するために使用できる2つの形式があります。TensorFlowSavedModel形式、古い Keras H5形式です 。推奨される形式はSavedModelです。 model.save()を使用するときのデフォルトです。

次の方法でH5形式に切り替えることができます。

  • save_format='h5'save()渡します。
  • .h5または.kerasで終わるファイル名をsave()渡します。

SavedModel形式

例:

 def get_model():
    # Create a simple model.
    inputs = keras.Input(shape=(32,))
    outputs = keras.layers.Dense(1)(inputs)
    model = keras.Model(inputs, outputs)
    model.compile(optimizer="adam", loss="mean_squared_error")
    return model


model = get_model()

# Train the model.
test_input = np.random.random((128, 32))
test_target = np.random.random((128, 1))
model.fit(test_input, test_target)

# Calling `save('my_model')` creates a SavedModel folder `my_model`.
model.save("my_model")

# It can be used to reconstruct the model identically.
reconstructed_model = keras.models.load_model("my_model")

# Let's check:
np.testing.assert_allclose(
    model.predict(test_input), reconstructed_model.predict(test_input)
)

# The reconstructed model is already compiled and has retained the optimizer
# state, so training can resume:
reconstructed_model.fit(test_input, test_target)
 
4/4 [==============================] - 0s 1ms/step - loss: 1.1917
WARNING:tensorflow:From /tmpfs/src/tf_docs_env/lib/python3.6/site-packages/tensorflow/python/ops/resource_variable_ops.py:1817: 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: my_model/assets
4/4 [==============================] - 0s 1ms/step - loss: 1.0581

<tensorflow.python.keras.callbacks.History at 0x7f13700096d8>

SavedModelに含まれるもの

model.save('my_model')my_modelと、以下を含むmy_modelという名前のフォルダーが作成されます。

ls my_model
assets  saved_model.pb  variables

モデルアーキテクチャとトレーニング構成(オプティマイザ、損失、メトリックを含む)は、 saved_model.pb格納されていsaved_model.pb 。重みは、 variables/ディレクトリに保存されvariables/

SavedModel形式の詳細については、 SavedModelガイド( ディスク上のSavedModel形式)を参照してください。

SavedModelがカスタムオブジェクトを処理する方法

モデルとそのレイヤーを保存するとき、SavedModel形式はクラス名、 関数の呼び出し 、損失、および重み(および実装されている場合は構成)を保存します。 call関数は、モデル/レイヤーの計算グラフを定義します。

モデル/レイヤー設定がない場合、call関数を使用して、トレーニング、評価、および推論に使用できる元のモデルのように存在するモデルを作成します。

それでも、カスタムモデルまたはレイヤークラスを作成するときは、常にfrom_configメソッドとfrom_configメソッドを定義することをおget_configします。これにより、必要に応じて後で計算を簡単に更新できます。詳細については、 カスタムオブジェクトに関するセクションを参照してください。

以下は、configメソッド上書きせずにSavedModel形式からカスタムレイヤーを読み込むときに何が起こるかの例です。

 class CustomModel(keras.Model):
    def __init__(self, hidden_units):
        super(CustomModel, self).__init__()
        self.dense_layers = [keras.layers.Dense(u) for u in hidden_units]

    def call(self, inputs):
        x = inputs
        for layer in self.dense_layers:
            x = layer(x)
        return x


model = CustomModel([16, 16, 10])
# Build the model by calling it
input_arr = tf.random.uniform((1, 5))
outputs = model(input_arr)
model.save("my_model")

# Delete the custom-defined model class to ensure that the loader does not have
# access to it.
del CustomModel

loaded = keras.models.load_model("my_model")
np.testing.assert_allclose(loaded(input_arr), outputs)

print("Original model:", model)
print("Loaded model:", loaded)
 
INFO:tensorflow:Assets written to: my_model/assets
WARNING:tensorflow:No training configuration found in save file, so the model was *not* compiled. Compile it manually.
Original model: <__main__.CustomModel object at 0x7f1370081550>
Loaded model: <tensorflow.python.keras.saving.saved_model.load.CustomModel object at 0x7f1328722e48>

上記の例にあるように、ローダーは、元のモデルのように機能する新しいモデルクラスを動的に作成します。

Keras H5フォーマット

Kerasは、モデルのアーキテクチャ、重み値、およびcompile()情報を含む単一のHDF5ファイルの保存もサポートしています。これは、SavedModelの軽量な代替品です。

例:

 model = get_model()

# Train the model.
test_input = np.random.random((128, 32))
test_target = np.random.random((128, 1))
model.fit(test_input, test_target)

# Calling `save('my_model.h5')` creates a h5 file `my_model.h5`.
model.save("my_h5_model.h5")

# It can be used to reconstruct the model identically.
reconstructed_model = keras.models.load_model("my_h5_model.h5")

# Let's check:
np.testing.assert_allclose(
    model.predict(test_input), reconstructed_model.predict(test_input)
)

# The reconstructed model is already compiled and has retained the optimizer
# state, so training can resume:
reconstructed_model.fit(test_input, test_target)
 
4/4 [==============================] - 0s 1ms/step - loss: 4.1064
4/4 [==============================] - 0s 1ms/step - loss: 3.8469

<tensorflow.python.keras.callbacks.History at 0x7f14171934a8>

制限事項

SavedModel形式と比較すると、H5ファイルに含まれないものが2つあります。

  • model.add_loss()およびmodel.add_metric()を介して追加された外部損失およびメトリックは保存されません(SavedModelとは異なります)。モデルにそのような損失と測定基準があり、トレーニングを再開したい場合は、モデルをロードした後、これらの損失を自分で追加する必要があります。これは、 self.add_loss()およびself.add_metric()self.add_loss() レイヤー内で作成された損失/メトリックには適用されないことに注意してください。レイヤーが読み込まれる限り、これらの損失とメトリックはレイヤーのcallメソッドの一部であるため保持されます。
  • カスタムレイヤーなどのカスタムオブジェクト計算グラフは 、保存されたファイルには含まれません。ロード時に、Kerasはモデルを再構築するためにこれらのオブジェクトのPythonクラス/関数にアクセスする必要があります。 カスタムオブジェクトを参照してください。

アーキテクチャを保存する

モデルの構成(またはアーキテクチャ)は、モデルに含まれるレイヤー、およびこれらのレイヤーの接続方法を指定します*。モデルの構成がある場合、モデルは、重みについて新しく初期化された状態で作成され、コンパイル情報はありません。

*これは、サブクラス化されたモデルではなく、機能またはシーケンシャルAPIを使用して定義されたモデルにのみ適用されます。

順次モデルまたは機能APIモデルの構成

これらのタイプのモデルは、レイヤーの明示的なグラフです。それらの構成は常に構造化された形式で利用できます。

API

get_config()およびfrom_config()

config = model.get_config()を呼び出すと、モデルの構成を含むPython dictが返されます。同じモデルは、 Sequential.from_config(config)Sequentialモデルの場合)またはModel.from_config(config) (Functional APIモデルの場合)を介して再構築できます。

同じワークフローは、シリアライズ可能なレイヤーでも機能します。

レイヤーの例:

 layer = keras.layers.Dense(3, activation="relu")
layer_config = layer.get_config()
new_layer = keras.layers.Dense.from_config(layer_config)
 

順次モデルの例:

 model = keras.Sequential([keras.Input((32,)), keras.layers.Dense(1)])
config = model.get_config()
new_model = keras.Sequential.from_config(config)
 

機能モデルの例:

 inputs = keras.Input((32,))
outputs = keras.layers.Dense(1)(inputs)
model = keras.Model(inputs, outputs)
config = model.get_config()
new_model = keras.Model.from_config(config)
 

to_json()およびtf.keras.models.model_from_json()

これはget_config / from_config似ていますが、モデルをJSON文字列に変換し、元のモデルクラスなしでロードできるようにします。これはモデルに固有でもあり、レイヤー用ではありません。

例:

 model = keras.Sequential([keras.Input((32,)), keras.layers.Dense(1)])
json_config = model.to_json()
new_model = keras.models.model_from_json(json_config)
 

カスタムオブジェクト

モデルとレイヤー

サブクラス化されたモデルとレイヤーのアーキテクチャは、 __init__メソッドとcallメソッドで定義されていcall 。それらはPythonバイトコードと見なされ、JSON互換の構成にシリアル化できません-バイトコードのシリアル化を試みることができます(たとえばpickleを介して)が、それは完全に安全ではなく、モデルを別のシステムにロードできないことを意味します。

カスタム定義レイヤーを含むモデル、またはサブクラス化されたモデルを保存/ロードするには、 get_configおよびオプションでfrom_configメソッドを上書きする必要がfrom_configます。さらに、Kerasが認識できるように、カスタムオブジェクトの登録を使用する必要があります。

カスタム関数

カスタム定義関数(アクティブ化の損失や初期化など)には、 get_configメソッドは必要ありません。関数名は、カスタムオブジェクトとして登録されている限り、ロードに十分です。

TensorFlowグラフのみを読み込む

Kerasによって生成されたTensorFlowグラフをロードすることが可能です。その場合、 custom_objectsを提供する必要はありません。あなたはこのようにすることができます:

 model.save("my_model")
tensorflow_graph = tf.saved_model.load("my_model")
x = np.random.uniform(size=(4, 32)).astype(np.float32)
predicted = tensorflow_graph(x).numpy()
 
INFO:tensorflow:Assets written to: my_model/assets

この方法にはいくつかの欠点があることに注意してください。

  • トレーサビリティ上の理由から、使用されたカスタムオブジェクトに常にアクセスできる必要があります。再作成できないモデルをプロダクションに入れたくないでしょう。
  • tf.saved_model.loadによって返されるオブジェクトは、 tf.saved_model.loadモデルではありません。そのため、使いやすさは劣ります。たとえば、 .predict()または.fit()アクセスできません。

使用が推奨されていない場合でも、カスタムオブジェクトのコードを失ったり、 tf.keras.models.load_model()モデルをロードするときに問題が発生した場合など、狭い場所にいる場合に役立ちます。

詳細については、 tf.saved_model.loadに関するページをご覧くださいtf.saved_model.load

構成メソッドの定義

仕様:

  • get_configは、 get_configアーキテクチャおよびモデルを保存するAPIと互換性を持たせるために、JSONシリアル化可能な辞書を返す必要があります。
  • from_config(config)classmethod )は、構成から作成された新しいレイヤーまたはモデルオブジェクトを返す必要があります。デフォルトの実装はcls(**config)返します。

例:

 class CustomLayer(keras.layers.Layer):
    def __init__(self, a):
        self.var = tf.Variable(a, name="var_a")

    def call(self, inputs, training=False):
        if training:
            return inputs * self.var
        else:
            return inputs

    def get_config(self):
        return {"a": self.var.numpy()}

    # There's actually no need to define `from_config` here, since returning
    # `cls(**config)` is the default behavior.
    @classmethod
    def from_config(cls, config):
        return cls(**config)


layer = CustomLayer(5)
layer.var.assign(2)

serialized_layer = keras.layers.serialize(layer)
new_layer = keras.layers.deserialize(
    serialized_layer, custom_objects={"CustomLayer": CustomLayer}
)
 

カスタムオブジェクトの登録

Kerasは設定を生成したクラスのメモを保持します。上記の例から、 tf.keras.layers.serializeはシリアル化された形式のカスタムレイヤーを生成します。

 {'class_name': 'CustomLayer', 'config': {'a': 2} }
 

Kerasは、すべての組み込みレイヤー、モデル、オプティマイザー、およびメトリッククラスのマスターリストを保持しています。これは、 from_configを呼び出す正しいクラスを見つけるために使用されます。クラスが見つからない場合は、エラーが発生します( Value Error: Unknown layer )。このリストにカスタムクラスを登録するには、いくつかの方法があります。

  1. 読み込み関数でcustom_objects引数を設定します。 (上記の「configメソッドの定義」のセクションの例を参照)
  2. tf.keras.utils.custom_object_scopeまたはtf.keras.utils.CustomObjectScope
  3. tf.keras.utils.register_keras_serializable

カスタムレイヤーと関数の例

 class CustomLayer(keras.layers.Layer):
    def __init__(self, units=32, **kwargs):
        super(CustomLayer, self).__init__(**kwargs)
        self.units = units

    def build(self, input_shape):
        self.w = self.add_weight(
            shape=(input_shape[-1], self.units),
            initializer="random_normal",
            trainable=True,
        )
        self.b = self.add_weight(
            shape=(self.units,), initializer="random_normal", trainable=True
        )

    def call(self, inputs):
        return tf.matmul(inputs, self.w) + self.b

    def get_config(self):
        config = super(CustomLayer, self).get_config()
        config.update({"units": self.units})
        return config


def custom_activation(x):
    return tf.nn.tanh(x) ** 2


# Make a model with the CustomLayer and custom_activation
inputs = keras.Input((32,))
x = CustomLayer(32)(inputs)
outputs = keras.layers.Activation(custom_activation)(x)
model = keras.Model(inputs, outputs)

# Retrieve the config
config = model.get_config()

# At loading time, register the custom objects with a `custom_object_scope`:
custom_objects = {"CustomLayer": CustomLayer, "custom_activation": custom_activation}
with keras.utils.custom_object_scope(custom_objects):
    new_model = keras.Model.from_config(config)
 

インメモリモデルのクローン作成

tf.keras.models.clone_model()tf.keras.models.clone_model()てモデルのメモリ内クローン作成を行うこともできます。これは、構成を取得し、その構成からモデルを再作成することと同じです(そのため、コンパイル情報やレイヤーの重みの値は保持されません)。

例:

 with keras.utils.custom_object_scope(custom_objects):
    new_model = keras.models.clone_model(model)
 

モデルの重み値のみを保存およびロードする

モデルの重みのみを保存およびロードするように選択できます。これは次の場合に役立ちます。

  • 推論用のモデルのみが必要です。この場合、トレーニングを再開する必要がないため、コンパイル情報やオプティマイザの状態は必要ありません。
  • 転移学習を行っています。この場合、以前のモデルの状態を再利用して新しいモデルをトレーニングするため、以前のモデルのコンパイル情報は必要ありません。

インメモリウェイト転送用のAPI

ウェイトは、 get_weightsおよびset_weightsを使用して、異なるオブジェクト間でコピーできます。

以下の例。

メモリ内でのあるレイヤーから別のレイヤーへのウェイトの転送

 def create_layer():
    layer = keras.layers.Dense(64, activation="relu", name="dense_2")
    layer.build((None, 784))
    return layer


layer_1 = create_layer()
layer_2 = create_layer()

# Copy weights from layer 2 to layer 1
layer_2.set_weights(layer_1.get_weights())
 

あるモデルから互換性のあるアーキテクチャを持つ別のモデルにメモリ内で重みを転送する

 # Create a simple functional model
inputs = keras.Input(shape=(784,), name="digits")
x = keras.layers.Dense(64, activation="relu", name="dense_1")(inputs)
x = keras.layers.Dense(64, activation="relu", name="dense_2")(x)
outputs = keras.layers.Dense(10, name="predictions")(x)
functional_model = keras.Model(inputs=inputs, outputs=outputs, name="3_layer_mlp")

# Define a subclassed model with the same architecture
class SubclassedModel(keras.Model):
    def __init__(self, output_dim, name=None):
        super(SubclassedModel, self).__init__(name=name)
        self.output_dim = output_dim
        self.dense_1 = keras.layers.Dense(64, activation="relu", name="dense_1")
        self.dense_2 = keras.layers.Dense(64, activation="relu", name="dense_2")
        self.dense_3 = keras.layers.Dense(output_dim, name="predictions")

    def call(self, inputs):
        x = self.dense_1(inputs)
        x = self.dense_2(x)
        x = self.dense_3(x)
        return x

    def get_config(self):
        return {"output_dim": self.output_dim, "name": self.name}


subclassed_model = SubclassedModel(10)
# Call the subclassed model once to create the weights.
subclassed_model(tf.ones((1, 784)))

# Copy weights from functional_model to subclassed_model.
subclassed_model.set_weights(functional_model.get_weights())

assert len(functional_model.weights) == len(subclassed_model.weights)
for a, b in zip(functional_model.weights, subclassed_model.weights):
    np.testing.assert_allclose(a.numpy(), b.numpy())
 

ステートレスレイヤーの場合

ステートレスレイヤーは重みの順序または数を変更しないため、余分な/欠落しているステートレスレイヤーがあっても、モデルは互換性のあるアーキテクチャを持つことができます。

 inputs = keras.Input(shape=(784,), name="digits")
x = keras.layers.Dense(64, activation="relu", name="dense_1")(inputs)
x = keras.layers.Dense(64, activation="relu", name="dense_2")(x)
outputs = keras.layers.Dense(10, name="predictions")(x)
functional_model = keras.Model(inputs=inputs, outputs=outputs, name="3_layer_mlp")

inputs = keras.Input(shape=(784,), name="digits")
x = keras.layers.Dense(64, activation="relu", name="dense_1")(inputs)
x = keras.layers.Dense(64, activation="relu", name="dense_2")(x)

# Add a dropout layer, which does not contain any weights.
x = keras.layers.Dropout(0.5)(x)
outputs = keras.layers.Dense(10, name="predictions")(x)
functional_model_with_dropout = keras.Model(
    inputs=inputs, outputs=outputs, name="3_layer_mlp"
)

functional_model_with_dropout.set_weights(functional_model.get_weights())
 

ウェイトをディスクに保存して再度ロードするためのAPI

次の形式でmodel.save_weightsを呼び出すことにより、重みをディスクに保存できます。

  • TensorFlowチェックポイント
  • HDF5

model.save_weightsのデフォルトの形式はTensorFlowチェックポイントです。保存形式を指定する方法は2つあります。

  1. save_format引数:値をsave_format="tf"またはsave_format="h5"ます。
  2. path引数:パスが.h5または.hdf5で終わる場合、HDF5形式が使用されます。他のサフィックスは、 save_formatが設定されていない限り、TensorFlowチェックポイントになります。

メモリ内のnumpy配列として重みを取得するオプションもあります。各APIには、以下に詳述する長所と短所があります。

TFチェックポイント形式

例:

 # Runnable example
sequential_model = keras.Sequential(
    [
        keras.Input(shape=(784,), name="digits"),
        keras.layers.Dense(64, activation="relu", name="dense_1"),
        keras.layers.Dense(64, activation="relu", name="dense_2"),
        keras.layers.Dense(10, name="predictions"),
    ]
)
sequential_model.save_weights("ckpt")
load_status = sequential_model.load_weights("ckpt")

# `assert_consumed` can be used as validation that all variable values have been
# restored from the checkpoint. See `tf.train.Checkpoint.restore` for other
# methods in the Status object.
load_status.assert_consumed()
 
<tensorflow.python.training.tracking.util.CheckpointLoadStatus at 0x7f1416793ba8>

フォーマットの詳細

TensorFlowチェックポイント形式は、オブジェクト属性名を使用して重みを保存および復元します。たとえば、 tf.keras.layers.Denseレイヤーを考えてtf.keras.layers.Denseます。このレイヤーには、 dense.kerneldense.bias 2つのウェイトが含まれています。レイヤーがtf形式で保存されると、結果のチェックポイントにはキー"kernel""bias" 、およびそれらに対応する重みの値が含まれます。詳細については、TFチェックポイントガイドの「読み込みの仕組み」を参照してください。

属性/グラフのエッジの名前は、変数の名前ではなく、親オブジェクトで使用されている名前に基づいていることに注意してください。以下の例のCustomLayerを検討してください。変数CustomLayer.varは、 "var_a"ではなく"var_a" "var"をキーの一部として保存されます。

 class CustomLayer(keras.layers.Layer):
    def __init__(self, a):
        self.var = tf.Variable(a, name="var_a")


layer = CustomLayer(5)
layer_ckpt = tf.train.Checkpoint(layer=layer).save("custom_layer")

ckpt_reader = tf.train.load_checkpoint(layer_ckpt)

ckpt_reader.get_variable_to_dtype_map()
 
{'save_counter/.ATTRIBUTES/VARIABLE_VALUE': tf.int64,
 '_CHECKPOINTABLE_OBJECT_GRAPH': tf.string,
 'layer/var/.ATTRIBUTES/VARIABLE_VALUE': tf.int32}

転移学習の例

基本的に、2つのモデルのアーキテクチャが同じである限り、それらは同じチェックポイントを共有できます。

例:

 inputs = keras.Input(shape=(784,), name="digits")
x = keras.layers.Dense(64, activation="relu", name="dense_1")(inputs)
x = keras.layers.Dense(64, activation="relu", name="dense_2")(x)
outputs = keras.layers.Dense(10, name="predictions")(x)
functional_model = keras.Model(inputs=inputs, outputs=outputs, name="3_layer_mlp")

# Extract a portion of the functional model defined in the Setup section.
# The following lines produce a new model that excludes the final output
# layer of the functional model.
pretrained = keras.Model(
    functional_model.inputs, functional_model.layers[-1].input, name="pretrained_model"
)
# Randomly assign "trained" weights.
for w in pretrained.weights:
    w.assign(tf.random.normal(w.shape))
pretrained.save_weights("pretrained_ckpt")
pretrained.summary()

# Assume this is a separate program where only 'pretrained_ckpt' exists.
# Create a new functional model with a different output dimension.
inputs = keras.Input(shape=(784,), name="digits")
x = keras.layers.Dense(64, activation="relu", name="dense_1")(inputs)
x = keras.layers.Dense(64, activation="relu", name="dense_2")(x)
outputs = keras.layers.Dense(5, name="predictions")(x)
model = keras.Model(inputs=inputs, outputs=outputs, name="new_model")

# Load the weights from pretrained_ckpt into model.
model.load_weights("pretrained_ckpt")

# Check that all of the pretrained weights have been loaded.
for a, b in zip(pretrained.weights, model.weights):
    np.testing.assert_allclose(a.numpy(), b.numpy())

print("\n", "-" * 50)
model.summary()

# Example 2: Sequential model
# Recreate the pretrained model, and load the saved weights.
inputs = keras.Input(shape=(784,), name="digits")
x = keras.layers.Dense(64, activation="relu", name="dense_1")(inputs)
x = keras.layers.Dense(64, activation="relu", name="dense_2")(x)
pretrained_model = keras.Model(inputs=inputs, outputs=x, name="pretrained")

# Sequential example:
model = keras.Sequential([pretrained_model, keras.layers.Dense(5, name="predictions")])
model.summary()

pretrained_model.load_weights("pretrained_ckpt")

# Warning! Calling `model.load_weights('pretrained_ckpt')` won't throw an error,
# but will *not* work as expected. If you inspect the weights, you'll see that
# none of the weights will have loaded. `pretrained_model.load_weights()` is the
# correct method to call.
 
Model: "pretrained_model"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
digits (InputLayer)          [(None, 784)]             0         
_________________________________________________________________
dense_1 (Dense)              (None, 64)                50240     
_________________________________________________________________
dense_2 (Dense)              (None, 64)                4160      
=================================================================
Total params: 54,400
Trainable params: 54,400
Non-trainable params: 0
_________________________________________________________________

 --------------------------------------------------
Model: "new_model"
_________________________________________________________________
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, 5)                 325       
=================================================================
Total params: 54,725
Trainable params: 54,725
Non-trainable params: 0
_________________________________________________________________
Model: "sequential_3"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
pretrained (Model)           (None, 64)                54400     
_________________________________________________________________
predictions (Dense)          (None, 5)                 325       
=================================================================
Total params: 54,725
Trainable params: 54,725
Non-trainable params: 0
_________________________________________________________________

<tensorflow.python.training.tracking.util.CheckpointLoadStatus at 0x7f1416704278>

通常、モデルの作成には同じAPIを使用することをお勧めします。シーケンシャルとファンクショナル、またはファンクショナルとサブクラス化などを切り替える場合は、常に事前トレーニング済みモデルを再構築し、事前トレーニング済みの重みをそのモデルにロードします。

次の質問は、モデルのアーキテクチャがまったく異なる場合、重みをどのように保存して異なるモデルにロードできるかということです。解決策は、 tf.train.Checkpointを使用して、正確なレイヤー/変数を保存および復元することです。

例:

 # Create a subclassed model that essentially uses functional_model's first
# and last layers.
# First, save the weights of functional_model's first and last dense layers.
first_dense = functional_model.layers[1]
last_dense = functional_model.layers[-1]
ckpt_path = tf.train.Checkpoint(
    dense=first_dense, kernel=last_dense.kernel, bias=last_dense.bias
).save("ckpt")

# Define the subclassed model.
class ContrivedModel(keras.Model):
    def __init__(self):
        super(ContrivedModel, self).__init__()
        self.first_dense = keras.layers.Dense(64)
        self.kernel = self.add_variable("kernel", shape=(64, 10))
        self.bias = self.add_variable("bias", shape=(10,))

    def call(self, inputs):
        x = self.first_dense(inputs)
        return tf.matmul(x, self.kernel) + self.bias


model = ContrivedModel()
# Call model on inputs to create the variables of the dense layer.
_ = model(tf.ones((1, 784)))

# Create a Checkpoint with the same structure as before, and load the weights.
tf.train.Checkpoint(
    dense=model.first_dense, kernel=model.kernel, bias=model.bias
).restore(ckpt_path).assert_consumed()
 
WARNING:tensorflow:From <ipython-input-21-eec1d28bc826>:15: Layer.add_variable (from tensorflow.python.keras.engine.base_layer) is deprecated and will be removed in a future version.
Instructions for updating:
Please use `layer.add_weight` method instead.

<tensorflow.python.training.tracking.util.CheckpointLoadStatus at 0x7f1416713358>

HDF5フォーマット

HDF5形式には、レイヤー名でグループ化された重みが含まれています。重みは、トレーニング可能な重みのリストをトレーニング不可能な重みのリスト( layer.weightsと同じ)に連結して並べられたリストです。したがって、チェックポイントに保存されているものと同じレイヤーとトレーニング可能なステータスがあるモデルは、hdf5チェックポイントを使用できます。

例:

 # Runnable example
sequential_model = keras.Sequential(
    [
        keras.Input(shape=(784,), name="digits"),
        keras.layers.Dense(64, activation="relu", name="dense_1"),
        keras.layers.Dense(64, activation="relu", name="dense_2"),
        keras.layers.Dense(10, name="predictions"),
    ]
)
sequential_model.save_weights("weights.h5")
sequential_model.load_weights("weights.h5")
 

変更することを注意layer.trainable異なっをもたらすことができるlayer.weightsモデルは、ネストされたレイヤーが含まれている場合に発注。

 class NestedDenseLayer(keras.layers.Layer):
    def __init__(self, units, name=None):
        super(NestedDenseLayer, self).__init__(name=name)
        self.dense_1 = keras.layers.Dense(units, name="dense_1")
        self.dense_2 = keras.layers.Dense(units, name="dense_2")

    def call(self, inputs):
        return self.dense_2(self.dense_1(inputs))


nested_model = keras.Sequential([keras.Input((784,)), NestedDenseLayer(10, "nested")])
variable_names = [v.name for v in nested_model.weights]
print("variables: {}".format(variable_names))

print("\nChanging trainable status of one of the nested layers...")
nested_model.get_layer("nested").dense_1.trainable = False

variable_names_2 = [v.name for v in nested_model.weights]
print("\nvariables: {}".format(variable_names_2))
print("variable ordering changed:", variable_names != variable_names_2)
 
variables: ['nested/dense_1/kernel:0', 'nested/dense_1/bias:0', 'nested/dense_2/kernel:0', 'nested/dense_2/bias:0']

Changing trainable status of one of the nested layers...

variables: ['nested/dense_2/kernel:0', 'nested/dense_2/bias:0', 'nested/dense_1/kernel:0', 'nested/dense_1/bias:0']
variable ordering changed: True

転移学習の例

HDF5から事前トレーニング済みの重みを読み込む場合、重みを元のチェックポイントモデルに読み込んでから、目的の重み/レイヤーを新しいモデルに抽出することをお勧めします。

例:

 def create_functional_model():
    inputs = keras.Input(shape=(784,), name="digits")
    x = keras.layers.Dense(64, activation="relu", name="dense_1")(inputs)
    x = keras.layers.Dense(64, activation="relu", name="dense_2")(x)
    outputs = keras.layers.Dense(10, name="predictions")(x)
    return keras.Model(inputs=inputs, outputs=outputs, name="3_layer_mlp")


functional_model = create_functional_model()
functional_model.save_weights("pretrained_weights.h5")

# In a separate program:
pretrained_model = create_functional_model()
pretrained_model.load_weights("pretrained_weights.h5")

# Create a new model by extracting layers from the original model:
extracted_layers = pretrained_model.layers[:-1]
extracted_layers.append(keras.layers.Dense(5, name="dense_3"))
model = keras.Sequential(extracted_layers)
model.summary()
 
Model: "sequential_6"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
dense_1 (Dense)              (None, 64)                50240     
_________________________________________________________________
dense_2 (Dense)              (None, 64)                4160      
_________________________________________________________________
dense_3 (Dense)              (None, 5)                 325       
=================================================================
Total params: 54,725
Trainable params: 54,725
Non-trainable params: 0
_________________________________________________________________