上一篇讲的 Transformer 只吃"词序列"。这篇论文用一个近乎粗暴的办法,让它直接吃图像——把图切成小方块,每块当一个词。就这么简单,却掀翻了 CNN 统治视觉的十年。
上一篇你已经知道:Transformer 处理的是一串 token。文字天然是一串词,切开就能用。那图像呢?
听起来很自然——文字的最小单位是词,图像的最小单位是像素。但算一下就知道这条路死得多惨:
# 一张标准的 224×224 输入图像
序列长度 n = 224 × 224 = 50,176 个 token
# 注意力复杂度是 O(n²)——每个 token 要和所有 token 算一次
注意力矩阵大小 = 50176 × 50176 = 2,517,630,976
# 25 亿个数,光存这一个矩阵(fp32)就要:
2.5e9 × 4 bytes ≈ 10 GB # 而这只是一层、一个头、一张图
还记得上一篇第 0 节那张对比表最后一行吗——Transformer 的复杂度是序列长度的平方。在文字里句子几十上百个词,平方一下还能接受;在图像里像素数直接把它送进了不可能的领域。
Transformer 在 NLP 摧枯拉朽,视觉界眼馋了三年却进不来。期间有很多妥协方案——只在局部窗口做注意力、用 CNN 先降维再上注意力、把注意力当作 CNN 的插件……都是打补丁,没人敢真的把 CNN 整个扔掉。
ViT 这篇论文的贡献,不在于发明了什么新机制——它几乎原封不动地用了标准 Transformer 编码器。它的贡献是提出了一个简单到让人拍大腿的预处理方式,把"图像"变成了"合法的 token 序列"。
解法一句话说完:把图像切成 16×16 像素的小方块(patch),每一块当作一个"词"。
# 同样的 224×224 图像
每边切成 224 ÷ 16 = 14 块
总共 14 × 14 = 196 个 patch
# 序列长度从 50176 降到 196,缩小了 256 倍
注意力矩阵 = 196 × 196 = 38,416 # 完全跑得动
这就是标题的由来:一张图片值 16×16 个词——图像被翻译成了一门"视觉语言",每个 patch 是一个"视觉单词"。
这是一个精度与算力的取舍旋钮。patch 越小,切出的块越多、序列越长、细节保留越好,但计算量按平方涨;patch 越大则相反。
论文对比过 16×16 和 32×32:更小的 patch 效果更好但更贵。16 是实践中的甜点。后来的模型也常用 14 或 8——这是个可调超参,不是什么神圣数字。
这一步是整篇论文唯一需要认真算的地方。跟着形状走一遍:
输入图像 3 × 224 × 224(RGB 三通道),切成 14×14 个块,每块 3 × 16 × 16。
每块把三个通道的所有像素拉直排成一列:16 × 16 × 3 = 768 个数值。
于是一块变成 768 维向量,196 块就是 196 × 768。
乘一个可学习矩阵 E(768 × D),把原始像素值投影到模型的隐藏维度 D。
ViT-Base 的 D 也是 768,所以这一步形状不变——但数值含义完全变了:从"原始像素亮度"变成了"可学习的语义表示"。
ViT-Base 里 16×16×3 = 768 恰好等于 d_model = 768,很多人误以为这两个数必须相等。它们完全无关。前者由 patch 尺寸决定,后者是模型宽度超参。ViT-Large 的 d_model 是 1024,patch 仍是 16×16(展平还是 768),投影矩阵就是 768×1024。
这正是你上一篇学的 d_model 概念——模型内部所有 token 统一的向量宽度。和 BEVFormer 的 256、Qwen 的 hidden_size 是同一个东西。
"切块 + 展平 + 线性投影"这三步,实现时不用真的写循环切图——一个卷积层就等价完成:
# 核大小=16、步长=16 的卷积 —— 卷积核不重叠地滑过整张图
# 每滑一次正好覆盖一个 patch,输出一个 D 维向量
patch_embed = nn.Conv2d(
in_channels=3,
out_channels=768, # D
kernel_size=16, # patch 尺寸
stride=16 # 步长=核大小 → 不重叠
)
x = patch_embed(img) # [B,3,224,224] → [B,768,14,14]
x = x.flatten(2).transpose(1,2) # → [B,196,768] 搞定
卷积核不重叠地滑动,每一次覆盖恰好是一个 patch,输出的就是该 patch 的投影结果。两行代码完成整个 patch embedding——这也是为什么说 ViT 的实现极其简洁。
ViT 号称"抛弃 CNN",结果第一层用的却是一个卷积。但这个卷积和 CNN 里的卷积作用完全不同:CNN 用小核(3×3)大量重叠滑动来提取局部特征、层层堆叠扩大感受野;ViT 这里的卷积只是一个高效的切块工具,核大小等于步长、完全不重叠、只有一层。它不承担"逐层提特征"的职责——那个活全交给后面的注意力了。
下面是一张模拟的驾驶场景图,按 ViT 的方式切成了网格。点击任意一个方块,右边会实时显示这一块经历的完整流水线:它在网格的哪个位置、序号是几、展平成多少个数、最后落到 token 序列的哪一格。
无论你点图像的哪个位置——天空、路面、车辆——处理流程完全相同。ViT 对每个 patch 一视同仁,不因为"这块看起来重要"就特殊对待。
哪块重要是后面的注意力层决定的,不是在切块阶段。这个"预处理阶段绝对公平、重要性交给注意力学"的设计,正是上一篇讲的 Transformer 哲学在视觉上的复刻。
分类任务需要一个代表整张图的向量。196 个 patch token 各代表一小块,谁来代表全局?
ViT 的做法(借自 BERT):凭空造一个可学习的 token,插在序列最前面。它不对应图像的任何位置,初始值是随机的可学习参数。
在 12 层注意力中,这个 CLS token 和所有 patch token 反复交互,不断从各个图像块吸收信息。到最后一层,它就成了整张图的浓缩表示,接一个分类头就能输出类别。
其实也可以——论文试过全局平均池化,效果差不多。CLS token 的优势是它可以主动学习"该关注谁"(通过注意力加权),而平均池化是无差别地一视同仁。ViT 选 CLS 主要是为了和 BERT 保持一致、便于对照实验。后来很多视觉模型反而回归了平均池化。
这是个有意思的细节:论文里的很多设计不是"最优解",而是"为了可比性的选择"。读论文时要能分辨这两者。
上一篇讲过:注意力机制本身没有顺序概念。对图像来说这个问题更严重——如果不加位置信息,把 196 个 patch 随机打乱,ViT 看到的是完全一样的东西。一张脸和把五官打散重排的图,在它眼里没有区别。
所以要给每个 patch 加上"我在图像的哪个位置"的信息。但这里 ViT 做了个和原始 Transformer 不同的选择:
| 原始 Transformer | ViT | |
|---|---|---|
| 编码方式 | 正弦/余弦函数,固定不变 | 可学习的参数,训练中优化 |
| 维度 | 1D(第几个词) | 1D(第几个 patch) |
| 为什么这样选 | 能外推到更长句子 | 图像尺寸通常固定,不需要外推 |
ViT 用的是一维位置编码(只知道"我是第 37 个"),论文作者也试过精心设计的二维编码(显式告诉它"我在第 3 行第 9 列")——结果几乎没有提升。
原因在可视化里揭晓了:模型自己从一维编号里学出了二维空间结构。把学到的位置编码两两算相似度会发现,相邻行、相邻列的 patch 位置编码彼此相似——它自己搞明白了"第 15 个和第 1 个是上下相邻的"这种二维关系(因为 14 个一行)。
这又是那句话的印证:不需要人告诉它结构,任务会逼它学出来。
加上这两个补丁后,送进 Transformer 的完整序列是:
z₀ = [ x_cls ; x¹E ; x²E ; ... ; x¹⁹⁶E ] + E_pos
# ↑CLS ↑196 个 patch 投影后的 token ↑位置编码(直接相加)
# 形状: [197, 768]
这是 ViT 最"偷懒"也最有说服力的地方——准备好 token 序列之后,它用的是一字未改的标准 Transformer 编码器。
如果 ViT 为视觉大改了架构,那结论会很模糊——到底是"Transformer 适合视觉"还是"改造后的新架构适合视觉"?
一字不改地照搬,然后证明它能打败 CNN,这个论证力度是压倒性的:说明 Transformer 是一个真正通用的架构,图像和文字的差异可以完全在"如何 token 化"这一步吸收掉。这个洞察后来直接催生了多模态大模型——只要能变成 token,什么都能塞进同一个 Transformer。你学的 VLA 正是这个思路的产物。
这一节是整篇论文最有价值的部分,也是最容易被跳过的部分。ViT 不是无条件优于 CNN——它有个很硬的前提条件。
卷积神经网络的结构里,天然内置了两条关于图像的假设(学术上叫"归纳偏置"):
这两条假设正确且免费——不需要从数据里学,CNN 一出生就懂。所以 CNN 在小数据集上也能训得不错。
ViT 的自注意力从第一层起就是全局的——每个 patch 平等地看所有 patch,它不知道相邻的块更相关,也不知道平移不变性。除了 patch 切分保留了一点点局部性,其余全靠自己从数据里学。
结果就是那个著名的结论:
| 训练数据规模 | 谁赢 | 为什么 |
|---|---|---|
| ImageNet(约 130 万张) | CNN 明显更好 | 数据不够,ViT 学不出那些"免费的先验" |
| ImageNet-21k(约 1400 万张) | 打平 | ViT 开始追上来 |
| JFT-300M(约 3 亿张) | ViT 反超 | 数据足够多时,学出来的先验比人手写的更好 |
论文原文的意思是:Transformer 缺少 CNN 固有的归纳偏置(如平移等变性和局部性),因此在数据量不足时泛化能力不好。
换个角度理解:归纳偏置是一种"带偏见的捷径"。数据少时,好偏见能救命;数据多时,偏见反而成了天花板——因为真实世界的规律可能比人写的假设更复杂。ViT 赌的是"数据管够",而这个赌注在 2020 年之后被证明是对的。
这个"先验 vs 数据"的取舍,是理解现代 AI 的一把钥匙,也直接解释了自动驾驶的路线之争:
手写规则的模块化系统 = 最强的归纳偏置。少量数据就能跑,但天花板低、长尾场景写不完。
端到端大模型(FSD v14、小鹏 VLA) = 最弱的归纳偏置。需要海量真实驾驶数据(小鹏 VLA 2.0 的训练数据是 10 亿公里级别),但上限高得多。
车企拼命囤积驾驶数据、建数据闭环,本质上就是在下 ViT 这一篇论证过的同一个赌注——用数据规模换掉人工先验。你 Project 3 里的数据闭环章节,价值根源就在这里。
论文做了几个漂亮的可视化,揭示了 ViT 内部真实的运作方式。这些发现比性能数字更有启发性。
论文统计了每一层各个注意力头的"平均注意力距离"(一个头关注的 patch 离自己有多远):
这个结果很有意思:ViT 自己"重新发明"了 CNN 的局部性——浅层有些头自发学会了只关注邻域。但同时它还保留了另一批全局头,这是 CNN 做不到的(CNN 要堆很多层才能获得大感受野)。
所以 ViT 的优势不是"全局比局部好",而是:它能同时拥有两者,并且由数据决定各自的比例。
前面第 4 节提过:可视化位置编码的两两相似度会发现,同行、同列的 patch 位置编码明显更相似。模型从"第几个"这个一维编号里,自己还原出了"第几行第几列"的二维布局。
把 patch embedding 的投影矩阵可视化,会看到边缘检测、颜色渐变、纹理方向这类基础视觉模式——和 CNN 第一层卷积核学到的东西高度相似。
ViT 没有被告知任何关于图像的知识,但它自发地重新发现了:局部性、二维空间结构、边缘与纹理特征。
这些正是几十年来计算机视觉研究者手工设计进 CNN 里的东西。当数据足够多时,学习可以复现设计,并且超越它。这是本篇论文留给整个领域最深的一句话。
这一篇不像 BEVFormer 那样直接就是智驾模型,但它是整个视觉侧的地基。串一下:
| ViT 的概念 | 在智驾模型里的位置 |
|---|---|
| Patch token | BEVFormer 里 6 路相机图像特征的 token;VLA 里的"视觉 token" |
| Patch embedding | 骨干网络输出的图像特征图(ResNet 或 ViT 都可以充当) |
| 纯注意力骨干 | BEVFormer 可用 ViT 系骨干替代 ResNet;后续 BEV 模型大量采用 |
| CLS token | UniAD 的 Ego Query、VAD 的场景全局向量——都是"一个代表全局的可学习 token" |
| 可学习位置编码 | BEV 网格的空间位置编码 |
| "万物皆可 token" | VLA 把视觉 token + 导航 token + 动作 token 拼成一个序列 |
你之前问过"导航指令怎么和摄像头画面一起进模型"——答案的根就在 ViT。
ViT 证明了图像可以被 token 化,并且和文字 token 在 Transformer 眼里没有区别。一旦这件事成立,把导航信息也编码成 token 塞进同一个序列,就变成了顺理成章的事。没有 ViT 打通"图像→token"这一步,就没有后来的多模态大模型,也就没有 VLA。
上一篇(Transformer)建立了处理 token 序列的通用机器;这一篇(ViT)证明了图像也能变成合法的 token 序列。两篇合起来,才让"用同一个架构处理所有模态"成为可能——这正是 BEVFormer、UniAD、VLA 全部成立的前提。
50176 个 token → O(n²) 注意力矩阵 25 亿。这个数字要能随口算出来。
[3,224,224] → [196, 768] → [197, 768],每一步为什么。
CNN 有什么先验、ViT 没有什么、为什么这导致了对数据量的不同需求。这是面试高频深度题。
那个 Conv2d(3, 768, kernel_size=16, stride=16) 的技巧,按你的"必须手敲"原则,这个要能默写。
下一篇建议读 LSS(Lift, Splat, Shoot)——它和 BEVFormer 是 BEV 的两大流派,你目前只学了 BEVFormer 这一派。读完 LSS,你的 BEV 认知才算完整。