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

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 класса. Вторая модель загружается путем динамического создания класса модели, который действует как исходная модель.

Настройка SavedModel

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

Формат Keras 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 метода слоя.
  • Вычисление графы пользовательских объектов , такие как пользовательские слои не включены в сохраненном файле. Во время загрузки Керасу потребуется доступ к классам / функциям Python этих объектов, чтобы восстановить модель. См объекты Пользовательские .

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

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

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

Конфигурация последовательной модели или функциональной модели 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 методу. Кроме того, вы должны использовать регистрацию настраиваемого объекта, чтобы Керас знал об этом.

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

Пользовательские определенные функции (например , потеря активации или инициализации) не нужен 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}
)

Регистрация настраиваемого объекта

Керас записывает, какой класс сгенерировал конфигурацию. Из приведенного выше примера, 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
_________________________________________________________________