Como funcionam as Redes Neurais Convolucionais (CNNs)
Nesse artigo pretendo fazer uma introdução completa às Redes Neurais Convolucionais, seu funcionamento e os principais termos relacionados. As referências usadas aqui também estão no meu github.
As Redes Neurais Convolucionais (CNN) ou Redes Convolucionais são um tipo de rede neural que utiliza a operação de convolução em vez da multiplicação por matrizes em ao menos uma de suas camadas.
Esse tipo de rede é bastante efetiva em aplicações em que os dados são dispostos de forma que a relação de vizinhança entre os elementos é relevante, como no caso de imagens que são representadas por matrizes bidimensionais de pixels, ou no caso de séries temporais ou dados de áudio, que são sequências unidimensionais de dados amostrados em intervalos de tempo regulares.
Por serem eficazes em extrair atributos das imagens, as CNNs são amplamente empregadas em aplicações de detecção e reconhecimento de objetos, reconhecimento facial, segmentação semântica, processamento e manipulação de imagens, dentre outras. Filtros inteligentes de câmeras também usam as CNNs para modificar a face da pessoa, sistemas de navegação de robôs e carros autônomos utilizam CNNs para detectar obstáculos e elementos do trajeto, e sistemas mais modernos de visão computacional também as empregam.
A operação de convolução é uma operação matemática linear entre duas funções. Sejam x(t) e w(a) funções reais, respectivamente chamadas de entrada e kernel. Considerando que x(t) é uma função contínua dependente do tempo t e que w(a) é uma função de peso que decai com maiores valores de a, podemos fazer uma média ponderada de x de forma que os valores mais recentes tenham maior relevância que os valores mais antigos através da expressão
que é a operação de convolução entre x e w, representada pelo operador asterisco (x * w). A saída s(t) é normalmente chamada de feature map, ou mapa de atributos.
A operação pode ser expandida também para funções discretas, que é o caso utilizado nos algoritmos de CNNs unidimensionais. A convolução se torna
No caso de uma convolução 2D, muito utilizada no processamento de imagens, e considerando I como a imagem de entrada e K como o kernel bidimensional, a convolução pode ser representada pela expressão
A imagem a seguir ilustra a convolução de diferentes kernels em diferentes funções:
Normalmente os kernels são muito menores do que as imagens, e através da operação de convolução um kernel é utilizado para processar toda uma imagem, portanto, diz-se que os parâmetros são compartilhados. Por conta dessa representação reduzida, cada camada da rede convolucional deve aprender uma quantidade menor de parâmetros.
As ANNs (redes neurais artificiais), por outro lado, utilizam matrizes de pesos para representar as relações entre os elementos da camada atual com as saídas da camada anterior. Como cada conexão é independente das outras, cada peso representa especificamente uma única conexão, o que faz com que a rede precise aprender uma grande quantidade de parâmetros. Comparativamente, por usarem menos parâmetros, as CNNs costumam ser mais eficientes em termos de memória e são treinadas mais rapidamente do que as ANNs.
Convolução em imagens
A convolução é amplamente utilizada em processamento de imagens. A depender do kernel utilizado, é possível manipular a nitidez e o desfoque, estilizar a imagem, ou inclusive detectar as arestas dos objetos presentes. A Figura abaixo exemplifica a aplicação de alguns diferentes tipos de kernels em uma imagem base.
O kernel é aplicado em cada pixel da imagem, e o resultado da convolução entre o pixel e a região da imagem substitui o pixel central na imagem resultante, como ilustrado na Figura abaixo. O kernel não pode ser aplicado nos pixels de borda, pois parte dele não teria correspondência com nenhum pixel, e como consequência a imagem gerada é um pouco menor do que a imagem original.
No exemplo da Figura acima ocorre a convolução de uma imagem (vermelha) de 16 pixels de largura por 12 pixels de altura com um kernel (azul) de 5 pixels de largura por 5 pixels de altura. A imagem resultante (verde) tem dimensões 12x8. O cálculo da largura e da altura da imagem resultante é feito pelas expressões abaixo:
Em CNNs, normalmente consideramos a convolução como uma camada da rede, e cada camada normalmente tem mais do que um kernel, produzindo a mesma quantidade de mapas de atributos.
Por exemplo, uma camada convolucional com 3 kernels que recebe como entrada uma imagem em escala de cinza de dimensões (win x hin x 1) irá produzir como saída uma matriz de dimensões (wout x hout x 3). Essa saída não é mais considerada uma imagem, mas sim um conjunto de três mapas de atributos que correspondem aos atributos reconhecidos pelos três kernels da camada. Encadeando os mapas de atributos através da estrutura de camadas da rede, ela pode capturar atributos mais gerais ou mais detalhados da imagem, e esse é o processo pelo qual as CNNs com diversas camadas aprendem a reconhecer objetos e características das imagens.
Kernel multicanal
Em uma imagem com mais de um canal, como no caso de imagens RGB, a convolução acontece com um kernel em cada canal. O conjunto de canais é chamado de filtro, e cada filtro tem a mesma quantidade de kernels que a imagem ou mapa de atributos onde esse filtro será aplicado.
Ao final da operação de convolução, um filtro de n kernels, aplicado a n camadas terá n mapas de atributos como saída. Uma operação de agregação como soma ou média deve ser feita nesses mapas de atributos para que o filtro tenha uma saída única.
Padding
Uma forma de contornar o problema convolução do kernel nas bordas é a partir da aplicação de padding na entrada da convolução, que nada mais é do que acrescentar pixels no perímetro da imagem. A Figura abaixo ilustra três tipos de padding, o zero padding (a) que insere o valor 0 em todo o perímetro da imagem original, o reflection padding (b) que reflete a imagem a partir da borda original ou o constant padding (c) que preenche a nova borda com um valor constante, que pode inclusive ser 0.
A largura e altura da borda nova inserida pelo padding é definida pelo projetista da rede, mas normalmente é feito de forma a anular o efeito de redução da imagem convolucionada. O cálculo das novas dimensões é feito de acordo com as expressões a seguir:
Pooling
Cada camada da rede convolucional gera um ou mais mapas de atributos a partir da entrada que for apresentada. Esses mapas contém as características que a rede conseguiu obter por meio da convolução naquela camada, e o treinamento é responsável por ajustar os kernels de forma que os atributos presentes nos mapas sejam os mais importantes para as camadas seguintes.
Essas camadas, no entanto, podem também gerar ruído e informações que são pouco relevantes para o restante da rede, por exemplo como o kernel de bordas detectou alguns contornos que não são relevantes para determinar a forma do cachorro.
A técnica de pooling substitui agrupamentos de pixels por um valor que os represente. A Figura abaixo ilustra um MaxPool
de tamanho 2, em que os pixels são agrupados em quadrados de tamanho 2x2 e são substituídos pelo maior valor de intensidade de pixel do grupo. Um comportamento similar pode ser feito com o AvgPool
, em que o pixel resultante é a média dos valores de intensidade dos pixels do grupo.
Como consequência imediata da aplicação do pooling, a informação fica mais condensada, mantendo apenas o que é importante para as próximas camadas e reduzindo ruído, e saída é reduzida em um fator que depende do tamanho do pooling, o que faz com que as camadas seguintes tenham menos pixels para processar, sendo mais eficientes. A Figura abaixo ilustra o efeito de se aplicar o MaxPool
(b) e o AvgPool
(c) na imagem de bordas detectadas do cachorro.
Outro importante efeito do MaxPool
é que ele torna a rede robusta a pequenas translações na entrada. Como os pixels de uma vizinhança são representados pelo maior valor dentre eles, não há nenhuma diferença na saída se o pixel mais intenso estiver em outra posição, desde que esteja dentro da mesma vizinhança.
Stride
Uma outra forma de se reduzir a dimensão da imagem após uma convolução é utilizando o parâmetro de stride, que representa quantos pixels o kernel anda em cada iteração. Nos exemplos anteriores o stride utilizado foi 1, o que significa que após a iteração o kernel se moverá para o pixel vizinho, e que quando a operação terminar a linha, ele irá para a linha seguinte. A parte superior da figura abaixo ilustra essa situação com um kernel de dimensões 5x5, e os pixels azuis correspondem ao centro do kernel em cada iteração.
A metade inferior da figura abaixo, no entanto, representa o uso do mesmo kernel, mas com um stride de 2 pixels. Isso significa que o kernel avançará para o segundo pixel depois de cada iteração, e quando a linha terminar, ele também irá para a segunda linha. Da mesma forma, os pixels azuis representados na figura correspondem à posição do centro do kernel em cada iteração. Como menos regiões são visitadas, a imagem final também é menor.
A redução de dimensão é importante para situações em que a dimensão da saída da rede é menor do que a dimensão da entrada, como um encoder que quer representar a imagem de uma forma comprimida, ou um classificador binário que tem como saída um único valor booleano que indica se a imagem faz parte da classe ou não.
O parâmetro de stride pode ser definido separadamente para a largura (wstride) e para a altura (hstride), e dessa forma o cálculo das dimensões da imagem considerando o padding e o stride é feito pelas expressões
Convolução Transposta
Em outros tipos de aplicação pode-se desejar aumentar a dimensão da imagem a cada camada. Alguns métodos generativos, por exemplo, partem de uma representação menor como um vetor de instruções e processam essa informação através de várias camadas até gerar uma representação maior como uma imagem.
Uma forma de fazer isso utilizando a operação de convolução é através da convolução transposta (muitas vezes confundida com deconvolução), que consiste em utilizar padding e stride para aumentar a imagem de entrada antes de passar ela por uma convolução “tradicional”, de forma que a imagem de saída seja maior do que a imagem de entrada. A Figura a seguir ilustra o processo pelo qual uma imagem 3x3 passa por uma convolução transposta de kernel 3x3 e stride 1 para gerar uma imagem 5x5.
O cálculo das dimensões da imagem gerada pela convolução transposta é feito a partir das expressões
A convolução transposta não é a única forma de resolver o problema do aumento de dimensão. Outras arquiteturas utilizam métodos clássicos de redimensionamento, como interpolação bilinear ou interpolação do vizinho mais próximo (nearest-neighbor interpolation) seguidos por camadas de convolução na nova resolução de imagem.
Artefatos Quadriculados
A convolução transposta gera uma imagem que é resultado da convolução de um kernel sobre uma imagem menor. Uma característica dessa operação é que o kernel se sobrepõe quando passa sobre a imagem de entrada, e nos pontos dessa sobreposição ele reforça a importância daqueles pixels na criação da imagem de maior dimensão. Como podemos ver na Figura abaixo, o resultado dessa sobreposição através de várias camadas reforça um padrão de artefatos quadriculados que aparecem periodicamente na imagem gerada.
Outros métodos de redimensionamento na rede convolucional, como a interpolação bilinear seguida por uma convolução, não apresentam esse efeito adverso, porém costumam gerar imagens mais desfocada.
Espero que tenham gostado. O texto é um excerto da minha dissertação de mestrado.
Um agradecimento especial ao meu cachorro Bruce, que gentilmente me cedeu sua foto para os exemplos.