HelloAI
L5 第 3 篇 🐣 难度 🕒 13 分钟

ViT 与 CLIP:让 Transformer 看图

把图像切成 patch,喂给 Transformer——视觉领域 2020 年最大的范式转变。

阿莱
2026/7/19

L3-06 我们看到 CNN 统治了视觉领域 8 年(2012-2020)。 L3-08 看到 Transformer 在 NLP 横扫一切。

2020 年一个自然的问题:能不能把 Transformer 也用在图像上?

答案是:能,而且效果惊人

第一站:ViT 的核心想法

Transformer 处理的是序列——但图像是 2D 网格。怎么办?

ViT(Vision Transformer,2020)的招数极其简单:

把图像切成小 patch,每个 patch 当 token

一张 224×224 的图
   ↓ 切成 16×16 的 patch(共 196 个)
   ↓ 每个 patch 展平成 256 维向量(16×16)
   ↓ 过一个线性层映射到 768 维
   ↓ 加位置编码
   ↓ 喂给标准 Transformer

完全照搬 NLP 的 Transformer 架构——只是把”词”换成”patch”。

为什么这能工作

CNN 的归纳偏好(inductive bias):

  • 局部性(neighbors matter)
  • 平移不变性(位置无关)
  • 层次性(low-level → high-level)

ViT 把这些全扔了——纯粹靠数据学。

问题:没有了归纳偏好,模型应该更难学才对——为什么能赢?

答:数据足够多时,没有归纳偏好反而更好——模型能学到比 CNN 设计的偏好更精细的模式。

经典发现

数据量CNN 表现ViT 表现
ImageNet(130 万张)
JFT-300M(3 亿张)强 + 反超

ViT 在大数据上反超 CNN——是后续视觉 Transformer 的核心理由。

第二站:用 PyTorch 看 ViT

import torch
import torch.nn as nn

class PatchEmbedding(nn.Module):
    def __init__(self, img_size=224, patch_size=16, in_channels=3, dim=768):
        super().__init__()
        self.n_patches = (img_size // patch_size) ** 2   # 196
        # 用 Conv2d 实现 patch 切分 + 投影(一举两得)
        self.proj = nn.Conv2d(in_channels, dim, kernel_size=patch_size, stride=patch_size)

    def forward(self, x):
        # x: (B, 3, 224, 224)
        x = self.proj(x)          # (B, 768, 14, 14)
        x = x.flatten(2)          # (B, 768, 196)
        x = x.transpose(1, 2)     # (B, 196, 768) ← 这是 Transformer 输入
        return x

class ViT(nn.Module):
    def __init__(self, n_classes=1000, dim=768, depth=12, n_heads=12):
        super().__init__()
        self.patch_embed = PatchEmbedding()
        self.cls_token = nn.Parameter(torch.zeros(1, 1, dim))   # 借鉴 BERT 的 [CLS]
        self.pos_embed = nn.Parameter(torch.zeros(1, 197, dim))  # 196 patches + 1 CLS

        encoder_layer = nn.TransformerEncoderLayer(
            d_model=dim, nhead=n_heads, dim_feedforward=4*dim, batch_first=True
        )
        self.transformer = nn.TransformerEncoder(encoder_layer, num_layers=depth)
        self.head = nn.Linear(dim, n_classes)

    def forward(self, x):
        x = self.patch_embed(x)                            # (B, 196, 768)
        cls = self.cls_token.expand(x.size(0), -1, -1)
        x = torch.cat([cls, x], dim=1)                     # (B, 197, 768)
        x = x + self.pos_embed
        x = self.transformer(x)
        return self.head(x[:, 0])                          # 用 CLS token 分类

整个 ViT 就这么简单——20-30 行代码。

第三站:CLIP 的革命

2021 年 OpenAI 发了一篇更”震撼”的论文:CLIP(Contrastive Language-Image Pre-training)。

核心思路

不是分类——而是让”图像”和”它的文字描述”靠近

                  共享的向量空间

图像 ──→ ViT ─────→ 图像向量 ←─────── 同一概念

文本 ──→ Transformer ─→ 文本向量 ←─── 同一概念

训练数据

**4 亿张”图 + 描述”**对——从互联网抓取。

不需要人工标注——alt 文本和 caption 自带标签

训练目标:对比学习

一个 batch 有 N 对(图,文):

图 1: 一只橘猫坐在窗台
图 2: 三只小狗在玩
图 3: 海边日落
...

文 1: "An orange cat on a window sill"
文 2: "Three puppies playing"
文 3: "Sunset at the beach"
...

训练目标:

  • (图 1, 文 1) 向量靠近
  • (图 1, 文 2/3/…) 向量远离
  • 同理 (图 2, 文 2) 等

数学上:最大化对角线、最小化非对角线的相似度矩阵。

训完能干啥(零样本!)

CLIP 训完后,能做任何分类任务而不需要再训练

classes = ["a photo of a cat", "a photo of a dog", "a photo of a car"]
text_features = [clip.encode_text(c) for c in classes]

image_features = clip.encode_image(my_image)
similarities = [cos_sim(image_features, t) for t in text_features]
predicted = classes[argmax(similarities)]

用任意文本描述当类别——零样本分类。

这是 ImageNet 之后视觉领域的第二次范式革命

第四站:CLIP 的下游应用

CLIP 的 encoder 极其有用:

1. 文搜图 / 图搜文

"夕阳下的金毛"  →  CLIP text encoder  →  向量
所有图片  →  CLIP image encoder  →  向量库
→ 余弦相似度找最匹配的图

Pinterest 风格搜索、相册自动整理——都基于这。

2. Stable Diffusion 的”耳朵”

Stable Diffusion 怎么”听懂”你的 prompt? 它用 CLIP text encoder 把 prompt 变成向量,喂给扩散模型作为”条件”

没有 CLIP,文生图无法工作。

3. 多模态大模型

GPT-4V / Claude 3.5 等多模态 LLM 的 vision encoder——底层都是 ViT 或 CLIP 衍生品

4. 零样本检测、分割

把 CLIP 的能力扩展到”找出图中所有猫的位置”—— GroundingDINO、SAM 等更先进的视觉模型基于这一思路。

第五站:ViT 和 CLIP 的局限

ViT 的问题

  • 数据饥渴:小数据集上不如 CNN
  • 计算贵:O(N²) 注意力,高分辨率图爆炸
  • 没有 inductive bias:失去 CNN 的”翻译不变性”等天然属性

CLIP 的问题

  • 训练数据偏见:网络数据带各种偏见(性别、种族、文化)
  • 细粒度差:分得清猫和狗,分不清两种猫
  • 依赖描述质量:alt 文本经常质量很差
  • 英文为主:非英语效果差

第六站:ViT/CLIP 之后

Swin Transformer(2021)

把 CNN 的”层次”思想加回 ViT——逐层降采样。 解决了 ViT 高分辨率慢的问题。

MAE(Masked Autoencoders, 2021)

何凯明的工作。像 BERT 一样掩码 patch,让 ViT 自监督学。 不需要 CLIP 那样的图文对——更便宜。

SAM(Segment Anything, 2023)

Meta 的工作。通用图像分割——用 prompt(点、框、文本)就能分割任何物体。 基础架构:ViT。

DINO / DINOv2

Self-supervised ViT——完全不需要标签。 DINOv2 已经是当前视觉表征学习的 SOTA。

第七站:今天的视觉 AI 栈

Foundation: ViT / CLIP (encoder)

Multimodal LLM: GPT-4V / Claude 3.5 / Gemini

Specific Tasks:
- 检测: GroundingDINO
- 分割: SAM, Mask2Former
- 生成: Stable Diffusion, Imagen
- 编辑: InstructPix2Pix
- 视频: Sora

整个栈都建立在 ViT/CLIP 之上

2020-2021 这两篇论文奠定了现代视觉 AI 的基础——影响和 Transformer 在 NLP 上一样大。

配套代码

用 OpenCLIP 试一下:

import open_clip
import torch
from PIL import Image

model, _, preprocess = open_clip.create_model_and_transforms('ViT-L-14', pretrained='laion2b_s32b_b82k')
tokenizer = open_clip.get_tokenizer('ViT-L-14')

image = preprocess(Image.open("photo.jpg")).unsqueeze(0)
text = tokenizer(["a photo of a cat", "a photo of a dog", "a sunset"])

with torch.no_grad():
    image_features = model.encode_image(image)
    text_features = model.encode_text(text)
    similarity = (image_features @ text_features.T).softmax(dim=-1)

print(similarity)  # [0.05, 0.92, 0.03] - 是狗

几行代码做”任意类别图像分类”——3 年前完全无法想象。

💡 一个观察

Transformer 已经吞噬了所有模态

  • 文本 ✓
  • 图像 ✓ (ViT)
  • 语音 ✓ (Whisper / SeamlessM4T)
  • 视频 ✓ (Video Transformer, Sora)
  • 蛋白质 ✓ (AlphaFold 2)
  • 决策 ✓ (Decision Transformer)
  • 机器人 ✓ (RT-2)

“Attention is all you need” 这个标题成了预言。

下一篇推荐:L5-04 CLIP 详解L4-04 Agent 构建

📬

读到这里说明你认真在学 🎯

订阅每周精选 —— 下一篇新文章 / 新可视化第一时间送到邮箱。

💬

讨论区

· 用 GitHub 账号登录评论
⚠️ Giscus 评论未配置 —— 在 src/components/Comments.astro 顶部填入 仓库 ID 和分类 ID(见组件注释里的配置步骤)。