FP8 Attention training

Delta-Matching for reliable FP8 attention training

An overview of Delta-Matching Download video

Delta-Matching recomputes the softmax backward's row correction from the operands that backward actually uses. This lets all seven attention-core matmuls use FP8. On our default 1.67B GDN/GQA model, validation CE is 1.4162, against 1.4178 for BF16/FP32 and 1.8970 for naive FP8 with stale delta.

In this article

A run can follow reference loss for thousands of updates while selected attention channels drift. In our experiments, larger models show faster channel-gain drift. Training on more tokens (scaling up the data) can then reveal degradation in loss and downstream performance that shorter runs miss. This exposes a scaling barrier for stale-delta FP8 attention implementations when moving from small tests to industrial-scale training.

The saved-output shortcut assumes matching operands, but FP8 rounds forward and backward operands differently. We study the resulting mismatch from 569M to 5.29B parameters and from 8K to 64K context.

How does delta become stale?

Write down softmax backward

For one attention head with queries QQ, keys KK, values VV, and head dimension dd,

S=QK⊤/d,P=softmax⁡(S),O=PV.S=QK^\top/\sqrt{d},\qquad P=\operatorname{softmax}(S),\qquad O=PV.

Write dXdX for the loss gradient with respect to XX. Backward computes

dP=dO V⊤,δi=∑jPijdPij,dSij=Pij(dPij−δi).\begin{gathered} dP=dO\,V^\top,\qquad \delta_i=\sum_j P_{ij}dP_{ij},\\ dS_{ij}=P_{ij}(dP_{ij}-\delta_i). \end{gathered}

For a fixed query row ii, δi\delta_i does not depend on the key index jj. In exact arithmetic, ∑jPij=1\sum_jP_{ij}=1. Expanding the row sum and substituting the definition of δi\delta_i gives

∑jdSij=∑jPij(dPij−δi)=∑jPijdPij−δi∑jPij=∑jPijdPij−(∑ℓPiℓdPiℓ)∑jPij=∑jPijdPij−(∑ℓPiℓdPiℓ)⋅1=∑jPijdPij−∑ℓPiℓdPiℓ=0.\begin{aligned} \sum_j dS_{ij} &=\sum_j P_{ij}(dP_{ij}-\delta_i)\\ &=\sum_j P_{ij}dP_{ij}-\delta_i\sum_jP_{ij}\\ &=\sum_j P_{ij}dP_{ij} -\left(\sum_\ell P_{i\ell}dP_{i\ell}\right)\sum_jP_{ij}\\ &=\sum_j P_{ij}dP_{ij} -\left(\sum_\ell P_{i\ell}dP_{i\ell}\right)\cdot1\\ &=\sum_j P_{ij}dP_{ij}-\sum_\ell P_{i\ell}dP_{i\ell}\\ &=0. \end{aligned}

The third line replaces δi\delta_i with its weighted sum. The next line uses row normalization. The two remaining sums are identical because jj and ℓ\ell are just different names for the same key index.

This is the zero-row-sum constraint. Why should it hold? Adding the same constant to every logit in a row leaves softmax unchanged, so the gradient must sum to zero along that direction.

Why can we reuse the forward output?

Start from δi\delta_i and substitute dP=dO V⊤dP=dO\,V^\top. Here kk indexes value channels,

δi=∑jPijdPij=∑jPij(∑kdOikVjk)=∑j∑kPijdOikVjk=∑kdOik(∑jPijVjk)=∑kdOikOik=⟨dOi,Oi⟩.\begin{aligned} \delta_i &=\sum_j P_{ij}dP_{ij}\\ &=\sum_j P_{ij}\left(\sum_k dO_{ik}V_{jk}\right)\\ &=\sum_j\sum_k P_{ij}dO_{ik}V_{jk}\\ &=\sum_k dO_{ik}\left(\sum_j P_{ij}V_{jk}\right)\\ &=\sum_k dO_{ik}O_{ik}\\ &=\langle dO_i,O_i\rangle. \end{aligned}

We swap the finite sums and take dOikdO_{ik} outside the sum over jj, since it does not depend on jj. The remaining inner sum is Oik=∑jPijVjkO_{ik}=\sum_jP_{ij}V_{jk}, which gives the final dot product.

Attention kernels can therefore compute the correction from the saved output and recompute probabilities tile by tile during backward, without storing the full attention matrix. The shortcut works because both expressions use the same operands.

What changes in FP8?

Let P^\widehat P, V^\widehat V, and dO^\widehat{dO} denote the decoded FP8 operands, including their block scales. Here PP is the FP32 softmax output from the FP8 attention path, before probability quantization. It is not the probability matrix from a separate BF16 reference run.

The forward and backward products are

O^=P^V^,O^ik=∑jP^ijV^jk,dP^=dO^V^⊤,dP^ij=∑kdO^ikV^jk.\begin{aligned} \widehat O&=\widehat P\widehat V, &\widehat O_{ik}&=\sum_j\widehat P_{ij}\widehat V_{jk},\\ \widehat{dP}&=\widehat{dO}\widehat V^\top, &\widehat{dP}_{ij}&=\sum_k\widehat{dO}_{ik}\widehat V_{jk}. \end{aligned}

These hats label products of quantized operands; the products themselves use FP32 accumulation. To isolate operand rounding, assume that PP is normalized, both passes use the same effective V^\widehat V, and accumulation, reduction, and output-storage rounding introduce no further error.

Backward now uses dP^\widehat{dP}, but the conventional correction still uses the output gradient before its FP8 cast,

δistale=⟨dOi,O^i⟩,dSijstale=Pij(dP^ij−δistale).\begin{aligned} \delta_i^{\mathrm{stale}}&=\langle dO_i,\widehat O_i\rangle,\\ dS_{ij}^{\mathrm{stale}}&=P_{ij}(\widehat{dP}_{ij}-\delta_i^{\mathrm{stale}}). \end{aligned}

Expand the stale gradient's row sum in the same way,

εi=∑jdSijstale=∑jPij(dP^ij−δistale)=∑jPijdP^ij−δistale∑jPij=∑jPijdP^ij−δistale.\begin{aligned} \varepsilon_i &=\sum_j dS_{ij}^{\mathrm{stale}}\\ &=\sum_jP_{ij}(\widehat{dP}_{ij}-\delta_i^{\mathrm{stale}})\\ &=\sum_jP_{ij}\widehat{dP}_{ij} -\delta_i^{\mathrm{stale}}\sum_jP_{ij}\\ &=\sum_jP_{ij}\widehat{dP}_{ij}-\delta_i^{\mathrm{stale}}. \end{aligned}

Normalization still removes ∑jPij\sum_jP_{ij}, but the two remaining terms need not match. To see their operands explicitly, substitute both FP8 products and exchange the finite sums,

εi=∑jPijdP^ij−⟨dOi,O^i⟩=∑jPij(∑kdO^ikV^jk)−∑kdOik(∑jP^ijV^jk)=∑kdO^ik(∑jPijV^jk)−∑kdOik(∑jP^ijV^jk)=⟨dO^i,PiV^⟩−⟨dOi,P^iV^⟩.\begin{aligned} \varepsilon_i &=\sum_j P_{ij}\widehat{dP}_{ij} -\langle dO_i,\widehat O_i\rangle\\ &=\sum_jP_{ij}\left(\sum_k\widehat{dO}_{ik}\widehat V_{jk}\right)\\ &\quad-\sum_kdO_{ik}\left(\sum_j\widehat P_{ij}\widehat V_{jk}\right)\\ &=\sum_k\widehat{dO}_{ik}\left(\sum_jP_{ij}\widehat V_{jk}\right)\\ &\quad-\sum_kdO_{ik}\left(\sum_j\widehat P_{ij}\widehat V_{jk}\right)\\ &=\langle\widehat{dO}_i,P_i\widehat V\rangle -\langle dO_i,\widehat P_i\widehat V\rangle. \end{aligned}

Separate the two mismatches

Let Mi=⟨dO^i,P^iV^⟩M_i=\langle\widehat{dO}_i,\widehat P_i\widehat V\rangle. Adding −Mi+Mi=0-M_i+M_i=0 preserves the residual but lets us group one pair with the same output gradient and another with the same weighted values,

εi=⟨dO^i,PiV^⟩−Mi+Mi−⟨dOi,P^iV^⟩=[⟨dO^i,PiV^⟩−⟨dO^i,P^iV^⟩]+[⟨dO^i,P^iV^⟩−⟨dOi,P^iV^⟩]=⟨dO^i,(Pi−P^i)V^⟩+⟨dO^i−dOi,P^iV^⟩.\begin{aligned} \varepsilon_i &=\langle\widehat{dO}_i,P_i\widehat V\rangle-M_i\\ &\quad+M_i-\langle dO_i,\widehat P_i\widehat V\rangle\\ &=\Bigl[\langle\widehat{dO}_i,P_i\widehat V\rangle -\langle\widehat{dO}_i,\widehat P_i\widehat V\rangle\Bigr]\\ &\quad+\Bigl[\langle\widehat{dO}_i,\widehat P_i\widehat V\rangle -\langle dO_i,\widehat P_i\widehat V\rangle\Bigr]\\ &=\langle\widehat{dO}_i,(P_i-\widehat P_i)\widehat V\rangle\\ &\quad+\langle\widehat{dO}_i-dO_i,\widehat P_i\widehat V\rangle. \end{aligned}

In the first bracket, dO^i\widehat{dO}_i is shared, leaving (Pi−P^i)V^(P_i-\widehat P_i)\widehat V. In the second, P^iV^\widehat P_i\widehat V is shared, leaving dO^i−dOi\widehat{dO}_i-dO_i.

The first term comes from probability quantization. The score gradient uses PP, while the saved output uses P^\widehat P. The second comes from output-gradient quantization. The backward matmul uses dO^\widehat{dO}, while the saved-output correction uses dOdO.

When these errors do not cancel, the score gradient has a nonzero row sum. We call the mismatched correction stale delta. A related saved-output rounding problem, and the direct backward correction, were studied in BF16 training by Qiu and Yao.

What does stale delta do to training?

Keep PP and dP^\widehat{dP} fixed, and define a centered correction and gradient,

δicentered=∑ℓPiℓdP^iℓ,dSijcentered=Pij(dP^ij−δicentered).\begin{aligned} \delta_i^{\mathrm{centered}}&=\sum_\ell P_{i\ell}\widehat{dP}_{i\ell},\\ dS_{ij}^{\mathrm{centered}}&=P_{ij}(\widehat{dP}_{ij}-\delta_i^{\mathrm{centered}}). \end{aligned}

The residual above is εi=δicentered−δistale\varepsilon_i=\delta_i^{\mathrm{centered}}-\delta_i^{\mathrm{stale}}. Subtracting the gradients shows where the extra term enters,

dSijstale−dSijcentered=Pij(dP^ij−δistale)−Pij(dP^ij−δicentered)=PijdP^ij−Pijδistale−PijdP^ij+Pijδicentered=Pij(δicentered−δistale)=εiPij.\begin{aligned} dS_{ij}^{\mathrm{stale}}-dS_{ij}^{\mathrm{centered}} &=P_{ij}(\widehat{dP}_{ij}-\delta_i^{\mathrm{stale}})\\ &\quad-P_{ij}(\widehat{dP}_{ij}-\delta_i^{\mathrm{centered}})\\ &=P_{ij}\widehat{dP}_{ij}-P_{ij}\delta_i^{\mathrm{stale}}\\ &\quad-P_{ij}\widehat{dP}_{ij}+P_{ij}\delta_i^{\mathrm{centered}}\\ &=P_{ij}(\delta_i^{\mathrm{centered}}-\delta_i^{\mathrm{stale}})\\ &=\varepsilon_iP_{ij}. \end{aligned}

The two PijdP^ijP_{ij}\widehat{dP}_{ij} terms cancel. Rearranging gives dSijstale=dSijcentered+εiPijdS_{ij}^{\mathrm{stale}}=dS_{ij}^{\mathrm{centered}}+\varepsilon_iP_{ij}.

The extra term is proportional to the entire probability row. In a small gradient-descent step on the logits, this term alone can sharpen the row when εi<0\varepsilon_i<0 and flatten it when εi>0\varepsilon_i>0.

Positive and negative residuals can nearly cancel across rows without canceling within individual channels. Channel gains are the learned RMSNorm scales on query and key channels. In our diagnostics, channels favored by the sharpened rows also show consistent upward pressure in Adam's gain updates.

Attention-row perturbations, channel preference and update pressure, and key RMSNorm gain trajectories.
Diagnostics for the 1.67B GDN/GQA model. The row-update panels use a scaled diagnostic logit step on BF16-recomputed activations. The gain panel records training trajectories.

Around step 6,000, the last attention layer's key RMSNorm gain for channel 7 reaches about 5.4 under stale delta, versus 1.2 with Delta-Matching. Later, a few channels account for more than 99% of the sum of squared query-key gain products in affected layers. This concentration can amplify selected logit contributions and push softmax toward saturation.

These diagnostics associate the residual with gain growth, without isolating its contribution to gain updates. We therefore track channel gains alongside loss.

Does matching the backward operands fix it?

We can fix this mismatch by computing the correction from the operands used by softmax backward,

δimatched=∑jPijdP^ij,dSijmatched=Pij(dP^ij−δimatched).\begin{aligned} \delta_i^{\mathrm{matched}}&=\sum_jP_{ij}\widehat{dP}_{ij},\\ dS_{ij}^{\mathrm{matched}}&=P_{ij}(\widehat{dP}_{ij}-\delta_i^{\mathrm{matched}}). \end{aligned}

Substitute this correction into the row sum, just as in the first proof,

∑jdSijmatched=∑jPij(dP^ij−δimatched)=∑jPijdP^ij−δimatched∑jPij=∑jPijdP^ij−(∑ℓPiℓdP^iℓ)∑jPij=∑jPijdP^ij−(∑ℓPiℓdP^iℓ)⋅1=∑jPijdP^ij−∑ℓPiℓdP^iℓ=0.\begin{aligned} \sum_j dS_{ij}^{\mathrm{matched}} &=\sum_jP_{ij}(\widehat{dP}_{ij}-\delta_i^{\mathrm{matched}})\\ &=\sum_jP_{ij}\widehat{dP}_{ij} -\delta_i^{\mathrm{matched}}\sum_jP_{ij}\\ &=\sum_jP_{ij}\widehat{dP}_{ij} -\left(\sum_\ell P_{i\ell}\widehat{dP}_{i\ell}\right)\sum_jP_{ij}\\ &=\sum_jP_{ij}\widehat{dP}_{ij} -\left(\sum_\ell P_{i\ell}\widehat{dP}_{i\ell}\right)\cdot1\\ &=\sum_jP_{ij}\widehat{dP}_{ij} -\sum_\ell P_{i\ell}\widehat{dP}_{i\ell}\\ &=0. \end{aligned}

After using ∑jPij=1\sum_jP_{ij}=1, the two weighted sums contain exactly the same backward operands and cancel. This equality uses the normalization and rounding assumptions stated above.

The probabilities stay in FP32 for this correction, and dP^\widehat{dP} is the FP32 result of an FP8 matmul. All seven standard attention-core matmuls can use FP8, while softmax and reductions use higher precision.

Could we just match dOdO in the saved-output shortcut? Replacing dOdO with dO^\widehat{dO} removes the output-gradient mismatch, but leaves the probability mismatch. We test both choices in the default 1.67B experiment.

Attention method Validation CE
BF16/FP32 reference 1.4178 ± 0.0004
cuDNN/TE FP8 1.6105 ± 0.0600
Naive FP8 with stale delta 1.8970 ± 0.0296
Naive FP8 with consistent dOdO 1.4667 ± 0.0175
Delta-Matching 1.4162 ± 0.0006

Mean ± standard error over two initialization seeds, after 30B Nemotron-CC tokens. The GDN/GQA model interleaves Gated DeltaNet and grouped-query attention. Its reference already uses FP8 FFNs; the FP8 methods also quantize attention projections and the attention core.

Training loss and downstream evaluation for the default 1.67B GDN/GQA model.
A 1.67B GDN/GQA hybrid trained on 30B tokens. Delta-Matching tracks the BF16/FP32 reference in training loss and overall downstream performance. Loss curves show the first initialization; shaded bands show within-window batch ranges.

In the kernel, Delta-Matching restores zero row sums up to FP32 rounding before dSdS is cast to FP8. That cast introduces another row-sum residual. In our kernel probes, normalized post-cast row-sum residuals remain about 5.3 to 6.3 times larger with stale delta than with Delta-Matching. Ordinary FP8 rounding remains, and downstream scores vary across benchmarks. For example, default RULER-8K is 47.8% with Delta-Matching versus 52.3% for the reference.

What changes as the model gets larger?

We pretrain 569M and 5.29B GDN/GQA hybrids for 30B tokens each. Both use attention head dimension 256 and the default training recipe and precision scopes.

Parameters BF16/FP32 validation CE Stale-delta validation CE Delta-Matching validation CE
569M 1.5233 ± 0.0005 1.5399 ± 0.0144 1.5228 ± 0.0015
5.29B 1.3349 ± 0.0011 1.9327 ± 0.0349 1.3374 ± 0.0005

Mean ± standard error over two initialization seeds.

At 569M, stale delta leaves a modest loss gap and largely preserves downstream performance. At 5.29B, its RULER-8K score falls from the reference's 53.5% to 16.3%, while Delta-Matching scores 58.6%.

Channel gains and training loss for the 569M and 5.29B GDN/GQA models.
Channel gains cover the first 6,000 optimizer steps; loss curves cover pretraining. The gain panels retain their own vertical scales.

The 569M model shows little excess channel-gain growth. At 5.29B, selected gains grow rapidly, more steeply than in the 1.67B trace above over the same first 6,000 updates. Depth, width, and head geometry also change across these models, so the results establish a scaling trend across the tested configurations.

How do architecture and optimizer choices affect training?

Architecture and optimizer choices change how strongly stale delta affects training. We compare each configuration with its own BF16/FP32 reference.

Attention variants change when failure appears

We test three variants of the 1.67B GDN/GQA model.

  • QK normalization prevents outright collapse here, but leaves a large loss gap. Removing query and key RMSNorm makes stale-delta training diverge.
  • NoPE delays degradation. We remove RoPE from GQA while the Gated DeltaNet layers remain order-sensitive. Stale delta tracks the reference for longer, then develops a loss gap.
  • A larger head dimension does not fix the mismatch. At head dimension 256, stale delta ends at validation CE 2.0709 versus 1.4135 for its reference.
Training loss for NoPE, no QK normalization, and head dimension 256.
Three attention variants, each with its own BF16/FP32 reference. Removing QK normalization causes the earliest failure; NoPE delays it.

Delta-Matching stays within 0.0011 validation CE of all three references, with broadly comparable downstream performance. The head-dimension-256 study keeps FFNs and attention projections in BF16; NoPE and no QK normalization use the default precision scopes.

Model architecture can hide the damage

Replacing GQA with multi-head latent attention (MLA), or Gated DeltaNet with Kimi Delta Attention (KDA), does not remove the failure. After 30B tokens, stale delta raises validation CE by 0.5090 in GDN/MLA and 0.4230 in KDA/GQA. Delta-Matching stays within 0.0006 of their references, changing only the softmax-attention backward pass.

Training loss for 1.67B GDN/MLA, KDA/GQA, and Mamba-2/GQA hybrids.
Stale delta develops large loss gaps in GDN/MLA and KDA/GQA. Mamba-2/GQA retains near-reference loss at this scale. Delta-Matching tracks all three references.

Near-reference loss does not always mean healthy attention. After 30B tokens, the 1.67B Mamba-2/GQA hybrid retains broadly comparable downstream performance. A single-initialization probe nevertheless finds severely inflated gains in its last attention layer. Masking that layer's attention output hurts less than masking the reference's, suggesting compensation elsewhere in the network.

Last-attention-layer probes find little gain corruption in the 1.475B pure-GQA model, but severe corruption in a single-seed 2.58B run despite a small loss gap. Masking results again suggest compensation, possibly through its other attention layers. Head geometry, precision scope, and implementation also change between these models, so this comparison does not isolate parameter count.

Muon still needs a matched correction

We also train the default 1.67B hybrid for the full 28,610 updates, about 30B tokens, with Muon. The three methods share one initialization and data order. Muon updates hidden weight matrices; AdamW still updates the other parameters, including normalization gains. The reference uses FP8 FFNs, while the FP8 methods also quantize attention and its projections.

Muon attention method Validation CE RULER-8K CS9
BF16/FP32 reference 1.3988 53.0 59.2
Naive FP8 with stale delta 1.8077 26.2 47.2
Delta-Matching 1.3985 58.7 60.4

Full-training results from one initialization, without across-seed error bars. RULER-8K and CS9 are percentages; CS9 averages nine individual benchmark accuracies.

Muon improves reference validation CE over the paired AdamW run, from 1.4174 to 1.3988, but stale delta still degrades loss and downstream performance. Delta-Matching matches the Muon reference's validation loss and preserves downstream quality. A probe of the first 4,500 updates also shows excess channel-gain growth under both optimizer recipes. Switching hidden-matrix updates to Muon does not repair the backward mismatch.

How much does the long-context token budget matter?

We next continue pretraining the 1.67B GDN/GQA model from 8K to 64K context. Each method starts from the same completed BF16/FP32 checkpoint for its initialization seed, with a fresh optimizer. Training runs for 28,000 updates, about 29.4B additional tokens, at a peak learning rate of 8×10−48\times10^{-4} instead of the base recipe's 2.4×10−32.4\times10^{-3}.

Aligned channel-gain and training-loss traces during 8K-to-64K context extension.
Channel gain above, training CE below. Dashed markers indicate loss-gap onset. The cuDNN/TE curve is a successful first-seed rerun on the same batches; its original run diverged. Reported cuDNN/TE averages use the rerun and the second seed.

Stale-delta FP8 and cuDNN/TE FP8 build excess channel gain while their losses initially remain close to the reference. Visible loss gaps appear later. For stale delta, the gap exceeds 0.01 only after roughly 19,000 optimizer steps, or 20B tokens. Delta-Matching maintains near-reference gains and tracks BF16/FP32 training loss.

With tokens per optimizer step fixed, a larger training-token budget also means more updates. A short test of loss alone would miss the later degradation in these runs. The corpus expanded at step 18,000, however, and TE's amplitude history reset while optimizer state was preserved. This experiment does not isolate training duration, unique-data volume, or learning rate as the cause of the onset.

At 64K context, the mean RULER score is 45.6% for Delta-Matching and 46.0% for BF16/FP32, compared with 40.1% for stale delta and 38.5% for cuDNN/TE FP8.