Esta página foi traduzida pela API Cloud Translation.
Switch to English

Usando o formato SavedModel

Ver em TensorFlow.org Executar no Google Colab Ver fonte no GitHub Download do caderno

Um SavedModel contém um programa TensorFlow completo, incluindo pesos e computação. Ele não exige que o código de construção do modelo original seja executado, o que o torna útil para compartilhar ou implantar (com TFLite , TensorFlow.js , TensorFlow Serving ou TensorFlow Hub ).

Este documento analisa alguns dos detalhes de como usar a API tf.saved_model baixo nível:

Criando um SavedModel a partir do Keras

Para uma introdução rápida, esta seção exporta um modelo Keras pré-treinado e atende a solicitações de classificação de imagem. O restante do guia preencherá detalhes e discutirá outras maneiras de criar os Modelos salvos.

 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.experimental.list_physical_devices('GPU')
if physical_devices:
  tf.config.experimental.set_memory_growth(physical_devices[0], 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.preprocessing.image.load_img(file, target_size=[224, 224])
plt.imshow(img)
plt.axis('off')
x = tf.keras.preprocessing.image.img_to_array(img)
x = tf.keras.applications.mobilenet.preprocess_input(
    x[tf.newaxis,...])
 
Downloading data from https://storage.googleapis.com/download.tensorflow.org/example_images/grace_hopper.jpg
65536/61306 [================================] - 0s 0us/step

png

Usaremos uma imagem de Grace Hopper como exemplo, e um modelo de classificação de imagens pré-treinado por Keras, pois é fácil de usar. Os modelos personalizados também funcionam e são detalhados posteriormente.

 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())
 
Downloading data from https://storage.googleapis.com/download.tensorflow.org/data/ImageNetLabels.txt
16384/10484 [==============================================] - 0s 0us/step

 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)
 
Downloading data from https://storage.googleapis.com/tensorflow/keras-applications/mobilenet/mobilenet_1_0_224_tf.h5
17227776/17225924 [==============================] - 1s 0us/step
Result before saving:
 ['military uniform' 'bow tie' 'suit' 'bearskin' 'pickelhaube']

A principal previsão para esta imagem é "uniforme militar".

 mobilenet_save_path = os.path.join(tmpdir, "mobilenet/1/")
tf.saved_model.save(pretrained_model, mobilenet_save_path)
 
WARNING:tensorflow:From /tmpfs/src/tf_docs_env/lib/python3.6/site-packages/tensorflow/python/ops/resource_variable_ops.py:1817: calling BaseResourceVariable.__init__ (from tensorflow.python.ops.resource_variable_ops) with constraint is deprecated and will be removed in a future version.
Instructions for updating:
If using Keras pass *_constraint arguments to layers.
INFO:tensorflow:Assets written to: /tmp/tmpmjpd1j0o/mobilenet/1/assets

O save-path segue uma convenção usada pelo TensorFlow Serving, em que o último componente do caminho ( 1/ aqui) é um número de versão do seu modelo - permite que ferramentas como o Tensorflow Serving raciocinem sobre a relativa frescura.

Podemos carregar o SavedModel novamente no Python com tf.saved_model.load e ver como a imagem do Almirante Hopper é classificada.

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

As assinaturas importadas sempre retornam dicionários. Para personalizar nomes de assinatura e chaves do dicionário de saída, consulte Especificando Assinaturas Durante a Exportação .

 infer = loaded.signatures["serving_default"]
print(infer.structured_outputs)
 
{'predictions': TensorSpec(shape=(None, 1000), dtype=tf.float32, name='predictions')}

A inferência em execução do SavedModel fornece o mesmo resultado que o 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)
 
Result after saving and loading:
 ['military uniform' 'bow tie' 'suit' 'bearskin' 'pickelhaube']

Executando um Modelo Saved no TensorFlow Serving

Os Modelos salvos são utilizáveis ​​no Python (mais sobre isso abaixo), mas os ambientes de produção geralmente usam um serviço dedicado para inferência sem executar o código Python. É fácil configurar a partir de um SavedModel usando o TensorFlow Serving.

Consulte o tutorial REST do TensorFlow Serving para obter mais detalhes sobre a veiculação, incluindo instruções para instalar o tensorflow_model_server em um notebook ou em sua máquina local. Como um esboço rápido, para servir o modelo de mobilenet exportado acima, aponte o servidor de modelo no diretório SavedModel:

 nohup tensorflow_model_server \
  --rest_api_port=8501 \
  --model_name=mobilenet \
  --model_base_path="/tmp/mobilenet" >server.log 2>&1
 

Em seguida, envie uma solicitação.

 !pip install -q requests
import json
import numpy
import requests
data = json.dumps({"signature_name": "serving_default",
                   "instances": x.tolist()})
headers = {"content-type": "application/json"}
json_response = requests.post('http://localhost:8501/v1/models/mobilenet:predict',
                              data=data, headers=headers)
predictions = numpy.array(json.loads(json_response.text)["predictions"])
 

As predictions resultantes são idênticas aos resultados do Python.

O formato SavedModel no disco

Um SavedModel é um diretório que contém assinaturas serializadas e o estado necessário para executá-las, incluindo valores e vocabulários variáveis.

ls {mobilenet_save_path}
assets  saved_model.pb  variables

O arquivo saved_model.pb armazena o programa ou modelo atual do TensorFlow e um conjunto de assinaturas nomeadas, cada uma identificando uma função que aceita entradas de tensores e produz saídas de tensores.

SavedModels pode conter múltiplas variantes do modelo (vários v1.MetaGraphDefs , identificados com o --tag_set bandeira para saved_model_cli ), mas isso é raro. As APIs que criam várias variantes de um modelo incluem tf.Estimator.experimental_export_all_saved_models e no TensorFlow 1.x tf.saved_model.Builder .

saved_model_cli show --dir {mobilenet_save_path} --tag_set serve
The given SavedModel MetaGraphDef contains SignatureDefs with the following keys:
SignatureDef key: "__saved_model_init_op"
SignatureDef key: "serving_default"

O diretório de variables contém um ponto de verificação de treinamento padrão (consulte o guia para pontos de verificação de treinamento ).

ls {mobilenet_save_path}/variables
variables.data-00000-of-00002  variables.data-00001-of-00002  variables.index

O diretório de assets contém arquivos usados ​​pelo gráfico TensorFlow, por exemplo, arquivos de texto usados ​​para inicializar tabelas de vocabulário. Não é utilizado neste exemplo.

SavedModels pode ter um diretório assets.extra para qualquer arquivo não usado pelo gráfico TensorFlow, por exemplo, informações para consumidores sobre o que fazer com o SavedModel. O próprio TensorFlow não usa esse diretório.

Salvando um Modelo Customizado

tf.saved_model.save suporta o salvamento de objetos tf.Module e suas subclasses, como tf.keras.Layer e tf.keras.Model .

Vejamos um exemplo de salvamento e restauração de um 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()
 

Quando você salva um tf.Module , nenhum tf.Variable atributos, tf.function -decorated métodos e tf.Module s encontrados através travessia recursiva são salvos. (Consulte o tutorial do ponto de verificação para obter mais informações sobre esse percurso recursivo.) No entanto, quaisquer atributos, funções e dados do Python são perdidos. Isso significa que, quando uma função tf.function é salva, nenhum código Python é salvo.

Se nenhum código Python é salvo, como o SavedModel sabe como restaurar a função?

Resumidamente, tf.function funciona rastreando o código Python para gerar um ConcreteFunction (um invólucro que pode ser tf.Graph torno de tf.Graph ). Ao salvar uma função tf.function , você está realmente salvando o cache de ConcreteFunctions da função tf.function .

Para saber mais sobre o relacionamento entre tf.function e ConcreteFunctions, consulte o guia 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)
 
Tracing with Tensor("x:0", shape=(), dtype=float32)
Saving model...
Tracing with Tensor("x:0", shape=(), dtype=float32)
INFO:tensorflow:Assets written to: /tmp/tmpmjpd1j0o/module_no_signatures/assets

Carregando e usando um modelo personalizado

Quando você carrega um SavedModel no Python, todos os atributos tf.Variable , métodos tf.function -decorated e tf.Module s são restaurados na mesma estrutura de objeto que o tf.Module original salvo.

 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 nenhum código Python é salvo, chamar uma função tf.function com uma nova assinatura de entrada falhará:

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

Ajuste fino básico

Objetos variáveis ​​estão disponíveis, e podemos fazer backprop através de funções importadas. Isso é suficiente para ajustar (ou seja, treinar novamente) um SavedModel em casos simples.

 optimizer = tf.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()))
 
loss=36.00 v=3.20
loss=12.96 v=3.92
loss=4.67 v=4.35
loss=1.68 v=4.61
loss=0.60 v=4.77
loss=0.22 v=4.86
loss=0.08 v=4.92
loss=0.03 v=4.95
loss=0.01 v=4.97
loss=0.00 v=4.98

Afinação geral

Um SavedModel da Keras fornece mais detalhes do que uma __call__ simples para tratar de casos mais avançados de ajuste fino. O TensorFlow Hub recomenda fornecer os seguintes itens, se aplicável, em SavedModels compartilhados com o objetivo de ajustar:

  • Se o modelo usar desistência ou outra técnica na qual a passagem para frente diferir entre treinamento e inferência (como normalização em lote), o método __call__ um argumento training= opcional, com valor de Python, cujo padrão é False mas que pode ser definido como True .
  • Ao lado do atributo __call__ , existem atributos .variable e .trainable_variable com as listas correspondentes de variáveis. Uma variável que era originalmente treinável, mas que deve ser congelada durante o ajuste fino é omitida de .trainable_variables .
  • Para estruturas, como Keras, que representam regularizadores de peso como atributos de camadas ou submodelos, também pode haver um atributo .regularization_losses . Ele contém uma lista de funções de argumento zero, cujos valores são destinados à adição à perda total.

Voltando ao exemplo inicial do MobileNet, podemos ver alguns deles em ação:

 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]])))
 
MobileNet has 83 trainable variables: conv1/kernel:0, conv1_bn/gamma:0, conv1_bn/beta:0, conv_dw_1/depthwise_kernel:0, conv_dw_1_bn/gamma:0, ...

 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]])))
 
MobileNet also has 54 non-trainable variables: conv1_bn/moving_mean:0, conv1_bn/moving_variance:0, conv_dw_1_bn/moving_mean:0, ...

Especificando assinaturas durante a exportação

Ferramentas como TensorFlow Serving e saved_model_cli podem interagir com SavedModels. Para ajudar essas ferramentas a determinar quais funções concretas usar, precisamos especificar assinaturas de veiculação. tf.keras.Model s especificam automaticamente as assinaturas de veiculação, mas teremos que declarar explicitamente uma assinatura de veiculação para nossos módulos personalizados.

Por padrão, nenhuma assinatura é declarada em um tf.Module personalizado.

 assert len(imported.signatures) == 0
 

Para declarar uma assinatura de veiculação, especifique uma ConcreteFunction usando as signatures kwarg. Ao especificar uma única assinatura, sua chave de assinatura será 'serving_default' , que é salva como a constante 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)
 
Tracing with Tensor("x:0", dtype=float32)
Tracing with Tensor("x:0", dtype=float32)
INFO:tensorflow:Assets written to: /tmp/tmpmjpd1j0o/module_with_signature/assets

 imported_with_signatures = tf.saved_model.load(module_with_signature_path)
list(imported_with_signatures.signatures.keys())

 
['serving_default']

Para exportar várias assinaturas, passe um dicionário de chaves de assinatura para ConcreteFunctions. Cada chave de assinatura corresponde a uma função 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)
 
Tracing with Tensor("x:0", shape=(None,), dtype=float32)
Tracing with Tensor("x:0", shape=(None,), dtype=float32)
INFO:tensorflow:Assets written to: /tmp/tmpmjpd1j0o/module_with_multiple_signatures/assets

 imported_with_multiple_signatures = tf.saved_model.load(module_multiple_signatures_path)
list(imported_with_multiple_signatures.signatures.keys())
 
['serving_default', 'array_input']

Por padrão, os nomes dos tensores de saída são bastante genéricos, como output_0 . Para controlar os nomes das saídas, modifique sua função tf.function para retornar um dicionário que mapeie os nomes das saídas. Os nomes das entradas são derivados dos nomes de argumentos da função Python.

 class CustomModuleWithOutputName(tf.Module):
  def __init__(self):
    super(CustomModuleWithOutputName, self).__init__()
    self.v = tf.Variable(1.)

  @tf.function(input_signature=[tf.TensorSpec([], 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})
 
INFO:tensorflow:Assets written to: /tmp/tmpmjpd1j0o/module_with_output_name/assets

 imported_with_output_name = tf.saved_model.load(module_output_path)
imported_with_output_name.signatures['serving_default'].structured_outputs
 
{'custom_output_name': TensorSpec(shape=(), dtype=tf.float32, name='custom_output_name')}

Modelos salvos de Estimadores

Os estimadores exportam Modelos salvos por meio de tf.Estimator.export_saved_model . Consulte o guia do Estimador para obter detalhes.

 input_column = tf.feature_column.numeric_column("x")
estimator = tf.estimator.LinearClassifier(feature_columns=[input_column])

def input_fn():
  return tf.data.Dataset.from_tensor_slices(
    ({"x": [1., 2., 3., 4.]}, [1, 1, 0, 0])).repeat(200).shuffle(64).batch(16)
estimator.train(input_fn)

serving_input_fn = tf.estimator.export.build_parsing_serving_input_receiver_fn(
  tf.feature_column.make_parse_example_spec([input_column]))
estimator_base_path = os.path.join(tmpdir, 'from_estimator')
estimator_path = estimator.export_saved_model(estimator_base_path, serving_input_fn)
 
INFO:tensorflow:Using default config.
WARNING:tensorflow:Using temporary folder as model directory: /tmp/tmp65c02lsq
INFO:tensorflow:Using config: {'_model_dir': '/tmp/tmp65c02lsq', '_tf_random_seed': None, '_save_summary_steps': 100, '_save_checkpoints_steps': None, '_save_checkpoints_secs': 600, '_session_config': allow_soft_placement: true
graph_options {
  rewrite_options {
    meta_optimizer_iterations: ONE
  }
}
, '_keep_checkpoint_max': 5, '_keep_checkpoint_every_n_hours': 10000, '_log_step_count_steps': 100, '_train_distribute': None, '_device_fn': None, '_protocol': None, '_eval_distribute': None, '_experimental_distribute': None, '_experimental_max_worker_delay_secs': None, '_session_creation_timeout_secs': 7200, '_service': None, '_cluster_spec': ClusterSpec({}), '_task_type': 'worker', '_task_id': 0, '_global_id_in_cluster': 0, '_master': '', '_evaluation_master': '', '_is_chief': True, '_num_ps_replicas': 0, '_num_worker_replicas': 1}
WARNING:tensorflow:From /tmpfs/src/tf_docs_env/lib/python3.6/site-packages/tensorflow/python/training/training_util.py:236: Variable.initialized_value (from tensorflow.python.ops.variables) is deprecated and will be removed in a future version.
Instructions for updating:
Use Variable.read_value. Variables in 2.X are initialized automatically both in eager and graph (inside tf.defun) contexts.
INFO:tensorflow:Calling model_fn.
WARNING:tensorflow:From /tmpfs/src/tf_docs_env/lib/python3.6/site-packages/tensorflow/python/feature_column/feature_column_v2.py:540: Layer.add_variable (from tensorflow.python.keras.engine.base_layer_v1) is deprecated and will be removed in a future version.
Instructions for updating:
Please use `layer.add_weight` method instead.
WARNING:tensorflow:From /tmpfs/src/tf_docs_env/lib/python3.6/site-packages/tensorflow/python/keras/optimizer_v2/ftrl.py:144: calling Constant.__init__ (from tensorflow.python.ops.init_ops) with dtype is deprecated and will be removed in a future version.
Instructions for updating:
Call initializer instance with the dtype argument instead of passing it to the constructor
INFO:tensorflow:Done calling model_fn.
INFO:tensorflow:Create CheckpointSaverHook.
INFO:tensorflow:Graph was finalized.
INFO:tensorflow:Running local_init_op.
INFO:tensorflow:Done running local_init_op.
INFO:tensorflow:Calling checkpoint listeners before saving checkpoint 0...
INFO:tensorflow:Saving checkpoints for 0 into /tmp/tmp65c02lsq/model.ckpt.
INFO:tensorflow:Calling checkpoint listeners after saving checkpoint 0...
INFO:tensorflow:loss = 0.6931472, step = 0
INFO:tensorflow:Calling checkpoint listeners before saving checkpoint 50...
INFO:tensorflow:Saving checkpoints for 50 into /tmp/tmp65c02lsq/model.ckpt.
INFO:tensorflow:Calling checkpoint listeners after saving checkpoint 50...
INFO:tensorflow:Loss for final step: 0.4131384.
INFO:tensorflow:Calling model_fn.
INFO:tensorflow:Done calling model_fn.
WARNING:tensorflow:From /tmpfs/src/tf_docs_env/lib/python3.6/site-packages/tensorflow/python/saved_model/signature_def_utils_impl.py:145: build_tensor_info (from tensorflow.python.saved_model.utils_impl) is deprecated and will be removed in a future version.
Instructions for updating:
This function will only be available through the v1 compatibility library as tf.compat.v1.saved_model.utils.build_tensor_info or tf.compat.v1.saved_model.build_tensor_info.
INFO:tensorflow:Signatures INCLUDED in export for Classify: ['serving_default', 'classification']
INFO:tensorflow:Signatures INCLUDED in export for Regress: ['regression']
INFO:tensorflow:Signatures INCLUDED in export for Predict: ['predict']
INFO:tensorflow:Signatures INCLUDED in export for Train: None
INFO:tensorflow:Signatures INCLUDED in export for Eval: None
INFO:tensorflow:Restoring parameters from /tmp/tmp65c02lsq/model.ckpt-50
INFO:tensorflow:Assets added to graph.
INFO:tensorflow:No assets to write.
INFO:tensorflow:SavedModel written to: /tmp/tmpmjpd1j0o/from_estimator/temp-1594862628/saved_model.pb

Este SavedModel aceita buffers de protocolo tf.Example serializados, que são úteis para servir. Mas também podemos carregá-lo com tf.saved_model.load e executá-lo no Python.

 imported = tf.saved_model.load(estimator_path)

def predict(x):
  example = tf.train.Example()
  example.features.feature["x"].float_list.value.extend([x])
  return imported.signatures["predict"](
    examples=tf.constant([example.SerializeToString()]))
 
 print(predict(1.5))
print(predict(3.5))
 
{'all_classes': <tf.Tensor: shape=(1, 2), dtype=string, numpy=array([[b'0', b'1']], dtype=object)>, 'logistic': <tf.Tensor: shape=(1, 1), dtype=float32, numpy=array([[0.5451435]], dtype=float32)>, 'probabilities': <tf.Tensor: shape=(1, 2), dtype=float32, numpy=array([[0.45485654, 0.5451435 ]], dtype=float32)>, 'logits': <tf.Tensor: shape=(1, 1), dtype=float32, numpy=array([[0.18106687]], dtype=float32)>, 'class_ids': <tf.Tensor: shape=(1, 1), dtype=int64, numpy=array([[1]])>, 'classes': <tf.Tensor: shape=(1, 1), dtype=string, numpy=array([[b'1']], dtype=object)>, 'all_class_ids': <tf.Tensor: shape=(1, 2), dtype=int32, numpy=array([[0, 1]], dtype=int32)>}
{'all_classes': <tf.Tensor: shape=(1, 2), dtype=string, numpy=array([[b'0', b'1']], dtype=object)>, 'logistic': <tf.Tensor: shape=(1, 1), dtype=float32, numpy=array([[0.21604054]], dtype=float32)>, 'probabilities': <tf.Tensor: shape=(1, 2), dtype=float32, numpy=array([[0.7839595 , 0.21604055]], dtype=float32)>, 'logits': <tf.Tensor: shape=(1, 1), dtype=float32, numpy=array([[-1.2888912]], dtype=float32)>, 'class_ids': <tf.Tensor: shape=(1, 1), dtype=int64, numpy=array([[0]])>, 'classes': <tf.Tensor: shape=(1, 1), dtype=string, numpy=array([[b'0']], dtype=object)>, 'all_class_ids': <tf.Tensor: shape=(1, 2), dtype=int32, numpy=array([[0, 1]], dtype=int32)>}

tf.estimator.export.build_raw_serving_input_receiver_fn permite criar funções de entrada que usam tensores brutos em vez de tf.train.Example s.

Carregar um SavedModel em C ++

A versão C ++ do carregador SavedModel fornece uma API para carregar um SavedModel a partir de um caminho, enquanto permite SessionOptions e RunOptions. Você precisa especificar as tags associadas ao gráfico a ser carregado. A versão carregada do SavedModel é referida como SavedModelBundle e contém o MetaGraphDef e a sessão na qual ele é carregado.

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

Detalhes da interface da linha de comandos SavedModel

Você pode usar a interface da linha de comando (CLI) do SavedModel para inspecionar e executar um SavedModel. Por exemplo, você pode usar a CLI para inspecionar os SignatureDef s do modelo. A CLI permite confirmar rapidamente que o tipo e a forma do Tensor de entrada correspondem ao modelo. Além disso, se você quiser testar seu modelo, poderá usar a CLI para fazer uma verificação de integridade, passando entradas de amostra em vários formatos (por exemplo, expressões Python) e buscando a saída.

Instale a CLI SavedModel

Em termos gerais, você pode instalar o TensorFlow de uma das seguintes maneiras:

  • Instalando um binário TensorFlow pré-construído.
  • Criando o TensorFlow a partir do código fonte.

Se você instalou o TensorFlow através de um binário pré-construído do TensorFlow, a CLI SavedModel já está instalada no seu sistema no nome do caminho bin/saved_model_cli .

Se você criou o TensorFlow a partir do código-fonte, deve executar o seguinte comando adicional para criar saved_model_cli :

 $ bazel build tensorflow/python/tools:saved_model_cli
 

Visão geral dos comandos

A CLI SavedModel suporta os dois comandos a seguir em um SavedModel:

  • show , que mostra os cálculos disponíveis em um SavedModel.
  • run , que executa uma computação a partir de um SavedModel.

comando show

Um SavedModel contém uma ou mais variantes de modelo (tecnicamente, v1.MetaGraphDef s), identificadas por seus conjuntos de tags. Para servir um modelo, você pode se perguntar que tipo de SignatureDef s são em cada variante de modelo e quais são suas entradas e saídas. O comando show permite examinar o conteúdo do SavedModel em ordem hierárquica. Aqui está a sintaxe:

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

Por exemplo, o comando a seguir mostra todos os conjuntos de tags disponíveis no SavedModel:

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

O comando a seguir mostra todas as chaves SignatureDef disponíveis para um conjunto de tags:

 $ 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"
 

Se houver várias tags no conjunto de tags, você deverá especificar todas as tags, cada uma separada por uma vírgula. Por exemplo:

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

Para mostrar todas as entradas e saídas TensorInfo para um SignatureDef específico, passe a chave SignatureDef para a opção signature_def . Isso é muito útil quando você deseja conhecer o valor da chave do tensor, o tipo e o formato dos tensores de entrada para executar o gráfico de computação posteriormente. Por exemplo:

 $ 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 todas as informações disponíveis no SavedModel, use a opção --all . Por exemplo:

$ 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 de run

Chame o comando run para executar uma computação gráfica, passando entradas e, em seguida, exibindo (e, opcionalmente, salvando) as saídas. Aqui está a sintaxe:

 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]
 

O comando run fornece as três maneiras a seguir para transmitir entradas para o modelo:

  • --inputs opção --inputs permite passar ndarray numpy em arquivos.
  • --input_exprs opção --input_exprs permite passar expressões Python.
  • --input_examples opção --input_examples permite passar tf.train.Example .

--inputs

Para passar dados de entrada em arquivos, especifique a opção --inputs , que assume o seguinte formato geral:

 --inputs <INPUTS>
 

em que INPUTS é um dos seguintes formatos:

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

Você pode passar várias entradas . Se você passar várias entradas, use um ponto-e-vírgula para separar cada uma das ENTRADAS .

saved_model_cli usa numpy.load para carregar o nome do arquivo . O nome do arquivo pode estar em qualquer um dos seguintes formatos:

  • .npy
  • .npz
  • formato de picles

Um arquivo .npy sempre contém um ndarray numpy. Portanto, ao carregar a partir de um arquivo .npy , o conteúdo será atribuído diretamente ao tensor de entrada especificado. Se você especificar um nome de variável com esse arquivo .npy , o nome da variável será ignorado e um aviso será emitido.

Ao carregar a partir de um .npz (zip), você pode opcionalmente especificar um nome de variável para identificar a variável no arquivo zip a ser carregada para a chave tensor de entrada. Se você não especificar um nome de variável , a CLI SavedModel verificará se apenas um arquivo está incluído no arquivo zip e o carregará para a chave tensor de entrada especificada.

Ao carregar a partir de um arquivo de pickle, se nenhum nome de variable_name for especificado entre colchetes, o que estiver dentro do arquivo de pickle será passado para a chave tensora de entrada especificada. Caso contrário, a CLI SavedModel assumirá que um dicionário está armazenado no arquivo pickle e o valor correspondente ao variable_name será usado.

--input_exprs

Para passar entradas através de expressões Python, especifique a opção --input_exprs . Isso pode ser útil quando você não possui arquivos de dados por aí, mas ainda deseja verificar o modelo com algumas entradas simples que correspondem ao tipo e forma dos SignatureDef s do modelo. Por exemplo:

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

Além das expressões Python, você também pode passar funções numpy. Por exemplo:

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

(Observe que o módulo numpy já está disponível para você como np .)

--input_examples

Para passar tf.train.Example como entradas, especifique a opção --input_examples . Para cada chave de entrada, é necessária uma lista de dicionário, onde cada dicionário é uma instância de tf.train.Example . As chaves do dicionário são os recursos e os valores são as listas de valores para cada recurso. Por exemplo:

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

Salvar saída

Por padrão, a CLI SavedModel grava a saída no stdout. Se um diretório for passado para a opção --outdir , as saídas serão salvas como arquivos .npy nome das chaves .npy saída no diretório especificado.

Use --overwrite para substituir os arquivos de saída existentes.