Transformers de Visão (ViT)
O que é um Vision Transformer (ViT)?
Um Vision Transformer (ViT) é uma arquitetura de rede neural que adapta o codificador Transformer (Transformer encoder) — originalmente projetado para texto — para entendimento de imagens. Em vez de processar pixels com filtros convolucionais como as RNCs (CNNs), o ViT:
- Divide uma imagem em subimagens (patches) de tamanho fixo
- Incorpora (embeds) cada subimagem em um vetor (um “token (token)”)
- Alimenta a sequência resultante de tokens em um codificador Transformer padrão baseado em autoatenção (self-attention)
Essa ideia simples funciona surpreendentemente bem, especialmente quando ViTs são pré-treinados (pretrained) em grandes conjuntos de dados (datasets) e depois passam por ajuste fino (fine-tuning) para tarefas posteriores (classificação, detecção, segmentação).
O ViT faz parte da família mais ampla de Transformers e compartilha muitos dos mesmos conceitos de projeto: incorporações de tokens, codificações posicionais, autoatenção multi-cabeça, blocos MLP, conexões residuais e normalização de camada.
Por que substituir convoluções por atenção?
As convoluções incorporam fortes vieses indutivos (inductive biases): localidade (pixels próximos importam mais) e equivariância a translação (translation equivariance). Esses vieses ajudam as CNNs a aprender de forma eficiente com dados limitados.
Transformers, por outro lado, dependem de autoatenção, que pode conectar quaisquer dois tokens independentemente da distância. Isso dá aos ViTs:
- Campos receptivos globais (global receptive fields) desde a primeira camada
- Modelagem flexível de dependências de longo alcance (por exemplo, relações entre objetos distantes)
- Excelente comportamento de escalonamento (scaling behavior) com o tamanho do modelo e do conjunto de dados
A contrapartida é que ViTs “puros” tipicamente precisam de mais dados e/ou pré-treinamento mais forte para igualar CNNs em escalas menores.
Transformando uma imagem em uma sequência: incorporações de subimagens
Patchificação
Dada uma imagem (x \in \mathbb{R}^{H \times W \times C}), o ViT a divide em subimagens sem sobreposição de tamanho (P \times P). O número de subimagens é:
[ N = \frac{H}{P} \cdot \frac{W}{P} ]
Cada subimagem é achatada em um vetor de comprimento (P \cdot P \cdot C).
Exemplo: para uma imagem RGB 224×224 e (P=16):
- Subimagens por lado: 224 / 16 = 14
- Total de subimagens: (14 \times 14 = 196)
- Comprimento da sequência de tokens (sem extras): 196
Projeção linear (incorporação de subimagem)
Cada subimagem achatada é mapeada para uma incorporação (D)-dimensional com uma camada linear aprendida:
[ \mathbf{z}_0^i = \mathbf{E} \cdot \text{patch}_i + \mathbf{b} ]
Na prática, essa projeção é frequentemente implementada como uma Conv2d (Conv2d) com tamanho de kernel (P) e stride (P), o que é computacionalmente conveniente e equivalente a patchificar+linear.
O token [CLS] (token de classificação)
Para classificação de imagens, o ViT comumente antepõe um token especial aprendido, [CLS], à sequência de subimagens:
[ [\mathbf{x}_{cls}, \mathbf{x}_1, \dots, \mathbf{x}_N] ]
Após o codificador Transformer, o estado oculto final correspondente a [CLS] é usado como uma representação agregada da imagem para classificação (semelhante ao BERT).
Nem todos os transformers de visão usam CLS — alguns usam pooling por média global (global average pooling) sobre os tokens de subimagem —, mas o CLS continua sendo uma escolha central de projeto no ViT “clássico”.
Codificações posicionais: dizendo ao modelo “de onde” vieram as subimagens
Transformers são invariantes a permutações, a menos que injetemos informação de posição. O ViT adiciona incorporações posicionais (positional embeddings) às incorporações de tokens:
[ \mathbf{z}_0^i \leftarrow \mathbf{z}_0^i + \mathbf{p}^i ]
Escolhas comuns:
- Incorporações posicionais absolutas aprendidas (learned absolute positional embeddings) (ViT original): uma tabela aprendida de tamanho ((N+1) \times D) (incluindo o CLS)
- Incorporações posicionais cientes de 2D (2D-aware positional embeddings) (várias variantes): incorporam a estrutura de linha/coluna
- Vieses posicionais relativos (relative positional biases) (populares em modelos posteriores): melhor transferência entre resoluções e frequentemente usados em transformers hierárquicos
Uma implicação prática: se você fizer ajuste fino de um ViT em uma resolução diferente da do pré-treinamento, muitas vezes precisa de interpolação de incorporação posicional (positional embedding interpolation) (mais sobre isso adiante).
Arquitetura central do ViT (codificador Transformer sobre tokens de subimagem)
Um ViT padrão é essencialmente:
- Incorporação de subimagem + codificação posicional
- Uma pilha de blocos de codificador Transformer
- Uma cabeça de classificação (MLP)
Cada bloco do codificador tipicamente segue uma estrutura de pré-normalização de camada (Pre-LayerNorm):
- Normalização de camada
- Autoatenção multi-cabeça (Multi-Head Self-Attention, MHSA)
- Conexão residual
- Normalização de camada
- MLP (MLP) (rede feed-forward)
- Conexão residual
Autoatenção multi-cabeça sobre subimagens
A autoatenção calcula interações entre todos os tokens. Para a matriz de tokens (X \in \mathbb{R}^{(N+1)\times D}):
[ Q = XW_Q,\quad K = XW_K,\quad V = XW_V ] [ \text{Attention}(Q,K,V) = \text{softmax}\left(\frac{QK^\top}{\sqrt{d}}\right)V ]
A atenção multi-cabeça executa isso em paralelo em múltiplas cabeças e concatena os resultados.
Propriedade-chave: qualquer subimagem pode atender a qualquer outra subimagem em uma única camada, permitindo raciocínio global cedo.
Bloco MLP (rede feed-forward)
O bloco MLP é tipicamente uma rede feed-forward de duas camadas aplicada independentemente por token:
- Linear (D \rightarrow rD) (frequentemente (r=4))
- Ativação GELU
- Linear (rD \rightarrow D)
Tamanhos típicos de modelo
Modelos ViT são comumente descritos com nomes como ViT-Ti/S/B/L/H:
- ViT-B/16: modelo Base, tamanho de subimagem 16, frequentemente (D=768), 12 camadas, 12 cabeças
- ViT-L/16: maior profundidade/largura (por exemplo, (D=1024), 24 camadas)
O tamanho da subimagem importa muito:
- Subimagens menores (por exemplo, 8×8) → mais tokens → melhor detalhe fino, porém maior custo computacional (a atenção é quadrática no número de tokens).
- Subimagens maiores (por exemplo, 32×32) → menos tokens → mais barato, mas pode perder detalhe.
Complexidade e o problema da “atenção quadrática”
A autoatenção é (O(T^2)) no comprimento da sequência (T). Para ViT, (T = N+1), onde (N) é o número de subimagens.
Se você aumenta a resolução da imagem, a contagem de tokens cresce quadraticamente com a resolução (para tamanho de subimagem fixo), e o custo de atenção cresce aproximadamente de forma quártica com o aumento da resolução linear:
- 224×224 com P=16 → 196 tokens
- 384×384 com P=16 → (24×24)=576 tokens
O tamanho da matriz de atenção salta de ~196² para ~576².
Por isso muitos transformers de visão práticos introduzem projetos hierárquicos (por exemplo, atenção em janelas), embora o “ViT puro” ainda seja amplamente usado, especialmente com resoluções moderadas ou implementações eficientes de atenção.
Pré-treinamento de ViTs: o que faz com que funcionem bem?
Pré-treinamento supervisionado em escala (receita original do ViT)
Os resultados originais do ViT foram fortes quando os modelos foram pré-treinados em conjuntos de dados rotulados muito grandes (por exemplo, ImageNet-21k, JFT-300M) e depois ajustados finamente em conjuntos menores (por exemplo, ImageNet-1k). Isso destacou um ponto-chave:
- ViTs escalam extremamente bem com dados
- Com poucos dados, ViTs puros podem ter desempenho inferior ao de CNNs, a menos que sejam regularizados cuidadosamente
Aumento de dados e detalhes de otimização importam
Treinamento eficaz de ViT frequentemente depende de:
- Otimizador AdamW (AdamW) (decaimento de peso desacoplado)
- Aquecimento de taxa de aprendizado (warmup) e agendas de decaimento cosseno
- Aumentos fortes: RandAugment, Mixup, CutMix
- Regularização: dropout, profundidade estocástica (DropPath), suavização de rótulo (label smoothing)
Essas escolhas podem ser tão importantes quanto a própria arquitetura.
Destilação e “treinar ViTs com menos dados” (por exemplo, ideias no estilo DeiT)
Um desenvolvimento prático importante foi mostrar que ViTs podem ser treinados de forma eficaz em conjuntos como ImageNet-1k com a receita certa, às vezes usando:
- Um modelo professor (teacher model) (frequentemente uma CNN) para fornecer alvos suaves
- Tokens de destilação ou perda de destilação
Mesmo que você não use destilação, a lição mais ampla permanece: ViTs se beneficiam de pipelines de treinamento cuidadosos.
Pré-treinamento auto-supervisionado: MAE, DINO, aprendizado contrastivo e modelagem mascarada
Pipelines modernos de ViT frequentemente usam Aprendizado Auto-Supervisionado para reduzir a dependência de rótulos e melhorar a transferência:
- Modelagem de imagem mascarada (masked image modeling) (por exemplo, estilo MAE): mascara muitas subimagens e treina o modelo (codificador/decodificador) para reconstruir o conteúdo ausente. Isso é análogo, em espírito, à modelagem de linguagem mascarada.
- Autodestilação / métodos de agrupamento (self-distillation / clustering methods) (por exemplo, estilo DINO): aprendem representações ao casar incorporações sob diferentes aumentos sem rótulos explícitos.
- Aprendizado contrastivo (contrastive learning) (por exemplo, estilo CLIP): alinha incorporações de imagem com incorporações de texto (multimodal), o que pode produzir backbones de ViT muito transferíveis.
Esses métodos são populares porque podem gerar representações que ajustam finamente bem para classificação e para tarefas de predição densa.
Ajuste fino de ViTs para classificação de imagens
Um fluxo de trabalho comum é:
- Pré-treinar um backbone de ViT (supervisionado ou auto-supervisionado)
- Substituir a cabeça de classificação para o seu conjunto de dados
- Fazer ajuste fino de algumas ou de todas as camadas
Escolhas práticas de ajuste fino
- Sondagem linear (linear probing): congelar o backbone e treinar apenas um classificador linear em cima do CLS ou de características agregadas
- Bom para avaliar rapidamente a qualidade das representações
- Ajuste fino completo (full fine-tuning): atualizar todos os pesos
- Em geral, melhor acurácia se você tiver dados suficientes
- Ajuste fino parcial (partial fine-tuning): congelar camadas iniciais e ajustar finamente camadas finais
- Pode ajudar quando os dados são limitados
- Decaimento de taxa de aprendizado por camada (layer-wise learning-rate decay): LR menor para camadas iniciais, maior para camadas finais e para a cabeça
- Comum para ajuste fino estável
Interpolação de incorporação posicional (quando a resolução muda)
Se um ViT é pré-treinado em 224×224 (subimagens 14×14), mas ajustado finamente em 384×384 (subimagens 24×24), a tabela de incorporação posicional absoluta aprendida não corresponde mais à contagem de tokens.
Uma correção padrão:
- Remodelar as incorporações posicionais das subimagens em uma grade 2D
- Interpolar (geralmente bicúbica) para o novo tamanho de grade
- Achatar de volta e concatenar com a incorporação posicional do CLS
Muitas bibliotecas lidam com isso automaticamente, mas é útil saber por que é necessário.
Usando ViTs além da classificação: detecção e segmentação
Tarefas de predição densa exigem saídas espacialmente estruturadas, não apenas um único rótulo. Há duas formas comuns de adaptar ViTs:
1) Usar um ViT como backbone que produz mapas de características
Você pode remodelar tokens de subimagem de volta para uma grade 2D para formar um “mapa de características”:
- Tokens (excluindo CLS): ((H/P \cdot W/P) \times D)
- Remodelar para: ((H/P) \times (W/P) \times D)
Depois, anexar uma cabeça específica da tarefa:
- Cabeças de detecção (detection heads) (frequentemente usando pirâmides de características / características multi-escala)
- Decodificadores de segmentação (segmentation decoders) (upsampling + conexões de atalho ou decodificadores Transformer)
Essa abordagem espelha como backbones de CNN são usados, exceto que as características se originam de blocos de atenção.
2) Projetos de detecção/segmentação com transformers de ponta a ponta
Algumas arquiteturas incorporam transformers de forma mais profunda no pipeline de detecção/segmentação (por exemplo, predição baseada em consultas, em que um conjunto de consultas aprendidas atende a características da imagem). Mesmo quando a abordagem original usava características de CNN, muitas versões modernas usam backbones no estilo ViT.
Na prática, ao transferir um ViT para detecção/segmentação, você frequentemente verá:
- Características multi-escala (seja via transformers hierárquicos, seja extraindo camadas intermediárias do ViT)
- Vieses posicionais relativos (melhor para tamanhos de entrada variáveis)
- Resoluções de entrada maiores e tamanhos de subimagem menores (melhor localização)
Exemplo prático: classificar imagens com um ViT pré-treinado (PyTorch + Hugging Face)
Abaixo está um exemplo mínimo de como executar inferência com um ViT pré-treinado em uma imagem. (Para treinamento/ajuste fino, você adicionaria um conjunto de dados, perda, otimizador e loop de treinamento.)
import torch
from PIL import Image
from transformers import AutoImageProcessor, AutoModelForImageClassification
device = "cuda" if torch.cuda.is_available() else "cpu"
model_name = "google/vit-base-patch16-224"
processor = AutoImageProcessor.from_pretrained(model_name)
model = AutoModelForImageClassification.from_pretrained(model_name).to(device)
model.eval()
img = Image.open("my_image.jpg").convert("RGB")
inputs = processor(images=img, return_tensors="pt").to(device)
with torch.no_grad():
logits = model(**inputs).logits
pred_id = logits.argmax(dim=-1).item()
print("Predicted class:", model.config.id2label[pred_id])
Esboço de ajuste fino (o que muda?)
Para ajuste fino no seu conjunto de dados:
- Substituir/redimensionar a cabeça de classificação para corresponder ao seu número de classes
- Treinar com AdamW, uma agenda warmup+cosine e aumentos padrão
- Considerar:
- congelar o backbone por algumas épocas (opcional)
- decaimento de taxa de aprendizado por camada (frequentemente útil)
Se você aumentar a resolução de treinamento, garanta que as incorporações posicionais sejam interpoladas (muitos frameworks fazem isso por você).
Exemplo prático: implementar incorporação de subimagem (trecho conceitual)
Isso demonstra o truque “Conv2d como patchifier” usado por muitas implementações de ViT:
import torch
import torch.nn as nn
class PatchEmbed(nn.Module):
def __init__(self, img_size=224, patch_size=16, in_chans=3, embed_dim=768):
super().__init__()
self.patch_size = patch_size
self.proj = nn.Conv2d(in_chans, embed_dim, kernel_size=patch_size, stride=patch_size)
def forward(self, x):
# x: [B, C, H, W]
x = self.proj(x) # [B, D, H/P, W/P]
x = x.flatten(2).transpose(1, 2) # [B, N, D], where N = (H/P)*(W/P)
return x
Um ViT típico então:
- antepõe um token CLS aprendido
- adiciona incorporações posicionais
- alimenta os tokens por blocos de codificador Transformer
Pontos fortes e limitações dos ViTs
Pontos fortes
- Contexto global via atenção desde o início
- Forte aprendizado por transferência (transfer learning) quando bem pré-treinado
- A arquitetura é simples e modular (fácil de escalar profundidade/largura)
- Funciona naturalmente com configurações multimodais (tokens de imagem se integram bem com tokens de texto)
Limitações
- O custo computacional (compute cost) cresce rapidamente com a resolução devido à atenção quadrática
- Viés indutivo embutido mais fraco do que em CNNs → pode exigir:
- mais dados
- regularização/aumento mais fortes
- otimização cuidadosa
- O ViT puro produz uma representação de escala única, a menos que seja adaptado; muitas tarefas densas se beneficiam de características hierárquicas/multi-escala
Variações comuns de projeto e “o que você verá na prática”
Mesmo que uma arquitetura seja rotulada como “semelhante a ViT”, ela pode incluir modificações como:
- Transformers hierárquicos (hierarchical transformers): reduzem gradualmente a resolução dos tokens para criar características multi-escala (bom para detecção/segmentação)
- Atenção em janelas / local (windowed / local attention): reduz o custo de atenção em alta resolução
- Híbrido CNN+Transformer (hybrid CNN+Transformer): stem convolucional + codificador Transformer
- Codificações posicionais relativas (relative positional encodings): melhor generalização para resoluções não vistas
- Pooling em vez de CLS: média dos tokens de subimagem para classificação
Entender o “ViT clássico” (patchificar + codificador + CLS + posições absolutas) torna essas variantes muito mais fáceis de acompanhar.
Quando você deve usar um ViT?
Use um ViT (ou um backbone derivado de ViT) quando:
- Você pode começar a partir de um checkpoint (checkpoint) pré-treinado forte
- Você se importa com a transferência para muitas tarefas (classificação + detecção/segmentação)
- Você quer uma representação escalável e nativa de transformers (incluindo extensões multimodais)
Considere alternativas (CNNs ou transformers hierárquicos/em janelas) quando:
- Você precisa treinar do zero em um conjunto de dados pequeno
- Você precisa de predições densas em resolução muito alta sob limites rígidos de computação
- Você precisa de forte viés de localidade e eficiência
Conexões com conceitos relacionados
- ViT é uma aplicação direta da Arquitetura Transformer a imagens.
- Ele contrasta com RNCs (CNNs), que codificam localidade e equivariância a translação por projeto.
- O treinamento moderno de ViT frequentemente depende de Aprendizado Auto-Supervisionado e Aprendizado por Transferência.
- Muitos usos posteriores envolvem Detecção de Objetos e Segmentação Semântica.
Se você quiser, posso adicionar uma seção focada em um destes tópicos: detalhes de interpolação de incorporação posicional, um loop completo de treinamento para ajuste fino, ou como backbones de ViT são conectados, na prática, a um detector/segmentador.