TensorFlow 2.0: melhores práticas e o que mudou
Confira as principais novidades na famosa biblioteca de Machine Learning da Google
Trabalho com o TensorFlow (TF) desde 2017. Acho que a primeira versão que eu mexi foi a v1.4. Lembro que apanhei bastante no começo por conta do novo “paradigma” introduzido pelo Tensorflow, como a construção de um grafo de operações por onde os dados fluiam, sessões, etc… Desde então, venho acompanhando os lançamentos das novas versões e vendo a tentativa do Google e da comunidade de tornar o TensorFlow mais user-friendly.
Recentemente, o TensorFlow 2.0 foi anunciado e com eles muitas novidades chegaram! Nesse artigo, eu vou tentar resumir essas principais mudanças e explicar as melhores e novas práticas de programação.
Resumo das principais mudanças
Muitas mudanças foram aplicadas no TensorFlow 2.0 para tornar os desenvolvedores mais produtivos, como:
Construção dos Modelos mais Fácil
O Keras é o framework mais utilizado pelos iniciantes na área justamente por sua facilidade de uso e rápida prototipagem. Para quem não conhece o Keras, ele é apenas uma camada de alto-nível para outros framework de Deep Learning, como TensorFlow, Theano e CNTK. No TensorFlow 2.0, o Keras foi “incorporado” ao TensorFlow através do módulo tf.keras
.
Com isso, toda a API do Keras (como Sequential, Functional e Subclassing; além dos métodos clássicos como
fit
,predict
,compile
, etc) também está presente no TF.
Agora, construir modelos no TensorFlow fica muito mais fácil e intuitivo!
Limpeza das APIs
Antigamente, o TF sofria com a quantidade de código duplicado, principalmente no módulo contrib, mantido pela comunidade em sua maioria. Era comum ter a mesma função de ativação, por exemplo, implementada em diversos lugares diferentes. Agora, muitas das API foram removidas (especialmente a tf.contrib
) ou movidas para outro lugares, enquanto outras foram trocadas pela sua versão equivalente do TF 2.0 (tf.summary
, tf.keras.metrics
, etf.keras.optimizer)
. Se você quiser converter seu código antigo pro novo, basta usar esse script.
Eager Execution
A Eager Execution, lançada com a v1.7 do TF, facilitou muito o desenvolvimento com TensorFlow, permitindo uma interface mais intuitiva, facilitando o debug e simplificando a especificação de modelos dinâmicos. Entretanto, a Eager Execution devia ser ativada manualmente e você ainda dependendia de uma função para transformar seu grafo da Eager Execution em um grafo executável pelo TF.
Agora, a Eager Execution torna-se o modo padrão de execução e a construção de grafos e sessões se tornam mais intuitivas e “pythonicas”.
O Fim das Variáveis Globais
O TensorFlow 1.X dependia fortemente de namespaces globais. Quando você chamava uma tf.Variable()
, por exemplo, ela seria colocada no grafo default e continuaria lá mesmo se você perdesse a referência Python que apontava para ela. Você até conseguia recuperá-la, mas só se você soubesse o nome daquela variável, o que era difícil de saber caso você não estivesse no controle das criações das variáveis. Por conta disso, um monte de mecanismos foram criados para tentar ajudar usuários nessa tarefa.
TensorFlow 2.0 elimina todos esses mecanismos em favor de um mecanismo default: rastrei suas variáveis! Se você perder o rastro de uma
tf.Variable
, ela será apagada pelo garbage collector.
Funções, não sessões
Uma chamada à session.run()
funciona quase como uma chamada de função: você especifica as entradas e a função a ser chamada, e recebe de volta o conjunto de saídas.
No TensorFlow 2.0, você pode “decorar” uma função Python usando
tf.function()
para marcar uma função a ser compilada em tempo de execução (JIT compilation). Dessa forma, o TensorFlow vai rodá-la em um único grafo!
Isso dá ao TensorFlow alguns benefícios no modo grafo:
- Desempenho: a função marcada com
tf.function()
pode ser otimizada (node pruning, kernel fusion, etc.) - Portabilidade: a função pode ser exportada/reimportada (SavedModel 2.0 RFC), permitindo ao usuários reuso e compartilhamento de funções TensorFlow.
Mas se tf.function()
depende de um interpretador Python, como isso vai funcionar nas APIs mobile, C++ e JavaScript? Nesses casos, para evitar reescrita de código, a ferramenta AutoGraph vai automaticamente converter um subconjunto de Python constructs no seu equivalente TensorFlow.
Compatibilidade e Continuidade
Para simplificar a migração para o TensorFlow 2.0, existe um ferramenta de conversão que atualiza código do TF 1.x para TF 2.0. Note que nem todo código pode ser convertido automaticamente. Por exemplo, algumas APIs deprecated não têm sua equivalente direta. Por conta disso, ainda irá existir o módulo tensorflow.compat.v1
que manterá o suporte completo à versão 1.X, exceto tf.contrib
.
Além disso, SavedModels ou GraphDefs salvos serão compatíveis com a versão 1.X. Isto significa que SavedModels salvos com 1.x continuarão funcionando na 2.x. Entretanto, uma vez que os nomes das variáveis nos checkpoints podem ser diferentes na 2.0, não há garantia que checkpoints pré-2.0 com código convertido para 2.0 irá necessariamente funcionar.
Melhores Práticas para TensorFlow 2.0
Refatore o seu código em funções pequenas
O TensorFlow 1.X era baseado no padrão “Pia de cozinha”, ou seja, todos os cálculos possíveis era previamente carregados e, então, os tensores selecionados eram calculados via session.run()
.
No TensorFlow 2.0, a ideia é escrever o código em pequenas funções que serão chamadas quando necessário.
Em geral, isso não significa necessariamente “decorar” as funções com tf.function
; só use tf.function
em cálculos de alto-nível — por exemplo, um passo do treinamento ou a etapa feedforward da sua rede.
Use camadas Keras e modelos para gerenciar variáveis
Quem usa Keras, sabe que é bem mais fácil utilizá-lo para fazer o treinamento dos modelos. Muito disso vem do fato que o Keras é quem gerencia as trainable_variables
, e não você como no caso do TensorFlow.
No TensorFlow 2.0, os modelos/camadas do Keras serão integrados com @tf.function
, tornando possível fazer diretamente o checkpoint ou exportar SavedModels a partir de objetos Keras. E você não precisa necessariamente usar o fit()
do Keras para fazer isso.
Combine tf.data.Datasets e @tf.function
A tf.data.Dataset
é o melhor jeito de iterar sobre dados de treinamento que não cabem na memória. Datasets são iterables (e não iterators) e funcionam como qualquer outra iterables Python no mode Eager. Você pode utilizar totalmente os recursos de prefetching/streaming assíncronos usando o tf.function()
, que substitui as iterações Python com as operações de grafo equivalente usando AutoGraph. Por exemplo:
@tf.function
def train(model, dataset, optimizer):
for x, y in dataset:
with tf.GradientTape() as tape:
prediction = model(x)
loss = loss_fn(prediction, y)
gradients = tape.gradients(loss, model.trainable_variables)
optimizer.apply_gradients(gradients, model.trainable_variables)
Se você usa o Keras.fit()
, não precisa se preocupar sobre a iteração do dataset.
model.compile(optimizer=optimizer, loss=loss_fn)
model.fit(dataset)
Aproveite o AutoGraph com o fluxo de controle Python
AutoGraph provê uma maneira de converter um fluxo de controle dependente de dados em um modo grafo equivalente com tf.cond
e tf.while_loop
.
Uma situação comum onde esse fluxo de controle acontece é em modelos sequenciais. Por exemplo, tf.keras.layers.RNN
encapsula uma célula RNN, permitindo “desenrolar” a recorrência tanto estaticamente quanto dinamicamente. À título de demonstração, você poderia reimplementar um desenrolar dinâmico da seguinte forma:
class DynamicRNN(tf.keras.Model):def __init__(self, rnn_cell):
super(DynamicRNN, self).__init__(self)
self.cell = rnn_celldef call(self, input_data):
# [batch, time, features] -> [time, batch, features]
input_data = tf.transpose(input_data, [1, 0, 2])
outputs = tf.TensorArray(tf.float32, input_data.shape[0])
state = self.cell.zero_state(input_data.shape[1], dtype=tf.float32)
for i in tf.range(input_data.shape[0]):
output, state = self.cell(input_data[i], state)
outputs = outputs.write(i, output)
return tf.transpose(outputs.stack(), [1, 0, 2]), state
Use tf.metrics para agregar dados e tf.summary para log
Por fim, um conjunto completo de símbolos de tf.summary
serão liberados em breve. Você pode acessar a versão 2.0 do tf.summary
através de:
from tensorflow.python.ops import summary_ops_v2
Próximos Passos
Esse artigo apresentou um breve resumo das mudanças do TensorFlow 2.0 e e foi baseado nesse artigo do perfil do TensorFlow no Medium. Mais detalhes sobre as mudanças e as melhores práticas podem ser conferidos nesse link (em inglês). Se você quiser aprender mais sobre o TensorFlow 2.0, recomenda-se dar uma olhada nesses artigos: