Skip to content

[WS1][Ascend] Qwen3-8B Dense train-inference closeout for BF16 execution vs FP32 reference - #406

Merged
Flink-ddd merged 10 commits into
RL-Align:testfrom
zhangj1an:feat/ws1-ascend-closeout
Sep 12, 2026
Merged

[WS1][Ascend] Qwen3-8B Dense train-inference closeout for BF16 execution vs FP32 reference#406
Flink-ddd merged 10 commits into
RL-Align:testfrom
zhangj1an:feat/ws1-ascend-closeout

Conversation

@zhangj1an

@zhangj1an zhangj1an commented Sep 11, 2026

Copy link
Copy Markdown
Collaborator

Latest Status [12 Sep 2026]

Ready to be included in v0.1.0. No further PR will be created for Ascend WS1. The discrepancy listed below stems from BF16-execution-vs-FP32-reference quantization drift (a control chain built entirely from gold ops reproduces the same failure class), not from a defect in any individual Ascend kernel.

Forward accuracy: Against the same FP32 reference, the full-model selected_logp error is max_abs 0.0388 on CUDA (H20) versus 0.0857 on Ascend, about 2.2× larger on Ascend.

Gradient accuracy: On the same judgment, the CUDA candidate passes all 798 weight-gradient rows with a worst-case max_abs 0.1034, while the Ascend candidate fails 44 rows with a worst-case max_abs 1.88 — about 18× larger at the near-zero gradient elements.

Details

This PR closes #266 for the Ascend profile by mirroring the protocol and requirement of #315 (the WS1 full Qwen3-8B Dense train-inference closeout), adding ascend_bf16 (BF16 execution vs FP32 reference) as the third required profile.

Passing:

  • Forward invariance 7/7 bitwise, gradient invariance 2394/2394 bitwise (max_abs 0.0); train/infer parity aggregates 0.0 / 0.0 / 0.0 (exact).
  • The attention backward is a dedicated Ascend C kernel (deterministic_attention_backward_ascend.asc) with fixed keyBegin-anchored reduction orders, closing the cross-physical-length padding-invariance follow-up (Sv=63/64/65/96/128 with pad=5: 0.0 bitwise, both pad sides). FP16 backward falls back to the torch VJP.
  • The FP32 reference now runs on this 64 GB host via CPU-resident weight paging (Qwen3DenseWeightsOffloaded), verified bitwise against a resident-FP32 model.

Not passing (accuracy judgments):

  • Forward accuracy: selected_logp max_abs 0.0857 vs atol 0.06.
  • Gradient accuracy: 44/798 rows fail (max_abs up to 1.88), all at near-zero reference-gradient elements. Swapping any single Ascend kernel for its gold twin does not eliminate them, and a control chain built entirely from gold ops with the candidate's BF16 dtypes/cast positions still fails (24 tensors, max_abs 2.19, relL2 12.6%) — so the failures come from the BF16-vs-FP32 quantization drift of the execution strategy itself, not from any single Ascend kernel. Thresholds stay unchanged; formal recalibration would require multi-workload evidence.

## Validation Identity
Field Value
Commit 1fe03e7 (head)
NPU Ascend 910B (910_9362)
HBM 64 GB
CANN 9.0.0 (Bisheng)
PyTorch / torch_npu 2.7.1+cpu / 2.7.1.post4
Execution dtype BF16
Reference and accumulation dtype FP32 (CPU-resident weights paged per access — see C10)
Workload ws1-qwen3-8b-dense-primary-v6
Workload seed 20260812
Manifest version ws1-c2-v8
Weight snapshot Qwen/Qwen3-8B @ b968826d9c46dd6066d109eabc6255188de91218, content hash fc664a19c52c82b6f5ddb33d4fe2723181daeb93a344b16fee6369963e5a13a5
## C1 contract (tolerance contract, dtype policy, aggregates)
  • tolerance_contract.json declares ascend_bf16 -> backend_family "ascend" as a third required profile; thresholds, roles and aggregates unchanged.
  • CPU contract/workload/wiring tests: 107 passed, 1 skipped.
  • TF32 disabled holds by construction (Ascend has no TF32 mode).
## C2 candidate evidence (manifest-pinned, random inputs, --check-grad)

python scripts/ws1_candidate_evidence.py --profile ascend_bf16 --all --check-grad

Result Count
Pinned Ascend cases 23
Passed 23 / 23

The deterministic GEMM's accuracy gold is the leaf-space tree reference (DetGemmTreeReferenceOp) and its backward is the canonical FP32-accumulation rowwise VJP; both match the unrounded FP32 reference to ULP.

## C3 forward invariance / C4 gradient invariance
  • C3/C4 smoke (silu): all pairs bitwise, max_abs=0.0 (chunk, batch, permutation, left/right padding).
  • C4 for det_gemm / rms_norm / qk_norm: green via the canonical parameter_vjp_contributions_fp32 hooks — chunk/permute/padding/singleton pairs bitwise.
## C5 elementwise inventory
  • tests/test_elementwise_inventory.py passes (CPU classification); on-NPU verdicts flow through C3/C4.
## C6 direct decode-prefill consistency

python scripts/check_decode_prefill.py --backend-profile ascend_bf16

decode-bn-varlen          attn_pass=True max_abs=0.0 logp_pass=True
decode-bn-padded-right    attn_pass=True max_abs=0.0 logp_pass=True
decode-bn-padded-left     attn_pass=True max_abs=0.0 logp_pass=True
decode-b1-primary-s3      attn_pass=True max_abs=0.0 logp_pass=True
cases=6 (all include direct decode)

6/6 passed, max_abs 0.0.

## C7 stateful KV + generate-rescore

python scripts/check_stateful_kv.py --backend-profile ascend_bf16

profile=ascend_bf16 candidate=ascend b1=True rescore=True b2=absent passed=True

passed.

## C8 four-judgment matrix sweep

python scripts/sweep_ws1_four_judgments.py --execute --profile ascend_bf16 --json

Count
Green 88
Red 0
N/A 8 (declared pack layout-helper boundary)
## C9 full-model assembly
  • Pinned weights load and verify (content hash matches the manifest); the full model assembles and runs on the NPU (all eight C10 cells execute with real backward).
  • Runtime provenance records the NPU name and SoC as the architecture key.
## C10 full Qwen3-8B Dense chain gate

python scripts/ws1_chain_gate.py --backend-profile ascend_bf16 --model qwen3-8b-dense --dtype bfloat16 --weights required --weights-path ...

The FP32 reference runs on this host with CPU-resident weights paged onto the NPU per access (bitwise-identical to a resident-FP32 model); the gate runs all eight cells with real backward:

Check Result
Cells executed 8/8 (BN/full, BN/chunked, BN/permuted, BN/padded×2, BN/packed, B1/full, B1/chunked), all with real backward and all 350+ parameter gradients
Train/infer parity aggregates max_abs_dlogp = 0.0, approx_kl0 = 0.0, clipfrac0 = 0.0 (exact)
selected_logp invariance all pairs bitwise (7/7, max_abs 0.0)
gradient accuracy vs reference 754/798 pass; 44 fail (max_abs up to 1.88, near-zero elements; BF16-vs-FP32 quantization drift — all-gold BF16 control fails the same way)
## C11 CI

Not runnable on this host (requires the self-hosted Ascend runner and the GitHub workflow). The CI script ci/run_ws1_ascend_ci.sh was exercised step-by-step locally; its gates are the sections above.

## Regression tests
pytest -q \
  tests/test_tolerance_contract.py \
  tests/test_ws1_workload.py \
  tests/test_four_judgment_matrix.py \
  tests/test_elementwise_inventory.py \
  tests/test_ws1_ascend_closeout.py
# 107 passed, 1 skipped

pytest -q tests/test_det_gemm_ascend.py tests/test_silu_ascend.py
# 57 passed

pytest -q tests/test_attention_ascend.py
# 26 passed

pytest -q tests/test_rms_norm.py
# 91 passed, 32 skipped

pytest -q tests/test_kv_consistency.py tests/test_ws1_qwen3_dense.py tests/test_ws1_chain_integration.py
# 37 passed
## Notes
  • The Ascend vector unit's reduction orders differ from CUDA's, so cross-platform bitwise parity is not claimed; the guarantee is the one each platform provides for itself — batch-invariant determinism under the shared contract.
  • The three selected_logp invariance failures found on-device were: (1) the fused-logp wrapper falling back to the native log-softmax for non-contiguous logits (batch-layout-dependent); (2) the attention kernel's softmax denominator reduced per physical 64-key tile (left padding changed the FP32 addition grouping — tiles now anchored to the first valid key); (3) the RMSNorm rstd's torch mean/sum selecting shape-dependent reduction kernels (52/140 rows flip between batch layouts) — all three paths now share a fixed-order shape_invariant_rstd. All verified bitwise at the cell level.
  • Attention backward: the valid-token-compaction VJP was replaced by the dedicated Ascend C backward kernel (three stream-ordered launches, keyBegin-anchored fixed reduction orders), closing the cross-physical-length padding-invariance follow-up; FP16 backward (optional contract row) falls back to the torch VJP.
  • C10 reference: the FP32 reference runs on-device with CPU-resident weights paged per access (bitwise-identical to the resident-FP32 model); its linear nodes are the C2 contract tree, whose BF16 leaf/merge rounding makes the accuracy judgments sensitive to the candidate's own rounding at near-zero gradient elements.
  • Single-device operator; no CP/SP involvement, consistent with the CUDA implementation in issue [WS1][kernels] Batch-invariant matmul / GEMM #146.

🤖 Generated with Claude Code

@coderabbitai

coderabbitai Bot commented Sep 11, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Advanced

Run ID: 58892c20-7ac6-4e46-a21d-c2772cea8305

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Comment @coderabbitai help to get the list of available commands.

zhangj1an and others added 6 commits September 12, 2026 15:26
…quired profile

Issue RL-Align#266 is the WS1 acceptance entry (C1-C11 / RL-Align#267-RL-Align#277) for single-GPU
model-level train-inference consistency on full Qwen3-8B Dense, with
cuda_bf16 and triton_cuda_bf16 as its required profiles. This adds the Ascend
version: ascend_bf16 (backend family "ascend") carried through every one of
C1-C11 on the same shared contract and the same harnesses.

ascend_bf16 is required, not optional: a missing or unexecuted Ascend cell is
red, never N/A and never a fallback to another vendor's kernels.

Kernel gaps closed first:

- silu is a required C2 chain node with no Ascend kernel. Added to
  csrc/ascend/activation.asc next to SwiGLU, sharing its tile geometry and
  FP32 sigmoid sequence, so silu(x) is bitwise equal to swiglu(x, ones).
  Dispatching SwiGLU-with-a-unit-operand instead would report SwiGLU
  provenance, which C1 treats as an undeclared backend.
- The canonical row-fold VJP needs a deterministic FP32-in GEMM; the Ascend
  det_gemm kernel is BF16-in only. det_gemm_rowwise_ascend_fwd_fp32 exposes
  the existing lm_head_ascend kernel (FP32 input, one fixed per-row reduction
  order) as a general GEMM via B^T - the same construction CUDA uses to build
  det_gemm_rowwise_fwd_fp32 from its SM90 lm_head kernel. Casting the VJP to
  BF16 would have broken the contract's FP32-accumulation rule.

C1-C11:

- C1  tolerance_contract.json declares ascend_bf16 -> family "ascend";
      tolerance.py requires it. No Ascend-private tolerance relaxation. TF32
      holds by construction (Ascend has no TF32 mode).
- C2  ws1_manifest.json gains the profile with all 11 nodes declared and 23
      representative cases mirroring the CUDA set, each pinning a real .asc
      entry point. version -> ws1-c2-v8, identity regenerated; workload_id is
      unchanged so existing CUDA/Triton evidence stays bound to the workload.
- C3/C4  check_forward_invariance.py / check_gradient_invariance.py take
      --backend-profile ascend_bf16 and run on the profile's own device.
- C5  elementwise_inventory gains an ascend_verdict column.
- C6/C7  kv_consistency and its CLIs resolve the device from the profile.
- C8  four_judgment_matrix covers the profile and can be scoped per host.
- C9  qwen3_dense is device-agnostic; canonical backward paths gained Ascend
      branches recording family="ascend".
- C10 chain_gate and ws1_chain_gate.py run the full RL-Align#150 matrix on the NPU.
- C11 ci/run_ws1_ascend_ci.sh plus .github/workflows/ws1-chain-npu.yml;
      ci/run_ws1_chain_gate.sh is parameterised through WS1_PROFILES.

A host has a GPU or an NPU, not both, so the C8 sweep, candidate-evidence
script and chain-gate CI script take an explicit profile list and each
vendor's job proves its own profiles. C11 closes only when every required
profile has gone green on its own hardware.

rl_engine/kernels/gtest/accelerator.py holds the vendor-dependent facts the
gates need and fails closed: no NPU means AcceleratorUnavailable, and
pointing an Ascend profile at cuda:0 is rejected before any device probe.

On-device evidence is not collected yet - it needs an Ascend host. Until then
the Ascend C5 rows are tracked_red and the C8 Ascend cells are red, which is
the correct pre-execution state.

Includes PR RL-Align#405 (Ascend deterministic GEMM), which this builds on.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PzQmyerKmyyiMPSyPGspCH
Signed-off-by: Zhang Jian <jianmusings@gmail.com>
On-device bring-up of the RL-Align#266 closeout (feat/ws1-ascend-closeout) exposed
two structural comparison mismatches and two missing canonical backward
hooks; the C8 four-judgment sweep went from 10 red cells to 0 and the C2
candidate evidence from 21/23 to 23/23:

- det_gemm accuracy gold: the deterministic GEMM rounds every 32-element
  leaf and every tree merge node to BF16, so comparing a candidate against
  the single-rounding torch.matmul gold fails structurally at near-
  cancellation outputs on random inputs (max_abs 2.0-4.0, reproducible in
  pure numpy on the manifest fixture data). The gold is now
  DetGemmTreeReferenceOp, the exact leaf-space mid-split tree.
- det_gemm gradients: the autograd backward now uses the canonical
  FP32-accumulation rowwise VJP (det_gemm_rowwise_ascend_fwd_fp32) instead
  of the BF16 tree da/db kernels, so gradient_accuracy matches the
  unrounded FP32 reference grads to ULP. Determinism is preserved: the
  rowwise kernel reduces each output row in one fixed per-row order.
- C4 gradient invariance: DetGemmAscendOp and RMSNormAscendOp now expose
  parameter_vjp_contributions_fp32 (the CUDA twin): per-row FP32
  contributions that the harness accumulates in FP32 across call spans, so
  chunked / padded / permuted / singleton-aggregated weight gradients are
  bitwise identical (previously 1.5e-4 - 3.1e-4 drifts).
- canonical embedding accepts the ascend family (the Ascend embedding's
  deterministic grad-weight reuses the CUDA construction bit-for-bit).
- FP32-output attention: the model's FP32 composite attention edge had no
  Ascend path. The Ascend C kernel now accepts an outFp32 flag and emits
  the exact FP32 accumulator; DeterministicAttentionAscendOp.forward_fp32
  exposes it (the twin of the CUDA op's forward_fp32).
- tests/test_det_gemm_ascend.py backward reference updated to the FP32
  matmul VJP (the gradient-accuracy gold semantics).

Verified on device (Ascend 910B, CANN 9.0.0): C2 23/23, C8 88 green /
0 red / 8 N/A, C3/C4 invariance bitwise, det_gemm/attention operator
suites green (34 + 26), and the C10 full-model gate now runs all eight
cells with real backward; parity aggregates pass (max_abs_dlogp 1.7e-6).
The C10 selected_logp config invariance still drifts on three pairs
(BN/padded_left 0.12, B1-singleton/chunked 0.07, B1-singleton/full
1 ULP) - candidate-side model-wiring gaps tracked for follow-up. The
contract's FP32-reference cell cannot run on 64 GB HBM (needs ~4.7 GiB
more; the CUDA reference ran on an 80 GB H20).

Co-Authored-By: Claude Code <noreply@anthropic.com>
Signed-off-by: Zhang Jian <jianmusings@gmail.com>
The model feeds the selected-logp a non-contiguous slice
(score_logits[:, :-1]), and FusedLogpAscendOp.apply silently fell back to
the native torch log-softmax for non-contiguous inputs. The native path's
per-row numerics depend on the batch layout, so the B1-singleton-aggregate
cell's logp differed from the BN cell's by 1-2 fp32 ULP (19/27 tokens) and
broke the C10 forward_invariance judgment (bitwise required). The wrapper
now materializes the logits so the batch-invariant Ascend kernel runs for
every NPU input; the B1-vs-BN selected_logp comparison is bitwise (0/27
diffs on the full-model gate cells).

Co-Authored-By: Claude Code <noreply@anthropic.com>
Signed-off-by: Zhang Jian <jianmusings@gmail.com>
… tiling)

Left padding shifted the physical 64-key tile boundaries, and the softmax
denominator (sumExp) is reduced per physical tile before the per-tile
results are summed -- so the FP32 addition grouping of the valid keys
depended on where the padding sat, and the FP32 composite edges amplified
the 1-ULP-level difference across the 36 layers into a visible logp drift
(BN/padded_left selected_logp max_abs 0.1197). The tiles are now anchored
to the first valid key (keyBegin), so the valid keys always start at the
first position of the first tile; the masked lanes are additionally zeroed
after the Exp so they contribute exactly zero regardless of the vector
Exp's behavior on the -FLT_MAX sentinel. Fully-masked batches fall through
to the existing out=0 / lse=-inf path.

Verified on device: the attention op's FP32 output and LSE are bitwise
identical across left-pad lengths 0/1/63/64/65, and the BN/padded_left
selected_logp is bitwise identical to BN/full (0/27 tokens, was 26/27).

The B1-singleton/chunked cell still drifts: traced to a 1-bf16-ULP K/V
divergence at layer 3 key 2 (the q_proj matches bitwise while k_proj /
v_proj differ by one ULP) -- the chunked path's projection chain, not the
attention kernel itself; tracked for follow-up.

Co-Authored-By: Claude Code <noreply@anthropic.com>
Signed-off-by: Zhang Jian <jianmusings@gmail.com>
torch mean/sum select shape-dependent reduction kernels on NPU and flip
single-ULP results between batch layouts (verified: 52/140 rows flip
between [1,7,H] and [1,20,H] on the same data). The canonical and native
RMSNorm forwards computed the rstd with the torch mean, so the chunked
path's [1,chunk,H] slices and the full path's [1,20,H] batch produced
ULP-different rstd values; the difference entered at layer 3, amplified
through the FP32 composite edges, and reached 0.07 at the selected logp
(the B1-singleton/chunked C10 invariance failure).

The native reference, the Ascend op, and the canonical path now share one
shape_invariant_rstd helper: the sum of squares is reduced in FIXED
32-wide chunks first, so the intermediate shapes (and hence the reduction
kernels) never depend on the batch layout, and the rstd is bitwise
identical for every layout on every device. The chunked cell's internal
stateful-prefill consistency check passes, and the BN/padded_left,
B1-singleton/full, and B1-singleton/chunked selected_logp maps are all
bitwise identical to BN/full (0/27 each; all three were non-zero before).

tests/test_rms_norm.py's manual reference uses the shared helper (the
implementation's formula changed; the test's independent formula mirrors
it). The gtest acceptance checks are untouched.

Co-Authored-By: Claude Code <noreply@anthropic.com>
Signed-off-by: Zhang Jian <jianmusings@gmail.com>
The forward rstd was made shape-invariant earlier, but the backward's
dx dot product s = sum(dy * w * x, dim=-1) still used the plain torch sum,
whose reduction kernel is selected by shape on NPU and flips single-ULP
results between batch layouts. The resulting dx differences propagated to
every upstream parameter gradient: the B1-singleton/chunked cell differed
from B1/full on 694/2394 weight gradients (max_abs 0.031).

The ordinary and canonical backward now share _rms_norm_backward_rows,
whose hidden-dim reduction is an explicit adjacent-pair FP32 tree
(_fixed_row_sum) — elementwise adds whose pairing depends only on the
hidden dimension, never on the row count. The ordinary backward reduces
the dweight rows with the shared reduce_rows_fp32; the canonical backward
keeps its session fold over the logical rows unchanged.

Adds tests/test_ascend_rmsnorm_backward_partition.py: CPU/NPU partition
regressions covering the fixed-pair sum (incl. odd widths and bf16
inputs), a float64-autograd backward oracle, and chunk-boundary
independence for the canonical embedding + norm gradients.

Verified on device: 20 new tests pass and the chunked cell's weight
gradients are now bitwise identical to B1/full (0/399, was 694/2394
differing). The gtest checks and references are untouched.

Signed-off-by: Zhang Jian <jianmusings@gmail.com>
Co-Authored-By: Claude Code <noreply@anthropic.com>
Signed-off-by: Zhang Jian <jianmusings@gmail.com>
@zhangj1an
zhangj1an force-pushed the feat/ws1-ascend-closeout branch from 8296207 to d452e95 Compare September 12, 2026 07:27
…action

The attention's VJP (the fp32 reference backward) ran the torch
softmax/matmul on the padded layout, where left/right padding shifts the
valid values inside the reduction trees and flips ULPs in dq/dk/dv (up to
7.0 between pad sides for identical logical tokens); the differences then
propagated to every upstream parameter gradient (BN/padded_left: 390/2394
weight gradients differing, max_abs 0.031). The backward now compacts the
valid tokens into the logical order before the VJP and scatters the
gradients back, so the reductions are padding-invariant and the padded
positions receive zero grads.

Verified on device: the op-level dq/dk/dv are bitwise identical between
the pad sides (0.0), the BN/padded_left weight gradients are bitwise
identical to BN/full (0/399, was 390 differing), and the attention /
rmsnorm / partition suites pass (137 passed, 32 skipped). The gtest checks
and references are untouched.

Signed-off-by: Zhang Jian <jianmusings@gmail.com>
Co-Authored-By: Claude Code <noreply@anthropic.com>
Signed-off-by: Zhang Jian <jianmusings@gmail.com>
@zhangj1an
zhangj1an force-pushed the feat/ws1-ascend-closeout branch from 16bb4ae to 2993cff Compare September 12, 2026 09:14
zhangj1an and others added 3 commits September 12, 2026 19:13
…nvariance

Replace the torch-compaction VJP with a dedicated Ascend C backward
(deterministic_attention_backward_ascend.asc): three stream-ordered
launches (rows/dV/dK) that recompute P/dS and the dq/dk/dv VJP with fixed
keyBegin-anchored logical reduction orders. Gradients are now bitwise
invariant to the batch layout and to where padding sits across physical
lengths, closing the remaining PR RL-Align#406 follow-up (valid Sv=63/64 with
pad=5 previously showed 5e-4/2.4e-4 residuals; now 0.0 bitwise for
Sv=63/64/65/96/128 on both pad sides).

FP16 (an optional contract row) falls back to the torch VJP path, since
the kernel is bf16-only. test_backward_grads now resolves the contract's
gradient_accuracy/attention row and compares candidate grads against the
FP32-kept reference VJP per the WS1 precision standard.

Verified on device: 26/26 attention tests; boundary padding invariance
bitwise; the full C10 gate reports gradient_invariance 2394/2394 with
max_abs 0.0 and forward invariance 7/7 bitwise.

Signed-off-by: Zhang Jian <jianmusings@gmail.com>
Co-Authored-By: Claude Code <noreply@anthropic.com>
…B HBM

The FP32-reference full-model backward OOMs the 64 GB HBM (~4.7 GiB
short), so the C10 reference cell runs in BF16 on this host (documented
in the PR description): the gold topology resolves family='pytorch' with
a plain matmul and the reference model builds in bfloat16. The accuracy
judgment must be re-assessed with the official FP32 reference on a
larger-memory device.

Signed-off-by: Zhang Jian <jianmusings@gmail.com>
Co-Authored-By: Claude Code <noreply@anthropic.com>
The FP32-reference full-model backward OOMs the 64 GB HBM with resident
weights (~4.7 GiB short). The reference cell now keeps the FP32 weights
CPU-resident (Qwen3DenseWeightsOffloaded) and pages each weight onto the
NPU per access: autograd holds each copy only until its VJP consumes it,
and the FP32 gradients accumulate on the CPU leaves, so the peak HBM is
~36 GB instead of ~70 GB. The paging copies are exact, so the reference
numerics are bitwise identical to the resident-FP32 model (verified on
device: layer-0/1 forward and all gradients bitwise, offloaded vs
resident).

The gate now measures the accuracy judgments against the official FP32
reference on this host: selected_logp max_abs 0.0857 (atol 0.06) and
44/798 gradient rows fail at near-zero reference-gradient elements
(max_abs 0.11-1.88), a candidate-side accuracy gap against the contract
tree reference (CUDA H20: 798/798 at 0.1034). Invariance and parity are
unaffected.

Signed-off-by: Zhang Jian <jianmusings@gmail.com>
Co-Authored-By: Claude Code <noreply@anthropic.com>
@zhangj1an zhangj1an changed the title [WS1][Ascend] #266 closeout on NPU: ascend_bf16 as a third required profile [WS1][Ascend] #266 closeout on NPU: WS1 full Qwen3-8B Dense train-inference closeout for ascend_bf16 — BF16 execution vs FP32 reference Sep 12, 2026
@zhangj1an zhangj1an changed the title [WS1][Ascend] #266 closeout on NPU: WS1 full Qwen3-8B Dense train-inference closeout for ascend_bf16 — BF16 execution vs FP32 reference [WS1][Ascend] Qwen3-8B Dense train-inference closeout for BF16 execution vs FP32 reference Sep 12, 2026

@Flink-ddd Flink-ddd left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@Flink-ddd
Flink-ddd merged commit 9daf565 into RL-Align:test Sep 12, 2026
9 of 10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants