最近在跑多模态大模型(MLLM)的视觉推理评测时,遇到一个很典型的场景:把一张图里狗的毛色从黑色改成白色,问题照样问“狗在做什么”,模型给出的答案和黑色狗时完全一样;单独看答案似乎没错,但一旦问题是“图中有几只白色的狗”,模型就露馅了。这不是视觉编码器分辨率不够,也不是文本指令写得不清楚,而是模型学会了依赖语言先验,在训练过程中把图像信号当成了可有可无的参考。这就是多模态大模型里常说的模态不平衡。
这个现象让我重新理解了一个调优方向:用“模态平衡”作为特权信息(privileged information),在训练阶段额外告诉模型“你看图的充分性如何”,推理时不增加任何输入,却能让视觉推理性能更稳。这篇文章想把这套思路拆开讲清楚:它到底在解决什么问题,为什么能有效,以及落地时真正要留意的坑。
1. 模态不平衡:不是“看错图”,而是训练目标让模型学会绕过图
1.1 现象:模型会“脑补”,而不是“看”
多模态大模型的常见失败方式不是“看不见”,而是“不想看”。把一张图倒过来、遮住一半、或者把关键物体换成不常见颜色,模型依然能给出和原图几乎相同的答案。表面上看起来像是泛化能力好,但如果你换一个需要精确看图的提问方式,结果马上崩溃。
比如视觉问答里问“图片里有几个人”,模型回答得很顺;可你把它换成“排在最左边的人穿什么颜色的衣服”,它就开始含糊。这类问题在CV领域叫视觉推理,它要求模型对空间关系、数量、属性组合、对象交互做多跳判断。语言先验在这里帮不上忙,因为训练集里不会总出现“左边第二个穿红衣服的人”这种固定搭配。
这类失败暴露了一个更底层的问题:MLLM在训练时并没有被强制要求“必须看图”。只要文本指令和答案之间的统计相关性足够高,模型就有机会通过语言模式蒙混过关。模态不平衡不是单指视觉性能差,而是指视觉和语言两条信息通道在模型内部没有得到同等对待。
1.2 原因:语言先验为什么能淹没视觉信号
现在主流MLLM的基本结构,通常是视觉编码器加一个投影层,再接一个大语言模型底座。视觉编码器把图像变成特征,投影层把它映射到语言模型的语义空间里,最后语言模型解码出文本答案。这套结构本身没有强制规定模型必须用多少视觉信息。
问题出在训练目标上。大多数MLLM的训练损失是文本token级别的交叉熵,也就是说,模型只需要把下一个词预测对,就算学对了。它并不直接检查“这个答案是否真的由图像内容推导出来”。于是,一个语言先验很强的底座,很可能在训练中逐渐发现:在某些问题上,不看图也能猜个大概;看图的梯度奖励反而比较稀疏,因为图像特征经过投影后信息损失严重,很难通过单层投影完整传递空间关系和细节。
这就是模态不平衡的机制来源。视觉特征不是不存在,而是它在梯度传播中被语言模型的自回归损失稀释了。越是大规模的底座,语言先验越强,视觉信号越容易被当成“额外噪声”而不是“必要条件”。
1.3 为什么视觉推理任务特别吃这个亏
如果我们只做简单的图像描述或者常识问答,模态不平衡的代价没那么明显。因为常识性答案本身就藏在语言模型参数里,看图只是为了确认一下。但视觉推理不是这样。
视觉推理的关键不是“图里有什么”,而是“图里的这些元素怎么组合、怎么排列、怎么互相影响”。这类任务要求模型把视觉信息拆解成可操作的关系,再交给语言模型做判断。如果模型在训练阶段已经形成“语言捷径”,那么面对新的推理问题时,它很容易用常见答案、常见组合去填空,而不是真去数一数、比一比、找一找。
所以,视觉推理几乎是最能放大模态不平衡代价的任务类型。也正因如此,很多研究开始把“模态平衡”视为一种训练信号,而不是仅仅在评估时看指标。思路也很直接:如果能找到一个方法,在训练阶段明确告诉模型“你现在看图的充分性不够,需要调整”,模型就更有可能学会依赖视觉信息。
2. 特权信息:给训练阶段加一个“参考答案”,推理时不占用成本
2.1 什么是学习中的特权信息
特权信息的概念最早可以追溯到统计学习里的Learning Using Privileged Information(LUPI)。大意是:训练时,我们可以给模型额外提供一些推理阶段拿不到的信息,帮助它学到更好的规律;推理时去掉这些额外信息,模型依然可以工作,而且比原来更稳定。
一个容易理解的类比是老师带学生做题。考试的时候,学生不能查答案,也不能问老师;但平时练习时,老师可以给提示、指出容易错的地方。提示不会出现在考试桌上,但学生通过一次次的提示,建立了更好的解题思路。训练阶段不把额外信息当成推理输入,而把它当成辅助监督信号,这就是特权信息的核心。
在很多多模态方法里,这层的表达方式不太一样,有时叫“辅助监督”,有时叫“正则化”,有时叫“课程提示”。但本质是同一个:让模型在训练时比推理时多看到一点约束,从而学会更鲁棒的表示。
2.2 为什么模态平衡能够充当这个特权信息
模态平衡描述的是视觉信息和语言信息在某个语义空间里是否协调。简单来说,如果一个样本里,视觉特征和文本特征高度一致,说明两条通道都在说同一件事;如果它们相差很远,说明模型可能更依赖其中一条通道,或者两条通道没有对齐。
问题在于,推理阶段我们不会额外给模型一个“这条样本视觉是否可靠”的开关。模型必须自己看图、自己判断。于是,模态平衡很适合做特权信息:训练时,我们额外计算一个平衡度指标,把它作为辅助信号,告诉模型“当前样本两个模态是不是均衡”;推理时,这个辅助信号完全消失,模型依然只接收图像和文本。
这个做法的好处是,它没有改变推理输入,也没有增加部署时的计算量。它改变的只是训练目标。模型在训练中被反复提醒“你的视觉和语言表示偏离了”,就会更早地把视觉特征拉进决策过程。
2.3 训练和推理的分离:关键价值在哪里
很多人第一次听到特权信息,会误以为这是一种数据增强或者多模态特征拼接。其实差别很大。如果把模态平衡直接拼进输入特征,推理时少了这一维,模型可能就不会用了。但如果把它变成损失函数里的一个正则项或者辅助任务,模型不会把平衡度当成输入,而会把“尽量让两个模态对齐”当成优化目标。
这就是训练和推理分离的价值:模型在训练阶段被培养出一种能力,推理时这种能力已经内化到表征里了。它不会因为少了额外输入而失效,反而会因为训练时被强制要求均衡,而对视觉细节更敏感。
这也是我判断这个方向“有长期价值”的原因:它不是在推理阶段打补丁,而是在训练阶段修根因。只要训练目标里缺少对视觉信号的强制依赖,模态不平衡就会反复出现;特权信息是把这个缺口补上的一种温和而有效的方式。
3. 实操路径:用模态平衡做特权信息,可以怎么落地
3.1 常见做法一:用模态表示对齐程度作为平衡信号
现在很多MLLM都有视觉编码器、文本编码器和语言模型解码器。我们可以把同一个样本里的图像和文本,在某个公共语义空间里分别得到两个向量,然后计算它们的余弦相似度、互信息或距离,作为模态平衡度。
一个非常粗粒度的想法是:如果相似度高,说明两个模态很对齐;如果相似度低,说明模型没有充分利用视觉信息,或者视觉与文本之间存在冲突。于是,可以在主任务损失之外,加一个辅助损失,鼓励模型提升这种平衡度。
这个做法实现起来不复杂,但要注意一点:对齐不等于推理正确。文本可能和图像很相似,但模型依然在“偷懒”。所以一般不建议只用相似度作为唯一信号,而是把它和主任务损失组合使用。
3.2 常见做法二:用不确定性或置信度作为调节权重
另一种思路是计算每个模态的置信度,再用它们的相对大小来构造平衡信息。比如,视觉特征通过一个小头产生一个置信度,文本特征也产生一个置信度,如果一个明显高于另一个,就认为模态不平衡。
这种做法更接近“模型是否确定自己看到了什么”。训练时,可以额外训练一个辅助头,预测当前样本的模态平衡状态,然后把预测结果作为注意力权重或损失权重,去调制主任务的梯度。这样,模型在遇到“视觉信号弱但文本先验强”的样本时,就能被拉回到相对均衡的状态。
不过,置信度头如果训练不好,会变成另一个过拟合模块。需要让辅助头保持轻量,同时要在验证集上单独检查它输出的平衡信号是否符合直觉。
3.3 一个最小可运行的实验流程
下面给出一套示例结构,不是某个具体论文的完整实现,但大致能表达“模态平衡作为特权信息”的训练思路。假设你已经有了一个MLLM主干,可以拿到图像特征和文本特征。
# 示例结构,不是完整训练代码 # 假设已有 vision_encoder, text_encoder, language_model,以及一个提取特征的入口 def compute_balance(visual_feat, text_feat, eps=1e-6): """ 计算模态平衡度: 在公共语义空间里比较视觉和文本表示的方向一致性。 返回 [batch] 的分数,越高代表两个模态越一致。 """ v_norm = visual_feat / (visual_feat.norm(dim=-1, keepdim=True) + eps) t_norm = text_feat / (text_feat.norm(dim=-1, keepdim=True) + eps) sim = (v_norm * t_norm).sum(dim=-1) return sim # 比如取值范围 [-1, 1] def train_step(batch): images = batch["images"] texts = batch["texts"] # 主任务前向 visual_feat = vision_encoder(images) text_feat = text_encoder(texts) outputs = language_model(visual_feat, text_feat, labels=batch["answers"]) main_loss = outputs.loss # 辅助平衡信号 balance = compute_balance(visual_feat, text_feat) # 示意:让平衡度尽量向 1 靠拢 balance_loss = (1 - balance.mean()) * lambda_balance loss = main_loss + balance_loss loss.backward() optimizer.step()实际落地时,你需要考虑几个关键点:视觉特征和文本特征必须来自同一个语义空间,否则余弦相似度没有意义;辅助损失不能直接用(1 - balance)强行拉,因为这可能让模型只学“把两个向量逼近”,而不学“看图”;最好先在小批量上检查balance分数的分布,确认它不是常数。
3.4 关键参数和验证方式
第一个关键参数是辅助损失权重。这个值太小,起不到作用;太大,会让模型把“模态对齐”当成首要目标,反而牺牲主任务准确率。我一般会从 0.01 开始,观察主任务 loss 和平衡分数的变化,再慢慢往上调。
第二个关键参数是温度系数。如果使用对比式平衡度计算,温度会影响相似度分布的锐利程度。温度过高,所有样本都会变成中等相似;温度过低,模型容易被困难样本带偏。
第三个关键点是验证方式。不要只看总准确率,建议把评测集拆成“强视觉依赖”和“弱视觉依赖”两个子集。比如,涉及空间关系、数量、颜色组合的题算强依赖,常识型问答题算弱依赖。如果加完辅助损失后,强依赖子集明显提升,弱依赖子集没有下降,说明模态平衡作为特权信息是有效的。
4. 从表征到推理:它为什么能提升视觉推理性能
4.1 表征层面:让视觉特征不再“躺平”
在普通训练里,视觉特征只要“不阻碍答案生成”,就可能不会得到足够强的梯度信号。语言模型可以通过文本上下文推断出答案,视觉特征即使存在噪声,也不会对最终交叉熵产生太大影响。于是,视觉编码器很容易进入一种“够用就好”的状态,尤其是对细节、空间位置这类信息,感受不到优化压力。
当我们把模态平衡作为特权信息加入后,模型多了一个目标:视觉特征和文本特征必须在语义空间里更一致。这迫使视觉编码器保留更多与文本内容相关的细节,而不是只输出一个粗略的全局向量。表征变得更有区分度,下游推理时自然更容易抓住关键视觉线索。
但这里要强调:这个效果不是凭空出现的。它要求平衡信号本身包含有效信息。如果视觉特征和文本特征只是被随机拉近,而没有和任务目标对齐,视觉特征可能变得平滑,但不会对推理有实际帮助。
4.2 推理层面:减少对语言捷径的依赖
推理阶段,特权信息已经不在输入里了。模型只能依靠它从训练中学到的策略。如果一个模型在训练时多次被模态平衡信号纠正,它的决策路径就会逐渐从“语言猜一猜”转向“先看图像,再结合语言”。
这种转变很难在单个样本上观察,但会在子集评测上体现出来。尤其是那些语言先验和图像内容矛盾很强的样本,一个被训练成依赖视觉的模型,会比一个“语言作弊”的模型更稳定。比如视觉推理题里经常出现的反常识设置:一条狗在车里,但标题文本可能提到“机场”。如果模型只看语言,就会往机场方向答;模态平衡训练会让视觉特征和文本特征在早期对齐,从而抑制错误的语言假想。
这也是我理解的“提升视觉推理性能”的本质:不是让模型变得能处理新的视觉结构,而是让已有的视觉信息真正参与决策。视觉能力一开始就有,只是没有被训练目标强制激活。
4.3 适用边界:什么任务有效,什么任务不适合
先说适合的场景。凡是需要从图像中读取细节、空间关系、数量、属性变化的视觉推理任务,都比较适合用模态平衡做辅助监督。因为这类任务很难靠语言先验糊弄过去,模态平衡能有效放大视觉信号的梯度。
不太适合的场景也有几类。第一类是纯文本推理主导的任务,图像只是装饰性背景,模型无论怎么看图都对答案没有帮助,这时候模态平衡只是额外噪声。第二类是数据规模非常小的情况,辅助损失很容易把模型带到过拟合状态,因为它多了一个可以拟合的表面目标。第三类是模态本身严重缺失——图像遮挡、模糊、文本描述不完整——这时候平衡信号本身不可靠,用它做监督反而会放大噪声。
所以,这个方案更适合在数据质量可控、视觉信息确实需要被强调的训练场景里用。它是在“视觉已经存在但没被用起来”的情况下发力,而不是在“视觉信息已经不可用”的情况下创造奇迹。
5. 不是加一个Loss就完事:避坑与排查链路
5.1 最容易踩的三个坑
第一个坑是把平衡度当成普通特征拼进模型。这样做不是不可以,但它就不再是特权信息了。推理时如果用户没有提供这个特征,模型要么表现退化,要么需要你单独训练一个预测头去估计它,这又引入了新的误差源。正确做法是在训练目标里作用,而不是在输入层拼接。
第二个坑是辅助损失和主任务梯度互相打架。如果主任务是生成文本,辅助任务是拉近视觉和文本特征,两者在优化方向上可能不是完全一致的。比如,文本生成更依赖上下文,而辅助任务更希望两个特征向量一致,这可能导致模型陷入一个“对齐但不够会说话”的状态。解决办法是降低辅助损失权重,或者用梯度裁剪、渐进式退火。
第三个坑是验证指标过于笼统。如果只在整体准确率上看到小幅提升,很难判断是模态平衡在起作用,还是训练轮次增加顺带带来的提升。最好固定一个强视觉依赖子集,用这个子集的准确率作为主要验证指标。
5.2 实验没效果时的排查顺序
先别急着调大权重,按下面这个顺序排查。
- 先看训练主损失有没有下降,如果连主任务都没有学好,辅助信号再强也救不回来。
- 再打印模态平衡分数,看它的分布是否合理。如果所有样本都接近同一个值,说明平衡信号没有携带有效信息,问题可能出在特征空间不对齐或编码器太弱。
- 检查视觉特征和文本特征是否来自同一个语义空间。有些实现里视觉特征还在像素语义空间,文本特征已经在语言语义空间,直接算余弦相似度没有意义。
- 检查辅助损失是否过大,导致模型把“对齐”看得比“答对”更重。观察强依赖子集的准确率是否反而下降。
- 检查评估集是不是真的需要视觉。如果评测样本里语言描述已经隐含答案,模态平衡加得再多,也不会带来视觉推理能力的提升。
- 最后确认一下,你定义的“模态平衡”是不是真的只在训练阶段使用。如果推理阶段不小心也用了,那么它的提升可能来自“额外输入”,而不是来自表征改善。
5.3 从实验到长期使用的工程化建议
如果在小规模实验上验证有效,想长期稳定使用,还需要考虑几件事。
首先是数据分布和模态缺失。真实业务数据里,图片质量、文本长度、语言表达风格差异很大。模态平衡信号在不同数据切片上的分布可能不一样,需要定期监控。如果某段时间图像偏模糊,平衡分数普遍偏低,辅助损失会被异常样本主导。
其次是多语言场景。很多MLLM不只处理英文,视觉特征和不同语言的文本特征对齐难度不一样。模态平衡对这些语言可能产生不同的影响,最好按语言分桶看指标。
然后是评测维度。建议至少保留三个子集:强视觉依赖、弱视觉依赖、反常识样本。这样能更早发现模型是不是回到了语言捷径的老路。
最后是实验记录。建议每轮实验都记录平衡分数的均值、标准差,以及它和主任务准确率的相关性。这些信息能帮你判断辅助信号是否还有效,而不是每次都要重跑全部训练才能发现问题。
回到最开始那个“白狗和黑狗”的问题。我现在会把它当作一个训练目标问题来看:模型不是没有视觉能力,而是没有被训练目标逼着用视觉。用模态平衡做特权信息,本质上是给训练过程加一条“必须看图”的底线。
如果你也想验证这个思路,建议先不要一上来就改完整模型。选一个中等规模的视觉问答数据集,加一个简单的平衡正则,跑一组有和没有的对照实验,再用强视觉依赖子集看差异。只要这个子集有明显提升,你就能确认这个方向是否值得继续投入。先把最小的闭环跑通,再谈更复杂的实现。