Keras-Modelle speichern und laden

Auf TensorFlow.org ansehen In Google Colab ausführen Quelle auf GitHub anzeigen Notizbuch herunterladen

Einführung

Ein Keras-Modell besteht aus mehreren Komponenten:

  • Die Architektur oder Konfiguration, die angibt, welche Schichten das Modell enthält und wie sie verbunden sind.
  • Ein Satz von Gewichtungswerten (der "Zustand des Modells").
  • Ein Optimierer (durch Kompilieren des Modells definiert).
  • Ein Satz von Verlusten und Metriken (definiert durch das Modell oder die Erstellung Aufruf add_loss() oder add_metric() ).

Die Keras-API ermöglicht es, alle diese Teile auf einmal auf der Festplatte zu speichern oder nur einige davon selektiv zu speichern:

  • Speichern Sie alles in einem einzigen Archiv im TensorFlow SavedModel-Format (oder im älteren Keras H5-Format). Dies ist die gängige Praxis.
  • Nur die Architektur/Konfiguration speichern, normalerweise als JSON-Datei.
  • Nur die Gewichtswerte speichern. Dies wird im Allgemeinen beim Trainieren des Modells verwendet.

Werfen wir einen Blick auf jede dieser Optionen. Wann würden Sie das eine oder das andere verwenden und wie funktionieren sie?

So speichern und laden Sie ein Modell

Wenn Sie nur 10 Sekunden Zeit haben, um dieses Handbuch zu lesen, müssen Sie Folgendes wissen.

Speichern eines Keras-Modells:

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

Laden des Modells zurück:

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

Schauen wir uns nun die Details an.

Aufstellen

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

Speichern und Laden des gesamten Modells

Sie können ein gesamtes Modell in einem einzigen Artefakt speichern. Es wird enthalten:

  • Die Architektur/Konfiguration des Modells
  • Die Gewichtswerte des Modells (die während des Trainings gelernt wurden)
  • Das Zusammenstellung Informationen des Modells (wenn die compile() aufgerufen wurde)
  • Der Optimierer und sein Status, falls vorhanden (dadurch können Sie das Training dort wieder aufnehmen, wo Sie aufgehört haben)

APIs

Es gibt zwei Formate , die Sie verwenden können , ein ganzes Modell auf der Festplatte zu speichern: das TensorFlow SavedModel Format, und das ältere Keras H5 - Format. Das empfohlene Format ist SavedModel. Es ist die Standardeinstellung , wenn Sie verwenden model.save() .

Sie können zum H5-Format wechseln, indem Sie:

  • Vorbei save_format='h5' zu save() .
  • Übergeben Sie einen Dateinamen , dass Enden in .h5 oder .keras zu save() .

SavedModel-Format

SavedModel ist das umfassendere Speicherformat, das die Modellarchitektur, Gewichtungen und die verfolgten Tensorflow-Untergraphen der Aufruffunktionen speichert. Dadurch kann Keras sowohl integrierte Ebenen als auch benutzerdefinierte Objekte wiederherstellen.

Beispiel:

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


model = get_model()

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

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

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

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

# The reconstructed model is already compiled and has retained the optimizer
# state, so training can resume:
reconstructed_model.fit(test_input, test_target)
4/4 [==============================] - 1s 2ms/step - loss: 0.5884
2021-08-25 17:49:05.320893: W tensorflow/python/util/util.cc:348] Sets are not currently considered sequences, but this may change in the future, so consider avoiding using them.
INFO:tensorflow:Assets written to: my_model/assets
4/4 [==============================] - 0s 2ms/step - loss: 0.5197
<keras.callbacks.History at 0x7f99486ad490>

Was das SavedModel enthält

Der Aufruf model.save('my_model') erstellt einen Ordner mit dem Namen my_model , mit folgendem Inhalt :

ls my_model
assets  keras_metadata.pb  saved_model.pb  variables

Die Modellarchitektur und Trainingskonfiguration (einschließlich der Optimierer, Verluste und Metriken) in gespeichert saved_model.pb . Die Gewichte werden in der gespeicherten variables/ Verzeichnis.

Ausführliche Informationen über das SavedModel Format finden Sie in die SavedModel Führung (Das SavedModel Format auf der Festplatte) .

Wie SavedModel benutzerdefinierte Objekte behandelt

Beim Speichern des Modells und seine Schichten, das SavedModel Format speichert die Klassennamen, Anruffunktion, Verluste und Gewichte (und die Config, wenn implementiert). Die Aufruffunktion definiert den Berechnungsgraphen des Modells/der Schicht.

In Abwesenheit der Modell-/Schichtkonfiguration wird die Aufruffunktion verwendet, um ein Modell zu erstellen, das wie das ursprüngliche Modell existiert, das trainiert, bewertet und für Inferenz verwendet werden kann.

Dennoch ist es immer eine gute Praxis , die definieren get_config und from_config Methoden , wenn ein benutzerdefiniertes Modell oder Schichtklasse zu schreiben. Auf diese Weise können Sie die Berechnung später bei Bedarf problemlos aktualisieren. Siehe den Abschnitt über Benutzerdefinierte Objekte für weitere Informationen.

Beispiel:

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

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

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

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


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

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

# Option 2: Load without the CustomModel class.

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

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

print("Original model:", model)
print("Model Loaded with custom objects:", loaded_1)
print("Model loaded without the custom object class:", loaded_2)
INFO:tensorflow:Assets written to: my_model/assets
WARNING:tensorflow:No training configuration found in save file, so the model was *not* compiled. Compile it manually.
WARNING:tensorflow:No training configuration found in save file, so the model was *not* compiled. Compile it manually.
Original model: <__main__.CustomModel object at 0x7f9949c86810>
Model Loaded with custom objects: <__main__.CustomModel object at 0x7f99681f61d0>
Model loaded without the custom object class: <keras.saving.saved_model.load.CustomModel object at 0x7f9aaceefd10>

Das erste geladene Modell wird die Config und geladen mit CustomModel Klasse. Das zweite Modell wird geladen, indem die Modellklasse dynamisch erstellt wird, die sich wie das Originalmodell verhält.

Das SavedModel konfigurieren

Neu in TensoFlow 2.4 Das Argument save_traces wurde hinzugefügt , um model.save , die Sie auf Toggle SavedModel Funktion Verfolgung ermöglicht. Die Funktionen werden gespeichert , die Keras zu re-load benutzerdefinierte Objekte ohne die ursprüngliche Klasse definitons zu ermöglichen, so dass , wenn save_traces=False , alle benutzerdefinierten Objekte haben müssen definiert get_config / from_config Methoden. Beim Laden müssen die benutzerdefinierten Objekte an die weitergegeben werden custom_objects Argument. save_traces=False reduziert den Platz Platte durch die SavedModel und spart Zeit verwendet.

Keras H5-Format

Keras unterstützt auch eine einzelne Datei zu speichern HDF5 des Modells Architektur, Gewichte Werte und enthalten compile() Informationen. Es ist eine leichte Alternative zu SavedModel.

Beispiel:

model = get_model()

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

# Calling `save('my_model.h5')` creates a h5 file `my_model.h5`.
model.save("my_h5_model.h5")

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

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

# The reconstructed model is already compiled and has retained the optimizer
# state, so training can resume:
reconstructed_model.fit(test_input, test_target)
4/4 [==============================] - 0s 1ms/step - loss: 1.6322
4/4 [==============================] - 0s 1ms/step - loss: 1.4790
<keras.callbacks.History at 0x7f9aacc0fd50>

Einschränkungen

Im Vergleich zum SavedModel-Format gibt es zwei Dinge, die nicht in die H5-Datei aufgenommen werden:

  • Externe Verluste und Messwerte über hinzugefügt model.add_loss() & model.add_metric() werden nicht gespeichert ( im Gegensatz zu SavedModel). Wenn Ihr Modell solche Verluste und Metriken aufweist und Sie das Training wieder aufnehmen möchten, müssen Sie diese Verluste nach dem Laden des Modells selbst wieder hinzufügen. Beachten Sie, dass dies gilt nicht für Verluste / Metriken innerhalb Schichten über erstellt self.add_loss() & self.add_metric() . Solange die Schicht geladen wird, werden diese Verluste & Metriken gehalten, da sie sind Teil der call - Methode der Schicht.
  • Die Berechnung grafische Darstellung von benutzerdefinierten Objekten wie benutzerdefinierte Schichten nicht in der gespeicherten Datei enthalten ist. Zur Ladezeit benötigt Keras Zugriff auf die Python-Klassen/-Funktionen dieser Objekte, um das Modell zu rekonstruieren. Siehe Benutzerdefinierte Objekte .

Die Architektur retten

Die Konfiguration (oder Architektur) des Modells gibt an, welche Schichten das Modell enthält und wie diese Schichten verbunden sind*. Wenn Sie über die Konfiguration eines Modells verfügen, kann das Modell mit einem frisch initialisierten Zustand für die Gewichte und ohne Zusammenstellungsinformationen erstellt werden.

*Beachten Sie, dass dies nur für Modelle gilt, die unter Verwendung der funktionalen oder sequentiellen API definiert sind, nicht untergeordnete Modelle.

Konfiguration eines sequentiellen Modells oder funktionalen API-Modells

Bei diesen Modelltypen handelt es sich um explizite Graphen von Schichten: Ihre Konfiguration liegt immer in strukturierter Form vor.

APIs

get_config() und from_config()

Aufruf config = model.get_config() wird eine Python dict zurückgeben , die die Konfiguration des Modells enthält. Das gleiche Modell kann dann über rekonstruiert werden Sequential.from_config(config) (für ein Sequential - Modell) oder Model.from_config(config) (für ein Functional API - Modell).

Der gleiche Workflow funktioniert auch für alle serialisierbaren Layer.

Layer-Beispiel:

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

Beispiel für ein sequentielles Modell:

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

Beispiel für ein Funktionsmodell:

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

Dies ist vergleichbar mit get_config / from_config , außer es das Modell in einen JSON - String verwandelt, die dann ohne die ursprüngliche Modell Klasse geladen werden. Es ist auch spezifisch für Modelle, es ist nicht für Ebenen gedacht.

Beispiel:

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

Benutzerdefinierte Objekte

Modelle und Ebenen

Die Architektur von Unterklassen - Modellen und Schichten werden in den Verfahren definiert __init__ und call . Sie sind Python - Bytecode in Betracht gezogen, die nicht in eine JSON-kompatible Konfiguration serialisiert werden kann - Sie könnten versuchen , den Bytecode Serialisierung (zB über pickle ), aber es ist völlig unsicher und bedeutet , dass Ihr Modell kann nicht auf einem anderen System geladen werden.

Um ein Modell mit individuell definierten Schichten oder ein subclassed Modell zu speichern / laden, sollten Sie die überschreiben get_config und optional from_config Methoden. Darüber hinaus sollten Sie das benutzerdefinierte Objekt registrieren, damit Keras es erkennt.

Benutzerdefinierte Funktionen

Benutzerdefinierte-Funktionen (zB Aktivierung Verlust oder Initialisierung) brauchen keine get_config Methode. Der Funktionsname ist zum Laden ausreichend, solange er als benutzerdefiniertes Objekt registriert ist.

Nur das TensorFlow-Diagramm laden

Es ist möglich, das von Keras generierte TensorFlow-Diagramm zu laden. Wenn Sie dies tun, werden Sie keine zur Verfügung stellen müssen custom_objects . Sie können dies so tun:

model.save("my_model")
tensorflow_graph = tf.saved_model.load("my_model")
x = np.random.uniform(size=(4, 32)).astype(np.float32)
predicted = tensorflow_graph(x).numpy()
WARNING:tensorflow:Compiled the loaded model, but the compiled metrics have yet to be built. `model.compile_metrics` will be empty until you train or evaluate the model.
INFO:tensorflow:Assets written to: my_model/assets

Beachten Sie, dass diese Methode mehrere Nachteile hat:

  • Aus Gründen der Rückverfolgbarkeit sollten Sie immer Zugriff auf die verwendeten benutzerdefinierten Objekte haben. Sie möchten kein Modell in Produktion nehmen, das Sie nicht neu erstellen können.
  • Die Aufgabe wird durch zurück tf.saved_model.load ist kein Keras Modell. Es ist also nicht so einfach zu bedienen. Zum Beispiel haben Sie keinen Zugriff haben .predict() oder .fit()

Auch wenn seine Verwendung abgeraten wird, kann es Ihnen helfen , wenn Sie in Zugzwang sind, zum Beispiel, wenn Sie den Code Ihrer benutzerdefinierten Objekte oder haben Probleme haben das Modell mit dem Laden tf.keras.models.load_model() .

Mehr erfahren Sie in der herausfinden Seite über tf.saved_model.load

Definieren der Konfigurationsmethoden

Spezifikationen:

  • get_config sollte einen JSON-serializable Wörterbuch zurück , um mit dem Keras kompatibel zu sein Architektur- und Modell sparende APIs.
  • from_config(config) ( classmethod ) sollte eine neue Schicht oder Modellobjekt zurück , die aus der Config erstellt wird. Die Standardimplementierung kehrt cls(**config) .

Beispiel:

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

Registrieren des benutzerdefinierten Objekts

Keras merkt sich, welche Klasse die Konfiguration generiert hat. Aus dem obigen Beispiel tf.keras.layers.serialize erzeugt eine serialisierte Form der benutzerdefinierten Schicht:

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

Keras hält eine Master - Liste aller Einbau-Schicht, Modell, Optimierer und metrische Klassen, die verwendet wird , um die richtige Klasse Anruf zu finden from_config . Wenn die Klasse nicht gefunden werden kann, dann wird ein Fehler ausgelöst ( Value Error: Unknown layer ). Es gibt mehrere Möglichkeiten, benutzerdefinierte Klassen in dieser Liste zu registrieren:

  1. Einstellen custom_objects Argument in der Ladefunktion. (siehe Beispiel im obigen Abschnitt "Definieren der config-Methoden")
  2. tf.keras.utils.custom_object_scope oder tf.keras.utils.CustomObjectScope
  3. tf.keras.utils.register_keras_serializable

Beispiel für benutzerdefinierte Layer und Funktionen

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)

Klonen von In-Memory-Modellen

Sie können auch im Speicher tun Klonen eines Modells über tf.keras.models.clone_model() . Dies entspricht dem Abrufen der Konfiguration und dem anschließenden erneuten Erstellen des Modells aus seiner Konfiguration (damit werden keine Kompilierungsinformationen oder Schichtgewichtungswerte beibehalten).

Beispiel:

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

Nur die Gewichtswerte des Modells speichern und laden

Sie können nur die Gewichte eines Modells speichern und laden. Dies kann nützlich sein, wenn:

  • Sie benötigen das Modell nur für die Inferenz: In diesem Fall müssen Sie das Training nicht neu starten, sodass Sie die Kompilierungsinformationen oder den Optimiererstatus nicht benötigen.
  • Sie führen Transfer-Lernen durch: In diesem Fall trainieren Sie ein neues Modell unter Wiederverwendung des Zustands eines früheren Modells, sodass Sie die Kompilierungsinformationen des früheren Modells nicht benötigen.

APIs für die In-Memory-Gewichtsübertragung

Gewichte können zwischen verschiedenen Objekten unter Verwendung kopiert werden get_weights und set_weights :

Beispiele unten.

Übertragen von Gewichten von einer Schicht auf eine andere, im Speicher

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

Übertragen von Gewichten von einem Modell auf ein anderes Modell mit einer kompatiblen Architektur im Speicher

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

Der Fall zustandsloser Schichten

Da zustandslose Schichten die Reihenfolge oder Anzahl der Gewichtungen nicht ändern, können Modelle kompatible Architekturen haben, selbst wenn zusätzliche/fehlende zustandslose Schichten vorhanden sind.

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

APIs zum Speichern von Gewichten auf der Festplatte und zum Zurückladen

Gewichte können durch den Aufruf auf der Festplatte gespeichert werden model.save_weights in den folgenden Formaten:

  • TensorFlow-Checkpoint
  • HDF5

Das Standardformat für model.save_weights ist TensorFlow Checkpoint. Es gibt zwei Möglichkeiten, das Speicherformat anzugeben:

  1. save_format Argument: Setzen Sie den Wert auf save_format="tf" oder save_format="h5" .
  2. path Wenn der Weg endet mit .h5 oder .hdf5 , dann wird das HDF5 - Format verwendet. Andere Suffixe werden in einem TensorFlow Kontrollpunkt führen , es sei denn save_format gesetzt.

Es besteht auch die Möglichkeit, Gewichtungen als numpy-Arrays im Arbeitsspeicher abzurufen. Jede API hat ihre Vor- und Nachteile, die im Folgenden beschrieben werden.

TF-Checkpoint-Format

Beispiel:

# Runnable example
sequential_model = keras.Sequential(
    [
        keras.Input(shape=(784,), name="digits"),
        keras.layers.Dense(64, activation="relu", name="dense_1"),
        keras.layers.Dense(64, activation="relu", name="dense_2"),
        keras.layers.Dense(10, name="predictions"),
    ]
)
sequential_model.save_weights("ckpt")
load_status = sequential_model.load_weights("ckpt")

# `assert_consumed` can be used as validation that all variable values have been
# restored from the checkpoint. See `tf.train.Checkpoint.restore` for other
# methods in the Status object.
load_status.assert_consumed()
<tensorflow.python.training.tracking.util.CheckpointLoadStatus at 0x7f9aaca4ced0>

Formatdetails

Das TensorFlow-Checkpoint-Format speichert und stellt die Gewichtungen unter Verwendung von Objektattributnamen wieder her. Betrachten wir zum Beispiel die tf.keras.layers.Dense Schicht. Die Schicht enthält zwei Gewichte: dense.kernel und dense.bias . Wenn die Schicht auf das gespeichert tf Format enthält der resultierende checkpoint der Tasten "kernel" und "bias" und deren entsprechenden Gewichtswerte. Weitere Informationen finden Sie unter „Einlegen von Mechanik“ in der TF - Checkpoint Führung .

Beachten Sie, dass Attribut / Graphkante genannt wird , nachdem der Name in Eltern - Objekt verwendet wird , nicht an den Namen der Variablen. Betrachten Sie die CustomLayer im Beispiel unten. Die Variable CustomLayer.var wird mit gespeicherten "var" als Teil des Schlüssels, nicht "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}

Beispiel für Transferlernen

Solange zwei Modelle dieselbe Architektur haben, können sie sich im Wesentlichen denselben Prüfpunkt teilen.

Beispiel:

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

# Extract a portion of the functional model defined in the Setup section.
# The following lines produce a new model that excludes the final output
# layer of the functional model.
pretrained = keras.Model(
    functional_model.inputs, functional_model.layers[-1].input, name="pretrained_model"
)
# Randomly assign "trained" weights.
for w in pretrained.weights:
    w.assign(tf.random.normal(w.shape))
pretrained.save_weights("pretrained_ckpt")
pretrained.summary()

# Assume this is a separate program where only 'pretrained_ckpt' exists.
# Create a new functional model with a different output dimension.
inputs = keras.Input(shape=(784,), name="digits")
x = keras.layers.Dense(64, activation="relu", name="dense_1")(inputs)
x = keras.layers.Dense(64, activation="relu", name="dense_2")(x)
outputs = keras.layers.Dense(5, name="predictions")(x)
model = keras.Model(inputs=inputs, outputs=outputs, name="new_model")

# Load the weights from pretrained_ckpt into model.
model.load_weights("pretrained_ckpt")

# Check that all of the pretrained weights have been loaded.
for a, b in zip(pretrained.weights, model.weights):
    np.testing.assert_allclose(a.numpy(), b.numpy())

print("\n", "-" * 50)
model.summary()

# Example 2: Sequential model
# Recreate the pretrained model, and load the saved weights.
inputs = keras.Input(shape=(784,), name="digits")
x = keras.layers.Dense(64, activation="relu", name="dense_1")(inputs)
x = keras.layers.Dense(64, activation="relu", name="dense_2")(x)
pretrained_model = keras.Model(inputs=inputs, outputs=x, name="pretrained")

# Sequential example:
model = keras.Sequential([pretrained_model, keras.layers.Dense(5, name="predictions")])
model.summary()

pretrained_model.load_weights("pretrained_ckpt")

# Warning! Calling `model.load_weights('pretrained_ckpt')` won't throw an error,
# but will *not* work as expected. If you inspect the weights, you'll see that
# none of the weights will have loaded. `pretrained_model.load_weights()` is the
# correct method to call.
Model: "pretrained_model"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
digits (InputLayer)          [(None, 784)]             0         
_________________________________________________________________
dense_1 (Dense)              (None, 64)                50240     
_________________________________________________________________
dense_2 (Dense)              (None, 64)                4160      
=================================================================
Total params: 54,400
Trainable params: 54,400
Non-trainable params: 0
_________________________________________________________________

 --------------------------------------------------
Model: "new_model"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
digits (InputLayer)          [(None, 784)]             0         
_________________________________________________________________
dense_1 (Dense)              (None, 64)                50240     
_________________________________________________________________
dense_2 (Dense)              (None, 64)                4160      
_________________________________________________________________
predictions (Dense)          (None, 5)                 325       
=================================================================
Total params: 54,725
Trainable params: 54,725
Non-trainable params: 0
_________________________________________________________________
Model: "sequential_3"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
pretrained (Functional)      (None, 64)                54400     
_________________________________________________________________
predictions (Dense)          (None, 5)                 325       
=================================================================
Total params: 54,725
Trainable params: 54,725
Non-trainable params: 0
_________________________________________________________________
<tensorflow.python.training.tracking.util.CheckpointLoadStatus at 0x7f9aaca76990>

Es wird im Allgemeinen empfohlen, beim Erstellen von Modellen bei der gleichen API zu bleiben. Wenn Sie zwischen sequentiell und funktional oder funktional und untergeordnet usw. wechseln, erstellen Sie immer das vortrainierte Modell neu und laden Sie die vortrainierten Gewichtungen in dieses Modell.

Die nächste Frage ist, wie können Gewichte gespeichert und in verschiedene Modelle geladen werden, wenn die Modellarchitekturen sehr unterschiedlich sind? Die Lösung ist die Verwendung tf.train.Checkpoint die genauen Lagen / Variablen zu speichern und wiederherzustellen.

Beispiel:

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

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

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


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

# Create a Checkpoint with the same structure as before, and load the weights.
tf.train.Checkpoint(
    dense=model.first_dense, kernel=model.kernel, bias=model.bias
).restore(ckpt_path).assert_consumed()
/tmpfs/src/tf_docs_env/lib/python3.7/site-packages/keras/engine/base_layer.py:2223: UserWarning: `layer.add_variable` is deprecated and will be removed in a future version. Please use `layer.add_weight` method instead.
  warnings.warn('`layer.add_variable` is deprecated and '
<tensorflow.python.training.tracking.util.CheckpointLoadStatus at 0x7f9aaca6f390>

HDF5-Format

Das HDF5-Format enthält Gewichtungen, die nach Layernamen gruppiert sind. Die Gewichte sind durch Verketten von Listen der Liste der trainierbare Gewichte auf die Liste der nicht-trainierbar Gewichte ( die gleichen wie geordnete layer.weights ). Somit kann ein Modell einen hdf5-Checkpoint verwenden, wenn es die gleichen Layer und trainierbaren Status hat wie im Checkpoint gespeichert.

Beispiel:

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

Beachten Sie, dass das Ändern layer.trainable in einer anderen führen kann layer.weights Ordnung , wenn das Modell verschachtelte Ebene enthält.

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

Beispiel für Transferlernen

Beim Laden von vortrainierten Gewichten aus HDF5 wird empfohlen, die Gewichte in das ursprüngliche Checkpoint-Modell zu laden und dann die gewünschten Gewichte/Layer in ein neues Modell zu extrahieren.

Beispiel:

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
_________________________________________________________________