[WS1][Ascend] [Qwen3-8b] LM head ops - #371
Merged
zhangj1an merged 1 commit intoSep 10, 2026
Merged
Conversation
zhangj1an
requested review from
Flink-ddd,
KJLdefeated,
bitborne and
inaniloquentee
as code owners
September 1, 2026 05:42
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Advanced Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Comment |
zhangj1an
force-pushed
the
feat/ascend-deterministic-lm-head
branch
from
September 10, 2026 14:12
0367637 to
adf454c
Compare
inaniloquentee
approved these changes
Sep 10, 2026
zhangj1an
force-pushed
the
feat/ascend-deterministic-lm-head
branch
from
September 10, 2026 16:44
adf454c to
aa4418a
Compare
Re-resolved onto latest test (includes RL-Align#370's logp entries): lm_head binding, registry enum + priority map, _C_npu.pyi, linear __init__, test_dispatch unioned with the existing ascend entries. Signed-off-by: zhangj1an <jianmusings@gmail.com>
zhangj1an
force-pushed
the
feat/ascend-deterministic-lm-head
branch
from
September 10, 2026 17:44
aa4418a to
dd0cf3b
Compare
zhangj1an
added a commit
to zhangj1an/rl-kernel
that referenced
this pull request
Sep 10, 2026
Re-resolved onto latest test (includes RL-Align#370, RL-Align#371): fused_linear_logp binding, registry enum + priority map, _C_npu.pyi, loss __init__, test_dispatch unioned. Signed-off-by: zhangj1an <jianmusings@gmail.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Latest Status [1 Sep 2026]
Ready for review.
Summary
Port of the CUDA deterministic LM-head (
csrc/cuda/embedding_lm_head_sm90.cu+SM90LMHeadOp) to Ascend NPU:_C_npu.lm_head_ascend): mirrors the SM90 CUDA kernel's structure —out[n, v] = dot(hidden[n, :], weight[v, :]) (+ bias[v])with one output element per block iteration, the full hidden-dimension reduction inside that block over a fixed tile order (products → per-tile sum → sequential scalar accumulation, all fp32), bias added in fp32, final cast to the output dtype with round-to-nearest. No Split-K, no algorithm selection.MAX_BLOCKS=128); the instruction sequence for an element depends only on H, never on N or block assignment, so a row's logits are bitwise identical across batch sizes, row positions, and block assignments on the NPU (verified withtorch.equal).torch.mv) has its own unspecified internal order, so bitwise parity with either is not claimed for this reduction op — the gtest comparison is tolerance-based per thereductioncontract (fp32 drift measured ~2.4e-4 vs atol/rtol 1e-4/1e-4 at the 2x16x4096x257 shape... see Test results; bf16/fp16 drift is ULP-scale after the output cast).AscendLMHeadOpmirrorsSM90LMHeadOp(forward/forward_fp32, bias support, dtype gate fp32/bf16/fp16, hidden[-1] == weight[1] check). The backward is the standard linear VJP computed in fp32 on the NPU (grad_hidden = grad @ W,grad_weight = grad^T @ H, bias = fixed-order row sum) then cast to the input dtypes — the CUDA op routes through_C.det_gemm, which has no NPU equivalent; gradients compare against the native backward at the gradient contract tolerance."ascend"inoperator_specs.py;ASCEND_LM_HEADin the registry with the NPU priority[ASCEND_LM_HEAD, PYTORCH_NATIVE_LM_HEAD];scripts/check_operator.pygains--device npusupport.csrc/ascend/npu_module.cppconsolidates the singlePYBIND11_MODULE(batch_invariant_logp + lm_head);batch_invariant_logp_ascend.asconly drops itsPYBIND11_MODULEblock.setup.pygains the Ascend extension build (bisheng,**/*.ascglob) with the CANN env export in_find_ascend_home.Build notes (same pattern as PR #320 / #355)
Each
.ascsource file can define only onePYBIND11_MODULE(linking multiple sources with Bisheng causes a duplicatePyInit__C_npuerror), so pybind registrations are consolidated incsrc/ascend/npu_module.cpp;batch_invariant_logp_ascend.asconly drops itsPYBIND11_MODULEblock.Files
csrc/ascend/lm_head_ascend.asccsrc/ascend/npu_module.cpp_C_npupybind registration (batch_invariant_logp + lm_head). New.csrc/ascend/batch_invariant_logp_ascend.ascPYBIND11_MODULE(moved to the aggregated file). Kernel logic unchanged.rl_engine/kernels/ops/ascend/linear/lm_head.pyAscendLMHeadOp(Ascend C forward + fp32-formula VJP backward). New.rl_engine/kernels/ops/ascend/linear/__init__.pyrl_engine/kernels/ops/ascend/__init__.pylinearsubpackage.rl_engine/_C_npu.pyilm_head_ascendtype stub.rl_engine/kernels/gtest/operator_specs.py"ascend"candidate for thelm_headop.rl_engine/kernels/registry.pyASCEND_LM_HEADbackend + NPU priority map entry.rl_engine/tests/test_dispatch.pyscripts/check_operator.py--device npu/ auto-detect support.tests/test_lm_head_ascend.pydocs/operators/lm_head.mdsetup.py_find_ascend_home.Test
Test results
Environment: Ascend 910, CANN 8.5.1 (Bisheng), torch 2.10.0 + torch_npu 2.10.0.post2.
lm_headascend candidate, fp32 × 2x16x4096x257, output + both gradientslm_headascend candidate, bf16 × 2x16x4096x257, output + both gradientslm_headascend candidate, fp16 × 2x16x4096x257, output + both gradientstorch.equal)tests/test_lm_head_ascend.pytests/test_batch_invariant_logp.pyrl_engine/tests/test_dispatch.pygtest raw output (fp32, representative)
pytest: tests/test_lm_head_ascend.py (summary)
Notes
torch.mvinternal order are unspecified platform internals, so no independent kernel can match them bit for bit. The Ascend kernel pins its own fixed order, which makes the op batch-invariant bitwise on the NPU — the property the WS1 contract'sforward_invariancerow requires (bitwise) — whileforward_accuracyis tolerance-based. Note the bf16 gtest numbers look large in absolute terms but are ULP-scale on logits of magnitude ~40 (max_rel 3.9e-3).torch.matmulon NPU), cast back to the input dtypes; gradients compare against the native backward at the gradient contract tolerance.NativeLMHeadOp.forward's bf16 dtype path is itself unavailable on this CANN (torch.mvrejects bf16), which is why the reference comparisons go throughforward_fp32.black/isort/flake8(line-length 100) pass for all modified Python files.