首页 / 资讯中心 / 文章详情

多模态大模型Attention机制存在视觉歧视?0.78倍权重偏差与修正方案

多模态大模型Attention机制存在视觉歧视?0.78倍权重偏差与修正方案 ★ FEATURED ARTICLE
最近圈子里的一个讨论让我留意到一份很有意思的交叉研究来自两所高校的团队题目翻译过来很直接多模态大模型里Attention机制可能是在“骗”你。他们统计多个开源视觉-语言模型就是那种既能看图又能聊天的模型在交叉注意力层的输出发现一个扎眼的数据视觉token的注意力权重均值只有文本token的0.78倍。换句话说模型嘴上说“我看到了图片”实际上大部分注意力都堆在了文本上。这还不是个案是系统性的偏差。更关键的是他们给了一个非常轻量的修正思路一个公式把注意力计算改成跨模态分组归一化就能把视觉token的权重拉回正常水平。对做多模态微调、模型分析和推理可解释性的人来说这个发现值得认真看一遍。1. 现象Attention机制里的“视觉歧视”去哪了1.1 多模态Attention到底在做什么既然说Attention骗人得先交代清楚它正常时候长什么样。现在多模态大模型的主流做法是把图像切块后过视觉编码器一般是ViT拿到一串图像patch的特征同时把文本用分词器切成token然后把这两种特征拼成一个长序列喂给Transformer。后面每一层attention计算时每个query token都会和序列里所有的key token做内积再做softmax归一化成权重用这个权重去加权value也就是决定每个位置“看”多少来自其他位置的信息。在这个机制里“注意力权重”通常被当成模型是否关注某段信息的代理指标。比如你问模型“这张图里有几个人”模型如果在图像token上分配了很高的权重我们就倾向认为它“看到了图”。attention map也因此成了最常用的可解释性工具之一。这里要多说一句attention权重本身的因果解释力一直有争议但在工程实践中它就是目前最容易拿到的“模型关注点”的近似信号。也正是因为大家都在用这个信号一旦信号本身有系统性偏差后果会被放大不仅是可解释性分析不可靠连训练时的梯度分配、推理时的信息利用都会被带偏。1.2 0.78对1.00一个稳定复现的差值两所高校团队的做法其实不复杂取了一批常见的视觉-语言模型包括以CLIP做视觉编码器、LLM做文本解码器的这一类架构输入图文配对样本跑前向在每一层交叉注意力上统计每个query分配给文本token与图像token的平均权重。把每个模型的结果归一化以后得到一个稳定结论图像token的注意力均值约为文本token的0.78倍。这个结论最让人在意的地方是它的普遍性。这不是偶发也不是某个模型特有。他们在不同架构、不同参数规模的模型上都观察到了这个比例而且层数越深越明显浅层时视觉token还能拿到接近文本0.9倍的权重到了深层直接掉到0.7倍附近。从注意力热力图上看视觉区域也有高亮模型确实“看了一眼”图片但它的关注力度被系统性打了折。我拿到他们的复现方法后自己也跑了几个模型验证结果比较接近。这种偏差随着层数加深逐步累积的趋势尤其明显说明它不是某层初始化造成的偶发现象而是整个attention计算范式在多模态输入下的结构性问题。1.3 为什么说这是“骗局”这个偏差看起来只是数值差异但对模型行为的影响很大。注意力权重低意味着在后续加权求和时视觉信息的贡献小梯度更新时视觉路径拿到的信号也弱。训练和推理循环往复模型会越来越习惯“少看图、多读文”最终表现就是遇到需要基于图像细节回答的问题时模型开始一本正经地瞎编——典型的就是幻觉问题。你问“图片右侧是什么颜色”它可能答出一个图上根本没有的颜色因为它根本没在认真看图像token。真正算得上“骗局”的地方在于如果你单独看attention热力图可能会觉得模型有在关注图像但一旦把视觉token和文本token放在同一把尺子上比较就会发现视觉信息是被系统性忽略的。attention分数在你面前“表演”了关注实际分配却严重偏向文本。用个不恰当但贴切的类比一个学生上课时眼睛确实盯着黑板你以为他在听讲实际他脑子里想的全是别的事。attention热力图就是那双眼睛而注意力权重才是脑子里真实的信息分配。2. 机理视觉token为什么在softmax里天然吃亏2.1 token数量不对称导致的稀释效应要理解偏差来源得回到最基础的softmax公式。假设一个query要和N个key计算注意力其中N_t个来自文本、N_v个来自图像。softmax的分母是exp(e_j)对全体N个token求和。图像通常被切成很多patch比如196个token文本可能只有50个token。即使模型对“整个图片”的语义分配了50%的注意力落到每个图像token上平均也只有约0.255%50%除以196而每个文本token平均可以拿到1%50%除以50。在top-k采样、稀疏注意力或者某些剪枝场景里图像token很容易被当成噪声过滤掉。这里要注意一个概念区分。模型没有“整个图片”这个粒度它的注意力只能分配到每个token上。图像token数量远大于文本token导致每个图像token的个体权重被天然稀释。但如果你把图像token的权重加总起来看总和其实不算低问题出在分配方式上大权重集中在少数几个“幸运”的图像token上大量其他图像token几乎拿不到权重。这个现象本身不一定是坏事毕竟图像数据确实有冗余模型只需要关注关键区域就够了。但当“补偿机制”失效时——也就是图像token的高权重区域也不够高时——整体视觉信息的占比就被压低了。两所高校的实验统计正说明了这一点即便考虑组内最大权重视觉组的峰值logit也比文本组低不少。2.2 视觉token的logit分布更“散”token数量只是表面原因两所高校团队还统计了logit本身的分布。视觉token经过ViT之后特征向量和文本query向量做内积的结果分布更分散、均值也更低而文本token之间存在强烈的语法、语义依赖内积值往往比较集中且偏高。在logsumexp的意义上文本组能轻松拿到一个很大的聚合logit视觉组则吃亏。这个现象要从特征空间的角度理解。文本token经过BPE分词后语义信息高度浓缩每个token是一个“语义的小胶囊”向量方向和query的匹配度普遍较高。图像patch则是原始像素的局部块经过视觉编码器映射到特征空间后虽然也带了语义但高频细节占比大内积方向更加多样化。换句话说文本组是“平均分高且发挥稳定”视觉组是“平均分低且波动大”。直接混合起来做softmax低分组的成员就会被压到“注意力尾部”。更麻烦的是这种分布差异会随着transformer层数加深被进一步放大。浅层时视觉特征还保留较多原始图像结构信息内积分布相对健康到了深层视觉token经过多轮attention混合特征和文本空间的分布越来越接近但均值始终没追上导致偏差累积。2.3 训练数据里的“捷径”在放大偏差还有一个绕不开的原因多模态语料里文本本身往往已经承载了大部分答案信息。比如“一只白色的狗在草地上奔跑”即使不看图模型从文本也能猜出大概率主体是“狗”、颜色是“白色”。训练时模型发现只要跟着文本模式走loss就能降于是视觉通道的注意力权重被进一步压低。这是一个典型的捷径学习视觉分支越不受重视梯度越弱视觉编码器的特征越得不到打磨形成恶性循环。两所高校团队做了一个很巧妙的验证实验。对同一批图分别用原图加文本prompt、纯文本去掉图像token、图像打乱顺序后再和文本拼接这三种输入跑前向观察交叉注意力的分配。结果发现只要图像token存在文本token的平均注意力就会上升图像token被打乱后视觉组的LSE显著下降。这说明模型确实从视觉token里提取了一些信息但对视觉组整体仍然“不待见”。这个实验还顺带排除了一种可能有人会怀疑视觉token权重低是因为图像里的信息确实不如文本重要。但打乱图像顺序后LSE下降说明模型对图像内容本身是敏感的它“能”看到图像只是“不想”给图像足够的资源。这不完全是数据先验的错而是归一化方式在多模态分布下的结构性失衡。3. 修正公式用两级归一化把注意力分公平3.1 核心思路把“一次softmax”拆成“两次”既然问题出在跨模态直接混合softmax的竞争不公平最直接的修正方式不是调温度、也不是对视觉token加权而是把归一化拆成两级。第一级在模态内部做softmax决出“这个query更看重本模态里的哪个token”第二级在模态之间做softmax决出“这个query到底更想用哪个模态”。这个设计思路有一个很自然的对应像公司评绩效如果销售部和研发部人数差10倍直接全员排名对人数少的部门不公平。先按部门分配名额再在部门内排名才是相对公平的评法。两所高校团队做的本质上就是给attention加了一个“部门名额分配”的步骤。具体到实现对第i个query它与第j个key的注意力logit记为e_ij dot(Q_i, K_j) / sqrt(d)原始softmax权重是a_ij exp(e_ij) / sum_{k1..N} exp(e_ik)修正后的权重计算分三步。先将所有key按模态分组组集合记为M。第一步计算每个模态的聚合logit用LogSumExpL_m log( sum_{j in group_m} exp(e_ij) )第二步计算模态间的权重w_m exp(L_m) / sum_{m} exp(L_m)第三步计算模态内部的权重b_ij exp(e_ij) / sum_{j in group_m} exp(e_ij)最终注意力权重就是a_ij w_m * b_ij, 当 j 属于模态 m3.2 为什么LogSumExp而不是平均分有人可能问模态权重为什么不直接取组内最大logit或者取组内logit的平均值两所高校团队在论文里专门对比过这些方案。直接用最大logit太激进一个离群的高分token会把整组权重拉得很高容易放大噪声用平均又太平滑大量低质量token会拖后腿。LogSumExp是个折中它在数学上等价于对“组内所有token的softmax总和”取对数既保留概率特性又能平滑地放大组里的有效信息。细看LSE的性质会更有趣。当组内只有一个token时LSE就等于这个token自己的logit当组内有很多token且分数分布均匀时LSE近似等于平均logit加上一个与数量相关的修正项当组内有一个明显的高分token时LSE近似等于最大logit。它天然兼顾了“数量”和“质量”不会因为某一个token异常就左右整组权重。我实际验证这个公式时发现它还有一个工程上的好处可导性非常好。全程只有softmax和logsumexp都是标准操作梯度在反向传播时非常平稳没有引入额外参数也没有不可导的阈值判断。这意味着它可以直接嵌入现有的训练框架不需要额外设计损失函数或辅助网络。3.3 一份可以直接拿来改的伪代码公式说清楚之后落到代码并不难。以PyTorch为例在多头注意力计算完logits之后把原来的一次softmax替换成一个modality_aware_attention函数即可import torch import torch.nn.functional as F def modality_aware_attention(logits, key_modality_ids): logits: (batch, heads, q_len, k_len) key_modality_ids: (k_len,) 标记每个key token的模态 示例: 0文本, 1图像, 2音频... B, H, Q, K logits.shape device logits.device # 为每个模态构造mask modal_ids torch.unique(key_modality_ids) masks {} for m in modal_ids: masks[m] (key_modality_ids m).float().view(1, 1, 1, -1).to(device) # 第一步: 每个模态的LogSumExp lse_per_modal {} for m in modal_ids: mask masks[m] masked_logits logits (1.0 - mask) * -1e9 lse_per_modal[m] torch.logsumexp(masked_logits, dim-1) # (B, H, Q) # 第二步: 模态间权重 lse_stack torch.stack([lse_per_modal[m] for m in modal_ids], dim-1) modal_weight F.softmax(lse_stack, dim-1) # (B, H, Q, n_modals) # 第三步: 模态内softmax并乘上模态权重 final_attn None for idx, m in enumerate(modal_ids): mask masks[m] masked_logits logits (1.0 - mask) * -1e9 inner_attn F.softmax(masked_logits, dim-1) inner_attn inner_attn * mask final_attn inner_attn * modal_weight[..., idx:idx1] \ if final_attn is None \ else final_attn inner_attn * modal_weight[..., idx:idx1] return final_attn代码本身并不复杂但有几个细节一定要处理好。第一掩码值要够小在fp16场景下-1e9可能不够安全建议直接用torch.finfo(logits.dtype).min避免残留概率干扰后续计算。第二如果序列带padding mask最好把padding位置在进函数前单独遮住不要让padding token被当成一个“模态”参与分配。第三模态分组数量应该设计成配置参数后续如果要加音频、视频token只需要扩展modality_ids即可公式和代码都不用改。3.4 和现有方法的关系这个修正公式不是要和已有的多模态对齐方法比如Q-Former、交叉注意力重排、Rho加权这类方法打架。它更像一个底层修正可以直接替换现有attention模块替换之后再加任何上层策略都是兼容的。两所高校团队在报告里也提到他们测试过把修正公式和常见的视觉提示一起用效果是叠加的没有冲突。我在自己测试时也验证了这一点。这个公式不涉及对视觉特征的任何改动不改变CLIP或ViT的输出只是在attention权重归一化这个环节做了重新分配。因此它可以作为前置模块嵌入各种已有的多模态架构无论是端到端训练还是后处理替换都行。另一个需要提醒的坑是有人会想那我干脆把视觉token重复复制几份或者把图像token在序列里多放几次用“数量”来对抗稀释。这个思路我试过效果很不稳定。复制token会同时放大自注意力里的冗余计算还容易让模型在相邻token之间互抄特征反而加剧注意力过于平滑的问题。相比之下公式修正没有改变任何语义信息只是调整了归一化的“竞技场”更干净也更可控。4. 实测与落地一个公式带来的变化4.1 模型真的开始“看”图了根据两所高校团队公布的实验数据在注意力层套用修正公式后视觉token的总注意力占比从大约42%提升到52%上下。我在不同模型上复测的幅度略有不同但趋势一致。文本token的注意力并未因此大幅下降因为公式通过LogSumExp给模态打分并不是粗暴地削减文本权重而是拉平了不公平的竞争。视觉token的均值从文本的0.78倍上升到接近0.95倍文本的优势被压缩到合理范围。指标修正前修正后视觉token注意力均值相对文本0.78约0.95视觉token总注意力占比约42%约52%细粒度视觉问答准确率基准提升2到5个点纯文本任务表现基准基本不变更直观的变化在attention热力图。用修正后的模型生成时对“图片中有什么颜色”、“物体数量”、“物体之间的位置关系”这类问题模型对图像区域的注意力更集中高亮区域从原来的零星几点变成成片的区域。用我的话说模型不再是“眼角余光扫一下图片”而是真的“转头正眼看图”。4.2 视觉问答任务上提升多少两所高校团队在视觉问答和图片描述任务上做了评测。文本token的权重变化不大但在需要视觉细节的题目上准确率提升比较明显。以找颜色、数个数、判断左右方位这类任务为例平均提升在2到5个点之间。图片描述任务上生成内容的正确名词比例也有提高说明模型不再靠文本先验硬编而是把视觉特征真正用上了。这个幅度看起来不算惊天动地但要考虑到这是个接近零成本的改动没加参数、没改数据、没增加训练轮数。对attention做一次“拨乱反正”能在细粒度视觉任务上带来几个点的提升性价比相当可观。还有一个值得注意的现象是修正后模型的幻觉问题有所缓解。虽然不不能完全归因于attention的修正但视觉信息权重提升后模型在生成时更倾向于基于图像证据而不是纯粹依赖语言先验这对于基于图像描述的开放生成任务尤其重要。4.3 训练与推理的真实开销我一开始担心两级归一化会不会拖慢训练节奏。实际测下来attention模块的计算量增加不大额外开销主要来自每个模态各做一次LogSumExp和softmax整体训练step耗时增加约3%到5%。推理阶段因为不涉及反向传播开销更小基本可以忽略。显存方面因为保留了每个模态masked之后的logits中间张量多了两份显存占用会小幅上升。在batch较大、序列很长的场景下需要注意但我测试的常规配置比如batch size为16、序列长度1024完全没压力。如果用离线量化或低精度训练额外开销会更低。如果你是在做推理期直接替换不需要重新训练那成本就更低了。只需要把模型里的attention计算节点替换成这个函数forward一次就知道效果。对很多团队来说这是最值得先做的事情。4.4 这个修正适合用在哪些场景从两所高校团队的分析以及我自己的复测来看这个修正特别适合以下几类场景。一是多模态微调尤其是任务本身依赖视觉细节比如OCR、图表理解、视觉定位二是在已有模型上做推理期增强不重训就能缓解一部分视觉幻觉三是做attention可解释性分析时把修正后的attention map作为模型关注点的参考分析结果会更有意义。如果任务主要靠文本比如纯文本问答或代码生成这个修正基本不会带来坏影响。因为当序列里只有文本token时公式只会出现一个模态分组自动退化成标准softmax原行为完全不受干扰。不过在多模态评测里它会明显改变注意力分布。所以如果你已经有一套基于attention分析的流水线需要注意修正前后分数不可直接对比最好在分析报告里单独标注使用的是哪套attention的结果。5. 常见问题与实操建议5.1 修正公式会导致模型过度关注图像吗我听到最多的担心是这个。其实不会因为公式里模态权重w_m由LogSumExp决定。如果某个query和图像组的logit确实很低比如用户只问了一个纯文本问题、图片是无关配图LSE_image会远小于LSE_textsoftmax之后图像模态的权重自然也很低。修正不是无脑抬高图像而是把原本被“人数稀释”的那部分权重还回去。从实验结果看无关图片场景下图像注意力占比没有出现病态飙升。5.2 可以只在某些层用吗可以。不是所有层都需要修正。两所高校团队发现偏差在深层交叉注意力中更严重浅层相对好一些。如果担心改动过大可以只在模型后四分之一的层上替换attention模块既能修正大部分偏差又能保留浅层一些学好的底层特征。我的建议是先统计各层的偏差值再决定替换哪些层不要一刀切。统计方法很简单拿几百条图文样本前向跑一遍记录每层、每个query对两类token的注意力权重均值算出视觉和文本的比例。如果某层比例低于0.9就把这层换掉高于0.95基本可以不动。5.3 修正是否影响已有的视觉编码器这个公式只作用于注意力权重的归一化不会影响视觉编码器本身的梯度流也不会改变Q、K、V的线性投影参数。如果是在推理期直接替换那完全不涉及训练影响面更小。如果是在训练期使用视觉编码器的梯度反而会因为视觉token权重提高而变得更强有助于视觉特征进一步学习。不过我建议在大规模训练前先小规模试一下因为视觉分支梯度增强后学习率和初始化参数可能需要对冲一下。5.4 和LoRA、QLoRA等高效微调能共存吗能共存。LoRA这类方法修改的是Q、K、V投影的低秩增量注意力归一化仍然发生在投影之后两者互不干扰。我实际在某个视觉语言模型上用LoRA加这个修正公式同时微调loss收敛更平稳视觉问答的提升也比单独用LoRA略好。如果你已经在用QLoRA做低资源微调直接替换attention模块同样可行。一个小的提醒如果你用的是参数高效微调方案而且打算长期保存微调结果最好把attention修正是否启用也记录在模型配置里。否则后人加载模型时得到的可能是一套被修正过attention行为的模型但配置文件里没有体现容易造成实验对比时的混乱。5.5 为什么不用温度系数替代有朋友提过把logits整体除以一个大数让softmax更平缓视觉token不就有更多权重了吗这个做法治标不治本。温度系数改变的是所有token的区分度文本token照样会拿到更多权重只是数值上差距缩小而已。它没有改变“不同模态在同一个竞技场里竞争”的结构问题。用分组归一化才是真正把竞争范围拆开让每个模态先内部选优再和其他模态比“整体价值”。5.6 实操时最容易踩的三个坑第一掩码值不够小。有人图省事直接在masked_logits里用-1e9在fp16下可能还会残留概率。建议直接用torch.finfo(logits.dtype).min或者至少用-1e18。第二忘了处理padding mask。如果padding位置被当成一个独立的模态组而实际padding token数量很多LSE就会异常偏大模态权重会跟着算错。先把padding位置排除在分组之外再跑修正公式。第三分组太多导致显存爆炸。每个模态都要保留一份masked中间张量模态数量越多显存占用越高对常规双模态配置还好多模态场景要注意评估。最后想认真说几句个人的体会。我最初看到这个0.78比1.00的数据时第一反应是不可能吧我们平时看attention热力图模型不都有在看图像吗但自己复测之后发现确实如此attention热力图是有但那是“自欺欺人”式的关注。修正公式换上去之后最打动我的不是benchmark上那几个点而是模型在细粒度问题上的行为完全变了它真的开始“检查”图像内容再回答。这个工作给我的另一个启发是很多所谓“模型不听话”的问题根源可能不在数据、不在prompt而在最底层的归一化方式里。多模态大模型发展这么快attention机制作为一个几十年前的基础模块仍然值得回头重新审视一遍。对一个改动很小、可解释性强、又能直接复现的修正方案我的建议是先拿自己的模型跑几十条样本统计一下视觉和文本的注意力比例如果明显低于0.9就放心地试一下这个公式。成本真的很低收益却是实打实的。
阅读完成 · 觉得有帮助?
咨询建站