Prhub

#42965 [BUGFIX] Multimodal benchmark with MistralTokenizer

原始 PR 作者 juliendenize 合并时间 2026-05-28 20:36 文件变更 1 提交数 3 评论 3 代码增减 +14 / -10

执行摘要

修复 MistralTokenizer 多模态基准测试崩溃

Multimodal benchmark crashes with AttributeError: 'MistralTokenizer' object has no attribute 'added_tokens_decoder' when using Mistral tokenizer mode (e.g., mistralai/Ministral-3-14B-Instruct-2512-BF16). The RandomMultiModalDataset.sample method unconditionally accesses tokenizer.added_tokens_decoder, which is not available on MistralTokenizer. This prevents benchmarking multimodal models with Mistral tokenizers. See related PR #39539 for an alternative approach. (PR body)

值得合入,修复明确且风险低。建议在合入前确认 is_mistral_tokenizer 函数已正确导入并覆盖所有 Mistral 分词器变种。该 PR 的设计决策——在调用侧做 fallback 而非修改 MistralTokenizer 本身——值得肯定,它保持了 MistralTokenizer 的接口纯净。

讨论亮点

Reviewer gemini-code-assist[bot] 指出原注释放置在 if-else 之间会造成混淆——注释解释 all_special_ids 不够精细,但 Mistral 分支恰恰使用了 all_special_ids。建议将注释移入 else 分支。作者 juliendenize 接受该建议,并通过提交评论和补丁方式将注释移至 else 块内。最终 tedhtchangtdoublep 批准了该 PR。

实现拆解

  1. 新增依赖导入:在 vllm/benchmarks/datasets/datasets.py 中导入 is_mistral_tokenizer 工具函数(来自 vllm.utils.mistral)。
  2. 条件分支逻辑:在 RandomMultiModalDataset.sample 方法中,新增 if is_mistral_tokenizer(tokenizer) 判断:
    • 若为 Mistral 分词器,直接使用 tokenizer.all_special_ids 作为 prohibited_tokens,避免访问不存在的 added_tokens_decoder
    • 否则,沿用原有逻辑:遍历 tokenizer.added_tokens_decoder 筛选出 specialTrue 的 token ID。
  3. 注释移动:将原有的长篇注释(解释为何不使用 all_special_ids)移入 else 分支内,以避免误导(因为 Mistral 分支确实使用了 all_special_ids)。
文件 模块 状态 重要度
vllm/benchmarks/datasets/datasets.py 基准测试 modified 5.88

关键源码片段

vllm/benchmarks/datasets/datasets.py dependency-wiring

唯一变更文件;修复 RandomMultiModalDataset.sample 方法中对 MistralTokenizer 的兼容性。

# ... (imports above)
from vllm.utils.mistral import is_mistral_tokenizer
# ...def sample(self, ...):
    # ...
    vocab_size = tokenizer.vocab_size
    if is_mistral_tokenizer(tokenizer):
        # MistralTokenizer 没有 added_tokens_decoder 属性,
        # 直接使用 all_special_ids 作为 prohibited_tokens。
        prohibited_tokens = tokenizer.all_special_ids
    else:
        # 非 Mistral tokenizer 使用原有逻辑:
        # 不可以直接使用 all_special_ids,因为它只返回
        # special_tokens_map.json 中的 id,无法排除全部
        # 占位符 token(如图像开始 / 结束标记),这些 token
        # 可能破坏 prompt 替换逻辑。
        prohibited_tokens = list(
            tok_id
            for tok_id, token in tokenizer.added_tokens_decoder.items()
            if token.special
        )
    all_tokens = np.arange(vocab_size)
    allowed_tokens = np.array(list(set(all_tokens) - set(prohibited_tokens)))
    # ...

评论区精华

注释位置混淆 style

Reviewer `gemini-code-assist[bot]` 指出原注释放置在 if-else 之间会误导:注释说 all_special_ids 不够精细,但 Mistral 分支却用了它。建议将注释移入 else 块。

结论:作者接受建议,将注释移至 else 块内。 · 已解决

风险与影响

风险极低。变更仅影响 RandomMultiModalDataset.sample 方法中的一个条件分支,且 Mistral 分支使用的 all_special_ids 是分词器标准接口,非 Mistral 分词器的原有逻辑完全不变。但需注意,is_mistral_tokenizer 函数的实现细节若存在误判(如对非 Mistral 分词器返回 True)则可能导致 prohibited_tokens 覆盖不全,影响基准测试中 prompt 替换逻辑的正确性。不过该函数是一个经过验证的工具函数,误判可能性低。

影响范围极小:仅修复了 RandomMultiModalDataset 基准数据集在使用 MistralTokenizer 时的崩溃问题。用户无需修改代码即可正常使用 Mistral 分词器进行多模态基准测试。对非 Mistral 分词器(如 HuggingFace 原生 tokenizer)无任何影响。

关联 Issue

未识别关联 Issue

当前没有检测到明确关联的 Issue 链接,后续同步到相关引用后会出现在这里。

完整报告

参与讨论