13. Vision Transformers
O capítulo anterior construiu um encoder para sequências de tokens de texto. Em 2021, Dosovitskiy et al. fizeram uma pergunta deliberadamente crua: e se você alimentar esse mesmo encoder, sem mudar nada, com uma imagem?1 A resposta — "An Image is Worth 16×16 Words" — foi que, com dados suficientes, um Transformer quase puro bate uma CNN em classificação de imagens, sem nenhuma convolução em lugar nenhum.
O resultado importou menos como número de classificação e mais como demonstração: a arquitetura não precisa saber que está olhando para uma imagem. É por isso que o ViT é o capítulo-ponte. Uma vez que você o tenha, os encoders de imagem dentro do CLIP, dentro de todo modelo de visão-linguagem, dentro do Stable Diffusion e do DiT deixam de ser coisas separadas a aprender.
O trade-off de viés indutivo, com o preço certo
O capítulo 10 chamou a convolução de um prior forte, barato e fixo. Uma camada de auto-atenção não tem nada disso: cada patch pode atender a qualquer outro patch desde a primeira camada e qualquer noção de que pixels vizinhos têm relação precisa ser aprendida dos dados.
Isso é uma troca, não uma melhoria e o artigo original mediu os dois lados dela:
| Dados de pré-treino | Resultado |
|---|---|
| Só ImageNet-1k (~1,3M de imagens) | O ViT perde para uma ResNet comparável |
| ImageNet-21k (~14M) | Aproximadamente empatados |
| JFT-300M (~300M) | O ViT vence e transfere melhor |
Leia a tabela como uma única frase: o prior convolucional é um piso em escala pequena e um teto em escala grande. Abaixo de um certo limiar de dados, ele dá de graça o que o Transformer precisa comprar; acima dele, proíbe relações que o Transformer fica livre para descobrir.
Esse limiar não é lei da natureza e ele se moveu
O DeiT2 mostrou que boa parte da afirmação "o ViT precisa de 300M de imagens" era, na verdade, "o ViT precisa de uma receita de treino que ninguém tinha escrito ainda" — com a augmentation, a regularização e um token de destilação certos, um ViT treina de forma competitiva só com o ImageNet-1k. O ConvNeXt8 depois fez o argumento simétrico do outro lado. A leitura moderna é que receita e escala explicam a maior parte do buraco que os artigos de 2021 atribuíram à arquitetura.
O pipeline
O único mecanismo genuinamente novo é o primeiro passo. Tudo depois dele é o encoder que você já conhece.
Uma imagem \(x \in \mathbb{R}^{H \times W \times C}\) é cortada em patches \(P \times P\) sem sobreposição, cada um achatado e projetado por uma única camada linear compartilhada:
Para \(224 \times 224\) e \(P = 16\), são \(N = 196\) tokens. Embeddings posicionais são somados, porque a atenção sozinha não distingue o patch do canto superior esquerdo do canto inferior direito e a sequência atravessa \(L\) blocos de encoder pré-norm padrão.
O patch embedding é uma convolução
Uma projeção linear sobre patches \(P \times P\) sem sobreposição é exatamente Conv2d(C, d, kernel_size=P, stride=P) e é assim que toda implementação faz — uma operação que divide em patches e projeta ao mesmo tempo. Ou seja, a afirmação "sem convoluções" tem um asterisco: há exatamente uma, no início e é o único lugar por onde um prior espacial entra no modelo.
O tamanho do patch é toda a decisão de projeto
\(P\) controla duas coisas ao mesmo tempo, em direções opostas e não há mais nada num ViT com tanta alavancagem.
Tudo que está dentro de um patch é comprimido em um vetor de dimensão \(d\) antes de a atenção sequer rodar. Nenhuma camada posterior recupera o que a projeção descartou. Arraste o tamanho do patch até 32 e veja um rosto desaparecer dentro de quatro tokens.
Puxe para o outro lado e a contagem explode: reduzir \(P\) pela metade quadruplica \(N\) e o termo de atenção cresce com \(N^2\). Dobrar a resolução faz o mesmo. Essa única quadrática é a razão pela qual:
- 14 e 16 são os tamanhos de patch quase universais, há cinco anos;
- modelos de visão-linguagem em alta resolução ladrilham uma imagem grande em vários recortes de 224 ou 336 pixels em vez de alimentar o encoder com uma grade enorme;
- o patch-and-pack no estilo NaViT9 existe — empacotar imagens de resolução variável numa única sequência, manter a proporção nativa e pular os tokens de que não se precisa.
O que mudou desde 2021
O ViT de 2021 ainda é reconhecível, mas um modelo que você baixaria hoje difere em cinco pontos e todos valem a pena porque aparecem no encoder de visão de todo modelo multimodal.
-
O token
[CLS]praticamente saiu de cena
Ler a classe de um token especial era um hábito herdado do BERT. A maioria dos encoders atuais usa global average pooling ou uma pequena cabeça de attention-pooling sobre os tokens de patch — tão bom ou melhor e remove um token estranho que precisa ser treinado para ser um resumo.
E num modelo de visão-linguagem a cabeça de classificação sumiu por completo: o que é passado ao modelo de linguagem são os tokens de patch, projetados no espaço de embedding do LLM.
-
Posições aprendidas → RoPE 2-D
Embeddings posicionais absolutos aprendidos precisam ser interpolados sempre que a resolução muda e essa interpolação custa acurácia. A RoPE 2-D — a rotação do capítulo 11, aplicada separadamente aos eixos de linha e coluna — é relativa por construção e absorve mudança de resolução sem cirurgia.
-
Tokens de registro
ViTs treinados colocam atenção enorme em alguns patches de fundo sem significado — usando-os como rascunho, o que destrói os mapas de atenção e prejudica tarefas densas. Acrescentar um punhado de tokens de registro10 sem uso dá ao modelo onde guardar esse estado. Os artefatos somem, os mapas de atenção voltam a ser interpretáveis e as métricas de predição densa melhoram. Uma correção de uma linha para um bug de cinco anos.
-
Hierarquia, onde ela é necessária
Um ViT simples mantém uma resolução do começo ao fim, o que é desperdício para detecção e segmentação. O Swin3 reintroduziu atenção em janelas e uma pirâmide; o ViTDet mostrou que um ViT simples também pode ser adaptado com uma pirâmide de features por cima. Os dois estão vivos; o backbone simples venceu para pré-treino, o hierárquico para tarefas densas downstream.
Para que serve um ViT, de fato, em 2026
Muito pouco do uso em produção é "classifique esta imagem em 1000 classes". O ViT venceu ao se tornar a forma padrão de transformar pixels em tokens para outra coisa:
| Uso | O que o ViT fornece |
|---|---|
| Modelos de visão-linguagem | O encoder de imagem. Tokens de patch são projetados e prefixados à sequência de texto — é assim que modelos da classe GPT-4o, Claude e Gemini enxergam. |
| Contrastivo imagem-texto | A torre de imagem do CLIP e do SigLIP (capítulo 19) e portanto o condicionamento de texto de todo gerador de imagens. |
| Features auto-supervisionadas | O DINOv2/v37 produz features densas de propósito geral sem rótulos, que batem as supervisionadas em segmentação, profundidade e correspondência — usadas congeladas, como extrator, em robótica e imagem médica. |
| Backbones generativos | DiT e MMDiT são ViTs operando sobre patches latentes em vez de patches de pixel (capítulo 22). |
| Pré-treino mascarado | MAE6 — mascare 75% dos patches e reconstrua. Barato (o encoder só vê os 25% visíveis) e um forte inicializador. |
A razão pela qual ele venceu não é acurácia
Um ViT e uma ConvNet moderna pontuam praticamente igual no ImageNet. O ViT está em toda parte mesmo assim, porque ele emite uma sequência de tokens no mesmo formato que o texto usa. Concatenar tokens de patch com tokens de palavra e rodar um único Transformer sobre os dois é trivial; fazer o equivalente com um mapa de features convolucional, não. O argumento é multimodalidade, não classificação.
CNN vs. ViT, atualizado
| CNN | Vision Transformer | |
|---|---|---|
| Operação central | Convolução (local, fixa) | Auto-atenção (global, aprendida) |
| Viés indutivo | Forte | Fraco — comprado com dados |
| Campo receptivo | Cresce com a profundidade | Global na camada 1 |
| Custo | \(O(\text{pixels})\) | \(O(N^2)\) em patches |
| Poucos dados | Vence | Precisa de pré-treino ou de receita estilo DeiT |
| Dados em escala web | Satura antes | Continua melhorando |
| Uso multimodal | Desajeitado | Nativo — ele já emite tokens |
| Ainda padrão em | Autoencoders latentes, segmentação médica, dispositivo | Tudo que fica antes de um LLM |
Referência de implementação
import torch, torch.nn as nn
class PatchEmbed(nn.Module):
"""Imagem -> sequência de tokens de patch. Um Conv2d faz patchify E projeção."""
def __init__(self, patch=16, in_ch=3, dim=768):
super().__init__()
self.proj = nn.Conv2d(in_ch, dim, kernel_size=patch, stride=patch)
def forward(self, x): # (B, C, H, W)
x = self.proj(x) # (B, dim, H/p, W/p)
return x.flatten(2).transpose(1, 2) # (B, N, dim)
class ViT(nn.Module):
"""Registros no lugar do token CLS, mean pooling no lugar da cabeça no CLS."""
def __init__(self, dim=768, depth=12, heads=12, n_classes=1000,
n_patches=196, n_registers=4):
super().__init__()
self.patch_embed = PatchEmbed(dim=dim)
self.reg = nn.Parameter(torch.zeros(1, n_registers, dim))
self.pos = nn.Parameter(torch.zeros(1, n_patches, dim))
layer = nn.TransformerEncoderLayer(dim, heads, dim * 4, activation='gelu',
norm_first=True, batch_first=True)
self.encoder, self.norm = nn.TransformerEncoder(layer, depth), nn.LayerNorm(dim)
self.head = nn.Linear(dim, n_classes)
self.n_reg = n_registers
def forward(self, x):
x = self.patch_embed(x) + self.pos
x = torch.cat([self.reg.expand(x.size(0), -1, -1), x], dim=1)
x = self.encoder(x)[:, self.n_reg:] # descarta os registros; eram rascunho
return self.head(self.norm(x).mean(dim=1))
import timm
# um ViT supervisionado, ajustado para suas classes
model = timm.create_model('vit_base_patch16_224', pretrained=True, num_classes=10)
# features do DINOv2, congeladas — em geral a opção mais forte quando há poucos rótulos
dino = torch.hub.load('facebookresearch/dinov2', 'dinov2_vitb14')
feats = dino.forward_features(img)['x_norm_patchtokens'] # (B, N, 768)
Pontos principais
- Um ViT é o encoder de texto aplicado a patches de imagem. A única parte nova é o patchify — e isso é uma convolução com passo.
- O prior convolucional é um piso em escala pequena e um teto em escala grande. É toda a troca.
- O limiar de dados de 2021 era em grande parte um problema de receita. DeiT e ConvNeXt fecharam o buraco pelos dois lados.
- O tamanho do patch é a decisão de projeto: a informação dentro de um patch é destruída antes de a atenção rodar, e \(N\) cresce quadraticamente quando \(P\) encolhe ou a resolução sobe. 14–16 é onde o meio-termo cai.
- Um encoder atual usa pooling no lugar do
[CLS], RoPE 2-D no lugar de posições aprendidas e tokens de registro para absorver os artefatos de atenção. - ViTs venceram porque emitem tokens, que é o que um modelo de linguagem come. A pontuação de classificação nunca foi o ponto.
- Em produção, um ViT costuma ser o encoder de visão de outra coisa: um VLM, CLIP/SigLIP, features do DINOv2, ou um backbone de difusão.
-
Dosovitskiy, A., et al. (2021). An Image is Worth 16×16 Words: Transformers for Image Recognition at Scale — ICLR. ↩
-
Touvron, H., et al. (2021). Training data-efficient image transformers & distillation through attention (DeiT) — ICML. A receita que removeu a exigência do JFT-300M. ↩
-
Liu, Z., et al. (2021). Swin Transformer: Hierarchical Vision Transformer using Shifted Windows — ICCV. ↩
-
Vaswani, A., et al. (2017). Attention Is All You Need — NeurIPS. (O bloco de encoder que o ViT reaproveita, sem mudanças.) ↩
-
Steiner, A., et al. (2021). How to train your ViT? Data, Augmentation, and Regularization in Vision Transformers — TMLR. ↩
-
He, K., et al. (2022). Masked Autoencoders Are Scalable Vision Learners — CVPR. Mascare 75%, codifique só o que está visível. ↩
-
Oquab, M., et al. (2024). DINOv2: Learning Robust Visual Features without Supervision — TMLR. Features auto-supervisionadas boas o bastante para usar congeladas. ↩
-
Liu, Z., et al. (2022). A ConvNet for the 2020s (ConvNeXt) — CVPR. ↩
-
Dehghani, M., et al. (2023). Patch n' Pack: NaViT, a Vision Transformer for any Aspect Ratio and Resolution — NeurIPS. ↩
-
Darcet, T., Oquab, M., Mairal, J., & Bojanowski, P. (2024). Vision Transformers Need Registers — ICLR. Os tokens-artefato de norma alta, diagnosticados e corrigidos. ↩