Skip to content

Fix Moonshine training-loss double-shift (train against labels, not labels[..., 1:])#46784

Merged
Rocketknight1 merged 3 commits into
huggingface:mainfrom
Incheonkirin:fix-moonshine-loss-double-shift
Jun 24, 2026
Merged

Fix Moonshine training-loss double-shift (train against labels, not labels[..., 1:])#46784
Rocketknight1 merged 3 commits into
huggingface:mainfrom
Incheonkirin:fix-moonshine-loss-double-shift

Conversation

@Incheonkirin

Copy link
Copy Markdown
Contributor

What does this PR do?

Moonshine right-shifts labels into decoder_input_ids, then computes the loss with self.loss_function (ForCausalLMLoss), which shifts again, so it trains against labels[..., 1:]. Switch to a plain CrossEntropyLoss, matching WhisperForConditionalGeneration/BartForConditionalGeneration and the VisionEncoderDecoderModel fix in #40863 for the same issue (#40111).

Only the training loss (when labels are passed) changes; logits and generate() are unchanged, and -100 stays ignored. Adds a regression test, with and without -100.

@Rocketknight1 Rocketknight1 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Yes, this looks good, and the test is nice!

Moonshine right-shifts labels into decoder_input_ids, then computes the loss via
ForCausalLMLoss, which shifts again, so it trains against labels[..., 1:]. Use a
plain CrossEntropyLoss instead, matching Whisper/Bart and the VisionEncoderDecoder
fix in #40863. Adds a regression test (with and without -100 padding).
@Rocketknight1
Rocketknight1 force-pushed the fix-moonshine-loss-double-shift branch from cf51c69 to 0263a7a Compare June 22, 2026 13:20
@Rocketknight1
Rocketknight1 enabled auto-merge June 22, 2026 13:20
@Rocketknight1

Copy link
Copy Markdown
Member

cc @Incheonkirin I'm seeing the CI complain about the modular conversion. Can you rerun make fix-repo?

@HuggingFaceDocBuilderDev

Copy link
Copy Markdown

The docs for this PR live here. All of your documentation changes will be reflected on that endpoint. The docs are available until 30 days after the last update.

auto-merge was automatically disabled June 22, 2026 14:51

Head branch was pushed to by a user without write access

@Rocketknight1

Copy link
Copy Markdown
Member

make fix-repo propagated the changes to moonshine-streaming too - can we copy the test over there too to make sure we don't break it?

make fix-repo propagated the loss fix to the streaming model, so the same no-double-shift test should guard it there too.
@github-actions

Copy link
Copy Markdown
Contributor

[For maintainers] Suggested jobs to run (before merge)

run-slow: moonshine, moonshine_streaming

@github-actions

Copy link
Copy Markdown
Contributor

CI Dashboard: View test results in Grafana

@Rocketknight1
Rocketknight1 enabled auto-merge June 24, 2026 11:10
@Rocketknight1

Copy link
Copy Markdown
Member

Yep, LGTM now!

@Rocketknight1
Rocketknight1 added this pull request to the merge queue Jun 24, 2026
Merged via the queue into huggingface:main with commit d8c2354 Jun 24, 2026
34 checks passed
OmkumarSolanki added a commit to OmkumarSolanki/transformers that referenced this pull request Jun 26, 2026
…t labels[..., 1:])

forward right-shifts the target into decoder_input_ids, then ForCausalLMLoss
shifts the labels again, so the model trains against labels[..., 1:]. Use a
plain CrossEntropyLoss instead, matching Whisper/Bart and the Moonshine fix
(huggingface#46784). Adds a regression test, with and without -100.
Stanley00 pushed a commit to stanley-fork/hf-transformers that referenced this pull request Jun 29, 2026
huggingface#46898)

* Fix Florence2 training-loss double-shift (same pattern as Moonshine huggingface#46784)

Florence2ForConditionalGeneration shifts labels right via shift_tokens_right()
to create decoder_input_ids, then calls self.loss_function() which maps to
ForCausalLMLoss — that function shifts labels again internally. This causes
the model to train against labels[..., 1:] instead of labels.

Replace self.loss_function() with plain CrossEntropyLoss, matching the fix
applied to Moonshine in huggingface#46784 and CohereASR.

* Fix import sort order for ruff I001

* Use shift_labels parameter instead of CrossEntropyLoss (per reviewer feedback)

* Fix ruff formatting (one param per line)

* Use do_shift_labels flag: only skip shift when shift_tokens_right() was called (per reviewer)

* Simplify: always pass shift_labels=labels (per reviewer feedback)
OmkumarSolanki added a commit to OmkumarSolanki/transformers that referenced this pull request Jun 29, 2026
…t labels[..., 1:])

forward right-shifts the target into decoder_input_ids, then ForCausalLMLoss
shifts the labels again, so the model trains against labels[..., 1:]. Use a
plain CrossEntropyLoss instead, matching Whisper/Bart and the Moonshine fix
(huggingface#46784). Adds a regression test, with and without -100.
eliaghazal added a commit to eliaghazal/transformers that referenced this pull request Jul 17, 2026
Moonshine's huggingface#46784 fix used a plain CrossEntropyLoss, dropping
num_items_in_batch handling; PPFormulaNet overrides Florence2's forward
and missed the huggingface#46898 fix, double-shifting labels. Both now pass labels
as shift_labels through self.loss_function, keeping gradient
accumulation reduction. Regression tests use a global token count that
differs from the per-step count so the summed path is distinguishable.
pull Bot pushed a commit to Zezo-Ai/transformers that referenced this pull request Jul 21, 2026
…ngface#46784) (huggingface#46895)

* Fix CohereASR training-loss double-shift (same as Moonshine fix huggingface#46784)

CohereAsrForConditionalGeneration shifts labels right via shift_tokens_right()
to create decoder_input_ids, then calls self.loss_function() which maps to
ForCausalLMLoss — that function shifts labels again internally. This causes
the model to train against labels[..., 1:] instead of labels.

Replace self.loss_function() with plain CrossEntropyLoss, matching the fix
applied to Moonshine in huggingface#46784.

* Use shift_labels parameter instead of CrossEntropyLoss (per reviewer feedback)

* Use do_shift_labels flag: only skip shift when shift_tokens_right() was called (per reviewer)

* Add test for training loss no double shift

* Retrigger CI

* Fix test: use ParakeetEncoderModelOutput with attention_mask

* Simplify: always pass shift_labels=labels (per reviewer feedback)
OmkumarSolanki added a commit to OmkumarSolanki/transformers that referenced this pull request Jul 22, 2026
Encoder-decoder LM heads right-shift their targets into decoder_input_ids, so
their logits are already position-aligned with `labels`. Two related bugs
mishandle that alignment:

- PPFormulaNet (and, pre-huggingface#46784, Moonshine) routed `labels` through
  ForCausalLMLoss without `shift_labels`, so the loss shifted a second time and
  the model trained against labels[..., 1:].
- Trainer._loss_shifts_labels counted num_items_in_batch over labels[..., 1:]
  for every ForCausalLMLoss head. Encoder-decoder heads have a real target at
  every non-`-100` position, so this under-counts targets (one position per
  sequence) and over-scales the gradient-accumulation loss.

Changes:
- trainer.py: _loss_shifts_labels additionally requires config.is_encoder_decoder
  to be false, so encoder-decoder heads count every valid label. Decoder-only
  heads are unchanged; Csm (decoder-only) is unaffected.
- pp_formulanet: compute the loss via self.loss_function with
  shift_labels=kwargs.pop("shift_labels", labels) (caller-safe, keeps
  num_items_in_batch normalization).
- moonshine / moonshine_streaming: route through self.loss_function with
  shift_labels=labels instead of a plain CrossEntropyLoss that ignored
  num_items_in_batch.

Tests: generic guard test_encoder_decoder_loss_no_double_shift in
tests/test_modeling_common.py, plus Trainer num_items, PPFormulaNet compute_loss
and Moonshine coverage.

Fixes huggingface#46901
SangbumChoi added a commit to SangbumChoi/transformers that referenced this pull request Jul 22, 2026
* upstream/main: (39 commits)
  Remove deprecated training args and `is_fast` property (huggingface#46917)
  Consistent output shape from `get_image_features` (huggingface#46405)
  Fix multi-device mxfp4 dequantization race in `_convert_moe_packed_tensors` (huggingface#47423)
  fix failed test cases for qwen3_omni_moe model (huggingface#47449)
  Fix Hunyuan-VL PIL image resize parity with reference preprocessing (huggingface#47233)
  Move `value` padding into the attention interfaces that need it (huggingface#47451)
  Simplify function dispatch for linear attention (huggingface#47450)
  [cache] Allow sliding window layers to be roll-backed for speculative decoding (huggingface#47447)
  Fix double-shifted training loss in GitForCausalLM (huggingface#47395)
  Fix CohereASR training-loss double-shift (same as Moonshine fix huggingface#46784) (huggingface#46895)
  Warn when `group_by_length` is silently ignored for iterable datasets (huggingface#47379)
  Update bug report list (huggingface#46607)
  Fix shape mismatch in KyutaiSpeechToText `generate()` last window (huggingface#46952)
  Optimize flash attention max seqlen computation in vision attention (huggingface#47170)
  fix: remove unreachable return in special token builder (huggingface#47420)
  Add Harry to slow CI (huggingface#47454)
  BLT: vectorize patch length processing (huggingface#47385)
  Fix `TrackioCallback` fails to log evaluation metrics after training ends (huggingface#46935)
  [Kimi] add integration tests (huggingface#47383)
  Fix typo in `MusicgenForCausalLM.generate()` (huggingface#46974)
  ...
OmkumarSolanki added a commit to OmkumarSolanki/transformers that referenced this pull request Jul 22, 2026
Encoder-decoder LM heads right-shift their targets into decoder_input_ids, so
their logits are already position-aligned with `labels`. Two related bugs
mishandle that alignment:

- PPFormulaNet (and, pre-huggingface#46784, Moonshine) routed `labels` through
  ForCausalLMLoss without `shift_labels`, so the loss shifted a second time and
  the model trained against labels[..., 1:].
- Trainer._loss_shifts_labels counted num_items_in_batch over labels[..., 1:]
  for every ForCausalLMLoss head. Encoder-decoder heads have a real target at
  every non-`-100` position, so this under-counts targets (one position per
  sequence) and over-scales the gradient-accumulation loss.

Changes:
- trainer.py: _loss_shifts_labels additionally requires config.is_encoder_decoder
  to be false, so encoder-decoder heads count every valid label. Decoder-only
  heads are unchanged; Csm (decoder-only) is unaffected.
- pp_formulanet: compute the loss via self.loss_function with labels=None and the
  aligned targets passed as shift_labels. shift_labels is popped from kwargs
  before the inner model call, so a caller-supplied value is honored, does not
  collide with the derived one, and does not leak into the inner model.
- moonshine / moonshine_streaming: same form, replacing a plain CrossEntropyLoss
  that ignored num_items_in_batch.

Tests:
- tests/test_modeling_common.py: generic guard
  test_encoder_decoder_loss_no_double_shift. It supplies the right-shifted
  decoder_input_ids itself and compares gradients of the reported loss w.r.t.
  output.logits against the aligned and the shifted cross-entropy, which stays
  decisive on testers whose initialization makes the logits near-uniform. No
  per-model branches.
- Dia and ProphetNet skip it via @unittest.skip in their own testers.
- tests/trainer/test_trainer.py: Bart encoder-decoder num_items_in_batch count.
- The Moonshine loss test also covers num_items_in_batch scaling and a
  caller-supplied shift_labels.

Fixes huggingface#46901
pull Bot pushed a commit to k-tahiro/transformers that referenced this pull request Jul 23, 2026
…els (huggingface#46903)

Fix encoder-decoder loss alignment and Trainer token counting

Encoder-decoder LM heads right-shift their targets into decoder_input_ids, so
their logits are already position-aligned with `labels`. Two related bugs
mishandle that alignment:

- PPFormulaNet (and, pre-huggingface#46784, Moonshine) routed `labels` through
  ForCausalLMLoss without `shift_labels`, so the loss shifted a second time and
  the model trained against labels[..., 1:].
- Trainer._loss_shifts_labels counted num_items_in_batch over labels[..., 1:]
  for every ForCausalLMLoss head. Encoder-decoder heads have a real target at
  every non-`-100` position, so this under-counts targets (one position per
  sequence) and over-scales the gradient-accumulation loss.

Changes:
- trainer.py: _loss_shifts_labels additionally requires config.is_encoder_decoder
  to be false, so encoder-decoder heads count every valid label. Decoder-only
  heads are unchanged; Csm (decoder-only) is unaffected.
- pp_formulanet: compute the loss via self.loss_function with labels=None and the
  aligned targets passed as shift_labels. shift_labels is popped from kwargs
  before the inner model call, so a caller-supplied value is honored, does not
  collide with the derived one, and does not leak into the inner model.
- moonshine / moonshine_streaming: same form, replacing a plain CrossEntropyLoss
  that ignored num_items_in_batch.

Tests:
- tests/test_modeling_common.py: generic guard
  test_encoder_decoder_loss_no_double_shift. It supplies the right-shifted
  decoder_input_ids itself and compares gradients of the reported loss w.r.t.
  output.logits against the aligned and the shifted cross-entropy, which stays
  decisive on testers whose initialization makes the logits near-uniform. No
  per-model branches.
- Dia and ProphetNet skip it via @unittest.skip in their own testers.
- tests/trainer/test_trainer.py: Bart encoder-decoder num_items_in_batch count.
- The Moonshine loss test also covers num_items_in_batch scaling and a
  caller-supplied shift_labels.

Fixes huggingface#46901
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.

3 participants