Prhub

#35805 [CPU] Fix weight missing issue in fused_input_proj_cpu for GPTQ INT4 for Qwen 3.5

原始 PR 作者 yanbing-j 合并时间 2026-08-31 10:14 文件变更 1 提交数 2 评论 2 代码增减 +4 / -2

执行摘要

修复 Qwen3.5 CPU GPTQ INT4 权重缺失

PR body 明确指出根因:For GPTQ Int4 Qwen3.5, the projection modules do not register a plain weight parameter, so accessing .weight inside LazyValue can recurse through getattr; checking _parameters directly avoids triggering attribute fallback and is equivalent for modules that do register weight. 即 GPTQ INT4 量化后的投影模块未把权重注册进 nn.Module._parameters,LazyValue 惰性求值中直接访问 .weight.dtype 会走属性兜底并递归,最终表现为权重缺失、模型无法在 Intel CPU 上正常加载。

值得快速阅读:改动虽小,但揭示了 PyTorch 量化模块与 nn.Module.getattr 的隐蔽交互,是“惰性求值中避免属性递归”的典型范例。关注点:(1) LazyValue 惰性判定与 and 短路的配合;(2) 直接检查 _parameters 以绕过属性兜底的写法及其代价(依赖内部契约)。建议后续为 CPU + GPTQ INT4 场景补充单元测试,防止该路径再次回归。

讨论亮点

本 PR 没有任何 review 评论(review_comments_count = 0),唯一的审核记录是 mingfeima 的空文本 APPROVED。技术讨论集中在 PR body 中作者对根因的说明:GPTQ INT4 下投影模块不注册普通 weight 参数,.weight 访问会递归触发 __getattr__,而检查 _parameters 是等价且安全的写法。另外 mingfeima 在 CI 中两次发起 /rerun-failed-ci(PR Test Extra 与 AMD ROCm 7.2 任务曾失败),说明该改动在跨平台 CI 上经历过重跑后才通过。

实现拆解

  1. 根因定位:qwen3_5.py 的 init_fused_input_proj_cpu_enabled = LazyValue(lambda: ...) 原写法直接访问 self.in_proj_qkvz.weight.dtypeself.in_proj_ba.weight.dtype。对 GPTQ INT4 模块,weight 未注册进 _parameters,访问 .weight 会触发 nn.Module.__getattr__ 兜底并可能递归,导致求值失败。
  2. 修复方式:在 and 链最前插入 self.in_proj_qkvz._parameters.get("weight") is not Noneself.in_proj_ba._parameters.get("weight") is not None 两个前置条件,并把 dtype 判断改为 self.in_proj_qkvz._parameters["weight"].dtype。利用 and 短路求值,后续 use_intel_amx_backend(...).weight.size(0) 访问时参数必然已存在,不会再落入 __getattr__ 递归;对正常注册 weight 的模块,_parameters["weight"].weight 完全等价。
  3. 配套与验证:本 PR 未新增任何测试文件,也未改文档或配置;CI 中 PR Test Extra 与 AMD ROCm 7.2 任务一度失败,mingfeima 两次 /rerun-failed-ci 后通过,最终以空文本 APPROVED 合并。
文件 模块 状态 重要度
python/sglang/srt/models/qwen3_5.py 模型层 modified 5.23

关键符号

__init__(Qwen 3.5,含 _fused_input_proj_cpu_enabled LazyValue 判定) _fused_input_proj_cpu_enabled

关键源码片段

python/sglang/srt/models/qwen3_5.py bugfix

唯一变更文件:修复 Qwen 3.5 GPTQ INT4 下 fused_input_proj CPU 启用判定因访问 .weight 触发 __getattr__ 递归而报权重缺失的问题。

# 惰性判定:是否在 Intel CPU 上启用 fused input projection(AMX 后端)。
# 关键点:GPTQ INT4 量化的 Qwen 3.5 中,in_proj_qkvz / in_proj_ba 不会把权重
# 注册进 nn.Module 的 _parameters,直接访问 .weight 会触发 __getattr__ 兜底并可能递归,
# 因此需先查 _parameters 字典;对普通模块而言该写法与 .weight 访问语义等价。
self._fused_input_proj_cpu_enabled = LazyValue(
    lambda: (
        _is_cpu
        # 先确认参数真实存在,避免后续 .weight 访问落入 __getattr__ 递归路径
        and self.in_proj_qkvz._parameters.get("weight") is not None
        and self.in_proj_ba._parameters.get("weight") is not None
        # 直接从 _parameters 读取 dtype,等价于原 self.in_proj_qkvz.weight.dtype
        and self.in_proj_qkvz._parameters["weight"].dtype == torch.bfloat16
        and self.in_proj_ba._parameters["weight"].dtype == torch.bfloat16
        # 融合路径要求两个投影均无独立 bias
        and self.in_proj_qkvz.bias is None
        and self.in_proj_ba.bias is None
        # 仅在 Intel AMX 后端可用时启用
        and use_intel_amx_backend(self.in_proj_qkvz)
        and use_intel_amx_backend(self.in_proj_ba)
        # 权重第一维(输出维度)需满足 32 对齐的 AMX 约束
        and (
            self.in_proj_qkvz.weight.size(0) % 32 == 0
            and self.in_proj_ba.weight.size(0) % 32 == 0
        )
    )
)

评论区精华

GPTQ INT4 下 .weight 访问触发 __getattr__ 递归 正确性

PR body 说明:投影模块未注册普通 weight 参数,LazyValue 内访问 .weight 会递归触发 __getattr__;建议直接检查 _parameters 以避免属性兜底,对正常注册 weight 的模块语义等价。

结论:改为 `_parameters.get("weight") is not None` 前置判断并直接读 `_parameters["weight"].dtype`,利用 and 短路保证后续 `.weight.size(0)` 访问安全;讨论无未解决疑虑。 · 已解决

CI Extra 与 AMD ROCm 任务失败及重跑 other

mingfeima 两次评论 `/rerun-failed-ci`;PR Test Extra (Run #33149647059) 与 AMD ROCm 7.2 (Run #33149404312) 曾标记失败。

结论:重跑后通过,mingfeima 以空文本 APPROVED 合并;失败与本改动的因果关系未在评论区说明。 · 已解决

风险与影响

正确性/兼容性风险:直接读写 _parameters 属于 PyTorch 内部约定而非公开 API,若未来 Qwen 3.5 的投影模块改用 buffer 或自定义属性持有权重,_parameters.get("weight") 会返回 None,从而静默禁用 fused 路径,退化为逐模块计算——正确性不受影响,但 CPU 推理性能可能回退。回归风险:PR 未附带任何单元测试,修复依赖 Intel CPU 既有 CI 的端到端覆盖,缺少针对该场景的专项回归保障。平台风险:改动只在 _is_cpu 且 AMX 后端可用的分支中生效,GPU、AMD、XPU 等平台与其它模型完全不受影响;CI 中 AMD ROCm 7.2 与 Extra 任务曾失败,与改动本身的因果关系不明(重跑后通过)。

用户侧:解锁 Qwen 3.5 (GPTQ INT4) 在 Intel CPU(AMX bfloat16 后端)上的正常加载与推理,此前会因权重缺失报错。系统侧:改动位于模型构建期的惰性开关,不改变任何 kernel 与 forward 行为,不影响非 CPU 路径。团队侧:为 Intel CPU 量化推理路径补上一个关键缺口,但缺少针对该场景的回归测试,建议后续补充单元测试防止再次回归。

缺少测试覆盖 依赖 PyTorch 内部 _parameters 契约 跨平台 CI 曾失败

关联 Issue

未识别关联 Issue

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

完整报告

参与讨论