Como funcionam as Redes Neurais Convolucionais (CNNs)

Vinícius Trevisan
Data Hackers
Published in
10 min readDec 15, 2021
Original by Andrew Schultz on Unsplash

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:

Convolução entre três diferentes kernels e três diferentes funções. Em todos os gráficos, a linha vermelha representa o kernel, a linha azul representa a função original e a linha verde representa a convolução. Imagem do autor.

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.

Aplicações de diferentes kernels em uma imagem.
Aplicações de diferentes kernels em uma imagem. A imagem original é representada em (a), juntamente com o kernel identidade, que não faz modificações. Em (b) a imagem passou por um kernel que aumenta a nitidez reforçando o pixel central e aumentando a diferença dele em relação à sua vizinhança. Em (c) foi usado um kernel de desfoque, que substitui um pixel pela média dele com seus vizinhos. A imagem em (d) é o resultado de aplicar na imagem em escala de cinza um kernel que reforça suas linhas verticais, enquanto (e) reforça suas linhas horizontais. O kernel em (f) reforça os contornos (bordas) da imagem.

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.

Convolução bidimensional em imagens. O kernel (azul) é convolucionado sobre a imagem (vermelha), ou seja, operação é feita nos pixels da imagem que correspondem à posição em que o kernel está. O resultado da operação de convolução determina o valor do pixel central dessa região, e o conjunto dos resultados da aplicação do kernel em cada pixel forma a imagem convolucionada (verde), que é 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.

Aplicação de padding em imagens. Em (a) a imagem original passou por zero padding, que inseriu uma borda com valor 0 (preto) ao redor dela. A imagem em (b) é resultado do reflection padding, que criou bordas a partir da reflexão da imagem interna. Em (c), assim como em (a) a imagem passou por um padding constante, mas com um valor diferente de 0, nesse caso.

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.

Funcionamento do MaxPool. Cada grupo de 2x2 pixels é representado pelo valor de maior intensidade 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.

Aplicação de pooling em uma imagem de bordas. Em (a) a imagem de bordas detectadas por um kernel adequado. Em (b) e em (c) a aplicação do MaxPool e do AvgPool, respectivamente, ambos com tamanho 2. As imagens resultantes tem metade do tamanho da original.

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.

Efeito do stride na convolução. A parte superior da imagem mostra um caso em que o stride é 1, ou seja, o kernel anda um pixel na horizontal para cada iteração e no final da linha anda um pixel na vertical. A metade inferior ilustra um caso com valor de stride igual a 2, então o kernel anda dois pixels na horizontal e quando a linha termina, anda dois pixels na vertical. Em ambas as imagens os pixels azuis correspondem ao centro do kernel de tamanho 5x5 em cada iteração, e pode-se verificar que a convolução com maior stride gera uma saída de menor dimensão.

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.

Convolução transposta. Uma convolução de kernel 3x3 com stride 1 em uma imagem 5x5 geraria uma imagem de dimensões 3x3. A convolução transposta aplicada na imagem 3x3 (vermelha) para se obter uma imagem 5x5 (verde) é feita em duas etapas. Primeiramente (centro) se insere pixels de valor 0, representados pela cor cinza, entre os pixels da imagem original, juntamente com pixels de borda. Em seguida se faz uma convolução nessa imagem alterada também usando um kernel 3x3 e stride 1.

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.

Artefatos quadriculados causados pela convolução transposta. À esquerda, uma convolução de um kernel de 3x3 com stride 2 cria regiões de sobreposição conforme ele passa pela imagem. Essa sobreposição gera artefatos quadriculados na imagem convolucionada.

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.

--

--

Vinícius Trevisan
Data Hackers

Master in Deep Learning for CV | Data Scientist @ Enertiv | Generative AI Researcher | https://viniciustrevisan.com/