Skip to content

Nemotron 3.5 Transcribe on CUDA: the language model and the audio encoder - #1631

Open
BobClawblaw wants to merge 10 commits into
ashhart:mainfrom
BobClawblaw:nemotron-transcribe-model
Open

BobClawblaw wants to merge 10 commits into
ashhart:mainfrom
BobClawblaw:nemotron-transcribe-model

Conversation

@BobClawblaw

Copy link
Copy Markdown
Contributor

What this changes

Nemotron 3.5 Transcribe (nvidia/NVIDIA-Nemotron-3.5-Transcribe-30B-A4B @ c421d6acdb436edd7ae00565f6414631fb9bd56d,
the bf16 release as published) on CUDA: its language model on the Nemotron family (NeMo's names, cuda_salm.zig), and
its audio encoder (families/nemo_audio: host log-mel, the 32-layer ASR encoder, the streaming Sortformer and their
fusion, nemo_audio.cu), with tensorfold audio-check writing every encoder stage under NeMo's names. Exact bf16
caches; no conversion. Part of #1549; stacked on #1630.

Receipt

  • Environment: one GB10 (sm_121); references are NeMo's model in bf16 and fp32 under torch.
  • Language model with NeMo's audio rows: 4 of 5 clips token-identical, including a 130-second clip's 379 tokens; the
    fifth writes "at 'em" for "at em", a near tie that bf16 logits settle the other way.
  • Encoder: at every stage our rows are as far from NeMo's fp32 rows as NeMo's own bf16 rows, on eight clips; speaker
    features identical.
  • SUMMARY nemo_audio: 7 kernels, 7 pass gates 1-4, 0 fail; 5 controls: 5 red as planted, 0 missed
  • Full contract on the stack's top: SUMMARY 452 kernels: 452 pass gates 1-4, 0 fail, 6 known findings; 325 controls: 325 red as planted, 0 missed; 321 limit problems (the stack on main d33dcfc; none of the limit problems is in this stack's launches). The stack then rebased onto 3f0a715 without conflicts; the three sets, zig build test and the Transcribe clips were re-run there.
  • Shared code: nemotron/config.zig reads nemo_speechlm; other Nemotron checkpoints load as before (the A/B in
    relu^2 keeps a NaN: grouped experts, ck experts and Nemotron's pocket #1628 covers Lightning, Bolt and Super VL on this stack).

Overlap

atc overlap lists #1445 and #1446 as conflicting, in the shared registration lists (registry.zig, cuda_kernels.zig, kernels.zig, kernel-contract.md, tests/cuda/main.zig); whichever lands second rebases.

@cwschroeder

Copy link
Copy Markdown
Contributor

CI on 401185be6be2f705c383d811177fa1f2f1aac8bb now shows the same seven cuda_load_gpu recovery tests crashing on Linux and Windows. The first Linux stack goes through module.unload → experts_bf16.unload → nemotron/cuda_kernels.zig:146 (bf16x) during Bolt load-failure recovery.

Linux job · Windows job. The stacked #1632 also has the same seven crashes on Linux at f186fe5d68583465e7058fefa776de23adbc3c29.

This is from the CI logs; I have not reproduced it locally or established whether the fault is in the test setup or the cleanup path.

@ashhart

ashhart commented Oct 30, 2026

Copy link
Copy Markdown
Owner

Thanks for this, @BobClawblaw. It reads well: everything new sits behind the nemo_speechlm config, and the shared Nemotron loader changes keep the old tensor names for every other checkpoint. It rebases cleanly onto #1630, so we'll land it once #1628, #1629 and #1630 are in.

Two small things before then, if you have time:

  1. The unit tests in zig/src/families/nemo_audio (config parse, FFT, bf16 rounding, the Sortformer FIFO and pooling) don't run under any build step yet. A module's tests only run when that module is a test root, so nemotron's tests don't reach them. Could you add them to test-cuda-host, for example with an addTest on the nemo_audio module in zig/build/cuda.zig? They pass for us when run directly (6 of 6).
  2. The Transcribe language model loads its MTP head. Could you post a receipt that its drafted replies equal "draft": false, and that 2 and 4 concurrent streams equal their solo runs, on a text prompt?

We also noticed that Engine.audio isn't set anywhere yet, so audio rows don't reach run or the server in this PR. That's fine as a step toward #1549. The changelog entry says audio input comes in a follow-up.

@BobClawblaw

Copy link
Copy Markdown
Contributor Author

Thanks @cwschroeder and @ashhart. Pushed cb83265:

  • The seven cuda_load_gpu crashes: the load-recovery tests build an Engine by hand and null each optional kernel set, and this PR's new bf16x (the bf16 experts) was left undefined, so deinit unloaded a garbage module. Both hand-built engines now null it. Before the change zig build test-bolt-load-host crashes the same seven tests locally on Linux; after it, 40/40 pass.
  • nemo_audio's unit tests: test-cuda-host now runs them (an addTest on the module in zig/build/cuda.zig); a deliberately failing probe test in audio.zig failed under that step, then was removed. test-cuda-host: 272/272 steps succeed.

#1632 carries the same crash and will be rebased onto this. The drafted == "draft": false and 2/4-stream receipts on a text prompt will follow from one GB10 (the bf16 release is 63 GB).

@BobClawblaw

Copy link
Copy Markdown
Contributor Author

Thanks @ashhart.

  1. Done in bc2fbea: nemo_audio's tests now run under test-cuda-host, through a test-nemo-audio-host step in zig/build/cuda_family_host.zig on the module nemotron already imports (6 of 6 pass). Nemotron 3.5 Transcribe: audio input, the 65,536-token window, an hour of audio, MTP drafts #1632 is rebased onto it.
  2. Nemotron 3.5 Transcribe on CUDA: the language model and the audio encoder #1631 doesn't register Transcribe with the server yet, so serve refuses it there. The receipt is from Nemotron 3.5 Transcribe: audio input, the 65,536-token window, an hour of audio, MTP drafts #1632 at 06b023c, which is Nemotron 3.5 Transcribe on CUDA: the language model and the audio encoder #1631 plus the serving code. Greedy, text prompts, 512 tokens each, one GB10, the bf16 release:
Check Result
Drafted vs "draft": false, 4 prompts 4 of 4 bit-identical (MTP accepted 239–351 of 369–433 drafts per reply; 44–58 vs 34.6 tok/s)
2 concurrent streams vs solo 2 of 2 identical
4 concurrent streams vs solo 4 of 4 identical

And yes, Engine.audio is set in #1632, as the changelog says.

@BobClawblaw
BobClawblaw force-pushed the nemotron-transcribe-model branch from cb83265 to bc2fbea Compare October 30, 2026 11:19
@BobClawblaw

Copy link
Copy Markdown
Contributor Author

Correction to my comment above: cb83265 was replaced on this branch by bc2fbea (which carries the nemo_audio test step instead). The cuda_load_gpu crash fix is now d5d067e, on top of it: both hand-built load-recovery engines null bf16x. zig build test-bolt-load-host 40/40 and test-cuda-host pass on Linux. #1632 is being rebased onto it.

…n NVFP4 block holding one gets a NaN scale (max_nan, not fmaxf)
…e shared expert's slot, block-row and tiled, fp32 and bf16 out), gates 1-5 with two controls
…m) from its bf16 weights, audio rows spliced after the embeds

The checkpoint's llm.* tensors load as published: NeMo's layer names, fp32 A_log/D/dt_bias, the stacked [E, in, out]
experts on the bf16 grouped format with the shared expert as experts E and E + 1, TE's empty _extra_state skipped.
The audio encoder and CTC aligner tensors are left for the audio path. A prompt pass's audio rows replace the
<|audio|> positions' embeddings, as image rows do.
… ASR encoder, streaming Sortformer, fusion), audio-check

NeMo's vLLM SALM path as ParallelExpertEncoder's online inference runs it: the log-mel frontend on the host, the ASR
TransformerEncoder over context-extended 500-frame windows, the Sortformer over the same windows with its speaker
cache and FIFO, thresholded speakers through the sinusoid kernel, and the projection to the language model's width.
nemo_audio.cu adds q/k LayerNorm with partial RoPE, attention over a window's valid keys and the Sortformer head's
glue; the rest runs on the vision towers' kernels. audio-check writes every stage under the reference's names.
…r FIFO and pooling) run under test-cuda-host
The Bolt load-recovery tests build an Engine by hand and null each optional kernel set; bf16x was left
undefined, so deinit unloaded a garbage module (seven tests crashed on Linux and Windows CI).
@BobClawblaw
BobClawblaw force-pushed the nemotron-transcribe-model branch from d5d067e to 64098fe Compare October 30, 2026 11:52
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