Phase 04 - Lesson 18
Visão de Vocabulário Aberto — CLIP
This lesson includes a graded coding exercise that runs in your browser, unlocked with lifetime access.
Treine um codificador de imagem e um codificador de texto juntos para que pares (imagem, legenda) correspondentes caiam no mesmo ponto de um espaço compartilhado. Esse é o truque inteiro.
Tipo: Construir + Usar Linguagens: Python Pré-requisitos: Fase 4 Lição 14 (ViT), Fase 4 Lição 17 (Autossupervisão) Tempo: ~45 minutos
Objetivos de Aprendizagem
- Explicar a arquitetura de duas torres do CLIP e o objetivo de treinamento contrastivo
- Usar um CLIP (ou SigLIP) pré-treinado para classificação zero-shot sem nenhum treinamento específico da tarefa
- Implementar classificação zero-shot do zero: codificar prompts de classe, calcular similaridade de cosseno, pegar o argmax
- Distinguir os modelos CLIP, SigLIP, OpenCLIP e LLaVA/LLaMA-vision — para que serve cada um em 2026
O Problema
Classificadores tradicionais têm vocabulário fechado: um modelo ImageNet de 1000 classes só consegue prever 1000 rótulos. Cada nova categoria exige dados rotulados e uma cabeça re-treinada.
O CLIP (Radford et al., OpenAI 2021) mostrou que treinar com 400M de pares (imagem, legenda) extraídos da web produz um modelo capaz de classificar em qualquer conjunto de categorias na inferência, descritas puramente em linguagem natural. Você dá a ele uma nova classe escrevendo uma frase.
Essa capacidade — transferência zero-shot — é o motivo pelo qual todo sistema de visão moderno começa com um checkpoint da família CLIP. Detecção (Grounding DINO, OWL-ViT), segmentação (CLIPSeg, SAM), recuperação, moderação de conteúdo, VLMs e geração de texto para imagem, todos se apoiam em embeddings conjuntos no estilo CLIP.
O Conceito
Duas torres
flowchart LR
IMG["Imagem"] --> IENC["Codificador de imagem<br/>(ViT-L/14)"] --> IEMB["Embedding de imagem<br/>(1024,)"]
TXT["Legenda"] --> TENC["Codificador de texto<br/>(transformer)"] --> TEMB["Embedding de texto<br/>(1024,)"]
IEMB --> SIM["Similaridade de cosseno"]
TEMB --> SIM
style IENC fill:#dbeafe,stroke:#2563eb
style TENC fill:#fef3c7,stroke:#d97706
style SIM fill:#dcfce7,stroke:#16a34a
Ambos os codificadores terminam com uma projeção linear para a mesma dimensão de embedding (512 para CLIP-B/32, 1024 para CLIP-L/14). Normalize com L2 e calcule a similaridade de cosseno.
O objetivo
Dado um lote de N pares (imagem, legenda), construa uma matriz de similaridade NxN. Treine ambos os codificadores para que a diagonal (pares correspondentes) tenha alta similaridade e as posições fora da diagonal (não correspondentes) tenham baixa similaridade.
sim_matrix = image_embeddings @ text_embeddings.T / tau
loss_i2t = cross_entropy(sim_matrix, targets=arange(N))
loss_t2i = cross_entropy(sim_matrix.T, targets=arange(N))
loss = (loss_i2t + loss_t2i) / 2
Simétrico porque tanto a recuperação imagem-para-texto quanto texto-para-imagem devem funcionar. tau (temperatura) é tipicamente aprendido como um parâmetro escalar, inicializado em 0.07.
SigLIP: uma perda melhor
O SigLIP (Zhai et al., 2023) substituiu o softmax por um sigmoide por par:
loss = mean over pairs of log(1 + exp(-y_ij * sim_ij))
y_ij = +1 if matching, -1 otherwise
A perda por par remove a normalização em nível de lote que o CLIP exige. O SigLIP treina melhor com lotes pequenos e iguala ou supera o CLIP com a mesma quantidade de dados.
Classificação zero-shot
Dado um CLIP treinado:
- Para cada classe, componha um prompt: "a photo of a {class}".
- Codifique todos os prompts de classe com o codificador de texto ->
Tde forma (C, d). - Codifique a imagem de teste ->
Ide forma (1, d). - Similaridade =
I @ T.Tde forma (1, C). - Argmax -> classe prevista.
A engenharia de prompts importa. A OpenAI publicou 80 templates de prompt para o ImageNet ("a photo of a {}", "a blurry photo of a {}", "a sketch of a {}", ...). Faça a média dos embeddings de todos os templates por classe para ganhar de 1 a 3% extras de acurácia top-1.
Onde os modelos no estilo CLIP são usados em 2026
- Classificação zero-shot — uso direto.
- Recuperação de imagens — codifique todas as imagens uma vez, gere o embedding da consulta na inferência.
- Detecção condicionada por texto — Grounding DINO, OWL-ViT envolvem uma torre de texto CLIP em torno de um detector.
- Segmentação condicionada por texto — CLIPSeg; o SAM usa entradas de prompt de texto via CLIP.
- VLMs — LLaVA, Qwen-VL, InternVL conectam um codificador de visão da família CLIP a um LLM.
- Geração de texto para imagem — Stable Diffusion, DALL-E 3 se condicionam a embeddings de texto do CLIP.
Quando você tem um espaço de embedding compartilhado, toda tarefa de visão+linguagem vira um cálculo de distância.
Construa
Passo 1: Um modelo de duas torres minúsculo
O CLIP real é ViT + transformer. Para esta lição, as torres são pequenas MLPs sobre features pré-extraídas, de modo que o sinal de treinamento fique visível na CPU.
import torch
import torch.nn as nn
import torch.nn.functional as F
class TwoTower(nn.Module):
def __init__(self, img_in=128, txt_in=64, emb=64):
super().__init__()
self.image_proj = nn.Sequential(nn.Linear(img_in, 128), nn.ReLU(), nn.Linear(128, emb))
self.text_proj = nn.Sequential(nn.Linear(txt_in, 128), nn.ReLU(), nn.Linear(128, emb))
self.logit_scale = nn.Parameter(torch.ones([]) * 2.6592) # ln(1/0.07)
def forward(self, img_feats, txt_feats):
i = F.normalize(self.image_proj(img_feats), dim=-1)
t = F.normalize(self.text_proj(txt_feats), dim=-1)
return i, t, self.logit_scale.exp()
Duas projeções, saída de dimensão compartilhada, temperatura aprendida. Mesma forma da API real do CLIP.
Passo 2: Perda contrastiva
def clip_loss(image_emb, text_emb, logit_scale):
N = image_emb.size(0)
sim = logit_scale * image_emb @ text_emb.T
targets = torch.arange(N, device=sim.device)
l_i = F.cross_entropy(sim, targets)
l_t = F.cross_entropy(sim.T, targets)
return (l_i + l_t) / 2
Simétrica. logit_scale mais alto = softmax mais acentuado = mais confiante, mas com risco de instabilidade.
Passo 3: Classificador zero-shot
@torch.no_grad()
def zero_shot_classify(model, image_feats, class_text_feats, class_names):
"""
image_feats: (N, img_in)
class_text_feats: (C, txt_in) one averaged embedding per class
"""
i = F.normalize(model.image_proj(image_feats), dim=-1)
t = F.normalize(model.text_proj(class_text_feats), dim=-1)
sim = i @ t.T
pred = sim.argmax(dim=-1)
return [class_names[p] for p in pred.tolist()]
Uma linha por passo. Este é exatamente o procedimento zero-shot usado com um checkpoint CLIP de produção.
Passo 4: Verificação de sanidade
torch.manual_seed(0)
model = TwoTower()
img = torch.randn(8, 128)
txt = torch.randn(8, 64)
i, t, scale = model(img, txt)
loss = clip_loss(i, t, scale)
print(f"batch size: {i.size(0)} loss: {loss.item():.3f}")
A perda deve ficar próxima de log(N) = log(8) = 2.08 para um modelo inicializado aleatoriamente — o alvo da entropia cruzada simétrica quando nenhuma estrutura foi aprendida ainda.
Use
O OpenCLIP é o padrão da comunidade em 2026:
import open_clip
import torch
from PIL import Image
model, _, preprocess = open_clip.create_model_and_transforms("ViT-B-32", pretrained="laion2b_s34b_b79k")
tokenizer = open_clip.get_tokenizer("ViT-B-32")
image = preprocess(Image.open("dog.jpg")).unsqueeze(0)
text = tokenizer(["a photo of a dog", "a photo of a cat", "a photo of a car"])
with torch.no_grad():
image_features = model.encode_image(image)
text_features = model.encode_text(text)
image_features = image_features / image_features.norm(dim=-1, keepdim=True)
text_features = text_features / text_features.norm(dim=-1, keepdim=True)
probs = (100.0 * image_features @ text_features.T).softmax(dim=-1)
print(probs)
O SigLIP é mais novo, treina melhor em escalas pequenas e é preferido para trabalhos novos: google/siglip-base-patch16-224. O Hugging Face disponibiliza ambos.
Entregue
Esta lição produz:
outputs/prompt-zero-shot-class-picker.md— um prompt que projeta templates de classe para CLIP zero-shot dada uma lista de classes e um domínio.outputs/skill-image-text-retriever.md— uma skill que constrói um índice de embeddings de imagem com qualquer checkpoint CLIP, suporta consulta por texto e consulta por imagem.
Exercícios
- (Fácil) Use um OpenCLIP ViT-B/32 pré-treinado e faça classificação zero-shot no CIFAR-10 com o conjunto de 80 templates de prompt. Reporte a acurácia top-1; ela deve ficar em torno de 85-90%.
- (Médio) Compare embeddings de template único ("a photo of a {}") versus a média de 80 templates na mesma tarefa do CIFAR-10. Quantifique a diferença e explique por que os templates ajudam.
- (Difícil) Construa um índice de recuperação de imagens zero-shot: gere embeddings de 1.000 imagens com CLIP, construa um índice FAISS, consulte com uma descrição em linguagem natural. Reporte o recall@5 de recuperação para 20 consultas de teste que você escreva à mão.
Termos-Chave
| Termo | O que as pessoas dizem | O que realmente significa |
|---|---|---|
| Duas torres | "Codificador duplo" | Codificadores de imagem e texto separados que terminam em uma cabeça de projeção de dimensão compartilhada |
| Zero-shot | "Sem treinamento específico da tarefa" | Classificar em classes descritas apenas por texto na inferência; nenhum rótulo é tocado |
| Temperatura / logit_scale | "tau" | Escalar aprendido que escala a matriz de similaridade antes do softmax |
| Template de prompt | "A photo of a {}" | Envoltório em linguagem natural em torno dos nomes de classe; fazer a média de muitos templates aumenta a acurácia zero-shot |
| CLIP | "Modelo de imagem+texto" | O modelo da OpenAI de 2021; vocabulário do campo em 2026 |
| SigLIP | "CLIP com sigmoide" | Troca o softmax por um sigmoide por par; treina melhor com lotes pequenos |
| OpenCLIP | "Reprodução aberta" | Variantes de CLIP treinadas pela comunidade sobre o LAION; padrão de produção para pipelines de código aberto |
| VLM | "Modelo de visão-linguagem" | Um codificador da família CLIP mais um LLM, treinado para responder perguntas sobre imagens |
Leitura Adicional
- CLIP: Learning Transferable Visual Models from Natural Language Supervision (Radford et al., 2021)
- SigLIP: Sigmoid Loss for Language-Image Pre-Training (Zhai et al., 2023)
- OpenCLIP — a base de código da comunidade
- DINOv2 vs CLIP vs MAE: a features comparison — guia do HF com casos de uso lado a lado