Skip to content

[loss] fix softmax collapse with improved ce perf - #5723

Closed
ruisizhang123 wants to merge 1 commit into
pytorch:mainfrom
ruisizhang123:ce_fix
Closed

ruisizhang123 wants to merge 1 commit into
pytorch:mainfrom
ruisizhang123:ce_fix

Conversation

@ruisizhang123

Copy link
Copy Markdown
Collaborator

This is a replicate and revision of #5661.

I don't have write access to the repo; thus, I ended up submitting a new PR here.......

…d path faster

_LossParallelCrossEntropy computed the NLL as logsumexp(z) - z_t and a target
gradient of p_t - 1. Once the target margin exceeds ~16.6, fp32 rounds p_t to
exactly 1, so the loss and the target gradient both become exactly 0 while the
non-target gradients stay nonzero. The accurate target gradient needs the
non-target mass, which forward now computes, so forward also prepares the
backward instead of saving log-probabilities.

Numerics (TP > 1 only):
- NLL = softplus(logsumexp_{j != t}(z_j) - z_t). The owner rank drops the
  target term before the sum, so no small terms are lost against exp(0) = 1.
- Target gradient: p_t - 1 below p_t = 1/4, else -p_non_target (one rounding
  instead of the sum and the divide).
- Ignored labels act as targets on no shard, so their loss and gradient stay
  finite and are zeroed by the label mask.
- Matches loss_parallel() up to fp32 rounding below the collapse margin; not
  bitwise identical to it or to the previous code.

Memory/perf: the best form differs between eager and compiled, so the two run
different code with the same math, gated on torch.compiler.is_compiling() or
_is_non_strict_tracing() (the latter covers GraphTrainer's make_fx tracing).
- Eager cannot fuse, so forward turns one fresh fp32 [T, V_local] buffer in
  place into exp, then softmax, then the saved gradient, and backward scales it
  in place. The input logits are never modified.
- Compiled forward drops the target with a column mask instead of an in-place
  write, so Inductor fuses the sum into one read of the logits. It saves the
  input plus per-row stats, and backward rebuilds the gradient in one fused
  kernel. An in-place target write would force the fp32 buffer to be
  materialized, which made a compiled single-path version 10% slower than main.
- cross_entropy_loss passes pred to the Function without .float(); the Function
  upcasts internally. Otherwise the compiled region saves an fp32 copy of the
  logits instead of the bf16 input. Loss and gradients are bitwise unchanged.

Measured on H100, V=128256, TP=2, T=16384, loss forward + backward:

            old TP CE (eager)  new TP CE (eager)  old TP CE (compile)  new TP CE (compile)
  memory    15.66 GiB          5.87 GiB           7.83 GiB             3.91 GiB
  time      38.96 ms           27.11 ms           13.15 ms             6.29 ms

Eager and compiled gradients agree to ~1e-6. On real C4 (Qwen3-0.6B, TP=2 x
FSDP=4, 2000 steps) the new CE trains like main: last-100-step mean loss
4.5414 vs 4.5335 compiled and 4.5411 vs 4.5475 eager, a gap that changes sign
between modes and is ~20x below batch-to-batch noise.

The TP=1 F.cross_entropy path (and the batch-invariant policy-stats path) is
unchanged and still collapses at large margins, so TP=1 and TP>1 losses now
differ in that regime. Goldens for TP > 1 runs are regenerated (fake_pg/a10g,
real_pg/a10g, real_pg/mi350x); goldens without TP use F.cross_entropy and are
unchanged.

Test Plan:
Confident-target accuracy against an fp64 reference, ignored rows with a -inf
fallback logit, input logits untouched, and eager vs aot_eager compiled parity
over fp32 and bf16 logits:

```
pytest tests/unit_tests/cpu/components/test_loss.py
```

This PR was authored with an AI assistant (Claude Code).
@pytorch-bot pytorch-bot Bot added the ciflow/fake-pg Run 1-GPU Fake PG integration tests label Nov 1, 2026
@meta-cla meta-cla Bot added the CLA Signed This label is managed by the Meta Open Source bot. label Nov 1, 2026
@pytorch-bot

pytorch-bot Bot commented Nov 1, 2026

Copy link
Copy Markdown

The following ciflow label(s) have been added but CI has not been triggered yet because the workflows are awaiting approval:

  • ciflow/fake-pg

Once a maintainer approves the workflows (scroll to the bottom of the PR page), the corresponding CI jobs will be triggered automatically. Please ping one of the reviewers if you do not have access to approve and run workflows.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ciflow/fake-pg Run 1-GPU Fake PG integration tests CLA Signed This label is managed by the Meta Open Source bot.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant