Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
tensorflow
GitHub Repository: tensorflow/docs-l10n
Path: blob/master/site/es-419/guide/saved_model.ipynb
25115 views
Kernel: Python 3
#@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.

Uso del formato SavedModel

Un SavedModel contiene un programa completo de TensorFlow, que incluye los parámetros entrenados (es decir, las tf.Variable) y los cálculos. No requiere del código con el que se creó el modelo original para ejecutarse. Esta virtud hace que resulte útil compartirlo o implementarlo con TFLite, TensorFlow.js, TensorFlow Serving o TensorFlow Hub.

Un modelo se puede guardar y cargar en el formato SavedModel con las siguientes API:

Advertencia: Los modelos TensorFlow son código y hay que tener cuidado con que dicho código no sea confiable. Aprenda más en la sección sobre cómo usar TensorFlow de forma segura.

Creación de un SavedModel con Keras

Obsoleto: Para los objetos Keras, se recomienda usar el nuevo formato de alto nivel .keras y tf.keras.Model.export, como se demuestra en la guía aquí. El formato SavedModel de bajo nivel sigue siendo compatible con el código existente.

A modo de introducción breve, en esta sección se exporta un modelo keras previamente entrenado y se realizan solicitudes de clasificación con él. En el resto de la guía se completan los detalles y se analizan otras formas de crear SavedModels.

import os import tempfile from matplotlib import pyplot as plt import numpy as np import tensorflow as tf tmpdir = tempfile.mkdtemp()
physical_devices = tf.config.list_physical_devices('GPU') for device in physical_devices: tf.config.experimental.set_memory_growth(device, True)
file = tf.keras.utils.get_file( "grace_hopper.jpg", "https://storage.googleapis.com/download.tensorflow.org/example_images/grace_hopper.jpg") img = tf.keras.utils.load_img(file, target_size=[224, 224]) plt.imshow(img) plt.axis('off') x = tf.keras.utils.img_to_array(img) x = tf.keras.applications.mobilenet.preprocess_input( x[tf.newaxis,...])

Usaremos una imagen de Grace Hopper como ejemplo y un modelo de clasificación previamente entrenado de Keras, ya que es fácil de usar. Los modelos personalizados también funcionan; hablaremos en detalle sobre ellos más adelante.

labels_path = tf.keras.utils.get_file( 'ImageNetLabels.txt', 'https://storage.googleapis.com/download.tensorflow.org/data/ImageNetLabels.txt') imagenet_labels = np.array(open(labels_path).read().splitlines())
pretrained_model = tf.keras.applications.MobileNet() result_before_save = pretrained_model(x) decoded = imagenet_labels[np.argsort(result_before_save)[0,::-1][:5]+1] print("Result before saving:\n", decoded)

La principal predicción para esta imagen es el "uniforme militar".

mobilenet_save_path = os.path.join(tmpdir, "mobilenet/1/") tf.saved_model.save(pretrained_model, mobilenet_save_path)

La ruta de guardado sigue una convención usada por TensorFlow Serving donde el último componente de la ruta (aquí 1/) es un número de versión para su modelo. Permite usar herramientas como TensorFlow Serving para razonar sobre la frescura relativa.

El SavedModel se puede volver a cargar en Python con tf.saved_model.load y, entonces, ver cómo se clasifica la imagen de la almirante Hopper.

loaded = tf.saved_model.load(mobilenet_save_path) print(list(loaded.signatures.keys())) # ["serving_default"]

Las firmas importadas siempre devuelven diccionarios. Para personalizar los nombres y las claves del diccionario de salida, consulte Especificación de las firmas durante la exportación.

infer = loaded.signatures["serving_default"] print(infer.structured_outputs)

Al ejecutar la inferencia del SavedModel se obtiene el mismo resultado del modelo original.

labeling = infer(tf.constant(x))[pretrained_model.output_names[0]] decoded = imagenet_labels[np.argsort(labeling)[0,::-1][:5]+1] print("Result after saving and loading:\n", decoded)

Ejecución de SavedModel en TensorFlow Serving

Los SavedModel se pueden usar desde Python (más adelante se brindan más detalles), pero los entornos de producción normalmente usan un servicio exclusivo para la inferencia sin ejecutar código Python. Se configura con facilidad a partir de un SavedModel con TensorFlow Serving.

Para conocer un ejemplo completo de servicio de TensorFlow, consulte Entrene y sirva un modelo de TensorFlow con TensorFlow Serving.

El formato SavedModel en disco

Un SavedModel es un directorio que contiene firmas serializadas y el estado necesita ejecutarlas, incluidos los valores de variables y vocabularios.

!ls {mobilenet_save_path}

El archivo saved_model.pb almacena el programa de TensorFlow real, o el modelo, y configura un conjunto de firmas con nombre. Con cada una de ellas se identifica una función que acepta las entradas del tensor y produce las salidas de tensor.

Los SavedModels pueden contener múltiples variantes del modelo (múltiples v1.MetaGraphDefs, identificados con la etiqueta --tag_set para saved_model_cli), pero no es lo más común. Las API que crean múltiples variantes de un modelo incluyen tf.Estimator.experimental_export_all_saved_models y en TensorFlow, 1.x tf.saved_model.Builder.

!saved_model_cli show --dir {mobilenet_save_path} --tag_set serve

El directorio de variables contiene un punto de verificación de entrenamiento estándar (consulte la guía para entrenamiento de puntos de verificación).

!ls {mobilenet_save_path}/variables

El directorio assets contiene archivos que usa el grafo de TensorFlow; por ejemplo, los archivos de texto que se usan para inicializar las tablas de vocabulario. No se usa en este ejemplo.

SavedModels puede tener un directorio assets.extra para cualquier archivo que no use el grafo de TensorFlow. Por ejemplo, la información para consumidores sobre qué hacer con el SavedModel. TensorFlow mismo no usa este directorio.

El archivo fingerprint.pb contiene la huella digital de un SavedModel, que está compuesta de varias funciones hash de 64 bits que identifican de forma única el contenido del SavedModel. La API de huella, por el momento, es experimental, pero tf.saved_model.experimental.read_fingerprint se puede usar para leer la huella digital de SavedModel en un objeto tf.saved_model.experimental.Fingerprint.

Guardado de un modelo personalizado

tf.saved_model.save permite guardar objetos tf.Module y sus subclases, como tf.keras.Layer y tf.keras.Model.

Observemos un ejemplo en el que se muestra cómo guardar y restaurar un tf.Module.

class CustomModule(tf.Module): def __init__(self): super(CustomModule, self).__init__() self.v = tf.Variable(1.) @tf.function def __call__(self, x): print('Tracing with', x) return x * self.v @tf.function(input_signature=[tf.TensorSpec([], tf.float32)]) def mutate(self, new_v): self.v.assign(new_v) module = CustomModule()

Cuando guarda un tf.Module, también se guarda cualquier atributo tf.Variable o método decorado por tf.function y tf.Modules que se hallen a través de recorridos recursivos. (Para más información sobre los recorridos recursivos, consulte el tutorial sobre puntos de verificación). Sin embargo, se pierden cualquiera de las funciones, los datos y los atributos Python. Significa que cuando se guarda una tf.function, no se guarda ningún código de Python.

Si no se guarda ningún código Python, ¿cómo sabe el SavedModel qué tiene que hacer para restaurar la función?

En resumen, tf.function funciona rastreando el código Python para generar una función concreta (ConcreteFunction) (un encrustamiento invocable en torno a tf.Graph). Al guardar la tf.function, en realidad, se guarda el caché de la tf.function de las funciones concretas.

Para más información sobre la relación entre tf.function y las funciones concretas, consulte la guía sobre tf.function.

module_no_signatures_path = os.path.join(tmpdir, 'module_no_signatures') module(tf.constant(0.)) print('Saving model...') tf.saved_model.save(module, module_no_signatures_path)

Carga y uso de un modelo personalizado

Cuando carga un SavedModel en Python, todos los atributos de tf.Variable, los métodos decorados por tf.function y los tf.Module se restauran con la misma estructura de objeto del tf.Module.

imported = tf.saved_model.load(module_no_signatures_path) assert imported(tf.constant(3.)).numpy() == 3 imported.mutate(tf.constant(2.)) assert imported(tf.constant(3.)).numpy() == 6

Como no se guarda ningún código Python, si se invoca una tf.function con una firma de entrada nueva, fallará:

imported(tf.constant([3.]))
ValueError: Could not find matching function to call for canonicalized inputs ((<tf.Tensor 'args_0:0' shape=(1,) dtype=float32>,), {}). Only existing signatures are [((TensorSpec(shape=(), dtype=tf.float32, name=u'x'),), {})].

Ajuste fino básico

Hay objetos variables disponibles y es posible propagar hacia atrás (backprop) mediante funciones importadas. Esto es suficiente como para aplicar el ajuste fino (es decir, como para retener) un SavedModel en casos simples.

optimizer = tf.keras.optimizers.SGD(0.05) def train_step(): with tf.GradientTape() as tape: loss = (10. - imported(tf.constant(2.))) ** 2 variables = tape.watched_variables() grads = tape.gradient(loss, variables) optimizer.apply_gradients(zip(grads, variables)) return loss
for _ in range(10): # "v" approaches 5, "loss" approaches 0 print("loss={:.2f} v={:.2f}".format(train_step(), imported.v.numpy()))

Ajuste fino general

Un SavedModel de Keras aporta más detalles que una __call__ simple para abordar casos más avanzados de ajuste fino. TensorFlow Hub recomienda proporcionar, en caso de ser posible, lo siguiente en los SavedModels compartidos para ajuste fino:

  • Si el modelo usa la técnica de abandono (dropout) u alguna otra en que el pase hacia adelante difiere entre entrenamiento e inferencia (como la normalización por lotes), el método __call__ toma un argumento opcional de training= valuado por Python, que por defecto es False pero que se puede establecer como True.

  • Junto al atributo __call__, hay atributos .variable y .trainable_variable con las correspondientes listas de variables. De .trainable_variables se omite una variable que originalmente era entrenable, pero que esta prevista para quedar congelada durante el ajuste fino.

  • Para satisfacer los esquemas como Keras que representan regularizadores de peso, como atributos de capas o submodelos; también puede haber un atributo .regularization_losses. Este contiene una lista de las funciones de argumento cero cuyos valores fueron previstos para sumarlos a la pérdida total.

Si volvemos al ejemplo original de MobileNet, veremos a algunos en acción:

loaded = tf.saved_model.load(mobilenet_save_path) print("MobileNet has {} trainable variables: {}, ...".format( len(loaded.trainable_variables), ", ".join([v.name for v in loaded.trainable_variables[:5]])))
trainable_variable_ids = {id(v) for v in loaded.trainable_variables} non_trainable_variables = [v for v in loaded.variables if id(v) not in trainable_variable_ids] print("MobileNet also has {} non-trainable variables: {}, ...".format( len(non_trainable_variables), ", ".join([v.name for v in non_trainable_variables[:3]])))

Especificación de firmas durante la exportación

Las herramientas como TensorFlow Serving y saved_model_cli pueden interactuar con SavedModels. Para ayudar a esas herramientas a determinar qué funciones concretas usar, se deberán especificar las firmas de servicio (serving). Los tf.keras.Model especifican automáticamente las firmas de servicio, pero habrá que declarar explícitamente una firma de servicio para los módulos personalizados.

IMPORTANTE: A menos que necesite exportar su modelo a un entorno diferente de TensorFlow 2.x con Python, es probable que no haga falta exportar explícitamente las firmas. Si lo que busca es una forma de forzar una firma de entrada para una función específica, analice el argumento input_signature para tf.function.

Por defecto, en un tf.Module personalizado no se declaran las firmas.

assert len(imported.signatures) == 0

Para declarar una firma de servicio, especifique una función concreta con los kwarg de las signatures. Cuando especifique una firma sola, la clave de esa firma será ''serving_default'', que se guarda como la constante de tf.saved_model.DEFAULT_SERVING_SIGNATURE_DEF_KEY.

module_with_signature_path = os.path.join(tmpdir, 'module_with_signature') call = module.__call__.get_concrete_function(tf.TensorSpec(None, tf.float32)) tf.saved_model.save(module, module_with_signature_path, signatures=call)
imported_with_signatures = tf.saved_model.load(module_with_signature_path) list(imported_with_signatures.signatures.keys())

Para exportar múltiples firmas, pase un diccionario de claves de firmas a funciones concretas. Cada clave de firma corresponde a una función concreta.

module_multiple_signatures_path = os.path.join(tmpdir, 'module_with_multiple_signatures') signatures = {"serving_default": call, "array_input": module.__call__.get_concrete_function(tf.TensorSpec([None], tf.float32))} tf.saved_model.save(module, module_multiple_signatures_path, signatures=signatures)
imported_with_multiple_signatures = tf.saved_model.load(module_multiple_signatures_path) list(imported_with_multiple_signatures.signatures.keys())

Por defecto, los nombres del tensor de salida son bastante genéricos, como output_0. Para controlar los nombres de las salidas, modifique la tf.function para devolver un diccionario que mapee los nombres de las salidas con las salidas mismas. Los nombres de las entradas derivan de los nombres de los argumentos de las funciones de Python.

class CustomModuleWithOutputName(tf.Module): def __init__(self): super(CustomModuleWithOutputName, self).__init__() self.v = tf.Variable(1.) @tf.function(input_signature=[tf.TensorSpec(None, tf.float32)]) def __call__(self, x): return {'custom_output_name': x * self.v} module_output = CustomModuleWithOutputName() call_output = module_output.__call__.get_concrete_function(tf.TensorSpec(None, tf.float32)) module_output_path = os.path.join(tmpdir, 'module_with_output_name') tf.saved_model.save(module_output, module_output_path, signatures={'serving_default': call_output})
imported_with_output_name = tf.saved_model.load(module_output_path) imported_with_output_name.signatures['serving_default'].structured_outputs

División de protoclase

Nota: esta función será parte del lanzamiento de TensorFlow 2.15. En este momento, está disponible en la versión "nocturna", que se puede instalar con pip install tf-nightly.

Debido a los límites de la implementación del protobuf (búfer de protocolo), los tamaños de las protoclases no deben exceder los 2GB. De lo contrario puede causar los siguientes errores cuando intente guardar modelos muy grandes:

ValueError: Message tensorflow.SavedModel exceeds maximum protobuf size of 2GB: ...
google.protobuf.message.DecodeError: Error parsing message as the message exceeded the protobuf limit with type 'tensorflow.GraphDef'

Si desea guardar modelos que excedan el límite de 2GB, deberá guardarlos con una nueva opción de división de protoclase:

tf.saved_model.save( ..., options=tf.saved_model.SaveOptions(experimental_image_format=True) )

Acceda a más información en la guía sobre divisor de protoclase / biblioteca de fusión.

Carga de un SavedModel en C++

La versión C++ del cargador de SavedModel proporciona una API para cargar un SavedModel con una ruta y, a la vez, admite SessionOptions y RunOptions. Hay que especificar las etiquetas asociadas con el grafo que se cargará. A la versión cargada de SavedModel se la conoce como SavedModelBundle y contiene el MetaGraphDef y la sesión dentro de la que se carga.

const string export_dir = ... SavedModelBundle bundle; ... LoadSavedModel(session_options, run_options, export_dir, {kSavedModelTagTrain}, &bundle);

Detalles de la interfaz de línea de comandos de SavedModel

La interfaz de línea de comandos (CLI) de SavedModel se puede usar para inspeccionar y ejecutar un SavedModel. Por ejemplo, puede usar una interfaz de línea de comandos para inspeccionar las SignatureDef del modelo. La interfaz de línea de comandos le permitirá confirmar rápidamente el tensor de tipo d de entrada y la coincidencia de la forma del modelo. Además, si desea probar el modelo, puede usar la interfaz de línea de comandos para hacer una prueba de cordura (sanity check) mediante el pase de entradas en varios formatos (por ejemplo, en expresiones Python) y después una extracción de la salida.

Instalación de la interfaz de línea de comandos de SavedModel

En términos generales, TensorFlow se puede instalar de alguna de las siguientes dos maneras:

  • Mediante la instalación de un TensorFlow binario preconfigurado.

  • Mediante la creación de TensorFlow a partir de código fuente.

Si instaló TensorFlow con TensorFlow binario preconfigurado, entonces, la interfaz de línea de comandos del SavedModel ya está instalada en su sistema, en bin/saved_model_cli.

Si creó TensorFlow con código fuente, debe ejecutar el siguiente comando adicional para crear saved_model_cli:

$ bazel build //tensorflow/python/tools:saved_model_cli

Descripción general de los comandos

La interfaz de línea de comandos de SavedModel admite los siguientes dos comandos en un SavedModel:

  • show, que muestra los cálculos disponibles para SavedModel.

  • run, que ejecuta un cálculo de un SavedModel.

Comando show

Un SavedModel contiene una o más variantes del modelo (técnicamente, son v1.MetaGraphDef), identificadas por sus conjuntos de etiquetas. Se preguntará cuál es el tipo de SignatureDef de cada variante del modelo y cuáles son sus entradas y salidas, para servir al modelo. El comando show permite examinar el contenido de SavedModel en orden jerárquico. A continuación, la sintaxis:

usage: saved_model_cli show [-h] --dir DIR [--all] [--tag_set TAG_SET] [--signature_def SIGNATURE_DEF_KEY]

Por ejemplo, el siguiente comando muestra todos los conjuntos de etiquetas disponibles en el SavedModel:

$ saved_model_cli show --dir /tmp/saved_model_dir The given SavedModel contains the following tag-sets: serve serve, gpu

El siguiente comando muestra todas las claves de SignatureDef disponibles para un conjunto de etiquetas:

$ saved_model_cli show --dir /tmp/saved_model_dir --tag_set serve The given SavedModel `MetaGraphDef` contains `SignatureDefs` with the following keys: SignatureDef key: "classify_x2_to_y3" SignatureDef key: "classify_x_to_y" SignatureDef key: "regress_x2_to_y3" SignatureDef key: "regress_x_to_y" SignatureDef key: "regress_x_to_y2" SignatureDef key: "serving_default"

Si hay múltiples etiquetas en el conjunto de etiquetas, deberá especificarlas a todas, cada una por separado con una coma. Por ejemplo:

$ saved_model_cli show --dir /tmp/saved_model_dir --tag_set serve,gpu

Para mostrar todas las entradas y salidas de TensorInfo para una SignatureDef específica, pase la clave de SignatureDef a la opción signature_def. Esta acción resulta muy útil cuando lo que se desea es conocer el valor de la clave del tensor, el tipo d y la forma de los tensores de entrada para ejecutar más adelante los grafos de cálculo. Por ejemplo:

$ saved_model_cli show --dir \ /tmp/saved_model_dir --tag_set serve --signature_def serving_default The given SavedModel SignatureDef contains the following input(s): inputs['x'] tensor_info: dtype: DT_FLOAT shape: (-1, 1) name: x:0 The given SavedModel SignatureDef contains the following output(s): outputs['y'] tensor_info: dtype: DT_FLOAT shape: (-1, 1) name: y:0 Method name is: tensorflow/serving/predict

Para mostrar toda la información disponible en el SavedModel, use la opción --all. Por ejemplo:

$ saved_model_cli show --dir /tmp/saved_model_dir --all
MetaGraphDef with tag-set: 'serve' contains the following SignatureDefs:

signature_def['classify_x2_to_y3']:
  The given SavedModel SignatureDef contains the following input(s):
    inputs['inputs'] tensor_info:
        dtype: DT_FLOAT
        shape: (-1, 1)
        name: x2:0
  The given SavedModel SignatureDef contains the following output(s):
    outputs['scores'] tensor_info:
        dtype: DT_FLOAT
        shape: (-1, 1)
        name: y3:0
  Method name is: tensorflow/serving/classify

...

signature_def['serving_default']:
  The given SavedModel SignatureDef contains the following input(s):
    inputs['x'] tensor_info:
        dtype: DT_FLOAT
        shape: (-1, 1)
        name: x:0
  The given SavedModel SignatureDef contains the following output(s):
    outputs['y'] tensor_info:
        dtype: DT_FLOAT
        shape: (-1, 1)
        name: y:0
  Method name is: tensorflow/serving/predict

Comando run

Invoque el comando run para ejecutar el cálculo de un grafo, el paso de entradas y para después, mostrar (y, opcionalmente, guardar) las salidas. A continuación, la sintaxis:

usage: saved_model_cli run [-h] --dir DIR --tag_set TAG_SET --signature_def SIGNATURE_DEF_KEY [--inputs INPUTS] [--input_exprs INPUT_EXPRS] [--input_examples INPUT_EXAMPLES] [--outdir OUTDIR] [--overwrite] [--tf_debug]

El comando run ofrece las siguientes tres formas de pasar entradas al modelo:

  • La opción de --inputs le permitirá pasar ndarray de numpy en archivos.

  • La opción --input_exprs le permitirá pasar las expresiones de Python.

  • La opción --input_examples le permitirá pasar tf.train.Example.

--inputs

Para pasar archivos de datos de entrada, especifique la opción --inputs, que toma el siguiente formato general:

--inputs <INPUTS>

donde INPUTS (entradas) tiene alguno de los siguientes formatos:

  • <input_key>=<filename>

  • <input_key>=<filename>[<variable_name>]

Se pueden pasar múltiples INPUTS. Si lo hace, use un punto y coma para separarlos.

saved_model_cli usa numpy.load para cargar el nombre de archivo. El nombre de archivo se puede encontrar en cualquiera de los siguientes formatos:

  • .npy

  • .npz

  • formato pickle

Un archivo .npy siempre contiene un ndarray (arrglo nd) de numpy. Por lo tanto, cuando se carga un archivo .npy, el contenido se asignará directamente al tensor de entrada especificado. Si especifica un variable_name con ese archivo .npy, el variable_name se ignorará y se emitirá una advertencia.

Cuando haga una carga de un archivo .npz (zip), tendrá la opción de especificar un variable_name (nombre de variable) para identificar la variable dentro del archivo zip para cargar la clave del tensor de entrada. Si no especifica el variable_name, la interfaz de línea de comandos de SavedModel solamente controlará que el zip contenga un solo archivo y lo cargará para la clave del tensor de entrada especificado.

Cuando cargue de un archivo pickle, si no se especifica ningún variable_name entre los corchetes rectos, lo que esté dentro del archivo pickle pasará a la clave del tensor de entrada especificado. De lo contrario la interfaz de línea de comandos de SavedModel supondrá que se almacena un diccionario en el archivo pickle y que se usará el valor correspondiente al variable_name.

--input_exprs

Para pasar entradas a través de expresiones Python, especifique la opción --input_exprs. Esto puede resultar útil para cuando no se tienen archivos de datos cerca, pero aún se desea hacer el sanity check del modelo con algunas entradas simples que coincidan con el tipo d y la forma de las SignatureDef del modelo. Por ejemplo:

`<input_key>=[[1],[2],[3]]`

Además de expresiones de Python, también podría pasar funciones numpy. Por ejemplo:

`<input_key>=np.ones((32,32,3))`

(Tenga en cuenta que el módulo numpy ya está disponible como np.)

--input_examples

Para pasar tf.train.Example como entradas, especifique la opción --input_examples. Para cada clave de entrada, toma una lista de diccionario, donde cada diccionario es una instancia de tf.train.Example. Las claves de diccionario son las características, y los valores son las listas de valores de cada característica. Por ejemplo:

`<input_key>=[{"age":[22,24],"education":["BS","MS"]}]`

Guardado de salidas

Por defecto, la interfaz de línea de comandos de SavedModel escribe salidas a stdout. Si un directorio se pasa a la opción --outdir, las salidas se guardarán como archivos .npy con los nombres de las claves del tensor de salida del mismo directorio.

Use --overwrite para sobrescribir los archivos de salida existentes.