Demonstrações interativas da aula de introdução a redes convolucionais
Cinco visualizações que acompanham os slides: a convolução 2D zi,j = Σm,n wm,n·xi+m−2, j+n−2 com kernels clássicos e pesos compartilhados, o efeito de padding, stride e dilation no tamanho da saída, o pooling (max e average) e sua relação com invariância a translação, o crescimento do campo receptivo camada a camada pela recursão RFl = RFl−1 + (Kl − 1)·∏i<l Si, e a convolução aplicada a uma imagem real (a sua, se quiser) com kernels clássicos como Sobel, DoG e blur gaussiano.
A convolução transforma a entrada x em uma saída z: cada posição é uma combinação linear de uma janela 3×3 ao redor, zi,j = Σm=13 Σn=13 wm,n·xi+m−2, j+n−2, com os mesmos nove pesos wm,n (o kernel) reaparecendo em toda janela. Desenhe na entrada 8×8 (clique e arraste), escolha um kernel e veja o mapa de características recalculado ao vivo. Passe o mouse sobre qualquer célula para destacar a janela deslizante e o valor zi,j correspondente. Usamos zero padding para a saída manter o tamanho 8×8.
Entrada x (8×8): clique e arraste para desenhar; o mouse destaca a janela 3×3
Mapa de características z = w ∗ x (verde positivo, rosa negativo)
💡 Com o padrão borda vertical, o Sobel vertical responde forte na transição e zero nas regiões constantes; troque para o Sobel horizontal e a resposta some, cada kernel detecta uma orientação. No padrão xis, compare blur (suaviza) com sharpen (realça). Edite um peso do kernel (por exemplo, zere o w2,1 = −2 do Sobel) e observe o mapa inteiro mudar: são os mesmos nove pesos em todas as janelas.
Três hiperparâmetros vistos em aula controlam a geometria da convolução: o zero padding P completa a borda com zeros (com P = 0 temos o valid padding), o stride S é o deslocamento do kernel a cada passo (maior que 1 age como downsampling) e a dilation D insere D − 1 zeros entre os pesos, alcançando posições (i ± D, j ± D) sem aumentar o número de parâmetros (convenção do PyTorch: D = 1 é o kernel denso). Para entrada N×N e kernel K×K, o tamanho da saída é O = ⌊(N + 2P − D·(K−1) − 1)/S⌋ + 1. Use ⏭ ou ▶ para percorrer as posições do kernel.
Entrada N×N com anel de padding (tracejado); laranja: posições lidas pelo kernel
Saída O×O: células já computadas pela varredura
💡 Com N = 8, K = 3, P = 0, S = 1 a saída é 6×6 (valid); suba para P = 1 e ela volta a 8×8 (same). Com S = 2 a resolução cai para cerca da metade, como no slide de stride. Teste K = 3, D = 2: o kernel efetivo passa a cobrir 5×5 com apenas 9 pesos. E com K = 5, P = 0 em uma entrada 4×4 o kernel não cabe: a fórmula dá O ≤ 0.
O downsampling por pooling agrega cada janela 2×2 (stride 2) em um único valor: o max pooling mantém o maior valor da janela e o average pooling usa a média. Como visto em aula, queremos que a semântica seja estável frente a translações: f[t[x]] = f[x] define invariância e f[t[x]] = t[f[x]] define equivariância. Arraste o padrão na entrada (ou use os sliders de Δx, Δy) e compare a saída do pooling com a saída original deslocada: deslocamentos múltiplos do stride deslocam a saída inteira (equivariância), e deslocamentos de 1 pixel mudam pouca coisa no max pooling (invariância local).
Entrada 8×8: arraste o padrão; as janelas 2×2 do pooling estão demarcadas
Saída 4×4 do pooling; contorno rosa: células que diferem da saída original deslocada
💡 No max pooling, vá de Δ = (0, 0) para (1, 0): a entrada mudou em todas as células do padrão, mas pouquíssimas células da saída diferem. Agora teste Δ = (2, 0): nenhuma célula difere, a saída é exatamente a original deslocada uma posição (equivariância ao stride). Troque para average pooling e repita Δ = (1, 0): a média muda em quase todas as janelas tocadas, o max é mais robusto a translações pequenas.
O campo receptivo de uma unidade oculta é a região da entrada que ela enxerga. Empilhando convoluções (sem padding, visão 1D de um corte da imagem), ele cresce pela recursão vista em aula: RFl = RFl−1 + (Kl − 1)·∏i<l Si, com RF0 = 1. Cada camada acrescenta Kl − 1 posições do espaço original, multiplicadas pelo produto acumulado dos strides anteriores. Ajuste Kl e Sl de cada camada e clique em qualquer unidade de H1, H2, H3 para ver o cone do seu campo receptivo até a entrada x.
Pilha de camadas (corte 1D): clique em uma unidade para ver seu campo receptivo na entrada
RFl por camada l, pela recursão dos slides
💡 Com três convoluções 3×3 e S = 1, o RF cresce 3, 5, 7, como no exemplo dos slides. Mude S₂ = 2 e veja RF₃ = 9: o stride acelera o crescimento, ao custo de reduzir a resolução (note quantas unidades sobram em H₃). Compare também K = 5 em uma camada com duas camadas de K = 3: mesmo campo receptivo com 25 contra 18 pesos, a justificativa da VGG para empilhar kernels pequenos.
A mesma operação da seção 1, agora em uma imagem de verdade convertida para tons de cinza: cada pixel da saída é a soma da janela da entrada ponderada pelos pesos do kernel. Escolha um kernel clássico no menu (cada um foi desenhado à mão para extrair uma característica específica, exatamente o que uma CNN aprende sozinha durante o treino) e ajuste stride S, padding P e dilation D para ver o efeito na geometria: O = ⌊(N + 2P − D·(K−1) − 1)/S⌋ + 1 em cada eixo. Passe o mouse sobre o mapa de características para destacar a janela que o kernel leu na entrada, e envie uma foto sua para testar os filtros nela.
Entrada x em tons de cinza; laranja: janela lida pelo kernel na posição sob o mouse
Mapa de características z = w ∗ x; passe o mouse para inspecionar cada pixel
💡 Compare Sobel x e Sobel y na imagem padrão: cada um acende um conjunto diferente de bordas (verticais ou horizontais), por isso uma camada convolucional tem vários kernels em paralelo, um mapa de características por filtro. O DoG responde a manchas claras cercadas de escuro (centro e periferia, como células da retina) e o blur gaussiano é um passa-baixas que apaga os detalhes finos. Suba o stride para S = 4 e veja a saída encolher pela fórmula; aumente a dilation com o Sobel e o detector passa a reagir a transições mais largas sem ganhar nenhum peso novo. Ligue a ReLU no modo verde/rosa: tudo que era negativo (rosa) vira zero, é o mapa de ativações que segue para a próxima camada.