RSVP для вашего местного мероприятия TensorFlow Everywhere сегодня!
Эта страница переведена с помощью Cloud Translation API.
Switch to English

Сохранение и загрузка моделей Keras

Посмотреть на TensorFlow.org Запускаем в Google Colab Посмотреть исходный код на GitHub Скачать блокнот

Вступление

Модель Keras состоит из нескольких компонентов:

  • Архитектура или конфигурация, которая определяет, какие слои модель содержит и как они связаны.
  • Набор значений весов («состояние модели»).
  • Оптимизатор (определяется путем компиляции модели).
  • Набор потерь и показателей (определяется путем компиляции модели или вызова add_loss() или add_metric() ).

Keras API позволяет сохранить все эти части на диск сразу или только выборочно сохранить некоторые из них:

  • Сохранение всего в один архив в формате TensorFlow SavedModel (или в более старом формате Keras H5). Это стандартная практика.
  • Сохранение только архитектуры / конфигурации, обычно в виде файла JSON.
  • Сохранение только значений веса. Обычно это используется при обучении модели.

Давайте рассмотрим каждый из этих вариантов. Когда бы вы использовали один или другой и как они работают?

Как сохранить и загрузить модель

Если у вас есть всего 10 секунд, чтобы прочитать это руководство, вот что вам нужно знать.

Сохранение модели Keras:

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

Загрузка модели обратно:

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

А теперь давайте рассмотрим детали.

Настраивать

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

Сохранение и загрузка всей модели

Вы можете сохранить всю модель в одном артефакте. В него войдут:

  • Архитектура / конфигурация модели
  • Значения веса модели (полученные во время обучения)
  • Информация о компиляции модели (если был вызван compile() )
  • Оптимизатор и его состояние, если таковое имеется (это позволяет перезапустить обучение с того места, где вы остановились)

API

Есть два формата, которые вы можете использовать для сохранения всей модели на диск: формат 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.9237
INFO:tensorflow:Assets written to: my_model/assets
4/4 [==============================] - 0s 1ms/step - loss: 0.7730

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

Что содержит 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_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 0x7fd0a035bcf8>
Model Loaded with custom objects: <__main__.CustomModel object at 0x7fd1455d04e0>
Model loaded without the custom object class: <tensorflow.python.keras.saving.saved_model.load.CustomModel object at 0x7fd14553af98>

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

Настройка SavedModel

Новое в TensoFlow 2.4 В save_traces был добавлен model.save , который позволяет переключать трассировку функции SavedModel. Функции сохраняются, чтобы позволить Keras повторно загружать пользовательские объекты без исходных определений классов, поэтому, когда save_traces=False , все пользовательские объекты должны иметь определенные get_config / from_config . При загрузке настраиваемые объекты необходимо custom_objects аргументу 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.0153
4/4 [==============================] - 0s 1ms/step - loss: 0.9104

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

Ограничения

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

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

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

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

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

Конфигурация последовательной модели или функциональной модели API

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

API

get_config() и from_config()

Вызов config = model.get_config() вернет Python dict, содержащий конфигурацию модели. Затем ту же модель можно реконструировать с помощью 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()
INFO:tensorflow:Assets written to: my_model/assets

Обратите внимание, что у этого метода есть несколько недостатков:

  • По соображениям прослеживаемости у вас всегда должен быть доступ к используемым настраиваемым объектам. Вы бы не хотели запускать в производство модель, которую невозможно воссоздать.
  • Объект, возвращаемый tf.saved_model.load , не является моделью tf.saved_model.load . Так что пользоваться не так просто. Например, у вас не будет доступа к .predict() или .fit()

Даже если его использование не рекомендуется, оно может помочь вам, если вы находитесь в затруднительном положении, например, если вы потеряли код своих настраиваемых объектов или возникли проблемы с загрузкой модели с помощью tf.keras.models.load_model() .

Вы можете узнать больше на странице о tf.saved_model.load

Определение методов конфигурации

Характеристики:

  • get_config должен возвращать JSON-сериализуемый словарь, чтобы быть совместимым с API-интерфейсами get_config архитектуру и модель.
  • from_config(config) ( classmethod ) должен возвращать новый слой или объект модели, созданный из config. Реализация по умолчанию возвращает 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 является контрольная model.save_weights . Есть два способа указать формат сохранения:

  1. Аргумент save_format : установите значение save_format="tf" или save_format="h5" .
  2. Аргумент path : если путь заканчивается на .h5 или .hdf5 , то используется формат HDF5. Другие суффиксы приведут к созданию контрольной точки save_format если не установлено 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 0x7fd0a065f128>

Детали формата

Формат 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 0x7fd144b20b38>

Обычно рекомендуется придерживаться одного и того же 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.6/site-packages/tensorflow/python/keras/engine/base_layer.py:2281: 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 0x7fd1455c6cc0>

Формат HDF5

Формат HDF5 содержит веса, сгруппированные по именам слоев. Веса - это списки, упорядоченные путем объединения списка обучаемых весов со списком layer.weights весов (то же, что и 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
_________________________________________________________________