Vision Transformer (ViT)
本篇笔记系统介绍 Vision Transformer(ViT)的架构原理、前向传播过程、主要变体,以及与 CNN 的对比。ViT 是第一个证明"纯 Transformer 可以直接用于图像分类"的架构,标志着计算机视觉从 CNN 主导走向 Transformer 主导的转折点。
背景与动机
视觉编码器
在深度学习中,编码器(Encoder)指的是任何能够把原始数据(如像素、声音、单词)转化成抽象特征向量(Embedding)的模型。视觉编码器(Visual Encoder)指的是输入图片、输出向量的一个结构体。
在Transformer诞生之前的20年里,CNN是视觉编码器的唯一霸主。当我们把一张图片输入到CNN架构的神经网络,比如ResNet中时,图片会进入网络,一层层提取特征,最终输出一个特征向量。整个过程就是"编码"。
Transformer爆火后,Encoder(编码器,负责理解输入)和Decoder(解码器,负责生成输出)的概念广为人知。在此之前,我们很少会说CNN是一个视觉编码器,而是称其为Backbone或Feature Extractor。在2015年前后的Image Captioning任务时代,研究者们尝试把CNN接在RNN前面,此时人们才开始把CNN叫做Visual Encoder,把RNN叫Decoder。
为什么要把 Transformer 用于视觉
CNN有两个先天的局限性:
- 局部感受野的限制:CNN天然偏向局部建模,远距离依赖建模困难;如果想扩大感受野,需要堆叠层数
- CNN的归纳偏置太强:其两个核心假设(平移不变和局部连续)过强,无法让模型自己学到结构
在Transformer架构出现后,Self-Attention机制替代卷积和循环,并获得了全局建模与并行计算能力。
Attention机制有三个特点:
- 数据驱动结构
- 更弱的归纳偏置
- 全局信息天然建模
一开始Transformer被用于NLP任务。随着技术的进步,在2020年,去卷积化的Vision Transformer被提出了,该架构一般简称为ViT。
论文信息
"An Image is Worth 16x16 Words: Transformers for Image Recognition at Scale" Alexey Dosovitskiy et al., Google Brain, 2020 (ICLR 2021)
ViT架构的影响在于,其证明CNN不是唯一解,视觉也可以纯Attention。而ViT之所以能够成功,其核心关键在于数据。随着JFT-300M等大规模数据集的出现,大模型+大数据成为核心趋势,预训练范式成为主流,ViT逐渐成为主流。
值得注意的是,在小数据集上,CNN表现依然强于ViT,这是因为CNN有强归纳偏置,而ViT需要更多的数据才能学到空间结构。
ViT证明:视觉主干可以更通用、更可扩展(像NLP那样堆数据堆模型),于是大家都想搞视觉大模型预训练,但是由于分类标注数据不够规模化、类别封闭、迁移贵等原因很快就撞墙了。最终人们想出了"用语言做监督"的方法,也就是CLIP。CLIP用对比学习把图像和文本拉到了同一空间,从而获得开放词汇和zero-shot迁移能力。
ViT 架构详解
核心思想
ViT 的核心思想极其简洁:把图像当成一串 "词" 来处理。
在 NLP 中,Transformer 的输入是一个 token 序列。ViT 的做法是将图像切成若干个固定大小的 patch,每个 patch 就是一个 "visual token",然后直接送入标准 Transformer Encoder。整个过程不使用任何卷积操作。
完整架构图
输入图像 (H x W x C)
|
v
+--------------------------+
| Patch Partition |
| 切成 N = HW/P² 个 |
| P x P 大小的 patch |
+--------------------------+
|
v 每个 patch 展平为 P²C 维向量
+--------------------------+
| Linear Projection |
| (P²C) --> (D) |
| 即 Patch Embedding |
+--------------------------+
|
v 得到 N 个 D 维 token
+--------------------------+
| Prepend [CLS] Token |
| 序列长度 N --> N+1 |
+--------------------------+
|
v
+--------------------------+
| + Position Embedding |
| 可学习的 1D 位置编码 |
| (N+1) 个 D 维向量 |
+--------------------------+
|
v
+--------------------------+ +---------------------------------+
| Transformer Encoder x L | ---> | 每层的结构 (Pre-Norm): |
| | | z' = z + MSA(LN(z)) |
| L 层堆叠 | | z'' = z' + FFN(LN(z')) |
+--------------------------+ +---------------------------------+
|
v 取 [CLS] token 的输出
+--------------------------+
| MLP Classification Head |
| D --> num_classes |
+--------------------------+
|
v
分类结果
Patch Embedding 详解
这是 ViT 最关键的第一步:将 2D 图像转化为 1D token 序列。
步骤一:Patch Partition(切分)
给定一张输入图像,尺寸为 \(H \times W \times C\)(高度 \(\times\) 宽度 \(\times\) 通道数),选择 patch 大小为 \(P \times P\)。将图像均匀切分为不重叠的 patch:
其中 \(N\) 是 patch 的总数量,也是最终序列的长度(不算 [CLS] token)。
典型配置
对于 \(224 \times 224\) 的图像,\(P = 16\) 时,\(N = 224^2 / 16^2 = 196\) 个 patch。 对于 \(224 \times 224\) 的图像,\(P = 14\) 时,\(N = 224^2 / 14^2 = 256\) 个 patch。
步骤二:Flatten(展平)
每个 patch 的原始尺寸是 \(P \times P \times C\),将其展平为一个一维向量:
例如 \(P = 16\),\(C = 3\)(RGB 图像),则每个 patch 展平后的维度为 \(16^2 \times 3 = 768\)。
步骤三:Linear Projection(线性投影)
展平后的向量通过一个可学习的线性投影层,映射到 Transformer 的隐藏维度 \(D\)(即 \(d_{\text{model}}\)):
其中 \(\mathbf{E} \in \mathbb{R}^{(P^2 C) \times D}\) 是投影矩阵,\(\mathbf{b} \in \mathbb{R}^{D}\) 是偏置。
实现小技巧
在实际实现中,Patch Partition + Flatten + Linear Projection 三步通常用一个 步长等于 patch 大小的卷积 来高效实现:
# PyTorch 实现
self.proj = nn.Conv2d(
in_channels=C, # 输入通道数 (如 3)
out_channels=D, # 输出维度 (如 768)
kernel_size=P, # 卷积核大小 = patch 大小
stride=P # 步长 = patch 大小, 不重叠
)
# 输入: (B, C, H, W) --> 输出: (B, D, H/P, W/P)
# reshape 后: (B, N, D) 其中 N = (H/P) * (W/P)
[CLS] Token
借鉴 BERT 的做法,ViT 在 patch embedding 序列的最前面添加一个特殊的可学习 token,称为 [CLS] token(classification token)。
其中:
- \(\mathbf{x}_{\text{class}} \in \mathbb{R}^{D}\) 是一个可学习的参数向量,随机初始化并在训练中更新
- 分号 \(;\) 表示沿序列维度的拼接
- 添加 [CLS] token 后,序列长度从 \(N\) 变为 \(N + 1\)
为什么需要 [CLS] token?
[CLS] token 不对应任何具体的图像 patch,它的作用是在经过多层 Transformer Encoder 后,通过 Self-Attention 聚合所有 patch 的信息,最终作为整张图像的全局表示。分类头只需要读取 [CLS] token 的输出即可做分类。
替代方案
原论文也尝试过不使用 [CLS] token,而是对所有 patch token 的输出做 全局平均池化(Global Average Pooling, GAP)作为图像表示。实验表明两者效果相当,但使用 [CLS] token 更符合 NLP Transformer 的设计惯例。在一些后续工作中(如 DeiT),GAP 有时表现更好。
Position Embedding
Self-Attention 本身是排列不变的(permutation invariant)——它不关心 token 的顺序。但对于图像来说,patch 的空间位置显然很重要:左上角的 patch 和右下角的 patch 含义不同。因此必须注入位置信息。
ViT 使用可学习的 1D Position Embedding:
这是一个可学习的参数矩阵,每一行对应一个位置(包括 [CLS] 的位置),在训练中通过反向传播学习。
与标准 Transformer 的区别
标准 Transformer("Attention Is All You Need")使用的是固定的正弦余弦位置编码(sinusoidal positional encoding),而 ViT 使用的是可学习的位置编码。原论文实验表明,可学习的 1D 位置编码与手工设计的 2D 位置编码效果相当,说明模型可以从 1D 位置编码中自行学到 2D 空间结构。
为什么是 1D 而不是 2D?
虽然图像 patch 天然有 2D 的行列结构,但实验发现:
- 1D 可学习位置编码的效果与 2D 位置编码几乎相同
- 这说明 Transformer 可以从数据中自行学到 patch 之间的空间关系
- 使用 1D 编码更简单,也更通用
原论文中可视化学到的位置编码后发现:相邻位置的编码向量高度相似,同一行/列的编码呈现出规律性,模型确实自动学到了 2D 结构。
Transformer Encoder
ViT 使用标准的 Transformer Encoder,由 \(L\) 层相同的 Block 堆叠而成。每一层包含:
- Multi-Head Self-Attention (MSA)
- MLP (Feed-Forward Network)
- Layer Normalization (LN)
- Residual Connection(残差连接)
但有一个重要区别:ViT 使用 Pre-Norm(LayerNorm 在 Attention/MLP 之前),而原始 Transformer 使用 Post-Norm(LayerNorm 在 Attention/MLP 之后)。
每一层的前向传播公式:
其中 \(\ell = 1, 2, \ldots, L\)。
Pre-Norm vs Post-Norm
Post-Norm(原始 Transformer):\(\text{LN}(x + \text{Sublayer}(x))\)
Pre-Norm(ViT 使用):\(x + \text{Sublayer}(\text{LN}(x))\)
Pre-Norm 的优势:
- 训练更稳定,尤其是深层模型
- 残差路径上没有 LN 的非线性干扰,梯度流更通畅
- 不需要 warmup 也能稳定训练
Multi-Head Self-Attention 部分:
和标准 Transformer 一样,对输入做 Q、K、V 三个线性投影,然后计算缩放点积注意力:
其中 \(d_k = D / h\),\(h\) 是注意力头的数量。
Self-Attention 的计算复杂度为 \(O(N^2 \cdot D)\),其中 \(N\) 是序列长度(patch 数 + 1)。这意味着 patch 数越多(即图像分辨率越高或 patch 越小),计算量增长越快。
MLP 部分:
MLP(x) = GELU(x W_1 + b_1) W_2 + b_2
MLP 由两个全连接层组成,中间用 GELU 激活函数。隐藏层维度通常是 \(4D\)(即扩展 4 倍后再压缩回来)。
Classification Head
经过 \(L\) 层 Transformer Encoder 后,取 [CLS] token 对应的输出向量 \(\mathbf{z}_L^0\),送入分类头:
- 在预训练阶段:分类头是一个带有一个隐藏层的 MLP
- 在微调阶段:分类头通常简化为一个单层线性层
最终输出维度等于类别数,通过 softmax 得到分类概率分布。
完整前向传播公式总结
将所有步骤用公式串联起来:
其中 \(\mathbf{z}_L^0\) 是最后一层的 [CLS] token 输出。
前向传播数值示例
为了帮助理解 ViT 的每一步到底在做什么,我们用一个极简的数值例子来走一遍前向传播。
设定
| 参数 | 值 | 说明 |
|---|---|---|
| 图像尺寸 | \(4 \times 4 \times 3\) | 高4, 宽4, RGB 3通道 |
| Patch 大小 | \(P = 2\) | 每个 patch 为 \(2 \times 2\) |
| Patch 数量 | \(N = 4 \times 4 / 2^2 = 4\) | 4 个 patch |
| 每个 patch 展平维度 | \(P^2 \cdot C = 4 \times 3 = 12\) | 12 维向量 |
| 模型维度 | \(D = 4\) | 投影后的维度 |
| 序列长度 | \(N + 1 = 5\) | 加上 [CLS] token |
步骤一:Patch Partition + Flatten
假设输入图像(为简化只展示一个通道的像素值示意):
图像 (4x4, 仅示意一个通道):
+-------+-------+
| 1 2 | 5 6 |
| 3 4 | 7 8 | Patch 1: 左上 Patch 2: 右上
+-------+-------+
| 9 10 | 13 14 |
| 11 12 | 15 16 | Patch 3: 左下 Patch 4: 右下
+-------+-------+
每个 patch 为 \(2 \times 2 \times 3\),展平后为 12 维向量。假设展平后(包含 3 个通道):
类似地,\(\mathbf{x}_p^{(2)}, \mathbf{x}_p^{(3)}, \mathbf{x}_p^{(4)}\) 各为 12 维向量。
步骤二:Linear Projection
通过投影矩阵 \(\mathbf{E} \in \mathbb{R}^{12 \times 4}\) 将 12 维映射到 4 维(这里省略偏置):
假设投影后得到:
步骤三:Prepend [CLS] Token
[CLS] token 是一个可学习的参数向量,假设初始化为:
拼接后的序列矩阵 \(\mathbf{Z} \in \mathbb{R}^{5 \times 4}\):
第 0 行是 [CLS],第 1-4 行是 4 个 patch。
步骤四:加 Position Embedding
可学习的位置编码 \(\mathbf{E}_{\text{pos}} \in \mathbb{R}^{5 \times 4}\),假设为:
逐元素相加:
步骤五:Self-Attention(单头简化版)
为了简化,我们只展示单头注意力(\(h=1\), \(d_k = D = 4\))的关键步骤。
5a. 计算 Q, K, V
假设 \(W_Q, W_K, W_V \in \mathbb{R}^{4 \times 4}\) 为单位矩阵(简化),则 \(Q = K = V = \mathbf{z}_0\)。
5b. 计算注意力分数
先算 \(QK^T\)(即 \(\mathbf{z}_0 \mathbf{z}_0^T\),\(5 \times 5\) 矩阵),以第 0 行([CLS])为例:
- \(\text{Score}_{0,0} = \frac{0.1^2 + 0.1^2 + 0.1^2 + 0.1^2}{2} = \frac{0.04}{2} = 0.02\)
- \(\text{Score}_{0,1} = \frac{0.09 - 0.03 + 0.04 + 0.12}{2} = \frac{0.22}{2} = 0.11\)
- \(\text{Score}_{0,2} = \frac{0.11 + 0.03 - 0.04 + 0.06}{2} = \frac{0.16}{2} = 0.08\)
- \(\text{Score}_{0,3} = \frac{-0.03 + 0.09 + 0.07 - 0.01}{2} = \frac{0.12}{2} = 0.06\)
- \(\text{Score}_{0,4} = \frac{0.05 - 0.08 + 0.10 + 0.04}{2} = \frac{0.11}{2} = 0.055\)
5c. Softmax
对 [CLS] 行的分数做 softmax:
由于这些值都很接近(差异小),softmax 输出接近均匀分布(约 \([0.19, \; 0.21, \; 0.20, \; 0.20, \; 0.20]\))。这说明在初始阶段 [CLS] 会"均匀地看所有 patch"。
5d. 加权求和
也就是近似取所有 token 的平均值。随着训练的进行,注意力权重会变得不均匀,[CLS] 会学会更多关注有信息量的 patch。
数值示例的意义
这个简化示例展示了 ViT 前向传播的完整流程。实际模型中:
- \(W_Q, W_K, W_V\) 不是单位矩阵,而是学习到的投影
- 使用多头注意力,每个头关注不同的特征子空间
- 经过多层堆叠后,[CLS] token 的注意力分布会变得高度非均匀
- 最终 [CLS] token 的输出是对整张图像的高度抽象的全局表示
ViT 变体与改进
模型命名规则
ViT 有多个尺寸变体,命名格式为 ViT-{Size}/{Patch Size}:
| 模型 | 层数 \(L\) | 隐藏维度 \(D\) | 注意力头数 \(h\) | MLP 维度 | 参数量 |
|---|---|---|---|---|---|
| ViT-B/16 | 12 | 768 | 12 | 3072 | 86M |
| ViT-B/32 | 12 | 768 | 12 | 3072 | 86M |
| ViT-L/16 | 24 | 1024 | 16 | 4096 | 307M |
| ViT-L/32 | 24 | 1024 | 16 | 4096 | 307M |
| ViT-H/14 | 32 | 1280 | 16 | 5120 | 632M |
- B / L / H = Base / Large / Huge,表示模型规模
- /16 / /32 / /14 = patch 大小,patch 越小则 token 序列越长,计算量越大,但分辨率更高
Patch 大小的影响
以 \(224 \times 224\) 图像为例:
- P=32: \(N = 49\) 个 patch,计算快但分辨率粗糙
- P=16: \(N = 196\) 个 patch,计算量和效果的良好平衡
- P=14: \(N = 256\) 个 patch,分辨率高,计算量大
注意 Self-Attention 的计算量是 \(O(N^2)\),所以 patch 大小从 32 变为 16 时,计算量增长约 \((196/49)^2 = 16\) 倍。
DeiT: 数据高效训练
Data-efficient Image Transformers (DeiT),Touvron et al., 2021
ViT 的一个主要缺点是需要大量数据(如 JFT-300M)才能达到好的效果。DeiT 解决了这个问题,证明 ViT 可以仅在 ImageNet-1K 上从头训练并达到有竞争力的结果。
DeiT 的关键创新:
- 更强的数据增强和正则化:使用 RandAugment、Mixup、CutMix、Erasing、标签平滑、随机深度等训练技巧
- 知识蒸馏 Token(Distillation Token):除了 [CLS] token,再添加一个可学习的 distillation token,用于接收来自教师模型(通常是 CNN,如 RegNet)的知识蒸馏信号
序列结构对比:
ViT: [CLS] [Patch_1] [Patch_2] ... [Patch_N]
DeiT: [CLS] [DIST] [Patch_1] [Patch_2] ... [Patch_N]
^
蒸馏 token, 用教师模型的输出作为目标训练
DeiT 的蒸馏 token 产生的注意力模式与 [CLS] token 不同,形成了一种互补关系。最终推理时将两个 token 的输出取平均。
Swin Transformer: 层次化视觉 Transformer
Swin Transformer,Liu et al., 2021 (ICCV 2021 Best Paper)
ViT 使用全局 Self-Attention,计算量随图像分辨率平方增长,难以处理高分辨率图像和密集预测任务(如检测、分割)。Swin Transformer 通过两个创新解决了这些问题。
创新一:窗口注意力(Window Attention)
不对所有 patch 做全局注意力,而是将 patch 划分为不重叠的局部窗口(如 \(7 \times 7\)),每个窗口内部做 Self-Attention。
- 计算量从 \(O(N^2)\) 降为 \(O(N \cdot M^2)\),其中 \(M\) 是窗口大小
- 计算量随图像尺寸线性增长,而不是二次增长
创新二:移位窗口(Shifted Window)
连续两层交替使用不同的窗口划分方式:
第 l 层 (常规窗口): 第 l+1 层 (移位窗口):
+-----+-----+-----+ +--+--------+--+
| | | | | | | |
| W1 | W2 | W3 | +--+--------+--+
| | | | | | | |
+-----+-----+-----+ --> | | W' | |
| | | | | | | |
| W4 | W5 | W6 | +--+--------+--+
| | | | | | | |
+-----+-----+-----+ +--+--------+--+
移位后,原本属于不同窗口的 patch 可以在新窗口中交互信息,实现了跨窗口连接。
创新三:层次化结构
类似于 CNN 的多尺度特征图(如 FPN),Swin Transformer 通过 Patch Merging 逐层降低分辨率:
Stage 1: H/4 x W/4, C 维 (最高分辨率)
↓ Patch Merging
Stage 2: H/8 x W/8, 2C 维
↓ Patch Merging
Stage 3: H/16 x W/16, 4C 维
↓ Patch Merging
Stage 4: H/32 x W/32, 8C 维 (最低分辨率)
这种层次化结构使 Swin Transformer 可以作为通用的视觉骨干,直接用于检测和分割等下游任务。
MAE: 掩码自编码预训练
Masked Autoencoders (MAE),He et al., 2022 (CVPR 2022)
MAE 将 NLP 中 BERT 的掩码预训练思想迁移到视觉领域,是 ViT 的一种高效自监督预训练方法。
核心思想:
- 随机遮住输入图像的大部分 patch(通常 75%)
- 只把未遮住的 patch 送入 Encoder(大幅减少计算量)
- 用一个轻量级 Decoder 重建被遮住的 patch 的像素值
原图 patches: [P1] [P2] [P3] [P4] [P5] [P6] [P7] [P8] [P9]
| X X | X X X | X
v v v
Encoder 输入: [P1] [P4] [P8]
| | |
v v v
+------ Encoder (ViT, 只处理25%的token) ------+
|
v
+--- 补回 mask tokens + Decoder (轻量) ---+
|
v
重建目标: [P1] [P2] [P3] [P4] [P5] [P6] [P7] [P8] [P9]
^ ^ ^ ^ ^ ^
重建这些 patch 的像素值
MAE 的关键优势:
- 预训练效率极高(Encoder 只处理 25% 的 token)
- 不需要标注数据,纯自监督
- 预训练后 ViT 学到了强大的视觉表示,微调后在多个任务上达到 SOTA
为什么 75% 的遮盖率这么高?
与 NLP 不同,图像有很强的空间冗余性(相邻像素高度相似)。如果只遮住 15%(像 BERT 那样),模型可以轻易地通过插值恢复像素,学不到高层语义特征。高遮盖率迫使模型理解图像的全局结构才能重建。
ViT vs CNN 对比
| 对比维度 | CNN (如 ResNet) | ViT |
|---|---|---|
| 核心操作 | 卷积 (局部滑动窗口) | Self-Attention (全局) |
| 归纳偏置 | 强: 局部性 + 平移等变性 | 弱: 几乎无视觉先验 |
| 感受野 | 局部,逐层扩大 | 全局,每层都能看到所有 patch |
| 特征层次 | 天然多尺度 (conv1→conv5) | 单一尺度 (除非使用 Swin 等变体) |
| 小数据表现 | 好 (归纳偏置补偿数据不足) | 差 (需要大量数据学习空间结构) |
| 大数据表现 | 趋于饱和 | 持续提升,scaling law 明显 |
| 计算复杂度 | \(O(K^2 \cdot C_{\text{in}} \cdot C_{\text{out}} \cdot HW)\) | \(O(N^2 \cdot D)\),\(N\) 为 patch 数 |
| 位置信息 | 隐式编码在卷积结构中 | 需要显式添加位置编码 |
| 可解释性 | 特征图可视化 | 注意力图可视化 |
| 下游任务适配 | 天然适配检测/分割 (多尺度特征) | 需要改造 (如 Swin) 才能高效适配 |
关于归纳偏置的权衡
归纳偏置是一把双刃剑:
- 强归纳偏置 (CNN):在数据不足时是"免费的先验知识",帮助模型快速收敛;但也限制了模型的表达上限,大数据下容易成为瓶颈
- 弱归纳偏置 (ViT):给了模型更大的自由度,在大数据下可以学到更好的表示;但在小数据下需要更多的样本才能学到 CNN 天然具有的空间先验
这解释了为什么 ViT 在 JFT-300M 上预训练后效果超过 CNN,但在 ImageNet-1K 上从头训练时不如 CNN。
CLIP
更多关于CLIP的详细内容参见 Foundation Model/CLIP笔记。因为ViT与CLIP有很密切的关系,所以这里简单介绍一下。
2021年前后,大模型开始出现,更多的数据被需要。此时人们在想:视觉模型能不能不靠人工标签,学到更通用、可迁移的语义?" (监督信号/泛化范式问题)
此时有一个问题出现了:虽然ViT在更大数据集上表现更好,但是更大规模的高质量标注分类数据非常难搞:
- ImageNet级别的标注非常昂贵
- 类别体系十分有限
- 标注粒度死板(只能是某个类别)
此时人们意识到,问题不再是"主干用什么"(用CNN还是ViT),而是"监督信号用什么"。这就把CLIP推到了舞台中央:用互联网上天然存在的图文配对当监督。
在传统监督学习的分类任务中,其本质学的是:
把图像压进一个"固定标签集合"的判别边界里
其硬伤是显著的:
- 类别封闭(closed-set),只要训练时没见过,那么推理时就不可能知道
- 语义贫乏:"狗"这么一个简单的标签无法表达"一只黑色拉布拉多在草地上奔跑"
- 迁移成本高:换个数据集就需要重新训练分类头,甚至微调整个backbone
而大模型需要的是海量数据、低成本监督,监督信号最好还能覆盖开放语义。互联网上现成就有:图片+文本(标题、描述、alt-text)等,这些就是CLIP的数据来源逻辑。

CLIP的基本逻辑如上图所示:
- 对比预训练 Contrastive Pre-traning,一批里有N张图和N段文本(成对的),Image Encoder得到\(I_1...I_N\),Text Encoder得到\(T_1...T_N\),做一个\(N \times N\)的相似度矩阵\(S_{ij} = I_i \cdot T_j\)。训练目标是对角线(正确配对)相似度高,非对角(错误配对)相似度低。
- 用标签文本造分类器(Create classifier from label text)。传统分类器最后一层权重矩阵 = 类别参数,而CLIP分类器的类别文本的embedding = 类别原型。比如三个类别:a photo of a dog, a photo of a car, a photo of a plane,这些句子通过text encoder得到3个向量,他们就成了分类器的权重。
- zero-shot推理:新来一张图,图像通过image encoder得到向量\(I\),分别算与每个类别文本向量的相似度,相似度最大的那个类别就是预测结果
CLIP的关键不是"更强的视觉主干",而是把任务从"分类"改成"对齐"。把从"学一个分类器"变成"学一个共同语义空间":
- 图像编码器把图像变成向量\(v\)
- 文本编码器把文本变成向量\(t\)
- 训练目标:匹配的(image, text)向量相似度高,不匹配的低。
通过这一步,就可以学到一个同时容纳图像语义和语言语义的空间。一旦有了这个空间,分类就变成了一个检索问题:
- 把所有类别写成句子(prompt)
- 编码成文本向量
- 让图片向量去"找最像的那个文本"
如此一来,分类任务就变成了图文相似度检索。
总结来说,CLIP解决了如下问题:
- 开放词汇分类(open-vocabulary),只要你能写出来的类别,他就能试着分(能力取决于训练数据覆盖)
- 迁移成本下降,不需要为每一个人物训练一个新的分类头
- 因为监督来自于自然语言,因此语义更丰富,颗粒度更细,比如模型学到的概念是"red car"而不是"car"
- 监督可规模化,图文对获取成本远低于精标类别
然而,CLIP没有解决的问题包括:
- CLIP的工作是对齐,而不是"框出哪里",因此原生CLIP并没有定位和检测能力
- 组合泛化有限,"红色立方体在蓝色球体左边"这种强组合推理不一定稳
- 数据噪声和偏见问题严重,因为互联网图文对本身就很嘈杂,有偏差、捷径学习等问题
- 对prompt很敏感,"a photo of a dog" vs "a dog" 可能效果差很多,所以后来出现 prompt engineering / prompt ensembling 等技巧
ViT架构案例
Qwen2.5-VL-3B-Instruct
架构简单理解:
Input Image (H x W x C)
|
Split into patches (P x P) --> N patches
|
Linear projection to D-dim tokens (N x D)
|
Add positional embedding (+ optional [CLS])
|
Transformer Encoder x L
- (Window) Multi-head self-attention
- MLP (SwiGLU/GELU)
- Norm + residual
|
Output visual token sequence (N x D) / or CLS for classification
输入图片切成长宽为P的patches后,每个patch被编码为长度为D的embedding,然后作为序列导入到transformer中。
思考与讨论
ViT 为什么需要大数据?
ViT 在小数据集上不如 CNN,这个现象背后的原因值得深思。
CNN 的卷积操作本质上是一种硬编码的先验知识:
- 局部连接告诉模型:"相邻像素更相关"——模型不需要从数据中学习这一点
- 权重共享告诉模型:"同样的模式可以出现在图像的任何位置"——模型不需要为每个位置学习单独的检测器
而 ViT 的 Self-Attention 是全局的、无约束的:
- 每个 patch 可以关注任意其他 patch,模型需要自己学到"相邻 patch 更相关"
- 没有权重共享的假设,模型需要自己发现平移等变性
- 这些 CNN "免费获得"的知识,ViT 需要从数据中学习
因此,当数据不足时,ViT 无法学到好的空间先验,表现自然不如 CNN。但当数据足够多时,ViT 不受 CNN 的归纳偏置限制,可以学到更灵活、更强大的表示。
归纳偏置是利还是弊?
这个问题没有绝对答案,取决于数据规模和任务需求:
| 场景 | 强归纳偏置 (CNN) | 弱归纳偏置 (ViT) |
|---|---|---|
| 小数据 (< 10K) | 优势明显 | 过拟合严重 |
| 中等数据 (ImageNet级) | 表现稳定 | 需要精心训练 (DeiT) |
| 大数据 (JFT/LAION级) | 趋于饱和 | 持续提升 |
| 领域特定任务 | 效率高 | 可能过度 |
| 通用基础模型 | 灵活性不足 | 表达上限更高 |
从发展趋势来看,随着数据和算力的持续增长,弱归纳偏置的模型(如 ViT)在性能上限方面具有优势。但在资源受限的场景下,CNN 的效率优势仍然显著。
ViT 是否会完全取代 CNN?
短期来看,不会。原因如下:
- 资源效率:CNN 在边缘设备和移动端仍然有无可比拟的效率优势。MobileNet 系列的 CNN 可以在手机上实时运行,而同等精度的 ViT 计算量要大得多。
- 小数据场景:医学影像、工业检测等领域数据稀缺,CNN 的归纳偏置仍然很有价值。
- 混合架构:实践中越来越多的模型采用 CNN + Transformer 的混合架构(如 CoAtNet、EfficientFormer),结合两者优势。
长期来看,趋势确实在向 Transformer 倾斜:
- 大规模预训练 + 微调的范式天然适合 ViT
- ViT 与语言模型使用相同的架构,有利于多模态统一(如 CLIP、GPT-4V)
- Swin Transformer 等变体已经在检测、分割等 CNN 传统强项上取得了超越
一个更深层的视角
从"No Free Lunch"定理的角度看,没有一种架构在所有任务上最优。ViT 的成功更多是因为我们进入了"大数据 + 大模型"的时代,而不是因为 Transformer 在数学上优于卷积。如果未来某天出现了比 Self-Attention 更高效的全局建模机制,ViT 同样可能被取代。架构的演进始终是在效率、表达能力、归纳偏置之间寻找最佳平衡点。