Ta strona została przetłumaczona przez Cloud Translation API.
Switch to English

Zapisz i wczytaj modele Keras

Zobacz na TensorFlow.org Uruchom w Google Colab Wyświetl źródło na GitHub Pobierz notatnik

Wprowadzenie

Model Keras składa się z wielu komponentów:

  • Architektura lub konfiguracja określająca, jakie warstwy zawiera model i jak są one połączone.
  • Zbiór wartości wag („stan modelu”).
  • Optymalizator (definiowany przez kompilację modelu).
  • Zestaw strat i metryk (zdefiniowanych przez kompilację modelu lub wywołanie add_loss() lub add_metric() ).

Interfejs API Keras umożliwia jednoczesne zapisanie wszystkich tych elementów na dysku lub wybiórcze zapisywanie tylko niektórych z nich:

  • Zapisywanie wszystkiego w jednym archiwum w formacie TensorFlow SavedModel (lub w starszym formacie Keras H5). To jest standardowa praktyka.
  • Zapisywanie tylko architektury / konfiguracji, zwykle jako plik JSON.
  • Zapisywanie tylko wartości wag. Jest to zwykle używane podczas uczenia modelu.

Przyjrzyjmy się każdej z tych opcji: kiedy użyjesz jednej lub drugiej? Jak oni pracują?

Krótka odpowiedź na zapisywanie i ładowanie

Jeśli masz tylko 10 sekund na przeczytanie tego przewodnika, oto, co musisz wiedzieć.

Zapisywanie modelu Keras:

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

Ładowanie modelu z powrotem:

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

Teraz spójrzmy na szczegóły.

Ustawiać

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

Zapisywanie i ładowanie całego modelu

Możesz zapisać cały model jako pojedynczy artefakt. Będzie zawierał:

  • Architektura / config modelu
  • Wartości masy modelu (których nauczyliśmy się podczas treningu)
  • Została wywołana informacja o kompilacji modelu (jeśli compile() )
  • Optymalizator i jego stan, jeśli istnieje (umożliwia to ponowne uruchomienie treningu w miejscu, w którym został)

Pszczoła

Istnieją dwa formaty, których można użyć do zapisania całego modelu na dysku: format TensorFlow SavedModel i starszy format Keras H5 . Zalecany format to SavedModel. Jest to ustawienie domyślne, gdy używasz model.save() .

Możesz przejść do formatu H5 przez:

  • Przekazywanie save_format='h5' do save() .
  • Przekazanie nazwy pliku kończącej się na .h5 lub .keras aby save() .

Format SavedModel

Przykład:

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: 0.3312
WARNING:tensorflow:From /tmpfs/src/tf_docs_env/lib/python3.6/site-packages/tensorflow/python/training/tracking/tracking.py:111: Model.state_updates (from tensorflow.python.keras.engine.training) is deprecated and will be removed in a future version.
Instructions for updating:
This property should not be used in TensorFlow 2.0, as updates are applied automatically.
WARNING:tensorflow:From /tmpfs/src/tf_docs_env/lib/python3.6/site-packages/tensorflow/python/training/tracking/tracking.py:111: Layer.updates (from tensorflow.python.keras.engine.base_layer) is deprecated and will be removed in a future version.
Instructions for updating:
This property should not be used in TensorFlow 2.0, as updates are applied automatically.
INFO:tensorflow:Assets written to: my_model/assets
4/4 [==============================] - 0s 1ms/step - loss: 0.2840

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

Co zawiera SavedModel

Wywołanie model.save('my_model') tworzy folder o nazwie my_model , zawierający następujące elementy:

ls my_model
assets  saved_model.pb  variables

Architektura modelu i konfiguracja treningowa (w tym optymalizator, straty i metryki) są przechowywane w saved_model.pb . Wagi są zapisywane w katalogu variables/ .

Aby uzyskać szczegółowe informacje na temat formatu SavedModel, zobacz przewodnik SavedModel ( format SavedModel na dysku ) .

Jak SavedModel obsługuje obiekty niestandardowe

Podczas zapisywania modelu i jego warstw format SavedModel przechowuje nazwę klasy, funkcję wywołania , straty i wagi (oraz konfigurację, jeśli zaimplementowano). Funkcja wywołania definiuje wykres obliczeniowy modelu / warstwy.

W przypadku braku konfiguracji modelu / warstwy funkcja wywołania jest używana do tworzenia modelu, który istnieje jak oryginalny model, który można wytrenować, ocenić i wykorzystać do wnioskowania.

Niemniej jednak zawsze dobrą praktyką jest definiowanie metod get_config i from_config podczas pisania niestandardowego modelu lub klasy warstw. Pozwala to w razie potrzeby łatwo zaktualizować obliczenia później. Więcej informacji można znaleźć w sekcji dotyczącej obiektów niestandardowych .

Poniżej znajduje się przykład tego, co dzieje się podczas ładowania warstw niestandardowych z formatu SavedModel bez nadpisywania metod config.

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 0x7f0fbc3f8208>
Loaded model: <tensorflow.python.keras.saving.saved_model.load.CustomModel object at 0x7f1031981518>

Jak widać w powyższym przykładzie, program ładujący dynamicznie tworzy nową klasę modelu, która działa jak oryginalny model.

Format Keras H5

Keras obsługuje również zapisywanie pojedynczego pliku HDF5 zawierającego architekturę modelu, wartości wag i informacje compile() . Jest to lekka alternatywa dla SavedModel.

Przykład:

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: 0.3771
4/4 [==============================] - 0s 1ms/step - loss: 0.3432

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

Ograniczenia

W porównaniu z formatem SavedModel istnieją dwie rzeczy, które nie są uwzględniane w pliku H5:

  • Zewnętrzne straty i metryki dodane przez model.add_loss() i model.add_metric() nie są zapisywane (w przeciwieństwie do SavedModel). Jeśli masz takie straty i metryki w swoim modelu i chcesz wznowić szkolenie, po załadowaniu modelu musisz dodać te straty z powrotem. Zauważ, że nie dotyczy to strat / metryk utworzonych w warstwach przez self.add_loss() i self.add_metric() . Dopóki warstwa jest ładowana, te straty i metryki są zachowywane, ponieważ są częścią metody call warstwy.
  • Wykres obliczeniowy obiektów niestandardowych, takich jak warstwy niestandardowe, nie jest zawarty w zapisanym pliku. W czasie ładowania Keras będzie potrzebować dostępu do klas / funkcji tych obiektów w języku Python, aby zrekonstruować model. Zobacz Obiekty niestandardowe .

Ratowanie architektury

Konfiguracja (lub architektura) modelu określa, jakie warstwy zawiera model i jak te warstwy są połączone *. Jeśli masz konfigurację modelu, możesz utworzyć model ze świeżo zainicjowanym stanem dla wag i bez informacji o kompilacji.

* Należy pamiętać, że dotyczy to tylko modeli zdefiniowanych za pomocą funkcji lub sekwencyjnych interfejsów API bez modeli podklas.

Konfiguracja modelu sekwencyjnego lub modelu funkcjonalnego interfejsu API

Te typy modeli są jawnymi wykresami warstw: ich konfiguracja jest zawsze dostępna w ustrukturyzowanej formie.

Pszczoła

get_config() i from_config()

Wywołanie config = model.get_config() zwróci config = model.get_config() Pythona zawierające konfigurację modelu. Ten sam model można następnie zrekonstruować za pomocą Sequential.from_config(config) (dla modelu Sequential ) lub Model.from_config(config) (dla modelu funkcjonalnego interfejsu API).

Ten sam przepływ pracy działa również dla każdej warstwy możliwej do serializacji.

Przykład warstwy:

layer = keras.layers.Dense(3, activation="relu")
layer_config = layer.get_config()
new_layer = keras.layers.Dense.from_config(layer_config)

Przykład modelu sekwencyjnego:

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

Przykład modelu funkcjonalnego:

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() i tf.keras.models.model_from_json()

Jest to podobne do get_config / from_config , z tą różnicą, że zamienia model w ciąg JSON, który można następnie załadować bez oryginalnej klasy modelu. Jest również specyficzny dla modeli, nie jest przeznaczony dla warstw.

Przykład:

model = keras.Sequential([keras.Input((32,)), keras.layers.Dense(1)])
json_config = model.to_json()
new_model = keras.models.model_from_json(json_config)

Obiekty niestandardowe

Modele i warstwy

Architektura modeli i warstw podklasowych jest zdefiniowana w metodach __init__ i call . Są one uważane za kod bajtowy Pythona, którego nie można serializować do konfiguracji zgodnej z JSON - możesz spróbować serializować kod bajtowy (np. Przez pickle ), ale jest to całkowicie niebezpieczne i oznacza, że ​​twój model nie może być załadowany w innym systemie.

Aby zapisać / wczytać model z niestandardowymi warstwami lub modelem get_config , należy nadpisać metody get_config i opcjonalnie from_config . Ponadto należy użyć funkcji zarejestruj niestandardowy obiekt, aby Keras o tym wiedział.

Funkcje niestandardowe

Funkcje zdefiniowane przez użytkownika (np. Utrata aktywacji lub inicjalizacja) nie wymagają metody get_config . Nazwa funkcji jest wystarczająca do załadowania, o ile jest zarejestrowana jako obiekt niestandardowy.

Ładowanie samego wykresu TensorFlow

Możliwe jest załadowanie wykresu TensorFlow wygenerowanego przez Keras. Jeśli to zrobisz, nie będziesz musiał podawać żadnych custom_objects . Możesz to zrobić w ten sposób:

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

Zwróć uwagę, że ta metoda ma kilka wad:

  • Ze względu na identyfikowalność należy zawsze mieć dostęp do używanych obiektów niestandardowych. Nie chciałbyś wprowadzić do produkcji modelu, którego nie możesz odtworzyć.
  • Obiekt zwrócony przez tf.saved_model.load nie jest modelem Keras. Więc to nie jest tak łatwe w użyciu. Na przykład nie będziesz mieć dostępu do .predict() ani .fit()

Nawet jeśli jego użycie jest odradzane, może ci pomóc, jeśli jesteś w trudnej sytuacji, na przykład, jeśli zgubiłeś kod niestandardowych obiektów lub masz problemy z ładowaniem modelu za pomocą tf.keras.models.load_model() .

Możesz dowiedzieć się więcej na stronie o tf.saved_model.load

Definiowanie metod konfiguracji

Specyfikacje:

  • get_config powinien zwrócić słownik z możliwością serializacji JSON, aby był zgodny z interfejsami API zapisującymi architekturę i modele Keras.
  • from_config(config) ( classmethod ) powinien zwrócić nową warstwę lub obiekt modelu, który jest tworzony z classmethod config. Domyślna implementacja zwraca cls(**config) .

Przykład:

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

Rejestracja obiektu niestandardowego

Keras odnotowuje, która klasa wygenerowała plik config. Z powyższego przykładu, tf.keras.layers.serialize generuje serializowaną postać warstwy niestandardowej:

{'class_name': 'CustomLayer', 'config': {'a': 2} }

Keras przechowuje główną listę wszystkich wbudowanych klas warstwy, modelu, optymalizatora i metryki, która jest używana do znalezienia właściwej klasy do wywołania from_config . Jeśli nie można znaleźć klasy, zgłaszany jest błąd ( Value Error: Unknown layer ). Istnieje kilka sposobów rejestrowania niestandardowych klas na tej liście:

  1. Ustawienie argumentu custom_objects w funkcji ładowania. (zobacz przykład w sekcji powyżej „Definiowanie metod konfiguracji”)
  2. tf.keras.utils.custom_object_scope lub tf.keras.utils.CustomObjectScope
  3. tf.keras.utils.register_keras_serializable

Przykład warstwy i funkcji niestandardowej

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)

Klonowanie modelu w pamięci

Możesz także tf.keras.models.clone_model() model w pamięci za pomocą tf.keras.models.clone_model() . Jest to równoważne z uzyskaniem konfiguracji, a następnie odtworzeniem modelu z jego konfiguracji (więc nie zachowuje informacji o kompilacji ani wartości wag warstw).

Przykład:

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

Zapisywanie i ładowanie tylko wartości wag modelu

Możesz wybrać tylko zapisywanie i ładowanie ciężarów modelu. Może to być przydatne, jeśli:

  • Potrzebujesz tylko modelu do wnioskowania: w tym przypadku nie będziesz musiał ponownie uruchamiać uczenia, więc nie potrzebujesz informacji o kompilacji ani stanu optymalizatora.
  • Robisz uczenie transferowe: w tym przypadku będziesz trenować nowy model, ponownie wykorzystując stan poprzedniego modelu, więc nie potrzebujesz informacji kompilacji z poprzedniego modelu.

Interfejsy API do przenoszenia wagi w pamięci

Wagi można kopiować między różnymi obiektami za pomocą get_weights i set_weights :

Przykłady poniżej.

Przenoszenie ciężarów z jednej warstwy na drugą w pamięci

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

Przenoszenie wag z jednego modelu do innego z kompatybilną architekturą w pamięci

# 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())

Przypadek warstw bezpaństwowych

Ponieważ warstwy bezstanowe nie zmieniają kolejności ani liczby wag, modele mogą mieć kompatybilne architektury, nawet jeśli istnieją dodatkowe / brakujące warstwy bezstanowe.

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 do zapisywania ciężarów na dysku i wczytywania ich z powrotem

Wagi można zapisać na dysku, wywołując model.save_weights w następujących formatach:

  • Punkt kontrolny TensorFlow
  • HDF5

Domyślnym formatem dla model.save_weights jest punkt kontrolny TensorFlow. Istnieją dwa sposoby określenia formatu zapisu:

  1. save_format argument: Ustaw wartość na save_format="tf" lub save_format="h5" .
  2. argument path : jeśli ścieżka kończy się na .h5 lub .hdf5 , używany jest format HDF5. Inne sufiksy spowodują utworzenie punktu kontrolnego save_format chyba że ustawiono save_format .

Istnieje również opcja pobierania wag jako tablic numpy w pamięci. Każdy interfejs API ma swoje wady i zalety, które opisano szczegółowo poniżej.

Format TF Checkpoint

Przykład:

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

Szczegóły formatu

Format TensorFlow Checkpoint zapisuje i przywraca wagi przy użyciu nazw atrybutów obiektów. tf.keras.layers.Dense na przykład pod uwagę warstwę tf.keras.layers.Dense . Warstwa zawiera dwie wagi: dense.bias . dense.kernel i dense.bias . dense.bias . Gdy warstwa jest zapisywana w formacie tf , wynikowy punkt kontrolny zawiera klucze "kernel" i "bias" oraz odpowiadające im wartości wagi. Aby uzyskać więcej informacji, zobacz „Mechanika ładowania” w przewodniku po punkcie kontrolnym TF .

Zauważ, że nazwa krawędzi atrybutu / wykresu pochodzi od nazwy użytej w obiekcie nadrzędnym, a nie po nazwie zmiennej . Rozważmy CustomLayer w poniższym przykładzie. Zmienna CustomLayer.var jest zapisywana z "var" zmienną "var" jako częścią klucza, a nie "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}

Przenieś przykład nauki

Zasadniczo, o ile dwa modele mają tę samą architekturę, mogą współdzielić ten sam punkt kontrolny.

Przykład:

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

Generalnie zaleca się trzymanie się tego samego API do budowania modeli. Jeśli przełączysz się między sekwencyjną i funkcjonalną lub funkcjonalną i podklasą itp., Zawsze odbuduj wstępnie wytrenowany model i załaduj wstępnie wytrenowane wagi do tego modelu.

Kolejne pytanie brzmi: w jaki sposób można zapisywać wagi i ładować je do różnych modeli, jeśli architektury modeli są zupełnie inne? Rozwiązaniem jest użycie tf.train.Checkpoint do zapisywania i przywracania dokładnych warstw / zmiennych.

Przykład:

# 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-1-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 0x7f10306c6518>

Format HDF5

Format HDF5 zawiera wagi pogrupowane według nazw warstw. Wagi są listami uporządkowanymi przez konkatenację listy ciężarów layer.weights do trenowania z listą ciężarów, których nie można layer.weights (tak samo jak layer.weights ). W ten sposób model może używać punktu kontrolnego hdf5, jeśli ma te same warstwy i statusy do trenowania, jak zapisane w punkcie kontrolnym.

Przykład:

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

Zwróć uwagę, że zmiana layer.trainable może spowodować layer.weights kolejności layer.weights gdy model zawiera warstwy zagnieżdżone.

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

Przenieś przykład nauki

Podczas ładowania wstępnie wytrenowanych ciężarów z HDF5 zaleca się załadowanie ciężarów do oryginalnego modelu z punktami kontrolnymi, a następnie wyodrębnienie żądanych ciężarów / warstw do nowego modelu.

Przykład:

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
_________________________________________________________________