Inteligencia Artificial

Classificação de imagens usando Keras

O uso de IA para classificação de imagens talvez seja a aplicação mais divertida que poderíamos fazer com Keras. Nesse artigo veremos como utilizar o CIFAR-10, que é um dataset composto por 60k imagens coloridas de  dimensão 32×32 pixels, sendo 50.000 para treinamento e 10.000 para teste.

Para utilizar esse dataset, construiremos uma CNN (Convolutional Neural Network) que reconhecerá as imagens com considerável precisão.

Gostaria de sugerir que não copie o código antes de chegar ao final do artigo, porque uma parte desse código inicial não está adequado e estou dispondo duas possibilidade próximo da conclusão.

Documentação do Keras

A documentação é originalmente em inglês, não há muito o que se fazer a não ser colocar no Google Translator para os que não sabem inglês. Se procurar por exemplos de uso de classificação, sempre haverá algum recurso (ou vários) sem explicação, porque pressupõe-se que tais conceitos já foram absorvidos. Nesse caso, é sempre uma boa ideia consultar a documentação para não deixar lacunas no aprendizado.

O site oficial é esse.

Classificação de imagens com Keras e CIFAR-10 dataset

Esse dataset serve para classificação de imagens. As informações a respeito já estão descritas acima, nessa sessão vou deixar esse link para donwload (não baixe ainda) e uma demonstração das classes contidas nesse dataset:

Classificação de imagens

As classes são completa e mutualmente exclusivas, não havendo sobreposição entre carros de passeio e caminhões, por exemplo.

Existem 3 versões do dataset, uma em Python, uma em Matlab e uma versão binária para programadores C. O tutorial será em Python, portanto recomendo a primeira opção.

No Linux, já dentro do container Docker, baixe o dataset com o programa wget. Se não estiver instalado, preceda o download com o primeiro comando descrito abaixo:

Será criado um diretório chamado cifar-10-batches-py contendo lotes de dados nomeados como data_batch_1 até 5. Também haverá um test_batch para testes.

Leitura dos batches

Cada um desses arquivos do CIFAR-10 é um objeto produzido com cPickle. Meu ambiente está compilado para trabalhar com Python 2.7. A rotina para abrir um desses arquivos e retornar um dicionário é essa:

Se por acaso estiver utilizando um ambiente diferente do descrito nesse artigo e sua versão do Python for a 3, a rotina seria essa:

Formato dos dados

Abrindo-o desse modo, cada dicionário conterá os seguintes elementos:

  • data – um array numpy de 10000×3072 do tipo uint8. Cada linha de arrays armazena uma imagem colorida de 32×32. As primeiras 1024 entradas contém os valores do canal Red, depois, de forma repetida para os canais Green e Blue.
  • labels – uma lista de 10000 números na faixa de 0 a 9. Os números no índice i indicam o rótulo da imagem no array data.Outro arquivo contido no dataset é o batches.meta. Ele também contém um dicionário Python. Suas entradas:
  • label_names – uma lista de 10 elementos que dão nomes significativos aos labels numéricos no array labels supracitado. Por exemplo, label_names[0] == “airplane” e daí por diante.

Construindo a rede neural

Haverá um pouco mais de recurso utilizado nesse tutorial, já que classificar imagens é algo um pouco mais elaborado. Só de imports já dá uma sensação de que problemas estão por vir, mas não se preocupe, basta seguir o procedimento com tranquilidade. Minha recomendação é que primeiro faça a leitura do artigo até o final, sem compromisso de implementação, apenas para fazer o entendimento. Depois, retome e inicie o processo de implementação, já sabendo o que esperar. Isso te deixará menos ansioso e as coisas serão mais claras.

Headers ou imports

Quando estamos desenvolvendo, claro que não descemos da primeira à última linha de código com tudo em mente. Diversos momentos diferentes temos que incluir alguma biblioteca, mudar uma função, depois tem debug, etc. Em tutorial é tudo lindo, é Ctrl+C e Ctrl+V. Por mais essa razão não é necessário se preocupar, use o tempo poupado para fazer o entendimento, porque se for apenas copiar, colar e executar, o aprendizado irá por água abaixo. É assim que faço. Inclusive, se no meio de uma leitura eu vejo uma citação de algo que presumo que deveria saber previamente, paro a leitura e procuro referências sobre o tema. Não tenha preguiça de fazê-lo.

Uma coisa importante a notar nos imports é que poderíamos simplesmente importar o Keras:

Mas tudo o mais que não será usado viria junto para a memória. importando como está agora abaixo, apenas o que será utilizado estará residente na memória. Por isso, nem são tantos imports, se você reparar.

Reparou a última linha? O Keras tem alguns manipuladores de datasets. No tutorial do MNIST ficou claro o download automático do dataset; o processo será o mesmo.

Podemos aproveitar para declarar o tamanho do batch e o número de classes a utilizar.

Quanto menor o batch, mais updates em um epoch. O epoch é o número de repasses, já explicado em outro artigo. Se não estiver utilizando GPU, não desanime, largue o computador e vá curtir uns dias de férias com a família.

Carregamento do dataset

Agora é a primeira parte bacana, com o conforto de deixar o “trabalho duro” por conta da API. Para carregarmos o modelo, simplesmente fazendo duas tuplas que conterão os dados inicias:

Depois de carregado, devemos fazer casting dos dados porque eles são carregados como inteiros e necessariamente devem ser notação de ponto flutuante.

Modelagem da CNN

Dá pra fazer de várias maneiras, vou discorrer a respeito conforme estiver mais habituado com sua composição. O número de camadas pode variar, pode ser bom ou pode ser ruim, dependendo de um monte de fatores. Espero em breve mostrar um exemplo da mesma forma que aprendi que “mais” não significa “melhor”.

Com 4 camadas o modelo fica desse jeito:

Não é uma má ideia fazer uma função para comportar a estrutura da modelagem da rede, podendo inclusive permitir experimentação, criando outras funções com variação de camadas. Quanto mais camadas, maior o tempo de treinamento e, acredite, esse treinamento não será dos mais leves.

A impressão do sumário deve retornar algo como:

Classificação de imagens

O treinamento começou 00:35 com 3% de acurácia.

01:35 estava em 10% de acurácia. Restavam algumas horas ainda de treinamento, mas se não houvesse uma curva significativa no aprendizado, a decepção seria grande.

02:35 e a maioria das interações mostravam 0.0991. A angustia começou a tomar conta.

03:00 chega. Estava compilando na CPU. Pense num cara burro. Sofri por horas tentando compilar o TensorFlow a partir do momento que consegui jogar pra GPU e percebi que a versão do TensorFlow foi compilada com uma cudnn diferente da que eu tinha no computador.

09:30 fui dormir. Quando acordei, retomei, tentei fazer com uma imagem docker do TensorFlow, mas nada dava certo. Pesquisando no google, achei um repositório das versões do cudnn. Pior que não posso recomendar porque tudo vai depender da versão do seu TensorFlow e da versão CUDA e cudnn. Vai ter que ser na sorte.

18:55 enfim, compilando. Mas a acurácia não passava de 10%. O que estava errado? Como citei no início da série, não sou especialista em redes neurais e só utilizei frameworks profissionalmente para implementar junto com recursos de visão computacional. Mas mexendo aqui e ali, trocando camadas e chorando muito, acabei trocando o otimizador. Cara, o otimizador. Redes neurais não são feitas para amadores.

Optimizers

Na documentação do Keras tem o mesmo exemplo que usei com o otimizador SGD. Demorei demais pra suspeitar, mas por falta de conceitos.

SGD

O SGD é o Stochastic Gradient Descent. Vou ter que fazer um artigo bem chato falando sobre otimizadores.

Na documentação estão citados todos os suportados, mas não está claro o que fazem. Vou fazer experimentações. Por exemplo, o Adadelta “é uma versão mais robusta do Adagrad que adapta as taxas de aprendizado baseado no movimento das janelas de updates do gradiente. Desse modo, ele contia aprendendo quando muitos updates foram feitos. Comparado ao Adagrad em sua versão original do Adadelta, você não tem que configurar uma taxa inicial de aprendizado. Nessa versão a taxa inicial de aprendizado e fator decay podem ser configurados, assim como na maioria dos otimizadores do Keras”.

Parece bacana, mas funciona como? Quando deve ser utilizado? Por isso farei um artigo somente sobre otimizadores.

Adam

Troquei por esse otimizador e logo no começo do treinamento, chegou a 39%.  Será que o ideial seria modificar parâmetros do SGD ou tudo bem trocar só para aumentar a pseudo-acurácia?

Acurácia e perda

Já mostrado em outros artigos, imprimir os scores nos permite avaliar o resultado do trabalho. Para tal, incluímos duas linhas mais.

Aí terminou o treinamento e:

Classificação de imagens

Além de não ficar muito alta a precisão, ainda teve um loss altíssimo. O que fiz? Consultei um especialista, claro. Daí o Leonardo Lontra disse para criar um modelo mais profundo. Bem, até aqui já tive que mudar coisas no sistema operacional e no programa, além de passar uma quantidade enorme de horas testando e treinando o model. Há de se ter persistência então, vamos lá; enquanto não recebo as dicas, vou dando uns pulos.

Mexi em algumas coisas pra ver em que resultava; mudei o parâmetro loss de multi-classe para regressão, mudei a densidade da saída para 32 e algumas coisinhas mais. Para não testar com 100 epochs, baixei para 2 e assim pude ter uma ideia ao final se o ajuste resultara em algo positivo.

Classificação de imagens

É, ainda não está muito bom, mas melhorou de uma maneira fantástica.

Com um pedacinho do treino já deu 0.27 de precisão e 0.08 de perda. No treinamento anterior deu 0.47 de precisão e 0.37 de perda. Inegavelmente melhor. Daí, decidi rodar o aprendizado completo para ver o quanto baixaria, pois vi uma diferença de 0.0859 para 0.0816 em poucas linhas.

Com 20 epochs o valor de loss já estava em 0.0411 e a acurácia estava em 0.7127. Fantástico! Com o treinamento finalizado, a precisão ficou em 74.7%. Tá ruim mas tá bom. Em loss ficou com 0.0363; menos que 4%. Eu já me daria por satisfeito, mas lembra que citei o Leonardo Lontra mais acima? Pois então, enquanto eu fazia esse deep learning, ele preparou um modelo baseado no Squeezenet, com leves modificações. A que eu fiz oi baseado em 2 modelos, cada um tinha suas características e fui testando, mas no final tive que mexer também. Vejamos como ficou esse modelo que treinei:

Agora vamos testar o modelo fornecido pelo Lontra. Rodei com 20 epochs, já na primeira foi um susto; loss com 1.7603, mas acurácia começou alta também. Foi 0.4050 de cara!

O tempo por epoch foi quase o triplo, mas em 7 epochs já estava em 67% de precisão com loss de 0.8509. Fiquei só na expectativa. O modelo que fiz acima tem uma curva que para de crescer em dado momento, então 100 epochs não são necessários. Com a metade já fica bem próximo do resultado final.

O resultado do código do Lontra com 20 epochs:

Classificação de imagens

Não ficou muito atrativo com poucos epochs, mas o treinamento ainda estava crescendo. Nesse caso, vale a pena continuar. Já dei uma modificada no dropout, por sugestão dele. O dropout desativa neurônios aleatoriamente para prevenir overfiting. Baixei o número de epochs para 6, só para comparar o resultado; mas não melhorou, então vou compilar com 100 epochs pra comparar de igual pra igual. Mas por enquanto, já disponibilizo o código, que tem informações preciosas, mostrando como fazer a predição, salvamento automático do treinamento etc!

Farei um vídeo mostrando minhas experiências com a predição e os resultados do treinamento com esse dataset CIFAR10. Se gostou, deixe seu like na nossa página do facebook alí no canto superior direito. Se inscreva em nosso canal DobitAoByteBrasil no Youtube, clique no sininho para receber notificações e até a próxima!