Formato TF1 Hub

Em seu lançamento em 2018, o TensorFlow Hub oferecia um único tipo de ativo: formato TF1 Hub para importação em programas TensorFlow 1.

Esta página explica como usar o formato TF1 Hub no TF1 (ou o modo de compatibilidade TF1 do TF2) com a classe hub.Module e APIs associadas. (O uso típico é construir um tf.Graph , possivelmente dentro de um TF1 Estimator , combinando um ou mais modelos no formato TF1 Hub com tf.compat.layers ou tf.layers ).

Os usuários do TensorFlow 2 (fora do modo de compatibilidade TF1) devem usar a nova API com hub.load() ou hub.KerasLayer . A nova API carrega o novo tipo de ativo TF2 SavedModel, mas também tem suporte limitado para carregar o formato TF1 Hub no TF2 .

Usando um modelo no formato TF1 Hub

Instanciando um modelo no formato TF1 Hub

Um modelo no formato TF1 Hub é importado para um programa TensorFlow criando um objeto hub.Module a partir de uma string com seu URL ou caminho do sistema de arquivos, como:

m = hub.Module("path/to/a/module_dir")

Isso adiciona as variáveis ​​do módulo ao gráfico atual do TensorFlow. A execução de seus inicializadores lerá seus valores pré-treinados do disco. Da mesma forma, tabelas e outros estados são adicionados ao gráfico.

Módulos de cache

Ao criar um módulo a partir de uma URL, o conteúdo do módulo é baixado e armazenado em cache no diretório temporário do sistema local. O local onde os módulos são armazenados em cache pode ser substituído usando a variável de ambiente TFHUB_CACHE_DIR . Para obter detalhes, consulte Cache .

Aplicando um módulo

Uma vez instanciado, um módulo m pode ser chamado zero ou mais vezes como uma função Python de entradas de tensor para saídas de tensor:

y = m(x)

Cada chamada adiciona operações ao gráfico TensorFlow atual para calcular y de x . Se isso envolver variáveis ​​com pesos treinados, elas serão compartilhadas entre todos os aplicativos.

Os módulos podem definir várias assinaturas nomeadas para permitir a aplicação de mais de uma maneira (semelhante a como os objetos Python têm métodos ). A documentação de um módulo deve descrever as assinaturas disponíveis. A chamada acima aplica a assinatura chamada "default" . Qualquer assinatura pode ser selecionada passando seu nome para o argumento opcional signature= .

Se uma assinatura tiver várias entradas, elas devem ser passadas como um dict, com as chaves definidas pela assinatura. Da mesma forma, se uma assinatura tiver várias saídas, elas podem ser recuperadas como um dict passando as_dict=True , sob as chaves definidas pela assinatura (a chave "default" é para a saída única retornada se as_dict=False ). Portanto, a forma mais geral de aplicar um Módulo se parece com:

outputs = m(dict(apples=x1, oranges=x2), signature="fruit_to_pet", as_dict=True)
y1 = outputs["cats"]
y2 = outputs["dogs"]

Um chamador deve fornecer todas as entradas definidas por uma assinatura, mas não há necessidade de usar todas as saídas de um módulo. O TensorFlow executará apenas as partes do módulo que terminarem como dependências de um destino em tf.Session.run() . De fato, os editores de módulos podem optar por fornecer várias saídas para usos avançados (como ativações de camadas intermediárias) juntamente com as saídas principais. Os consumidores de módulo devem lidar com saídas adicionais com facilidade.

Experimentando módulos alternativos

Sempre que houver vários módulos para a mesma tarefa, o TensorFlow Hub incentiva a equipá-los com assinaturas (interfaces) compatíveis, de modo que tentar diferentes módulos seja tão fácil quanto variar o identificador do módulo como um hiperparâmetro com valor de string.

Para isso, mantemos uma coleção de assinaturas comuns recomendadas para tarefas populares.

Criando um novo módulo

Nota de compatibilidade

O formato TF1 Hub é voltado para o TensorFlow 1. Ele é apenas parcialmente compatível com o TF Hub no TensorFlow 2. Em vez disso, considere publicar no novo formato TF2 SavedModel .

O formato TF1 Hub é semelhante ao formato SavedModel do TensorFlow 1 em um nível sintático (mesmos nomes de arquivo e mensagens de protocolo), mas semanticamente diferente para permitir a reutilização, composição e retreinamento do módulo (por exemplo, armazenamento diferente de inicializadores de recursos, marcação diferente convenções para metagrafias). A maneira mais fácil de diferenciá-los no disco é a presença ou ausência do arquivo tfhub_module.pb .

Abordagem geral

Para definir um novo módulo, um publicador chama hub.create_module_spec() com uma função module_fn . Esta função constrói um gráfico representando a estrutura interna do módulo, usando tf.placeholder() para entradas a serem fornecidas pelo chamador. Em seguida, ele define assinaturas chamando hub.add_signature(name, inputs, outputs) uma ou mais vezes.

Por exemplo:

def module_fn():
  inputs = tf.placeholder(dtype=tf.float32, shape=[None, 50])
  layer1 = tf.layers.dense(inputs, 200)
  layer2 = tf.layers.dense(layer1, 100)
  outputs = dict(default=layer2, hidden_activations=layer1)
  # Add default signature.
  hub.add_signature(inputs=inputs, outputs=outputs)

...
spec = hub.create_module_spec(module_fn)

O resultado de hub.create_module_spec() pode ser usado, em vez de um caminho, para instanciar um objeto de módulo em um gráfico específico do TensorFlow. Nesse caso, não há ponto de verificação e a instância do módulo usará os inicializadores de variáveis.

Qualquer instância de módulo pode ser serializada em disco por meio de seu método export(path, session) . A exportação de um módulo serializa sua definição junto com o estado atual de suas variáveis ​​na session no caminho passado. Isso pode ser usado ao exportar um módulo pela primeira vez, bem como ao exportar um módulo ajustado.

Para compatibilidade com TensorFlow Estimators, hub.LatestModuleExporter exporta módulos do último checkpoint, assim como tf.estimator.LatestExporter exporta todo o modelo do último checkpoint.

Os editores de módulos devem implementar uma assinatura comum quando possível, para que os consumidores possam facilmente trocar módulos e encontrar o melhor para seu problema.

Exemplo real

Dê uma olhada em nosso exportador de módulos de incorporação de texto para obter um exemplo real de como criar um módulo a partir de um formato de incorporação de texto comum.

Afinação

Treinar as variáveis ​​de um módulo importado junto com as do modelo ao seu redor é chamado de ajuste fino . O ajuste fino pode resultar em melhor qualidade, mas adiciona novas complicações. Aconselhamos os consumidores a analisar o ajuste fino somente depois de explorar ajustes de qualidade mais simples e somente se o editor do módulo o recomendar.

Para consumidores

Para habilitar o ajuste fino, instancie o módulo com hub.Module(..., trainable=True) para tornar suas variáveis ​​treináveis ​​e importe REGULARIZATION_LOSSES do TensorFlow. Se o módulo tiver várias variantes de gráfico, certifique-se de escolher a apropriada para o treinamento. Normalmente, é aquele com tags {"train"} .

Escolha um regime de treinamento que não estrague os pesos pré-treinados, por exemplo, uma taxa de aprendizado menor do que para treinar do zero.

Para editores

Para facilitar o ajuste fino para os consumidores, lembre-se do seguinte:

  • O ajuste fino precisa de regularização. Seu módulo é exportado com a coleção REGULARIZATION_LOSSES , que é o que coloca sua escolha de tf.layers.dense(..., kernel_regularizer=...) etc. no que o consumidor obtém de tf.losses.get_regularization_losses() . Prefira esta forma de definir as perdas de regularização L1/L2.

  • No modelo do editor, evite definir a regularização L1/L2 por meio dos parâmetros l1_ e l2_regularization_strength de tf.train.FtrlOptimizer , tf.train.ProximalGradientDescentOptimizer e outros otimizadores proximais. Eles não são exportados juntamente com o módulo, e definir os pontos fortes de regularização globalmente pode não ser apropriado para o consumidor. Exceto para regularização L1 em ​​modelos amplos (ou seja, lineares esparsos) ou amplos e profundos, deve ser possível usar perdas de regularização individuais.

  • Se você usar dropout, normalização em lote ou técnicas de treinamento semelhantes, defina seus hiperparâmetros para valores que façam sentido em muitos usos esperados. A taxa de abandono pode ter que ser ajustada à propensão do problema-alvo ao overfitting. Na normalização em lote, o momento (também conhecido como coeficiente de decaimento) deve ser pequeno o suficiente para permitir o ajuste fino com pequenos conjuntos de dados e/ou grandes lotes. Para consumidores avançados, considere adicionar uma assinatura que exponha o controle sobre hiperparâmetros críticos.