Mais de um gráfico - reutilização de código

Grandes dutos de produção no TensorFlow são bastante difíceis de executar. O treinamento de modelos pequenos é fácil, e geralmente fazemos isso no início, mas assim que chegamos ao restante do pipeline, a complexidade aumenta rapidamente.

Um dos motivos é que a abstração do “Gráfico de Computação” usada pelo TensorFlow é uma correspondência aproximada, mas não exata, para o modelo de ML que esperamos treinar e usar. Como assim?

Normalmente, um modelo será usado de pelo menos três maneiras:

  • Treinamento - localizando os pesos ou parâmetros corretos para o modelo, dados alguns dados de treinamento. Geralmente feito periodicamente à medida que novos dados chegam.
  • Avaliação - calculando várias métricas durante o treinamento em um conjunto de dados diferente para avaliar a qualidade do treinamento ou para validação cruzada.
  • Exibição - previsão sob demanda para novos dados

Pode haver mais modos. Por exemplo, podemos treinar novamente um modelo existente ou aplicá-lo a uma grande quantidade de dados no modo em lote.

Embora o modelo conceitual seja o mesmo, esses casos de uso podem precisar de gráficos computacionais diferentes.
Por exemplo, se usarmos o TensorFlow Serving, não poderemos carregar modelos com operações da função Python.
Outro exemplo são as métricas de avaliação e as operações de depuração, como tf.Assert- talvez não desejemos executá-las ao servir por motivos de desempenho.

Acontece que precisamos de 3 a 5 gráficos diferentes para representar o nosso modelo. Existem algumas maneiras de fazer isso,  e escolher a correta não é fácil .

Não podemos simplesmente construir um gráfico e atualizá-lo à medida que avançamos?

Os gráficos do TensorFlow no Python são apenas anexados. As operações do TensorFlow implicitamente criam nós de gráfico e não há operações para remover nós. Mesmo se tentarmos substituir

 

O TensorFlow adicionará apenas um sufixo ao nome da operação:

O que está feito não pode ser desfeito, por assim dizer.

Então o que nós podemos fazer? Podemos tentar criar mais de um gráfico.

Criando vários gráficos com o mesmo código

Esse é o método que geralmente encontramos na documentação. Um exemplo de abandono pode ser assim:

O TensorFlow é fornecido com ferramentas como tf.variable_scopeessa, facilitando a criação de gráficos diferentes.

Essa abordagem tem uma grande desvantagem - o gráfico serializado não pode mais ser usado sem o código que o produziu. Mesmo uma pequena alteração (como alterar o nome de uma variável) interromperá o modelo em produção. Portanto, para reverter para uma versão mais antiga, também precisamos reverter para o código mais antigo. Isso nem sempre é prático com repositórios maiores e, em qualquer caso, requer algum esforço de operações.

Além disso, o treinamento e a avaliação não usam o mesmo gráfico (mesmo que compartilhem pesos) e exigem coordenação desajeitada para se mesclar.

Então, ainda queremos um gráfico, mas queremos usá-lo para treinamento e avaliação. E talvez servindo ... Mas definitivamente treinamento e avaliação.

Usando um gráfico com lógica condicional

O TensorFlow tem uma maneira de codificar comportamentos diferentes em um único gráfico - a tf.condoperação.

A grande vantagem é que agora temos toda a lógica em um gráfico, por exemplo, podemos vê-la no TensorBoard. Agora, nossos modelos serializados trabalham para treinamento e avaliação.
Existem duas fontes de complexidade que tornam a imagem menos positiva - preguiça e filas.

Preguiça e o operador condicional

Digamos que tenhamos uma operação cara que gostaríamos de executar apenas durante a avaliação. Se o colocarmos atrás de um operador condicional, esperamos que ele seja executado apenas no momento da avaliação. Isso também é verdade se alterarmos algo como resultado de uma condição como o caso da normalização de lote.

Nem sempre é assim que funciona no TF. A tf.condoperação é como uma caixa de preguiça, mas protege apenas o que está dentro.
Portanto, esse código funciona corretamente:

Mas isso irá interromper o abandono, mesmo que is_train == False.

 

Filas e o operador condicional

As filas são a maneira preferida (e com melhor desempenho) de obter dados no TensorFlow. Normalmente, o treinamento e a avaliação serão feitos simultaneamente em diferentes entradas, portanto, podemos tentar a abordagem acima para colocá-los no mesmo gráfico.

Não funciona no entanto. O TensorFlow nos informaria isso operation has been marked as not fetchablee travaria .

A questão é que o TensorFlow não nos permite enfileirar condicionalmente. No entanto, a shuffle_batchoperação cria a operação de fila e desenfileiramento juntas.
Para evitar isso, precisamos dividir a operação em uma parte condicional que cria a fila e a parte condicional que extrai da fila correta.

Aqui está um exemplo (o código completo é bastante longo, então deixei apenas as partes relevantes)

Portanto, podemos obter o que queremos com o operador condicional, mas o código é mais complexo e difícil de entender. Porém, as operações devem ser mais fáceis - temos gráficos e monitoramento serializados simples.

Poderíamos evitar completamente as condições e, de alguma forma, contornar a limitação somente de acréscimo?

Trabalhando com gráficos salvos

A maioria dos pipelines serializa gráficos, mesmo que apenas para veiculação. Uma coisa muito importante que podemos fazer com o modelo é servir representações serializadas. O serviço TensorFlow está fora do escopo desta publicação, mas a idéia geral é que, para obter um servidor completo, precisamos apenas executar:

A execução de nosso gráfico de treinamento no TensorFlow Serving não é a melhor idéia, no entanto. O desempenho é prejudicado pela execução de operações desnecessárias e as tf.py_funcoperações não podem ser carregadas pelo servidor.
Felizmente, o gráfico serializado não é como o único anexo que tínhamos quando começamos. É apenas um monte de objetos Protobuf para que possamos criar novas versões. Como exemplo, abaixo está uma versão simplificada e anotada da convert_variables_to_constantsfunção em graph_util_impl.py  que (sem surpresa) converte variáveis ​​em constantes. É útil porque isso pode ser mais rápido ao veicular em alguns casos.