Deep Learning com Keras e TensorFlow — Part1

Jéssica Santos
IA em Saúde: NeuralMed
7 min readSep 8, 2020

Entre o dias 06 e 08 de maio de 2019 participamos da QCon SP. Um dos maiores eventos de tecnologia do Brasil, com maior público senior e mais de 80 palestras técnicas. Nele apresentamos a palestra Deep Learning com Keras e TensorFlow, disponível no Youtube e a 1a parte descrita neste post.

Nós trabalhamos na NeuralMed, uma startup que usa Inteligência Artificial para auxiliar no diagnóstico de imagens médicas. Portanto, usar Deep Learning faz parte do nosso dia-a-dia, e é a base desse conhecimento que gostaríamos de compartilhar. Os tópicos principais que cobriremos são:

  • Como construir uma Convolutional Neural Network (CNN) usando Keras + Tensorflow
  • Como e porque usar TransferLearning
  • Alguns pontos de atenção e desafios encontrados

Por trabalharmos com imagens médicas esse será nosso caso de uso, mais especificamente imagens de raio-x de tórax. Porém, todas as redes construídas podem ser aplicadas em outros tipos de imagens, como classificação de gatos e cachorros, por exemplo.

Exemplo de raio-x de tórax

Por que Tensorflow + Keras?

Um artigo recente publicado no medium fez uma comparação entre as principais bibliotecas para Deep Learning e mostrou que Tensorflow é o que mais cresce e o que apresenta a maior demanda.

Resultado do estudo realizado por Jeff Hale

E porque então usar Keras? Keras roda em cima do Tensorflow e oferece uma linguagem muito mais amigável, fácil e rápida para escrever os modelos (já mencionamos que é muito mais fácil?)

Em sua versão 2.0 o Tensorflow passou a usar o Keras oficialmente como sua API de alto nível, já integrada dentro do tf.keras. Portanto, continuaremos usando o Keras como framework para Deep Learning. (Ainda mais com esse novo mascote!)

Keras mascote: Prof Keras

Convolutional Neural Network

CNN é uma categoria de redes de deep learning normalmente aplicadas para reconhecimento de imagens.

Como elas funcionam?

São baseadas principalmente em Filtros Convolucionais que extraem características de imagens. Esses filtros já são usados há muito tempos na área de Processamento de Imagens, a ideia é que ao aplicar essas matrizes sobre a imagem se consiga obter as características desejadas. O gif abaixo mostra os principais filtros (ou kernels) para extração de bordas.

Fonte: Gentle Dive into Math Behind Convolutional Neural Networks

Nas redes convolucionais esses filtros são aplicados em várias camadas. Além disso, os valores dos filtros são aprendidos, portanto a própria rede aprende quais características são relevantes para o problema em questão.

Fonte: CS231n Convolutional Neural Networks for Visual Recognition

Vamos a prática!

Construindo uma CNN pra predizer lateralidade do raio-X

Nosso desafio é criar uma rede para aprender se um raio X é lateral ou de frente. Esse é um dos poucos desafios da área médica que nós, leigos, conseguimos validar visualmente sem o auxílio de um médico:

Para facilitar vamos dividir a criação da rede em 5 etapas:

1. Input de dados

2. Definição da arquitetura

3. Compilação do Modelo

4. Treinamento

5. Validação

Antes de ver cada uma delas, vamos fazer todos os imports necessários para o funcionamento do código:

1. Input de Dados

Existem várias formas de inputar os dados para treinamento, vamos usar o Image Data Generator para ler as imagens a partir do disco.

O primeiro passo é instanciar o generator, nesta etapa existem vários parâmetros possíveis tanto para definir o formato da imagem que será lida, quanto para aplicar transformações fazendo image augmentor, todos os parâmetros podem ser consultados aqui. Neste exemplo definimos apenas que a imagem precisa ser normalizada (dividindo por 255) e que 30% dos dados serão separados para validação:

A próxima etapa é passar quais dados serão lidos. Aqui também há diferentes métodos possíveis, os mais comuns são flow_from_dataframe que lê as imagens de acordo com os caminhos específicados em um dataframe, e flow_from_directory, que lê os arquivos direto de uma pasta, cada classe deve estar em uma pasta separada:

Também definimos nesta etapa o BATCH_SIZE, ou seja, quantas imagens serão lidas por bloco, se os dados serão lidos de forma aleatória ( shuffle=True) e o seed. Outros parâmetros podem ser vistos na documentação do keras.

2. Definição da arquitetura

O método abaixo define a arquitetura da rede:

Os modelos do keras podem ser Functional API ou Sequential. Quando estamos definindo a nossa rede usamos o Sequential para definirmos as nossas camadas de forma sequencial.

A primeira camada adicionada neste exemplo é uma convolucional com 64 filtros e dimensão de 2x2, com função de ativação relu. Essa é a função de ativação tradicionalmente utilizada nas camadas intermediárias, ela ativa os neurônios que tiveram resultados maiores que 0, para outras funções disponíveis pelo Keras veja aqui. Também é na primeira camada que definimos qual o formato de entrada da rede, no caso passaremos imagens de 256x256:

Em seguida adicionamos uma camada de MaxPooling, uma camada que realiza o downsampling calculando o valor máximo de cada pool como mostrado na figura, para essa camada precisamos apenas definir o tamanho do pool.

Exemplo de funcionamento do MaxPooling, Fonte: Google Developers: ML Practicum: Image Classification

Depois adicionamos uma camada de Dropout, uma das técnicas atualmente mais utilizadas para evitar overfitting. Ele aleatoriamente desativa uma porcentagem de neurônios durante cada época de treinamento. Precisamos apenas definir a porcentagem que queremos desativar.

Cuidado para não exagerar na quantidade de dropouts nem na porcentagem ou acabará gerando underfitting

Após as camadas convolucionais precisamos redimensionar as features para 1 dimensão. Aqui faremos isso utilizando uma Flatten.

Depois disso adicionamos uma camada Densa com 256 neurônios e por fim a câmada de saída com 2 neurônios (um para cada classe), e a função de ativação sigmóide, que retorna a probabilidade da instância ser daquela classe.

Essa foi a arquitetura que nós definimos para o modelo em questão, as camadas usadas e seus respectivos tamanhos são totalmente parametrizáveis, recomendamos testar o modelo com diferentes arquiteturas para analisar os resultados.

3. Compilação

Precisamos definir como a rede irá aprender, isto é, qual a função de loss e o otimizador.

Para este exemplo utilizamos o otimizador Adam, com os valores padrões ( lr=0.001, beta_1=0.9, beta_2=0.999), você pode aprender mais sobre ele aqui.

Para o cálculo do loss usamos a função padrão para classificação binária: binary_crossentropy.

4. Treinamento

Como lemos os dados usando um generator, o fit do keras também será usando um fit_generator.

Também usaremos alguns callbacks:

ModelCheckPoint para salvar o modelo que tiver o melhor loss durante o treinamento e,

EarlyStop para interromper o treinamento caso a rede pare de aprender.

Definidos os callbacks, vamos ao treinamento em si:

Como faremos a leitura de dados com um generator, utilizaremos o fit_generator para o treinamento. Como é possível ver no código a definição dos parâmetros é bem simples: passamos os generators, a quantidade de passos que será preciso no treinamento e na validação por época, isto é, quantos batchs são precisos para terminar de ler todos os dados, a quantidade máxima de épocas e os callbacks já criados.

O resultado do treinamento foi o seguinte:

Como podemos ver, apesar de termos colocado como máximo 50 épocas, o modelo parou na 10a época devido ao early_stop. Conseguimos ver que ele parou mesmo de aprender na época 5, onde atingiu 0.9853 de acurácia na validação.

5. Avaliação:

Sempre importante separar uma quantidade de dados para testar o modelo no final. Aqui faremos apenas um teste visual para efeito de demonstração

Precisamos carregar o modelo salvo. Lembre-se que o modelo que treinou até a última época e estiver em memória não é necessariamente o melhor, o melhor foi salvo pelo ModelCheckpoint.

Depois carregamos as imagens de teste em memória usando os métodos do próprio keras:

E, por fim, fazemos a predição para estas imagens:

Com as predições e os valores reais podemos calcular as métricas necessárias para validar nosso modelo. Geralmente utilizamos uma matriz de confusão ou calculamos métricas como precisão e sensibilidade, que são mais ou menos importantes de acordo com o problema. Porém, a fim de ilustrar melhor os resultados, iremos apenas visualizar os resultados para cada imagem.

Como podemos perceber, o modelo acerta bem quase todas as imagens separadas para teste, errando apenas a terceira imagem, provavelmente por estar de cabeça para baixo. Se quisemos corrigir esse tipo de erro poderíamos usar o image augmentor e gerar mais algumas imagens em diferentes posições.

Originally published at https://neuralmed.ai.

--

--

Jéssica Santos
IA em Saúde: NeuralMed

Lead Data Scientist na NeuralMed. Trabalho há 7 anos com tecnologia, hoje principalmente com Deep Learning aplicada a saúde. Vegetariana com 3 cachorros.