Modelos salvos reutilizáveis

Introdução

O TensorFlow Hub hospeda SavedModels para TensorFlow 2, entre outros ativos. Eles podem ser carregados de volta em um programa Python com obj = hub.load(url) [ saiba mais ]. O obj retornado é o resultado de tf.saved_model.load() (consulte o guia SavedModel do TensorFlow). Este objeto pode ter atributos arbitrários que são tf.functions, tf.Variables (inicializados a partir de seus valores pré-treinados), outros recursos e, recursivamente, mais objetos desse tipo.

Esta página descreve uma interface a ser implementada pelo obj carregado para ser reutilizada em um programa TensorFlow Python. SavedModels em conformidade com esta interface são chamados de SavedModels reutilizáveis .

Reutilizar significa construir um modelo maior em torno obj , incluindo a capacidade de ajustá-lo. O ajuste fino significa treinamento adicional dos pesos no obj carregado como parte do modelo circundante. A função de perda e o otimizador são determinados pelo modelo circundante; obj define apenas o mapeamento de ativações de entrada para saída (a "passagem direta"), possivelmente incluindo técnicas como abandono ou normalização em lote.

A equipe do TensorFlow Hub recomenda implementar a interface Reusable SavedModel em todos os SavedModels que devem ser reutilizados no sentido acima. Muitos utilitários da biblioteca tensorflow_hub , principalmente hub.KerasLayer , exigem SavedModels para implementá-lo.

Relação com SignatureDefs

Esta interface em termos de tf.functions e outros recursos do TF2 é separada das assinaturas do SavedModel, que estão disponíveis desde o TF1 e continuam a ser usadas no TF2 para inferência (como implantação de SavedModels no TF Serving ou TF Lite). As assinaturas para inferência não são expressivas o suficiente para suportar o ajuste fino, e tf.function fornece uma API Python mais natural e expressiva para o modelo reutilizado.

Relação com bibliotecas de construção de modelos

Um SavedModel reutilizável usa apenas primitivos do TensorFlow 2, independentemente de qualquer biblioteca específica de construção de modelo, como Keras ou Sonnet. Isso facilita a reutilização em bibliotecas de construção de modelos, livres de dependências do código original de construção de modelos.

Será necessária alguma adaptação para carregar SavedModels reutilizáveis ​​ou salvá-los de qualquer biblioteca de construção de modelo. Para Keras, hub.KerasLayer fornece o carregamento, e o salvamento integrado de Keras no formato SavedModel foi redesenhado para TF2 com o objetivo de fornecer um superconjunto dessa interface (consulte a RFC de maio de 2019).

Relação com "APIs SavedModel comuns" específicas da tarefa

A definição de interface nesta página permite qualquer número e tipo de entradas e saídas. As APIs Common SavedModel para TF Hub refinam essa interface geral com convenções de uso para tarefas específicas para tornar os modelos facilmente intercambiáveis.

Definição de interface

Atributos

Um SavedModel reutilizável é um SavedModel do TensorFlow 2 tal que obj = tf.saved_model.load(...) retorna um objeto que possui os seguintes atributos

  • __call__ . Obrigatório. Uma tf.function que implementa o cálculo do modelo (o "passe para frente") sujeito à especificação abaixo.

  • variables : uma lista de objetos tf.Variable, listando todas as variáveis ​​usadas por qualquer invocação possível de __call__ , incluindo aquelas treináveis ​​e não treináveis.

    Esta lista pode ser omitida se estiver vazia.

  • trainable_variables : uma lista de objetos tf.Variable tais que v.trainable é verdadeiro para todos os elementos. Essas variáveis ​​devem ser um subconjunto de variables . Estas são as variáveis ​​a serem treinadas ao ajustar o objeto. O criador do SavedModel pode optar por omitir aqui algumas variáveis ​​que eram originalmente treináveis ​​para indicar que elas não devem ser modificadas durante o ajuste fino.

    Esta lista pode ser omitida se estiver vazia, em particular, se o SavedModel não suportar ajuste fino.

  • regularization_losses : uma lista de tf.functions, cada uma recebendo zero entradas e retornando um único tensor flutuante escalar. Para ajuste fino, o usuário do SavedModel é aconselhado a incluí-los como termos de regularização adicionais na perda (no caso mais simples, sem dimensionamento adicional). Normalmente, eles são usados ​​para representar regularizadores de peso. (Por falta de entradas, essas tf.functions não podem expressar regularizadores de atividade.)

    Esta lista pode ser omitida se estiver vazia, em particular, se o SavedModel não suportar ajuste fino ou não desejar prescrever regularização de peso.

A função __call__

Um obj SavedModel restaurado possui um atributo obj.__call__ que é um tf.function restaurado e permite que obj seja chamado da seguinte maneira.

Sinopse (pseudocódigo):

outputs = obj(inputs, trainable=..., **kwargs)

Argumentos

Os argumentos são os seguintes.

  • Há um argumento posicional obrigatório com um lote de ativações de entrada do SavedModel. Seu tipo é um dos

    • um único Tensor para uma única entrada,
    • uma lista de tensores para uma sequência ordenada de entradas não nomeadas,
    • um ditado de tensores codificados por um conjunto específico de nomes de entrada.

    (Revisões futuras desta interface podem permitir ninhos mais gerais.) O criador do SavedModel escolhe um deles e as formas e dtypes do tensor. Quando for útil, algumas dimensões da forma deverão ser indefinidas (principalmente o tamanho do lote).

  • Pode haver um training opcional de argumento de palavra-chave que aceita um booleano Python, True ou False . O padrão é False . Se o modelo suportar ajuste fino e se seu cálculo diferir entre os dois (por exemplo, como no abandono e na normalização em lote), essa distinção será implementada com este argumento. Caso contrário, este argumento pode estar ausente.

    Não é necessário que __call__ aceite um argumento training com valor de Tensor. Cabe ao chamador usar tf.cond() se necessário para despachar entre eles.

  • O criador do SavedModel pode optar por aceitar mais kwargs opcionais de nomes específicos.

    • Para argumentos com valor de tensor, o criador do SavedModel define seus tipos e formas permitidos. tf.function aceita um valor padrão do Python em um argumento que é rastreado com uma entrada tf.TensorSpec. Tais argumentos podem ser usados ​​para permitir a customização de hiperparâmetros numéricos envolvidos em __call__ (por exemplo, taxa de abandono).

    • Para argumentos com valor Python, o criador do SavedModel define seus valores permitidos. Tais argumentos podem ser usados ​​como sinalizadores para fazer escolhas discretas na função rastreada (mas lembre-se da explosão combinatória de traços).

A função __call__ restaurada deve fornecer rastreamentos para todas as combinações permitidas de argumentos. A inversão training entre True e False não deve alterar a permissibilidade dos argumentos.

Resultado

As outputs da chamada obj podem ser

  • um único Tensor para uma única saída,
  • uma lista de tensores para uma sequência ordenada de saídas sem nome,
  • um ditado de tensores codificados por um conjunto específico de nomes de saída.

(Revisões futuras desta interface podem permitir ninhos mais gerais.) O tipo de retorno pode variar dependendo dos kwargs com valor Python. Isso permite que sinalizadores produzam saídas extras. O criador do SavedModel define os tipos e formas de saída e sua dependência das entradas.

Callables nomeados

Um SavedModel reutilizável pode fornecer várias peças do modelo da maneira descrita acima, colocando-as em subobjetos nomeados, por exemplo, obj.foo , obj.bar e assim por diante. Cada subobjeto fornece um método __call__ e atributos de suporte sobre as variáveis, etc., específicos para aquela peça do modelo. Para o exemplo acima, haveria obj.foo.__call__ , obj.foo.variables e assim por diante.

Observe que esta interface não cobre a abordagem de adicionar um tf.function simples diretamente como tf.foo .

Espera-se que os usuários de SavedModels reutilizáveis ​​lidem apenas com um nível de aninhamento ( obj.bar mas não obj.bar.baz ). (As futuras revisões desta interface podem permitir um aninhamento mais profundo e podem dispensar o requisito de que o próprio objeto de nível superior possa ser chamado.)

Observações finais

Relação com APIs em processo

Este documento descreve uma interface de uma classe Python que consiste em primitivas como tf.function e tf.Variable que sobrevivem a uma viagem de ida e volta através da serialização via tf.saved_model.save() e tf.saved_model.load() . Porém, a interface já estava presente no objeto original que foi passado para tf.saved_model.save() . A adaptação a essa interface permite a troca de peças de modelo entre APIs de construção de modelo em um único programa TensorFlow.