Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
tensorflow
GitHub Repository: tensorflow/docs-l10n
Path: blob/master/site/pt-br/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.

Um SavedModel contém um programa TensorFlow completo, incluindo parâmetros treinados (ou seja, objetos tf.Variable) e computação. Ele não requer a execução do código de construção do modelo original, o que o torna útil para compartilhamento ou implantação com TFLite, TensorFlow.js, TensorFlow Serving ou TensorFlow Hub.

Você pode salvar e carregar um modelo no formato SavedModel usando as seguintes APIs:

  • API tf.saved_model de baixo nível. Este documento descreve detalhadamente como usar esta API.

    • Salvar: tf.saved_model.save(model, path_to_dir)

    • Carregar: model = tf.saved_model.load(path_to_dir)

  • API tf.keras.Model de alto nível. Consulte o guia de salvamento e serialização do keras.

  • Se quiser apenas salvar/carregar pesos durante o treino, consulte o guia de checkpoints.

Atenção: os modelos do TensorFlow são códigos, e é importante ter cuidado com código não confiável. Saiba mais em Como usar o TensorFlow com segurança.

Criando um SavedModel a partir do Keras

Obsoleto: para objetos do Keras, recomenda-se usar o novo formato de alto nível .keras e tf.keras.Model.export, conforme demonstrado neste guia. O formato de baixo nível SavedModel continua com suporte para códigos existentes.

Para uma introdução rápida, esta seção exporta um modelo Keras pré-treinado e atende solicitações de classificação de imagem com ele. O restante do guia preencherá detalhes e discutirá outras maneiras de criar 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,...])

Você usará uma imagem de Grace Hopper como exemplo em execução e um modelo de classificação de imagens pré-treinado do Keras, pois é mais fácil de usar. Modelos personalizados também funcionam e serão abordados em detalhes 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())
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)

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)

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

Você pode carregar o SavedModel de volta no Python com tf.saved_model.load e ver como a imagem do Admiral Hopper é classificada.

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

Assinaturas importadas sempre retornam dicionários. Para personalizar nomes de assinaturas e chaves de dicionários de saída, veja Especificando assinaturas durante a exportação.

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

Executar a inferência 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)

Executando um SavedModel no TensorFlow Serving

Os SavedModels podem ser usados ​​a partir do Python (mais sobre isso abaixo), mas ambientes de produção normalmente usam um serviço dedicado para inferência sem executar código Python. Isto é fácil de configurar a partir de um SavedModel usando o TensorFlow Serving.

Consulte o Tutorial REST do TensorFlow Serving para um exemplo completo com o tensorflow-serving.

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 de variáveis ​​e vocabulários.

!ls {mobilenet_save_path}

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

Os SavedModels podem conter múltiplas variantes do modelo (múltiplas v1.MetaGraphDefs, identificadas com o sinalizador --tag_set para saved_model_cli), mas isto é raro. APIs que criam múltiplas 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

O diretório variables ​​contém um checkpoint de treinamento padrão (veja o guia de checkpoints de treinamento).

!ls {mobilenet_save_path}/variables

O diretório assets contém arquivos usados ​​pelo grafo do 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 quaisquer arquivos não usados ​​pelo grafo do TensorFlow, por exemplo, informações para consumidores sobre o que fazer com o SavedModel. O próprio TensorFlow não usa esse diretório.

O arquivo fingerprint.pb contém a impressão digital do SavedModel, que é composta por vários hashes de 64 bits que identificam exclusivamente o conteúdo do SavedModel. A API de impressão digital é atualmente experimental, mas tf.saved_model.experimental.read_fingerprint pode ser usado para ler a impressão digital SavedModel num objeto tf.saved_model.experimental.Fingerprint.

Salvando um modelo personalizado

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 como salvar e restaurar 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, quaisquer atributos tf.Variable, métodos decorados tf.function e tf.Module encontrados via travessia recursiva são salvos. (Consulte o tutorial sobre checkpoints para saber mais sobre essa travessia recursiva.) No entanto, quaisquer atributos, funções e dados do Python são perdidos. Isto significa que quando uma tf.function é salva, nenhum código Python é salvo.

Se nenhum código Python for salvo, como o SavedModel saberá restaurar a função?

Resumidamente, tf.function funciona rastreando o código Python para gerar um ConcreteFunction (um wrapper em torno de tf.Graph que pode ser chamado). Ao salvar um tf.function, você está na verdade salvando o cache tf.function de ConcreteFunctions.

Para saber mais sobre o relacionamento entre tf.function e ConcreteFunctions, veja o guia 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)

Carregando e usando um modelo personalizado

Quando você carrega um SavedModel em Python, todos os atributos de tf.Variable, métodos decorados com tf.function e tf.Module são restaurados na mesma estrutura de objeto do tf.Module salvo originalmente.

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, a chamada de tf.function com uma nova assinatura de entrada falhará:

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'),), {})].

Tuning básico

Objetos variáveis ​​estão disponíveis e você pode fazer backprop através de funções importadas. Isto é suficiente para fazer um ajuste fino (ou seja, treinar novamente) um SavedModel em 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()))

Tuning geral

Um SavedModel da Keras fornece mais detalhes do que uma simples __call__ para abordar casos mais avançados de ajuste fino. O TensorFlow Hub recomenda fornecer o seguinte, se aplicável, em SavedModels compartilhados para fins de tuning:

  • Se o modelo usar dropout ou outra técnica na qual o passo para frente difere no treinamento e na inferência (como normalização em lote), o método __call__ usará um argumento training= opcional com valor Python cujo padrão é False, mas pode ser definido como True.

  • Depois do atributo __call__, existem os atributos .variable e .trainable_variable com listas de variáveis ​​correspondentes. Uma variável que era originalmente treinável, mas que deveria ser congelada durante o ajuste fino, é omitida de .trainable_variables.

  • Para o bem de estruturas como Keras, que representam regularizadores de peso como atributos de camadas ou submodelos, também poderá haver um atributo .regularization_losses. Ele contém uma lista de funções com argumento zero cujos valores devem ser adicionados à perda total.

Voltando ao exemplo inicial do MobileNet, você poderá ver alguns desses 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]])))
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]])))

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 ConcreteFunctions usar, você precisa especificar assinaturas de serviço. Os tf.keras.Model especificam automaticamente assinaturas de serviço, mas você terá que declarar explicitamente uma assinatura de serviço para nossos módulos personalizados.

IMPORTANTE: a menos que você precise exportar seu modelo para um ambiente diferente do TensorFlow 2.x com Python, provavelmente não será necessário exportar assinaturas explicitamente. Se você está procurando uma maneira de impor uma assinatura de entrada para uma função específica, veja o argumento input_signature para tf.function.

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

assert len(imported.signatures) == 0

Para declarar uma assinatura de serviço, especifique uma ConcreteFunction usando o kwarg signatures. 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)
imported_with_signatures = tf.saved_model.load(module_with_signature_path) list(imported_with_signatures.signatures.keys())

Para exportar múltiplas assinaturas, passe um dicionário de chaves de assinatura para ConcreteFunctions. Cada chave de assinatura corresponde a uma ConcreteFunction.

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 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 seu tf.function para retornar um dicionário que mapeia nomes de saída para saídas. Os nomes das entradas são derivados dos nomes dos 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(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

Divisão de arquivos proto

Observação: este recurso fará parte da versão 2.15 do TensorFlow. No momento, ele está disponível em build noturno, que você pode instalar com pip install tf-nightly.

Devido aos limites da implementação do protobuf, os tamanhos do proto não podem ultrapassar 2 GB. Isso pode levar aos seguintes erros ao tentar salvar modelos muito 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'

Se você quiser salvar modelos que ultrapassam o limite de 2 GB, será necessário salvar usando a nova opção de divisão de proto:

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

Encontre mais informações no guia da biblioteca de divisão / fusão de proto.

Carregue um SavedModel em C++

A versão C++ do carregador do SavedModel fornece uma API para carregar um SavedModel de um caminho, enquanto permite SessionOptions e RunOptions. Você deve especificar as tags associadas ao grafo a ser carregado. A versão carregada do SavedModel é chamada de 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}, &amp;bundle);

Detalhes da interface de linha de comando SavedModel

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

Instale a SavedModel CLI

Em termos gerais, você pode instalar o TensorFlow de uma das duas maneiras a seguir:

  • Instalando um binário TensorFlow pré-compilado.

  • Compilando o TensorFlow a partir do código-fonte.

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

Se você compilou o TensorFlow a partir do código-fonte, deverá executar o seguinte comando adicional para compilar o saved_model_cli:

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

Visão geral dos comandos

A CLI do SavedModel oferece suporte aos dois comandos a seguir num SavedModel:

  • show, que mostra as computações disponíveis num SavedModel.

  • run, que executa uma computação em um SavedModel.

Comando show

Um SavedModel contém uma ou mais variantes de modelos (tecnicamente, v1.MetaGraphDef), identificadas por seus conjuntos de tags. Para servir um modelo, você pode se perguntar que tipo de SignatureDef existem 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 múltiplos tags no conjunto de tags, você deverá especificar todas as tags, cada tag 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 do TensorInfo para um SignatureDef específico, passe a chave SignatureDef para a opção signature_def. Isso é muito útil quando você deseja saber o valor da chave do tensor, o dtype e formato dos tensores de entrada para executar o grafo 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 run

Chame o comando run para executar uma computação do grafo, passando entradas e 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 três maneiras de passar entradas para o modelo, mostradas a seguir:

  • A opção --inputs permite que você passe numpy ndarray em arquivos.

  • A opção --input_exprs permite que você passe expressões Python.

  • A opção --input_examples permite que você passe tf.train.Example.

--inputs

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

--inputs <INPUTS>

onde INPUTS é um dos seguintes formatos:

  • <input_key>=<filename>

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

Você pode passar múltiplos INPUTS. Se você fizer isso, use ponto e vírgula para separar cada uma das INPUTS.

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

  • .npy

  • .npz

  • formato pickle

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

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

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

--input_exprs

Para passar entradas via expressões Python, especifique a opção --input_exprs. Isto pode ser útil quando você não tem arquivos de dados disponíveis, mas ainda deseja verificar a integridade do modelo com algumas entradas simples que correspondem ao dtype e ao formato dos SignatureDef 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 objetos tf.train.Example como entradas, especifique a opção --input_examples. Para cada chave de entrada, é necessária uma lista de dicionários, onde cada dicionário é uma instância de tf.train.Example. As chaves do dicionário são as características e os valores são as listas de valores de cada característica. Por exemplo:

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

Salvando a saída

Por padrão, a CLI do SavedModel grava a saída em stdout. Se um diretório for passado para a opção --outdir, as saídas serão salvas como arquivos .npy nomeados com base nas chaves do tensor de saída no diretório fornecido.

Use --overwrite para sobrescrever arquivos de saída existentes.