No seu lançamento em 2018, o TensorFlow Hub ofereceu um único tipo de ativo: formato TF1 Hub para importação para 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")
Nota: Veja mais informações sobre outros tipos de identificador válidos aqui .
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, das entradas do tensor às saídas do tensor:
y = m(x)
Cada chamada adiciona operações ao gráfico atual do TensorFlow para calcular y
de x
. Se isso envolver variáveis com pesos treinados, estes serão compartilhados entre todas as aplicações.
Os módulos podem definir múltiplas assinaturas nomeadas para permitir a aplicação de mais de uma maneira (semelhante à forma como os objetos Python possuem 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 múltiplas entradas, elas deverão ser passadas como um ditado, com as chaves definidas pela assinatura. Da mesma forma, se uma assinatura tiver múltiplas saídas, estas podem ser recuperadas como um dict passando as_dict=True
, sob as chaves definidas pela assinatura (a chave "default"
é para a única saída retornada se as_dict=False
). Portanto, a forma mais geral de aplicação de um Módulo é:
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á nenhum requisito para usar todas as saídas de um módulo. O TensorFlow executará apenas as partes do módulo que terminam como dependências de um destino em tf.Session.run()
. Na verdade, os editores de módulos podem optar por fornecer vários resultados para usos avançados (como ativações de camadas intermediárias), juntamente com os resultados principais. Os consumidores do módulo devem lidar com saídas adicionais normalmente.
Experimentando módulos alternativos
Sempre que houver vários módulos para a mesma tarefa, o TensorFlow Hub incentiva equipá-los com assinaturas (interfaces) compatíveis, de modo que tentar diferentes seja tão fácil quanto variar o identificador do módulo como um hiperparâmetro com valor de string.
Para esse fim, 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 TensorFlow 1. Ele é apenas parcialmente suportado pelo TF Hub no TensorFlow 2. 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 arquivos e mensagens de protocolo), mas semanticamente diferente para permitir a reutilização, composição e novo treinamento do módulo (por exemplo, armazenamento diferente de inicializadores de recursos, marcação diferente convenções para metagráficos). 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 editor 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)
. Exportar um módulo serializa sua definição junto com o estado atual de suas variáveis na session
no caminho passado. Isto 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 ponto de verificação mais recente, assim como tf.estimator.LatestExporter
exporta o modelo inteiro do ponto de verificação mais recente.
Os editores de módulos devem implementar uma assinatura comum sempre que possível, para que os consumidores possam facilmente trocar módulos e encontrar o melhor para o seu problema.
Exemplo real
Dê uma olhada em nosso exportador de módulo 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
O treinamento das 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 acrescenta novas complicações. Aconselhamos os consumidores a fazer o ajuste fino somente depois de explorar ajustes de qualidade mais simples e somente se o editor do módulo recomendar isso.
Para consumidores
Para permitir 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 diversas variantes de gráfico, certifique-se de escolher aquela apropriada para o treinamento. Normalmente, é aquele com tags {"train"}
.
Escolha um regime de treino que não estrague os pesos pré-treinados, por exemplo, uma taxa de aprendizagem 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 detf.layers.dense(..., kernel_regularizer=...)
etc. no que o consumidor obtém detf.losses.get_regularization_losses()
. Prefira esta forma de definir perdas de regularização L1/L2.No modelo do editor, evite definir a regularização L1/L2 por meio dos parâmetros
l1_
el2_regularization_strength
detf.train.FtrlOptimizer
,tf.train.ProximalGradientDescentOptimizer
e outros otimizadores proximais. Estes não são exportados juntamente com o módulo, e definir 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 com 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 lotes grandes. Para consumidores avançados, considere adicionar uma assinatura que exponha o controle sobre hiperparâmetros críticos.