引言
Transformer 从 NLP 走进视觉,带来两件事:一是 ViT 证明「纯注意力也能做视觉」,二是 CLIP 这类多模态模型让「零样本分类」与「图文检索」成为工程标配。理解 ViT 与 CLIP,是理解当下视觉系统的基础。
本文按「注意力 → ViT → Swin 与 ConvNeXt → 多模态」的顺序拆解,重点讲清 patch embedding、位置编码、窗口注意力、对比学习这些机制,并给出可直接运行的代码。
目录
- 自注意力回顾
- ViT
- DeiT 与训练技巧
- Swin
- ConvNeXt
- 多模态 CLIP
- BLIP-2 与 LLaVA
- 代码:timm 加载 ViT 与 Swin
- 代码:CLIP 零样本分类
- 模型对比表
- 自监督与大规模预训练
- 视觉基础模型与提示工程
- 多模态检索与向量数据库
- 端侧与部署
- 权衡取舍
- 常见坑清单
- 小结
1. 自注意力回顾
自注意力把输入序列的每个元素映射为查询 Q、键 K、值 V,用 Q 与 K 的相似度做加权,再对 V 加权求和:
Attention(Q, K, V) = softmax(Q K^T / sqrt(d)) V
多头注意力并行多组 QKV,捕捉不同子空间的关系。关键性质是「全局感受野」:任意两个位置都能直接交互,这是它与卷积最大的区别。代价是复杂度对序列长度 N 是 O(N²),序列越长越贵,这也是 ViT 处理高分辨率图像时的主要瓶颈。
2. ViT
2.1 patch embedding
ViT 把图像切成固定大小的 patch(如 16×16),每个 patch 展平后线性投影成一个向量,再当作序列输入 Transformer。224×224 的图切成 16×16 的 patch,得到 196 个 token,加上 class token 共 197 个。
2.2 class token 与位置编码
ViT 借鉴 BERT,在序列最前面加一个可学习的 class token,用它的输出做分类。由于注意力本身无序,必须加位置编码(position embedding)提供位置信息。ViT 用可学习的一维位置编码。
2.3 数据需求
ViT 缺少卷积的归纳偏置(局部性、平移不变性),因此在小数据集上表现不如 CNN,必须在超大规模数据(如 JFT-300M、ImageNet-21K)上预训练才能超越 CNN。ViT-B/16 约 8600 万参数,ImageNet-21K 预训练后在 ImageNet-1K 上约 84% Top-1。
3. DeiT 与训练技巧
DeiT(Data-efficient Image Transformer)证明「不用超大私有数据也能训好 ViT」,靠的是强数据增强与蒸馏。它引入蒸馏 token,让 ViT 向一个强 CNN 教师学习。DeiT-B 在 ImageNet-1K 上约 81.8% Top-1,把 ViT 的训练门槛大幅降低。
训练 ViT 的常见技巧:
- 强增强:RandAugment、Mixup、Cutmix,弥补归纳偏置的缺失。
- 随机深度(stochastic depth):训练时随机丢弃整个残差分支,缓解过拟合。
- 层学习率衰减:浅层用更小学习率,保护预训练特征。
- 优化器:AdamW 配合较长的 warmup,避免早期训练不稳。
- 权重衰减与标签平滑:抑制过拟合,提升泛化。
4. Swin
Swin Transformer 用两个设计降低计算并引入层次:
- 窗口注意力:把注意力限制在局部窗口内,复杂度从 O(N²) 降到线性,同时保持局部建模。
- 移位窗口:相邻层之间移动窗口位置,让窗口间能跨窗交互,弥补局部性限制。
- 层次结构:像 CNN 一样逐层下采样,输出多尺度特征,天然适配检测与分割。
Swin-B 约 8800 万参数,ImageNet-1K 上约 83.5% Top-1,是检测分割里最常用的 Transformer 骨干。
5. ConvNeXt
ConvNeXt 反过来把 Transformer 的设计经验搬回 CNN:大核 7×7 深度卷积、LayerNorm、GELU、更少的归一化与激活、倒瓶颈结构。在同等训练配方下,ConvNeXt 与 Swin 精度相当,但纯卷积结构在部署上更成熟、算子支持更好。ConvNeXt-B 约 8900 万参数,Top-1 约 83.8%。
结论:CNN 与 Transformer 之争在工程上已趋于融合,选型更多看部署生态与具体任务。
6. 多模态 CLIP
CLIP(Contrastive Language-Image Pre-training)用 4 亿图文对做对比学习:把图像编码器与文本编码器映射到同一空间,让匹配的图文对相似度更高、不匹配的更低。
它的工程价值在于零样本分类:把类别名写成 prompt(如 “a photo of a cat”),编码成文本向量,与图像向量比相似度,取最高者作为预测。无需训练即可分类新类别。CLIP 的 ViT-L/14 图像编码器约 3 亿参数,在 ImageNet 零样本上约 76% Top-1。
CLIP 也支撑图文检索、图像相似度、内容审核等任务。
7. BLIP-2 与 LLaVA
多模态大模型把视觉编码器与大语言模型连接:
- BLIP-2:用 Q-Former 把图像特征压缩成少量 query token,喂给冻结的 LLM,实现图像问答与描述。
- LLaVA:用简单的投影层把视觉特征映射到 LLM 的词嵌入空间,再用指令数据微调,实现对话式视觉理解。
- SAM:用 prompt(点、框)驱动分割,是「视觉基础模型」的另一代表。
这类模型的工程形态是「视觉编码器加投影层加 LLM」,理解这个三段式结构,就理解了大部分多模态系统的骨架。
8. 代码:timm 加载 ViT 与 Swin
timm 提供统一接口加载各种预训练视觉模型,是工程上最省心的选择。
import torch
import timm
from PIL import Image
from timm.data import resolve_data_config, create_transform
def load_and_predict(model_name, image_path, topk=5):
model = timm.create_model(model_name, pretrained=True).eval()
config = resolve_data_config({}, model=model)
transform = create_transform(**config)
x = transform(Image.open(image_path).convert("RGB")).unsqueeze(0)
with torch.inference_mode():
probs = model(x).softmax(dim=1)[0]
p, idx = probs.topk(topk)
return model_name, [(int(i), round(float(v), 4)) for v, i in zip(p, idx)]
for name in ["vit_base_patch16_224", "swin_tiny_patch4_window7_224", "convnext_tiny"]:
print(load_and_predict(name, "dog.jpg"))
注意 resolve_data_config 会自动读取模型配套的输入尺寸与归一化参数,避免手动配置出错。ViT 与 Swin 的输入尺寸和归一化常常不同,手动写很容易踩坑。
9. 代码:CLIP 零样本分类
open_clip 或 transformers 都能加载 CLIP,下面演示零样本分类。
import torch
import open_clip
from PIL import Image
model, _, preprocess = open_clip.create_model_and_transforms(
"ViT-B-32", pretrained="laion2b_s34b_b79k"
)
model.eval()
tokenizer = open_clip.get_tokenizer("ViT-B-32")
labels = ["a photo of a cat", "a photo of a dog", "a photo of a car"]
text = tokenizer(labels)
image = preprocess(Image.open("unknown.jpg")).unsqueeze(0)
with torch.inference_mode():
image_feat = model.encode_image(image)
text_feat = model.encode_text(text)
image_feat /= image_feat.norm(dim=-1, keepdim=True)
text_feat /= text_feat.norm(dim=-1, keepdim=True)
probs = (100.0 * image_feat @ text_feat.T).softmax(dim=-1)[0]
for label, p in zip(labels, probs.tolist()):
print(f"{label:<25s} {p:.4f}")
prompt 的措辞会显著影响零样本精度,工程上常做 prompt ensemble:用多个模板(“a photo of a {}"、“a picture of a {}")取平均,能提升一两个点。
10. 模型对比表
| 模型 | 类型 | 参数量 | ImageNet Top-1 | 特点 |
|---|---|---|---|---|
| ResNet-50 | CNN | 25.6M | 76.1% | 部署成熟 |
| ConvNeXt-B | CNN | 89M | 83.8% | 现代化 CNN |
| ViT-B/16 | Transformer | 86M | 84.0%(21K 预训练) | 需大数据 |
| DeiT-B | Transformer | 86M | 81.8% | 仅 ImageNet 训练 |
| Swin-B | Transformer | 88M | 83.5% | 层次结构,检测分割友好 |
| CLIP ViT-L/14 | 多模态 | 约 428M | 76.0% 零样本 | 开放词汇 |
指标口径不同:ViT-B/16 的 84% 需要 ImageNet-21K 预训练,DeiT-B 只在 ImageNet-1K 训练,直接比较时要看清训练数据。
11. 自监督与大规模预训练
ViT 的崛起离不开自监督预训练,它让「无标注数据」也能训出强视觉表征。
- MAE(Masked Autoencoder):随机遮住 75% 的 patch,只让编码器看剩下的,再让解码器重建被遮住的部分。重建任务迫使模型学习语义,MAE 预训练的 ViT 微调后精度很高。
- DINO / DINOv2:自蒸馏,学生网络预测教师网络的输出,无需负样本,产出的特征在下游任务上泛化好。DINOv2 的 ViT-g 特征被广泛用于检索与分割。
- SimCLR / MoCo:对比学习,同一图的不同增强视为正样本,其他图视为负样本。MoCo 用队列与动量编码器扩大负样本量。
import torch
def random_masking(x, mask_ratio=0.75):
# x: (B, N, D) patch 序列,返回保留的 token 与索引
B, N, D = x.shape
len_keep = int(N * (1 - mask_ratio))
noise = torch.rand(B, N)
ids_shuffle = noise.argsort(dim=1)
ids_keep = ids_shuffle[:, :len_keep]
x_kept = torch.gather(x, 1, ids_keep.unsqueeze(-1).expand(-1, -1, D))
return x_kept, ids_keep
tokens = torch.randn(2, 196, 768)
kept, idx = random_masking(tokens, 0.75)
print(kept.shape, idx.shape) # (2, 49, 768) (2, 49)
自监督预训练的价值在于:工业场景往往有海量无标注图像,用 MAE 或 DINOv2 预训练再加少量标注微调,效果常优于直接 ImageNet 迁移。
12. 视觉基础模型与提示工程
「视觉基础模型」指那些在超大规模数据上训练、可零样本或少量样本迁移到新任务的模型,代表有 CLIP、DINOv2、SAM。它们的工程用法是「组合」:
- CLIP 提供开放词汇的语义理解与图文对齐。
- DINOv2 提供通用的稠密视觉特征,适合检索与匹配。
- SAM 提供类别无关的精细分割。
提示工程对 CLIP 尤其重要。把类别名放进自然语言模板,比直接编码单词更准:
templates = [
"a photo of a {}",
"a picture of a {}",
"a close-up photo of a {}",
"a bad photo of a {}",
]
def build_prompts(class_names, templates):
# 每个类别生成多个 prompt,编码后取平均
prompts = []
for name in class_names:
prompts.append([t.format(name) for t in templates])
return prompts
print(build_prompts(["cat", "dog"], templates)[0])
prompt ensemble 通常能带来一两个点的零样本提升,代价是文本编码次数乘以模板数。工程上可预先离线编码所有类别的文本向量并缓存。
13. 多模态检索与向量数据库
CLIP 的另一大用途是跨模态检索:用文本搜图,或用图搜图。做法是把图像与文本都编码成同维度向量,再在向量库里做近邻搜索。
import numpy as np
def cosine_topk(query, gallery, k=3):
# query: (D,), gallery: (N, D),均已 L2 归一化
sims = gallery @ query
idx = np.argsort(-sims)[:k]
return idx, sims[idx]
D, N = 512, 100
gallery = np.random.rand(N, D).astype(np.float32)
gallery /= np.linalg.norm(gallery, axis=1, keepdims=True)
query = np.random.rand(D).astype(np.float32)
query /= np.linalg.norm(query)
idx, scores = cosine_topk(query, gallery, k=3)
print("top3:", idx, np.round(scores, 4))
规模小时用暴力检索即可;上百万级用 FAISS 建索引(IVF、HNSW)。大规模检索可用 FAISS 建索引(IVF、HNSW),与检索增强系统的向量检索高度相通。
14. 端侧与部署
ViT 的注意力在端侧部署上有两个难点:算子在部分芯片上支持不完整、高分辨率下注意力显存大。工程手段有:
- 用专为端侧设计的模型:MobileViT、EfficientFormer、MobileSAM,把注意力量化到 int8。
- 用窗口注意力或线性注意力降低复杂度,避免全图 O(N²)。
- 导出 ONNX 时确认注意力算子被正确转换,必要时改写为等价算子组合。
- 用 TensorRT 或 NCNN 做图优化与算子融合,实测端侧延迟。
| 模型 | 类型 | 参数量 | 端侧友好度 |
|---|---|---|---|
| MobileViT-S | 混合 | 约 5.6M | 高 |
| EfficientFormer-L | 纯 Transformer | 约 31M | 中 |
| MobileSAM | 分割 | 约 10M | 高 |
| ViT-B/16 | 纯 Transformer | 86M | 低 |
端侧部署的通用手段与 CNN 一致,量化、剪枝、蒸馏的细节见模型压缩与端侧部署。
15. 权衡取舍
- 数据需求:ViT 依赖大规模预训练,小数据集用 CNN 或 DeiT 更稳。
- 计算复杂度:自注意力 O(N²),高分辨率图像代价大,Swin 的窗口注意力缓解了这点。
- 部署生态:CNN 与 Swin 的算子支持更好,纯 ViT 的注意力算子在某些端侧芯片上支持有限。
- 零样本与微调:CLIP 零样本方便但精度有限,有标注数据时微调仍更优。
- 精度与显存:多模态大模型显存需求高,服务化要算好批大小。
- 预训练成本与收益:自监督预训练成本高,只有数据规模足够大时才划算。
- 窗口大小与感受野:Swin 窗口越小越快,但跨窗建模依赖移位,窗口过小损失全局信息。
- 零样本与开放词汇:CLIP 零样本省标注,但细粒度分类仍不如有监督微调。
16. 常见坑清单
- 位置编码尺寸不匹配:换输入分辨率时位置编码需要插值,直接加载会报错。
- 小数据集直接训 ViT:缺少归纳偏置,过拟合严重,先用预训练权重。
- 归一化参数不一致:ViT 与 CNN 的均值和方差常常不同,混用会掉点。
- patch size 与分辨率不匹配:patch 16 需要分辨率为 16 的倍数,否则要 padding。
- 注意力显存爆炸:高分辨率下注意力矩阵巨大,需用窗口注意力或梯度检查点。
- CLIP 类别名写得太随意:prompt 措辞影响精度,用 ensemble 提升稳定性。
- 忽略训练数据口径:拿 21K 预训练的 ViT 与 1K 训练的 CNN 比不公平。
- 盲目替换骨干:检测任务换 Swin 后 FPN 配置也要相应调整。
- 自监督预训练数据不匹配:用自然图像预训练的权重迁到医学图像,收益有限。
- 向量未归一化:检索时忘记 L2 归一化,余弦相似度退化成点积,排序错乱。
- 忽略文本编码缓存:每次请求都重编码类别文本,浪费算力。
- 随机深度设为 0:深层 ViT 不设随机深度容易过拟合。
- 学习率过大:ViT 对学习率敏感,过大会破坏预训练权重。
17. 小结
视觉 Transformer 的工程主线是:小数据用 CNN 或 DeiT,大数据用 ViT,检测分割用 Swin,开放词汇用 CLIP。理解四个关键机制:patch embedding 把图像变序列、位置编码补位置信息、窗口注意力降复杂度、对比学习连接图文。多模态大模型的结构可概括为「视觉编码器加投影层加大语言模型」,抓住这个骨架就能快速上手。选型时别忘了部署生态与训练数据口径这两个常被忽视的维度。
延伸阅读
- 卷积网络与经典骨干架构 — CNN 与 Transformer 的对照
- 图像分割与实例分割 — SAM 的 ViT 骨干
- 注意力机制与 FlashAttention 内核 — 注意力算子优化
- AI 计算机视觉专题 — 更广的视觉算法视角
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。