-
Notifications
You must be signed in to change notification settings - Fork 504
MBridge VLM distillation / QAD support #1938
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
78fd487
bdb745c
0ba8471
cbddfbe
d5e97d8
f0787db
c3f770e
5f65a35
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,16 +1,16 @@ | ||
| # Megatron Bridge | ||
|
|
||
| This directory contains examples of using Model Optimizer with [NeMo Megatron-Bridge](https://github.com/NVIDIA-Nemo/Megatron-Bridge) framework for quantization, distillation, pruning, etc. | ||
| This directory contains examples of using Model Optimizer with the [NeMo Megatron-Bridge](https://github.com/NVIDIA-Nemo/Megatron-Bridge) framework for quantization, pruning, and distillation. These workflows can be used on their own or combined. | ||
|
|
||
| <div align="center"> | ||
|
|
||
| | **Section** | **Description** | **Link** | | ||
| | :------------: | :------------: | :------------: | | ||
| | Pre-Requisites | Development environment setup | \[[Link](#pre-requisites)\] | | ||
| | Post-Training Quantization | Quantizing a model | \[[Link](#post-training-quantization)\] | | ||
| | Sanity-Check Generation | Quick generation check with vLLM | \[[Link](#sanity-check-generation)\] | | ||
| | Distillation | Distilling a pruned or quantized model | \[[Link](#distillation)\] | | ||
| | Pruning | Pruning a model using Minitron algorithm | \[[Link](#pruning)\] | | ||
| | Sanity-Check Generation | Quick generation check with vLLM | \[[Link](#sanity-check-generation)\] | | ||
| | Resources | Extra links to relevant resources | \[[Link](#resources)\] | | ||
|
|
||
| </div> | ||
|
|
@@ -47,6 +47,13 @@ docker run \ | |
| > [!WARNING] | ||
| > Use `python -m pip` instead of `pip` to avoid conflicts with the system-wide installed packages in the NeMo containers. You may also refer to this [doc](https://github.com/NVIDIA-NeMo/Megatron-Bridge/blob/main/docker/common/README.md#installing-packages-inside-the-container) on how to correctly install packages in the NeMo containers without breaking existing torch installation. | ||
|
|
||
| > [!NOTE] | ||
| > **Working with MoE Vision-Language Models (e.g. Qwen3.5-VL-MoE)?** The `nemo:26.06` container's Megatron-Bridge lacks the MoE expert weight mappings these models need (dense VLMs such as Gemma3-VL and Qwen3-VL work as-is). Until the `nemo:26.08` container is released, mount the latest [Megatron-Bridge `main`](https://github.com/NVIDIA-NeMo/Megatron-Bridge) source over the pre-installed copy by adding this to the `docker run` command above: | ||
| > | ||
| > ```bash | ||
| > -v ${MEGATRON_BRIDGE_SRC_DIR}:/opt/Megatron-Bridge | ||
| > ``` | ||
|
|
||
| You also need to login with your HuggingFace token to download gated datasets / models. | ||
| Note that the default dataset for pruning and quantization is [`nemotron-post-training-dataset-v2`](https://huggingface.co/datasets/nvidia/Nemotron-Post-Training-Dataset-v2), which is gated. | ||
|
|
||
|
|
@@ -59,7 +66,7 @@ hf auth login --token <your token> | |
| This section shows how to quantize a HuggingFace model using ModelOpt in the Megatron-Bridge framework. Quantization is a two-step flow: | ||
|
|
||
| 1. [quantize.py](quantize.py) applies post-training quantization (PTQ) with calibration and saves a **Megatron checkpoint** (with ModelOpt state). Tensor / pipeline / expert parallelism are all supported, and the checkpoint can be reloaded for further training (Quantization Aware Training / Quantization Aware Distillation). | ||
| 2. [export.py](export.py) converts that Megatron checkpoint to a **HuggingFace (unified) checkpoint** that deploys directly with TensorRT-LLM, vLLM, or SGLang. | ||
| 2. [export_quantized_megatron_to_hf.py](export_quantized_megatron_to_hf.py) converts that Megatron checkpoint to a **HuggingFace (unified) checkpoint** that deploys directly with TensorRT-LLM, vLLM, or SGLang. | ||
|
|
||
| `quantize.py` supports the following formats via `--quant_cfg` (e.g. `fp8`, `nvfp4`, `int8_sq`, `int4_awq`, `w4a8_awq`, ...). You can also pass any full config name exposed by ModelOpt (e.g. `NVFP4_DEFAULT_CFG`) or a YAML `--recipe` (e.g. `general/ptq/nvfp4_default-kv_fp8`, authoritative for quant_cfg + algorithm + KV-cache). KV-cache quantization can be enabled on top via `--kv_cache_quant` (e.g. `fp8`, `nvfp4`). | ||
|
|
||
|
|
@@ -81,20 +88,20 @@ torchrun --nproc_per_node 2 quantize.py \ | |
| **Step 2 — export** the Megatron checkpoint to a deployable HuggingFace checkpoint: | ||
|
|
||
| ```bash | ||
| torchrun --nproc_per_node 2 export.py \ | ||
| torchrun --nproc_per_node 2 export_quantized_megatron_to_hf.py \ | ||
| --hf_model_name_or_path Qwen/Qwen3-8B \ | ||
| --megatron_path /tmp/Qwen3-8B-NVFP4-megatron \ | ||
| --pp_size 2 \ | ||
| --export_unified_hf_path /tmp/Qwen3-8B-NVFP4-hf | ||
| ``` | ||
|
|
||
| > [!NOTE] | ||
| > The HuggingFace unified exporter does not gather tensor-parallel-sharded weights. Use `--pp_size` on `export.py` to shard a large model with pipeline parallelism across GPUs for export. | ||
| > The HuggingFace unified exporter can't split weights across GPUs with tensor parallelism. For large models, use `--pp_size` on `export_quantized_megatron_to_hf.py` to shard the export across GPUs with pipeline parallelism instead. | ||
|
|
||
| > [!TIP] | ||
| > To recover the accuracy lost during quantization, fine-tune the quantized Megatron checkpoint (from step 1) with [Quantization Aware Distillation (QAD)](#quantization-aware-distillation-qad) before running the step 2 export. | ||
|
|
||
| To see the full usage for advanced configurations, run `torchrun --nproc_per_node 1 quantize.py --help` (or `export.py --help`). | ||
| To see the full usage for advanced configurations, run `torchrun --nproc_per_node 1 quantize.py --help` (or `export_quantized_megatron_to_hf.py --help`). | ||
|
|
||
| ### Vision-Language Models (VLMs) | ||
|
|
||
|
|
@@ -104,15 +111,7 @@ For a vision-language model (e.g. Qwen3.5-VL, Gemma3-VL), `quantize.py` automati | |
| - A **text** dataset runs text-only calibration of the language model (vision tower idle). | ||
|
|
||
| > [!NOTE] | ||
| > HuggingFace unified export (`export.py`) of a quantized VLM is not yet supported; the quantized VLM is saved in Megatron checkpoint format only. | ||
|
|
||
| ## Sanity-Check Generation | ||
|
|
||
| [generate_vllm.py](generate_vllm.py) runs a quick generation check on a unified HuggingFace checkpoint using vLLM. vLLM auto-detects the ModelOpt quantization from the exported `hf_quant_config.json`, so no extra quant flags are needed: | ||
|
|
||
| ```bash | ||
| python generate_vllm.py --model nvidia/NVIDIA-Nemotron-3-Nano-30B-A3B-NVFP4 --trust_remote_code | ||
| ``` | ||
| > HuggingFace unified export (`export_quantized_megatron_to_hf.py`) of a quantized VLM is not yet supported; the quantized VLM is saved in Megatron checkpoint format only. | ||
|
|
||
| ## Distillation | ||
|
|
||
|
|
@@ -184,6 +183,41 @@ torchrun --nproc_per_node 8 distill.py \ | |
| --output_dir /tmp/test_distill | ||
| ``` | ||
|
|
||
| ### Vision-Language Models (VLMs) | ||
|
|
||
| For a vision-language model (e.g. Qwen3.5-VL, Gemma3-VL), `distill.py` distills only the **language model** (on text data) and leaves the vision tower and projector untouched — matching the pruning and quantization behavior. It composes with pruning and QAD (`--student_megatron_path`) exactly as for LLMs, and the HF export reuses `--student_hf_path` (no `--student_hf_model` needed). | ||
|
|
||
| ```bash | ||
| torchrun --nproc_per_node 8 distill.py \ | ||
| --tp_size 8 \ | ||
| --teacher_hf_path Qwen/Qwen3-VL-2B-Thinking \ | ||
| --student_hf_path Qwen/Qwen3-VL-2B-Thinking \ | ||
| ... | ||
| ``` | ||
|
|
||
| ### Converting to Hugging Face format (optional) | ||
|
|
||
| A **non-quantized** distilled checkpoint (LLM or VLM) is saved in Megatron distributed format. If you need a HuggingFace checkpoint, there are two ways to convert it (for a **QAD** checkpoint, which retains quantization state, use [export_quantized_megatron_to_hf.py](export_quantized_megatron_to_hf.py) instead — see [QAD](#quantization-aware-distillation-qad)): | ||
|
|
||
| **Inline** -- add `--hf_export_path` to the `distill.py` command to automatically convert the **final** checkpoint after distillation: | ||
|
|
||
| ```bash | ||
| torchrun --nnodes 1 --nproc_per_node 8 distill.py \ | ||
| ... \ | ||
| --hf_export_path /path/to/save/distilled_hf_ckpt | ||
| ``` | ||
|
|
||
| `--student_hf_path` builds the student and provides the exported config / tokenizer. `--student_hf_model` is a reference HF model with a **homogeneous** architecture, used as the export template only for **heterogeneous** (Puzzletron/NAS) students; for homogeneous models and VLMs, omit it -- it defaults to `--student_hf_path`. | ||
|
|
||
| **Separate conversion** -- convert **any** saved iteration (intermediate or final) with [export_distilled_megatron_to_hf.py](export_distilled_megatron_to_hf.py): | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why to emphasize it?
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The bold any is intentional — it contrasts with the Inline path just above, which only exports the final checkpoint. It flags that the separate script can export any intermediate iteration too. Happy to drop the emphasis if you'd prefer. |
||
|
|
||
| ```bash | ||
| torchrun --nproc_per_node 1 export_distilled_megatron_to_hf.py \ | ||
| --student_hf_path <student_hf_model_or_path> \ | ||
| --megatron_path <distill_output_dir>/checkpoints/iter_<iter_number> \ | ||
| --hf_export_path /path/to/save/distilled_hf_ckpt | ||
| ``` | ||
|
|
||
| ### Quantization Aware Distillation (QAD) | ||
|
|
||
| To recover the accuracy lost during [Post-Training Quantization](#post-training-quantization), distill the quantized model (student) from the original, unquantized model (teacher). Pass the quantized **Megatron checkpoint** produced by `quantize.py` via `--student_megatron_path` (the ModelOpt quantizers are restored automatically, so distillation trains the fake-quantized student), while `--student_hf_path` provides the student architecture and `--teacher_hf_path` points to the original unquantized model. We also use a smaller learning rate for QAD: | ||
|
|
@@ -204,38 +238,12 @@ torchrun --nproc_per_node 8 distill.py \ | |
| --output_dir /output/qwen3_8b_nvfp4_qad | ||
| ``` | ||
|
|
||
| The distilled checkpoint retains the ModelOpt quantization state, so it can be converted to a deployable HuggingFace checkpoint with [export.py](export.py) (point `--megatron_path` at `<output_dir>/checkpoints`), exactly like the PTQ checkpoint in [step 2 above](#post-training-quantization). | ||
| The distilled checkpoint retains the ModelOpt quantization state, so it can be converted to a deployable HuggingFace checkpoint with [export_quantized_megatron_to_hf.py](export_quantized_megatron_to_hf.py) (point `--megatron_path` at `/output/qwen3_8b_nvfp4_qad/checkpoints`), exactly like the PTQ checkpoint in [step 2 above](#post-training-quantization). | ||
|
|
||
| ### Slurm Usage | ||
|
|
||
| To run the distillation script on a Slurm cluster for multi-node training, you just need use `python` instead of `torchrun` and set the number of nodes using `#SBATCH --nodes=<num_nodes>` clause in your Slurm script. | ||
|
|
||
| ### Converting to Hugging Face format (optional) | ||
|
|
||
| The distilled checkpoint is saved in Megatron distributed format. If you need a HuggingFace checkpoint, there are two ways to convert it: | ||
|
|
||
| **Inline** -- add `--hf_export_path` and `--student_hf_model` to the `distill.py` command to automatically convert the final checkpoint after distillation: | ||
|
|
||
| ```bash | ||
| torchrun --nnodes 1 --nproc_per_node 8 distill.py \ | ||
| ... \ | ||
| --hf_export_path /path/to/save/distilled_hf_ckpt \ | ||
| --student_hf_model Qwen/Qwen3-4B | ||
| ``` | ||
|
|
||
| `--student_hf_model` should match the base architecture of the student (used as a template for export). For non-Puzzletron (i.e. standard) models, it should be same as `--student_hf_path`. | ||
|
|
||
| **Separate conversion** -- convert any saved iteration using the Megatron-Bridge conversion script: | ||
|
|
||
| ```bash | ||
| uv run python /opt/Megatron-Bridge/examples/conversion/convert_checkpoints.py export \ | ||
| --hf-model <path_to_pruned_hf_ckpt> \ | ||
| --megatron-path <distill_output_dir>/checkpoints/iter_<iter_number> \ | ||
| --hf-path <path_to_save_distilled_hf_ckpt> | ||
| ``` | ||
|
|
||
| For more details, see the [Megatron-Bridge conversion README](https://github.com/NVIDIA-NeMo/Megatron-Bridge/tree/main/examples/conversion). | ||
|
|
||
| ### Distillation Results | ||
|
|
||
| See [examples/pruning/](../pruning/README.md#tutorials--results) for distillation experiment results covering Minitron and Puzzletron pruning algorithms. | ||
|
|
@@ -336,6 +344,21 @@ torchrun --nproc_per_node 2 prune_minitron.py \ | |
| --output_hf_path /tmp/Qwen3.5-4B-Pruned-3B | ||
| ``` | ||
|
|
||
| ## Sanity-Check Generation | ||
|
|
||
| [generate_vllm.py](generate_vllm.py) runs a quick generation check on an exported HuggingFace checkpoint using vLLM — a useful smoke test for a **quantized**, **pruned**, or **distilled** model to confirm it still produces coherent text. For quantized checkpoints, vLLM auto-detects the ModelOpt quantization from the exported `hf_quant_config.json`, so no extra flags are needed: | ||
|
|
||
| ```bash | ||
| # Quantized model | ||
| python generate_vllm.py --model nvidia/NVIDIA-Nemotron-3-Nano-30B-A3B-NVFP4 --trust_remote_code | ||
|
|
||
| # Pruned model | ||
| python generate_vllm.py --model /tmp/Qwen3-8B-Pruned-6B | ||
| ``` | ||
|
|
||
| > [!NOTE] | ||
| > `--trust_remote_code` is only needed for models that ship custom modeling code (e.g. Nemotron); Qwen models don't require it. | ||
|
|
||
| ## Resources | ||
|
|
||
| - 📅 [Roadmap](https://github.com/NVIDIA/Model-Optimizer/issues/1699) | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.