一、先把整条数据流摊开看

大多数人第一次接触视觉语言模型,会把它想成一个能看图的对话模型。这个印象有用,也有害。有用在于对外接口确实一样,有害在于它掩盖了整条链路里最容易出问题的三个位置:图像怎么变成 token、token 怎么被语言模型当成词、以及训练时到底哪部分参数在动。本文就按这条链路的顺序走,每一节给出一个明确的工程判断,而不是罗列名词。

一次完整推理大致是这样串起来的:图像像素先经过一个视觉编码器,得到一组向量;这组向量经过投影层(也常被叫作 projector、connector、resampler)被映射到语言模型的词嵌入空间;接着这些向量被当作视觉 token,和文本 token 拼在同一条序列里,送进自回归解码器;解码器像对待普通词一样逐 token 生成回答。整个过程中没有所谓的图片分支和语言分支切换,只有一次从视觉坐标系到语言坐标系的搬运。

这条链路里真正需要做设计决策的,是三个接口。第一个接口是视觉编码器输出什么形状的向量:分辨率、patch 大小、是否保留层级特征、视觉塔本身有没有做过图文对比学习预训练。第二个接口是投影层保留多少 token,也就是信息瓶颈开多大。第三个接口是训练时谁在动,这直接决定了模型能学到什么、又会丢掉什么。

要注意的是,这三个接口并不是各自独立可调的。一旦把输入分辨率从 224 提到 672,token 数会接近九倍增长,投影层的计算量和激活值显存跟着涨,训练时的有效序列长度也涨。这时候往往需要同步改投影层结构和训练配比,否则很容易出现训练 loss 完全正常、推理时一句话插进几千个视觉 token 直接超长或显存爆掉的落差。

另一个常见误解是把视觉编码器当成纯粹的特征提取器,好像它只负责吐向量。实际情况是:如果视觉塔本身没有做过图文对齐预训练,它输出的空间是纯视觉的,它的语义轴和语言模型的语义轴几乎互不相干。此时投影层即使做得很大,也只能做有限搬运而不是翻译,它变不出训练数据里没有的对应关系。这是理解后面所有训练阶段的前提。

本文会尽量把判断说清楚,而不是给一张名词对照表。凡是涉及具体论文编号、具体跑分和具体消融数字的地方,我只描述机制本身,因为这些细节在不同实现之间差异极大,写死反而容易误导读者。

二、视觉编码器选型:CNN 与 ViT 到底在争什么

视觉编码器的历史比大语言模型长得多。在图像分类时代,主线是 CNN:从早期网络到残差家族,再到 EfficientNet 和 ConvNeXt 这一批现代卷积设计。ViT 则在中途插入,用切块加全局自注意力替换卷积,一度被认为会吃掉卷积。但在视觉语言模型这个场景里,结论并不是简单的谁胜出,而是分层的和扁平的各占一个生态位。

2.1 卷积骨干的有效区间

卷积的强项是局部性和高效性。卷积核共享参数,对高频细节极其敏感,小字、细笔画、纹理这类信号能被抓得很实;参数量小、显存占用低、推理快。在端侧部署、实时视频流、需要极低延迟的场景里,卷积骨干依然是最划算的选择,这是纯粹的经济学判断,不是技术落后。

卷积的短板也明显。感受野的增长依赖层数堆叠,深层语义提取的效率不如自注意力;更重要的是它天然输出多尺度层级特征,而语言模型的输入是一串扁平的 token,层级特征往 token 序列的映射并不自然,往往需要额外的融合模块。

实际项目里很少见到纯卷积的视觉塔,更常见的是混合结构:卷积骨干负责前段细节捕捉,后面接一到两层自注意力做全局融合。这种结构在文档、表格、界面截图这类输入上尤其常见,因为卷积对细粒度文字边缘的响应比固定 patch 切分的自注意力更锐利。

2.2 自注意力家族的四个改造方向

第一个改造方向是把全局注意力换成窗口注意力或者近似线性注意力,目标是把复杂度从平方级降到接近线性,同时让大分辨率在显存上变得可行。第二个方向是改 patch 嵌入方式,比如用卷积做切块、或者在送入主干之前先做一次像素重排聚合,把输入变成数量更少但通道更厚的 token。第三个方向是位置编码适配,因为插值式绝对位置编码在分辨率大幅变化时容易失效,通常需要可插值的相对位置偏置或二维旋转编码顶上。第四个方向是训练分辨率与使用分辨率解耦,也就是在较低分辨率上做主要预训练,在目标高分辨率上做继续训练。

这四个方向在工程上基本是正交的,实践中经常同时用上两到三个。把低分辨率预训练和裁剪式微调结合起来,是目前把单个视觉塔服务多种输入形态最省事的办法。

关于视觉塔要不要保留层级特征,这里有一个经常被忽略但影响很大的分歧。只取最后一层,特征语义化程度高、token 数少、开销小,但空间细节丢得厉害,文字、坐标、小物体会糊成一团。取最后一层加若干层中间特征,细节能救回来一部分,代价是 token 数成倍增长,而且不同层特征的语义粒度并不一致,需要分别投影之后再拼接或者加权融合。

2.3 层级特征与单层特征的分歧

我的经验判断是这样的:如果输入以自然场景为主,比如识别、描述、通用问答,单层特征加上足够的分辨率通常就够了,层级特征带来的增益抵不上 token 翻倍的代价。如果输入以文字密集图像为主,比如票据、论文、截图、表格、界面,几乎必须走多层特征这条路。因为最后一层特征已经把笔画信息抹掉了,投影层再强、分辨率再高也变不回来,多层特征是在保留那些后面再也没机会拿到的信息。

视觉塔形态优势代价更适合
卷积骨干混合后段注意力细节锐利、吞吐高、显存省全局建模弱、需额外融合模块端侧、实时、文档细节
纯自注意力单层特征全局建模强、token 少、扩展性好细节弱、高分辨率成本陡增自然场景、通用问答
自注意力多层特征融合细节与语义兼顾token 数成倍、投影结构复杂文档、表格、图表、定位
低分辨率预训练加高分辨率续训一套权重服务多种输入续训数据和算力成本多形态输入的通用系统

2.4 选型速查

归纳成四句话:追求通用视觉问答和稳定吞吐,用成熟的开源图文对齐权重初始化视觉塔,最省心也最不容易出结构性错误;追求文档和界面理解,选在重 OCR 监督数据上继续训练过的视觉塔,并且一定要走多层特征;追求实时或端侧,回到卷积家族并接受更高的 token 数;追求极限精度且有充足算力,直接上大分辨率自注意力加多层特征,顺便准备好更贵的 serving 方案。

还有一点要提醒:视觉塔的权重来源比架构细节更影响最终效果。同样一个自注意力主干,用通用图文对齐权重初始化和用纯图像自监督权重初始化,最终的收敛速度和对齐难度差别非常大。在算力紧张时,把预算花在换一组更好的视觉塔初始化上,通常比花在加大语言模型上更划算。

2.5 视觉塔的分辨率适配

视觉塔被训练时的输入尺寸和实际使用时的尺寸往往不一致,这是多形态输入系统必须处理的问题。最常见的做法是使用二维旋转编码这类可以按分辨率插值的方案,而不是固定网格的绝对位置编码;前者能在不重新训练的情况下接受非正方形的输入,后者一旦分辨率变化,位置编码就跑偏了,模型会安静地输出一些看起来正常但定位全错的结果。

另一个容易被忽略的点是感受野与尺寸的绑定。自注意力骨干的有效感受野随着输入增大而相对缩小,同样的窗口注意力配置在低分辨率上能覆盖全图,在高分辨率上只能看到局部。如果不相应地调整窗口大小或者注意力层数,高分辨率输入的全局理解会明显退化,而退化表现出来的是回答范围变窄、不做跨区域推理,很容易被误判成数据问题或者模型能力问题。

还有一个折中路径值得记住:视觉塔内部保持低分辨率以控制算力,只在视觉塔输出之后、进入语言模型之前做一次空间上采样。这等于把高分辨率的成本只花在进入语言模型之前的极短路径上,是算力受限时能跑出不错效果的现实做法,代价是补回来的细节非常有限,对文字密集类输入帮助有限,只适合自然场景。

三、分辨率、视觉 token 数量与信息瓶颈

分辨率决策的本质,是在看得清和算得起之间找平衡点。视觉 token 数大致等于图像高乘宽除以 patch 面积,再除以空间下采样倍数。一边翻倍就是 token 数变四倍,而自回归解码器里的序列长度变四倍,注意力开销和 KV 缓存占用都会跟着上一个台阶,在长上下文场景里这个代价往往超过模型本身的算力。

更麻烦的是文字密集场景下的分辨率需求差异极大。一张风景照在较低分辨率上足够描述,一张满页密排的文档扫描件在同样分辨率下就是一团糊。固定分辨率的方案必然在两端都有浪费:一端浪费算力,另一端直接看不清。

所以现在的主流思路不再是说我用多少分辨率,而是说我给这张图多少 token 预算,然后由一套策略决定这个预算怎么花。这个转变听起来只是措辞变化,但它把设计问题从架构层挪到了策略层,后面所有的动态分辨率方案都是在这个框架下展开的。

预算分配的常见策略有三种。第一种是固定预算加缩略图:整体降采样成一张小图保住全局语义和物体布局,再拼一张高分辨率的局部图保住细节,两张图共享预算。第二种是动态切块:先做一次轻量的内容判断,按有效区域切图,空白和纯色区域少给甚至不给 token。第三种是动态分辨率原生:直接把图像按宽高比切成若干条带,每条带的高度固定、宽度可变,于是每条带的 token 数由高决定,再用一个二维位置编码把条带位置编码回去,让模型知道这一条带在原图里位于哪里。

  • 缩略图加局部图:实现最简单,行为可预测,调试友好,适合先跑通系统。
  • 动态切块:token 用得最省,对版式复杂的文档和界面最有效,但切块策略本身是个独立的质量瓶颈。
  • 原生动态分辨率:表达力最强,没有额外的缩略图浪费,但对位置编码和训练数据覆盖度要求高,实现和训练都要更小心。

关于分辨率与文字识别,有一个必须讲清楚的判断:图里的文字高度低于多少像素时,识别就开始不可靠。这个经验阈值在不同模型上差别不小,但总体规律是稳定的——八个像素以下基本不可靠,十二到十六像素是可用的下限。按这个标准回头看,一张 A4 满页文档如果整页塞进几百像素的输入里,每个字大概率只有几个像素,这时候任何架构都救不回来,问题不在模型而在你没给它足够的像素。

应对办法只有两个:要么切块分块阅读,让每个字落在可用像素区间内;要么干脆先走一遍 OCR,把识别出来的文本和原图一起送进模型,让文字信息走文本通道而不是靠像素硬读。后者在实践中往往比无限堆分辨率更划算,也更稳。

# 视觉 token 预算与实际字高估算(工程里上线前先算这个)
def estimate_tokens(h, w, patch=14, merge=2, extra_patches=0):
    gh, gw = h // patch, w // patch
    n = (gh * gw) // (merge * merge)
    return n + extra_patches          # extra_patches 用于缩略图等附加 token

def estimate_text_pixels(h, w, lines=45, chars_per_line=38):
    # 满页密排文档的粗略字高估算
    return h / lines

for res in (224, 336, 448, 672, 896):
    side = res * res
    n = estimate_tokens(res, res)
    px = estimate_text_pixels(res, res)
    print(f"res={res} tokens={n} text_h={px:.1f}px")

# 结论:整页密排文档想直接靠像素读,
# 输入边长至少要 1120 以上才勉强进可用区间,
# 这时 token 已经上万,必须切块。

还有一个常被忽略的性能事实:视觉 token 数量和训练吞吐的关系不是线性的。语言模型部分的序列变长后,训练时的显存峰值上升很快,而视觉塔那部分的算力占比反而在下降。等你把分辨率推高到视觉塔不再是瓶颈的时候,你实际上已经进入了一个纯语言模型的显存问题,这时候优化方向应该是序列并行、梯度检查点或者 KV 缓存量化,而不是继续折腾视觉塔。

四、投影层:从视觉空间到语言空间

投影层承担的是坐标系转换这件事。视觉编码器输出的向量维度通常在一千左右,局部性强、语义轴偏向视觉;语言模型的隐藏维度往往在两三千到上万,语义轴完全不同。投影层要做的不是简单拉伸维度,而是把前者空间的语义结构重排到后者空间的语义结构里。这一步做砸了,后面再大的模型也救不回来。

目前有三种主流设计。线性层就是一个矩阵乘法,参数量最少、信息保真度最高、几乎不会过拟合,但表达能力受限。MLP 是两层全连接加激活,是最常见的折中。query-based 设计用一组可学习 query 通过交叉注意力从视觉特征里抽取固定数量的向量,输出的 token 数和输入 token 数解耦,结构上常被称为 resampler 或者 Q-Former 类结构。

4.1 三种结构的取舍

线性层适用于一种特定情况:视觉特征本身已经高度对齐,比如视觉塔在大量高质量图文数据上做过对齐训练,投影层只需要做一次坐标变换就够了。在这种情况下加非线性层不但没收益,反而增加过拟合风险。很多规模不大的实用系统就停在两层线性上,而且效果并不差。

MLP 是绝大多数生产系统的选择。它比线性层多了一次非线性,表达能力明显上一台阶,参数增量也完全可控。唯一要注意的是中间层的宽度不要盲目放大,因为激活值显存是随视觉 token 数线性增长的,中间维度放大两倍意味着这一段的显存也放大两倍。

query-based 的核心价值是解耦与压缩。输入几千个视觉 token,输出固定的几百个,显存和推理成本大幅下降,而且输出长度固定让系统层的批处理和缓存更容易做。代价是信息瓶颈。压缩比超过某个阈值之后,需要精确对齐的任务会明显掉点,比如计数、坐标定位、小字读取。另一个问题是 query 向量在训练中容易退化成只提取全局语义而忽略局部细节,表现为模型描述整体很流畅,但一问到某个具体位置的小细节就答不出来。

投影层类型参数量信息保真过拟合风险适用边界
单层线性极小最高最低视觉塔已强对齐、小模型
两层 MLP中等高中等生产系统默认选择
多层 MLP较大高偏高收益快速饱和,不推荐默认启用
query resampler中等受压缩比限制高高分辨率、长上下文、内存敏感

4.2 参数量陷阱

这里有个容易踩的坑。投影层的参数量只随视觉特征维度线性增长,看着不多,所以很容易被低估;但它的计算量和激活值显存是随视觉 token 数增长的。一个四千维到八千维的矩阵本身不算大,可如果有四千个视觉 token 依次过这个矩阵,激活值显存就会吃掉非常可观的一块。所以在动态高分辨率系统里,投影层设计必须和 token 预算联动考虑,而不是先定结构再往上加分辨率。

还有一个深度问题值得单独说。早期实践里有深投影和浅投影的区分:深投影是串好几层非线性变换,浅投影就是一层线性。可重复的结论是比较一致的,超过两三层之后收益迅速衰减,而浅投影在训练早期收敛更快、显存更省。所以现在更常见的做法是保持一到两层的浅到中等深度,把省下来的算力全部花在高分辨率和多层特征上,这笔账比加深投影划算得多。

import torch
import torch.nn as nn

class VisionProjector(nn.Module):
    def __init__(self, vis_dim=1280, llm_dim=4096, n_layer=2):
        super().__init__()
        self.pre_norm = nn.LayerNorm(vis_dim)          # 抑制异常大维度
        layers, d = [], vis_dim
        for _ in range(n_layer):
            layers += [nn.Linear(d, llm_dim), nn.GELU()]
            d = llm_dim
        self.mlp = nn.Sequential(*layers)
        self.post_norm = nn.LayerNorm(llm_dim)

    def forward(self, feats):
        # feats: [B, T, vis_dim],T 为视觉 token 数
        x = self.pre_norm(feats)
        x = self.mlp(x)
        return self.post_norm(x)                       # 输出维度对齐 LLM 词嵌入空间

# 高分辨率动态切块时的预算分配
def merge_budget(feature_list, budget=576):
    # 多层特征按层号加权拼接,超预算时从最深层开始降采样
    merged = torch.cat(feature_list, dim=-1)
    if merged.shape[1] <= budget:
        return merged
    keep = budget // len(feature_list)
    return torch.cat([f[:, :keep] for f in feature_list], dim=-1)

4.3 归一化这个细节

视觉特征在做投影之前要不要做归一化,投影之后要不要再接一层归一化,会显著影响训练稳定性,尤其在半精度训练下。不归一化的视觉特征里经常有一两个维度是异常大值,这些维度会直接把注意力里的内积放大到溢出的程度,表现为训练前几百步 loss 突然变成非数。稳妥的做法是投影前一层归一化,投影后再一层归一化,两层都很便宜,但能省掉大量排查时间。

4.4 投影层的常见结构变体

实际工程里还会看到几类变体。最常见的一类是在 MLP 后面接一个视觉 token 的类型嵌入和一个时间或图片序号嵌入,用来告诉模型这些 token 分别属于哪张图、哪一帧。这一步在单图任务上完全可以省略,但在多图和视频任务上不做,模型基本学不会跨图和跨帧的指代关系。

第二类是把 MLP 和 query resampler 组合起来:先用 MLP 把维度映射到语言空间,再用一组 query 做一次跨注意力抽取输出固定数量的 token。这个组合的好处是两头的问题分开解决,坏处是链路变长之后调试变得困难,出问题时很难判断是维度映射错了还是压缩错了。

第三类是把逐位置的映射改成分组映射,比如按空间分块分别用不同的小投影层,处理不同尺度下产生的特征。这个做法在多层特征融合时比较自然,因为不同层特征的统计分布本来就不同,用同一套投影参数去处理,训练早期往往需要更长的预热才能收敛。

一个通用建议是:投影层的结构选择应该在项目早期定下来并冻结,不要指望在后期靠改投影层来救效果。投影层结构一旦变动,视觉塔的预训练权重和解码器的微调状态都需要重新适配,成本远高于在最开始就选一个合理但并不惊艳的结构。

五、三阶段训练:每一阶段究竟在学什么

目前最常见的配方是三阶段:视觉对齐预训练、指令微调、偏好优化。每一阶段的目标、解冻范围和数据类型都有讲究,混在一起做是最常见的性能损失来源。

5.1 阶段一:视觉对齐预训练

这一阶段只训练投影层,冻结视觉塔和语言模型。目的非常单一:让投影层学会把视觉空间映射到语言空间,让视觉 token 在语言模型眼里变得可读。数据通常是超大规模的粗标注图文对。

为什么只训投影层?因为此时语言模型的下一个词预测能力已经成型,而大规模图文对的文本监督很弱、噪声很大。如果在这里放开整个模型,梯度绝大部分来自文本侧,语言模型会先被大量粗糙文本带偏,视觉那部分还没学到什么东西就被淹没了。冻结两头、只让中间的接头学习,是成本和稳定性都最优的折中,这也是整条链路里性价比最高的一步。

但纯冻结也有代价。视觉塔如果是通用对比学习权重,它的特征空间对文字密集图像和细粒度关系并不敏感,投影层不可能凭空造出这些信息。所以比较成熟的做法是在这一阶段后期逐步解冻视觉塔的最后几个 block,用明显更小的学习率做特征适配;配合上对齐预训练使用的图文对里适当提高高分辨率、含文字图像的比例。判断标准很简单:解冻之后如果验证集上的文字相关任务不涨反跌,就说明适配过头了,退回去只解冻 projector。

5.2 阶段二:指令微调

这一阶段解冻语言模型(通常全参),投影层继续训练,视觉塔保持冻结或者用极低学习率微调。数据从粗标注描述换成结构化指令:多轮对话、视觉问答、推理题、文字识别、定位、拒答样本。

这一阶段最容易出问题的不是收敛,而是数据格式不统一。同一个数据集里,有的样本把图像放在用户轮次,有的放在系统提示里,有的用占位符插在文本中间;多图样本的顺序和分隔符规则也各不相同。如果格式不统一,模型学到的是位置偏置而不是看图能力,表现就是换个提问方式就明显掉点。解决办法是把所有数据统一走一条预处理模板,图像统一插在用户轮次开头、或者统一用占位符占位,并在训练前抽样人工核对几百条。

第二个问题是数据配比。指令微调里视觉数据和纯文本数据的比例非常敏感。纯文本比例太低,语言能力会塌掉,模型开始输出语无伦次的句子;比例太高,模型过拟合到看图说话的短答案模式,复杂推理和长回答能力下降。经验上视觉相关样本占比在几十个百分点比较稳,但要按任务调:做垂直领域问答就要往上调,做通用助手就要往下调,多留一些纯文本样本保底。

第三个问题是长序列与打包训练。把多个样本拼成一条长序列做高效训练是标准做法,但视觉 token 占比高的时候容易出问题:视觉 token 的位置编号在拼接后必须重新连续编号,不能沿用单样本内部的编号;注意力掩码的实现如果只按文本长度裁剪,会把视觉 token 屏蔽掉或者让填充位泄漏。这两个 bug 的表现都是训练 loss 看着正常、推理效果莫名其妙地差,而且非常难通过看 loss 曲线发现。

# 三阶段训练的解冻范围与数据来源(伪代码)
STAGES = [
    dict(name="align_pretrain",
         trainable=["projector"],              # 只训接头
         data="web_captions_pretrain",          # 大规模粗标注
         lr_proj=1e-4, lr_vision=0.0, lr_llm=0.0),
    dict(name="vision_adapt",
         trainable=["projector", "vision_top_blocks"],
         data="highres_text_captions",          # 提高含文字高分辨率样本比例
         lr_proj=1e-5, lr_vision=1e-6, lr_llm=0.0),
    dict(name="sft",
         trainable=["projector", "llm"],        # 全参解冻语言模型
         data="instruction_tuning_mix",         # 多轮/推理/OCR/定位/拒答
         lr_proj=1e-5, lr_vision=0.0,   lr_llm=2e-5),
    dict(name="preference",
         trainable=["llm"],
         data="vision_pref_pairs",              # 幻觉对比 + 拒答 + 不确定性
         lr_proj=0.0,   lr_vision=0.0,   lr_llm=2e-6),   # 学习率压到 SFT 的十分之一
]

for st in STAGES:
    for name, p in zip(["proj", "vis", "llm"], ["projector", "vision", "llm"]):
        p.requires_grad_(name in st["trainable"])
        set_lr(opt, p, st[f"lr_{name}"])
    run(st["data"])
阶段解冻范围主要数据真正在学的东西
对齐预训练仅投影层海量粗标注图文对视觉空间到语言空间的坐标映射
视觉适配投影层加视觉塔末段高分辨率含文字图补齐细节特征的可用性
指令微调语言模型全参结构化多模态指令任务格式、轮次行为、回答风格与边界
偏好优化语言模型视觉偏好对抑制幻觉、校准拒答与不确定性表达

5.3 阶段三:偏好优化

这一阶段用偏好数据做直接偏好优化或者它的变体:同一个提示下给出优选和劣选回答,让模型把概率质量挪向优选侧。视觉偏好数据的构造比文本难,因为视觉任务上哪个回答更好往往更有争议,自动构造的比例高了会引入噪声标签。

视觉偏好数据里最有价值的一类是幻觉对比。做法是构造一个视觉上并不存在的细节,让模型明确拒答或者纠正。这直接压制的是幻觉,也就是模型在图像证据不足时用语言先验补全出看起来合理但事实不存在的描述的趋势。这个现象是视觉语言模型最顽固的缺陷之一,而且在短回答格式下尤其严重。

另一个高收益做法是拒答与不确定性训练,让模型在图像模糊、分辨率不足、问题超出图像内容时学会说看不清或者图里没有,而不是硬编。这个能力和前面第三节讲的 token 预算设计是强相关的:预算给得少,本来就看不清,如果模型没被教会看不清时该说什么,它就会自信地输出错误细节。所以在预算受限的系统里,拒答训练不是锦上添花,而是必要的配套。

需要提醒的是,偏好优化在视觉任务上的增益通常没有文本任务那么大,而且很容易引入过度拒答:模型变得什么都说不确定,问它图里有什么它开始打太极。比较稳的做法是把学习率压到指令微调的十分之一到五分之一,并且刻意保留一部分证据充分、需要给出确定性回答的样本做平衡,同时在验证集上专门盯过度拒答这个指标的曲线。

5.4 数据:真正决定上限的那一半

架构决定天花板,数据决定你能摸到多少。有个很反直觉的经验:对齐预训练阶段的数据量可以巨大,质量可以粗糙;而指令微调阶段的数据量往往小两个数量级,但每一条的格式正确性直接决定成败。同样规模的算力预算下,把指令微调数据从十万条扩到三十万条粗造数据的收益,通常远小于把十万条数据的格式统一并人工抽查一遍。

数据来源大致分三类。第一类是从网页、视频帧抓来的弱标注数据,用自动描述或者现成字幕生成指令,量大但噪声高,主要用来支撑对齐预训练。第二类是人工构造的高质量指令,覆盖多轮对话、推理、定位、拒答等场景,量小但每条都有明确的行为目标,是指令微调的主力。第三类是合成数据,用强模型批量生成问题、答案、偏好对,介于前两者之间。

合成数据是 2026 年变化最大的部分。用强模型给一张图生成十几个不同角度的问题,再让另一个强模型生成优选和劣选回答,这样构造出的偏好对在幻觉抑制上的效果非常好,因为劣选回答可以是人工注入的典型幻觉模式,比如把不存在的物体说得很肯定。但合成数据有个致命风险是风格趋同:几万条都由同一个模型生成,答案的句式、长度、犹豫程度会高度一致,模型学到的是这个生成器的风格而不是真实的多样性。缓解办法是混合多个生成器,并在合成数据里强制注入一定比例的人工样本和格式异常样本。

数据处理环节有三个细节值得单独强调。一是去重,尤其是图文对的语义去重,网络上的图文对重复率非常高,重复样本会让模型在某些表述上产生异常强的偏置。二是配比平衡,不能让某一类任务在数据里占到压倒性比例,尤其是多轮对话和单轮问答的比例,会直接影响模型遇到多轮时是继续问还是直接答。三是长度分布,长回答样本和短回答样本的比例决定了模型的默认输出长度,很多模型答得太啰嗦或者太短,根源就在这里而不在模型。

最后是数据与架构的联动。一个具体例子:当你的系统大量使用切块策略时,训练数据里就必须包含同样切块逻辑产出的多图样本,否则推理时模型看到的是一种分布,训练时看到的是另一种分布,表现就是单图任务好、多图任务差。同理,如果推理时用了 OCR 双通道,训练时也应该有相当比例的 OCR 文本加图像的样本。架构决策和数据管线必须一起改,否则一半的改动不会生效。

六、特殊输入形态:多图、视频与文字密集图像

上面讲的是单图标准链路。真实业务里最常出问题的其实是下面这几种输入形态,它们的难点不在模型本身,而在序列组织方式。

6.1 多图输入

多图输入的实现比看起来琐碎得多,需要解决三件事。第一是图像 token 在序列里的排布,是全部前置放在文本前面,还是用占位符插在对应轮次里,两种方案的行为差异明显:前置方案让模型对图序更敏感,插占位符方案对图文交错指令支持更好但预处理更复杂。第二是位置编码的连续性,多张图拼进同一条序列时位置编号必须整体连续编号,如果不连续,模型会把图与图之间的边界当成语义断裂,表现就是跨图推理(比较两张图)明显弱于单图任务。第三是图片顺序敏感性,很多实现对图片顺序非常敏感,交换两张图的顺序答案就变,这在图文对照类任务是硬伤,需要在训练数据里显式加入顺序扰动的样本来增强鲁棒性。

多图还有一个容量问题:N 张图意味着 N 倍 token,预算立刻吃紧。常见做法是给主图足额预算、次图压缩,或者更进一步,只对问题里显式引用的那几张图分配足额预算,其余图走缩略图。这个策略的本质是用语言侧的指代信息去指导视觉侧的预算分配,效果往往比均匀分配好很多。

6.2 视频输入

视频输入最省事的实现是抽帧成伪图像序列,常见帧率在每秒一到四帧,然后整条走图像通路。这条路的最大好处是能直接白嫖图像侧的预训练和全部工程链路,坏处是丢掉了帧间连续性和真实的时间顺序信息,模型看到的其实是若干张不相干的照片。

真正的改进方向是在视觉塔里引入三维位置编码,把注意力改成时空分离的形式,比如时间维用局部窗口注意力、空间维用全注意力,或者反过来做轴向注意力。这类设计能让模型回答动作顺序、状态变化、跨帧一致性问题,而不只是单帧描述。代价是显存和实现复杂度显著上升,而且训练数据的需求从图文对变成了视频加高质量字幕,供给量少一个数量级。

6.3 文字密集图像:文档、表格与界面

文档、表格、图表、界面截图是最能拉开模型差距的场景,也是最容易翻车的场景。典型失败模式有这么几种:分辨率不足导致笔画糊成色块;多栏排版被当成单栏,串起来的阅读顺序是错的;表格的行列对齐被破坏,模型把相邻单元格内容连读;图表的图例和坐标轴文字被当成背景噪声忽略;以及旋转文本、艺术字体、手写体直接不认识。

对应的工程手段有四类。第一是提高有效分辨率,也就是切块分块阅读,这是物理层面的必要条件,前面已经算过字高阈值。第二是把 OCR 提取的文本作为辅助输入,和原图一起送入模型,让文字信息走文本通道而不是靠像素硬读,这样既省 token 又更准。第三是要求模型输出坐标,也就是 grounding,用检测式的中间监督把文字块和位置绑在一起训练,这对后续的表格抽取几乎是必需的。第四是最朴素但最有效的方案,先用现成工具把图像渲染成结构化文本,再让模型读结构化文本。

表格和数值抽取这类任务要特别强调:如果目标是精确拿到每一个单元格的数值,必须让模型输出单元格级别的定位信息,光让它读出表格内容一定会出现串行和错位。这也是为什么文档类模型的训练数据里,坐标监督比纯文本监督值钱得多——坐标监督实际上给了模型一个必须对齐视觉证据的强约束。

6.4 高维输出任务

除了一句话回答,视觉语言模型还能输出分割掩码、深度图、人体姿态、检测框。这把视觉任务从判别式变成了密集预测,对应的输出头设计完全不同:要么在语言侧之后接一个解码头,要么反过来把视觉 token 当作 query 去驱动一个下游分割网络,要么直接在文本侧生成归一化坐标序列再解析。前两种通常更准,第三种更灵活但需要额外的坐标解码与后处理。

成本控制的关键是让高分辨率特征只在需要时进入后续网络。如果分割头需要全分辨率,那就只在这条路径上走高分辨率,文本生成路径继续用压缩后的 token,两条路共享视觉塔但不共享特征分辨率。这几乎是所有多任务视觉语言系统的标准做法。

七、2026 年的几个新趋势

7.1 原生多模态与统一 token 空间

这条线的核心主张是:不要再在后面接一个投影层,而是从训练开始就让图像、文本、音频共用一套 token 空间和一套模型权重。好处很直接,模态之间可以互相补全,图上文交错的长文档理解能力显著更强,语言侧的推理也能被视觉证据更好地约束。代价是训练成本极高、数据配比极难调、任何一个模态的数据质量不足都会拖累全局。

到 2026 年,这条线已经从论文阶段进入有真实系统落地的阶段,但对中小团队来说,全量预训练已经不现实。更现实的路径是在开源统一模型的权重上做领域继续训练,重点补自己场景里最缺的那个模态(比如大量专业图表,或者特定格式的文档),而不是试图从头训一个统一模型。这条路上一个务实的判断是:领域继续训练的数据质量比数据量更重要,几万条高质量领域样本通常比几百万条通用样本收益更大。

7.2 视频与时空建模

视频理解的重心正在从抽帧问答转向时空推理:动作顺序、状态变化、跨帧一致性、长时事件。这要求模型不能只把帧当成独立图片,而要有显式的时间位置编码和时间维注意力结构。判断一条系统是不是真的做了时空建模,有一个非常简单的测试方法:把输入帧的顺序打乱,如果答案完全不变,那它基本没有做时间建模,只是在做多图描述。

7.3 视觉 token 的极限压缩

减少视觉 token 是当前最有工程价值的方向之一,因为它的收益是直接的:序列短了,显存降了,吞吐上去了,同卡能服务的并发数上去了。主要手段包括相似 token 合并、空间池化、query resampler,以及按内容自适应的 token 数分配。风险在于压缩比和细节任务精度之间的权衡,而且如果压缩策略是不可学习的硬规则,模型适配它需要额外的训练量。我的判断是,把压缩放在视觉塔后段、在语言模型之前,是收益和损失最平衡的位置;放在语言模型内部做动态 token 数则更激进,目前还比较脆。

7.4 视觉稀疏化与混合专家

把语言侧的稀疏专家思路搬到视觉侧,是让高分辨率从成本红线变成可选项的关键手段:高分辨率特征只在少数被选中的专家里做重计算,其他 token 走共享的轻量路径。2026 年更成熟的做法是把稀疏选择放在视觉塔的后段而不是全塔,前段保持稠密以免损失低层细节。这个判断的直接依据是低层特征承担的是边缘和纹理提取,这类信息对稀疏选择不友好,而高层语义才适合稀疏化。

7.5 从能看,到会用工具

架构层面一个明显的趋势是模型开始学会调用外部视觉工具:放大裁剪、调用 OCR、调用检测器、调用渲染器。这本质上是把分辨率预算问题从架构层转移到推理策略层——与其在一次前向里塞进所有像素,不如让模型自己决定要看哪里、看几眼、多看几轮。这个方向的价值很大,但随之而来的训练难题也不小:需要构造先调用工具再回答的轨迹数据,监督信号比普通指令数据稀疏得多,且推理时多轮调用的延迟成本需要业务侧能接受。它已经更接近智能体的训练范式,而不是纯架构问题。

7.6 评测与诊断:怎么判断是哪一环出了问题

多模态系统的评测有个特殊困难:效果不好的时候,很难判断是视觉塔没看清、投影层压丢了、还是语言模型没学会用。乱调一通是效率最低的诊断方式。一个可操作的诊断顺序是先测视觉塔的特征质量,再测投影层的信息保真,最后才怀疑语言模型。

第一步是把输入图片单独送进视觉塔,看特征图在文字区域是否有明显响应。如果响应本身就很弱,问题在视觉塔或者分辨率,立刻调分辨率或换塔,不用再看别处。第二步是检查投影前后的特征相似度,如果投影之后不同区域的 token 变得几乎无法区分,那问题在投影层的压缩比或者归一化,把它换成更浅的投影再测。第三步才是看语言模型的行为,比如它是否理解问题、是否只是不看图就用语言先验乱猜。

有一个非常实用的对照实验:把图像换成一张纯白图或者随机噪声图,再问同样的问题。如果模型回答的内容和看真图时几乎一样,说明它根本没在用视觉信息,问题一定在前两环,而不是语言模型。这个测试成本极低,但能一次性排除掉最容易被误判的环节,值得作为标准排查动作写进调试流程里。

评测集的组织方式也很重要。按任务类型分层比按数据来源分层更有诊断价值:自然场景问答、文字读取、计数、空间定位、细粒度识别、拒答校准,每一层单独打分。一个笼统的综合分上升了,但文字读取层掉了,这种变化在综合分里根本看不出来,等到线上出问题就晚了。

八、工程选型清单与常见翻车点

把前面的判断压成一份可执行的清单,按顺序过一遍,比读十篇架构综述有用。

  • 先算 token 预算,再定分辨率。拿真实业务图像估算字高和 token 数,先确认纯像素方案在物理上够不够。
  • 视觉塔优先选已对齐的成熟权重,必要时只解冻末段几个 block,且只在文字任务上验证是否真的涨点。
  • 投影层默认两层 MLP,浅到中等深度优先,把省下的算力花在分辨率和多层特征上。
  • 投影前后各加一层归一化,避免半精度训练早期的非数崩溃。
  • 指令微调前先统一数据格式模板,抽样人工核对,位置编号拼接后重新连续编号。
  • 视觉与纯文本样本配比从几十个百分点的视觉占比起步,再按任务往上调。
  • 拒答与幻觉偏好数据必须配,否则高分辨率受限时模型会自信地编造细节。
  • 偏好优化的学习率压到指令微调的十分之一量级,并单独盯过度拒答指标。
  • 文字密集场景优先考虑 OCR 文本加图像的双通道,而不是无限堆分辨率。
  • 表格和数值抽取任务必须加坐标监督,只做文本监督一定会串行错位。

几个高频翻车点值得单独强调。第一个是把对齐预训练和指令微调混在一起做,一个基线都没有就想调数据配比,这样调出来的任何提升都不可归因。第二个是分辨率提高后没有同步调整长序列训练配置,训练时正常、推理时超长。第三个是多图样本位置编号没有重新连续编号,跨图推理莫名其妙地差。第四个是视觉塔解冻过头,把原本对齐好的特征空间搞乱,通用能力反而退化。

还有一个关于部署的现实判断:视觉 token 多不等于效果一定好,但它几乎一定意味着更贵的推理成本。在写方案文档的时候,应该同时给出 token 数、单请求显存占用和预估吞吐三个数字,而不是只给效果对比。这三个数字才是让业务方做决策的依据。

场景推荐视觉塔分辨率策略投影层
通用图像问答成熟自注意力权重中等固定分辨率两层 MLP
文档与表格多层特征,重文字监督动态切块加双通道 OCR浅投影保细节
实时视频流卷积骨干混合注意力低分辨率抽帧线性到浅 MLP
长视频理解带三维位置编码的自注意力时序窗口加空间压缩query resampler
高分辨率图像编辑分层细节保留的自注意力最高分辨率多层特征融合投影

8.1 显存与吞吐的真实瓶颈

分辨率推高之后,瓶颈几乎一定会从视觉塔转移到语言侧的序列长度上。这时候有效的优化顺序是:先做梯度检查点和视觉塔的冻结缓存,再做序列并行或者分桶降低 padding 浪费,最后才考虑量化。每一步的收益是递减的,但第一步通常就能解决大部分问题,因为对齐预训练和指令微调两个阶段的显存峰值来源完全不同,前者由投影层的大 batch 高分辨率决定,后者由长序列的全参语言模型决定。

8.2 推理与部署侧的视觉 token 治理

训练时确定的 token 预算,在部署时还会再被放大一次成为成本问题,原因很简单:线上请求的图片往往比训练集里的更难,需要更多 token 才能看清,而用户又不愿意等。部署侧常用的手段是缓存和分级预算这两类。

缓存方面,多轮对话里最典型的问题是同一张图在每一轮都被重新编码。正确做法是把视觉 token 缓存在前缀里,后续轮次只追加文本 token,这样前缀缓存能命中,视觉编码的开销只付一次。前提是视觉 token 的位置排在文本之前或者用占位符插入但保持同一段前缀不变,如果每轮都把图像插在不同位置,缓存就永远命中不了。这一条在实现上的收益往往比任何视觉侧的优化都大,而且几乎不改动模型。

分级预算是更主动的做法:先用一个轻量判别器或者规则快速判断图片难度和尺寸,简单的图片给小预算直接回答,复杂的图片才给足预算走完整流程。对于文档截图这类几乎必然需要高预算的输入,这个判断几乎不会错;对于聊天配图这种简单场景,能省下的算力非常可观。

压缩也可以放在推理侧做,而且往往比训练侧压缩更划算,因为它不需要重新训练模型。做法是保留视觉 token 的第一个和最后一个、加上按相似度选出的若干代表 token,其余丢弃,再让模型自己在推理时适应这种稀疏输入。但如果模型从没见过稀疏输入,效果还是会掉,所以更稳的顺序是:先上缓存,再上分级预算,最后才考虑推理侧裁剪。

部署侧最后要盯的指标有三个:视觉 token 的 P95 长度、单请求显存峰值、以及缓存命中率。这三个数决定了并发容量和尾延迟,比平均 token 数有用得多,因为用户感知到的卡顿几乎全部来自长尾。

九、总结

回看整条链路,多模态大模型的架构选择其实收敛成三个决定性的问题:视觉塔输出什么形状的向量、投影层保留多少 token、训练时哪部分参数在动。这三个问题的答案共同决定了一个模型的能力边界和成本结构,其余细节都是在这三个答案之下做局部优化。

对绝大多数工程团队来说,最省力的路径是:成熟的已对齐视觉塔 + 两层 MLP 投影 + 明确的 token 预算 + 规范的三阶段训练流程,把省下来的精力放在数据质量和特殊输入形态的处理上。真正能形成差异化的,不是把某个结构改得更花哨,而是把文档、多图、视频这几种难输入形态认真处理好。

值得记住的一个判断是,架构能补救的是分辨率不足带来的问题,补救不了的是根本没有给够像素。文字密集任务上的投入产出比,优先级应该排在几乎所有架构改造之前——多花一倍算力把整页文档的输入从 448 提到 896,效果通常好过把投影层从两层加深到四层。

本文只讨论架构层面。真正的多模态应用开发、系统提示工程、工具调用编排属于另一条主线,和本文的取舍判断基本正交,混在一起讲反而会让两边都讲不清楚。架构能给你的,是一个上限明确、成本可预估的基础模型;上限之上能做到多少,取决于工程和数据。

另一个容易被低估的判断是,多模态系统的能力上限往往由最弱的那一环决定,而不是由最强的那一环决定。一个视觉塔很先进但投影层压缩过度的系统,在文字任务上会输给一个视觉塔普通但分辨率给足的朴素系统。所以做架构评审的时候,应该找的是最短的那块板,而不是最长的那块。

而站在 2026 年这个时间点回看,原生多模态的收敛、时空建模的补齐、以及模型学会主动调用视觉工具这三条线正在往同一个方向汇合:视觉不再是模型的一次性输入,而是一种可以按需取用、按需放大、按需切换表示的动态资源。理解这一点,比记住任何一个具体结构都更重要。