Repository navigation
mel: drop the NeMo padding frame before the encoder (parakeet, canary, canary-qwen) (#252) - #255
Merged
Merged
Conversation
The constant-pad STFT emits floor(n/hop)+1 frames but NeMo counts floor(n/hop) as valid and masks the rest in pre_encode, attention and the conv module. We fed the padding frame unmasked, so whenever floor(n/hop) is a multiple of 8 it became an extra encoder frame that leaked into every real frame through attention, emptying or truncating transcripts. Drop it on the single and batch offline paths for non-kestrel checkpoints. Kestrel models (ultra) already mask it in-graph; streaming paths are untouched.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes #252. NeMo's centered STFT emits
floor(n/hop)+1mel frames but NeMo only countsfloor(n/hop)as valid and masks the last one through pre_encode, attention and the conv module. We fed it unmasked. Whenfloor(n/hop)is a multiple of 8 it becomes an extra encoder frame that bleeds into every real frame via attention, so parakeet clips of those lengths came back empty or cut off.The same bug was in canary and canary-qwen (same NeMo frontend, no length masking in our encoder). For canary-qwen the extra frame was also passed to the LLM as an extra audio token.
Now the padding frame is dropped before the encoder on the single and batch offline paths of parakeet (non-kestrel), canary and canary-qwen. Dropping it is equivalent to NeMo's masking for the valid frames. Ultra (kestrel) already masked it in-graph. Streaming paths are untouched: buffered streaming already masks it, and NeMo's cache-aware path keeps the frame.
Present since the original parakeet impl.
AI Assistance
Yes, AI assisted with the investigation, code and tests.
Validation
Parakeet:
--batch. Same output with or without the last sample.+1reproduces the old (broken) output exactly.validate.py all --family parakeet --variant parakeet-tdt-0.6b-v3: unchanged vs main (jfk exact).[256,256]local attention (Parakeet longform memory #199), which NeMo itself reproduces in local mode. Not addressed here.MelFrontendis a no-op for parakeet: v3 and ultra dumps and transcripts are bitwise identical to the first commit on both issue clips and jfk (CPU).Canary / canary-qwen, encoder vs NeMo on the 5.04s clip (valid frames):
LibriSpeech test-clean (2620), Q8_0 Metal, before vs after:
canary-1b-v2: 62 of the +78 come from one utterance (
672-122797-0008) that now loops on "The boy was a man of the world...". NeMo F32 produces exactly the same output; the old build only avoided it because of the padding frame (last encoder frame cos vs NeMo 0.93 -> 0.99998). The other changed utterances net +16, near-tie word choices. No NeMo WER baseline on file for 1b-v2.Tests:
ctestdefault passes;mel_unitand the real-model smokes (parakeet_real, decoder, multitalker e2e, buffered stream eos, stream/offline interleave) pass. clang-format clean.Not run: WER for canary-1b / canary-1b-flash (same code path as 180m-flash),
validate.pyfor canary / canary-qwen.