Por dentro do cuda-convnet: camadas do AlexNet em duas GPUs e a era ImageNet

O marco alcançado pelo AlexNet em 2012 apoiou-se na engenharia de GPUs de baixo nível (cuda-convnet), numa arquitetura dividida entre duas GPUs, e desencadeou diretamente uma corrida rápida por modelos de visão computacional mais profundos e eficientes.
1. Como o cuda-convnet funcionava por dentro
Antes de a NVIDIA criar a cuDNN, Alex Krizhevsky escreveu o cuda-convnet diretamente em CUDA C++ para extrair a velocidade máxima das primeiras GPUs com arquitetura Fermi (a GTX 580). As bibliotecas modernas normalmente desdobram convoluções em enormes multiplicações de matrizes (im2col), o que consome quantidades imensas de memória. Como cada GTX 580 tinha apenas 3 GB de VRAM, Krizhevsky desenvolveu rotinas diretas para GPU que economizavam memória.
- Organização da memória orientada a lotes (CHWN). Os frameworks modernos adotam por padrão NCHW (Lote, Canais, Altura, Largura), que coloca todos os pixels de uma única imagem lado a lado na memória. Krizhevsky armazenava os tensores na ordem CHWN, deixando a dimensão do lote (N) por último. Quando 32 threads da GPU em um warp processavam 32 imagens diferentes exatamente na mesma posição de pixel, suas leituras de memória eram totalmente contíguas (coalescidas), maximizando a largura de banda da VRAM.
- Divisão em blocos na memória compartilhada. Ler a VRAM global leva centenas de ciclos de clock, enquanto a memória compartilhada no chip (apenas 48 KB por Streaming Multiprocessor na GTX 580) leva somente alguns ciclos. Os blocos de threads carregavam pequenas regiões 2D de pixels das imagens e pesos dos filtros na memória compartilhada, calculando dezenas de produtos escalares antes de gravar os resultados finais de volta na VRAM.
- Reutilização de registradores no nível do warp. Krizhevsky atribuía a cada bloco de threads o cálculo simultâneo de um bloco de pixels de saída para vários filtros. Mantendo os pesos dos filtros nos rápidos registradores da GPU, uma única região da imagem carregada da memória compartilhada era reutilizada em muitos filtros de uma só vez, reduzindo drasticamente o tráfego de memória.
2. Camadas específicas do AlexNet e divisão entre duas GPUs
O AlexNet continha 8 camadas treináveis (5 convolucionais e 3 totalmente conectadas), totalizando cerca de 60 milhões de parâmetros. Como uma única GTX 580 não dispunha dos mais de 3 GB de VRAM necessários para ativações, pesos e gradientes, Krizhevsky dividiu a rede verticalmente em dois fluxos paralelos.
| Camada | Tipo | Total de filtros / neurônios | Divisão por GPU | Comunicação entre GPUs |
|---|---|---|---|---|
| Entrada | Imagem RGB | 224 × 224 × 3 | Copiada para ambas as GPUs | Nenhuma |
| Conv 1 | 11×11 Conv (passo 4) + MaxPool | 96 filtros | 48 filtros por GPU | Isolada (sem sincronização) |
| Conv 2 | 5×5 Conv + MaxPool | 256 filtros | 128 filtros por GPU | Isolada (lê apenas a Conv 1 da própria GPU) |
| Conv 3 | 3×3 Conv | 384 filtros | 192 filtros por GPU | Sincronização completa (lê todos os 256 mapas de ambas as GPUs) |
| Conv 4 | 3×3 Conv | 384 filtros | 192 filtros por GPU | Isolada (lê apenas a Conv 3 da própria GPU) |
| Conv 5 | 3×3 Conv + MaxPool | 256 filtros | 128 filtros por GPU | Isolada (lê apenas a Conv 4 da própria GPU) |
| FC 6 | Totalmente conectada + Dropout | 4.096 neurônios | 2.048 neurônios por GPU | Sincronização completa (conecta-se a todas as saídas da Conv 5) |
| FC 7 | Totalmente conectada + Dropout | 4.096 neurônios | 2.048 neurônios por GPU | Sincronização completa (conecta-se a todos os neurônios da FC 6) |
| FC 8 | Totalmente conectada (Softmax) | 1.000 classes | 1.000 classes (combinadas) | Sincronização completa (produz as previsões finais do ImageNet) |
- Especialização natural das características. Curiosamente, como as camadas 1 e 2 nunca se comunicavam entre GPUs, as duas placas desenvolviam consistentemente funções especializadas. Uma GPU aprendia detectores de bordas e texturas independentes de cor (filtros em escala de cinza semelhantes aos de Gabor), enquanto a outra se especializava em manchas e tonalidades de cores específicas.
- Minimização dos gargalos do PCIe. Transferir dados pelo barramento PCIe da placa-mãe era lento. Ao restringir rigorosamente a comunicação entre GPUs à Conv 3 e às camadas totalmente conectadas, Krizhevsky mantinha as GPUs calculando em velocidade máxima sem paradas para transferências de dados.
3. O que aconteceu depois na era ImageNet
A vitória do AlexNet iniciou uma corrida anual na competição ImageNet, levando as redes de 8 camadas a mais de 150 em apenas três anos.
- 2013 – ZFNet (visualização e ajuste). Matthew Zeiler e Rob Fergus introduziram técnicas de deconvolução para visualizar o que o AlexNet realmente estava vendo nas camadas ocultas. Ao diminuir os filtros da primeira camada do AlexNet de 11×11 para 7×7 e reduzir o passo, o ZFNet baixou a taxa de erro top-5 no ImageNet de 15,3% para 14,8%.
- 2014 – VGGNet (profundidade com filtros uniformes 3×3). Pesquisadores de Oxford demonstraram que empilhar muitas convoluções pequenas de 3×3 (até 16 ou 19 camadas) funcionava melhor do que usar filtros grandes isolados (como 11×11 ou 5×5). Duas camadas 3×3 empilhadas cobrem a mesma área de visão de 5×5 com menos parâmetros e ativações ReLU não lineares adicionais, reduzindo as taxas de erro para 7,3%.
- 2014 – GoogLeNet / Inception (eficiência multiescala). O Google introduziu o módulo Inception, que executava convoluções de 1×1, 3×3 e 5×5 em paralelo dentro da mesma camada e combinava as saídas. Usando convoluções de 1×1 de baixo custo para comprimir a profundidade dos canais antes dos cálculos caros, o GoogLeNet chegou a 22 camadas e a uma taxa de erro de 6,7%, usando 12 vezes menos parâmetros que o AlexNet.
- 2015 – ResNet (conexões de salto & 152 camadas). Simplesmente empilhar mais camadas acabava prejudicando a precisão do treinamento, porque os gradientes desapareciam durante a retropropagação. A Microsoft Research resolveu isso com conexões residuais (conexões de salto), permitindo que os dados contornassem camadas por atalhos diretos de identidade. Isso viabilizou redes de 152 camadas, alcançando uma taxa de erro top-5 de 3,57% e superando pela primeira vez o desempenho humano no benchmark (~5%).