Major Bug in parallel generation / left padding.

这个报错通常出现在对多模态(含图片)提示词做左填充(left padding)后调用 generate 的场景,且当填充后的总长度满足 total_padded_length % 32 == 1 时触发。优先排查填充后总长度是否命中该条件,并对比同一序列在无填充与有填充下的 prefill logi

快速结论:这个报错通常出现在对多模态(含图片)提示词做左填充(left padding)后调用 generate 的场景,且当填充后的总长度满足 total_padded_length % 32 == 1 时触发。优先排查填充后总长度是否命中该条件,并对比同一序列在无填充与有填充下的 prefill logits。

适用环境:Issue 中确认环境为 transformers 5.13.1 / 5.14.1(两者结果相同),Python 3.12.13,PyTorch 2.12.0+cu130,单张 NVIDIA RTX PRO 6000 Blackwell Workstation Edition,bf16,SDPA 注意力,模型为 google/gemma-4-12B-it,device_map=”auto”,未使用分布式或并行设置。

最快修复方案:暂无确认的一步修复方案。Issue 中只给出了定位结果(触发条件为填充后总长度满足 % 32 == 1)以及一套待执行的隔离诊断计划,没有已验证通过并合并的代码修复。

注意事项:不要直接把“对 sequence_length % 32 做永久特判”当作正式修复;维护者明确表示在定位到首个错误发生的环节(多模态准备、mask 构造或 PyTorch SDPA 后端)之前,不应急于加这种特殊情况处理。该问题只在含图片输入时复现,纯文本提示在已测试的各个填充长度下未受影响。

问题场景

使用 Gemma4UnifiedForConditionalGeneration(google/gemma-4-12B-it,bf16,SDPA)对多模态提示词(282 个 token,含一张图片)做左填充后进行生成。当填充长度为 7(即填充后总长度为 289)时,prefill logits 出现异常;后续定位表明真正的触发条件不是“填充长度为 7”,而是填充后总长度满足 total_padded_length % 32 == 1。该现象在 batch=1 与 batch=2 左填充批次中一致复现,未填充的那一行保持正常。用户侧表现为:greedy 生成在填充行上静默输出退化的描述式文本,而不是正常的指令跟随回复,容易误认为是采样波动。

报错原文

Major Bug in parallel generation / left padding.

B. padded, batch=1, pad=6    max|dLogit|=  0.5000 mean=0.13692 argmax_flipped=False
B. padded, batch=1, pad=7    max|dLogit|= 40.1875 mean=11.57746 argmax_flipped=True
    solo top5: 'I':27.25  'The':27.25  'You':22.62  'There':21.12  'In':21.00
    this top5: '

原因分析

可能原因:填充后的总长度落在 % 32 == 1 这一对齐边界上时,模型内部某个环节(多模态特征插入、mask 构造、multimodal block/group ID 生成或所选 PyTorch SDPA 后端)对有效 token 的处理出现偏差,导致 prefill 阶段的 logits 被破坏。Issue 中已排除的项包括:

  • 不是批次大小问题:batch=1 的 pad=7 与 batch=2 中较短行填充 7 的表现一致。
  • 不是 collation 问题:input_ids、attention_mask、token type ids 都是同步填充,填充行后缀与单独编码逐字节一致,position_ids 跟随 attention mask。
  • 不是渐进数值噪声:其他填充长度(1–6、8、9、15、16、63、64)都保持在 bf16 抖动范围内,只有命中 % 32 == 1 时出现离散式跳变。
  • 纯文本提示在所有已测填充长度下不受影响,图片输入是复现的必要条件。

截至 Issue 关闭时,首个错误值究竟源自 Gemma 4 的多模态准备、mask 构造还是 PyTorch SDPA 后端,尚未最终确定。

环境排查

  • transformers 版本:确认是否为 5.13.1 或 5.14.1(两者在本脚本上逐位相同)。
  • Python 版本:3.12.13。
  • PyTorch 版本:2.12.0+cu130(CUDA),确认实际使用的注意力后端。
  • 注意力实现:是否使用 SDPA;建议同时对比 eager 与 SDPA。
  • 显卡:RTX PRO 6000 Blackwell;如有条件,在非 Blackwell 架构上交叉验证。
  • 精度:bf16;建议同时用 float32 对比,以区分数值与结构性问题。
  • 模型与处理器:google/gemma-4-12B-it 及其配套 processor。
  • 输入类型:确认提示词是否包含图片;纯文本在已测长度下不复现。
  • 填充后总长度:计算 total_padded_length % 32,确认是否等于 1。

解决步骤

  1. 先复现并确认触发条件:用相同提示词与图片,在 batch=1 下扫描填充长度,记录每个长度下的 max|dLogit| 是否跳到 ~40 且 argmax 翻转;同时记录填充后总长度,确认触发点是否都满足 total_padded_length % 32 == 1。
  2. 独立变化提示词长度与填充长度(例如分别覆盖 31、32、33;63、64、65;95、96、97;287、288、289、290),确认触发因素是“填充后总长度”而非“某个固定填充量”。
  3. 在 processor 输出层逐项对比填充与未填充:input_ids、attention_mask、mm_token_type_ids、图片位置输入,确认有效 token 的一一对应关系是否被破坏。
  4. 在多模态准备阶段对比:投影后的图片特征、图片占位符 mask、插入图片特征后的 embedding、以及(如可开启)每层输入,定位是否在进入语言模型前就已经出现差异。
  5. 在位置与 mask 准备阶段对比:有效 token 的 position_ids、生成的多模态 block 序列 ID、全局 attention mask、滑动窗口 attention mask。
  6. 若 embedding 与 mask 在有效 token 坐标上等价,则固定同一组 Q、K、V 与完全物化的 attention mask,分别用 eager、SDPA 以及支持的各个 SDPA 后端、bf16 与 float32 对比,判断问题是否可缩减为独立的 PyTorch SDPA 复现。
  7. 根据以上结果确定修复层级:若差异出现在语言模型之前,修多模态特征插入或序列对齐元数据;若等价有效 token 坐标上的 mask 不一致,修 padding-aware 的 mask 或 block ID 构造;若 eager 正确而 SDPA 错误,向 PyTorch 上游提交最小复现;若两者都失败,则将修复限制在 Gemma 4 模型路径内。
  8. 在定位完成前,如需临时缓解,应严格限定在已确认受影响的后端/设备/dtype/路径上,仅作用于受影响的多模态 prefill,通过一次性警告显式提示,并在合入上游修复后可移除;不要在模型配置上做全局改动,以免影响并发请求的线程安全。

验证方法

对同一非填充 token 序列与多模态输入,加上被 mask 的左填充后,应满足:多模态特征位置、有效 token 的 position IDs、多模态 block/group IDs、有效 token 间的注意力可见性、最终 token logits(除 dtype 级数值抖动外)以及确定性 greedy 输出均不发生实质变化。具体可检查填充与未填充下 max|dLogit| 回到 bf16 抖动水平(约 0.5–0.66),argmax 不再翻转,填充行的 greedy 输出与单独运行时一致。建议补充 fast deterministic unit test(基于 tiny Gemma 4 配置)以覆盖 % 32 == 1 附近的对齐边界,防止回归。

参考来源

huggingface/transformers #47651

GamsGo AI

AI 工具推荐

想把多个 AI 模型放在一个入口?

GamsGo AI 集成 ChatGPT、DeepSeek、Gemini、Claude、Midjourney、Veo 等常用模型,适合写作、绘图、视频和日常 AI 工作流。

了解 GamsGo AI

推广链接:通过此链接购买,我可能获得佣金,不影响你的价格。

这个方案解决了吗?

celebrityanime
celebrityanime
文章: 29164

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注