多模态大模型这几年的演进速度快得有点让人措手不及。我一直觉得判断一个方向是不是真火不用看论文数量看身边搞CV和搞NLP的朋友是不是开始互相看对方的代码就够了。以前做视觉的和做语言的基本是两条平行线各自在自己的框架里折腾。现在好了所有人都被拽到同一个Transformer架构下开始面对同一批问题归一化怎么放、激活函数选哪个、不同模态的表征怎么对齐。这个标题把多模态模型里最容易被人忽略、但恰恰决定训练成败的几个关键点拎出来了。我见过太多人拿着单模态的惯性思维去迁移到多模态场景结果训练loss死活降不下去或者模型看起来收敛了实际推理时泛化能力一塌糊涂。这篇文章我就从归一化、激活函数、架构一致性、当前难点这四个角度把我实际踩过的坑和看到过的解决思路完整拆开讲。1. 为什么多模态模型对“基础模块”突然变得这么敏感很多人有个误区觉得多模态模型的核心难点全在“怎么融合”上比如用交叉注意力还是用门控机制或者干脆在特征拼接之后再用一个大模型去处理。融合机制当然重要但如果你把融合模块做得非常复杂底层的归一化和激活函数细节却是随便抄的单模态配置那训练时大概率会出问题。先说一个我自己反复遇到的现象同一个模型结构单模态数据训练时一切正常一旦换成图文混合数据梯度更新就开始变得非常不稳定。loss曲线像锯齿一样跳动batch size 调大也没用学习率降到很低还是抖。后来排查来排查去发现根子不在优化器也不在数据采样策略而是出在归一化层的统计量上。为什么多模态对归一化这么敏感因为不同模态的数值分布差异实在太大了。图像经过视觉编码器之后特征图的均值和方差跟文本token经过embedding层之后的分布完全不是一个量级。如果你在整个主干网络里用同一个全局的归一化统计量那视觉特征和文本特征就会互相拉扯。BatchNorm在这种场景下几乎是灾难因为batch内部的“不同模态样本”本身就构成了一种隐式的distribution mismatch统计量会被拽到一个不上不下的中间位置。此外多模态模型的训练通常会用到大规模噪声数据比如图片描述文本里经常有大量与图像内容不完全匹配的句子。这种数据本身的标签噪声就比纯文本语料大得多一旦归一化或者激活函数不能给网络提供足够的“缓冲”模型就很容易被个别异常样本带偏。我个人的体会是多模态大模型里基础模块的作用不是“锦上添花”而是“托底”底没托住上面堆再多花活都白搭。1.1 视觉塔和语言塔的行为差异为什么值得单独关注我们常说的“多模态大模型”其实内部通常不是一个完全共享参数的神经网络。主流做法是先用一个视觉编码器比如ViT系列的变种把图像变成patch序列再用一个文本编码器把token变成向量序列最后再由一个融合层或者语言模型主干去处理混合序列。问题就出在这个“融合”上。视觉编码器产出的特征有一个显著特点相邻patch的特征具有很强的空间相关性而且整体特征的“绝对幅值”通常比文本token向量大不少。文本token的向量在embedding空间中往往是稀疏的、各向异性的而视觉patch特征则相对稠密。这种差异如果不处理直接拼在一起送入后续层会让后续层的学习陷入混乱。你让同一层神经元既要响应视觉patch的高幅值输入又要响应文本token的低幅值输入权重更新方向就会反复横跳。所以你在看很多开源多模态项目的时候会注意到它们对视觉塔和语言塔的初始化权重、归一化方式都做了区别对待。视觉塔一般会沿用单模态预训练时的权重语言塔则按语言模型的惯例初始化两个塔在早期训练阶段甚至可能各自保留独立的norm层到中后期才开始交叉更新。这种做法不是为了省显存是为了别让两个塔在早期训练时就互相“污染”。2. 归一化跨模态统计量的偏置问题比你想的更致命归一化层在多模态模型里承担的职责已经从“加速收敛”变成了“维持不同模态数值分布的相对稳定”。这一点很多人没有意识到。单模态模型里归一化做的主要是“标准化”这件事把均值拉到0附近、方差拉到1附近保证梯度流不会因为特征尺度差异过大而爆炸。但在多模态模型里归一化面临一个新任务它要在不抹掉模态特定信息的前提下让不同模态的特征在同一个数值区间里有可比性。这里有一个很微妙的平衡——如果归一化做得太狠视觉特征的“局部对比度”和文本特征的“语义区分度”都会被削弱模型能获取的信息量就下降了如果做得太松跨模态的数值gap又会导致训练不稳定。具体到实际选择我见过不少人直接在所有层都用LayerNorm理由是“Transformer标配”。这在纯语言模型里问题不大但在多模态场景下LayerNorm的两个统计量mean和variance是逐token独立计算的它不感知token来自哪个模态。视觉patch和文本token在同一个序列里交替出现时LayerNorm会把它们各自归一化这其实是好事但它也带来一个副作用模态间的绝对尺度信息被彻底抹掉了。那怎么办呢现在很多多模态方案会在输入融合层之前加一个可学习的“模态残差偏置”或者“模态缩放系数”。就是说视觉patch和文本token归一化之后再各自乘一个可学习的权重、加一个可学习的偏置让模型自己去决定应该让视觉特征保持多大的幅值、文本特征保持多大的幅值。这个设计初看很小但实际对训练稳定性的帮助非常大。2.1 RMSNorm 与 LayerNorm省略均值计算到底会失去什么RMSNorm这些年很火很多新一代大模型都拿它替代LayerNorm理由也正当——省掉均值计算带来的额外开销且实验显示在许多任务上跟LayerNorm效果差不多。但我得提醒一句RMSNorm的性质和LayerNorm并不完全相同在多模态场景下差异会被放大。LayerNorm会同时减去均值再做缩放这意味着它对特征的“整体偏移”是不敏感的模型依赖的是特征之间的相对关系。RMSNorm不减去均值只做尺度缩放所以如果某个模态的特征整体向某个方向偏移RMSNorm不会去纠正它这个偏移信息会继续向后传播。在纯文本场景token本身的均值偏移通常比较稳定影响不大但在多模态场景视觉塔输出的特征均值随图像内容波动很大比如一张暗光图像的特征整体偏小一张高对比度图像的特征整体偏大。如果你用RMSNorm去处理这种动态范围变化很大的视觉特征模型就需要额外学习一套“补偿机制”来应付这种偏移这会拖慢收敛速度。所以我的建议很明确视觉塔的子模块尽量保留较完整的归一化策略不要盲目追随语言模型那边的RMSNorm“省算力”偏好融合主干如果显存不是极度紧张用LayerNorm仍然是一个更稳妥的选择。很多实际训练不稳定问题根源就是这类细节的“迁移误解”。2.2 归一化层放在哪里Pre-Norm与Post-Norm对多模态训练震荡的影响还有一个容易被忽略的问题归一化层放在残差分支之前还是之后。语言模型领域已经达成一种默契Pre-Norm先归一化再进子层比Post-Norm先过子层再归一化稳定很多大模型几乎默认都用Pre-Norm。这个结论在多模态场景依然成立但理由有了新的层次。Post-Norm的设计有一个隐含假设子层输出和残差输入的幅度相近归一化能有效地把两者“揉”到一个可控范围。但多模态模型的子层输出尤其是融合注意力的输出在不同模态混合输入时波动极大。如果某些patch特征很强势、某些token特征很微弱注意力输出的幅度就会跟着剧烈波动Post-Norm在这种场景需要不断调整统计量去追赶这个波动导致训练早期loss居高不下。Pre-Norm没有这个问题因为归一化罩在输入侧子层输出再大也会被残差结构兜住一部分。我实测下来多模态大模型的早期训练阶段Pre-Norm训练曲线的平滑度比Post-Norm要明显好。不过Pre-Norm也会削弱“identity shortcut”的传递能力所以现在不少模型会在子层的输出侧再加一个尺度很小的额外归一化相当于把Pre和Post的优点折中了一下。你如果是从头训练一个多模态模型我建议你直接从Pre-Norm起步不要想去挑战Post-Norm的高难度调参。3. 激活函数门控机制的兴起正在改写FFN的默认配置激活函数这个话题在单模态时代基本已经讨论到有点无聊了。ReLU及其变体、GELU、Swish翻来覆去就是那几个人。但多模态大模型把激活函数的问题重新推到了台前因为FFN前馈网络在Transformer里占了参数量的大头而FFN里激活函数的选择直接决定了这个模型能不能有效地对多模态信息进行“路由”。传统FFN长这样先将输入线性变换到一个高维空间过激活函数再变换回原来的维度。比如把4096维的输入映射到11008维过GELU再映射回4096维。这种设计的本质是“单层稀疏激活”——不是所有11008维的神经元都会对所有输入产生响应不同的输入会激活不同的子集。在文本场景里这种稀疏性让FFN承担了一部分“知识记忆”的功能。但在多模态场景问题来了同一个FFN需要同时服务视觉patch和文本token它们的“激活模式”差异非常大简单的高维映射激活函数不一定能有效区分这两种不同的输入类型。于是SwiGLU这一系的门控激活函数就变得特别重要。SwiGLU的计算方式是把输入分成两个分支一个分支过Swish激活另一个分支做门控两者做逐元素乘积。这等于让网络在FFN内部多了一层“软路由”——门控分支可以学到“看到视觉特征时保留哪些通道、看到文本特征时保留哪些通道”。这种通道级的动态选择能力对多模态信息混合处理来说太关键了。3.1 GELU与SwiGLU为什么门控分支对视觉特征更友好GELU本身没什么问题它在激活函数里提供了平滑的梯度比ReLU在负区间的硬零要友好很多。但你仔细想一下GELU本质上仍然是对“单个输入标量”做非线性变换它缺少一种能力根据输入的其他维度来判断“这个维度要不要被激活”。SwiGLU天然就具备这种上下文感知能力因为它接收的是一个向量拆成的两份一份做变换一份做门控门控信号来自输入本身不同维度的组合。视觉patch特征跟文本token特征在空间结构上有一个本质区别视觉特征是“局部相关的”相邻patch维度之间有很强的相关性文本token特征则是“语义离散的”维度之间的相关性弱很多。SwiGLU的门控分支可以针对这种结构性差异做出不同的通道放行决策相当于在FFN内部形成了一个隐式的“模态感知开关”。我实测过一组对比实验同样的多模态训练配置把GELU替换成SwiGLU之后loss下降速度并没有变快多少但最终收敛的泛化指标有稳定提升说明FFN的参数被利用得更充分了。当然SwiGLU不是没有代价。它需要把输入映射到两个不同的中间空间参数量和计算量比普通FFN增加了不少。对于显存紧张的小规模训练你可以在FFN的第二个线性层上做一些低秩近似来压缩也可以把中间维度略为缩小。但我的经验是多模态模型在FFN上的投入是值得的因为信息混合的主要场所就在FFN里这里省了融合能力立刻下降。3.2 激活值动态范围视觉特征带来的“过饱和”问题激活函数还有一个不那么直观但实际很困扰人的问题激活值的动态范围。文本token经过多层Transformer之后激活值的分布相对稳定因为语言本身的统计规律比较整齐。而视觉patch特征经过深层网络后激活值的动态范围经常出现“过饱和”——某几个patch的特征幅度特别大其他patch的特征被压得非常小。这个现象在注意力可视化里很常见就是你说的“attention sink”所有注意力都被少数几个特殊token拽走了。在多模态模型里这个问题会更严重因为图像中的高纹理区域会产生大量高响应的patch特征它们会抢占激活函数的非线性饱和区导致后续层能学到的有效信息变少。处理这个问题有几个思路。一个是在FFN激活之前对激活值做clip或者逐通道缩放把动态范围压到一个可控区间。另一个是增加激活函数的“软饱和”特性比如Mish这类激活函数在正区间几乎线性、负区间平缓的特性可以让高响应的patch特征不至于被无脑放大。还有一个思路是调整注意力分数让视觉patch之间不产生过度的竞争——但这已经属于架构层面了后面我会细说。我自己的经验是多模态模型训练中后期出现loss平台期很多时候不是模型容量不够而是激活函数把某些特征的增益调得过高以至于优化器很难找到更好的局部解。这时候适当调低FFN输出端的scale factor或者换一个更平缓的激活函数平台期可能就突破了。4. 架构一致性不是“能跑通”就行而是每层都在对齐接下来是架构一致性。这个词说起来有点抽象但落到实际就一句话你的模型各个模块之间的“语法”应该是一致的否则信息在传导过程中会出现不兼容。多模态模型最常见的架构不一致问题发生在各模态编码器与融合主干之间。比如说视觉编码器输出的特征是patch序列它的位置编码是绝对位置编码每个patch对应图像上的一块固定区域而文本侧可能是用RoPE旋转位置编码或者ALiBi这类相对位置编码。这两种位置编码机制直接混用会产生一个非常隐蔽的bug语义上对应同一个“位置”的token在视觉侧和文本侧的位置信息表达方式完全不同。模型在融合注意力层里要同时理解两种位置编码语言学起来事倍功半。我曾见过一个多模态项目视觉塔用绝对位置编码文本塔用RoPE融合层使用的是常见注意力机制。训练了十多万步图文检索任务的指标始终上不去。单独看两个塔各自的下游任务能力都很正常一融合就拉胯。后来把视觉塔的位置编码也改成RoPE或者用某种方式把视觉patch的位置映射到与RoPE兼容的旋转坐标里指标很快就突破了。这就是架构一致性的典型教训。4.1 嵌入空间的对齐CLIP式对比损失和生成式损失的协作机制架构一致性还有一个层面是“损失函数与架构的匹配”。多模态模型训练通常不只有一个loss。常见的做法是对比损失拉近图文匹配对的表征再用生成式loss比如文本生成或图像重建来补足细粒度信息。这两个loss对架构的要求不同对比损失希望最终的融合表征是一个紧凑的、判别性强的空间生成式损失希望表征保留足够的生成细节特征空间往往需要更“冗余”。如果你不加处理直接把两个loss加在一起网络会处在一个很尴尬的位置既要让表征空间收敛到判别方向又要保留生成所需的信息。很多做多模态训练的团队都经历过这个“loss打架”的阶段。处理方式我觉得目前比较有共识的做法是“分阶段调度loss”。早期训练阶段用对比损失做主要驱动先把不同模态的表征拉到一个大致对齐的空间里等到对齐的骨架建立起来了再逐步加入生成式损失让模型在已经对齐的骨架上去细化各模态内部的表示。这个过程类似你先教会模型“图像里的猫对应文本里的猫”再让模型去学“猫的纹理和姿态应该被详细编码”。如果反过来或者同时上模型容易顾此失彼最终表征空间既不对齐也不细腻。4.2 位置编码机制的统一RoPE在多模态场景里的适配细节RoPE旋转位置编码现在是语言模型的宠儿它通过旋转矩阵把位置信息注入attention计算中核心优势是能天然泛化到比训练时更长的序列。但这个东西搬到视觉task上不是直接拿来就能用的。因为图像是二维网格结构而RoPE本质上是一维序列的位置旋转。你如果把patch序列按行优先拉平RoPE会认为第1行的第3个patch和第3行的第1个patch距离非常远但它们在图像上其实只有垂直方向的近邻关系。这种距离认知的错误会让视觉注意力受限在错误的范围内。现在很多多模态框架会把二维位置信息拆解成“行位置”和“列位置”两个维度然后分别做旋转编码再把两个旋转角合并到一个高维向量上。这样patch之间的距离度量就同时考虑了行和列方向更符合图像的二维结构直觉。这些细节在单模态文本模型里根本不存在但多模态架构就避不开。另一个细节是patch的大小与位置编码周期之间的关系。图像分辨率一变patch数量就会变有效感受野也跟着变。如果你的位置编码是在固定patch数量下学出来的换一个分辨率推理时新patch的位置就落在了一组“没见过”的旋转角上性能就会掉。解决办法是引入分辨率无关的相对位置偏置或者把patch设计得足够小让位置编码在更大分辨率范围内仍能插值出合理值。这类问题不解决你的模型“训练时全对换分辨率就崩”。4.3 残差连接与尺度控制跨模态信息传导的“高速公路”残差连接在深度网络中的地位不用多说。但在多模态模型里残差连接多了一层含义它是不同模态信息进行“无损叠加”的主要通道。当一个视觉patch的表征和文本token的表征被加到同一个残差流里残差连接承担的不单单是梯度传播还有信息混合。这里我要提醒一个经常被忽略的工程细节残差分支的输出应该保持怎样的尺度在单模态模型和多模态模型里有微妙的不同。单模态模型里残差分支的输出与主分支的输出量级大体接近两者相加不会产生太大波动。但多模态模型里如果视觉塔的特征尺度天然偏大残差分支的输出就会占比过高导致文本token的表征被视觉信号“淹没”模型会退化成“看图说话工具”对文本指令的响应非常弱。所以在源码里你会看到很多多模态模型在残差分支上乘了一个可学习的缩放系数初始值设得非常小比如0.1甚至0.01。这个是故意的。先把跨模态信息的影响压住让模型的文本能力不被破坏再在训练中逐步放大跨模态信息。这跟LoRA微调的思想有点像先确保原模型能力不退化再让新模态信息慢慢融入。5. 当前难点模态竞争、分配倾斜与训练动态的失控说完了归一化、激活函数和架构一致性来谈谈当前多模态大模型真正让人头疼的难点。在我看来很多难点不是单一模块能解决的而是上述几个因素纠缠在一起导致的系统性问题。其中最扎眼的就是“模态竞争”。同一个Transformer主干接收了视觉和文本两种信号它们在attention计算里会竞争同一组K和V的表示空间。如果视觉patch在attention里总是占据主导文本token就没有存在感模型就会变成“视觉主导型”对精细的文本指令不敏感反过来如果文本token过于强势视觉细节信息就会被忽略图像理解能力就上不去。这种竞争不是显式的它不会直接报错而是表现为模型在某个模态上的能力远低于单模态基线。很多团队在训练多模态模型时只盯着联合指标看联合指标涨了就觉得没问题但单独拉出图文检索、纯文本生成、图像分类三个benchmark一看往往有一个模态的能力是明显拖后腿的。这个现象现在越来越被理解为一个结构性的训练不均衡问题。5.1 模态分配倾斜为什么模型总爱“偷懒”走捷径模态分配倾斜这个词听着学术其实本质就是“模型找到了一个偷懒的解”。说具体点就是如果视觉特征的信息熵低、容易判别模型就会倾向于只看视觉特征来预测答案忽略文本里的细粒度信息如果文本里直接写着答案的关键词模型也可能不去看图像直接根据语言先验输出结果。这种“捷径学习”在多模态数据里极其常见因为多模态数据集的标注质量通常不如单模态数据集干净。图文对里的相关性常常是弱相关——文本描述了段落主旨但图像里的很多细节文本根本没提到。模型完全可以通过只学习模式匹配来提升训练集指标但一到需要真正跨模态推理的测试样本上就露馅了。目前学界和工业界都在尝试各种对抗手段。一个思路是在训练中显式添加“模态丢弃”cross-modal contrastive learning里的某些augmentation技巧随机遮挡掉某个模态的部分信息逼模型学会从单一不完整信息中推理另一个思路是设计损失函数惩罚“只依赖单模态就能做出预测”的情况。老实说这两个方向都还不够成熟。我自己训练的时候也会在eval set里特意构造一些“文本误导”样本比如文本描述与图像细节矛盾用这种样本去检查模型是否真的在跨模态推理而不是走捷径。5.2 大规模分布式训练中的数值精度问题BF16与损失缩放说到训练动态的失控数值精度问题值得单独拿出来讲。多模态模型通常比单模态模型更大因为要容纳视觉编码器、文本编码器、融合主干三部分参数。显存压力一上来几乎所有训练都得用混合精度。BF16是目前大模型训练的主流精度方案。它的优点是动态范围跟FP32基本一致不容易直接溢出缺点是尾数精度很低只有8位尾数。对于激活值动态范围很大的视觉特征来说BF16是一个隐藏的风险视觉特征的幅值在不同样本间变化大但BF16的精度在幅值较大时反而更粗糙这就导致视觉特征在低精度表示下“信息折损”比文本特征更严重。很多多模态训练loss不稳定的情况追根到底就是BF16精度下视觉特征的表示误差被多次前向传播放大了。处理办法通常有两个一是对视觉编码器单独的模块保持FP32不参与BF16量化二是在loss缩放和梯度裁剪上做更保守的设置让梯度更新幅度不至于被低精度打到抖动。这两种方法都会多占一些显存但多模态模型本来就值得为视觉塔的数值稳定性多付出一些代价。5.3 数据构成与训练动态的匹配一旦batch组成失衡norm和激活函数都会失真最后一个难点可能出乎很多人意料数据构成直接影响归一化层和激活函数的行为。前面我讲过BatchNorm在多模态场景不适用主要是因为batch统计量会被不同模态样本拉扯。但如果你用的是LayerNorm/RMSNorm这类逐token的归一化是不是就完全没有数据构成的问题了也不是。归一化层统计量的稳定性和激活函数激活模式的合理性都依赖训练数据的分布相对稳定。多模态数据集的构成如果剧烈波动比如一个batch几乎全是自然图像短文本下一个batch几乎全是文档截图长文本模型的归一化层就要不断适应两种不同的输入分布。LayerNorm虽然不计算batch统计量但它是可学习的缩放和偏置模型已经学出来的那套参数在两种数据分布之间反复横跳loss曲线就会周期性震荡。这不是模型架构本身的问题是“数据调度”的问题。我见过的多模态数据pipeline一般都会做很严格的数据配比控制确保每个batch里不同数据源的比例大体稳定不会某一步全是A类型、下一步全是B类型。如果你是从零自己搭训练流程务必在sample level做一次distribution smoothing。这一点对训练动态的影响比调整学习率策略还大。6. 给正在训练多模态模型的你一份可操作的配置检查清单聊了这么多原理和踩坑经验最后汇总一份我自己跑多模态实验时总会过一遍的检查清单。这不算什么独家秘密但确实能帮你避掉大部分“看起来很难查其实很基础”的问题。视觉塔与语言塔的归一化策略不要简单共享。视觉侧优先为LayerNorm或等效的完整标准化方案语言侧可以沿用你预训练模型自带的RMSNorm设置但在融合主干里建议保留统一且更稳的标准化方式。SwiGLU作为FFN默认激活函数确实能提升多模态融合的上限。如果计算资源紧优先压低中间维度而不是退回GELU如果训练初期出现震荡检查输入层的scale factor和输出层的残差缩放系数。位置编码一致性是绝对的高优先项。视觉patch和文本token不能各用一套无法互相理解的位置表达。在做任何融合之前先确认两者在“位置语义”上能对应上。如果你不确定最简单的办法是所有模态统一采用同一种相对位置方案然后用可学习的投影层做适配。训练数据构成做平稳化处理。在构建batch时确保不同数据源的配比大致稳定不要出现“大起大落”的分布波动。这条做不到上面所有架构优化都会被打折扣。混合精度训练时对视觉编码器单独评估一下BF16带来的损失。如果视觉任务指标下降超过可接受区间给视觉塔单独开FP32通道或者引入部分计算层分离不要为了省显存低估视觉特征对精度的敏感性。我个人的实际体会是多模态大模型的能力上限很大程度上是由这些“地基”细节决定的。相比继续往里堆参数、堆数据量级把归一化、激活函数、架构一致性这些环节打磨好收益往往出人意料地高。很多人训练了很大规模的多模态模型最终效果却不如一些参数量小但工程细节打磨精良的模型差距就在这里。最后一句话总结这段经历多模态大模型的工程复杂度远不是单模态模型加一个视觉编码器那么简单。你越是早一点意识到“每个基础模块都需要重新审视”就越少走弯路。
阅读完成 · 觉得有帮助?