Skip to content

fix(deepseek-v4): use vanilla RMSNorm weight semantics#315

Merged
SeedSource merged 4 commits into
Avarok-Cybersecurity:mainfrom
SeedSource:fix/v4-vanilla-rmsnorm
Jul 14, 2026
Merged

fix(deepseek-v4): use vanilla RMSNorm weight semantics#315
SeedSource merged 4 commits into
Avarok-Cybersecurity:mainfrom
SeedSource:fix/v4-vanilla-rmsnorm

Conversation

@SeedSource

Copy link
Copy Markdown
Collaborator

Summary

DeepSeek-V4 ships vanilla RMSNorm weights (out = x * w / rms), including values far from 1. Atlas was applying the offset-from-1 storage convention to them: the loader pre-subtracted 1.0 and stored bf16(w - 1), so the offset-from-1 rms_norm kernel (out = x * (1 + w) / rms) would recover 1 + (w - 1) = w.

That round-trip is exact only when w is near 1. V4's norm weights are ≈ 0.03, so it stored ≈ −0.97, and BF16's rounding error there becomes a large relative error on the weight once 1 is added back — catastrophic cancellation. For weights that straddle zero it is worse than degradation: bf16(w - 1) rounds to exactly -1.0, so 1 + (-1.0) collapses the weight to zero, destroying sign and magnitude together.

This PR loads V4's norm weights exactly and dispatches the vanilla RMSNorm kernelkernels/gb10/common/rms_norm_vanilla.cu, which already exists in-tree and is already used by the DFlash drafter for exactly this reason. V4 was simply wired to the wrong kernel.

Important

This is a correctness fix. It is NOT a resolution of the remaining DeepSeek-V4 parity gap.
On our frozen 8-question eval the score moved 12/40 → 15/40, but loop degeneration remained in 8/8 questions and every answer still finished on length rather than a clean stop. The dominant failure mode is unchanged and still under investigation. Please do not read this PR as "V4 is fixed."

Measured — all 249 norm tensors in the checkpoint

norm class tensors weight range rel. err (mean / max) sign flips after this PR
attn_norm 46 0.0084 … 2.50 3.44 % / 16.4 % 0 0
q_norm 46 0.0098 … 0.240 3.52 % / 19.3 % 0 0
kv_norm 46 −0.0332 … 2.00 4.94 % / 100 % 23 0
ffn_norm 46 0.0259 … 0.797 0.49 % / 5.66 % 0 0
compressor.norm 62 −0.226 … 3.83 55.9 % / 100 % 423 0
final_norm 1 −0.0114 … 0.555 0.34 % / 31.6 % 0 0

The fix is exactly lossless: the checkpoint is already BF16, so bf16(w) == w and the new error is 0.00e+00 on every tensor.

Why this went unnoticed

The offset round-trip is bit-exact when w ≳ 0.63 — measured on-device, zero error across the near-one (0.63–0.99) and large (1.0–3.83) regimes. That describes every other model family Atlas serves, all of which store genuine offsets (initialised to 0) or weights near 1. DeepSeek-V4 is the first checkpoint whose norm weights sit near zero. A unit test pins this property in both directions so it cannot regress.

Scope

  • Explicit model predicateships_vanilla_norm_weights(config) := config.model_type == "deepseek_v4". Not inferred from weight statistics.
  • Every V4 checkpoint norm weight loads exactly (dense_auto); every launch consuming one dispatches rms_norm_vanillaattn_norm, ffn_norm, q_norm, kv_norm, compressor.norm, the final norm, and the MTP norms.
  • The unweighted q_b_norm normalize is deliberately left on the offset kernel. It is driven by a zero-filled weight buffer (1 + 0 = 1), so pointing it at the vanilla kernel would multiply the Q path by zero. Only the norms that consume a checkpoint weight are switched.
  • V4 without the mHC highway now fails at load. Those fused residual_add_rms_norm / rms_norm_residual paths are offset-only, have no vanilla twin in-tree, and would silently apply x * (1 + w) to an exactly-loaded weight. Better to refuse than to serve wrong numbers.
  • dense_minus_one is removed — V4 was its only caller. The offset loader existed solely for this model.
  • No MoE, routing, attention, or other model family is touched. Models that genuinely store offsets (Qwen3-Next, init 0) keep dense + the offset kernel and are bit-exact unchanged.

Tests

Unit (norm_convention_tests, runs in CI — no GPU needed):

  • only deepseek_v4 takes the vanilla path; qwen3_next / qwen3_5_moe / deepseek_v3 / llama / … all keep offset-from-1
  • the offset round-trip is bit-exact for weights ≥ 0.63 (the regression guarantee for every other family)
  • it is > 1 % wrong on real attn_norm values, ~20 % wrong at q_norm's tail
  • it collapses a small negative compressor weight to exactly zero

Device (crates/spark-model/examples/rmsnorm_vanilla_microtest.rs) — the production kernels against an F64 host formula:

  • 120 cells (6 weight regimes × 4 hidden dims × 5 seeds), 0 failures
  • the offset path is bit-exact unchanged for non-V4 weights
  • the unweighted q_b_norm normalize is bit-exact unchanged
  • old vs new on V4's own distributions: old = 16.9 % / 20.1 % / 100 % max rel. error on q_norm / attn_norm / straddle-zero; new = 0 on all three

CI gates run locally, all green: cargo fmt --all --check, cargo clippy --workspace --tests, cargo test --workspace (0 failures).

Validation

Served EP=2 on 2× DGX Spark (GB10, SM121), native-MXFP4 DeepSeek-V4-Flash, dual-rail RoCE. Baseline and fixed images were built from the same Dockerfile and launcher, differing only in this change, with the spark binary sha256 verified inside the running container on both nodes.

🤖 Generated with Claude Code

https://claude.ai/code/session_015RxQjRmdoT6qfWofRnmqfw

DeepSeek-V4 ships HF-vanilla RMSNorm weights (`out = x * w / rms`), including
values far from 1. Atlas applied the offset-from-1 storage convention to them:
the loader pre-subtracted 1.0 and stored `bf16(w - 1)` so the offset-from-1
`rms_norm` kernel (`out = x * (1 + w) / rms`) would recover `1 + (w - 1) = w`.

That round-trip is exact only when `w` is near 1. DeepSeek-V4's norm weights are
around 0.03, so it stored about -0.97, and BF16's rounding error there becomes a
large RELATIVE error on the weight once 1 is added back (catastrophic
cancellation). Measured across all 249 norm tensors in the checkpoint:

  attn_norm        3.44 % mean relative error, 16.4 % max
  q_norm           3.52 % mean, 19.3 % max
  kv_norm          4.94 % mean, 100 % max, 23 sign flips (layers.4 is near zero)
  compressor.norm  55.9 % mean, 100 % max, 423 sign flips
  ffn_norm         0.49 % mean
  final_norm       0.34 % mean, 31.6 % max

Weights that straddle zero are not merely degraded: `bf16(w - 1)` rounds to
exactly -1.0, so `1 + (-1.0)` collapses the weight to zero, destroying sign and
magnitude together.

This loads V4's norm weights exactly and dispatches the vanilla RMSNorm kernel,
which already exists in-tree (`kernels/gb10/common/rms_norm_vanilla.cu`) and is
already used by the DFlash drafter for precisely this reason.

Scope:
  - guarded by an explicit model predicate (`model_type == "deepseek_v4"`), not
    inferred from weight statistics
  - every V4 checkpoint norm weight loads exactly (`dense_auto`); every launch
    consuming one dispatches `rms_norm_vanilla` (attn_norm, ffn_norm, q_norm,
    kv_norm, compressor.norm, final norm, MTP norms)
  - the UNWEIGHTED q_b_norm normalize keeps the offset kernel and its zero-filled
    weight buffer (1 + 0 = 1). Pointing it at the vanilla kernel would multiply
    by zero
  - V4 without the mHC highway now fails at load: the fused residual+norm kernels
    are offset-only with no vanilla twin, and would silently apply (1 + w)
  - `dense_minus_one` is removed. V4 was its only caller
  - no MoE, routing, attention, or other model family is touched. Models that
    genuinely store offsets (Qwen3-Next, init 0) keep `dense` + the offset kernel

Verification:
  - unit tests (`norm_convention_tests`): the offset round-trip is bit-exact for
    weights >= 0.63 (which is why every other family was unaffected), >1 % wrong
    for real attn_norm values, ~20 % wrong at q_norm's tail, and collapses a small
    negative compressor weight to zero
  - device test (`examples/rmsnorm_vanilla_microtest.rs`): the production kernels
    against an F64 host formula, 120 cells (6 weight regimes x 4 hidden dims x
    5 seeds), 0 failures. The offset path and the unweighted normalize are both
    bit-exact unchanged

This is a correctness fix. It is NOT a resolution of the remaining DeepSeek-V4
parity gap: on a frozen 8-question eval the score moved 12/40 -> 15/40, but the
loop degeneration remained in 8/8 questions, so the dominant failure is unchanged
and still under investigation.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015RxQjRmdoT6qfWofRnmqfw
SeedSource and others added 3 commits July 13, 2026 07:12
The `typos` linter flags `tru` as a misspelling of `true`/`thru`. It was an
abbreviation for the F64 ground-truth vector; spell it out.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015RxQjRmdoT6qfWofRnmqfw
…est (typos CI)

The previous rename used a word boundary, which does not split on an underscore,
so `pred_tru` survived. Spell it out.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015RxQjRmdoT6qfWofRnmqfw
@SeedSource SeedSource merged commit d9ff867 into Avarok-Cybersecurity:main Jul 14, 2026
12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant