Utilizando o Tensorboard

O Tensorflow é um dos frameworks de machine learning mais populares da atualidade. Ele utiliza o conceito de grafos para descrever o fluxo dos dados e operações do modelo. Cada nó representa uma operação matemática e cada conexão ou aresta do grafo representa uma array multidimensional, mais conhecida como tensor.

A sua interface é relativamente simples e intuitiva. Porém, modelos muito grandes e complexos podem dificultar o processo de depuração da rede. E visualizar os parâmetros e a estrutura do grafo é uma ótima maneira de debugar e entender melhor o seu programa.

O Tensorboard é uma ferramenta do Tensorflow que permite fazer justamente isso.


Tensorboard

O Tensorboard é uma ferramenta que permite a visualização de qualquer estatística de uma rede neural como, por exemplo, parâmetros de treinamento (perda, acurácia e pesos), imagens e até o grafo construído. Isso pode ser muito útil para entender o fluxo dos tensores no grafo e assim debugar e otimizar o modelo.

Neste artigo, iremos falar um pouco sobre como usar o Tensorboard junto com alguns exemplos práticos.

Conceitos básicos

O Tensorboard trabalha lendo event files que é onde o Tensorflow escreve as summary data (os dados a serem visualizados). O Tensoboard geralmente vem instalado junto com o Tensorflow e para executá-lo basta utilizar o seguinte comando: tensorboard --logdir=[dir] onde [dir] é onde estão localizados os event files. Para visualizar os dados basta acessar o link que será exibido no terminal.

Para escrever um event file é preciso criar uma instância FileWriter, e para isso basta chamar seu construtor tf.summary.FileWriter([dir], [graph]), onde [dir] é o diretório dos event files e [graph] é o grafo construído.

Para gerar os dados que serão observados podemos utilizar a função tf.summary.scalar(name, data)onde scalar pode ser histogram, image, audio e text, dependendo do tipo do dado a ser visualizado.

Por fim usamos writer.add_summary(summary, step) para escrever os dados no event file, onde writer é uma instância de FileWriter.

Visualizando o grafo

O código abaixo mostra como criar o grafo para visualização no Tensorboard:

O comando citado acima para criar o FileWriter já cria o grafo para visualização também. Porém o grafo gerado pode ser difícil de entender, principalmente se o modelo for grande e complexo.

Uma técnica utilizada para facilitar a visualização é utilizar nomes intuitivos para as variáveis e trabalhar com escopos. Com isso, o Tensorboard irá criar uma hierarquia dos nós, encapsulando os nós de um escopo em um único nó.

O nome das variáveis são definidas pelo parâmetro opcional name que é passado nas funções do tensorflow, e os escopos são definidos com a função tf.name_scope(name).

Dois grafos representando a mesma rede. Do lado esquerdo foi adicionado escopos e a sua leitura é bem mais clara.

Outras características dos grafos que ajudam na sua visualização são que nós com mesma estrutura possuem a mesma cor e também é possível navegar por dentro dos nós para ver o que tem dentro de cada um.

Visualizando escalares

No código a seguir, estamos criando summary para as perdas de um classificador multi-task, e também para a perda total:

Calcula as perdas e depois cria o summary

O tf.summary.merge_all() é uma função útil para você não precisar ficar escrevendo cada um dos summary no event file, ele junta todos os summary já definidos em um único buffer, e assim basta chamar a função writer.add_summary() uma única vez.

Como já foi dito, para gerar o dado a ser escrito no Tensorboard, basta utilizar a função tf.summary.scalar(data, name). Observar variáveis como a perda, acurácia, gradiente de alguma camada ou até o learning rate pode ser bastante útil para verificar se a rede está convergindo ou não. A possibilidade de observar o gráfico dessas variáveis "ao vivo" ajuda a identificar comportamentos não esperados o quanto antes.

the Gradiente da última camada e a perda total para três learning rates (0.0005, 0.5 e 1.0)

Na figura acima, podemos perceber que para o learning rate igual a 1, o gradiente chega rapidamente a 0, mas a perda está muito alta, com isso já podemos descartá-lo pois a rede não irá aprender mais nada e isso indica que devemos diminuir o lr. Para o learning rate igual a 0.0005, vemos que a perda está caindo muito devagar e o gradiente aumentando, isso pode indicar que estamos no caminho certo, mas devemos aumentar o lr para convergir mais rápido. O learning rate igual a 0.05 apresenta um bom resultado.

Visualizando histogramas

Primeiro cria as variáveis representando os pesos e depois cria os histogramas deles

Os histogramas utilizam a função tf.summary.histogram(data, name). Um histograma é basicamente uma coleção de valores representados pela sua frequência/densidade dentro da coleção. No Tensorboard, eles são utilizados para a visualização dos pesos da rede ao longo do tempo. Visualizar os pesos é importante pois podem indicar que a sua inicialização ou o learning rate estão errados.

Histograma da última camada de uma rede para diferentes learning rates

Os histogramas possuem três dimensões, a profundidade (dimensão y) indica a época, quanto mais fundos (e mais escuros) mais antigos são aqueles valores. A dimensão z indica a densidade dos valores indicados na dimensão x.


Os outros tipos (image, audio e text) não serão mencionados aqui pois eles são utilizados apenas em tipos de redes específicas. Mas eles podem dar uma boa base para você conseguir ver o que a rede está aprendendo. Por exemplo, em uma rede para reconhecimento de imagens, é possível observar quais padrões cada camada está aprendendo.

Exemplo das imagens obtidas depois da primeira convolução (a quarta imagem é a imagem original)

O código

Os códigos e as imagens do Tensorboard foram obtidos de um classificador multi-task feito para reconhecer uma sequência de caracteres de tamanho máximo 5. Os dados foram gerados sinteticamente a partir do conjunto de dados not MNIST. A rede consiste basicamente de duas camadas convolucionais, duas camadas fully-connected e cinco classificadores (foi adicionado uma classe 'branco', para os casos de a sequência não ter tamanho 5).

Os resultados obtidos no conjunto de teste foram:

Perda total: 1.1103163957595825
Acurácia global: 71.62%
Caractere 0: 90.34%
Caractere 1: 91.02%
Caractere 2: 93.16%
Caractere 3: 94.73%
Caractere 4: 97.71%
Média dos caracteres: 93.39%

A acurácia global corresponde à quantidade de sequências certas, ou seja, em que todos os caracteres da sequência foram corretamente classificados.

O código pode ser encontrado no seguinte repositório do Github:


Conclusão

O Tensorboard é uma ótima ferramenta para você conseguir entender e depurar a sua rede. Com ele, é possível observar o comportamento da perda, da acurácia, do learning rate e também dos pesos do modelo. Além disso, é uma boa forma de conseguir fazer validação cruzada para a escolha dos hiperparâmetros.

Uma desvantagem do seu uso é que o código pode ficar muito verboso, isto é, como devemos ficar criando vários escopos, nomeando as variáveis (para tornar a visualização legível) e para cada variável que queremos observar adicionar uma linha com o tf.summary, o código pode ficar difícil de ler.

Porém acredito que com uma boa organização de código, isso não vai ser um problema :)

Referências:

- Tensoflow: https://www.tensorflow.org/guide/summaries_and_tensorboard
- Ganegedara, Thushan. TensorBoard Tutorial: https://www.datacamp.com/community/tutorials/tensorboard-tutorial
- Mobiny, Aryan. How to Use TensorBoard?: https://itnext.io/how-to-use-tensorboard-5d82f8654496