Skip to content

Auto-select the monolithic head-loss implementation (#507)#568

Merged
jlamypoirier merged 4 commits into
jlp_monolithic_head_lossfrom
jlp_auto_loss_implementation
Jul 20, 2026
Merged

Auto-select the monolithic head-loss implementation (#507)#568
jlamypoirier merged 4 commits into
jlp_monolithic_head_lossfrom
jlp_auto_loss_implementation

Conversation

@jlamypoirier

Copy link
Copy Markdown
Collaborator

Claude Opus 4.8 note: authored with Claude Code on behalf of @jlamypoirier.

Stacked on #549 (jlp_monolithic_head_loss); this PR only adds the config-ergonomics layer on top of the monolithic kernels.

Motivation

Using the fused kernels currently requires restructuring the head config into a nested monolithic wrapper and hand-setting use_triton:

head:
  losses:
    fused:
      type: monolithic
      use_triton: true
      losses: {cross_entropy: {type: label}, z_loss: {type: z_loss}}

That nested type appears in no example/production config. This PR lets losses stay flat and picks the implementation automatically:

head:
  loss_implementation: auto     # default — nothing to write in the common case
  losses: {cross_entropy: {type: label}, z_loss: {type: z_loss}}

What changed

  • loss_implementation head knob {auto, compiled, triton, per_loss}, default auto.
    • auto: fuse combinable losses; use triton when a group is triton-eligible and triton is available, else the compiled path.
    • compiled / triton: force a fused backend (triton errors at config time on an ineligible set).
    • per_loss: unfused per-loss behavior (the pre-change path).
  • LanguageModelHeadConfig.get_effective_losses() applies the default-CE fallback and groups combinable losses by effective logits scale (one softmax serves one scale) into an internally-synthesized MonolithicLossConfig. Non-combinable losses (e.g. DPO) stay standalone. Called from _validate, so --validate catches an ineligible triton set early.
  • Removed the user-facing type: monolithic from the dynamic-type registry; the class stays as the internal grouping vehicle. The head build loop, has_main_loss/registration logic, and metric names are unchanged.

Behavior note

auto is the default, so it changes the realized kernel for existing configs (including plain single-CE), moving them onto the fused path. Equivalence to per_loss is covered by the head/loss parity tests (rms-close; the fused path accumulates in fp32 and casts once, so it is at least as accurate). per_loss remains the escape hatch.

Tests

  • test_get_effective_losses (new): grouping by scale, singleton wrapping, DPO standalone, backend mapping, and config-time rejection of an ineligible triton set.
  • tests/layers/test_lm_head.py builds losses flat and drives the backend through loss_implementation (drops the manual wrapping); adds auto distillation variants (interpreter-safe distribution kernel).
  • tests/layers/test_lm_losses.py unchanged (it exercises the kernels directly, not via the registry).

CPU validation green: test_lm_head.py 144 passed / 54 skipped (skips = triton, unavailable on CPU), test_lm_losses.py 586 passed / 21 skipped, and fast-llm train gpt --config examples/mistral.yaml --validate accepts the new default. Not yet run on GPU: the triton label-family paths, auto→triton, and the full tests/models suite (the default-kernel change).

🤖 Generated with Claude Code

jlamypoirier and others added 4 commits July 17, 2026 13:54
Losses stay configured flat: the head groups combinable losses that share a
softmax into one fused kernel and picks the backend from a single head-level
`loss_implementation` knob (auto/compiled/triton/per_loss, default `auto`).
`auto` uses triton when a group is triton-eligible and triton is available,
else the compiled path; `per_loss` keeps the unfused per-loss behavior.

Grouping is by effective logits scale (one softmax serves one scale);
non-combinable losses (e.g. DPO) stay standalone. The nested `type: monolithic`
loss is removed as a user-facing concept — `MonolithicLossConfig` is now
synthesized internally by `LanguageModelHeadConfig.get_effective_losses`.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…507)

Guard against a user loss whose name collides with a synthesized fused-group
key (which would silently drop an entry), mirroring the `lm_head_loss` reserved
name. Split a compound initializer and trim a redundant comment.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The distributed-consistency model tests compare gradients across parallel
layouts. GRPO/GSPO clip the importance-sampling ratio at `1 ± epsilon`, and
that clip bound is a discontinuity: tiny floating-point differences between
layouts (single-GPU vs pipeline/tensor-parallel reduction order) flip a token
or segment across the boundary, changing its gradient contribution
discontinuously and intermittently blowing past the comparison tolerance —
the same failure mode as MoE top-k routing.

Disable the clip (large epsilons) for the `llama_grpo`/`llama_gspo` model
configs so the comparison is smooth. The clip itself stays covered by the
single-GPU `test_lm_losses`/`test_lm_head` tests, which don't compare across
layouts.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ments (#507)

- Reserve only the synthesized fused-group names (monolithic / monolithic_<index>)
  instead of the whole `monolithic` prefix, so flat loss names like `monolithic_kd`
  are allowed.
- Rename `named` -> `multiple_groups` and annotate the `effective` local.
- Drop redundant per-member comments on `LossImplementation`, keeping only the
  non-obvious `auto` fallback note.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@jlamypoirier
jlamypoirier merged commit f0c337b into jlp_monolithic_head_loss Jul 20, 2026
1 check passed
@jlamypoirier
jlamypoirier deleted the jlp_auto_loss_implementation branch July 20, 2026 21:47
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