1. ViT的设计原点:一次“违背直觉”的尝试
2020年之前,你要是跟一个搞计算机视觉的人说,图像识别可以完全不用卷积神经网络,只用Transformer那套自注意力机制,他大概率会觉得你在开玩笑。因为在当时,卷积网络统治视觉领域已经快十年了,从AlexNet到ResNet,再到EfficientNet,每一代经典模型都在用局部感受野、权重共享、平移等变性这些“归纳偏置”来处理图像。归纳偏置是什么意思?它就是你在让模型学东西之前,先人为告诉它一些先验规则——比如“图像里的特征跟位置有关,相邻的像素大概率关系更紧密”,卷积核就是这么设计的,用一个小的滑动窗口扫过整张图,天然就把局部性刻进了骨子里。
而Vision Transformer(简称ViT)做的事情,恰恰是反着来的。它把一张图像切成一个一个的小方块,我们叫它Patch,然后把每个Patch当作一个“词”送进标准的Transformer编码器里。这里没有任何滑动窗口,没有局部感受野,没有卷积操作,纯粹靠全局自注意力去挖掘图像里每个区域之间的关系。第一次看到这个方案的时候,我的第一反应是和不少人一样:这能好吗?一张图片可能有几百个Patch,每个Patch都要跟所有其他Patch计算关联,计算量先不说,关键是它真的能学到有意义的视觉特征吗?
但结果大家也知道了。ViT在大规模数据集上做预训练之后,跑到ImageNet分类任务上,准确率第一次超过了同时代的卷积网络。这件事的意义不在某个数字上,而是它证明了一个更根本的问题:卷积网络那些“祖传”的归纳偏置,或许并不是视觉理解的必需品,至少在数据量足够大的时候,模型完全可以从零开始,自己学出比人工先验更有效的模式。
这篇博文想聊的就是ViT从诞生到现在,这将近十年时间里的演进脉络。我会从它的核心设计思路讲起,说说它后续在效率、结构、训练范式上走过的几条关键路线,再结合我自己实际训练和部署ViT模型踩过的坑,给你一份真正能用的经验总结。内容偏向工程实践和认知复盘,适合已经在用深度学习方法做视觉任务、或者对Transformer架构有基本了解的朋友。
1.1 从“图像当作句子”这个隐喻说起
要理解ViT,先要理解它最重要的一步操作:Patch Embedding。假设输入是一张224×224的RGB图像,ViT先把它切成16×16大小的Patch,那就会得到(224/16)² = 196个Patch。每个Patch展平之后是一个16×16×3 = 768维的向量,然后通过一个可学习的线性投影,把维度映射到Transformer的隐藏层维度D,这个过程就相当于给每个Patch做了一个“词嵌入”。再叠加一个位置编码(Position Embedding),因为Transformer本身没有顺序概念,必须把Patch的位置信息显式加进去,这样每个Patch就带上了它在图像中的坐标语义。
比较经典的实现里,位置编码通常会用一个可学习的1×196的向量矩阵,训练过程中自己更新。也有研究者后来尝试用2D形式的相对位置编码,效果在某些任务上会更好。这一步的直观理解是:你把一张照片拆成196块拼图,每块拼图上标注“它在整张图的哪个位置”,然后一股脑丢给一个擅长处理序列的模型。图像信息就这样被彻底序列化了,原本二维的空间结构被压缩成一维的顺序,模型需要靠注意力来重建“哪块和哪块属于同一个物体”这种空间关系。
这里有个经常被忽略的细节:为什么是16×16,而不是8×8或者32×32?Patch的大小其实是一个权衡。Patch越小,序列长度越长,注意力计算量指数上升,因为自注意力的复杂度是O(n²),n是序列长度。Patch越大,序列越短,计算量小了,但每个Patch内的空间细节会在展平之后丢失掉,毕竟一个32×32的Patch里,不同物体边缘的细粒度信息被粗暴压缩成了一个向量。ViT原文对多个Patch大小做过对比,最终16×16被选为一个在计算量和性能之间比较均衡的折中方案。后来一些轻量级模型,比如MobileViT,也会用更小的3×3或者4×4 Patch配合卷积来做局部建模,那是另一套思路了,后面讲到演进的时候会再展开。
1.2 全局注意力带来的范式变化
ViT真正区别于CNN的地方,是它利用自注意力机制构建了全局上下文关联。卷积操作的感受野是局部且有限的,想看到更大的范围,只能靠加深网络层数,让高层特征慢慢“看到”更大视野,这种方式是隐式的、渐进的。而Transformer在第一层就直接计算所有Patch之间的两两关系,每个位置从一开始就能获得全图的全局信息,这种能力是显式的、一步到位的。
听起来很美好,但代价也很明显。标准Transformer编码器里每一层的自注意力,计算量和内存占用都是序列长度的平方。对一张224×224图像而言,196个Patch还算能接受,可一旦换成高分辨率输入,比如384×384,Patch数量就变成576个,计算量直接涨了近9倍。再往后如果要做目标检测、语义分割这类密集预测任务,经常需要输入分辨率达到1024×1024甚至更高,序列长度会膨胀到几千,纯ViT结构在这种场景下几乎不可用。
正因如此,ViT早期被很多人诟病“训练不稳定、需要超大规模数据、推理速度慢”。这个批评有一定道理,但也促成了后续一系列重要的演进方向:要么想办法降低注意力的计算复杂度,比如窗口注意力、稀疏注意力;要么从结构上做改变,比如引入多尺度金字塔设计;要么换个训练范式,比如用自监督预训练来缓解对大数据集的依赖。这些也都是我这篇博文后面要重点展开的内容。
2. 演进的第一条线:数据效率问题
ViT最初的效果其实非常依赖预训练数据规模。论文里直接使用了包含数亿张图片的私有数据集做预训练,才能让ViT超过CNN。这在我们普通工程师的日常项目里几乎不现实,谁会为了一个分类任务去准备几亿张图?于是“怎么用更少的数据把ViT训好”就成了最紧迫的问题,这个方向也催生了几个关键进展。
第一个绕不开的工作是DeiT(Data-efficient Image Transformers)。它提出了一套实用训练策略,核心手段是知识蒸馏。具体做法是,在训练过程中引入一个已经训练好的卷积网络作为教师模型,让学生模型——也就是ViT——去模仿教师的输出分布,而不仅仅是学习硬标签。简单说,硬标签只能告诉模型“这张图是猫”,而教师模型的软输出会额外暗示“这张图有75%像猫,20%像狗,5%像狐狸”,这种具有模糊性的信息对提升学生模型泛化能力帮助很大。
在DeiT的实验中,他们发现用了蒸馏之后,ViT在ImageNet上只需要约120万张训练图就能达到相当不错的性能。这个量级对很多团队來說已经是可以承受的了。我当时照着DeiT的训练配置复现过一遍,最有感触的一个细节是:整组训练策略里最重要的一环并不是模型结构本身,而是数据增强。DeiT把当时图像分类领域里一堆有效增强手段——RandAugment、Mixup、CutMix、随机擦除——全部叠加了进去。如果训练ViT的时候只带基础增强,性能下滑非常明显。我后来在生产环境里做一个小规模图像分类模型,就是直接沿用了这套训练配方,效果比我自己组合的增强方案稳定很多,准确率上有三四个点的差距,这在很多业务里是决定性的差异了。
第二条线是Swin Transformer。前面提到纯ViT的序列长度问题,Swin给出的解法是引入滑动窗口注意力(Shifted Window Attention)。它先把图像分成长方形网格,每个网格内部的Patch只在本地做自注意力,这样就相当于把全局注意力限定到局部窗口范围内。然后,每隔一层Transformer,窗口位置会偏移一半(Shift),这样不同层的窗口互相错开,信息可以在层与层之间传递,弥补局部窗口带来的感受野受限问题。
更重要的是,Swin引入了类似CNN金字塔的结构:不同阶段输出不同分辨率的特征图,从高分辨率到低分辨率逐层下采样。这意味着Swin可以直接套用到检测、分割等需要多尺度特征的任务上,比如检测框架里的特征金字塔网络(FPN)在Swin上工作得很顺利。ViT虽然全局建模能力更强,但因为它输出的是单一分辨率的序列,迁移到密集预测任务时就特别别扭,往往得额外设计一个专门的特征提取头才行。
从演进逻辑看,Swin不是推翻ViT,而是用工程化的方式把ViT的核心优势——全局建模能力——保留下来,同时通过结构设计绕开计算瓶颈。这个思路后来延伸出了很多变体,比如PVT、Twins、Focal Transformer等等,大家本质上都在做同一件事:如何在保持Transformer优点的同时,让它对多尺度特征和密集预测更友好。
2.1 自监督预训练带来了什么改变
数据效率问题还有一个更大的破局点,那就是自监督预训练。2021年末到2022年初,MAE(Masked Autoencoder)的出现让视觉Transformer的训练方式彻底改变了格局。MAE的思路是:把输入图像的75% Patch全部遮盖掉,只留25%的Patch喂给ViT编码器,然后在解码器端重建被遮住的像素。模型要完成这个任务,就必须真正理解被遮挡区域的语义内容,而不是仅仅做表面纹理匹配。
这个玩法很像自然语言处理里BERT的掩码语言建模,但它更激进——遮盖比例高达75%。为什么可以遮这么多?因为图像本身存在大量空间冗余,不像语言每个词都承载着稠密的语义。相邻Patch之间的内容相关性非常强,如果不遮得多一些,模型只需要“抄”邻近可见Patch的内容就能轻松完成重建,根本学不到高层次特征。MAE最让我惊艳的地方在于:它让ViT的预训练不再依赖人工标注,一个视觉模型在海量无标注图片上先学一遍通用表征,然后下游任务只需要少量微调,效果就能和从头训练甚至超过。
这个范式对实际工程影响非常大。之前训练一个大模型做垂直场景任务,你首先得找标注团队花几个月标数据,然后模型训练出来可能因为数据量不够还是欠拟合。有了自监督预训练,直接用无标注的行业图片做MAE预训练,把通用视觉表征学好,再用仅有的少量标注数据做下游微调,整个流程又快又省人力。我见过好几个垂直领域的项目,比如工业质检里只带了几千张缺陷图的场景,之前用CNN做分类总是过拟合,换用MAE预训练的ViT骨干之后,泛化能力一下提升了不少。
3. 演进的第二条线:结构设计的进化
如果说数据效率是让ViT“活下来”的关键,那么结构设计上的持续进化,就是让它从能用到好用的过程。这里我梳理出了几个我认为对ViT演进非常重要的结构分支,每个分支都在解决不同的问题。
3.1 金字塔多尺度结构
前面提到的Swin是金字塔结构的一种实现,但它的核心亮点其实是窗口注意力代替全局注意力来降低复杂度。在Swin之前,PVT(Pyramid Vision Transformer)提出了一种更“CNN化”的Transformer结构:它把编码器分成多个阶段,每个阶段内做全局自注意力,但阶段之间做空间下采样,同时通道数递增。这样从第一阶段到第四阶段,特征图尺寸从原始的H/4逐渐变成H/32,和ResNet系列骨干的层级结构完全对应。
这种结构的最大价值在于兼容性。目标检测和语义分割的框架里,几乎都会用到从骨干网络提取多尺度特征的过程,像特征金字塔、PANet这些模块的设计就是假设输入是多尺度的。PVT和Swin因为输出特征图本身就是多尺度的,所以能无缝替换掉原来的CNN骨干,不需要改动检测或分割框架的任何模块。而单纯的ViT只能输出H/16大小的单一特征,想要用它做检测,还得专门设计一套极其繁琐的适配流程。
我自己的实践感受是,如果项目需要做检测或者分割,且希望用Transformer架构,尽量直接选金字塔设计的变体,不要拿着原始的ViT硬上。你把时间花在解决ViT的序列长度适配问题上,远不如选一个本来就设计好的金字塔结构来得高效。
3.2 混合架构与局部建模
另一个非常值得关注的方向是CNN与Transformer的混合架构。代表性的工作包括 MobileViT、LocalViT、以及后来的下一代架构如 ConvNeXt 和 FastViT。这类工作的核心动机是:Transformer虽然全局建模强,但缺少CNN天然具备的局部归纳偏置,比如边缘特征、纹理特征的提取;而局部细节对视觉任务来说往往很重要,不能完全依赖全局注意力。
我举个例子,MobileViT的设计非常巧妙。它把图像按通道维度分成几个小组,每个组内先用一个3×3卷积提取局部空间特征,然后把每个空间位置看作一个序列,再做一次全局注意力。这样既保留了卷积网络的局部建模能力和计算效率,又引入了全局上下文信息,整个模型的参数量小、推理速度快,非常适合移动端部署。我在一个手机端图像分类项目里试过MobileViT,模型体量只有三四百万参数,但准确率比同体量CNN高好几个点,推理时间也没有明显增加。
沿着混合架构的方向,2022年之后出现了一类很有意思的“ConvNeXt化”趋势,就是把CNN结构按照Transformer的设计哲学重新审视和改造。比如ConvNeXt模型本质上是一个纯卷积网络,但它的设计规格从ResNet逐步向Swin Transformer靠拢——更大的卷积核、更少的激活函数、更深的倒瓶颈结构——最终在ImageNet上实现了和Swin相当甚至更好的效果。这其实是对“Transformer一定比CNN好”这个判断的有力反思:很多时候出色的性能并不来自特定的某个算子,而是更大规模计算配合更优结构设计带来的整体效果。
3.3 注意力计算效率
当模型规模越来越大,处理图像分辨率越来越高时,自注意力的O(n²)复杂度就成了绕不开的瓶颈。怎么解决?各路研究者做了很多尝试,总结下来大致有这么几条路线。
一是稀疏注意力。既然每个Patch没必要跟所有Patch都做全连接计算,那我人为选择一部分有意义的关联来计算就行。Swin的窗口注意力就是一种稀疏化,它的计算被限制在固定的局部窗口内。长距离依赖则通过窗口偏移来间接实现。另一种实现是轴向注意力(Axial Attention),分别对行和列做注意力计算,复杂度从O(n²)降到了O(n√n),虽然降低了精度上限,但在高分辨率场景下性价比非常高。
二是低秩近似。Linear Transformer、Performer这类工作通过核技巧或者随机特征映射,把注意力计算近似成一个低秩矩阵乘法,复杂度降低到线性。听起来很美好,但实际工程中往往需要配合更复杂的数值处理,如果实现不好,精度损失会比较明显。我在一些实际任务上测过Performer,大模型上性能退化在可接受范围,小模型上则非常明显,这就限制了它的工程普及度。
三是减少序列长度。这在多模态模型里很常见。图像经过一个预训练ViT提取特征后,通常会产生大量Patch Token,为了跟文本序列拼接到一起时节省计算量,研究者会设计Token Merging或Token Pruning的方法,把冗余的Patch合并或者直接剪掉。比如有个实现叫ToMe(Token Merging),它通过计算每对Token之间的相似度,每层合并掉最相似的那几对,可以在几乎不损失精度的前提下把序列长度压缩约50%。我试过一次,推理速度提升明显,尤其在高分辨率输入、批量处理场景下效果更好。
4. 演进的主战场:多模态与统一建模
要说最近这两年ViT最活跃的应用方向,必须提多模态。从2021年CLIP开始,视觉Transformer已经不再局限于图像分类这个单一任务,而是和文本模型一起,构成图文理解乃至视频理解系统的骨干。CLIP的核心思想是用对比学习把图片和文本映射到同一个向量空间:正样本对(图片和其匹配的描述)之间的距离拉近,负样本对之间的距离推远。训练完成之后,你不需要针对具体任务重新训练,只需要给模型一段文字描述,比如“一只在草地上奔跑的柴犬”,模型就能直接检索出最匹配的图片。这种Zero-shot能力在多模态检索和内容理解场景里非常受欢迎。
CLIP用的视觉编码器就是ViT,这背后有个非常特别的设计考虑:为什么选ViT而不是CNN?因为CLIP需要视觉编码器的输出Token与文本编码器的Token做联合建模。文本Token天生是“序列化”的,如果视觉编码器能输出同样格式的Patch Token序列,那两个模态在特征融合时就能站在一个更统一的接口上进行交互。后续像Flamingo、LLaVA等视觉语言模型,更进一步让语言模型在推理时动态地读取ViT输出Token,把视觉信息直接当作“外部记忆”提供给文本生成过程。
这种做法带来了一个完全不同的训练节奏。传统视觉模型需要先预训练、再微调、然后面对每一个新任务还要重新出数据。多模态时代,这个大模型可以通过一个统一接口去理解视觉世界和语言的边界,任务的迁移不需要再为每个下游任务设计单独的模型头。我当时第一次用CLIP做商品图文匹配时,内心是有一点震撼的:你只需要把图文对整理成统一格式,丢给对比学习训练一下,检索准确率比以往辛辛苦苦设计的多模态融合模型还高,而且训练过程不需要任何人工标注。
4.1 视频理解与ViT的时空扩展
除了静态图像,ViT在视频领域也有重要扩展。视频本质上是一个有很多帧的图像序列,把ViT从空间维度扩展到时空维度,可以通过在输入阶段就把多个连续帧Patch拼接到一起,把时间维度和空间维度统一序列化;或者直接替换注意力机制,让自注意力能同时覆盖空间和时间范围,这就是Video Vision Transformer系列的工作方向。
空间上,每一帧被切成Patch;时间上,不同帧的相同空间位置Token会被打包到同一时间注意力窗口中。经过多层的空间+时间注意力交叉之后,模型能够同时识别物体的外观变化和运动轨迹。用ViT做视频理解的明显优势是,它不依赖预设的光流等手工运动特征,能从数据中直接学出“运动意味着什么”。我在一个用户行为视频分类项目里实验过,直接把预训练好的基于图像的ViT权重复用到视频模型中做初始化,再用小规模视频数据微调,效果远超从零训练的CNN视频模型,而且收敛得特别快。这说明ViT在时空联合建模上的学习效率确实高,只要初始化得当。
4.2 长尾与细粒度场景中的挑战
当然,ViT的演进并不只有高光时刻。在真实场景里,长尾分布问题和细粒度分类问题至今依然是ViT的一大软肋。所谓长尾分布,就是数据集中绝大多数类别拥有大量样本,而尾部类别只拥有极少样本,这在真实业务中几乎随处可见。ViT因为依赖全局注意力,模型对数据分布的敏感度比CNN更高,在数据不均匀时,尾部类别的性能会掉得很厉害。细粒度分类则与之相反,类别之间的差异非常细微,比如不同品种的鸟类、不同缺陷类型的工业表面缺陷,这种任务要求模型对微小局部差异极其敏感,而ViT的Patch化输入和全局注意力并不天然擅长这种高精度局部特征建模。
针对这些缺陷,工业界和学术界想了不少办法。解决长尾问题比较有效的思路是在训练时使用重采样或者重加权策略,同时利用预训练模型在大数据上学到的通用特征做迁移。另一个思路是把ViT和CNN做局部特征融合,用CNN在细粒度图像上提取局部细节,用ViT做全局上下文语义补充。这种做法在医学影像和工业质检的场景里特别实用。我自己在表面缺陷检测项目里的体会是,把Swin骨干和ResNet骨干做双分支融合,会比单纯用任何一种主干网络在细粒度缺陷样本上的表现都更稳定。
5. 十年视野下的路线回顾与技术选型建议
回顾从ViT提出至今的演进路径,我会把它分成三个阶段。第一阶段,大约是2020年前后,是“证明阶段”,大家集中精力证明“纯注意力架构可以用于视觉并且能达到业界顶级水平”。第二阶段,2021到2022年,是“工程化阶段”,焦点转向效率和数据,DeiT、Swin、MAE等一系列工作让它从论文走向工程可用。第三阶段,2023年之后,是“泛化阶段”,ViT不再只是图像模型,而是成为多模态乃至通用人工智能模型的核心视觉骨干,同时它也在与CNN、状态空间模型等其他架构持续融合。
走到现在,如果你让我给一个技术选型的建议,我不会像一些极端观点那样说“ViT已经全面取代CNN了”。真实情况是,二者各有各的适用场景。分类任务上,如果数据量有限、算力有限,CNN仍然是可靠的起点。如果数据规模很大、或者任务本身涉及大量长距离依赖的建模——比如场景理解、跨模态检索、高分辨率图像理解——ViT及它的各种变体则更有优势。如果做视频理解或多模态任务,ViT几乎是你绕不开的选择。
5.1 训练一个ViT模型的实操要点
如果你决定尝试用ViT做一个新项目,我这里有几条实践经验可以分享。
第一条,不要从零训练。除非你有千万级别的标注数据,否则几乎注定欠拟合。一定要用在大规模数据上预训练好的权重作为初始化,这个步骤能给你的模型带来10个点以上的跨越。行业内许多开源仓库都提供了在ImageNet-21K甚至更大规模数据上训练好的模型权重,直接拿来做下游任务的骨干就行。
第二条,一定要重视优化器设置。ViT对学习率的敏感性比CNN高得多。AdamW加上cosine学习率衰减是比较稳妥的通用配方。初始学习率通常在1e-4到5e-4之间,具体看你批量大小。Batch size翻倍时,学习率可以按比例调大,但增幅不要超过2倍。此外,warmup阶段必不可少,根据模型规模从500步到5000步不等,用来稳定训练初期避免损失震荡。
第三条,数据增强要认真对待。这一点前面提到过,再强调一次:Mixup、CutMix、RandAugment这些手段对ViT的提升效果通常非常显著。我之前试过一组对照实验,同样的模型和数据集,带全套增强比只带基础裁剪翻转的版本精度高出接近5个百分点,而且泛化能力明显更好。
5.2 推理部署中的工程经验
模型训练好了,部署阶段又是一个新战场。ViT模型在GPU上推理时并行度高,但在CPU或者边缘设备上就比较吃亏,因为它缺乏CNN那种天然的局部计算结构,推理库很难针对自注意力做太激进的重排优化。
如果要在边缘设备上跑ViT,我试过两条比较有效的路径。一是使用支持注意力的推理框架和编译器,比如一些专门优化Transformer推理的运行时,通过算子融合、内存复用等手段把性能拉上去。二是用ONNX导出配合量化感知训练。ViT对INT8量化的敏感度比CNN更高,盲目的后训练量化经常会有明显性能下降,所以必须做量化感知训练,让模型在训练阶段就适应低精度计算。我在一个移动端分类项目中,通过量化感知训练加注意力融合优化,最终在设备上跑到了接近实时帧率,效果和浮点模型相比只掉了一个点以内。
如果推理场景对延迟极其敏感,还有一个思路是直接选择轻量级架构,比如MobileViT或者Mobile-Former,它们在设计阶段就考虑了部署效率,比直接压缩一个标准ViT要省事得多。
5.3 下一个方向的个人判断
到现在这个阶段,我认为ViT的演进远未结束,但它的形态会和前几年大不一样。一个明显的趋势是架构融合:CNN擅长局部高频细节提取,ViT擅长建模全局低频依赖,两者优势互补,未来必然有更多统一架构出现。另一个趋势是蒸馏与自监督训练的普及,让大规模预训练的成本不断降低,垂直领域的小团队也能训练出自己的“通用视觉模型”。还有一个方向是高效注意力机制的持续精进,配合更强大的硬件,我相信高分辨率实时视觉理解会在不久将来走入更多生产系统。
我在实际项目中反复验证过的一件事是:不要迷信任何一种架构,选型永远要跟着任务走。ViT很强,但它的强项需要数据和算力支撑;CNN很稳,但在数据量大到一定程度时上限确实不如Transformer。把每种架构的局限和优势都摸清楚,你才算真正理解了过去十年视觉模型演进的精髓。