Phase 04 - Lesson 17

Visão Auto-Supervisionada — SimCLR, DINO, MAE

This lesson includes a graded coding exercise that runs in your browser, unlocked with lifetime access.

Os rótulos são o gargalo da visão supervisionada. O pré-treino auto-supervisionado os elimina: aprenda características visuais a partir de 100M de imagens sem rótulo e faça o fine-tuning em 10 mil rotuladas.

Tipo: Aprender + Construir Linguagens: Python Pré-requisitos: Fase 4 Lição 04 (Classificação de Imagens), Fase 4 Lição 14 (ViT) Tempo: ~75 minutos

Objetivos de Aprendizagem

  • Percorrer as três grandes famílias auto-supervisionadas — contrastiva (SimCLR), professor-aluno (DINO), reconstrução mascarada (MAE) — e enunciar o que cada uma otimiza
  • Implementar uma perda InfoNCE do zero e explicar por que um batch de 512 funciona, mas um batch de 32 falha
  • Explicar por que a taxa de mascaramento de 75% do MAE não é arbitrária e como ela difere dos 15% do BERT para texto
  • Usar checkpoints DINOv2 ou MAE da ImageNet para linear probing e recuperação zero-shot

O Problema

A ImageNet supervisionada tem 1,3M de imagens rotuladas, cuja anotação custou cerca de US$ 10M. Conjuntos de dados médicos e industriais são menores e ainda mais caros de rotular. Toda equipe de visão pergunta: podemos pré-treinar em dados sem rótulo baratos — frames do YouTube, web crawls, gravações de webcam, varreduras de satélite — e depois fazer o fine-tuning em um pequeno conjunto rotulado?

A aprendizagem auto-supervisionada é a resposta. Um ViT auto-supervisionado moderno treinado na LAION ou na JFT alcança ou supera a acurácia da ImageNet supervisionada quando passa por fine-tuning. Ele também transfere melhor para tarefas downstream (detecção, segmentação, profundidade) do que o pré-treino supervisionado. O DINOv2 (Meta, 2023) e o MAE (Meta, 2022) são os padrões de produção atuais para características visuais transferíveis.

A mudança conceitual é que a tarefa-pretexto — aquilo que o modelo é treinado para fazer — não precisa ser a tarefa downstream. O que importa é que ela force o modelo a aprender características úteis. Prever a cor de imagens em tons de cinza, rotacionar imagens e pedir ao modelo que classifique a rotação, mascarar patches e reconstruí-los — tudo funcionou. As três abordagens que escalam são a aprendizagem contrastiva, a destilação professor-aluno e a reconstrução mascarada.

O Conceito

Três famílias

flowchart LR
    A["Contrastiva<br/>SimCLR, MoCo, CLIP"] --> AT["pares positivos<br/>(mesma imagem, 2 augs)<br/>aproximados,<br/>negativos afastados"]
    B["Professor-aluno<br/>DINO, BYOL, iBOT"] --> BT["o aluno prevê<br/>a saída do professor;<br/>o professor é a EMA do aluno"]
    C["Reconstrução mascarada<br/>MAE, BEiT, SimMIM"] --> CT["mascarar 75% dos patches;<br/>reconstruir alvos de<br/>pixel ou token"]

    style A fill:#dbeafe,stroke:#2563eb
    style B fill:#fef3c7,stroke:#d97706
    style C fill:#dcfce7,stroke:#16a34a

Aprendizagem contrastiva (SimCLR)

Pegue uma imagem, aplique duas augmentations aleatórias e obtenha duas views. Passe ambas pelo mesmo encoder mais uma projection head. Minimize uma perda que diz "esses dois embeddings devem estar próximos" e "este embedding deve estar distante dos embeddings de toda outra imagem no batch."

Loss for positive pair (z_i, z_j) among 2N views per batch:

   L_ij = -log( exp(sim(z_i, z_j) / tau) / sum_k in batch \ {i} exp(sim(z_i, z_k) / tau) )

sim = cosine similarity
tau = temperature (0.1 standard)

Essa é a perda InfoNCE. Ela exige muitos negativos por positivo, então o tamanho do batch importa — o SimCLR precisa de 512-8192. O MoCo introduziu uma fila de momentum com batches passados para desacoplar a contagem de negativos do tamanho do batch.

Professor-aluno (DINO)

Duas redes com a mesma arquitetura: aluno e professor. O professor é uma média móvel exponencial (EMA) dos pesos do aluno. Ambos veem views augmentadas da imagem. A saída do aluno é treinada para coincidir com a do professor — sem negativos explícitos.

loss = CE( student_output(view_1),  teacher_output(view_2) )
     + CE( student_output(view_2),  teacher_output(view_1) )

teacher_weights = m * teacher_weights + (1 - m) * student_weights   (m ≈ 0.996)

Por que não colapsa para "prever uma constante": a saída do professor é centrada (subtrai a média por dimensão) e afiada (divide por uma temperatura pequena). A centralização impede que uma dimensão domine; a afiação impede o colapso da saída para uma distribuição uniforme.

O DINO é o que o DINOv2 escala, em 142M de imagens curadas. As características resultantes são o SOTA atual para recuperação visual zero-shot e predição densa.

Reconstrução mascarada (MAE)

Mascare 75% dos patches de uma entrada de ViT. Passe apenas os 25% visíveis pelo encoder. Um pequeno decoder recebe a saída do encoder mais mask tokens nas posições mascaradas e é treinado para reconstruir os pixels dos patches mascarados.

Encoder:  visible 25% of patches -> features
Decoder:  features + mask tokens at masked positions -> reconstructed pixels
Loss:     MSE between reconstructed and original pixels on masked patches only

Escolhas de design fundamentais que fazem o MAE funcionar:

  • Taxa de mascaramento de 75% — alta. Força o encoder a aprender características semânticas; reconstruir 25% seria quase trivial (pixels vizinhos são tão correlacionados que uma CNN daria conta).
  • Encoder/decoder assimétricos — o grande encoder ViT só vê os patches visíveis; um pequeno decoder (8 camadas, 512 dimensões) cuida da reconstrução. Pré-treino 3x mais rápido que o BEiT ingênuo.
  • Alvo de reconstrução no espaço de pixels — mais simples que o alvo tokenizado do BEiT e funciona melhor em ViT.

Após o pré-treino, descarte o decoder. O encoder é o extrator de características.

Por que 75% e não 15%

O BERT mascara 15% dos tokens. O MAE mascara 75%. A diferença é a densidade de informação.

  • A linguagem natural tem alta entropia por token. Prever 15% dos tokens ainda é difícil porque cada posição mascarada tem muitas continuações plausíveis.
  • Os patches de imagem têm baixa entropia — uma vizinhança não mascarada frequentemente determina os pixels do patch mascarado quase exatamente. Para que a predição exija compreensão semântica, é preciso mascarar de forma agressiva.

75% é alto o suficiente para que a simples extrapolação espacial não consiga resolver a tarefa; o encoder precisa representar o conteúdo da imagem.

Avaliação por linear-probe

Após o pré-treino auto-supervisionado, a avaliação padrão é um linear probe: congele o encoder, treine um único classificador linear por cima usando os rótulos da ImageNet. Reporta a acurácia top-1.

  • SimCLR ResNet-50: ~71% (2020)
  • DINO ViT-S/16: ~77% (2021)
  • MAE ViT-L/16: ~76% (2022)
  • DINOv2 ViT-g/14: ~86% (2023)

O linear probe é uma medida pura da qualidade das características; o fine-tuning normalmente adiciona de 2 a 5 pontos, mas também mistura o efeito do retreino da head.

Construa

Passo 1: Pipeline de augmentation de duas views

import torch
import torchvision.transforms as T

two_view_train = lambda: T.Compose([
    T.RandomResizedCrop(96, scale=(0.2, 1.0)),
    T.RandomHorizontalFlip(),
    T.ColorJitter(0.4, 0.4, 0.4, 0.1),
    T.RandomGrayscale(p=0.2),
    T.ToTensor(),
])


class TwoViewDataset(torch.utils.data.Dataset):
    def __init__(self, base):
        self.base = base
        self.aug = two_view_train()

    def __len__(self):
        return len(self.base)

    def __getitem__(self, i):
        img, _ = self.base[i]
        v1 = self.aug(img)
        v2 = self.aug(img)
        return v1, v2

Cada getitem retorna duas views augmentadas da mesma imagem; rótulos não são necessários.

Passo 2: Perda InfoNCE

import torch.nn.functional as F

def info_nce(z1, z2, tau=0.1):
    """
    z1, z2: (N, D) L2-normalised embeddings of paired views
    """
    N, D = z1.shape
    z = torch.cat([z1, z2], dim=0)  # (2N, D)
    sim = z @ z.T / tau              # (2N, 2N)

    mask = torch.eye(2 * N, dtype=torch.bool, device=z.device)
    sim = sim.masked_fill(mask, float("-inf"))

    targets = torch.cat([torch.arange(N, 2 * N), torch.arange(0, N)]).to(z.device)
    return F.cross_entropy(sim, targets)

Normalize os embeddings com L2 antes de chamar. tau=0.1 é o padrão do SimCLR; valores menores tornam a perda mais afiada e exigem mais negativos.

Passo 3: Sanity check do InfoNCE

z1 = F.normalize(torch.randn(16, 32), dim=-1)
z2 = z1.clone()
loss_same = info_nce(z1, z2, tau=0.1).item()
z2_random = F.normalize(torch.randn(16, 32), dim=-1)
loss_random = info_nce(z1, z2_random, tau=0.1).item()
print(f"InfoNCE with identical pairs:  {loss_same:.3f}")
print(f"InfoNCE with random pairs:     {loss_random:.3f}")

Pares idênticos devem dar uma perda baixa (próxima de 0 para um batch grande e temperatura fria). Pares aleatórios devem dar log(2N-1) = ~log(31) = ~3,4 com um batch de 16 pares.

Passo 4: Mascaramento no estilo MAE

def random_mask_indices(num_patches, mask_ratio=0.75, seed=0):
    g = torch.Generator().manual_seed(seed)
    n_keep = int(num_patches * (1 - mask_ratio))
    perm = torch.randperm(num_patches, generator=g)
    visible = perm[:n_keep]
    masked = perm[n_keep:]
    return visible.sort().values, masked.sort().values


num_patches = 196
visible, masked = random_mask_indices(num_patches, mask_ratio=0.75)
print(f"visible: {len(visible)} / {num_patches}")
print(f"masked:  {len(masked)} / {num_patches}")

Simples, rápido e determinístico para um dado seed. Implementações reais de MAE fazem isso em batch e mantêm máscaras por amostra.

Use

O DINOv2 é o padrão de produção em 2026:

import torch
from transformers import AutoImageProcessor, AutoModel

processor = AutoImageProcessor.from_pretrained("facebook/dinov2-base")
model = AutoModel.from_pretrained("facebook/dinov2-base")
model.eval()

# Per-image embeddings for zero-shot retrieval
with torch.no_grad():
    inputs = processor(images=[pil_image], return_tensors="pt")
    outputs = model(**inputs)
    embedding = outputs.last_hidden_state[:, 0]  # CLS token

O embedding resultante de 768 dimensões é a espinha dorsal dos pipelines modernos de recuperação de imagens, correspondência densa e transferência zero-shot. O fine-tuning em uma tarefa downstream raramente precisa de mais do que uma head linear.

Para embeddings de imagem-texto, o SigLIP ou o OpenCLIP é o equivalente; para fine-tuning no estilo MAE, o repositório timm traz todos os checkpoints de MAE.

Entregue

Esta lição produz:

  • outputs/prompt-ssl-pretraining-picker.md — um prompt que escolhe SimCLR / MAE / DINOv2 dados o tamanho do conjunto de dados, o compute e a tarefa downstream.
  • outputs/skill-linear-probe-runner.md — uma skill que escreve a avaliação por linear-probe para qualquer encoder congelado + conjunto de dados rotulado.

Exercícios

  1. (Fácil) Verifique que a perda InfoNCE cai quando você diminui a temperatura para embeddings bem alinhados e sobe quando você diminui a temperatura para embeddings aleatórios. Produza um gráfico de tau in [0.05, 0.1, 0.2, 0.5] vs perda.
  2. (Médio) Implemente um buffer de centro no estilo DINO. Mostre que, sem a centralização, o aluno colapsa para um vetor constante em poucas épocas.
  3. (Difícil) Treine o MAE no CIFAR-100 usando a TinyUNet da Lição 10 como backbone. Reporte a acurácia do linear-probe em 10, 50 e 200 épocas. Mostre que um linear probe pré-treinado com MAE supera um linear probe supervisionado treinado do zero no mesmo subconjunto de 1.000 imagens.

Termos-Chave

Termo O que as pessoas dizem O que realmente significa
Auto-supervisionado "Sem rótulos" Uma tarefa-pretexto que produz representações úteis a partir de dados sem rótulo
Tarefa-pretexto "A tarefa falsa" O objetivo usado durante a SSL (reconstruir patches, casar views); descartado após o pré-treino
Linear probe "Encoder congelado + head linear" Avaliação padrão de SSL: treinar apenas um classificador linear por cima de características congeladas
InfoNCE "Perda contrastiva" softmax sobre similaridades de cosseno; o par positivo é a classe-alvo, todos os outros são negativos
Professor EMA "Professor de média móvel" Professor cujos pesos são uma média móvel exponencial dos do aluno; usado por BYOL, MoCo, DINO
Taxa de mascaramento "% de patches ocultos" Fração de patches mascarados durante o MAE; 75% para visão, 15% para texto
Colapso de representação "Saída constante" Falha de SSL em que o encoder produz um vetor constante para todas as entradas; evitada por centralização, afiação ou negativos
DINOv2 "Backbone de SSL de produção" O ViT auto-supervisionado da Meta de 2023; as características de imagem de propósito geral mais fortes em 2026

Leitura Adicional

0 lifetime access. Curriculum based on AI Engineering from Scratch by Rohit Ghumare (MIT, used under attribution).