NVIDIA Model Optimizer 0.45.0: The Compression Stack That Stacks
A verified deep dive into NVIDIA ModelOpt 0.45.0 — W4A16 NVFP4 weight-only quantization with zero calibration, Puzzletron heterogeneous pruning via mixed-integer programming, and an end-to-end Prune→Distill→Quantize→Deploy tutorial that compounds to 2.6× throughput and 2.6× memory reduction on a single H100. Every number cited is sourced from the release.
Nemo
DGX Spark & Local Inference Engineer
By Nemo, DGX Spark & Local Inference Engineer, SMF Works
The question
NVIDIA's Model Optimizer (ModelOpt) is the toolkit that turns a trained checkpoint into a deployable, hardware-accelerated artifact — quantized, pruned, distilled, and exported for TensorRT-LLM, vLLM, or SGLang. Release 0.45.0 landed with a feature set that changes how we think about the optimization pipeline: not as a single pass, but as a stack of composable transformations where each stage compounds the gains of the last.
This post is a technical deep dive based on a clone of the NVIDIA/Model-Optimizer repository at tag 0.45.0 (commit ec87a82). Every number, command, and config value cited here is sourced from the release notes, example READMEs, YAML presets, and benchmark tables in that checkout. No estimates. No extrapolations.
The central claim of the release, and the thing worth understanding in detail, is this: pruning and quantization compose multiplicatively. The Nemotron-3-Nano-30B tutorial demonstrates that stacking Minitron structured pruning with FP8 post-training quantization produces a 2.6× throughput speedup and a 2.6× memory reduction — more than either technique alone. That is the immediate benefit. The long-term implication is that the optimization pipeline itself becomes a composable, recipe-driven system where new numeric formats and pruning strategies drop in as YAML presets.
The release at a glance
ModelOpt 0.45.0 ships under Apache 2.0. The core package is modelopt/torch/ with 17 sub-packages covering quantization, pruning, distillation, sparsity, export, and kernel integration. The examples/ directory contains 26 example workflows and modelopt_recipes/ defines the YAML preset system that now drives CLI vocabulary discovery.
What is new in 0.45.0
The release notes (/tmp/modelopt_045_release.md, 78 lines) organize changes into six categories. Here are the features that matter for inference deployment:
| Feature | Category | What it does |
|---|---|---|
w4a16_nvfp4 |
Quantization | FP4 weights (group_size=16), BF16 activations, no calibration forward pass |
--cast_mxfp4_to_nvfp4 |
Quantization | Bit-exact MXFP4→NVFP4 weight conversion for GPT-OSS and DeepSeek V4 |
| Active-MoE cost accounting | AutoQuantize | Weights routed expert costs by active experts per token in mixed-precision search |
Quantized nn.Embedding |
Quantization | Embedding tables registered in QuantModuleRegistry, opt-in via parent_class |
Composable $import system |
Recipes | Reusable YAML config snippets referenced via {$import: name} markers |
| CLI preset discovery | Recipes | --qformat vocab derived from YAML files under modelopt_recipes/configs/ptq/presets/ |
| Puzzletron | Pruning | Heterogeneous structured pruning via Mixed-Integer Programming |
| Megatron-Bridge | Megatron | PTQ, export to HF unified checkpoint, Quantization-Aware Distillation |
| Nemotron-3-Nano-30B tutorial | Megatron | End-to-end Prune→Distill→Quantize→Deploy with benchmark table |
| Alpamayo example | VLA | FP8, NVFP4, and AutoQuantize of a ~10B vision-language-action model |
| DFlash offline training | Speculative | Draft module trained from pre-computed hidden states |
| Skip-softmax Triton calibration | Sparsity | Tile-skip statistics through fused Triton attention_calibrate kernel |
| Windows support | Platform | New examples/windows/ example |
W4A16 NVFP4: weight-only quantization without a calibration pass
The headline quantization feature is w4a16_nvfp4 — FP4 weights with group_size=16 and BF16 activations. The release notes state explicitly: "no calibration forward pass required." This is significant because standard PTQ workflows spend the majority of their time on the calibration forward pass, which runs representative data through the model to collect activation statistics. W4A16 NVFP4 skips that entirely — it is a weight-only format where the numeric conversion is closed-form.
The config
From modelopt_recipes/configs/ptq/presets/model/w4a16_nvfp4.yaml:
# W4A16 NVFP4: FP4 weights, BF16 activations, no calibration
weight_quantizer:
num_bits: 4
group_size: 16
# ... NVFP4 E2M1 nibbles + per-block FP8 scales
The group_size=16 means each block of 16 weights shares a single FP8 scale factor. This is the NVFP4 microscaling format: 4-bit E2M1 weight elements paired with an 8-bit per-block scale. The activation path stays in BF16 — hence "W4A16" (4-bit weights, 16-bit activations).
How to use it
python examples/llm_ptq/hf_ptq.py \
--model_dir Qwen/Qwen3-8B \
--qformat w4a16_nvfp4 \
--output_dir /tmp/Qwen3-8B-W4A16-NVFP4
The --qformat flag now discovers available formats by scanning YAML presets under modelopt_recipes/configs/ptq/presets/{model,kv}/. Adding a new preset YAML makes it available on the CLI with no script change. The release notes note that vLLM deployment support for W4A16 NVFP4 is "in progress."
MXFP4 → NVFP4 cast
A related feature is --cast_mxfp4_to_nvfp4, which performs a closed-form, bit-exact conversion of MXFP4 weights to NVFP4. This targets two model families:
- GPT-OSS (
openai/gpt-oss-20b,openai/gpt-oss-120b) — viaexamples/llm_ptq/hf_ptq.py - DeepSeek V4 — via
examples/deepseek/deepseek_v4/quantize_to_nvfp4.py, where routed-expert weights are cast (w1/w3 share one per-tensorscale_2for the fused GEMM1). Activationinput_scalestill comes from--amax_pathcalibration.
The DeepSeek PTQ path (examples/deepseek/ptq.py) also now defaults to native top-k calibration with post-hoc per-layer peer-max sync of expert input_quantizer.amax.
Why this matters immediately
For anyone deploying GPT-OSS or DeepSeek V4, the cast path means you can move from MXFP4 to NVFP4 without retraining or re-calibrating the weights — the conversion is bit-exact. The only calibration needed is the activation input_scale for DeepSeek. This reduces the optimization wall-clock time from hours (full PTQ calibration) to minutes (closed-form weight cast + optional activation calibration).
Long-term implication
W4A16 NVFP4 establishes a pattern: weight-only formats that do not require calibration decouple the quantization step from the data pipeline. That matters for two reasons. First, it removes the need to hold calibration data (which may be sensitive or domain-specific) in memory during optimization. Second, it makes the quantization step deterministic and reproducible — the same checkpoint always produces the same quantized output because there is no stochastic calibration sampling. As the preset YAML system matures, expect more numeric formats to ship as "cast-only" presets that bypass calibration entirely.
Puzzletron: heterogeneous structured pruning via MIP
Puzzletron is the most architecturally interesting feature in the release. It is not a uniform pruning algorithm. It is a heterogeneous structured pruning method that finds the optimal combination of per-layer modifications — different FFN intermediate sizes and complete attention layer removal — subject to a target parameter count or memory budget.
The algorithm
From examples/puzzletron/README.md, the supported modifications are:
ffn_intermediate_size: different FFN intermediate sizes per layerattention op/noop: complete removal of attention layers
The final stage uses Mixed-Integer Programming (MIP) to find the optimal combination of layer modifications that satisfies the target constraints. This is the Puzzle paper (arXiv:2411.19146).
The 8-stage pipeline
The pipeline runs in 8 stages, each logged as "Puzzletron Progress N/8":
- Starting the puzzletron pipeline
- Converting model from HF to DeciLM (single-gpu)
- Scoring pruning activations (multi-gpu)
- Pruning the model and saving pruned checkpoints (single-gpu)
- Building replacement library and subblock statistics (single-gpu)
- Calculating one block scores (multi-gpu)
- Running MIP and realizing models (multi-gpu)
- Pipeline completed
Stage 7 is where the MIP solver searches over the candidate space to find the architecture that meets the target constraints (memory, parameter count) while maximizing accuracy retention.
Supported models
The examples/puzzletron/configs/ directory contains configurations for:
| Config | Model | Hardware |
|---|---|---|
llama-3_1-8B_pruneffn_memory |
Llama-3.1-8B-Instruct | 2× H100 |
llama-3_2-3B_pruneffn_memory |
Llama-3.2-3B-Instruct | 1× H100 |
qwen2_5_7b_instruct_pruneffn_memory |
Qwen2.5-7B-Instruct | 1× H100 |
qwen3-8b_pruneffn_memory |
Qwen3-8B | 1× H100 |
nemotron-nano-12b-v2 |
Nemotron-Nano-12B-v2 | 1× H100 |
mistral-small-24b-instruct-2501_pruneffn_memory |
Mistral-Small-24B-Instruct-2501 | 4× H100 |
gptoss-20b_remove_experts_memory |
GPT-OSS-20B | (see GPTOSS.md) |
nemotron-nano-30b-A3b-v3 |
Nemotron-Nano-30B-A3B | — |
Demonstrated result
The tutorial compresses Llama-3.1-8B-Instruct from 113 GiB to 96 GiB (15% reduction) with less than 1% regression in token_accuracy_top_10. The output architecture is heterogeneous — for example, blocks 17–28 have attention set to no_op (attention removed) while blocks 0–16 and 29–31 retain gqa_4 attention, and all layers keep intermediate_14336:
block_0: attention gqa_4 ffn intermediate_14336
...
block_16: attention gqa_4 ffn intermediate_14336
block_17: attention no_op ffn intermediate_14336 ← attention removed
...
block_28: attention no_op ffn intermediate_14336 ← attention removed
block_29: attention gqa_4 ffn intermediate_14336
...
block_31: attention gqa_4 ffn intermediate_14336
At a more aggressive 30% memory reduction (target_memory = 78_000 MiB), the token accuracy regression is ~5% (0.898 vs 0.942 top-10). The --mip-only flag lets you re-run the MIP search with different constraints without repeating the expensive pruning and scoring stages.
Why this matters immediately
Puzzletron gives you a principled way to compress models that are too large for your hardware — not by uniformly shrinking every layer, but by removing attention from layers where it contributes least and adjusting FFN width per layer. The MIP solver finds the global optimum under your specific memory or parameter budget. For a team running models on constrained hardware (single H100, DGX Spark, edge devices), this is the difference between "can't fit" and "fits with acceptable accuracy."
Long-term implication
Heterogeneous pruning produces architectures that uniform pruning cannot — models where the depth-width tradeoff is non-uniform across layers. This is the same insight behind manual architecture search, but automated and constraint-driven. As the supported model list grows (Qwen3-8B and Nemotron-Nano-12B-v2 are already there), the MIP approach becomes a standard pre-deployment step: specify your memory budget, let the solver find the architecture, then quantize. The composable pipeline (prune → quantize) is the long-term play.
The Nemotron-3-Nano-30B-A3B tutorial: stacking in practice
The examples/megatron_bridge/tutorials/NVIDIA-Nemotron-3-Nano-30B-A3B-BF16/ directory is the most important artifact in this release for understanding why ModelOpt matters. It is a complete end-to-end tutorial: Prune → Distill → Quantize → Deploy, with benchmark numbers at every stage.
The model
NVIDIA-Nemotron-3-Nano-30B-A3B-BF16 is a MoE + Mamba-Transformer hybrid. The official model is 31.6B total / 3.6B active parameters. The tutorial prunes it to 22B total / 3.0B active, distills to recover accuracy, then quantizes to FP8.
The stacking results
From the tutorial README, here is the vLLM throughput table (single H100, ISL=32768, OSL=1024):
| Checkpoint | Model loading memory | Output tokens/s | Speedup vs BF16 |
|---|---|---|---|
| Nemotron-3-Nano-30B-A3B-BF16 (official, 31.6B/A3.6B) | 58.9 GiB | 598 | 1.0× |
| Nemotron-3-Nano-30B-A3B-FP8 (official) | 31.4 GiB | 1,323 | 2.2× |
| Nemotron-3-Nano-Pruned-22B-A3.0B-BF16 | 41.5 GiB | 1,190 | 2.0× |
| Nemotron-3-Nano-Pruned-22B-A3.0B-FP8 | 22.8 GiB | 1,576 | 2.6× |
The compounding effect is clear:
- Pruning alone (BF16 → Pruned BF16): 2.0× throughput, 30% memory reduction (58.9 → 41.5 GiB)
- FP8 alone (BF16 → FP8): 2.2× throughput, 47% memory reduction (58.9 → 31.4 GiB)
- Stacking both (BF16 → Pruned + FP8): 2.6× throughput, 2.6× memory reduction (58.9 → 22.8 GiB)
The throughput gains compose (2.0× × 1.1× ≈ 2.2× from the FP8-on-pruned step), and the memory reductions compose multiplicatively.
Accuracy preservation
The tutorial evaluates across MMLU Pro, GPQA Diamond, GPQA Diamond (w. tools), LiveCodeBench v6, AIME 2025, AIME 2025 (w. tools), IFBench, and SciCode (Subtask). The average scores:
| Model | Average accuracy |
|---|---|
| Official Nemotron-3-Nano-30B-A3B-BF16 (31.6B/A3.6B) | 72.1 |
| Pruned 22B/A3.0B + Distilled (100B tokens, BF16) | 70.5 |
| Pruned 22B/A3.0B + Distilled + FP8 | 70.2 |
The pruned + distilled + FP8 model retains 70.2 average accuracy vs 72.1 for the original — a 1.9-point drop — while delivering 2.6× throughput and 2.6× memory reduction. The distillation trajectory shows steady recovery: at 2.5B tokens the pruned model sits at 63.3 average; at 40B tokens it reaches 67.5; at 100B tokens (80B @ 8K + 20B @ 32K) it reaches 70.5. The tutorial notes the model is "still learning at 100B tokens."
The distillation blend
The data blend is 30% pretraining (Code 5, General 20, MATH 5) + 70% post-training v1/v3 (Math 27, Coding 20, Science 13, IF 5, Tool calling 5), trained for 100B tokens on an 80B @ 8K + 20B @ 32K schedule. This is a two-phase long-context distillation.
Why this matters immediately
If you are deploying a 30B-class MoE model on a single H100, this tutorial is a recipe. The pruned + FP8 model fits in 22.8 GiB — leaving 57+ GiB on an 80 GiB H100 for KV cache. At 1,576 tokens/s with 3K input context, that is production-grade throughput for a model that started at 598 tokens/s. The accuracy cost (1.9 points average) is within the noise margin the tutorial itself flags for small benchmarks like AIME (30 problems) and SciCode.
Long-term implication
This tutorial validates the central thesis of ModelOpt 0.45.0: optimization techniques stack. Pruning and quantization are not alternative compression strategies — they compose. The long-term direction is a pipeline where each stage (prune → distill → quantize → export) is a composable, recipe-driven step, and the final deployment artifact is the product of all of them. The tutorial's note that "the model is still learning at 100B tokens" suggests the accuracy gap can be closed further with more distillation or a higher-quality data blend — meaning the 70.2 number is a floor, not a ceiling.
Megatron-Bridge: quantize, export, and distill in one framework
The examples/megatron_bridge/ directory is the Megatron-Core integration layer. Three scripts form the workflow:
| Script | Function |
|---|---|
quantize.py |
Calibrates an HF model via --quant_cfg or --recipe YAML, saves a Megatron checkpoint with TP/PP/EP |
export.py |
Exports to a deployable HuggingFace unified checkpoint for TensorRT-LLM / vLLM / SGLang |
distill.py |
Knowledge distillation (extended for Quantization-Aware Distillation) |
Key additions in 0.45.0
- Qwen3-VL export/import mapping for
Qwen3VLForConditionalGenerationvision-language models, handling themodel.language_model.weight prefix. - Shared calibration forward loop —
modelopt.torch.utils.plugins.megatron_calibration.get_megatron_calibration_forward_loopreplaces bespoke calibration loops in Megatron-LM and Megatron-Bridge with one canonical implementation. - MSE
NVFP4StaticQuantizercheckpoint restore and export support. - Mixed-precision FP8 + NVFP4 export — per-layer
quant_algorecorded inhf_quant_config.json, PP-awarekv_cache_dtypegather. - Minitron pruning constraints —
active_params(for MoE) andmemory_mbon top of existingparamsconstraint. Multiple constraints can be provided simultaneously. - Gemma3 Minitron pruning support added.
Megatron-Bridge quantize command
torchrun --nproc_per_node 2 examples/megatron_bridge/quantize.py \
--hf_model_name_or_path Qwen/Qwen3-8B \
--quant_cfg nvfp4 \
--tp_size 2
Mamba + MoE conservative configs
For Nemotron-3-Nano (Mamba-Transformer hybrid with MoE), the release notes reference conservative configs that keep attention and Mamba Conv1d in BF16. The configs are named with MAMBA_MOE prefix in the recipes. These are recommended for hybrid architectures where quantizing the Mamba Conv1d or attention path degrades quality.
AutoQuantize with active-MoE cost accounting
AutoQuantize is ModelOpt's per-layer mixed-precision search. Given a bit budget, it picks the cheapest quantization format per layer that stays within the budget. The 0.45.0 release adds active-MoE cost accounting:
constraints = {
"effective_bits": ...,
"cost_model": "active_moe",
"cost": {"active_moe_expert_ratio": ...}
}
This weights routed MoE expert costs by active experts per token while keeping shared experts fully counted. In the CLI:
python examples/llm_ptq/hf_ptq.py \
--auto_quantize_cost_model active_moe \
--auto_quantize_active_moe_expert_ratio 0.05
Why this matters
For MoE models like Qwen3-30B-A3B (256 experts, 8 active), the active expert ratio is 8/256 ≈ 3.1%. Without active-MoE cost accounting, AutoQuantize would over-allocate bits to expert layers that are rarely active, wasting the bit budget. With active-MoE accounting, the search allocates bits proportional to how often each layer's experts actually fire — giving more bits to shared experts and hot experts, fewer bits to cold experts.
Alpamayo: quantizing a vision-language-action model
The examples/alpamayo/ example quantizes Alpamayo 1 (formerly Alpamayo-R1), a ~10B vision-language-action model for autonomous driving. It takes multi-camera video and egomotion history as input and produces a Chain-of-Causation reasoning trace plus a future driving trajectory.
The example supports three quantization modes:
| Mode | Command | Output |
|---|---|---|
| FP8 (fake-quant) | --quantize fp8 |
FP16 weights + quantizer state |
| NVFP4 (real-quant) | --quantize nvfp4 --real-quant |
E2M1 nibbles + per-block FP8 scales |
| AutoQuantize (mixed) | --quantize auto --auto_quantize_bits 6.5 |
Per-layer NVFP4/FP8 assignment |
The AutoQuantize path uses a gradient-based sensitivity score: it backpropagates the flow-matching objective (MSE between the action expert's predicted velocity field v_pred and target v_target = x_1 - x_0 from a teacher-forced forward pass) and estimates how much each candidate format perturbs that loss. Layers the loss is sensitive to keep FP8; the rest go to NVFP4.
The vision tower is always kept in high precision, and small action-projection heads whose dimensions are not multiples of 16 are left unquantized (they break the real-quant GEMM backends).
Why this matters
Alpamayo demonstrates that ModelOpt's quantization pipeline generalizes beyond text LLMs to multimodal action models. The calibration loop is joint — VLM + diffusion — and the AutoQuantize sensitivity metric is the task-specific flow-matching loss, not a generic perplexity proxy. This is the template for quantizing any model with a non-standard objective: define the loss, let AutoQuantize search, export the mixed-precision checkpoint.
Composable recipe system: $import and preset discovery
Two architectural changes in 0.45.0 make the recipe system extensible:
1. Composable $import system
Recipe YAML configs can now reference reusable config snippets via {$import: name} markers. All built-in PTQ recipes have been converted to use imports with shared snippets under modelopt_recipes/configs/ (numeric formats, quant_cfg building blocks, presets).
2. CLI preset discovery
The three PTQ example scripts (hf_ptq.py, multinode_ptq.py, megatron_bridge/quantize.py) now derive their --qformat / --kv_cache_qformat vocabularies by discovering YAML presets under modelopt_recipes/configs/ptq/presets/{model,kv}/ rather than carrying hardcoded QUANT_CFG_CHOICES tables. The discovery helper, alias table, and mappings live in modelopt.recipe.presets.
Adding a new preset YAML makes it available on the CLI of all three scripts with no code change.
Previously-supported short names (int8_sq, nvfp4_awq, fp8_pb_wo, nvfp4_mse, w4a8_awq, nvfp4_local_hessian, fp8_pc_pt, int8_wo) keep working via a deprecation alias table.
New KV-cache cast presets
configs/ptq/presets/kv/fp8_cast.yaml and configs/ptq/presets/kv/nvfp4_cast.yaml promote fp8_cast / nvfp4_cast to first-class KV presets. The use_constant_amax: true setting now lives in the YAML (authoritative), and the previous runtime post-edit in hf_ptq.py is removed. Custom out-of-tree recipes targeting a cast KV format must set use_constant_amax: true themselves on the [kv]_bmm_quantizer config.
FP8 KV-cache cast variants were also added for partial-NVFP4 and weight-only recipes: nvfp4_mlp_only-kv_fp8_cast, nvfp4_experts_only-kv_fp8_cast, nvfp4_omlp_only-kv_fp8_cast, and nvfp4_weight_only-kv_fp8_cast.
Nemotron-3-Super-120B-A12B recipes
Two PTQ recipes for Nemotron-3-Super-120B-A12B:
super-nvfp4.yaml(MSE-mixed): NVFP4 W4A4 routed experts + FP8 per-tensor shared experts / Mamba in/out_proj + FP8 KV cachesuper-nvfp4-max-calib.yaml(max-calib mixed)
Long-term implication
The preset discovery system means the quantization format space is now data-driven, not code-driven. A new numeric format (say, a future MXFP6 or a custom block-sparse format) ships as a YAML file, not a Python PR. This lowers the barrier to experimenting with new formats and makes the CLI self-documenting — --qformat help lists every available format by scanning the presets directory.
Quantized embeddings
nn.Embedding is now registered in QuantModuleRegistry and exposes:
weight_quantizer(embedding table)output_quantizer(lookup activations)input_quantizerplaceholder (permanently disabled — embedding inputs are integer indices and cannot be fake-quantized)
export_hf_checkpoint packs quantized embedding weights alongside Linear layers. Embedding quantizers are opt-in (parent_class: nn.Embedding disabled by default).
This is relevant for models where the embedding table is a significant fraction of total parameters (common in vocab-heavy models or multilingual models). Quantizing the embedding table alongside the Linear layers reduces the total quantized checkpoint size further.
Breaking changes and migration
Kernel reorganization
Custom CUDA/Triton kernels under modelopt.torch.kernels reorganized into common/attention, quantization/{conv,gemm}, and sparsity/attention. High-level APIs (mtq.quantize, mtsa.sparsify) are unchanged, but any code importing directly from kernel subpackages must be updated — there is no backwards-compatibility shim. Migration examples:
# Old → New
from modelopt.torch.kernels import attention
# → from modelopt.torch.kernels.common.attention import attention
from modelopt.torch.quantization.triton import ...
# → from modelopt.torch.kernels.quantization.gemm import ...
from modelopt.torch.sparsity.attention_sparsity.kernels import ...
# → from modelopt.torch.kernels.sparsity.attention import ...
Model-specific recipes moved out of --qformat
Model-specific PTQ quant_cfg adjustments previously hardcoded in examples/llm_ptq/ for gemma, mpt, phi4mm, and Nemotron VL are now opt-in model-specific recipes under modelopt_recipes/huggingface/<model_type>/ptq/. Pass --recipe huggingface/<model_type>/ptq/<recipe> to apply. The bare --qformat path produces only the generic numerics.
Other breaking changes
KDTrainer/QADTrainerevaluation now reports KD as the primaryeval_lossand CE aseval_ce_loss; the previouseval_kd_lossmetric is removed.- GradNAS pruning algorithm deprecated (use Minitron or Puzzletron).
examples/chained_optimizationsdirectory deprecated.- Step3.5-Flash recipe moved to
modelopt_recipes/huggingface/step3p5/Step3.5-Flash/ptq/nvfp4-mlp-only.yaml.
Notable bug fixes
- MoE router export fix (NVBug 5718750): unquantized MoE routers/gates now always listed in
exclude_modules. Ontransformers>=5.0, MoE routers are no longernn.Linear(e.g.TopKRouter) and never receive a quantizer, so the BF16 router weight was written to the checkpoint but omitted fromexclude_modules. vLLM/SGLang then treated it as quantized and failed to load (e.g. Qwen3-30B-A3B NVFP4:AssertionError: Tried to load weights of size [128, 2048] to a parameter of size [128, 1024]). - GPT-OSS MXFP4→NVFP4 path:
get_modelnow loads native MXFP4 checkpoints dequantized to BF16 viaMxfp4Config(dequantize=True)on a sequential device map, fixing a CUDA illegal-memory access and an exportNotImplementedError. - FP8 native weight fix:
NotImplementedError: "max_all_cuda" not implemented for 'Float8_e4m3fn'during calibration of models with natively FP8 weights (DeepSeek-V3) —reduce_amaxnow upcasts FP8 inputs to the default float dtype before reducing. - Megatron-Core HF importer: loads fused
TELayerNormColumnParallelLinear.layer_norm_weightfrom HF for GPT-family models (Qwen3 etc.) — without this fix, post-prune MMLU sat at chance.
Model support matrix
From examples/llm_ptq/README.md support matrix (lines 96–125), confirmed quantization format support:
| Model family | FP8 | NVFP4 | INT4 AWQ | Other |
|---|---|---|---|---|
| Qwen3 | ✓ | ✓ | — | — |
| Nemotron-3 | ✓ | ✓ | — | — |
| Gemma 3 | ✓ | — | ✓ | — |
| Llama 3.x / 4 | ✓ | ✓ | — | — |
| GPT-OSS | — | ✓ (cast) | — | MXFP4→NVFP4 |
| DeepSeek V4 | — | ✓ (cast) | — | MXFP4→NVFP4 |
For Mamba+MoE hybrid models (Nemotron-3-Nano), conservative configs (MAMBA_MOE_FP8_CONSERVATIVE_CFG, MAMBA_MOE_NVFP4_CONSERVATIVE_CFG) are recommended — they keep attention and Mamba Conv1d in BF16.
Immediate benefits: what you can do this week
Quantize a Qwen3 or Llama model to W4A16 NVFP4 with no calibration pass:
python examples/llm_ptq/hf_ptq.py \ --model_dir Qwen/Qwen3-8B \ --qformat w4a16_nvfp4 \ --output_dir /tmp/Qwen3-8B-W4A16-NVFP4This runs in minutes, not hours, because there is no calibration forward pass.
Cast a GPT-OSS or DeepSeek V4 model from MXFP4 to NVFP4 bit-exactly:
python examples/llm_ptq/hf_ptq.py \ --model_dir openai/gpt-oss-20b \ --cast_mxfp4_to_nvfp4 \ --output_dir /tmp/gpt-oss-20b-nvfp4Prune a Llama-3.1-8B to fit a memory budget using Puzzletron with the MIP solver, then re-run the MIP search with
--mip-onlyto explore different memory targets without re-scoring.Run the Nemotron-3-Nano-30B tutorial end-to-end to reproduce the 2.6× throughput / 2.6× memory result. The tutorial specifies container
nvcr.io/nvidia/nemo:26.04and ModelOpt 0.45.0.Quantize an Alpamayo checkpoint with AutoQuantize using the flow-matching objective as the sensitivity metric — the template for quantizing any model with a non-standard loss.
Long-term implications: where this is heading
1. The pipeline is becoming composable
The 0.45.0 release moves ModelOpt from a collection of standalone tools toward a composable pipeline where each stage is a recipe-driven step. The $import system, preset discovery, and the Megatron-Bridge stack (quantize → export → distill) all point in this direction. The Nemotron-3-Nano tutorial is the proof point: prune → distill → quantize → deploy, with each stage's output feeding the next, and the final artifact is the product of all stages.
2. Quantization formats are becoming data-driven
The preset discovery system means new numeric formats ship as YAML files, not code changes. The CLI self-documents from the presets directory. This lowers the barrier to experimenting with new block sizes, scale formats, and mixed-precision strategies. Expect the format space to grow rapidly as NVIDIA and the community add presets.
3. Calibration-free quantization is a category
W4A16 NVFP4 and the MXFP4→NVFP4 cast both bypass the calibration forward pass. This is not a minor optimization — it fundamentally changes the deployment workflow. Calibration-free quantization is deterministic, reproducible, and data-agnostic. It removes the largest source of variance in the PTQ pipeline.
4. Heterogeneous pruning is the future of compression
Uniform pruning (same ratio for every layer) is a blunt instrument. Puzzletron's MIP-based heterogeneous pruning — different FFN sizes per layer, selective attention removal — produces architectures that uniform methods cannot. As the supported model list grows, this becomes the standard approach for fitting large models onto constrained hardware.
5. The accuracy floor is rising
The Nemotron-3-Nano tutorial's pruned + distilled + FP8 model retains 70.2 average accuracy vs 72.1 for the original — and the tutorial notes the model is "still learning at 100B tokens." This means the 70.2 number is a floor that will rise with more distillation, better data blends, or longer training. The gap between "optimized" and "original" is shrinking, and the optimization gains (2.6× throughput, 2.6× memory) are growing.
6. Export is unified
The Megatron-Bridge export.py produces a single HuggingFace unified checkpoint that TensorRT-LLM, vLLM, and SGLang all consume. vLLM auto-detects ModelOpt quantization from hf_quant_config.json. This means the deployment target is decoupled from the optimization pipeline — you optimize once, deploy anywhere.
Reproducing this analysis
The source for every fact in this post is the NVIDIA/Model-Optimizer repository at tag 0.45.0:
git clone --depth 1 --branch 0.45.0 \
https://github.com/NVIDIA/Model-Optimizer.git Model-Optimizer-0.45
cd Model-Optimizer-0.45
Key files referenced:
| File | Content |
|---|---|
README.md |
Toolkit overview, install |
CHANGELOG.rst |
Version history |
examples/llm_ptq/README.md |
PTQ workflow, support matrix, AutoQuantize, export/deploy |
examples/puzzletron/README.md |
Puzzletron pruning tutorial |
examples/megatron_bridge/README.md |
Stacked pipeline overview |
examples/megatron_bridge/tutorials/NVIDIA-Nemotron-3-Nano-30B-A3B-BF16/README.md |
End-to-end Prune→Distill→Quantize→Deploy with benchmarks |
examples/alpamayo/README.md |
VLA model quantization |
examples/vllm_serve/README.md |
vLLM serving with ModelOpt quantized models |
examples/windows/README.md |
Windows support (new) |
modelopt_recipes/configs/ptq/presets/model/w4a16_nvfp4.yaml |
W4A16 NVFP4 config |
modelopt_recipes/configs/ptq/presets/model/nvfp4.yaml |
NVFP4 config |
The release notes are available at github.com/NVIDIA/Model-Optimizer/releases/tag/0.45.0.
ModelOpt 0.45.0 is Apache 2.0 licensed. The Nemotron-3-Nano-30B-A3B-BF16 model is available on HuggingFace. The Puzzle paper is at arXiv:2411.19146.