Skip to content

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有两个先天的局限性:

  1. 局部感受野的限制:CNN天然偏向局部建模,远距离依赖建模困难;如果想扩大感受野,需要堆叠层数
  2. 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 = \frac{H \times W}{P^2} \]

其中 \(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\),将其展平为一个一维向量:

\[ \mathbf{x}_p^{(i)} \in \mathbb{R}^{P^2 \cdot C} \]

例如 \(P = 16\)\(C = 3\)(RGB 图像),则每个 patch 展平后的维度为 \(16^2 \times 3 = 768\)

步骤三:Linear Projection(线性投影)

展平后的向量通过一个可学习的线性投影层,映射到 Transformer 的隐藏维度 \(D\)(即 \(d_{\text{model}}\)):

\[ \mathbf{z}_0^{(i)} = \mathbf{x}_p^{(i)} \mathbf{E} + \mathbf{b} \]

其中 \(\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)
这个卷积等价于对每个 patch 做独立的线性投影,但利用了 GPU 对卷积的优化,速度更快。

[CLS] Token

借鉴 BERT 的做法,ViT 在 patch embedding 序列的最前面添加一个特殊的可学习 token,称为 [CLS] token(classification token)。

\[ \mathbf{z}_0 = [\mathbf{x}_{\text{class}}; \; \mathbf{z}_0^{(1)}; \; \mathbf{z}_0^{(2)}; \; \cdots; \; \mathbf{z}_0^{(N)}] + \mathbf{E}_{\text{pos}} \]

其中:

  • \(\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

\[ \mathbf{E}_{\text{pos}} \in \mathbb{R}^{(N+1) \times D} \]

这是一个可学习的参数矩阵,每一行对应一个位置(包括 [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 堆叠而成。每一层包含:

  1. Multi-Head Self-Attention (MSA)
  2. MLP (Feed-Forward Network)
  3. Layer Normalization (LN)
  4. Residual Connection(残差连接)

但有一个重要区别:ViT 使用 Pre-Norm(LayerNorm 在 Attention/MLP 之前),而原始 Transformer 使用 Post-Norm(LayerNorm 在 Attention/MLP 之后)。

每一层的前向传播公式:

\[ \mathbf{z}'_\ell = \mathbf{z}_{\ell-1} + \text{MSA}(\text{LN}(\mathbf{z}_{\ell-1})) \]
\[ \mathbf{z}_\ell = \mathbf{z}'_\ell + \text{MLP}(\text{LN}(\mathbf{z}'_\ell)) \]

其中 \(\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 三个线性投影,然后计算缩放点积注意力:

\[ \text{Attention}(Q, K, V) = \text{softmax}\left(\frac{QK^T}{\sqrt{d_k}}\right) 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\),送入分类头:

\[ \mathbf{y} = \text{MLP}_{\text{head}}(\text{LN}(\mathbf{z}_L^0)) \]
  • 在预训练阶段:分类头是一个带有一个隐藏层的 MLP
  • 在微调阶段:分类头通常简化为一个单层线性层

最终输出维度等于类别数,通过 softmax 得到分类概率分布。

完整前向传播公式总结

将所有步骤用公式串联起来:

\[ \mathbf{z}_0 = [\mathbf{x}_{\text{class}}; \; \mathbf{x}_p^{(1)}\mathbf{E}; \; \mathbf{x}_p^{(2)}\mathbf{E}; \; \cdots; \; \mathbf{x}_p^{(N)}\mathbf{E}] + \mathbf{E}_{\text{pos}} \]
\[ \mathbf{z}'_\ell = \mathbf{z}_{\ell-1} + \text{MSA}(\text{LN}(\mathbf{z}_{\ell-1})), \quad \ell = 1 \ldots L \]
\[ \mathbf{z}_\ell = \mathbf{z}'_\ell + \text{MLP}(\text{LN}(\mathbf{z}'_\ell)), \quad \ell = 1 \ldots L \]
\[ \mathbf{y} = \text{LN}(\mathbf{z}_L^0) \]

其中 \(\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^{(1)} = [1, 2, 3, 4, \; 0.5, 1, 1.5, 2, \; 0.1, 0.2, 0.3, 0.4] \in \mathbb{R}^{12} \]

类似地,\(\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 维(这里省略偏置):

\[ \mathbf{z}^{(i)} = \mathbf{x}_p^{(i)} \mathbf{E} \in \mathbb{R}^{4} \]

假设投影后得到:

\[ \mathbf{z}^{(1)} = [0.8, \; -0.3, \; 0.5, \; 1.2] $$ $$ \mathbf{z}^{(2)} = [1.1, \; 0.2, \; -0.4, \; 0.7] $$ $$ \mathbf{z}^{(3)} = [-0.2, \; 0.9, \; 0.6, \; -0.1] $$ $$ \mathbf{z}^{(4)} = [0.5, \; -0.7, \; 1.0, \; 0.3] \]

步骤三:Prepend [CLS] Token

[CLS] token 是一个可学习的参数向量,假设初始化为:

\[ \mathbf{x}_{\text{class}} = [0.1, \; 0.1, \; 0.1, \; 0.1] \]

拼接后的序列矩阵 \(\mathbf{Z} \in \mathbb{R}^{5 \times 4}\)

\[ \mathbf{Z} = \begin{bmatrix} 0.1 & 0.1 & 0.1 & 0.1 \\ 0.8 & -0.3 & 0.5 & 1.2 \\ 1.1 & 0.2 & -0.4 & 0.7 \\ -0.2 & 0.9 & 0.6 & -0.1 \\ 0.5 & -0.7 & 1.0 & 0.3 \end{bmatrix} \]

第 0 行是 [CLS],第 1-4 行是 4 个 patch。

步骤四:加 Position Embedding

可学习的位置编码 \(\mathbf{E}_{\text{pos}} \in \mathbb{R}^{5 \times 4}\),假设为:

\[ \mathbf{E}_{\text{pos}} = \begin{bmatrix} 0.0 & 0.0 & 0.0 & 0.0 \\ 0.1 & 0.0 & -0.1 & 0.0 \\ 0.0 & 0.1 & 0.0 & -0.1 \\ -0.1 & 0.0 & 0.1 & 0.0 \\ 0.0 & -0.1 & 0.0 & 0.1 \end{bmatrix} \]

逐元素相加:

\[ \mathbf{z}_0 = \mathbf{Z} + \mathbf{E}_{\text{pos}} = \begin{bmatrix} 0.1 & 0.1 & 0.1 & 0.1 \\ 0.9 & -0.3 & 0.4 & 1.2 \\ 1.1 & 0.3 & -0.4 & 0.6 \\ -0.3 & 0.9 & 0.7 & -0.1 \\ 0.5 & -0.8 & 1.0 & 0.4 \end{bmatrix} \]

步骤五: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. 计算注意力分数

\[ \text{Score} = \frac{QK^T}{\sqrt{d_k}} = \frac{\mathbf{z}_0 \mathbf{z}_0^T}{\sqrt{4}} = \frac{\mathbf{z}_0 \mathbf{z}_0^T}{2} \]

先算 \(QK^T\)(即 \(\mathbf{z}_0 \mathbf{z}_0^T\)\(5 \times 5\) 矩阵),以第 0 行([CLS])为例:

\[ \text{Score}_{0,j} = \frac{\mathbf{z}_0^{(0)} \cdot \mathbf{z}_0^{(j)}}{2} \]
  • \(\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:

\[ \alpha_{0,j} = \text{softmax}([0.02, \; 0.11, \; 0.08, \; 0.06, \; 0.055]) \]

由于这些值都很接近(差异小),softmax 输出接近均匀分布(约 \([0.19, \; 0.21, \; 0.20, \; 0.20, \; 0.20]\))。这说明在初始阶段 [CLS] 会"均匀地看所有 patch"。

5d. 加权求和

\[ \text{output}_0 = \sum_j \alpha_{0,j} \cdot V_j \approx \frac{1}{5} \sum_j \mathbf{z}_0^{(j)} \]

也就是近似取所有 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 的关键创新:

  1. 更强的数据增强和正则化:使用 RandAugment、Mixup、CutMix、Erasing、标签平滑、随机深度等训练技巧
  2. 知识蒸馏 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 的一种高效自监督预训练方法。

核心思想:

  1. 随机遮住输入图像的大部分 patch(通常 75%)
  2. 只把未遮住的 patch 送入 Encoder(大幅减少计算量)
  3. 用一个轻量级 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的数据来源逻辑。

1771619394464

CLIP的基本逻辑如上图所示:

  1. 对比预训练 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\)。训练目标是对角线(正确配对)相似度高,非对角(错误配对)相似度低。
  2. 用标签文本造分类器(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个向量,他们就成了分类器的权重。
  3. zero-shot推理:新来一张图,图像通过image encoder得到向量\(I\),分别算与每个类别文本向量的相似度,相似度最大的那个类别就是预测结果

CLIP的关键不是"更强的视觉主干",而是把任务从"分类"改成"对齐"。把从"学一个分类器"变成"学一个共同语义空间"

  • 图像编码器把图像变成向量\(v\)
  • 文本编码器把文本变成向量\(t\)
  • 训练目标:匹配的(image, text)向量相似度高,不匹配的低。

通过这一步,就可以学到一个同时容纳图像语义和语言语义的空间。一旦有了这个空间,分类就变成了一个检索问题:

  • 把所有类别写成句子(prompt)
  • 编码成文本向量
  • 让图片向量去"找最像的那个文本"

如此一来,分类任务就变成了图文相似度检索。

总结来说,CLIP解决了如下问题:

  1. 开放词汇分类(open-vocabulary),只要你能写出来的类别,他就能试着分(能力取决于训练数据覆盖)
  2. 迁移成本下降,不需要为每一个人物训练一个新的分类头
  3. 因为监督来自于自然语言,因此语义更丰富,颗粒度更细,比如模型学到的概念是"red car"而不是"car"
  4. 监督可规模化,图文对获取成本远低于精标类别

然而,CLIP没有解决的问题包括:

  1. CLIP的工作是对齐,而不是"框出哪里",因此原生CLIP并没有定位和检测能力
  2. 组合泛化有限,"红色立方体在蓝色球体左边"这种强组合推理不一定稳
  3. 数据噪声和偏见问题严重,因为互联网图文对本身就很嘈杂,有偏差、捷径学习等问题
  4. 对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?

短期来看,不会。原因如下:

  1. 资源效率:CNN 在边缘设备和移动端仍然有无可比拟的效率优势。MobileNet 系列的 CNN 可以在手机上实时运行,而同等精度的 ViT 计算量要大得多。
  2. 小数据场景:医学影像、工业检测等领域数据稀缺,CNN 的归纳偏置仍然很有价值。
  3. 混合架构:实践中越来越多的模型采用 CNN + Transformer 的混合架构(如 CoAtNet、EfficientFormer),结合两者优势。

长期来看,趋势确实在向 Transformer 倾斜:

  • 大规模预训练 + 微调的范式天然适合 ViT
  • ViT 与语言模型使用相同的架构,有利于多模态统一(如 CLIP、GPT-4V)
  • Swin Transformer 等变体已经在检测、分割等 CNN 传统强项上取得了超越

一个更深层的视角

从"No Free Lunch"定理的角度看,没有一种架构在所有任务上最优。ViT 的成功更多是因为我们进入了"大数据 + 大模型"的时代,而不是因为 Transformer 在数学上优于卷积。如果未来某天出现了比 Self-Attention 更高效的全局建模机制,ViT 同样可能被取代。架构的演进始终是在效率、表达能力、归纳偏置之间寻找最佳平衡点。


评论 #