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()이 호출된 경우)
  • 존재하는 옵티마이저와 그 상태(훈련을 중단한 곳에서 다시 시작할 수 있게 해줌)

APIs

전체 모델을 디스크에 저장하는 데 사용할 수 있는 두 형식은 TensorFlow SavedModel 형식이전 Keras H5 형식입니다. 권장하는 형식은 SavedModel입니다. 이는 model.save()를 사용할 때의 기본값입니다.

다음을 통해 H5 형식으로 전환할 수 있습니다.

  • 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: 3.8732
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 969us/step - loss: 3.6179

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

SavedModel이 포함하는 것

model.save('my_model')을 호출하면 다음을 포함하는 my_model 폴더를 생성합니다.

ls my_model
assets  saved_model.pb  variables

모델 아키텍처 및 훈련 구성(옵티마이저, 손실 및 메트릭 포함)은 saved_model.pb에 저장됩니다. 가중치는 variables/ 디렉토리에 저장됩니다.

SavedModel 형식에 대한 자세한 내용은 SavedModel 가이드(디스크의 SavedModel 형식)를 참조하세요.

SavedModel이 사용자 정의 객체를 처리하는 방법

모델과 모델의 레이어를 저장할 때 SavedModel 형식은 클래스명, 호출 함수, 손실 및 가중치(구현된 경우에는 구성도 포함)를 저장합니다. 호출 함수는 모델/레이어의 계산 그래프를 정의합니다.

모델/레이어 구성이 없는 경우 호출 함수는 훈련, 평가 및 추론에 사용될 수 있는 기존 모델과 같은 모델을 만드는 데 사용됩니다.

그럼에도 불구하고 사용자 정의 모델 또는 레이어 클래스를 작성할 때 항상 get_configfrom_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 0x7f0afc45cbe0>
Loaded model: <tensorflow.python.keras.saving.saved_model.load.CustomModel object at 0x7f0afc1299b0>

위 예제에서 볼 수 있듯이 로더는 기존 모델처럼 작동하는 새 모델 클래스를 동적으로 만듭니다.

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 943us/step - loss: 2.2069
4/4 [==============================] - 0s 903us/step - loss: 2.0274

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

제한 사항

SavedModel 형식과 비교하여 H5 파일에 포함되지 않은 두 가지가 있습니다.

  • model.add_loss()model.add_metric()을 통해 추가된 외부 손실 및 메트릭은 SavedModel과 달리 저장되지 않습니다. 모델에 이러한 손실 및 메트릭이 있고 훈련을 재개하려는 경우 모델을 로드한 후 이러한 손실을 다시 추가해야 합니다. self.add_loss()self.add_metric()을 통해 레이어 내부에 생성된 손실/메트릭에는 적용되지 않습니다. 레이어가 로드되는 한 이러한 손실 및 메트릭은 레이어의 call 메서드의 일부이므로 유지됩니다.
  • 사용자 정의 레이어와 같은 사용자 정의 객체의 계산 그래프는 저장된 파일을 포함하지 않습니다. 로딩 시 Keras는 모델을 재구성하기 위해 이러한 객체의 Python 클래스/함수에 접근해야 합니다. 사용자 정의 객체를 참조하세요.

아키텍처 저장

모델의 구성(또는 아키텍처)은 모델에 포함된 레이어와 이러한 레이어의 연결 방법*을 지정합니다. 모델 구성이 있는 경우 가중치에 대해 새로 초기화된 상태로 컴파일 정보 없이 모델을 작성할 수 있습니다.

*이 기능은 서브 클래스 모델이 아닌 함수형 또는 Sequential API를 사용하여 정의된 모델에만 적용됩니다.

Sequential 모델 또는 Functional API 모델의 구성

이러한 유형의 모델은 레이어의 명시적 그래프입니다. 구성은 항상 구조화된 형식으로 제공됩니다.

APIs

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)

Sequential 모델 예제:

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

Functional 모델 예제:

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 메서드에 정의되어 있습니다. 그것들은 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에 의해 반환된 객체는 Keras 모델이 아닙니다. 따라서 사용하기가 쉽지 않습니다. 예를 들면, .predict() 또는 .fit()에 접근할 수 없습니다.

사용을 권장하지는 않지만, 사용자 정의 객체의 코드를 잃어버렸거나 tf.keras.models.load_model() 모델을 로드하는 데 문제가 있는 경우와 같이 곤란한 상황에서는 도움이 될 수 있습니다.

tf.saved_model.load와 관련된 페이지에서 자세한 내용을 확인할 수 있습니다.

구성 메서드 정의하기

명세:

  • get_config는 Keras 아키텍처 및 모델 저장 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} }

from_config를 호출할 올바른 클래스를 찾는 데 사용되는 모든 내장 레이어, 모델, 옵티마이저 및 메트릭 클래스의 마스터 목록을 유지합니다. 클래스를 찾을 수 없으면 오류가 발생합니다(Value Error: Unknown layer). 다음 목록은 사용자 정의 클래스를 등록하는 몇 가지 방법입니다.

  1. 로딩 함수에서 custom_objects 인수 설정(위의 "구성 메서드 정의하기" 섹션의 예 참조).
  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()을 통해 모델의 인메모리 복제를 수행할 수도 있습니다. 이는 구성을 가져온 다음 구성에서 모델을 다시 생성하는 것과 같습니다(따라서 컴파일 정보 또는 레이어 가중치 값을 유지하지 않습니다).

예제:

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

모델의 가중치 값만 저장 및 로딩

모델의 가중치 값만 저장하고 로드하도록 선택할 수 있습니다. 다음과 같은 경우에 유용할 수 있습니다.

  • 추론을 위한 모델만 필요합니다. 이 경우 훈련을 다시 시작할 필요가 없으므로 컴파일 정보나 옵티마이저 상태가 필요하지 않습니다.
  • 전이 학습을 수행하고 있습니다. 이 경우 이전 모델의 상태를 재사용하는 새 모델을 훈련하므로 이전 모델의 컴파일 정보가 필요하지 않습니다.

인메모리 가중치 전이를 위한 API

get_weightsset_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 Checkpoint
  • HDF5

model.save_weights의 기본 형식은 TensorFlow 체크포인트입니다. 저장 형식을 지정하는 두 가지 방법이 있습니다.

  1. save_format 인수: save_format="tf" 또는 save_format="h5"에 값을 설정합니다.
  2. path 인수: 경로가 .h5 또는 .hdf5로 끝나면 HDF5 형식이 사용됩니다. save_format을 설정하지 않으면 다른 접미어의 경우 TensorFlow 체크포인트로 결과가 발생합니다.

인메모리 numpy 배열로 가중치를 검색하는 옵션도 있습니다. 각 API에는 장단점이 있으며 아래에서 자세히 설명합니다.

TF Checkpoint 형식

예제:

# 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 0x7f0aa8133c18>

형식 세부 사항

TensorFlow Checkpoint 형식은 객체 속성명을 사용하여 가중치를 저장하고 복원합니다. 예를 들어, tf.keras.layers.Dense 레이어를 고려해 봅시다. 레이어에는 dense.kerneldense.bias 두 가지 가중치가 있습니다. 레이어가 tf 형식으로 저장되면 결과 체크포인트에는 "kernel""bias"와 해당 가중치 값이 포함됩니다. 자세한 정보는 TF Checkpoint 가이드의 "로딩 메커니즘"을 참조하세요.

속성/그래프 에지는 변수명이 아니라 부모 객체에서 사용된 이름에 따라 이름이 지정됩니다. 아래 예제의 CustomLayer를 고려해 봅시다. 변수 CustomLayer.var"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}

전이 학습 예제

기본적으로 두 모델이 동일한 아키텍처를 갖는 한 동일한 검사점을 공유할 수 있습니다.

예제:

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 0x7f0aa808e0f0>

일반적으로 모델을 빌드할 때 동일한 API를 사용하는 것이 좋습니다. Sequential 및 Functional 또는 Functional 및 서브 클래스 등 간에 전환하는 경우, 항상 사전 훈련된 모델을 다시 빌드하고 사전 훈련된 가중치를 해당 모델에 로드합니다.

다음 질문은 모델 아키텍처가 상당히 다른 경우 어떻게 다른 모델에 가중치를 저장하고 로드하는가입니다. 해결책은 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 0x7f0aa8096cc0>

HDF5 format

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
_________________________________________________________________