apply_chat_template returns all-zero assistant_masks for multimodal inputs

当你在 Transformers 里对多模态对话(含 image/video/audio)调用 apply_chat_template 并开启 return_assistant_tokens_mask=True 时,如果图像占位 token 被展开,assistant mask 会全为 0,优先排查

快速结论:当你在 Transformers 里对多模态对话(含 image/video/audio)调用 apply_chat_template 并开启 return_assistant_tokens_mask=True 时,如果图像占位 token 被展开,assistant mask 会全为 0,优先排查 offset_mapping 与 generation_indices 的字符索引是否错位。

适用环境:Issue 中确认的受影响环境为 transformers==5.3.0,模型示例为 Qwen/Qwen3.5-0.8B,使用 AutoProcessor。后续评论提到在 5.17 仍可复现。操作系统、Python、CUDA、显卡型号在 Issue 中未给出。

最快修复方案:暂无确认的一步修复方案。维护者明确表示修复会并入更大的重构 PR,不倾向单独合并 workaround。可优先尝试 Issue 评论中提到的临时方案(见下方解决步骤),但均非官方已验证修复。

注意事项:该 Issue 被标记为 WIP / bug,最终仍未在讨论链中给出正式合并的修复。评论中的替代方案都依赖模板中的特定头部/结束 token,换模型或换 chat template 后需要重新适配,且不保证与旧版本行为一致。

问题场景

用户在 Transformers 中使用 AutoProcessor.from_pretrained(...) 加载多模态模型(例如 Qwen/Qwen3.5-0.8B),构造包含 image 与 text 混排的 messages,然后调用 processor.apply_chat_template(..., tokenize=True, return_dict=True, return_tensors="pt", return_assistant_tokens_mask=True)。期望得到 assistant 回复位置为 1 的 assistant_masks,实际拿到的是全 0 的 mask,导致后续基于 mask 的训练/微调(例如 LoRA 微调)拿不到正确的 assistant token 位置。

报错原文

apply_chat_template returns all-zero assistant_masks for multimodal inputs

Actual behavior:
assistant_masks is all zeros.

Expected behavior:
The tokens corresponding to the assistant response should be marked with 1 in assistant_masks.

原因分析

Issue 作者与维护者已确认根因:generation_indices 是基于 render_jinja_template 返回的原始(未展开)文本 prompt 计算的;而 offset_mapping 来自处理器/分词器路径对多模态占位 token 展开之后的文本(例如 <|image_pad|> 会被复制成 N 份)。两者字符坐标空间不一致,导致用 bisect_left 在 offset 中查找 assistant 起止字符位置时完全找不到对应区间,最终 assistant_masks 退化为全 0。

环境排查

  • 确认 transformers 版本:Issue 报错版本为 5.3.0,评论提到 5.17 仍复现,建议先记录自己的版本。
  • 确认使用的是 AutoProcessor,并且输入 messages 中包含 image/video/audio 多模态内容。
  • 确认 chat template 中是否存在类似 <|image_pad|> 会被展开为多份的占位 token。
  • 确认调用参数中开启了 return_assistant_tokens_mask=True
  • Python、CUDA、PyTorch、显卡型号等环境项在本 Issue 中未提供,排查时按自身环境记录即可,不必强行对齐。

解决步骤

  1. 先用最小复现脚本确认问题:构造含 image + text 的 user 消息与 assistant 文本消息,调用 apply_chat_template 并打印 inputs["assistant_masks"],若全为 0 则命中本问题。
  2. 可优先尝试社区评论中的 stopgap 方案:改用正则匹配方式,直接从已 tokenize 的 input_ids 中提取 assistant 区间并自行生成 mask。Gemma 风格模板匹配 "<|turn>model\n""<turn|>" 之间的 token;Qwen3 instruction 风格匹配 "<|im_start|>assistant\n<think>\n\n</think>\n\n"<|im_end|> 之间的 token。参考实现:LUOXIAO92/MultimodalAssistantMask。该方案的头部/结束 token 可自行替换以适配不同模板。
  3. 维护者在讨论中给出的修复方向(尚未合并):用 processor(**inputs, return_text_replacement_offsets=True).text_replacement_offsets 重新调整字符索引,从而避免重新 tokenize 占位符并推断其长度。这属于后续重构的一部分。
  4. 如果不想自行实现 mask 生成,可关注上游 PR 进展;在正式修复合并前,不要依赖当前版本的 return_assistant_tokens_mask=True 在多模态输入下输出正确 mask。

验证方法

在修复或采用 workaround 后,重新运行最小复现脚本,确认 inputs["assistant_masks"] 不再全为 0,且为 1 的位置正好对应 assistant 回复的 token 区间;对照 input_ids 中 assistant 文本对应的 token,逐段核对 mask 起始与结束位置是否正确。

参考来源

huggingface/transformers #44521

GamsGo AI

AI 工具推荐

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

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

了解 GamsGo AI

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

这个方案解决了吗?

celebrityanime
celebrityanime
文章: 25297

发表回复

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