![]() | ![]() | ![]() | ![]() |
前書き
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
-
model.save()
またはtf.keras.models.save_model()
-
tf.keras.models.load_model()
モデル全体をディスクに保存するために使用できる形式は、 TensorFlowSavedModel形式と古いKerasH5形式の2つです。推奨される形式は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: 0.3312 WARNING:tensorflow:From /tmpfs/src/tf_docs_env/lib/python3.6/site-packages/tensorflow/python/training/tracking/tracking.py:111: Model.state_updates (from tensorflow.python.keras.engine.training) is deprecated and will be removed in a future version. Instructions for updating: This property should not be used in TensorFlow 2.0, as updates are applied automatically. WARNING:tensorflow:From /tmpfs/src/tf_docs_env/lib/python3.6/site-packages/tensorflow/python/training/tracking/tracking.py:111: Layer.updates (from tensorflow.python.keras.engine.base_layer) is deprecated and will be removed in a future version. Instructions for updating: This property should not be used in TensorFlow 2.0, as updates are applied automatically. INFO:tensorflow:Assets written to: my_model/assets 4/4 [==============================] - 0s 1ms/step - loss: 0.2840 <tensorflow.python.keras.callbacks.History at 0x7f0fbc3f3780>
SavedModelに含まれるもの
呼び出しmodel.save('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形式は、クラス名、呼び出し関数、損失、および重み(および実装されている場合は構成)を格納します。呼び出し関数は、モデル/レイヤーの計算グラフを定義します。
モデル/レイヤー構成がない場合、呼び出し関数を使用して、元のモデルと同様に存在するモデルを作成します。このモデルは、トレーニング、評価、および推論に使用できます。
それでも、カスタムモデルまたはレイヤークラスを作成するときは、 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 0x7f0fbc3f8208> Loaded model: <tensorflow.python.keras.saving.saved_model.load.CustomModel object at 0x7f1031981518>
上記の例に見られるように、ローダーは元のモデルのように機能する新しいモデルクラスを動的に作成します。
KerasH5フォーマット
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: 0.3771 4/4 [==============================] - 0s 1ms/step - loss: 0.3432 <tensorflow.python.keras.callbacks.History at 0x7f1031030cf8>
制限事項
SavedModel形式と比較すると、H5ファイルに含まれないものが2つあります。
-
model.add_loss()
およびmodel.add_metric()
を介して追加された外部損失およびメトリックは保存されません(SavedModelとは異なります)。モデルにそのような損失とメトリックがあり、トレーニングを再開したい場合は、モデルをロードした後、これらの損失を自分で追加する必要があります。これは、self.add_loss()
およびself.add_metric()
介してレイヤー内に作成された損失/メトリックには適用されないことに注意してください。レイヤーがロードされる限り、これらの損失とメトリックはレイヤーのcall
メソッドの一部であるため、保持されます。 - カスタムレイヤーなどのカスタムオブジェクトの計算グラフは、保存されたファイルに含まれていません。ロード時に、Kerasはモデルを再構築するために、これらのオブジェクトのPythonクラス/関数にアクセスする必要があります。カスタムオブジェクトを参照してください。
アーキテクチャの保存
モデルの構成(またはアーキテクチャ)は、モデルに含まれるレイヤーと、これらのレイヤーの接続方法を指定します*。モデルの構成がある場合は、重みの初期化された状態でモデルを作成でき、コンパイル情報はありません。
*これは、サブクラス化されたモデルではなく、機能APIまたはシーケンシャルAPIを使用して定義されたモデルにのみ適用されることに注意してください。
シーケンシャルモデルまたは機能APIモデルの構成
これらのタイプのモデルは、レイヤーの明示的なグラフです。それらの構成は、常に構造化された形式で利用できます。
API
-
get_config()
およびfrom_config()
-
tf.keras.models.model_to_json()
およびtf.keras.models.model_from_json()
get_config()
およびfrom_config()
config = model.get_config()
を呼び出すと、モデルの構成を含むconfig = model.get_config()
が返されます。次に、同じモデルを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文字列に変換し、元のモデルクラスなしでロードできる点がfrom_config
ます。また、モデルに固有であり、レイヤーを対象としたものではありません。
例:
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
方法を。さらに、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.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
)。このリストにカスタムクラスを登録するには、いくつかの方法があります。
- ロード関数で
custom_objects
引数を設定します。 (上記の「構成メソッドの定義」のセクションの例を参照してください) -
tf.keras.utils.custom_object_scope
またはtf.keras.utils.CustomObjectScope
-
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()
介してモデルのメモリ内クローンを作成することもできます。これは、構成を取得してから、その構成からモデルを再作成することと同じです(したがって、コンパイル情報やレイヤーの重みの値は保持されません)。
例:
with keras.utils.custom_object_scope(custom_objects):
new_model = keras.models.clone_model(model)
モデルの重み値のみを保存およびロード
モデルの重みのみを保存およびロードするように選択できます。これは、次の場合に役立ちます。
- 推論に必要なのはモデルだけです。この場合、トレーニングを再開する必要がないため、コンパイル情報やオプティマイザーの状態は必要ありません。
- 転移学習を行っています。この場合、前のモデルの状態を再利用して新しいモデルをトレーニングするため、前のモデルのコンパイル情報は必要ありません。
インメモリウェイト転送用のAPI
get_weights
とset_weights
を使用して、異なるオブジェクト間で重みをコピーできます。
-
tf.keras.layers.Layer.get_weights()
:numpy配列のリストを返します。 -
tf.keras.layers.Layer.set_weights()
:モデルの重みを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つの方法があります。
-
save_format
引数:値をsave_format="tf"
またはsave_format="h5"
ます。 -
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 0x7f103069d828>
フォーマットの詳細
TensorFlowチェックポイント形式は、オブジェクト属性名を使用して重みを保存および復元します。たとえば、 tf.keras.layers.Dense
レイヤーについて考えてtf.keras.layers.Dense
ます。このレイヤーには、 dense.kernel
とdense.bias
2つの重みが含まれています。レイヤーがtf
形式で保存されると、結果のチェックポイントには、キー"kernel"
と"bias"
、およびそれらに対応する重み値が含まれます。詳細については、TFチェックポイントガイドの「ロードメカニズム」を参照してください。
属性/グラフのエッジは、変数の名前ではなく、親オブジェクトで使用されている名前にちなんで名付けられていることに注意してください。以下の例のCustomLayer
について考えて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 (Functional) (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 0x7f103066e630>
通常、モデルの構築には同じ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-1-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 0x7f10306c6518>
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 _________________________________________________________________