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:

  1. Para cada classe, componha um prompt: "a photo of a {class}".
  2. Codifique todos os prompts de classe com o codificador de texto -> T de forma (C, d).
  3. Codifique a imagem de teste -> I de forma (1, d).
  4. Similaridade = I @ T.T de forma (1, C).
  5. 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

  1. (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%.
  2. (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.
  3. (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

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