Prhub

#50777 [Bugfix] Default Gemma3 Model intermediate_tensors to None

原始 PR 作者 taneem-ibrahim 合并时间 2026-08-04 03:07 文件变更 1 提交数 4 评论 1 代码增减 +1 / -1

执行摘要

Gemma3 模型 intermediate_tensors 补默认值,修复 pooling 启动失败

PR body 指出 Gemma3Model.forward 将 intermediate_tensors 声明为必填位置参数,而其他可 pooling 模型(如 BertModel)均默认其为 None;pooling 路径等从不产生 intermediate tensors 的调用方会省略该参数,V2 Model Runner 编译 forward 绑定时报 TypeError: missing a required argument: 'intermediate_tensors'。该问题由 #48290 的 CI 暴露:embeddinggemma-300m 解析为 attn_type=encoder_only 后进入 Model Runner V2 路径,在 Language Models Test (MTEB) 的引擎初始化阶段失败。因此本修复是 landing #48290 的前置条件。

值得快速阅读:单行改动修复了模型签名契约不一致,展示了默认值约定对 compiled forward 绑定路径的重要性;关联 #48290 的 Model Runner V2 迁移脉络,可作为理解 V2 编译绑定的入门案例。

讨论亮点

维护者 yewentao256 在 gemma3.py 上提出唯一一条 review 意见:建议去掉默认值旁的说明注释,只保留 intermediate_tensors: IntermediateTensors | None = None,,原话为 "We don't need the comment here"。作者采纳并更新提交,最终合并版本为无注释的单行改动。其余评论为维护者的致谢与 LGTM,没有技术争议。

实现拆解

  1. 根因定位:对比 Gemma3Model.forward 与 BertModel 等模型的签名,确认唯一差异是 intermediate_tensors 没有默认值,导致 compiled forward 在 bind_locals 阶段无法匹配参数。
  2. 签名修正:在 vllm/model_executor/models/gemma3.py 中为该参数补上 = None,使签名变为 intermediate_tensors: IntermediateTensors | None = None,与仓库内 pooling 模型的约定一致。内部 assert intermediate_tensors is not None 的逻辑保持不变,非首 PP rank 仍强制要求显式传入,未改变流水线并行语义。
  3. 验证方式:手动用 VLLM_USE_V2_MODEL_RUNNER=1 加载 google/embeddinggemma-300m 进行 pooling,输出 ENGINE_STARTED_OK 且 embedding 维度为 768;并运行 tests/models/language/generation/test_gemma.py 确认生成路径无回归(其中 test_dummy_loader 的失败在改动前后均存在,属既有问题)。pre-commit 检查通过。未新增自动化测试文件,属于单行签名契约修复。
文件 模块 状态 重要度
vllm/model_executor/models/gemma3.py 模型层 modified 4.7

关键符号

Gemma3Model.forward

关键源码片段

vllm/model_executor/models/gemma3.py data-contract

唯一变更文件,修复 Gemma3Model.forward 签名契约,使 intermediate_tensors 可省略,解除 pooling + V2 路径的启动失败。

修复后的 Gemma3Model.forward 方法核心逻辑如下,注意默认值与 PP 语义的配合:

def forward(
    self,
    input_ids: torch.Tensor | None,
    positions: torch.Tensor,
    # 与 BertModel 等可 pooling 模型保持一致的签名约定:默认 None。
    # pooling 路径等调用方从不产生 intermediate tensors,会省略该参数;
    # 若无默认值,V2 Model Runner 编译 forward 绑定参数时会直接抛
    # TypeError: missing a required argument。
    intermediate_tensors: IntermediateTensors | None = None,
    inputs_embeds: torch.Tensor | None = None,
    **kwargs,
) -> torch.Tensor | IntermediateTensors:
    # 首 PP rank 从 embed 结果构造 hidden_states,
    # 非首 rank 则必须依赖上游 rank 传下来的 intermediate_tensors。
    if get_pp_group().is_first_rank:
        if inputs_embeds is not None:
            hidden_states = inputs_embeds
        else:
            hidden_states = self.embed_input_ids(input_ids)
        residual = None
    else:
        # 防御性断言保留:非首 rank 仍强制要求 intermediate_tensors 非空。
        assert intermediate_tensors is not None
        hidden_states = intermediate_tensors["hidden_states"]
        residual = intermediate_tensors["residual"]
    for layer in islice(self.layers, self.start_layer, self.end_layer):
        hidden_states, residual = layer(
            positions,
            hidden_states,
            residual,
            **kwargs,
        )
    # 非末 rank 继续把 hidden_states 与 residual 封装后传给下一段。
    if not get_pp_group().is_last_rank:
        return IntermediateTensors(
            {"hidden_states": hidden_states, "residual": residual}
        )
    hidden_states, _ = self.norm(hidden_states, residual)
    return hidden_states

评论区精华

去掉 intermediate_tensors 默认值旁的冗余注释 style

yewentao256 在 gemma3.py 上建议只保留默认值赋值,不需注释:intermediate_tensors: IntermediateTensors | None = None,,原话为 "We don't need the comment here"。

结论:作者接受建议,后续提交移除注释,最终合并版本为无注释的单行改动。 · 已解决

风险与影响

改动仅涉及一个函数参数的默认值,对显式传参的既有调用(生成、PP 路径)零影响;非首 rank 的 assert 保留,漏传仍会以断言方式暴露,不会静默出错。主要欠缺是 PR 未附带自动化回归测试,当前依赖手动验证命令,后续若重构 pooling 编译绑定逻辑可能再次回归。

修复 Gemma3 文本模型(含 embeddinggemma-300m 等 encoder-only 用法)在 Model Runner V2 下的 pooling 启动;为 #48290 扫清测试阻塞;对普通生成服务无用户可见行为变化。影响范围小但具有阻塞性。

缺专用回归测试 模型签名契约变更

关联 Issue

未识别关联 Issue

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

完整报告

参与讨论