Convolution: modelo mental correto
Convolução 2D é um filtro deslizante que calcula produto escalar numa janela. Três propriedades importam: locality (pixel vizinho influencia mais), weight sharing (mesmo filtro em todas as posições — poucos parâmetros) e translation equivariance (objeto deslocado gera feature map deslocado). É esse bias indutivo que faz CNN aprender com menos dados que um MLP.
import torch.nn as nn
block = nn.Sequential(
nn.Conv2d(in_channels=64, out_channels=128, kernel_size=3, stride=1, padding=1),
nn.BatchNorm2d(128),
nn.ReLU(inplace=True),
nn.MaxPool2d(kernel_size=2), # downsample 2x
)ResNet: skip connections
class BasicBlock(nn.Module):
def __init__(self, ch):
super().__init__()
self.conv1 = nn.Conv2d(ch, ch, 3, padding=1, bias=False)
self.bn1 = nn.BatchNorm2d(ch)
self.conv2 = nn.Conv2d(ch, ch, 3, padding=1, bias=False)
self.bn2 = nn.BatchNorm2d(ch)
def forward(self, x):
identity = x
out = torch.relu(self.bn1(self.conv1(x)))
out = self.bn2(self.conv2(out))
out = out + identity # skip connection
return torch.relu(out)O truque conceitual: a rede aprende o resíduo (quanto mudar), não a representação toda. Se mudar nada é ótimo, basta zerar os pesos de F(x) — gradiente flui direto pelo atalho.
EfficientNet e ConvNeXt
EfficientNet (2019) trouxe compound scaling. ConvNeXt (2022) pegou o design do Swin Transformer e aplicou em CNN: kernel 7x7, LayerNorm em vez de BatchNorm, GELU, inverted bottleneck. Mostrou que CNN bem modernizada ainda compete com ViT no ImageNet.
# Transfer learning pragmático com timm
import timm
model = timm.create_model("convnext_tiny", pretrained=True, num_classes=10)
# Congelar backbone nas primeiras épocas
for p in model.parameters():
p.requires_grad = False
for p in model.head.parameters():
p.requires_grad = TrueTransfer learning: receita que funciona
# Fase 1: head only, lr alto (1e-3), 3-5 epochs
# Fase 2: descongela tudo, lr baixo (1e-4 head, 1e-5 backbone), 10-20 epochs
from torch.optim import AdamW
param_groups = [
{"params": model.head.parameters(), "lr": 1e-4},
{"params": [p for n, p in model.named_parameters() if "head" not in n], "lr": 1e-5},
]
optim = AdamW(param_groups, weight_decay=1e-4)Em 2026, para 90% dos problemas de classificação com <100k imagens: timm + ConvNeXt-Tiny ou EfficientNet-B0 pretrained → fine-tune em 2 fases → acurácia competitiva com uma fração do custo de treinar ViT do zero.