Repository navigation
[2/2] Export vLLM fake-quant checkpoints through Megatron-Bridge - #2889
kevalmorabia97 wants to merge 7 commits into
Conversation
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. 🗂️ Base branches to auto review: 3
Please check the settings in the CodeRabbit UI or the ⚙️ Run configuration
You can disable this status message by setting the Use the checkbox below for a quick retry:
Comment |
a0285e8 to
e8e648a
Compare
172382b to
2a6a089
Compare
…ns (#2911) ### What does this PR do? Type of change: Bug fix The default PTQ exclusions (`configs/ptq/units/default_disabled_quantizers`, imported by the `general/ptq/*` recipes and `mtq.*_CFG` presets) disable multi-token-prediction layers with `mtp.*`, which only matches an MTP block at the model root (HF checkpoints, Megatron LLMs). Megatron vision-language models nest it under the language model (`language_model.mtp.layers.0...`), so their MTP layers were quantized. For Qwen3.5/3.6 VLMs quantized with `examples/megatron_bridge/quantize.py` and a general recipe, `export_quantized_megatron_to_hf.py --vllm_fakequant_export` then refuses the checkpoint ("MTP quantization is not supported by vLLM fakequant export/reload"). The pattern is now `*mtp.*`, matching the model-specific recipes that already use `*mtp*`. Models whose MTP block was nested and previously quantized by a general recipe now keep it in BF16, which is what the exclusion intends. Recipes that quantize MTP on purpose (e.g. the Nemotron 3.5 Super ones) do not import this unit after their MTP entries, so they are unaffected. ### Usage No API change. ### Testing - New `tests/unit/recipe/test_presets.py::test_default_exclusions_disable_mtp_at_any_depth`: with `general/ptq/fp8_default-kv_fp8`, MTP quantizers at the root and under `language_model.` are disabled and decoder quantizers stay enabled; fails without the change. - `tests/unit/recipe`: 492 passed. - Found while validating vLLM fake-quant export of Qwen3.6-35B-A3B (#2889): the default `nvfp4_default-kv_fp8` checkpoint was refused on `language_model.mtp.layers.0.eh_proj.input_quantizer`. ### Before your PR is "*Ready for review*" - Is this change backward compatible?: ✅ (nested MTP layers that a general recipe used to quantize now stay in BF16, as the exclusion intends) - If you copied code from any other sources or added a new PIP dependency, did you follow guidance in `CONTRIBUTING.md`: N/A - Did you write any new necessary tests?: ✅ - Did you update [Changelog](https://github.com/NVIDIA/Model-Optimizer/blob/main/CHANGELOG.rst)?: ✅ - Did you get Claude approval on this PR?: ❌ ### Additional Information Related to #2889 (vLLM fake-quant export through Megatron-Bridge). 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> <!-- coderabbit-ai-generated:v1 --> ## Summary by CodeRabbit * **Bug Fixes** * Corrected default post-training quantization recipes for Megatron vision-language models so MTP layers remain unquantized consistently, including nested language-model layers. * Preserved quantization behavior for unrelated decoder projection layers. * Added regression coverage to verify both root-level and nested MTP exclusions in the default FP8 recipe. * **Documentation** * Added a changelog entry for the 0.49.0 bug fix. <!-- end of auto-generated comment: release notes by coderabbit.ai --> Signed-off-by: Keval Morabia <28916987+kevalmorabia97@users.noreply.github.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
cjluo-nv
left a comment
There was a problem hiding this comment.
Bot review (gpt-6.1-sol) — DM the bot to share feedback.
Changes requested: sequential weight quantizers pass validation but are not folded by the new Bridge export path.
Needs action:
- Fix sequential weight folding in
export_vllm_fakequant, or reject it collectively before mutation; add a regression test comparing exported weights with the sequential QDQ result (see inline comment).
No action needed:
- The Bridge design is justified by architecture, VLM, and parallelism coverage versus extending the existing exporter.
- Existing test edits add coverage without weakening assertions. The new test’s NVIDIA header matches
LICENSE_HEADER. - Tests were inspected, not executed.
|
/claude review |
export_quantized_megatron_to_hf.py --vllm_fakequant_export now checks the quantizers, folds the weight quantizers into BF16 weights and saves with bridge.save_hf_pretrained, which writes the enabled activation and KV-cache quantizers to quantizer_state.pth and a versioned quant_recipe.yaml under HF names for vllm_serve_fakequant.py. - Any TP / EP / PP (new --tp_size, fake-quant only) and every architecture Megatron-Bridge maps; a VLM's language-model-only QAD checkpoint keeps the HF vision tower. - Weight-only formats (e.g. GGML IQ2) get no sidecars and serve with stock vLLM. - check_mcore_vllm_fakequant_quantizers shares the existing exporter's checks. - Tests: parity with export_mcore_gpt_to_hf_vllm_fq (tiny Qwen3, Nemotron-H), export parallelism invariance (Qwen3.5-VL-MoE EP, Nemotron-H PP), and a fake-quant export step in the QAD example test. They skip without a Megatron-Bridge that writes the sidecars. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Keval Morabia <28916987+kevalmorabia97@users.noreply.github.com>
mtq.fold_weight bakes the weight quantizer into the weight, so any state the quantizer keeps to re-derive that weight (e.g. a pinned GGML payload) is stale afterwards. Dropping all of its buffers, not only _amax and _pre_quant_scale, lets Megatron-Bridge save a folded IQ2 model without unmapped quantizer tensors. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Keval Morabia <28916987+kevalmorabia97@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Keval Morabia <28916987+kevalmorabia97@users.noreply.github.com>
Folding now drops every weight-quantizer buffer, which included the SVDQuant LoRA factors that SVDQuantLinear.fold_weight read afterwards, so the folded weight silently lost the low-rank correction. Read the factors before folding. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Keval Morabia <28916987+kevalmorabia97@users.noreply.github.com>
test_mlflow_utils requires a single args.checkpoint_exported = True per save; the vLLM fake-quant branch added a second one. Move the unified export into export_unified_hf so both paths share it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Keval Morabia <28916987+kevalmorabia97@users.noreply.github.com>
fold_weight skipped SequentialQuantizer weight quantizers, so the Megatron-Bridge fake-quant export saved W4A8 weights unquantized. Fold the whole chain, member by member in the weight dtype as the forward pass does, then disable and clean each member. The export now matches the Megatron exporter bitwise for W4A8 too. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Keval Morabia <28916987+kevalmorabia97@users.noreply.github.com>
…ests test_quantize_and_export now also exports each quantized checkpoint with --vllm_fakequant_export (EP, TP and PP across its three cases) instead of a separate test quantizing three more models; test_qad keeps the fake-quant export only for the VLM, the one case only QAD produces. The parity test keeps one NVFP4 + FP8 KV case and the W4A8 case. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Keval Morabia <28916987+kevalmorabia97@users.noreply.github.com>
2a6a089 to
f6cc496
Compare
cjluo-nv
left a comment
There was a problem hiding this comment.
Bot review (gpt-6.1-sol) — DM the bot to share feedback.
LGTM: the prior sequential-folding bug is resolved with regression coverage; human sign-off remains for the justified existing-test edits.
Needs action:
- Confirm the existing-test extensions in
tests/examples/megatron_bridge: assertions remain intact, sidecar checks require a capable Bridge, and the timeout increase accommodates the added export.
No action needed:
- ✔️ Resolved since the last review: sequential weights now fold through both generic and TE grouped-linear paths; CPU QDQ and bitwise exporter-comparison tests cover the fix.
- Bridge reuse is justified by architecture, VLM, and TP/EP/PP coverage versus extending the old exporter.
- Buffer cleanup preserves SVDQuant corrections; the new NVIDIA header matches
LICENSE_HEADER. - Tests were inspected, not executed.
What does this PR do?
Type of change: new feature
examples/megatron_bridge/export_quantized_megatron_to_hf.py --vllm_fakequant_exportnow exports through Megatron-Bridge instead of ModelOpt's Megatron exporter: it checks the quantizers, folds the weight quantizers into BF16 weights (mtq.fold_weight) and callsbridge.save_hf_pretrained. Megatron-Bridge writes the enabled activation and KV-cache quantizers toquantizer_state.pthand a versionedquant_recipe.yamlunder HF names, whichvllm_serve_fakequant.py <dir>loads automatically. This is how the numerics team evaluates QAD checkpoints in vLLM (OMNIML-6019).What the Megatron-Bridge path adds over the old exporter:
--tp_sizeis new and only allowed with this flag), and every architecture Megatron-Bridge maps;vllm serveloads the export.mtq.fold_weightnow drops every buffer of a folded weight quantizer (e.g. a pinned GGML payload), not only_amax/_pre_quant_scale; otherwise Megatron-Bridge could not save a folded IQ2 model.SVDQuantLinear.fold_weightreads its low-rank factors (also buffers of the weight quantizer) before folding, so SVDQuant folds keep their correction term.mtq.fold_weightalso folds sequential weight quantizers (e.g. W4A8 INT4 + FP8 weights), which it used to skip, leaving them unfolded and enabled.check_mcore_vllm_fakequant_quantizerscollects the existing exporter's checks (unsupported activation settings, MTP andlm_headquantization) so both paths reject the same inputs before anything is written.Builds on #2888 (vLLM reload of the versioned recipe; this PR targets its branch); #2873 added the flag on the old exporter. Needs a Megatron-Bridge with NVIDIA-NeMo/Megatron-Bridge#6565; the script raises a clear error and the new tests skip otherwise (the CI's nemo:26.08 container is older).
Usage
torchrun --nproc_per_node 8 export_quantized_megatron_to_hf.py \ --hf_model_name_or_path Qwen/Qwen3-8B \ --megatron_path /output/qwen3_8b_nvfp4_qad/checkpoints \ --tp_size 8 --vllm_fakequant_export \ --export_unified_hf_path /tmp/Qwen3-8B-NVFP4-QAD-vllm-fq python examples/vllm_serve/vllm_serve_fakequant.py /tmp/Qwen3-8B-NVFP4-QAD-vllm-fq -tp 8Testing
Ran in nemo:26.10.rc3 with NVIDIA-NeMo/Megatron-Bridge#6565, 2× RTX 6000 Ada, unless noted:
tests/gpu_megatron/torch/export/test_vllm_fakequant_mbridge_export.py(new): tiny Nemotron-H (NVFP4 + FP8 KV) and Qwen3 (W4A8, sequential weight quantizers) exported both ways match the old exporter (weights bitwise, ranges equal, recipe a superset of the old recipe's settings).test_quantize_export.py::test_quantize_and_exportalso exports each checkpoint it already quantizes with--vllm_fakequant_export, at EP (Qwen3-MoE, per-expert names), TP (Qwen3-VL, vision tower bit-exact) and PP (Nemotron-H).test_qad.pyadds that export only for the Qwen3.5-VL-MoE case (packed experts), since only QAD saves a VLM's language model alone. With 2 GPUs: 6 passed in 25 min (slowest case 386 s).test_tensor_quant_cpu.py::test_fold_weight_folds_sequential_weight_quantizer(new) fails without the sequential fold.test_tensor_quant_cpu.py::test_fold_weight_folds_svdquant_lora(new) fails without the SVDQuant change;test_unified_hf_export_and_check_safetensors(incl.nvfp4_svdquant) passes.tests/unit,tests/gpu/torch/{quantization,export},tests/gpu_megatron/torch/{export,quantization},tests/gpu_vllm,tests/examples/{megatron_bridge,vllm_serve}: no new failures; the remaining failures also fail on main in the same container (missing optional packages, transformers/torch API drift, pytest timeouts). The CI image (nemo:26.08, older Megatron-Bridge) passestests/gpu_megatron/torch/{export,quantization},test_qad.py,test_quantize_export.pyandtests/examples/vllm_serve, with the new tests skipped.On Qwen3.6 the three engines see the same 410 quantizers and ranges; with the MoE layers quantized to NVFP4, tiny kernel differences flip expert routing, which is where the 0.016 spread comes from (per-token |Δ| ≈ 0.2 with the MoE quantized vs 0.07–0.1 without).
vllm serveloads the export; Megatron logits are bitwise identical before and after folding.backendandbackend_extra_args: both survive export and vLLM reload.Before your PR is "Ready for review"
--vllm_fakequant_export(added in Megatron-Bridge: GGML PTQ, QAD and fake-quant export for large MoE and VLM models #2873, unreleased) now needs a Megatron-Bridge with the quantizer sidecars.CONTRIBUTING.md: N/AAdditional Information
Merge order: #2888 (versioned recipe reload) → NVIDIA-NeMo/Megatron-Bridge#6565 → this PR. OMNIML-6019.
Known limits found while testing (unchanged by this PR): MTP and
lm_headactivation quantization cannot be reloaded by vLLM fake quant, so the export refuses them. The general PTQ recipes quantized the MTP layers of Megatron VLMs by mistake; #2911 fixes that.vllm_serve_fakequant.pystill runs a one-sample calibration before loading the sidecars, so offline clusters need--modelopt-quant-dataset <local jsonl>.🤖 Generated with Claude Code