Skip to content

fix: keep the last subsampled frame for unified mels of 8k+1 frames - #111

Merged
localai-org-maint-bot merged 1 commit into
mudler:masterfrom
mudler-agent:fix/offline-625-frame-parity
Oct 30, 2026
Merged

localai-org-maint-bot merged 1 commit into
mudler:masterfrom
mudler-agent:fix/offline-625-frame-parity

Conversation

@mudler-agent

Copy link
Copy Markdown
Contributor

What

Fix a numerical mismatch with NeMo for the Nemotron unified ASR checkpoint on clips whose trimmed mel has 8k + 1 frames (for example 625 frames, a clip of 100000 to 100159 samples).

Cause

The unified frontend trims the mel to NeMo's valid length, so every mel frame is valid. The offline subsampling still assumed the untrimmed convention (valid length = T - 1, one trailing pad frame from center padding). The three stride-2 convolutions map a length n to ceil(n / 2), so T and T - 1 give different valid output lengths when T = 8k + 1. There the last output frame was masked to zero (78 valid frames instead of 79), which changed the encoder output and dropped the last tokens.

Change

Subsampling::offline_entry_valid(T) returns T when the checkpoint trims the mel (parakeet.unified.valid_length) and T - 1 otherwise. The single, batched and tiled offline paths and valid_out_len use it. Other models keep T - 1 and are unchanged. Streaming paths pass explicit lengths and are not affected.

Verification

Unified f32 GGUF, English, against NeMo (encoder max abs difference, tokens):

Samples Mel frames Before After
100000 625 0.115, 42 vs 45 tokens 1.8e-7, 45/45
101280 633 not run 4.8e-7, 43/43
98720 617 not run 4.8e-7, 45/45
100160 626 (control) not run 4.8e-7, 45/45
  • New checkpoint-free test of the valid output length with the gate on and off (test_unified_synthetic).
  • ctest -LE model: all pass on a CPU build.
  • Not run: German, f16, GPU.

This also explains the two failing runs of test_unified_stream seen with a window of exactly 625 frames.

🤖 Generated with Claude Code

The unified frontend trims the mel to NeMo's valid length, so every
mel frame is valid. The offline subsampling still assumed the
untrimmed convention (valid length = T - 1, one trailing pad frame
from center padding). The three stride-2 convolutions map a length n
to ceil(n / 2), so T and T - 1 give different valid output lengths
when T = 8k + 1, for example 625 frames (a clip of 100000 to 100159
samples). There the last output frame was masked to zero: 78 valid
frames instead of 79. The encoder output differed from NeMo by 0.115
and the transcript lost its last tokens.

Take the entry valid length as T when the checkpoint trims the mel
(parakeet.unified.valid_length), in the single, batched and tiled
offline paths. Other models keep T - 1 and are unchanged.

Add a checkpoint-free test of the valid output length with the gate
on and off.

Assisted-by: Claude:claude-sonnet-5-5 [Claude Code]
@localai-org-maint-bot
localai-org-maint-bot merged commit 2eac2e7 into mudler:master Oct 30, 2026
7 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.

3 participants