Эта страница была переведа с помощью 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

Пример:

def get_model():
    # Create a simple model.
    inputs = keras.Input(shape=(32,))
    outputs = keras.layers.Dense(1)(inputs)
    model = keras.Model(inputs, outputs)
    model.compile(optimizer="adam", loss="mean_squared_error")
    return model


model = get_model()

# Train the model.
test_input = np.random.random((128, 32))
test_target = np.random.random((128, 1))
model.fit(test_input, test_target)

# Calling `save('my_model')` creates a SavedModel folder `my_model`.
model.save("my_model")

# It can be used to reconstruct the model identically.
reconstructed_model = keras.models.load_model("my_model")

# Let's check:
np.testing.assert_allclose(
    model.predict(test_input), reconstructed_model.predict(test_input)
)

# The reconstructed model is already compiled and has retained the optimizer
# state, so training can resume:
reconstructed_model.fit(test_input, test_target)
4/4 [==============================] - 0s 1ms/step - loss: 1.1917
WARNING:tensorflow:From /tmpfs/src/tf_docs_env/lib/python3.6/site-packages/tensorflow/python/ops/resource_variable_ops.py:1817: calling BaseResourceVariable.__init__ (from tensorflow.python.ops.resource_variable_ops) with constraint is deprecated and will be removed in a future version.
Instructions for updating:
If using Keras pass *_constraint arguments to layers.
INFO:tensorflow:Assets written to: my_model/assets
4/4 [==============================] - 0s 1ms/step - loss: 1.0581

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

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

Ниже приведен пример того, что происходит при загрузке пользовательских слоев из формата SavedModel без перезаписи методов конфигурации.

class CustomModel(keras.Model):
    def __init__(self, hidden_units):
        super(CustomModel, self).__init__()
        self.dense_layers = [keras.layers.Dense(u) for u in hidden_units]

    def call(self, inputs):
        x = inputs
        for layer in self.dense_layers:
            x = layer(x)
        return x


model = CustomModel([16, 16, 10])
# Build the model by calling it
input_arr = tf.random.uniform((1, 5))
outputs = model(input_arr)
model.save("my_model")

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

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

print("Original model:", model)
print("Loaded model:", loaded)
INFO:tensorflow:Assets written to: my_model/assets
WARNING:tensorflow:No training configuration found in save file, so the model was *not* compiled. Compile it manually.
Original model: <__main__.CustomModel object at 0x7f1370081550>
Loaded model: <tensorflow.python.keras.saving.saved_model.load.CustomModel object at 0x7f1328722e48>

Как видно из приведенного выше примера, загрузчик динамически создает новый класс модели, который действует как исходная модель.

Формат 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: 4.1064
4/4 [==============================] - 0s 1ms/step - loss: 3.8469

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

Ограничения

По сравнению с форматом 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() вернет 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 2 to layer 1
layer_2.set_weights(layer_1.get_weights())

Перенос весов из одной модели в другую модель с совместимой архитектурой в памяти

# Create a simple functional model
inputs = keras.Input(shape=(784,), name="digits")
x = keras.layers.Dense(64, activation="relu", name="dense_1")(inputs)
x = keras.layers.Dense(64, activation="relu", name="dense_2")(x)
outputs = keras.layers.Dense(10, name="predictions")(x)
functional_model = keras.Model(inputs=inputs, outputs=outputs, name="3_layer_mlp")

# Define a subclassed model with the same architecture
class SubclassedModel(keras.Model):
    def __init__(self, output_dim, name=None):
        super(SubclassedModel, self).__init__(name=name)
        self.output_dim = output_dim
        self.dense_1 = keras.layers.Dense(64, activation="relu", name="dense_1")
        self.dense_2 = keras.layers.Dense(64, activation="relu", name="dense_2")
        self.dense_3 = keras.layers.Dense(output_dim, name="predictions")

    def call(self, inputs):
        x = self.dense_1(inputs)
        x = self.dense_2(x)
        x = self.dense_3(x)
        return x

    def get_config(self):
        return {"output_dim": self.output_dim, "name": self.name}


subclassed_model = SubclassedModel(10)
# Call the subclassed model once to create the weights.
subclassed_model(tf.ones((1, 784)))

# Copy weights from functional_model to subclassed_model.
subclassed_model.set_weights(functional_model.get_weights())

assert len(functional_model.weights) == len(subclassed_model.weights)
for a, b in zip(functional_model.weights, subclassed_model.weights):
    np.testing.assert_allclose(a.numpy(), b.numpy())

Случай слоев без состояния

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

inputs = keras.Input(shape=(784,), name="digits")
x = keras.layers.Dense(64, activation="relu", name="dense_1")(inputs)
x = keras.layers.Dense(64, activation="relu", name="dense_2")(x)
outputs = keras.layers.Dense(10, name="predictions")(x)
functional_model = keras.Model(inputs=inputs, outputs=outputs, name="3_layer_mlp")

inputs = keras.Input(shape=(784,), name="digits")
x = keras.layers.Dense(64, activation="relu", name="dense_1")(inputs)
x = keras.layers.Dense(64, activation="relu", name="dense_2")(x)

# Add a dropout layer, which does not contain any weights.
x = keras.layers.Dropout(0.5)(x)
outputs = keras.layers.Dense(10, name="predictions")(x)
functional_model_with_dropout = keras.Model(
    inputs=inputs, outputs=outputs, name="3_layer_mlp"
)

functional_model_with_dropout.set_weights(functional_model.get_weights())

API для сохранения весов на диск и их загрузки обратно

Веса можно сохранить на диск, вызвав model.save_weights в следующих форматах:

  • Контрольная точка TensorFlow
  • HDF5

Форматом по умолчанию для model.save_weights является контрольная 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 0x7f1416793ba8>

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

Формат 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 (Model)           (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 0x7f1416704278>

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

Следующий вопрос: как можно сохранять веса и загружать их в разные модели, если архитектуры моделей сильно различаются? Решение состоит в том, чтобы использовать tf.train.Checkpoint для сохранения и восстановления точных слоев / переменных.

Пример:

# Create a subclassed model that essentially uses functional_model's first
# and last layers.
# First, save the weights of functional_model's first and last dense layers.
first_dense = functional_model.layers[1]
last_dense = functional_model.layers[-1]
ckpt_path = tf.train.Checkpoint(
    dense=first_dense, kernel=last_dense.kernel, bias=last_dense.bias
).save("ckpt")

# Define the subclassed model.
class ContrivedModel(keras.Model):
    def __init__(self):
        super(ContrivedModel, self).__init__()
        self.first_dense = keras.layers.Dense(64)
        self.kernel = self.add_variable("kernel", shape=(64, 10))
        self.bias = self.add_variable("bias", shape=(10,))

    def call(self, inputs):
        x = self.first_dense(inputs)
        return tf.matmul(x, self.kernel) + self.bias


model = ContrivedModel()
# Call model on inputs to create the variables of the dense layer.
_ = model(tf.ones((1, 784)))

# Create a Checkpoint with the same structure as before, and load the weights.
tf.train.Checkpoint(
    dense=model.first_dense, kernel=model.kernel, bias=model.bias
).restore(ckpt_path).assert_consumed()
WARNING:tensorflow:From <ipython-input-21-eec1d28bc826>:15: Layer.add_variable (from tensorflow.python.keras.engine.base_layer) is deprecated and will be removed in a future version.
Instructions for updating:
Please use `layer.add_weight` method instead.

<tensorflow.python.training.tracking.util.CheckpointLoadStatus at 0x7f1416713358>

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