In [ ]:
#@title Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

Guardando y Serializando Modelos con TensorFlow Keras

Note: Nuestra comunidad de Tensorflow ha traducido estos documentos. Como las traducciones de la comunidad son basados en el "mejor esfuerzo", no hay ninguna garantia que esta sea un reflejo preciso y actual de la Documentacion Oficial en Ingles. Si tienen sugerencias sobre como mejorar esta traduccion, por favor envian un "Pull request" al siguiente repositorio tensorflow/docs. Para ofrecerse como voluntario o hacer revision de las traducciones de la Comunidad por favor contacten al siguiente grupo docs@tensorflow.org list.

La primera parte de esta guia cubre el guardado y serialización para modelos secuenciales y modelos creados con la API funcional y para modelos secuenciales. Las APIs de guardado y serialización son exactamente las mismas para ambos tipos de modelos.

El guardado para las subclases personalizadas de Modelo se explica en la sección "Guardar modelos de subclases". Las APIs en este caso son ligeramente diferentes a las de los modelos secuenciales o funcionales.

Configurar


In [ ]:
import tensorflow as tf

tf.keras.backend.clear_session()  # Para restablecer fácilmente el estado del portátil.

Parte I: Guardar modelos secuenciales o modelos funcionales

Consideremos el siguiente modelo:


In [ ]:
from tensorflow import keras
from tensorflow.keras import layers

inputs = keras.Input(shape=(784,), name='digits')
x = layers.Dense(64, activation='relu', name='dense_1')(inputs)
x = layers.Dense(64, activation='relu', name='dense_2')(x)
outputs = layers.Dense(10, activation='softmax', name='predictions')(x)

model = keras.Model(inputs=inputs, outputs=outputs, name='3_layer_mlp')
model.summary()

Opcionalmente, entrenaremos este modelo, solo para que tenga valores de peso para guardarlos, así como un estado optimizador. Por supuesto, tambien puede guardar modelos que nunca ha entrenado, pero obviamente eso es menos interesante.


In [ ]:
(x_train, y_train), (x_test, y_test) = keras.datasets.mnist.load_data()
x_train = x_train.reshape(60000, 784).astype('float32') / 255
x_test = x_test.reshape(10000, 784).astype('float32') / 255

model.compile(loss='sparse_categorical_crossentropy',
              optimizer=keras.optimizers.RMSprop())
history = model.fit(x_train, y_train,
                    batch_size=64,
                    epochs=1)

In [ ]:
# Guardar predicciones para futuras verificaciones
predictions = model.predict(x_test)

Modelo-Completo Guardando

Puede guardar un modelo creado con la API funcional en un solo archivo. Posteriormente, puede volver a crear el mismo modelo a partir de este archivo, incluso si ya no tiene acceso al codigo que creo el modelo.

Este archivo incluye:

  • Los modelos de arquitectura
  • Los valores de peso del modelo (que se aprendieron durante el entrenamiento)
  • La configuración de entrenamiento del modelo (lo que pasó a 'compilar'), si corresponde
  • El optimizador y su estado, si corresponde (esto le permite reiniciar el entrenamiento donde lo dejó)

In [ ]:
# Guardar el Modelo
model.save('path_to_my_model.h5')

# Recrea exactamente el mismo modelo solo desde el archivo
new_model = keras.models.load_model('path_to_my_model.h5')

In [ ]:
import numpy as np

# Verifique que el estado esté preservado
new_predictions = new_model.predict(x_test)
np.testing.assert_allclose(predictions, new_predictions, rtol=1e-6, atol=1e-6)

# Tenga en cuenta que el estado del optimizador también se conserva:
# puede reanudar el entrenamiento donde lo dejó.

Exportar a 'SavedModel'

Tambien puede exportar un modelo completo al formato 'SavedModel' de TensorFlow. 'SavedModel' es un formato de serialización independiente para objetos de Tensorflow, compatible con el servicio de TensorFlow y las implementaciones de TensorFlow que no sean Python.


In [ ]:
# Exportar el modelo a 'SavedModel'
keras.experimental.export_saved_model(model, 'path_to_saved_model')

# Recrea exactamente el mismo modelo
new_model = keras.experimental.load_from_saved_model('path_to_saved_model')

# Verifique que el estado esté guardado
new_predictions = new_model.predict(x_test)
np.testing.assert_allclose(predictions, new_predictions, rtol=1e-6, atol=1e-6)

# Tenga en cuenta que el estado del optimizador también se conserva:
# puede reanudar el entrenamiento donde lo dejó.

Los archivos 'SavedModel' que se crearon contienen:

  • Un punto de control TensorFlow que contiene los pesos del modelo.
  • Un prototipo 'SavedModel' que contiene el grafico subyacente de Tensorflow. Separar los graficos que se guardan para prediccion (servicio), capacitacion y evaluacion. Si el modelo no se compilo antes, solo el grafico de inferencia se exporta
  • La configuracion de arquitectura del modelo, si esta disponible.

Solo arquitectura de guardado

A veces, solo esta interesado en la arquitectura del modelo y no necesita guardar los valores de peso o el optimizador. En este caso, puede recuperar la "configuracion" del modelo mediante el metodo get_config (). La configuracion es un dict de Python que le permite recrear el mismo modelo, inicializado desde cero, sin ninguna de la información aprendida previamente durante el entrenamiento.


In [ ]:
config = model.get_config()
reinitialized_model = keras.Model.from_config(config)

# ¡Tenga en cuenta que el estado del modelo no se conserva! Solo guardamos la arquitectura.
new_predictions = reinitialized_model.predict(x_test)
assert abs(np.sum(predictions - new_predictions)) > 0.

Alternativamente, puede usar 'to_json()' de 'from_json ()', que usa una cadena JSON para almacenar la configuracion en lugar de un 'dict' de Python. Esto es util para guardar la configuracion en el disco.


In [ ]:
json_config = model.to_json()
reinitialized_model = keras.models.model_from_json(json_config)

Guardando solo con pesos

A veces, solo le interesa el estado del modelo, sus valores de peso, y no la arquitectura. En este caso, puede recuperar los valores de pesos como una lista de matrices Numpy a traves de 'get_weights()', y establecer el estado del modelo a través de 'set_weights':


In [ ]:
weights = model.get_weights()  # Recupera el estado del modelo.
model.set_weights(weights)  # Establece el estado del modelo.

Puede combinar get_config()/from_config() y get_weights()/set_weights() para recrear su modelo en el mismo estado. Sin embargo, a diferencia de model.save(), esto no incluira la configuracion de entrenamiento y el optimizado tendría que volver a llamar a compile() antes de usar el modelo para el entrenamiento.


In [ ]:
config = model.get_config()
weights = model.get_weights()

new_model = keras.Model.from_config(config)
new_model.set_weights(weights)

# Verifique que el estado esté preservado
new_predictions = new_model.predict(x_test)
np.testing.assert_allclose(predictions, new_predictions, rtol=1e-6, atol=1e-6)

# Tenga en cuenta que el optimizador no se conserva
# entonces el modelo debe compilarse nuevamente antes de entrenar
# (y el optimizador comenzará desde un estado en blanco).

La alternativa de guardar en disco a 'get_weights()' y 'set_weights(weights)' es 'save_weights(fpath)' y 'load_weights(fpath)'.

Aquí hay un ejemplo que guarda en el disco:


In [ ]:
# Guardar configuración JSON en el disco
json_config = model.to_json()
with open('model_config.json', 'w') as json_file:
    json_file.write(json_config)
# Guardar pesos en el disco
model.save_weights('path_to_my_weights.h5')

# Recargue el modelo de los 2 archivos que guardamos
with open('model_config.json') as json_file:
    json_config = json_file.read()
new_model = keras.models.model_from_json(json_config)
new_model.load_weights('path_to_my_weights.h5')

# Verifique que el estado esté preservado
new_predictions = new_model.predict(x_test)
np.testing.assert_allclose(predictions, new_predictions, rtol=1e-6, atol=1e-6)

# Tenga en cuenta que el optimizador no se conservo.

Pero recuerde que la forma más simple y recomendada es solo esto:


In [ ]:
model.save('path_to_my_model.h5')
del model
model = keras.models.load_model('path_to_my_model.h5')

Weights-only Guardando en formato 'SavedModel'

Tenga en cuenta que 'save_weights' puede crear archivos en el formato Keras HDF5 o en el formato TensorFlow 'SavedModel'. El formato se infiere de la extension de archivo que proporciona: si es ".h5" o ".keras", el marco utiliza el formato Keras HDF5. Cualquier otra cosa por defecto es 'SavedModel'.


In [ ]:
model.save_weights('path_to_my_tf_savedmodel')

Para una total explicidad, el formato se puede pasar explicitamente a traves del argumento 'save_format', que puede tomar el valor "tf" o "h5":


In [ ]:
model.save_weights('path_to_my_tf_savedmodel', save_format='tf')

Guardar modelos subclasificados

Los modelos secuenciales y los modelos funcionales son estructuras de datos que representan un DAG de capas. Como tal, se pueden serializar y deserializar de forma segura.

Un modelo subclasificado difiere en que no es una estructura de datos, es una pieza de código. La arquitectura del modelo se define a través del cuerpo del método de llamada. Esto significa que la arquitectura del modelo no se puede serializar de forma segura. Para cargar un modelo, deberá tener acceso al código que lo creó (el código de la subclase de modelo). Alternativamente, podría estar serializando este código como bytecode (por ejemplo, mediante pickling), pero eso no es seguro y, en general, no es portátil.

Para obtener más información sobre estas diferencias, vea el artículo "¿Qué son las API simbólicas e imperativas en TensorFlow 2.0?".

Consideremos el siguiente modelo subclasificado, que sigue la misma estructura que el modelo de la primera seccion:


In [ ]:
class ThreeLayerMLP(keras.Model):

  def __init__(self, name=None):
    super(ThreeLayerMLP, self).__init__(name=name)
    self.dense_1 = layers.Dense(64, activation='relu', name='dense_1')
    self.dense_2 = layers.Dense(64, activation='relu', name='dense_2')
    self.pred_layer = layers.Dense(10, activation='softmax', name='predictions')

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

def get_model():
  return ThreeLayerMLP(name='3_layer_mlp')

model = get_model()

En primer lugar, un modelo subclasificado que nunca se ha utilizado no se puede guardar..

Esto se debe a que es necesario invocar un modelo subclasificado en algunos datos para crear sus pesos.

Hasta que se haya llamado al modelo, no conoce la forma y el tipo de datos de entrada que debería ser esperando, y por lo tanto no puede crear sus variables de peso. Puede recordar que en el modelo funcional de la primera sección, la forma y el tipo de las entradas se especificaron de antemano (a través de 'keras.Input (...)'), por eso los modelos funcionales tienen un estado tan pronto como Estás instanciado.

Vamos a entrenar el modelo para darle un estado:


In [ ]:
(x_train, y_train), (x_test, y_test) = keras.datasets.mnist.load_data()
x_train = x_train.reshape(60000, 784).astype('float32') / 255
x_test = x_test.reshape(10000, 784).astype('float32') / 255

model.compile(loss='sparse_categorical_crossentropy',
              optimizer=keras.optimizers.RMSprop())
history = model.fit(x_train, y_train,
                    batch_size=64,
                    epochs=1)

La forma recomendada de guardar un modelo subclasificado es usar 'save_weights' para crear un punto de control TensorFlow 'SavedModel', que contendra el valor de todas las variables asociadas con el modelo:

  • Los pesos de las capas
  • El estado del optimizador
  • Cualquier variable asociada con métricas de modelo con estado (si las hay)

In [ ]:
model.save_weights('path_to_my_weights', save_format='tf')

In [ ]:
# Guardar predicciones para futuras verificaciones
predictions = model.predict(x_test)
# También guarde la pérdida en el primer lote
# para luego afirmar que el estado del optimizador fue preservado
first_batch_loss = model.train_on_batch(x_train[:64], y_train[:64])

Para restaurar su modelo, necesitará acceso al codigo que creó el objeto modelo.

Tenga en cuenta que para restaurar el estado del optimizador y el estado de cualquier métrica con estado, debe compila el modelo (con los mismos argumentos que antes) y llámalo con algunos datos antes de llamar a 'load_weights':


In [ ]:
# Recrea el modelo
new_model = get_model()
new_model.compile(loss='sparse_categorical_crossentropy',
                  optimizer=keras.optimizers.RMSprop())

# Esto inicializa las variables utilizadas por los optimizadores,
# así como cualquier variable métrica con estado
new_model.train_on_batch(x_train[:1], y_train[:1])

# Cargue el estado del modelo anterior.
new_model.load_weights('path_to_my_weights')

# Compruebe que se ha conservado el estado del modelo.
new_predictions = new_model.predict(x_test)
np.testing.assert_allclose(predictions, new_predictions, rtol=1e-6, atol=1e-6)

# El estado del optimizador también se conserva,
# para que pueda reanudar el entrenamiento donde lo dejó
new_first_batch_loss = new_model.train_on_batch(x_train[:64], y_train[:64])
assert first_batch_loss == new_first_batch_loss

You've reached the end of this guide! This covers everything you need to know about saving and serializing models with tf.keras in TensorFlow 2.0.