Redes Convolucionais: Reconhecer objetos em imagens

Redes Convolucionais: Reconhecer  objetos em imagens

 

Recentemente fizemos duas publicações sobre reconhecimento de imagens. No primeiro post houve uma explicação sobre como o computador reconhece uma imagem, além de como podemos extrair informações ou aplicar filtros. Já no segundo post explicamos como funcionam as redes neurais, um processo de aprendizado de máquina que vem revolucionando diversas área como: medicina (como classificação de tumores malignos), artes (como criação de nova músicas) ou engenharia (criação de carros autônomos).

Hoje vamos continuar este assunto, e ainda unir os assuntos das duas publicações anteriores. Como  as redes neurais são aplicadas e como isto permite que o computador consiga reconhecer objetos, faces e animais em uma foto ou um vídeo!

Resumo

Rede neural é um processo de aprendizado capaz de encontrar uma função matemática que separe alguns conjuntos de dados. Assim consegue classificar um dado novo baseado em um conjunto de dados como: altura, peso e cor. A rede neural consegue olhar para estes dados e criar uma função que encontre um padrão de separação deles, e quando observar um dado novo ela conseguirá classificar sozinha.

Explicamos a seguir que uma imagem é um conjunto de pixels, o qual é representado no computador como uma matriz. Ou seja, se queremos fazer que a rede neural reconheça imagens, passaremos essa matriz no lugar dos dados, certo? Bem para responder essa pergunta vamos fazer um teste!

Teste!

Vamos fazer um código que permita com que redes neurais consigam classificar imagens. Neste caso, as imagens serão fotos do rosto de algumas personalidades, e nosso objetivo é classificar de quem são esses rostos.

O primeiro item que precisamos são as imagens. Elas precisam estar pré-classificadas, ou pelo menos parte delas, para nosso algoritmo poder aprender. A biblioteca Sklearn tem algumas imagens que podemos usar de teste. Para usá-las, precisamos do seguinte comando:

from sklearn.datasets import fetch_lfw_people

lfw_people = fetch_lfw_people(min_faces_per_person=70, resize=0.4)

Com isto iremos baixar um conjunto de dados (representado em python como um dicionário), aonde teremos várias imagens de rostos e o nome de seu respectivo dono. Abaixo temos um exemplo destas imagens:

Agora precisamos treinar a rede neurais! Mostramos uma explicação detalhada deste processo em nosso primeiro post, por isto aqui vamos apenas demonstrar o código comentado:

from sklearn.neural_network import MLPClassifier

x = lfw_people.data
y = lfw_people.target 

xTreino,xTeste,yTreino,yTeste = train_test_split(x,y)

rn = MLPClassifier()
rn.fit(xTreino,yTreino)

 

O último passo é verificar a qualidade desta classificação e podemos fazer isso ao calcular a acurácia para cada face:

preditos = rn.predict(xTeste)

print(classification_report(preditos,yTeste))

Ops! Nossa classificação ficou muito ruim. Será que podemos melhora-la? Sim! Para isto vamos usar um tipo diferente de rede neural, as redes neurais convolucionais. Mas antes de entendê-la, vamos descobrir o que deu errado.

Erro 

Quando passamos uma imagem para uma rede neural, transformamos a imagem, que originalmente é uma matriz em uma lista. Assim, cada pixel servirá de entrada na rede neural! Esse processo é conhecido como Flatten:  

Este processo nos trás dois erros. O primeiro, é abrirmos uma lista com todos os pixels da imagem. Ao se trabalhar com grande número de pixels,o aprendizado de rede torna-se confuso e lento e praticamente inviável de ser “rodado”. O segundo problema é que  quando aplicamos o processo de Flatten, fazemos com que os pixels sejam analisados separadamente, ou seja, sem observar o relacionamento espacial dos pixels. Este relacionamento é muito importante para extraímos informações úteis da imagem.

Relacionamento Espacial

Observe o pixel abaixo e veja se consegue tirar alguma conclusão do que ele representa em uma imagem: 

Se demonstramos alguns pixels em volta você poderá perceber essa imagem representa a íris de um olho azul, ou seja, o relacionamento espacial com a imagem foi crucial para seu o entendimento!

Rede Neurais Convolucionais

Esse relacionamento entre os pixels é muito importante e, para podermos aproveitar essa informação, não poderíamos utilizar apenas uma rede neural densa comum. Para fazer isso de uma maneira eficiente vamos utilizar um tipo específico de rede neural chama Rede Neural Convolucional.

Essa rede é composta de uma sequência de passos e camadas. O primeiro passo é um passo de convolução, seguido de uma camada de Pooling e uma rede conectada.

Passo de Convolução

Um filtro convolucional é uma matriz que pode ser aplicado em uma imagem e gerar uma nova imagem através de uma operação matemática. Não vamos entrar em sua formulação matemática, mas vamos explicar seu funcionamento através de um gif:

Perceba um detalhe importante: o filtro convolucional não é aplicado em apenas um pixel, mas sim em grupo de pixels. Isso permite que sua imagem de saída ainda preserve as relações espaciais entre os pixels e dessa maneira, resolvemos o “primeiro” problema.

Diferentes filtros convulsionais podem gerar diferentes saídas, nas quais cada uma pode enfatizar uma informação diferente da imagem original. Nas redes neurais convulsionais é comum utilizar vários filtros diferentes em um mesmo passo, o que permite que cada filtro preserve uma característica da imagem original:

E aqui está a grande sacada das redes convolucionais: elas aprendem os melhores valores para se colocar em cada filtro, ou seja, os pesos aplicados na rede são na verdade os números dentro de cada ponto da matriz de convolução. 

Relu

Quando aplicamos um filtro convolucional, nossa imagem de saída pode apresentar valores negativos. Na computação uma imagem não tem valores negativos! Assim os valores seriam lidos como um ruído em nossa imagem e para resolver esses problemas aplicamos o Relu, um simples passo que consiste em transformar todo o número negativo em zero. Segue abaixo:

Pooling

Ao aplicar vários filtros, teremos várias imagens de retorno, assim o dado inicial que já é grande se torna maior ainda! Para diminuirmos o tamanho do dado (ou o tamanho da imagem) aplicamos uma função de polling. Sua operação consiste em dividir a imagem em quadrantes e retirar somente o valor mais importante dele, normalmente o maior valor (conhecido como maxpooling) ou o valor médio (avarange pooling). 

A imagem abaixo exemplifica essa operação ao converter uma matriz de 16 pixel em uma matriz de apenas 4 pixels a partir da aplicação de um maxpolling de 2×2.

Rede Conectada

Todo esse processo serve apenas como uma forma de extração de dados de uma imagem. Eles servirão de entrada para uma rede neural normal, normalmente, chamada de camada densa.
Já os dados de saída do processo de convolução são uma serie de imagens. E como passamos isto para uma rede neural normal? Aqui utilizamos o Flatten!

No final temos uma arquitetura como demonstrada abaixo:

Arquitetura da rede

Porém os 3 passos do da parte convolucional, podem ser utilizados quantas vezes o programador decidir ser necessário. Por exemplo, poderíamos repetir 2x o uso um passo convolucional, seguido de relu, com MaxPolling, assim temos uma arquitetura como a desenhada a baixo:

Ou poderíamos seguir os seguintes passos: 

Conv > ReLu > Conv > Relu >Pool >Relu > MaxP > Conv > Relu > Pool > Relu > MaxP > Conv > Relu > Pool

Como na imagem abaixo:

Ou seja, temos infinitas possibilidades de organizações destes dados que podem ser desenhados conforme um programador achar necessário. Então, desenhar a arquitetura de uma rede é questão de criatividade e  de vários testes!