视觉 Transformer(ViT) 本节摘要:一张图是 patch 的网格,一句话是 token 的网格——同一个 Transformer 两者通吃。2020 年之前,计算机视觉等于卷积,ImageNet、COCO、检测基准的每个 SOTA 都用 CNN 骨干,Transformer 是给语言用的。Dosovitskiy 等(2020)的《An Image is Worth 16x16 Words》证明可以彻底丢掉卷积:把图切成固定大小的 patch,每个 patch 线性投影成嵌入,把这条序列喂进一个原版 Transformer 编码器。规模够大(ImageNet-21k 预训练或更大)时,ViT 追平甚至击败 ResNet。
本节摘要:一张图是 patch 的网格,一句话是 token 的网格——同一个 Transformer 两者通吃。2020 年之前,计算机视觉等于卷积,ImageNet、COCO、检测基准的每个 SOTA 都用 CNN 骨干,Transformer 是给语言用的。Dosovitskiy 等(2020)的《An Image is Worth 16x16 Words》证明可以彻底丢掉卷积:把图切成固定大小的 patch,每个 patch 线性投影成嵌入,把这条序列喂进一个原版 Transformer 编码器。规模够大(ImageNet-21k 预训练或更大)时,ViT 追平甚至击败 ResNet。这是 2026 年更大模式的起点:一种架构,多种模态——Whisper 把音频切成 token,ViT 把图像切成 patch,机器人用 action token,视频用 pixel token。Transformer 不在乎,喂它一条序列它就学。到 2026 年,ViT 及其后裔(DeiT、Swin、DINOv2、ViT-22B、SAM 3)占据大半视觉;CNN 只在边缘设备和延迟敏感任务上仍占优。本节将带你纯标准库实现 patchify + 线性嵌入,看清「图像→token」只需两步,然后讲清为什么 ViT 比 CNN 更吃数据(它丢掉了平移不变性和局部性这两个归纳偏置)。
阅读完本节,你应当能够:
[CLS] 与位置编码、标准 Transformer 编码器、分类头。H×W×C 图切成 N×(P·P·C) 的扁平 patch 序列(行优先)。P 的卷积,并能用 nn.Conv2d 两行实现。Dosovitskiy 等的论文标题就是论点:一张图值 16×16 个词。把图切成 patch、当 token 喂进原版 Transformer 编码器,规模够大就能赢。这是 2026 年「一种架构、多种模态」模式的起点。
把 H×W×C 图切成 N×(P·P·C) 的扁平 patch 序列。典型设置:224×224 图、16×16 patch → 196 个 patch,每个 768 个值。
image (224, 224, 3) → 14×14 网格的 16x16x3 patch → 196 个长度 768 的向量
patch 大小是杠杆:更小 = 更多 token、更好分辨率、O(N²) 注意力代价更大;更大 = 更粗、更便宜。
一个学习矩阵把每个扁平 patch 投影到 d_model。等价于 kernel 大小 P、步长 P 的卷积。PyTorch 里就是 nn.Conv2d(C, d_model, kernel_size=P, stride=P)——两行实现。
[CLS],加位置编码[CLS] token,其最终隐状态是用于分类的图像表示(同 BERT)。堆 L 个 LayerNorm → 自注意力 → + → LayerNorm → MLP → +,与 BERT 完全相同,无任何视觉专用层。这是论文的教学性结论:视觉不需要特殊架构,只需要把图变成 token。
分类取 [CLS] 隐状态 → 线性 → softmax。DINOv2 或 SAM 丢弃 [CLS],直接用 patch 嵌入。
| 模型 | 年份 | 关键改动 |
|---|---|---|
| ViT | 2020 | 原版,固定 patch,全全局注意力 |
| DeiT | 2021 | 蒸馏;只在 ImageNet-1k 上可训 |
| Swin | 2021 | 层次化 + 滑动窗口,亚二次代价 |
| DINOv2 | 2023 | 自监督(无标签),最佳通用视觉特征 |
| ViT-22B | 2023 | 220 亿参数,缩放定律适用 |
| SigLIP | 2023 | ViT + 语言配对,sigmoid 对比损失 |
| SAM 3 | 2025 | 分割万物,ViT-Large + 可提示掩码解码器 |
ViT 需要海量数据才能匹敌 CNN,因为它没有任何 CNN 的归纳偏置(平移不变性、局部性)。没有 >1 亿标注图或强自监督预训练,等算力下 CNN 仍赢。DeiT 在 2021 年用蒸馏技巧修复,DINOv2 在 2023 年用自监督永久修复。
💡 归纳偏置的代价:CNN 把「局部性」和「平移不变性」硬编码进架构(卷积核 + 权重共享),所以小数据上赢;ViT 不假设任何东西,所以更吃数据,但一旦数据够了就 scale 得更远——和第 01 节 Transformer vs RNN 的逻辑完全一致。
完整代码见原课程 phases/07-transformers-deep-dive/09-vision-transformers/code/main.py。纯标准库 patchify + 线性嵌入 + sanity check。无训练——任何现实规模的 ViT 都需要 PyTorch 和数小时 GPU。
24×24 RGB 图,表示成 (R,G,B) 元组的行的列表。用 6×6 patch → 16 个 patch,每个 108 维嵌入。
def patchify(image, P): H, W = len(image), len(image[0]) patches = [] for i in range(0, H, P): for j in range(0, W, P): patch = [] for di in range(P): for dj in range(P): patch.extend(image[i + di][j + dj]) patches.append(patch) return patches
行优先:按行主序遍历网格。每个 ViT 都用这个顺序。
每个扁平 patch 乘一个随机 (patch_flat_size, d_model) 矩阵,前置 [CLS] 后验证输出形状是 (N_patches + 1, d_model)。
打印 ViT-Base 的参数量:12 层、12 头、d=768、patch=16。对比 ResNet-50(约 2500 万):ViT-Base 约 8600 万,ViT-Large 约 3.07 亿,ViT-Huge 约 6.32 亿。
from transformers import ViTImageProcessor, ViTModel from PIL import Image processor = ViTImageProcessor.from_pretrained("google/vit-base-patch16-224-in21k") model = ViTModel.from_pretrained("google/vit-base-patch16-224-in21k") img = Image.open("cat.jpg") inputs = processor(img, return_tensors="pt") out = model(**inputs).last_hidden_state # (1, 197, 768): [CLS] + 196 patch cls_emb = out[:, 0] # 图像表示
原课程产出 outputs/skill-vit-configurator.md:一个配置器 Skill,给定数据集大小、分辨率、算力预算,为新视觉任务选 ViT 变体和 patch 大小。
code/main.py,验证 patch 数等于 (H/P)*(W/P)、扁平 patch 维度等于 P*P*C。row 和 col 各一组独立正弦码,拼接。喂进微型 PyTorch ViT,在 CIFAR-10 上对比可学习位置嵌入的精度。P×P patch、扁平化、线性投影成嵌入——图变成了 token 序列。(H/P)·(W/P)。P 的 nn.Conv2d,两行实现。[CLS] 当图像摘要:前置可学习 token,其最终隐状态用于分类。下一节,我们把 Transformer 扩展到音频——看 Whisper 如何用 encoder-decoder 把音频频谱图变成文本,以及音频 patch 如何像图像 patch 一样被 token 化。