快速结论:当你在 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 中未提供,排查时按自身环境记录即可,不必强行对齐。
解决步骤
- 先用最小复现脚本确认问题:构造含 image + text 的 user 消息与 assistant 文本消息,调用
apply_chat_template并打印inputs["assistant_masks"],若全为 0 则命中本问题。 - 可优先尝试社区评论中的 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 可自行替换以适配不同模板。 - 维护者在讨论中给出的修复方向(尚未合并):用
processor(**inputs, return_text_replacement_offsets=True).text_replacement_offsets重新调整字符索引,从而避免重新 tokenize 占位符并推断其长度。这属于后续重构的一部分。 - 如果不想自行实现 mask 生成,可关注上游 PR 进展;在正式修复合并前,不要依赖当前版本的
return_assistant_tokens_mask=True在多模态输入下输出正确 mask。
验证方法
在修复或采用 workaround 后,重新运行最小复现脚本,确认 inputs["assistant_masks"] 不再全为 0,且为 1 的位置正好对应 assistant 回复的 token 区间;对照 input_ids 中 assistant 文本对应的 token,逐段核对 mask 起始与结束位置是否正确。
参考来源
huggingface/transformers #44521
AI 工具推荐
想把多个 AI 模型放在一个入口?
GamsGo AI 集成 ChatGPT、DeepSeek、Gemini、Claude、Midjourney、Veo 等常用模型,适合写作、绘图、视频和日常 AI 工作流。
推广链接:通过此链接购买,我可能获得佣金,不影响你的价格。
这个方案解决了吗?
可以继续搜索完整报错,或查看同一工具的其他排查指南。
![[Bug]: Drop down menu of ´Add Lora to prompt´ does nont have a ´none´ selection and always charge the last Lora selected](https://www.chat-gpts.plus/wp-content/uploads/2026/09/9041-6fefaf96-768x403.jpg)

