Redes Neurais | Teoria #3

Um verdadeiro hands on em redes neurais.

Eduardo Eiras de Carvalho
Turing Talks
8 min readOct 27, 2019

--

Escrito por Caio Deberaldini, Eduardo Eiras, Guilherme Fernandes e Fernando Matsumoto.

http://bigpixelgraphics.com/photo-restoration-service/

A imagem acima é fruto de um tipo de aplicação de redes neurais, que é a restauração de imagens. Hoje vamos entender um pouco de representação de imagens por computadores, assim como iremos utilizar tudo que aprendemos nas últimas semanas sobre rede neurais para fazer predições sobre características de imagens, animados?

Caso não tenham lido os últimos dois textos ou não entendam absolutamente nada de redes neurais, recomendamos que, primeiramente, leiam eles:

Como uma imagem é representada no computador?

Assim como todas as coisas no nosso computador, imagens são representadas por números e, neste caso especificamente, uma série de números em matrizes. Quando abrimos uma imagem, estamos nada mais do que abrindo uma ou mais matrizes que contêm um valor para cada pixel do nosso computador.

Para entender um pouco mais sobre o que está acontecendo, vamos entender o conceito de espaço de cores.

RGB

O espaço de cores mais utilizado para imagens em computador é o RGB — Red, Green, Blue — ou seja, ele é um conjunto de três matrizes em que cada uma possui um valor correspondente à vermelho, verde ou azul. Este valor vai de 0 a 255 e representa a intensidade da cor em questão.

Assim, cada pixel é um vetor de tamanho 3, por exemplo, um valor RGB (0, 0, 255) representa um azul intenso, (0, 0, 0) é preto e (255, 255, 255) é a superposição das três cores, ou seja, branco.

O RGB pode ser visto como um sistema cartesiano de cores:

Espaço de cores RGB

Como podemos então representar imagens em preto e branco? Isso pode ser feito com uma matriz em que cada pixel tem o valor da média dos três valores do espaço RGB.

HSV

Um segundo espaço de cores amplamente utilizado é o HSV (Hue, Saturation, Value), geralmente representado por um cilindro:

Espaço de cores HSV
  • Hue: também chamado de matiz, é um ângulo que representa a cor do pixel, por exemplo, de 0º à 60º este valor é vermelho
  • Saturation: a saturação representa a quantidade de cinza do modelo. Assim, um valor perto de 0 possui uma tonalidade acinzentado, enquanto 100 representa a cor de forma mais vívida.
  • Value: valor, ou brilho, indica a intensidade de uma cor, onde 0 ela é totalmente preta e 100 há maior representação da cor

O fato do espaço de cores HSV representar também brilho e saturação é muito útil na análise de imagens, uma vez que nem sempre as imagens que analisaremos estarão em perfeitas condições de brilho.

Como abrir uma imagem no Python

Agora temos que colocar isso em prática! Há algumas bibliotecas de python que nos permitem visualizar imagens. Neste artigo, abordaremos matplotlib e opencv.

Matplotlib

Antes de começarmos com os comandos, é bom saber que uma desvantagem deste biblioteca é que ela apenas suporta imagens com a extensão .png

Inicialmente iremos importar a biblioteca com o nome plt por comodidade), e o módulo matplotlib.image que nos permite carregar e visualizar imagens.

Para carregar uma imagem dando seu nome como img, usamos o comando img = mpimg.imread('nome_do_arquivo.png').

Por curiosidade, caso execute o comando print(img), irá receber uma matriz! Para visualizarmos corretamente a imagem, usaremos o comando plt.imshow(img).

Opencv

Esta biblitoteca, ao contrário do matplotlib, segue a ordem BGR (Blue Green Red) de cores, ao invés de RGB. Assim, talvez você deva checar como sua imagem está representada antes de usar qualquer uma destas bibliotecas. Uma outra diferença é que opencv não possui a limitação de abrir apenas arquivos .png.

No python, a versão que usaremos é o cv2. A função que lê imagens é cv2.imread('nome_do_arquivo') e para visualizar devemos executar cv2.imshow('nome_da_janela', img).

Uma peculiaridade desta biblioteca é que devemos executar um comando específico para fechar a imagem. O comando waitKey(0) deixa a janela aberta até o usuário pressionar alguma tecla, em seguida a janela é fechada com destroyAllWindows.

Agora que sabemos como as imagens são representadas por um computador, veremos como treinar uma rede neural em um dataset de imagens.

MNIST

Instituto Nacional de Tecnologia e Padrões Modificado ou MNIST é uma base de dados de dígitos escritos a mão. Este é equivalente ao “Hello World!” de Deep Learning. Consiste em 70,000 imagens, as quais estão divididas em 60,000 para treinamento e 10,000 para testes.

Em geral, as redes neurais carecem de um número alto de observações para oferecerem uma boa generalização. Simon Haykin em “Redes Neurais Princípios e prática” afirma que o tamanho do conjunto de treinamento (N) precisa ser da ordem da razão entre o número de parâmetros livres da rede (W) e a fração de erros (ε) de classificação permitida sobre os dados de teste. Matematicamente:

Lembrando que os parâmetros livres correspondem aos pesos sinápticos e níveis de bias.

Amostra de imagens do MNIST

Cada imagem é composta, por 28x28 pixels, totalizando 784 pixels que serão inputs para a nossa rede neural. Sem mais delongas, vamos à prática.

Carregando o dataset

Com as seguintes linhas de código podemos importar o dataset MNIST do módulo Tensorflow de Python.

Normalizando as matrizes e alterando o formato

Precisamos alterar o formato das imagens, que vêm do dataset como matrizes, em vetores. Fazemos isso empilhando as linhas da matriz e convertendo essa pilha em um vetor, por exemplo. Essa etapa é necessária já que os números que representam cada pixel correspondem às entradas da rede neural.

Além disso, normalizamos os números da matriz, dividindo pelo seu valor RGB máximo (255), para que seus valores estejam entre 0 e 1. Isso é importante porque garante otimização de performance dos algoritmos.

Funções Sigmóide

A implementação dessas funções se dá de maneira bem simples, seguindo as fórmulas que vimos nos últimos posts:

Codificação das Labels

A ideia aqui é converter as nossas saídas (labels) que correspondem aos dígitos de 0 a 9, em vetores com zeros e uns. Por exemplo, o número 0 corresponde ao vetor [1 0 0 0 0 0 0 0 0 0], o 1 ao [0 1 0 0 0 0 0 0 0 0] e assim sucessivamente.

Até poderíamos fazer a rede neural fornecer uma saída entre 0 e 9. Um dos problemas com essa abordagem é que cada label se refere a certos conjuntos de pixels. Dessa forma, não se deve pensar nas labels como números, mas sim como classificações (da mesma forma que fazemos com cores, por exemplo).

Lembrando que a saída é um número entre 0 e 1, podemos pensar que cada uma das saídas da rede indica a probabilidade de uma das classificações. A saída do primeiro neurônio, por exemplo, indica a probabilidade do dígito ser “0”. Dessa forma, a classificação gerada pela rede seria a mais provável, ou seja, aquela correspondente à maior saída da rede.

Inicialização dos Parâmetros Theta

Antes de começar a treinar a rede neural, precisamos inicializar valores para esses parâmetros, certo? Se inicializarmos esses valores com zero, as ativações da primeira camada serão todas zero, fazendo com que todos os gradientes da primeira camada fiquem iguais (pela fórmula que vimos no último post). Isso pode dificultar o aprendizado da rede. Uma solução prática é inicializar com valores distribuídos segundo uma Normal(0, 0.1). Existe uma alternativa melhor para inicialização que você pode encontrar aqui.

Função de Custo e Algoritmos de Feedforward e Backpropagation

O primeiro passo para calcular o gradiente da função de custo é implementar o feedforward. Para fazer isso, traduzimos as equações que vimos no último post para código:

Para lidar com a bias unit, adicionamos uma coluna com 1s no começo das ativações (a1 e a2). Cada elemento da coluna corresponde à bias unit em uma das imagens. Além disso, a multiplicação de matrizes é representada em código pelo símbolo @.

Em seguida, calculamos o custo, conforme dado no último post:

No código, a somatória dupla é realizada por meio de uma matriz. Cada termo da matriz corresponde a uma combinação de i e k. Basta então somar todos os elementos da matriz e dividir por m.

Por último, o backpropagation corresponde à tradução direta das equações que vimos no último post para python. No final, a função nnRegCostFunction retorna o custo e os gradientes do custo em função de theta1 e theta2.

Classificação e Acurácia

Para obter as classificações geradas pela rede, primeiramente realizamos o feedforward da mesma maneira que fizemos acima. A partir das ativações da última camada, podemos obter as classificações com a função np.argmax, que nos diz qual dos 10 neurônios de saída tem a maior ativação (para cada imagem).

Em seguida, basta contar os acertos e dividir pelo total de imagens para obter a acurácia.

Treinando a Rede Neural

Para treinar a rede neural, precisamos utilizar todas as funções que definimos acima. Primeiramente, criamos os parâmetros theta1 e theta2, considerando uma camada escondida com 800 neurônios.

Em seguida, realizamos o gradiente descendente. Essa etapa consiste em, repetidamente, calcular os gradientes do custo e fazer a seguinte operação (D é o gradiente e α é a taxa de aprendizado):

O loop para após um número máximo de iterações (max_iter) ou quando o erro fica baixo o suficiente, conforme determinado por epsilon.

Resultados

Até agora construímos uma rede neural usando apenas numpy, mas não verificamos se ela funciona nem como ela performa. É isso que fazemos no notebook abaixo.

Primeiro, obtemos os parâmetros theta1 e theta2 da rede treinada com alpha=.3. Em seguida, verificamos a acurácia da rede e vemos as suas predições para algumas imagens da base de teste.

Link alternativo: https://nbviewer.jupyter.org/gist/fernandokm/bc6f39be5d85fd31de8879edada24c12

Parabéns!

Você conseguiu construir com sucesso uma rede neural à mão que classifica as imagens do dataset MNIST! O código completo pode ser encontrado aqui.

Você deve estar se perguntando se não existe uma biblioteca de python que já tenha uma rede neural implementada, assim como temos feito com os outros modelos vistos até agora. E a resposta é sim, e esse módulo se chama TensorFlow.

No entanto, optamos por fazê-lo a mão, a fim de retirar a sensação de “caixa preta” que geralmente as bibliotecas trazem consigo. Até porque redes neurais é um dos modelos mais importantes na realização de predições, e sobretudo abre uma porta porta para um novo patamar em Machine Learning, devido a sua vasta gama de aplicações.

Gostaram do texto? Esperamos muito que sim. Estamos sempre buscando aproximar as pessoas desse mundo de inteligência artificial, da maneira mais agradável possível. Se quiserem conhecer mais do que fazemos, não deixem de nos seguir nas redes sociais: Facebook, Instagram, Linkedin e, obviamente, no Medium.

Por hoje é só. Até logo!

--

--