给模型一张菜单,问它「第二行的价格是多少」,模型需要先保留小字,再找到第二行,最后把数字读出来。如果预处理已经把字缩成模糊的一团,后面的语言模型再大,也无法从图像中找回被丢掉的笔画。视觉模型的设计,首先是在决定保留哪些信息、让哪些区域交换信息,以及把多少信息交给下游。
Vision Transformer(ViT)把图像切成 patch,用 Transformer 在这些 patch 之间交换信息。今天许多多模态模型仍沿用这个骨架,但在分辨率、位置编码、预训练和输出压缩上有不同选择。本文先从输入推导基本 ViT,再沿着这些选择理解实际模型。Attention 的更多变体见这一篇,视觉自监督目标见 SSL。
模型部分是 2026-09-20 核对的代表性公开模型快照,不试图列全所有发布。这里的「开源模型」包括公开权重模型,不意味着每个模型都公开了完整训练数据或采用同一种许可证。具体配置以文中指定的 checkpoint 为准。
怎样把一张图交给 Transformer
从像素到 patch token
Transformer 接受一串向量。最直接的办法是把每个像素当成一个 token,但一张
折中的办法是先把相邻像素组成小块。设 RGB 图像为
个 patch。每块展平后有
这里使用 row vector,
Patch embedding 不需要显式地切出一堆小图片:Conv2d(C, D, kernel_size=P, stride=P) 可以实现同一个线性操作,输出再从空间网格展平成序列。因而「ViT 不用卷积」通常指主体采用 Transformer,并不排斥卷积形式的输入投影。
更小的 patch 会产生更密的空间采样,但也会增加序列长度。较大的 patch 则让最早的投影一次处理更大的区域;这不等于对区域取平均,块内像素仍有各自的权重。不过,若
序列顺序并不会自动告诉模型二维位置
把 patch 按行展平之后,数组下标有了先后顺序,但没有位置输入的 self-attention 并不知道这个下标。若同时重排输入 token,输出也只会跟着重排。菜单上第二行和第三行即使有完全相同的价格,模型也需要知道哪个价格属于哪一行;只有内容向量还不够。
原始 ViT 给每个 token 加一个 learned absolute position embedding。若任务是分类,还在前面放一个可学习的 CLS token,让它通过 attention 汇总整张图:
CLS 是一个额外的读出位置,不对应某块像素。图像分类可以读它,也可以对 patch features 做 pooling;分割、定位或多模态理解则往往需要保留 patch 序列。ViT 不要求一定有 CLS,也不要求最后只输出一个向量。
当网格从
每个 patch 怎样借用其他区域的信息
菜单上的一个笔画可能属于数字 3,也可能属于 8;只看局部不够,还需要同一个字符附近的笔画和整行布局。Attention 让每个 token 根据当前内容选择需要读取的其他 token。
对 normalized features
Query 与每个 key 的内积衡量匹配程度,经 softmax 变成一组和为 1 的读取权重,再对 value 加权求和:
Softmax 沿每个 query 对应的 key 维度进行;
读到其他区域的信息后,还要在每个位置内组合特征。于是一个典型 pre-norm block 是
Attention 负责位置之间的信息交换,MLP 负责每个位置内部的通道变换,residual connection 保留原有表示。现代模型可能换成 RMSNorm、SwiGLU 等,但这个分工仍然有用。经过多层之后,一个 patch feature 已经包含上下文,不能再把它理解为原始小方块的独立描述。
现在可以把位置问题再往前推进一步。找菜单中某道菜的价格时,「同一行、右边」比「图像第几个格子」更容易迁移到另一张排版相似的菜单。我们希望整对 patch 同时平移后,二者的空间关系不变;还希望加入位置时不任意放大内容向量。旋转正好满足第二个要求:它改变方向,保持长度;若两个向量都额外旋转相同角度,内积也不变,因此第一项要求也有了实现途径。
具体地,把通道两两分组,让位置为
这就是 RoPE 把相对位移放进内容匹配的方式。2D RoPE 分别为行、列坐标分配通道组与旋转频率,使横向距离和纵向距离可区分。不同实现会选择不同的频率和坐标尺度;它解决的是空间关系的表示,超出训练尺度的泛化仍需验证。Absolute embedding 与 RoPE 也可以同时使用:前者给单个位置提供地址,后者直接影响两个位置之间的比较。
有了这些部件,如何让网络真的学会识别?原始分类 ViT 在最终 CLS 上接 classification head,用真实类别的 cross-entropy 训练,梯度经过各层一直回到 patch projection。局部笔画检测、跨区域读取和全局汇总是共同学出来的。与卷积逐层扩展局部感受野相比,full attention 从第一层就允许远处位置交换信息,但「应该优先利用邻近结构」也更多交给数据与训练来学习。菜单问答的目标更细,不能只靠一个整图类别;接下来既要保留更密的输入,又要设计能利用这些位置的输出。
分辨率为什么会变成设计的中心
假设 patch size 固定,把高宽都放大两倍,token 数会变成四倍。Full attention 的矩阵大小是
| 输入尺寸 | patch 网格 | patch tokens | 单个 head 的 attention 矩阵元素数 |
|---|---|---|---|
| 196 | 38,416 | ||
| 784 | 614,656 | ||
| 3,136 | 9,834,496 |
表中忽略 CLS 等额外 token。对固定宽度,attention 的两次核心矩阵乘法约为
要保留菜单的小字,又不能无限增加计算,有三种不同层次的调整。
先改变输入。 把整张图缩放到固定正方形最简单,却可能扭曲长宽比或丢掉小字。Dynamic resolution 根据原图比例和像素预算选择网格;「native resolution」在实际实现中通常仍包括预算限制、resize 和整除对齐,不等于始终逐像素使用原图。
再改变通信范围。 每个 token 只在含
还可以切成多个 crop。 对一张很长的菜单,把各段分别放大后送入已有的固定尺寸 encoder,就能在复用同一套权重的同时保住笔画。若每块有
窗口与 crop 不是同一个操作:窗口可以在同一个 encoder 的后续全局层互相通信;独立 crop 在进入融合模块之前则是不同的视觉序列。两者也都不能替代位置编码。
从 ViT 输出到 LLM 输入,还差什么
一个 encoder 输出
保留空间网格,合并相邻特征
若希望压缩之后仍能对应图像区域,最直接的办法是每次合并相邻
输入维度从
也可以直接对邻域取平均,再投影。这样同样减少位置数,但平均会先抹掉组内差异;concat 则允许后续层区分「左上角是什么」与「右下角是什么」。这是一种表达能力与计算量的取舍,并不意味着 concat 在所有任务上都更好。
让固定数量的 query 主动读取图像
另一种目标是:无论输入有多少 patch,都只交给 LLM
其中
无论使用 merger、pooling 还是 resampler,在 ViT 之后压缩,只节省压缩之后的计算。ViT 已经处理过的 patch 不会因此变少。做延迟分析时,需要分别记录 encoder 的 token 数与送入 LLM 的 token 数。
怎样从这些向量学会回答「18」
到这里,我们只是让图像变成 LLM 可以接收的一串向量,尚未教会 LLM 使用它们。对图中的菜单,可以构造一个训练样本:输入菜单和「第二行的价格是多少」,目标答案是「18」。令视觉序列为
这里
如果只更新 connector,梯度仍需穿过 LLM 才能告诉 connector 哪些视觉特征有用,但 ViT 和 LLM 的权重保持冻结;联合训练则可以继续调整它们。前一种方式先建立接口,后一种方式让视觉表示适应实际问答需求。这个 loss 只告诉网络最终答案是否正确,未显式标出「应该看哪一个 patch」;这也解释了为什么预训练特征、OCR 与定位数据会影响模型能否学到可靠的读取方式。
同样的 ViT,为什么学到的东西不同
Architecture 决定信息怎样流动,训练目标决定模型保留什么。假设一张菜单的配文只有「今日午餐」:模型认出这是一张菜单,就可能完成图文匹配,却没有理由精确区分价格 18 与 19。CLIP (Radford et al., 2021) 用匹配与不匹配的图文训练全局语义表征,适合检索;若下游要读小字或做分割,就还需要检查局部特征是否保留了足够的信息。
SigLIP 2 (Tschannen et al., 2025) 的图文 sigmoid loss 分别判断一对图文是否匹配;它进一步加入需要描述区域、预测位置的训练,让只知道整图主题不再足够。Self-distillation 让局部 view 学习全图 teacher 的语义,masked prediction 则要求被遮住位置的特征匹配 teacher 的对应位置,把训练压力落实到局部表示。这里的 decoder 用于预训练,不意味着发布的视觉 encoder 本身就是一个问答 LLM。
它也提供 NaFlex:训练时使用不同形状和长度的 patch 网格,让同一个 checkpoint 适应长宽比与分辨率变化。对长菜单,这意味着可以把预算分给有内容的长条区域,而不用先强行变成正方形。它与固定分辨率版本同时存在,下游模型也可能在初始化后重新训练自己的动态输入路径。
DINOv3 (Siméoni & others, 2025) 的主要视觉预训练不依赖图文配对,其 ViT 使用 RoPE 并保留 dense features。这里有一个容易忽略的矛盾:网络既需要整合全图信息,又希望每个 patch 的输出忠实对应局部。Register 的出发点是,一些背景 patch 会被网络借作存放全局信息的空间,形成影响 dense features 的异常值;增加不对应像素的额外 token,给这种计算提供专门位置 (Darcet et al., 2023)。读出空间特征时排除 registers 和 CLS,剩下的才对应图像网格。
Register 仍不能保证训练越久,局部关系越好。DINOv3 的 Gram anchoring 因而进一步约束「哪些 patch 彼此相似」:用较早、空间特征较好的 teacher 提供两两相似度,要求 student 保持这些关系。这样不必把每个特征向量锁死在 teacher 的数值上,仍允许整体表示继续变化。它保护的是局部结构,不能自动赋予「这个符号读作 18」的语言对应;接入 LLM 后还需要上一节的图文训练。更完整的训练目标见 SSL 的 DINO 系列部分。
近期公开模型怎样组合这些部件
前面得到的部件并不只有一种组合方式。继续用读菜单这个任务看:有的模型优先降低保留小字时的 encoder 成本,有的优先降低交给 LLM 的长度,有的则改善视觉信息进入语言计算的位置。
Qwen:动态网格、窗口与多层注入
Qwen2.5-VL (Bai & others, 2025) 让输入高宽对齐到 28 的倍数:先用
即使已经控制了 encoder 成本,还可能遇到另一个瓶颈:若只拿最终层特征接入 LLM,所有有用信息都必须经过同一个出口。识别「这是一张菜单」与分辨价格的一笔,可能需要不同层次的表示。最直接的补充办法是把中间层特征也接到输入序列上,但这样会增加 LLM 的上下文长度。
Qwen3-VL (Qwen Team, 2025) 采用另一种补充途径:各层的特征仍对应同一批空间位置,先用各自 merger 对齐数量和维度,再加到 LLM 早期层的对应视觉位置。这就是它的 DeepStack。每次注入都更新现有位置上的向量,不增加位置数;额外代价来自中间特征、mergers 和加法。这种设计给不同深度的特征提供直接路径,并不保证某一层专门负责 OCR、另一层专门负责语义。
该模型的 encoder 使用 SigLIP 2 初始化,并结合插值 absolute position embedding 与 2D RoPE。其 DeepStack 选择三个视觉深度,注入 LLM 前三层;这与原始 DeepStack 使用多尺度输入的方式也有区别。
官方 Qwen3-VL-8B-Instruct 配置 (Qwen Team, n.d.) 的视觉深度为 27,宽度为 1152,patch size 为 16,DeepStack 索引为 [8, 16, 24]。Qwen3.5-9B (Qwen Team, n.d.a) 具有相同的这些基本视觉尺寸,但 deepstack_visual_indexes 为 [],未配置这三路注入。相同的张量尺寸可以兼容不同的数据流;权重来源与训练流程则需要另外核对。
还要区分两处位置编码:ViT 内部的 2D RoPE 描述 patch 间的空间关系;LLM 侧的 MRoPE 则组织文字、图像和视频位置。不能把语言侧的时间编码当成视觉 encoder 已经进行了完整的跨帧 attention。
InternVL:把高分辨率拆成可复用的 tiles
InternVL3.5 (OpenGVLab, n.d.) 延续 InternViT–MLP–LLM 路线,通过动态数量的 tiles 处理高分辨率图像。一个
但各个 tile 未必需要同样的预算:菜单中的空白区域与密集价格表,包含的信息量不同。Flash 变体增加 Visual Resolution Router,为每个 tile 在 256 与 64 个输出 token 之间选择,把固定压缩率改成按内容分配。若四个 tiles 中两个走 256、两个走 64,它们贡献
MiniCPM-V:用 query 同时压缩空间与时间
如果输入换成摄像头拍摄菜单的视频,相邻帧可能几乎一样。逐帧独立压缩仍会重复占用 token,但先平均像素又可能把镜头运动造成的文字位移混在一起。一种办法是先保留各帧的视觉特征和时间位置,再让 query 联合读取,以学习哪些内容重复、哪些变化需要保留。
MiniCPM-V 4.5 的配置 (OpenBMB, n.d.) 给出 SigLIP 分支、patch size 14 和 query_num=64;它的 3D-Resampler 正是把时间坐标纳入读取。官方例子将六个
按原始 patch 计数,每帧有
OneVision-Encoder:在进入 ViT 前选择变化
3D-Resampler 已经缩短了送入 LLM 的序列,但它读取的是 ViT 处理后的特征:若 64 帧大多重复,encoder 仍先计算每一帧的全部 patch。能否把预算前移,让 ViT 本身不必反复编码没有变化的背景?OneVision-Encoder(简称 OV-Encoder,与 LLaVA-OneVision 不是同一个模型)把视频编码器已有的信息变成了一个选择器 (Tang et al., 2026)。
HEVC 把一段视频组织成 Group of Pictures(GOP):I-frame 独立保存完整画面,P-frame 则利用参考帧、motion vector 和 prediction residual 表示变化。OV-Encoder 对 I-frame 保留全部空间 patch;对每个 P-frame,把 motion vector 的幅值与亮度 residual 的能量聚合到 ViT patch 网格,再在整段视频的固定预算内优先选择变化显著的位置。这里 motion vector 和 residual 只负责决定选哪里;真正输入 ViT 的仍是解码后的 RGB patch,而不是压缩码流中的运动向量或残差。
以论文的默认视频设置为例,64 帧、GOP 长度 32 会产生两个 I-frame。若每帧原本有 256 个 patch,dense 编码需要
个 patch;总预算
这是 ViT 输入 patch 数的减少,不代表端到端延迟恰好提升八倍;视频解码、筛选、attention 之外的层和后续模型都仍有成本。
筛选也带来新的位置问题。某个 P-frame 可能只保留原网格的第 3、8、14 块,压紧后的序列下标却是连续的;若按下标做位置编码,模型会误以为这些 patch 在时空中相邻。OV-Encoder 因而让每个 token 携带原始
同一个 ViT 因此可以接受三种序列:图像保留完整空间网格;普通视频可按时间 chunk 采一帧并保留整帧 patch;codec 路径则保留 dense 时间采样、稀疏选择每个 P-frame 的空间 patch。后者并非总是占优:它依赖可取得 motion vector 与 residual 的 HEVC 预处理,也假设「编码残差大」通常能代理「语义上值得看」。I-frame 为静态区域提供底图,但细微且低残差的事件仍可能被固定预算漏掉。
最后还要回答这些 sparse patches 应该学到什么。OV-Encoder 先用冻结视觉教师离线聚类,得到图像的 object-level centers 与视频的 motion-level centers;每个样本可以对应多个正中心,再用 multi-label sigmoid loss 训练 pooled visual embedding。第二阶段提高图像分辨率、加入视频与 OCR 标签,使共享 backbone 同时学习物体、动作和文字线索。公开的 Large checkpoint 是约 0.3B 参数、
Gemma:从固定视图到显式 token budget
Gemma 3 的多模态版本 (Gemma Team, 2025) 用 SigLIP 处理
还有一种选择是把预算交给调用者:只问「这是不是菜单」时,可以接受较少位置;要求逐行抄出小字时,则保留更密的空间采样。Gemma 4 的官方 model card (Google DeepMind, n.d.) 提供 70、140、280、560、1120 等视觉 token budget,并支持可变长宽比。以 31B checkpoint 为例 (Google DeepMind, n.d.b),patch size 为 16、pooling kernel 为 3、默认输出长度配置为 280。Processor 同时满足空间整除与预算限制,实际非 padding 长度还取决于输入形状。
同一家族也有另一条路径:Gemma 4 12B Unified 的官方说明明确采用 encoder-free 结构,把图像 patch 通过轻量投影直接交给共享模型。它把视觉建模放入共享网络,省去独立 ViT;视觉输入的表示与计算预算仍然需要设计。
回到整条数据流,可以把上述模型放在同一张表里。表中的 token 数按指定输入单元计数,不是相同画质下的性能比较。
| 模型/版本 | 视觉输入与 encoder | 交给下游的方式 | 主要设计取舍 |
|---|---|---|---|
| SigLIP 2 | 固定分辨率或 NaFlex ViT | pooled embedding 或 patch features;不自带 LLM | 图文语义与局部特征共同训练 |
| DINOv3 ViT | RoPE、CLS 与 registers | 全局和 dense features;不自带 LLM | 视觉表征与空间一致性 |
| Qwen2.5-VL | 每 | 降低高分辨率 encoder 成本 | |
| Qwen3-VL-8B | 为中间视觉特征提供直接路径 | ||
| Qwen3.5-9B | 动态空间网格与下游压缩 | ||
| InternVL3.5 / Flash | InternViT,动态数量的 | 标准每 tile 256 tokens;Flash 可路由为 64 或 256 | 按 tile 内容分配下游预算 |
| MiniCPM-V 4.5 | SigLIP, | 64-query 3D-Resampler | 联合压缩帧间重复与空间信息 |
| OneVision-Encoder-Large | 带 | 借助 codec 信号在 ViT 之前分配视频 token 预算 | |
| Gemma 3 多模态版本 | SigLIP, | 每视图 256 tokens,可增加 crops | 固定视图预算与局部细节 |
| Gemma 4-31B | 根据任务选择保留多少细节 |
把一张图的 token 账算清楚
考虑预处理后恰好为
Qwen2.5-VL 的
这些数字只计视觉特征位置,未包括边界标记、文字问题或视频时间戳。两者的 temporal patch size 都涉及帧分组;静态图像通过各自 processor 的图像路径处理,不能因为配置里有 2 就把上述静态图像 token 数再除以 2。
如果采用一个
这条链也能帮助定位失败。小字在 resize 时消失,应该先检查输入预算;ViT 很慢,应该检查 patch 数和 attention 范围;LLM prefill 或上下文压力大,应该检查 merger 后的序列;局部特征已经存在却没有被答案使用,再检查 connector、注入方式与多模态训练。仅增加语言模型参数,不能自动解决所有这些问题。
怎样读一个新模型的视觉实现
先看 processor 实际输出的尺寸、crop 数、padding mask 和网格坐标,再沿 patch embedding 找到 encoder 的真实输入长度。对视频,还要确认抽帧或 patch 筛选发生在 ViT 之前,还是只在 ViT 之后压缩输出。随后检查位置编码、window/full attention 的分布,以及是否有 CLS 或 registers。最后追踪 connector 改变的是通道、位置数量还是注入层数,并核对送入 LLM 的实际张量。
模型名称和 image_size 默认值只能提供线索,不能代替这条数据流。例如,vision_config 中的默认尺寸可能用于初始化位置表,而 processor 仍支持动态尺寸;语言模型的 MoE、linear attention 或 sliding attention 也不能直接推断到视觉分支上。
读 ViT 时最有用的四个问题始终是:模型看到了什么分辨率,哪些位置能交换信息,训练让它保留什么,最终下游收到多少特征? 原始 ViT 给出一个简单骨架,而近期模型的不同设计,正是在这四个问题上作出不同回答。