Salva e carica i modelli Keras

Visualizza su TensorFlow.org Esegui in Google Colab Visualizza la fonte su GitHub Scarica il taccuino

introduzione

Un modello Keras è costituito da più componenti:

  • L'architettura, o configurazione, che specifica quali livelli contiene il modello e come sono connessi.
  • Un insieme di valori di pesi (lo "stato del modello").
  • Un ottimizzatore (definito compilando il modello).
  • Una serie di perdite e metriche (definito dalla compilazione del modello o chiamando add_loss() o add_metric() ).

L'API Keras consente di salvare tutti questi pezzi su disco contemporaneamente o di salvarne solo alcuni in modo selettivo:

  • Salvare tutto in un unico archivio nel formato TensorFlow SavedModel (o nel vecchio formato Keras H5). Questa è la pratica standard.
  • Salvataggio solo dell'architettura/configurazione, in genere come file JSON.
  • Salvare solo i valori dei pesi. Viene generalmente utilizzato durante l'addestramento del modello.

Diamo un'occhiata a ciascuna di queste opzioni. Quando useresti l'uno o l'altro e come funzionano?

Come salvare e caricare un modello

Se hai solo 10 secondi per leggere questa guida, ecco cosa devi sapere.

Salvataggio di un modello Keras:

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

Ricaricare il modello:

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

Ora, diamo un'occhiata ai dettagli.

Impostare

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

Salvataggio e caricamento dell'intero modello

Puoi salvare un intero modello in un singolo artefatto. Comprenderà:

  • Architettura/configurazione del modello
  • I valori di peso del modello (appresi durante l'allenamento)
  • Informazioni compilazione del modello (se compile() è stato chiamato)
  • L'ottimizzatore e il suo stato, se presente (questo ti consente di riavviare l'allenamento da dove avevi lasciato)

API

Ci sono due formati è possibile utilizzare per salvare un intero modello su disco: il formato tensorflow SavedModel, e il più vecchio formato di Keras H5. Il formato consigliato è SavedModel. E 'il valore di default quando si utilizza model.save() .

Puoi passare al formato H5:

  • Passando save_format='h5' a save() .
  • Il passaggio di un nome che termina in .h5 o .keras a save() .

Formato modello salvato

SavedModel è il formato di salvataggio più completo che salva l'architettura del modello, i pesi e i sottografi Tensorflow tracciati delle funzioni di chiamata. Ciò consente a Keras di ripristinare sia i livelli incorporati che gli oggetti personalizzati.

Esempio:

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>

Cosa contiene il SavedModel

Calling model.save('my_model') crea una cartella denominata my_model , contenente le seguenti informazioni:

ls my_model
assets  keras_metadata.pb  saved_model.pb  variables

Il modello di architettura, e la configurazione di formazione (tra cui l'ottimizzatore, perdite, e metriche) sono memorizzati in saved_model.pb . I pesi vengono salvati nella variables/ directory.

Per informazioni dettagliate sul formato SavedModel, vedere la guida SavedModel (Il formato SavedModel su disco) .

Come SavedModel gestisce gli oggetti personalizzati

Quando si salva il modello e suoi strati, i negozi formato SavedModel il nome della classe, la funzione di chiamata, le perdite, e pesi (e la configurazione, se attuate). La funzione di chiamata definisce il grafico di calcolo del modello/livello.

In assenza della configurazione del modello/livello, la funzione di chiamata viene utilizzata per creare un modello esistente come il modello originale che può essere addestrato, valutato e utilizzato per l'inferenza.

Tuttavia, è sempre una buona pratica per definire il get_config e from_config metodi durante la scrittura di un modello personalizzato o classe di livello. Ciò consente di aggiornare facilmente il calcolo in un secondo momento, se necessario. Vedere la sezione relativa oggetti personalizzati per ulteriori informazioni.

Esempio:

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>

Il primo modello caricato viene caricata utilizzando il config e CustomModel di classe. Il secondo modello viene caricato creando dinamicamente la classe del modello che si comporta come il modello originale.

Configurazione del modello salvato

Nuovo in TensoFlow 2.4 L'argomento save_traces è stato aggiunto alla model.save , che consente di attivare o disattivare la funzione SavedModel tracing. Le funzioni vengono salvati per consentire al Keras di oggetti personalizzati ricaricare senza le definitons classe originale, in modo che quando save_traces=False , tutti gli oggetti personalizzati devono essere definiti get_config / from_config metodi. Quando si carica, gli oggetti personalizzati devono essere passati al custom_objects argomento. save_traces=False riduce lo spazio su disco utilizzato dal SavedModel e risparmio di tempo.

Formato Keras H5

Keras supporta anche il salvataggio di un singolo file HDF5 contenente l'architettura del modello, i valori pesi, e compile() le informazioni. È un'alternativa leggera a SavedModel.

Esempio:

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>

Limitazioni

Rispetto al formato SavedModel, ci sono due cose che non vengono incluse nel file H5:

  • Perdite e metriche esterne aggiunti tramite model.add_loss() e model.add_metric() non vengono salvate (a differenza SavedModel). Se si dispone di tali perdite e metriche sul modello e si desidera riprendere l'addestramento, è necessario aggiungere nuovamente queste perdite dopo aver caricato il modello. Si noti che questo non si applica alle perdite / metriche create all'interno strati via self.add_loss() e self.add_metric() . Fintanto che lo strato viene caricato, queste perdite e metriche vengono mantenuti, in quanto fanno parte della call metodo dello strato.
  • Il grafico calcolo di oggetti personalizzati come ad esempio livelli personalizzati non sono inclusi nel file salvato. Al momento del caricamento, Keras avrà bisogno dell'accesso alle classi/funzioni Python di questi oggetti per ricostruire il modello. Vedere gli oggetti personalizzati .

Salvare l'architettura

La configurazione (o architettura) del modello specifica quali livelli contiene il modello e come questi livelli sono collegati*. Se si dispone della configurazione di un modello, è possibile creare il modello con uno stato appena inizializzato per i pesi e nessuna informazione di compilazione.

*Nota che questo si applica solo ai modelli definiti utilizzando l'API funzionale o sequenziale non modelli sottoclasse.

Configurazione di un modello sequenziale o modello API funzionale

Questi tipi di modelli sono grafici espliciti di livelli: la loro configurazione è sempre disponibile in forma strutturata.

API

get_config() e from_config()

Richiamo config = model.get_config() restituirà un dict Python contenente la configurazione del modello. Lo stesso modello può essere ricostruito tramite Sequential.from_config(config) (per un Sequential modello) o Model.from_config(config) (per un modello funzionale API).

Lo stesso flusso di lavoro funziona anche per qualsiasi livello serializzabile.

Esempio di livello:

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

Esempio di modello sequenziale:

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

Esempio di modello funzionale:

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

Questo è simile al get_config / from_config , tranne che trasforma il modello in una stringa JSON, che possono poi essere caricato senza la classe del modello originale. È anche specifico per i modelli, non è pensato per i livelli.

Esempio:

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

Oggetti personalizzati

Modelli e strati

L'architettura di modelli sottoclasse e strati sono definiti nei metodi __init__ e call . Essi sono considerati bytecode Python, che non può essere serializzato in una configurazione compatibile con JSON - si potrebbe provare serializzare il bytecode (ad esempio via pickle ), ma è del tutto pericoloso e significa che il vostro modello non può essere caricato su un sistema diverso.

Al fine di salvare / caricare un modello con livelli personalizzati definiti, o di un modello sottoclasse, è necessario sovrascrivere il get_config e opzionalmente from_config metodi. Inoltre, dovresti usare la registrazione dell'oggetto personalizzato in modo che Keras ne sia consapevole.

Funzioni personalizzate

Funzioni custom-definiti (ad esempio perdita di attivazione o di inizializzazione) non necessitano di get_config metodo. Il nome della funzione è sufficiente per il caricamento purché sia ​​registrato come oggetto personalizzato.

Caricamento solo del grafico TensorFlow

E' possibile caricare il grafico TensorFlow generato dal Keras. In tal caso, non sarà necessario fornire alcun custom_objects . Puoi farlo in questo modo:

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

Tieni presente che questo metodo presenta diversi inconvenienti:

  • Per motivi di tracciabilità, dovresti sempre avere accesso agli oggetti personalizzati che sono stati utilizzati. Non vorresti mettere in produzione un modello che non puoi ricreare.
  • L'oggetto restituito da tf.saved_model.load non è un modello Keras. Quindi non è così facile da usare. Ad esempio, non si avrà accesso a .predict() o .fit()

Anche se il suo uso è sconsigliato, può aiutare se sei in un punto stretto, per esempio, se hai perso il codice dei vostri oggetti personalizzati o hanno problemi caricamento del modello con tf.keras.models.load_model() .

Potete saperne di più nella pagina su tf.saved_model.load

Definizione dei metodi di configurazione

Specifiche:

  • get_config dovrebbe restituire un dizionario JSON-serializzabile in modo da essere compatibile con il Keras architettura- e le API modello di risparmio.
  • from_config(config) ( classmethod ) dovrebbe restituire un nuovo oggetto livello o modello che viene creato dalla configurazione. I rendimenti implementazione predefinita cls(**config) .

Esempio:

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

Registrazione dell'oggetto personalizzato

Keras tiene nota di quale classe ha generato la configurazione. Dall'esempio precedente, tf.keras.layers.serialize genera una forma serializzata dello strato personalizzato:

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

Keras mantiene un elenco principale di tutti i built-in strati, il modello, ottimizzatore, e le classi metriche, che viene utilizzato per trovare la classe giusta alla chiamata from_config . Se la classe non può essere trovato, quindi viene generato un errore ( Value Error: Unknown layer ). Esistono alcuni modi per registrare le classi personalizzate in questo elenco:

  1. Impostazione custom_objects argomento della funzione di caricamento. (vedi l'esempio nella sezione precedente "Definizione dei metodi di configurazione")
  2. tf.keras.utils.custom_object_scope o tf.keras.utils.CustomObjectScope
  3. tf.keras.utils.register_keras_serializable

Esempio di layer e funzioni personalizzati

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)

Clonazione del modello in memoria

È anche possibile fare la clonazione in memoria di un modello tramite tf.keras.models.clone_model() . Ciò equivale a ottenere la configurazione e poi a ricreare il modello dalla sua configurazione (quindi non conserva le informazioni di compilazione o i valori dei pesi dei livelli).

Esempio:

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

Salvare e caricare solo i valori dei pesi del modello

Puoi scegliere di salvare e caricare solo i pesi di un modello. Questo può essere utile se:

  • È necessario solo il modello per l'inferenza: in questo caso non sarà necessario riavviare l'addestramento, quindi non sono necessarie le informazioni di compilazione o lo stato dell'ottimizzatore.
  • Stai eseguendo l'apprendimento del trasferimento: in questo caso addestrerai un nuovo modello riutilizzando lo stato di un modello precedente, quindi non hai bisogno delle informazioni di compilazione del modello precedente.

API per il trasferimento del peso in memoria

I pesi possono essere copiate tra diversi oggetti utilizzando get_weights e set_weights :

Esempi di seguito.

Trasferimento di pesi da uno strato all'altro, in memoria

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

Trasferire pesi da un modello ad un altro modello con architettura compatibile, in memoria

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

Il caso degli strati apolidi

Poiché i livelli senza stato non modificano l'ordine o il numero di pesi, i modelli possono avere architetture compatibili anche se sono presenti livelli senza stato aggiuntivi/mancanti.

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 per salvare i pesi su disco e ricaricarli

I pesi possono essere salvati su disco chiamando model.save_weights nei seguenti formati:

  • Checkpoint TensorFlow
  • HDF5

Il formato predefinito per model.save_weights è tensorflow checkpoint. Esistono due modi per specificare il formato di salvataggio:

  1. save_format argomento: Impostare il valore di save_format="tf" o save_format="h5" .
  2. path argomento: Se le estremità del percorso con .h5 o .hdf5 , allora il formato HDF5 viene utilizzato. Altri suffissi si tradurrà in un posto di blocco a meno che tensorflow save_format è impostato.

C'è anche un'opzione per recuperare i pesi come array numpy in memoria. Ogni API ha i suoi pro e contro che sono dettagliati di seguito.

Formato checkpoint TF

Esempio:

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

Dettagli formato

Il formato TensorFlow Checkpoint salva e ripristina i pesi utilizzando i nomi degli attributi degli oggetti. Ad esempio, si consideri il tf.keras.layers.Dense strato. Lo strato contiene due pesi: dense.kernel e dense.bias . Quando lo strato viene salvato nella tf formato, il punto di controllo risultante contiene i tasti "kernel" e "bias" ei corrispondenti valori di peso. Per ulteriori informazioni, vedere "Caricamento meccanica" nella guida TF Checkpoint .

Si noti che il bordo attributo / grafico viene chiamato dopo il nome usato in oggetto principale, non il nome della variabile. Si consideri il CustomLayer nell'esempio qui sotto. La variabile CustomLayer.var viene salvato con "var" come parte della chiave, non "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}

Esempio di apprendimento del trasferimento

In sostanza, finché due modelli hanno la stessa architettura, sono in grado di condividere lo stesso checkpoint.

Esempio:

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>

In genere si consiglia di attenersi alla stessa API per la creazione di modelli. Se si passa da Sequenziale a Funzionale, o Funzionale e sottoclasse, ecc., ricostruire sempre il modello pre-addestrato e caricare i pesi pre-addestrati su quel modello.

La domanda successiva è: come possono essere salvati e caricati i pesi su modelli diversi se le architetture del modello sono molto diverse? La soluzione è quella di utilizzare tf.train.Checkpoint per salvare e ripristinare gli esatti strati / variabili.

Esempio:

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

Formato HDF5

Il formato HDF5 contiene pesi raggruppati per nomi di livelli. I pesi sono elenchi ordinati concatenando l'elenco dei pesi addestrabili alla lista dei pesi non addestrabili (stessi come layer.weights ). Pertanto, un modello può utilizzare un checkpoint hdf5 se ha gli stessi livelli e stati addestrabili salvati nel checkpoint.

Esempio:

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

Nota che cambiando layer.trainable può comportare una diversa layer.weights ordine quando il modello contiene livelli nidificati.

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

Esempio di apprendimento del trasferimento

Quando si caricano pesi preaddestrati da HDF5, si consiglia di caricare i pesi nel modello checkpoint originale, quindi estrarre i pesi/livelli desiderati in un nuovo modello.

Esempio:

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
_________________________________________________________________