Сохранение и загрузка моделей 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

Есть два формата , которые можно использовать , чтобы сохранить всю модель на диск: формат TensorFlow SavedModel и старый формат Keras H5. Рекомендуемый формат — SavedModel. Это по умолчанию при использовании model.save() .

Вы можете переключиться на формат H5:

  • Переходя save_format='h5' , чтобы save() .
  • Передача файла , который заканчивается в .h5 или .keras , чтобы save() .

Формат сохраненной модели

SavedModel — это более полный формат сохранения, который сохраняет архитектуру модели, веса и отслеживаемые подграфы Tensorflow функций вызова. Это позволяет Keras восстанавливать как встроенные слои, так и пользовательские объекты.

Пример:

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 [==============================] - 1s 2ms/step - loss: 0.5884
2021-08-25 17:49:05.320893: W tensorflow/python/util/util.cc:348] Sets are not currently considered sequences, but this may change in the future, so consider avoiding using them.
INFO:tensorflow:Assets written to: my_model/assets
4/4 [==============================] - 0s 2ms/step - loss: 0.5197
<keras.callbacks.History at 0x7f99486ad490>

Что содержит SavedModel

Вызов model.save('my_model') создает папку с именем my_model , содержащий следующее:

ls my_model
assets  keras_metadata.pb  saved_model.pb  variables

Архитектура модели и конфигурация обучения ( в том числе оптимизатора, потерь и метрик) сохраняются в saved_model.pb . Веса сохраняются в variables/ каталога.

Для получения более подробной информации о формате SavedModel см руководство SavedModel (формат SavedModel на диске) .

Как SavedModel обрабатывает пользовательские объекты

При сохранении модели и ее слои, SavedModel формат сохраняет имя класса, функцию вызова, потери и весы (и конфигурацию, в случае их реализации). Функция вызова определяет граф вычислений модели/слоя.

В отсутствие конфигурации модели/слоя функция вызова используется для создания модели, которая существует как исходная модель, которую можно обучить, оценить и использовать для вывода.

Тем не менее, это всегда хорошая практика , чтобы определить get_config и from_config методы при написании пользовательской модели или класса слоя. Это позволяет вам легко обновить вычисление позже, если это необходимо. Смотрите раздел о пользовательских объектах для получения дополнительной информации.

Пример:

class CustomModel(keras.Model):
    def __init__(self, hidden_units):
        super(CustomModel, self).__init__()
        self.hidden_units = hidden_units
        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

    def get_config(self):
        return {"hidden_units": self.hidden_units}

    @classmethod
    def from_config(cls, config):
        return cls(**config)


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")

# Option 1: Load with the custom_object argument.
loaded_1 = keras.models.load_model(
    "my_model", custom_objects={"CustomModel": CustomModel}
)

# Option 2: Load without the CustomModel class.

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

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

print("Original model:", model)
print("Model Loaded with custom objects:", loaded_1)
print("Model loaded without the custom object class:", loaded_2)
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.
WARNING:tensorflow:No training configuration found in save file, so the model was *not* compiled. Compile it manually.
Original model: <__main__.CustomModel object at 0x7f9949c86810>
Model Loaded with custom objects: <__main__.CustomModel object at 0x7f99681f61d0>
Model loaded without the custom object class: <keras.saving.saved_model.load.CustomModel object at 0x7f9aaceefd10>

Первая загруженная модель загружаются с использованием конфигурации и CustomModel класса. Вторая модель загружается путем динамического создания класса модели, который действует как исходная модель.

Настройка сохраненной модели

Новое в TensoFlow 2.4 Аргумент save_traces был добавлен model.save , который позволяет переключать трассировке функции SavedModel. Функции сохраняются , чтобы позволить Keras для повторной нагрузки пользовательских объектов без исходных definitons класса, поэтому , когда save_traces=False , все пользовательские объекты должны быть определены get_config / from_config методы. При загрузке, пользовательские объекты должны быть переданы custom_objects аргумента. save_traces=False уменьшает дисковое пространство , используемое в SavedModel и экономии времени.

Формат Керас H5

Keras также поддерживает сохранение одного файла , содержащего hdf5 архитектуру модели, значения веса и compile() информацию. Это облегченная альтернатива 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: 1.6322
4/4 [==============================] - 0s 1ms/step - loss: 1.4790
<keras.callbacks.History at 0x7f9aacc0fd50>

Ограничения

По сравнению с форматом SavedModel в файл H5 не включаются две вещи:

  • Внешние потери и показатели , добавленные через model.add_loss() & model.add_metric() не сохраняются ( в отличие от SavedModel). Если у вас есть такие потери и метрики на вашей модели и вы хотите возобновить обучение, вам нужно добавить эти потери обратно самостоятельно после загрузки модели. Обратите внимание , что это не относится к убыткам / метрик , созданных внутри слоев с помощью self.add_loss() & self.add_metric() . Пока загружается слой, эти потери и показатели сохраняются, так как они являются частью call метода слоя.
  • Вычисление графы пользовательских объектов , такие как пользовательские слои не включены в сохраненном файле. Во время загрузки Keras потребуется доступ к классам/функциям Python этих объектов, чтобы восстановить модель. См объекты Пользовательские .

Сохранение архитектуры

Конфигурация (или архитектура) модели определяет, какие слои содержит модель и как эти слои связаны*. Если у вас есть конфигурация модели, то модель может быть создана со свежим инициализированным состоянием для весов и без информации о компиляции.

*Обратите внимание, что это относится только к моделям, определенным с использованием функциональных или последовательных API-моделей без подклассов.

Конфигурация модели Sequential или модели Functional API

Эти типы моделей представляют собой явные графы слоев: их конфигурация всегда доступна в структурированном виде.

API

get_config() и from_config()

Вызов config = model.get_config() будет возвращать Dict Python , содержащий конфигурацию модели. Же модель может затем быть восстановлена с помощью Sequential.from_config(config) (для Sequential модели) или Model.from_config(config) (для функциональной модели 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 . Они считаются Python байт - код, который не может быть сериализован в JSON-совместимой конфигурации - вы можете попробовать сериализации байткод (например , через pickle ), но это совершенно небезопасно и означает , что ваша модель не может быть загружен на другой системе.

Для сохранения / загрузки модели с определенным пользователем слоями, или подклассами модели, вы должны переписать get_config и , возможно , from_config методу. Кроме того, вы должны зарегистрировать пользовательский объект, чтобы Keras знал об этом.

Пользовательские функции

Пользовательские определенные функции (например , потеря активации или инициализации) не нужен get_config метод. Имени функции достаточно для загрузки, если оно зарегистрировано как пользовательский объект.

Загрузка только графика TensorFlow

Можно загрузить график TensorFlow, сгенерированный Keras. Если вы сделаете это, вам не нужно будет предоставлять какие - либо 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()
WARNING:tensorflow:Compiled the loaded model, but the compiled metrics have yet to be built. `model.compile_metrics` will be empty until you train or evaluate the model.
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 должен вернуть JSON-сериализации словарь для того , чтобы быть совместимым с Keras architecture- и модели экономии APIs.
  • 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 аргумента в функции загрузки. (см. пример в разделе выше «Определение методов конфигурации»)
  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_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 1 to layer 2
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 контрольно - пропускной пункт. Существует два способа указать формат сохранения:

  1. save_format аргумент: установите значение save_format="tf" или save_format="h5" .
  2. path аргумент: Если путь заканчивается с .h5 или .hdf5 , то используется формат HDF5. Другие суффиксы приведет к TensorFlow контрольно - пропускной пункт , если save_format не установлен.

Существует также возможность получения весов в виде массивов 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 0x7f9aaca4ced0>

Сведения о формате

Формат TensorFlow Checkpoint сохраняет и восстанавливает веса, используя имена атрибутов объекта. Например, рассмотрим tf.keras.layers.Dense слой. Слой содержит два веса: dense.kernel и dense.bias . Когда слой сохраняется в tf формате, в результате контрольной точки содержит ключи "kernel" и "bias" и соответствующие им значения веса. Для получения дополнительной информации см «Загрузка механика» в руководстве TF Checkpoint .

Обратите внимание , что атрибут / график края назван после того, как имя , используемое в родительском объекте, а не имя переменной. Рассмотрим CustomLayer в примере ниже. Переменная CustomLayer.var сохраняется с "var" , как часть ключа, а не "var_a" .

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

Обычно рекомендуется придерживаться одного и того же 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()
/tmpfs/src/tf_docs_env/lib/python3.7/site-packages/keras/engine/base_layer.py:2223: UserWarning: `layer.add_variable` is deprecated and will be removed in a future version. Please use `layer.add_weight` method instead.
  warnings.warn('`layer.add_variable` is deprecated and '
<tensorflow.python.training.tracking.util.CheckpointLoadStatus at 0x7f9aaca6f390>

формат 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
_________________________________________________________________