![]() | ![]() | ![]() | ![]() |
Introdução
Um modelo Keras consiste em vários componentes:
- A arquitetura, ou configuração, que especifica quais camadas o modelo contém e como elas estão conectadas.
- Um conjunto de valores de pesos (o "estado do modelo").
- Um otimizador (definido pela compilação do modelo).
- Um conjunto de perdas e métricas (definido pela compilação do modelo ou chamando
add_loss()
ouadd_metric()
).
A API Keras permite salvar todas essas partes em disco de uma só vez ou salvar apenas algumas delas seletivamente:
- Salvar tudo em um único arquivo no formato TensorFlow SavedModel (ou no formato Keras H5 mais antigo). Esta é a prática padrão.
- Salvar apenas a arquitetura/configuração, normalmente como um arquivo JSON.
- Salvando apenas os valores dos pesos. Isso geralmente é usado ao treinar o modelo.
Vamos dar uma olhada em cada uma dessas opções. Quando você usaria um ou outro, e como eles funcionam?
Como salvar e carregar um modelo
Se você tiver apenas 10 segundos para ler este guia, aqui está o que você precisa saber.
Salvando um modelo Keras:
model = ... # Get model (Sequential, Functional Model, or Model subclass)
model.save('path/to/location')
Carregando o modelo de volta:
from tensorflow import keras
model = keras.models.load_model('path/to/location')
Agora, vamos ver os detalhes.
Configurar
import numpy as np
import tensorflow as tf
from tensorflow import keras
Salvamento e carregamento de todo o modelo
Você pode salvar um modelo inteiro em um único artefato. Incluirá:
- A arquitetura/configuração do modelo
- Os valores de peso do modelo (que foram aprendidos durante o treinamento)
- Informações de compilação do modelo (se
compile()
foi chamado) - O otimizador e seu estado, se houver (isso permite que você reinicie o treinamento de onde parou)
API
-
model.save()
outf.keras.models.save_model()
-
tf.keras.models.load_model()
Existem dois formatos que você pode usar para salvar um modelo inteiro para o disco: o formato TensorFlow SavedModel, eo formato Keras H5 mais velho. O formato recomendado é SavedModel. É o padrão quando você usa model.save()
.
Você pode alternar para o formato H5:
- Passando
save_format='h5'
parasave()
. - Passando um nome de arquivo que termina em
.h5
ou.keras
parasave()
.
Formato SavedModel
SavedModel é o formato de salvamento mais abrangente que salva a arquitetura do modelo, os pesos e os subgráficos do Tensorflow rastreados das funções de chamada. Isso permite que o Keras restaure tanto as camadas internas quanto os objetos personalizados.
Exemplo:
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>
O que o SavedModel contém
Chamando model.save('my_model')
cria uma pasta chamada my_model
, contendo o seguinte:
ls my_model
assets keras_metadata.pb saved_model.pb variables
A arquitetura do modelo e configuração de formação (incluindo o otimizador, perdas e métricas) são armazenados em saved_model.pb
. Os pesos são salvos no variables/
diretório.
Para obter informações detalhadas sobre o formato SavedModel, consulte o guia SavedModel (O formato SavedModel no disco) .
Como SavedModel lida com objetos personalizados
Ao salvar o modelo e suas camadas, o formato lojas SavedModel o nome da classe, função de chamada, perdas e pesos (ea configuração, se implementado). A função de chamada define o gráfico de computação do modelo/camada.
Na ausência da configuração do modelo/camada, a função de chamada é usada para criar um modelo que existe como o modelo original que pode ser treinado, avaliado e usado para inferência.
No entanto, é sempre uma boa prática para definir a get_config
e from_config
métodos ao escrever um modelo personalizado ou classe camada. Isso permite que você atualize facilmente o cálculo posteriormente, se necessário. Veja a seção sobre objetos personalizados para mais informações.
Exemplo:
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>
O primeiro modelo carregado é carregado usando a configuração e CustomModel
classe. O segundo modelo é carregado criando dinamicamente a classe de modelo que age como o modelo original.
Configurando o SavedModel
Novo em TensoFlow 2.4 O argumento save_traces
foi adicionado ao model.save
, que lhe permite alternar função SavedModel traçado. Funções são salvos para permitir que o Keras para objetos personalizados re-carga, sem a definição dada classe original, então quando save_traces=False
, todos os objetos personalizados deve ter definido get_config
/ from_config
métodos. Ao carregar, os objetos personalizados deve ser passado para o custom_objects
argumento. save_traces=False
reduz o espaço em disco usado pelo SavedModel e economizando tempo.
Formato Keras H5
Keras também suporta a gravação de um único arquivo HDF5 contendo a arquitetura do modelo, os valores de pesos, e compile()
informações. É uma alternativa leve ao SavedModel.
Exemplo:
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>
Limitações
Comparado com o formato SavedModel, há duas coisas que não são incluídas no arquivo H5:
- Perdas externas e métricas adicionadas via
model.add_loss()
&model.add_metric()
não são salvos (ao contrário SavedModel). Se você tiver essas perdas e métricas em seu modelo e quiser retomar o treinamento, precisará adicionar essas perdas depois de carregar o modelo. Note que isto não se aplica a perdas / métricas criadas dentro de camadas viaself.add_loss()
&self.add_metric()
. Enquanto a camada é carregado, essas perdas e métricas são mantidos, uma vez que são parte dacall
método da camada. - O gráfico cálculo de objetos personalizados, tais como camadas personalizadas não está incluído no arquivo salvo. No momento do carregamento, Keras precisará acessar as classes/funções Python desses objetos para reconstruir o modelo. Ver objetos personalizados .
Salvando a arquitetura
A configuração (ou arquitetura) do modelo especifica quais camadas o modelo contém e como essas camadas são conectadas*. Se você tiver a configuração de um modelo, o modelo poderá ser criado com um estado recém-inicializado para os pesos e sem informações de compilação.
*Observe que isso se aplica apenas a modelos definidos usando os modelos funcionais ou Sequential apis não subclassificados.
Configuração de um modelo sequencial ou modelo de API funcional
Esses tipos de modelos são gráficos explícitos de camadas: sua configuração está sempre disponível de forma estruturada.
API
-
get_config()
efrom_config()
-
tf.keras.models.model_to_json()
etf.keras.models.model_from_json()
get_config()
e from_config()
Chamar config = model.get_config()
retornará um dicionário Python contendo a configuração do modelo. O mesmo modelo pode então ser reconstruída via Sequential.from_config(config)
(para uma Sequential
modelo) ou Model.from_config(config)
(para um modelo API Funcional).
O mesmo fluxo de trabalho também funciona para qualquer camada serializável.
Exemplo de camada:
layer = keras.layers.Dense(3, activation="relu")
layer_config = layer.get_config()
new_layer = keras.layers.Dense.from_config(layer_config)
Exemplo de modelo sequencial:
model = keras.Sequential([keras.Input((32,)), keras.layers.Dense(1)])
config = model.get_config()
new_model = keras.Sequential.from_config(config)
Exemplo de modelo funcional:
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()
Isto é semelhante ao get_config
/ from_config
, exceto que transforma o modelo em uma string JSON, que pode então ser carregado sem a classe modelo original. Também é específico para modelos, não é para camadas.
Exemplo:
model = keras.Sequential([keras.Input((32,)), keras.layers.Dense(1)])
json_config = model.to_json()
new_model = keras.models.model_from_json(json_config)
Objetos personalizados
Modelos e camadas
A arquitectura de modelos e camadas subclasse são definidos nos métodos __init__
e call
. Eles são considerados Python bytecode, que não pode ser serializado em uma configuração JSON-compatíveis - você poderia tentar serializar o bytecode (por exemplo, através pickle
), mas é completamente inseguro e significa que o seu modelo não pode ser carregado em um sistema diferente.
A fim de salvar / carregar um modelo com camadas definido por medida, ou um modelo de uma subclasse, você deve substituir o get_config
e, opcionalmente, from_config
métodos. Além disso, você deve usar registrar o objeto personalizado para que o Keras esteja ciente disso.
Funções personalizadas
Funções costume-definidos (por exemplo, perda de activação ou de inicialização) não precisa de um get_config
método. O nome da função é suficiente para carregar desde que esteja registrado como um objeto personalizado.
Carregando apenas o gráfico do TensorFlow
É possível carregar o gráfico do TensorFlow gerado pelo Keras. Se você fizer isso, você não precisará fornecer quaisquer custom_objects
. Você pode fazer assim:
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
Observe que esse método tem várias desvantagens:
- Por motivos de rastreabilidade, você deve sempre ter acesso aos objetos personalizados que foram usados. Você não gostaria de colocar em produção um modelo que não pode ser recriado.
- O objeto retornado por
tf.saved_model.load
não é um modelo Keras. Portanto, não é tão fácil de usar. Por exemplo, você não terá acesso a.predict()
ou.fit()
Mesmo se o seu uso é desencorajado, ele pode ajudá-lo se você estiver em um local apertado, por exemplo, se você perdeu o código de seus objetos personalizados ou ter problemas ao carregar o modelo com tf.keras.models.load_model()
.
Você pode saber mais na página sobre tf.saved_model.load
Definindo os métodos de configuração
Especificações:
-
get_config
deve retornar um dicionário JSON-serializável, a fim de ser compatível com o Keras Architecture- e APIs de economia de modelo. -
from_config(config)
(classmethod
) deve retornar uma nova camada ou modelo de objecto que é criado a partir da configuração. A implementação padrão retornacls(**config)
.
Exemplo:
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}
)
Registrando o objeto personalizado
Keras mantém uma nota de qual classe gerou a configuração. A partir do exemplo acima, tf.keras.layers.serialize
gera uma forma em série da camada de costume:
{'class_name': 'CustomLayer', 'config': {'a': 2} }
Keras mantém uma lista mestra de todos os alto-camada, modelo, otimizador, e aulas de métricas, que é usado para encontrar a classe correta de chamada from_config
. Se a classe não pode ser encontrado, em seguida, será gerado um erro ( Value Error: Unknown layer
). Existem algumas maneiras de registrar classes personalizadas nesta lista:
- Definir
custom_objects
argumento na função de carregamento. (veja o exemplo na seção acima "Definindo os métodos de configuração") -
tf.keras.utils.custom_object_scope
outf.keras.utils.CustomObjectScope
-
tf.keras.utils.register_keras_serializable
Exemplo de camada e função personalizada
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)
Clonagem de modelo na memória
Você também pode fazer a clonagem em memória de um modelo via tf.keras.models.clone_model()
. Isso é equivalente a obter a configuração e, em seguida, recriar o modelo de sua configuração (para que não preserve informações de compilação ou valores de pesos de camada).
Exemplo:
with keras.utils.custom_object_scope(custom_objects):
new_model = keras.models.clone_model(model)
Salvando e carregando apenas os valores de pesos do modelo
Você pode optar por salvar e carregar apenas os pesos de um modelo. Isso pode ser útil se:
- Você só precisa do modelo para inferência: neste caso, você não precisará reiniciar o treinamento, portanto, não precisará das informações de compilação ou do estado do otimizador.
- Você está fazendo o aprendizado de transferência: neste caso, você estará treinando um novo modelo reutilizando o estado de um modelo anterior, portanto, não precisa das informações de compilação do modelo anterior.
APIs para transferência de peso na memória
Pesos podem ser copiados entre diferentes objetos usando get_weights
e set_weights
:
-
tf.keras.layers.Layer.get_weights()
: Retorna uma lista de arrays numpy. -
tf.keras.layers.Layer.set_weights()
: Define os pesos modelo para os valores naweights
argumento.
Exemplos abaixo.
Transferindo pesos de uma camada para outra, na memória
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())
Transferindo pesos de um modelo para outro modelo com arquitetura compatível, na memória
# 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())
O caso das camadas sem estado
Como as camadas sem estado não alteram a ordem ou o número de pesos, os modelos podem ter arquiteturas compatíveis mesmo se houver camadas sem estado extras/ausentes.
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 para salvar pesos em disco e carregá-los de volta
Pesos podem ser salvos no disco chamando model.save_weights
nos seguintes formatos:
- Ponto de verificação do TensorFlow
- HDF5
O formato padrão para model.save_weights
é TensorFlow checkpoint. Existem duas maneiras de especificar o formato de salvamento:
-
save_format
argumento: Definir o valor parasave_format="tf"
ousave_format="h5"
. -
path
argumento: Se as extremidades caminho com.h5
ou.hdf5
, então o formato HDF5 é usado. Outros sufixos irá resultar em um posto de controle TensorFlow menossave_format
está definido.
Há também uma opção de recuperar pesos como matrizes numpy na memória. Cada API tem seus prós e contras que são detalhados abaixo.
Formato de ponto de verificação TF
Exemplo:
# 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>
Detalhes do formato
O formato TensorFlow Checkpoint salva e restaura os pesos usando nomes de atributos de objetos. Por exemplo, considere o tf.keras.layers.Dense
camada. A camada contém dois pesos: dense.kernel
e dense.bias
. Quando a camada é guardado para o tf
formato, o ponto de verificação resultante contém as teclas de "kernel"
e "bias"
e os seus valores correspondentes de peso. Para mais informações, consulte "Carregamento de mecânica" no guia TF Checkpoint .
Note-se que borda atributo / gráfico é denominado a seguir ao nome utilizado no objecto principal, não o nome da variável. Considere o CustomLayer
no exemplo abaixo. A variável CustomLayer.var
é salvo com "var"
como parte da chave, não "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}
Exemplo de transferência de aprendizado
Essencialmente, desde que dois modelos tenham a mesma arquitetura, eles podem compartilhar o mesmo ponto de verificação.
Exemplo:
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>
Geralmente, é recomendado manter a mesma API para construir modelos. Se você alternar entre Sequencial e Funcional, ou Funcional e subclasse, etc., sempre reconstrua o modelo pré-treinado e carregue os pesos pré-treinados para esse modelo.
A próxima pergunta é: como os pesos podem ser salvos e carregados em modelos diferentes se as arquiteturas dos modelos são bem diferentes? A solução é usar tf.train.Checkpoint
para salvar e restaurar as camadas exatas / variáveis.
Exemplo:
# 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
O formato HDF5 contém pesos agrupados por nomes de camada. Os pesos são listas ordenadas concatenando a lista de pesos treináveis para a lista de pesos não treináveis (o mesmo que layer.weights
). Assim, um modelo pode usar um ponto de verificação hdf5 se tiver as mesmas camadas e status treináveis salvos no ponto de verificação.
Exemplo:
# 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")
Note-se que a mudança layer.trainable
pode resultar em um diferente layer.weights
ordenação quando o modelo contém camadas aninhadas.
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
Exemplo de transferência de aprendizado
Ao carregar pesos pré-treinados do HDF5, é recomendável carregar os pesos no modelo com checkpoint original e, em seguida, extrair os pesos/camadas desejados em um novo modelo.
Exemplo:
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 _________________________________________________________________