# PR #47410 完整报告

- 仓库：`vllm-project/vllm`
- 标题：support GLM-5.2 gate use FP32
- 合并时间：2026-07-02 22:45
- 原文链接：http://prhub.com.cn/vllm-project/vllm/pull/47410

---

# 执行摘要

- 一句话：GLM-5.2 MoE 门控强制使用 FP32 路由
- 推荐动作：推荐精读。这是一个小而精的适配性修复，展示了在 vLLM 中如何优雅地处理模型变体的特殊配置。关键设计点：
 - 集中路由类型判断逻辑到一个函数 `_get_moe_router_dtype`，易于扩展。
 - 利用 `force_fp32_compute` 统一处理 fallback，而不需要改动下游 kernel 选择逻辑。
 - `allow_dsv3_router_gemm` 的条件修复也值得关注，说明谨慎的 kernel 路径前置检查是必要的。

# 功能与动机

GLM-5.2 模型配置（`glm_moe_dsa`）没有暴露 `moe_router_dtype`，但其 MoE 路由需要 FP32 计算。原作者在 PR body 中说明“support GLM-5.2 gate use FP32”，直接点明动机。

# 实现拆解

1. **新增 `_get_moe_router_dtype` 函数 **( 文件 `deepseek_v2.py`)：根据 config 的 `model_type` 是否为 `glm_moe_dsa` 或 `moe_router_dtype` 是否为 `"float32"`，决定返回 `torch.float32` 或 `None`。这个函数作为集中决策点，避免了在多个地方重复判断。

2. **修改 `DeepseekV2MoE.__init__`**( 同上 )：调用 `_get_moe_router_dtype`，并将结果作为 `params_dtype`、`out_dtype` 和 `force_fp32_compute` 传给 `GateLinear` 构造器。当 `router_dtype` 为 `torch.float32` 时，`force_fp32_compute=True` 会触发 `GateLinear` 内部的 FP32 fallback（若没有专用 kernel）。

3. **补充条件守卫 `self.gate.out_dtype is None`**( 同上 )：在 ROCm Aiter MoE 融合路径中，若 `out_dtype` 已被外部设置（如本 PR），则跳过 `set_out_dtype`，避免重复覆盖。

4. **修复 `GateLinear.__init__` 中 `allow_dsv3_router_gemm` 的条件 **( 文件 `gate_linear.py`)：增加 `self.weight.dtype == torch.bfloat16` 检查，确保只有 BF16 权重才会启用 DSV3 专用 kernel，避免 FP32 权重错误使用该 kernel 路径。

5. **无测试 / 配置 / 部署配套变更**：本次改动完全属于源码适配，未引入新测试或配置项。

关键文件：
- `vllm/model_executor/models/deepseek_v2.py`（模块 模型加载；类别 source；类型 data-contract；符号 _get_moe_router_dtype）: 核心修改：新增 `_get_moe_router_dtype` 函数，修改 `DeepseekV2MoE.__init__` 传递路由精度参数给 `GateLinear`。
- `vllm/model_executor/layers/fused_moe/router/gate_linear.py`（模块 MoE 内核；类别 source；类型 data-contract）: 修复 `allow_dsv3_router_gemm` 条件，增加 `weight.dtype == torch.bfloat16` 检查，防止 FP32 权重误用 DSV3 专用 kernel。

关键符号：_get_moe_router_dtype

## 关键源码片段

### `vllm/model_executor/models/deepseek_v2.py`

核心修改：新增 `_get_moe_router_dtype` 函数，修改 `DeepseekV2MoE.__init__` 传递路由精度参数给 `GateLinear`。

```python
# vllm/model_executor/models/deepseek_v2.py

# ... (imports 省略 )

def _get_moe_router_dtype(
    config: DeepseekV2Config | DeepseekV3Config,
) -> torch.dtype | None:
    """根据模型配置决定 MoE 路由器权重和计算的精度。
    
    GLM-5.2 (`glm_moe_dsa`) 强制使用 FP32，
    其他模型若配置了 `moe_router_dtype="float32"` 也返回 FP32，
    其余情况返回 `None`（使用默认精度）。
    """
    router_dtype = getattr(config, "moe_router_dtype", None)
    if getattr(config, "model_type", None) == "glm_moe_dsa":
        # Older GLM-5/5.2 configs require fp32 routing but do not expose
        # moe_router_dtype yet.
        return torch.float32
    if router_dtype == "float32":
        return torch.float32
    return None

class DeepseekV2MoE(nn.Module):
    def __init__(self, config, parallel_config, quant_config=None, prefix=""):
        # ... 其他初始化代码 ...
        
        # 获取路由精度，None 表示用默认精度（通常是 BF16）
        self.router_dtype = _get_moe_router_dtype(config)
        
        self.gate = GateLinear(
            config.hidden_size,
            config.n_routed_experts,
            params_dtype=self.router_dtype,  # 权重精度
            out_dtype=self.router_dtype,      # 输出精度
            force_fp32_compute=self.router_dtype == torch.float32,  # 强制 FP32 计算
            prefix=f"{prefix}.gate",
        )
        # ... 后续代码 ...
        
        if (
            self.is_rocm_aiter_moe_enabled
            and self.gate.e_score_correction_bias is not None
            and self.gate.out_dtype is None  # 新增：若 out_dtype 已设定，不覆盖
        ):
            # Accumulates in fp32; avoids bf16->fp32 cast.
            self.gate.set_out_dtype(self.gate.weight.dtype)

```

### `vllm/model_executor/layers/fused_moe/router/gate_linear.py`

修复 `allow_dsv3_router_gemm` 条件，增加 `weight.dtype == torch.bfloat16` 检查，防止 FP32 权重误用 DSV3 专用 kernel。

```python
# vllm/model_executor/layers/fused_moe/router/gate_linear.py

class GateLinear(ReplicatedLinear):
    # ... 类定义 ...
    
    def __init__(self, ..., force_fp32_compute=False, ...):
        # ... 前置代码 ...
        
        self.allow_dsv3_router_gemm = (
            self.allow_specialized_router_gemm
            and self.weight.dtype == torch.bfloat16  # 新增：只有 BF16 权重才能用此 kernel
            and output_size in self.DSV3_SUPPORTED_NUM_EXPERTS
            and input_size in self.DSV3_SUPPORTED_HIDDEN_SIZES
            and (input_size, output_size) not in self.DSV3_UNSUPPORTED_SHAPES
        )
        # ... 后续代码 ...

```

# 评论区精华

无 review 评论。PR 被 ywang96 直接 approve，表明改动简单明确。claude[bot] 的自动回复仅说明 fork PR 需人工审核。

- 暂无高价值评论线程

# 风险与影响

- 风险：低风险。改动仅影响 `glm_moe_dsa` 类型模型的路由器行为，且修改都是条件性添加：
 - **回归风险**：对于非 `glm_moe_dsa` 模型，`_get_moe_router_dtype` 返回 `None`，行为与之前完全一致。
 - **性能风险**：FP32 计算可能略慢，但这是正确性前提，且只有 GLM-5.2 模型会走此路径。
 - **无安全 / 兼容性问题**。
 - 影响：仅影响 GLM-5.2（`glm_moe_dsa` 模型类型）用户。他们现在可以正确加载并使用该模型，MoE 路由不再因精度问题导致数值错误。对其他模型和系统无影响。
 - 风险标记：低风险 , 无测试覆盖

# 关联脉络

- 暂无明显关联 PR