Repository navigation
[Fix] Preserve FP32 coefficients and accumulation in mHC post - #2260
RunningLeon wants to merge 1 commit into
Conversation
Review: PR #2260 [Fix] Preserve FP32 coefficients and accumulation in mHC post
SummaryApprove with comments(一项合并前置)。 本 PR 把 mHC post 展开的数值契约从"刻意复刻 HF eager 的 BF16 中间舍入"改为"系数与累加全程 FP32、仅最终输出 cast 一次、累加顺序对齐 LMDeploy"——即对齐基准从 HF 参考改为部署引擎。方向正确(与 #2269 同一哲学:基准跟着部署走),测试质量高(FP64 独立解析 VJP),实测数据有三方背书。 训练影响与证据边界:
Main Flowchart before this PR修改前:hc_post 的数值契约 = 逐位复刻 HF eager 的 BF16 中间舍入(三处舍入点 + eager 必须编译)。 flowchart TB
subgraph CALL["调用方:Glm53DecoderLayer(每层 attn/ffn 回写各一次,45 层 ×2)"]
C1["hc_post(x, residual, post, comb)<br/>xtuner/v1/module/decoder_layer/mhc.py"]
end
subgraph DISPATCH["hc_post dispatch"]
D{"x.is_cuda and dtype==bf16<br/>and _hc_post_fused_available()?"}
end
subgraph FUSED["_hc_post_fwd_kernel(xtuner/v1/ops/hc_post.py)"]
K1["post/comb 载入后 .to(bf16)"] --> K2["mix 累加 → acc.to(bf16).to(f32)<br/>(复刻 HF 的 matmul 舍入边界)"] --> K3["(p*xt).to(bf16).to(f32)"] --> K4["prod + acc → 输出"]
end
subgraph EAGER["_hc_post_eager(@maybe_compile;必须编译才安全)"]
E1["post/comb .to(bf16)"] --> E2["broadcast 乘 + sum(dim=-2)<br/>不编译则物化 5 倍 H×D 中间量 ~5GiB"] --> E3["post_dt*x + mixed(.to(bf16))"]
end
C1 --> D
D -->|"是(生产路径)"| K1
D -->|"否"| E1
Main Flowchart after this PR修改后(绿色 = 本 PR 改动):三处 BF16 中间舍入全部移除,系数与累加全程 FP32,累加顺序 = flowchart TB
subgraph CALL["调用方:Glm53DecoderLayer(接口/签名不变,零改动)"]
C1["hc_post(x, residual, post, comb)"]
end
subgraph DISPATCH["hc_post dispatch(条件不变)"]
D{"x.is_cuda and dtype==bf16<br/>and _hc_post_fused_available()?"}
end
subgraph FUSED["_hc_post_fwd_kernel(重写)"]
K1["post/comb 载入保 FP32"] --> K2["acc = post * x(先)"] --> K3["for hi 升序: acc += combₕᵢ·residualₕᵢ<br/>(FP32 全程,与 lmdeploy 同序)"] --> K4["仅输出时单次 cast bf16"]
end
subgraph EAGER["_hc_post_eager(重写为流循环)"]
E1["out = post.float()*x.float()"] --> E2["for hi: out += combₕᵢ.float() × residualₕᵢ.float()<br/>(可安全 eager:峰值 3GiB,不再物化 H 倍中间量)"] --> E3["单次 .to(residual.dtype)"]
end
subgraph BWD["_hc_post_bwd_op(系数精度贯穿 VJP)"]
B1["pf/cf 不再 .to(dt)(原 bf16 舍入删除)"]
end
C1 --> D
D -->|"是"| K1
D -->|"否"| E1
K4 -.-> B1
classDef new fill:#e8f5e9,stroke:#2e7d32,stroke-width:2px
class K1,K2,K3,K4,E1,E2,E3,B1 new
修改点对应: 前向/反向两侧同步:backward 的 核心实现与对应的单测
上机环境:h200-0078、GLM_ENV pt29_glm2、 抽象与信息隐藏评估
新增功能守则(R1-R3)本 PR 非新增功能类,属既有函数的行为变更,R1-R3 天然满足:无新类、无新接触点、无按特性分叉的条件(dispatch 条件原样未动)。唯二"新增"是测试类 公开接口的线性业务流程评估新 单测建议
与 LMDeploy 的一致性主张:逐行核验 ✅lmdeploy 与内部交付 02 patch 的关系:同一实现的上游化 ✅maningsheng 交付包 e2e 验证(本 review 实测,运行中/见附录)在本组修正后的基准栈上(MoE-only FP8 + R3 + 双侧 KPool-BF16 = 3.122e-4)叠加本 PR:见附录(出数后补)。 其他 issues
结论方向、实现、测试、三方实测数据都成立。合并前置仅 CI golden 重基线(issue 0);issue 1-4 为建议项。 附录(2026-11-11):e2e 实测配置 = 本组基准栈(MoE-only FP8 + R3 复用 + 双侧 KPool BF16,即 3.122e-4 那组)+ 本 PR,唯一变量为 mHC post 数值契约:
完整阶梯(同栈同机同 seed,MoE-only FP8 部署基准 + R3):基线 3.729e-4 → +PR2269 3.431(-8.0%)→ +KPool-BF16 3.122(-16.3%)→ +本 PR 2.757e-4(累计 -26.1%)。 交叉验证:2.757e-4 与内部交付三 patch 组合的实测 2.756e-4(maningsheng README,另一机器)几乎一致——两套独立测量收敛。零 ckpt、零报错;work_dirs/rl53moe-pr2260mhc-R3-20261111。 附录 2(2026-11-11 晚,交叉审查后补充)
|
|
默认开启还是 opt-in? 建议默认开启(不加开关)。 理由:
这一点是我这边的求证给出的。具体可以问问赵老师 |
Summary
Keep mHC post coefficients and intermediate arithmetic in FP32, casting only the final activation. Apply the same precision policy to the eager fallback and fused Triton forward/backward.
This is a numerical-policy alignment with LMDeploy, not a claim that the previous implementation used the wrong mathematical formula or that this PR establishes end-to-end zero-diff training.
Motivation
hc_prereturns FP32 Sinkhorn coefficients, buthc_postcurrently roundspostandcombto BF16. The forward also rounds the residual mixture andpost*xbefore their final addition. LMDeploy instead accumulatespost*xfollowed by source streams in FP32 and casts only once.Early rounding can remove small signals under cancellation. For example, positive coefficients
post=[1.003,1],comb=0.5,x=1, and residual streams-1produce zero instead of approximately0.003in the first output stream. Rounding the saved coefficients also changes the VJP forxandresidual.Changes
post*x, thencomb[src,out]*residual[src]in source order in FP32; cast only the final output.No configuration/checkpoint format changes, external dependencies, or changes to Sinkhorn, KDA, KPool, MoE, or distributed collectives.
Validation completed before opening this draft
Based on upstream/main
ed8b200b61fddf821387632a9d730b9f854309a2; Python 3.12, PyTorch 2.11.0+cu128, Triton 3.6.0, NVIDIA H200.x,residual,post, andcomb; H=1/2/4/8, D=31/257/4096/2053, contiguous and non-contiguous inputs; both eager fallback and fused dispatch.hc_post_expand: all four shapes pass BF16 tolerances. The large H=4 samples are not bit-identical: 15/2,097,152, 150/16,777,216, and 569/67,108,864 elements differ; max absolute difference <=0.0078125.mypyskipped; test-file Ruff checks/format pass separately (the repo's hook filter excludestests/).git diff --checkpasses.The existing shared conftest needs
expecttest, absent from the validation environment. It was installed only in an external task-local test-dependency directory and added to PYTHONPATH, not added to this PR or the user's Python environment.Limitations / draft review points
transformers.models.glm5_next. The initial unfiltered run reports these six import failures; they are explicitly deselected in the successful focused command. Transformers was not upgraded.Latest-main integration recheck (2026-11-06)
Draft head remains
de5e8cd7dfa3768962f0946f42d440643599aae9; no duplicate mHC PR or force-push.Fetched upstream/main
46c7d492a93104a55d6e454d1ffa560c5ae68027and merged it into the PR head in an isolated worktree without conflicts. Production mHC code is unchanged by this integration.On that integrated tree, the focused CPU/GPU cancellation, independent FP64 forward/all-four-VJP, contiguous/strided, and eager/compiled pre/post parameter-chain tests pass: 12 passed, 6 deselected.
python -m pytest --noconftest tests/ops/test_hc_post.py \ -k 'forward_and_all_gradients_against_fp64 or coefficients_survive_cancellation or hc_pre_post_parameter_gradients' -qUsed the same verl environment, with no dependency installation, full RL, or model checkpoint changes. Shared conftest was explicitly bypassed for this bounded recheck. Peak observed process GPU memory was 874 MiB. This is a focused integration recheck, not full-repository CI or a replacement for the limitations above.