Regenerating everything from scratch
July 24, 2026 · View on GitHub
End-to-end pipeline: NeMo checkpoints → tokenizer export → reference dump →
GGUF conversion → quantization → verification. All commands run from the repo
root. Artifacts land in models/ (gitignored).
0. Python venv
The scripts need NeMo with the MagpieTTS model class (MagpieTTSModel,
nemo/collections/tts/models/magpietts.py) plus gguf for writing/reading
GGUF. Tested with nemo-toolkit 2.8.0rc0 (editable checkout of
github.com/NVIDIA/NeMo main), torch CPU, gguf 0.18.0.
python3 -m venv .venv
.venv/bin/pip install torch --index-url https://download.pytorch.org/whl/cpu
.venv/bin/pip install -e /path/to/NeMo # or: 'nemo-toolkit[tts]'
.venv/bin/pip install gguf pyyaml hydra-core transformers
1. Checkpoints
Both .nemo files are plain tar archives; the scripts read the extracted
trees (config, weights, tokenizer/G2P resources).
hf download nvidia/magpie_tts_multilingual_357m \
magpie_tts_multilingual_357m.nemo --local-dir models
hf download nvidia/nemo-nano-codec-22khz-1.89kbps-21.5fps \
nemo-nano-codec-22khz-1.89kbps-21.5fps.nemo --local-dir models
mkdir -p models/magpie_extracted models/nanocodec_extracted
tar -xf models/magpie_tts_multilingual_357m.nemo -C models/magpie_extracted
tar -xf models/nemo-nano-codec-22khz-1.89kbps-21.5fps.nemo -C models/nanocodec_extracted
2. Tokenizer export
Instantiates every sub-tokenizer exactly as NeMo does (config order = global id space) and dumps the aggregated vocab, per-tokenizer configs and parsed G2P dictionaries to JSON for the converter. No weights are loaded.
.venv/bin/python scripts/export_tokenizer.py \
--extract-dir models/magpie_extracted \
--out models/tokenizer_export.json
zh/ja limitation: the Chinese (ChinesePhonemesTokenizer, jieba) and
Japanese (JapanesePhonemeTokenizer, pyopenjtalk) tokenizers need external
segmenters at encode time. Their vocab slices are exported (the global id
space must stay intact), but the C++ tokenizer reports zh/ja as
unsupported and throws. All other languages (en, de, es, fr, it, pt, hi, ar,
vi, ko, ...) encode natively.
3. Reference dump (NeMo ground truth)
Runs the checkpoint in NeMo on CPU (fp32, seed 1234, deterministic G2P) with forward hooks on every component and writes the gold tensors (tokenizer ids, per-layer encoder outputs, decoder step inputs/outputs/logits/cross-attn, prior evolution, LT logits, final codes, codec latent, waveform) to a GGUF consumed by the C++ parity tests.
.venv/bin/python scripts/dump_reference.py \
--nemo models/magpie_tts_multilingual_357m.nemo \
--out models/ref_dump_en_speaker0.gguf \
--text "Hello world, this is a test of the text to speech system." \
--language en --speaker 0 --seed 1234
--max-steps N clamps max_decoder_steps for quick hook-validation runs
(e.g. --max-steps 8 finishes in seconds; do NOT use such a dump as the test
reference; the decode is truncated).
4. GGUF conversion
One self-contained GGUF (arch magpie-tts): all TTS weights verbatim,
NanoCodec decoder with weight norm folded, every hparam as magpie.* KV, the
tokenizer vocab/configs as KV and the G2P dictionaries as raw-byte tensors.
Keep the default f32 output; quantized variants come from step 5 (the
converter's own --dtype f16 allowlist is known-broken for
audio_embeddings.*, see docs/quantization.md).
.venv/bin/python scripts/convert_magpie_to_gguf.py \
--magpie-dir models/magpie_extracted \
--codec-dir models/nanocodec_extracted \
--tokenizer-json models/tokenizer_export.json \
--out models/magpie-tts-multilingual-357m-f32.gguf
5. Quantization
Selective by tensor-name allowlist: only ggml_mul_mat/ggml_get_rows
consumers are quantized; raw-float* tensors (norms, position embeddings,
audio embeddings, baked context, the whole codec) stay F32. Rationale and
size table: docs/quantization.md. K-quants need a built libggml-base.so
from any build*/ tree (or MAGPIE_GGML_LIB).
for q in f16 q8_0 q6_k q5_k q4_k; do
.venv/bin/python scripts/quantize_gguf.py \
--src models/magpie-tts-multilingual-357m-f32.gguf \
--out models/magpie-tts-multilingual-357m-$q.gguf --type $q
done
6. Verify
Build, then run the test suite against the f32 model and the reference dump (tests skip with exit 77 when the env vars are unset):
cmake -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build -j
MAGPIE_MODEL=models/magpie-tts-multilingual-357m-f32.gguf \
MAGPIE_REF_DUMP=models/ref_dump_en_speaker0.gguf \
ctest --test-dir build --output-on-failure # 7/7
Quantized files are checked by scripts/verify_quants.sh (per quant: load,
teacher-forced replay drift vs the reference dump, optional parakeet.cpp ASR
round-trip):
MAGPIE_BUILD_DIR=build MAGPIE_REF_DUMP=models/ref_dump_en_speaker0.gguf \
PARAKEET_CLI=/path/to/parakeet-cli PARAKEET_ASR_MODEL=/path/to/ctc.gguf \
scripts/verify_quants.sh # or: verify_quants.sh q8_0
KV-cache vs reference numerics: the production C++ decode loop is
KV-cached; NeMo's reference config recomputes the full sequence each step, so
cached positions keep the attention prior of their step instead of the
latest one. This is an accepted approximation (drift up to ~1.2 on raw logits
at f32, no decode decisions changed), see
docs/architecture-magpietts.md §3.7. The strict
e2e gate (test_e2e_replay, atol 1e-4) runs the uncached replay path
(magpie_tts_replay::use_kv_cache = false) to isolate true numerics;
MAGPIE_REPLAY_ATOL relaxes it for quantized models.