diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 448f085adb7..edfec36beb8 100755 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -89,6 +89,7 @@ Changelog - Add mergeable LoRA QAD for fake-quantized dense Megatron/Transformer Engine linear layers. Enable it with ``--lora_rank`` in Megatron-Bridge PTQ, train through the normal QAD flow, and export a standalone quantized checkpoint without deployment adapters. - Add optional Top-P (nucleus) truncation to the Megatron top-k logits KD loss (now ``TopLogitsKLLoss``) via ``logit_kl_top_p`` and ``logit_kl_top_p_min_k`` in ``DistillationConfig``: after the global Top-K selection, only the smallest prefix whose cumulative teacher probability reaches ``top_p`` (with a floor of ``min_k`` entries) contributes to the KL, mirroring ``--logits-save-top-p`` / ``--logits-save-top-p-min-k`` in Megatron-LM's logits saver. - Add an end-to-end W4A4 NVFP4 PTQ and QAD tutorial for Qwen3.6-35B-A3B also covering evaluation and vLLM throughput benchmarking. See `examples/megatron_bridge/tutorials/Qwen3.6-35B-A3B/README.md `_ for details. +- Add an end-to-end W4A4 NVFP4 PTQ and QAD tutorial for NVIDIA Nemotron 3.5 Super VL, covering chat-data blend preparation, assistant-only distillation, Hugging Face export and vLLM serving. See `examples/megatron_bridge/tutorials/NVIDIA-Nemotron-3.5-Super-VL-120B-A12B-BF16/README.md `_ for details. - Add ``--mlflow `` to the ``examples/megatron_bridge`` scripts that write a checkpoint -- ``prune_minitron.py``, ``quantize.py``, ``distill.py``, ``export_quantized_megatron_to_hf.py`` and ``export_distilled_megatron_to_hf.py`` (MLflow's own ``MLFLOW_TRACKING_URI`` is honoured too). Each run records the invocation, its arguments as searchable params and its log -- a distillation records its training metrics instead of the rank-0 log -- and writes ``.experiment.json`` into the checkpoint it produced, so a pruning, a quantization, the distillation that refines its checkpoint and the export that deploys it can be traced to one another; uploading the checkpoints themselves stays off unless ``--mlflow_log_checkpoints`` is passed. - Add ``--ep_size`` to ``examples/megatron_bridge/export_quantized_megatron_to_hf.py`` so large MoE models with grouped-GEMM experts can be exported with their experts sharded across GPUs. Checkpoints built with ``--no_moe_grouped_gemm`` must still be exported at ``--ep_size 1``. - Add unified HF export of GLM-5 / GLM-5.2 (``glm_moe_dsa``) checkpoints quantized with ``examples/megatron_bridge``. The MTP layer, which Megatron-Bridge does not build for these models, is copied through unquantized from the source checkpoint. DSA sparse-attention models also gain FP8 KV-cache quantization on Megatron. diff --git a/examples/megatron_bridge/README.md b/examples/megatron_bridge/README.md index a6428d79022..19deab559ba 100644 --- a/examples/megatron_bridge/README.md +++ b/examples/megatron_bridge/README.md @@ -20,7 +20,7 @@ This directory contains examples of using Model Optimizer with the [NeMo Megatro > > Or the [Qwen3.6-35B-A3B W4A4 NVFP4 + QAD tutorial](tutorials/Qwen3.6-35B-A3B/README.md) for an end-to-end quantization-aware distillation workflow. > -> Or the [NemotronH omni VL W4A4 + QAD tutorial](tutorials/NemotronH-Omni-W4A4-QAD/README.md) for a Mamba-Transformer MoE vision-language model. +> Or the [NVIDIA Nemotron 3.5 Super VL W4A4 + QAD tutorial](tutorials/NVIDIA-Nemotron-3.5-Super-VL-120B-A12B-BF16/README.md) for a Mamba-Transformer MoE vision-language model. ## Pre-Requisites diff --git a/examples/megatron_bridge/tutorials/NemotronH-Omni-W4A4-QAD/README.md b/examples/megatron_bridge/tutorials/NVIDIA-Nemotron-3.5-Super-VL-120B-A12B-BF16/README.md similarity index 86% rename from examples/megatron_bridge/tutorials/NemotronH-Omni-W4A4-QAD/README.md rename to examples/megatron_bridge/tutorials/NVIDIA-Nemotron-3.5-Super-VL-120B-A12B-BF16/README.md index 461f2e201c9..8df736d75a4 100644 --- a/examples/megatron_bridge/tutorials/NemotronH-Omni-W4A4-QAD/README.md +++ b/examples/megatron_bridge/tutorials/NVIDIA-Nemotron-3.5-Super-VL-120B-A12B-BF16/README.md @@ -1,6 +1,6 @@ -# NemotronH Omni VL: W4A4 NVFP4 PTQ + QAD + Deployment +# NVIDIA Nemotron 3.5 Super VL: W4A4 NVFP4 PTQ + QAD + Deployment -End-to-end W4A4 quantization of a NemotronH omni vision-language checkpoint (architecture `NemotronH_Omni_Reasoning_V3`), a Mamba-Transformer MoE language model with a vision tower: mixed NVFP4/FP8 post-training quantization → Quantization-Aware Distillation (QAD) → Hugging Face export → vLLM serving. This document covers: +End-to-end W4A4 quantization of [NVIDIA Nemotron 3.5 Super VL](https://huggingface.co/nvidia/NVIDIA-Nemotron-3.5-Super-VL-120B-A12B-BF16) (architecture `NemotronH_Omni_Reasoning_V3`), a Mamba-Transformer MoE language model with a vision tower: mixed NVFP4/FP8 post-training quantization → Quantization-Aware Distillation (QAD) → Hugging Face export → vLLM serving. This document covers: 1. **[Data Preparation](#1-data-preparation)**: the chat-format QAD blend, from `data_blend.yaml` 2. **[Quantization](#2-quantization)**: W4A4 PTQ with `examples/megatron_bridge/quantize.py` @@ -10,6 +10,8 @@ End-to-end W4A4 quantization of a NemotronH omni vision-language checkpoint (arc Only the language model (including its MTP head) is quantized; the vision tower and projector stay BF16 and are copied from the source checkpoint. +The PTQ recipe is the same as the one used for the released [`nvidia/NVIDIA-Nemotron-3.5-Super-VL-120B-A12B-NVFP4`](https://huggingface.co/nvidia/NVIDIA-Nemotron-3.5-Super-VL-120B-A12B-NVFP4) checkpoint. The calibration data and the QAD training data are all public Hugging Face datasets. The evaluation results below differ from the official model card scores. + ## Results All checkpoints were served with the same vLLM settings (see [Section 5](#5-evaluation-and-serving)); the quantized ones with an FP8 KV cache. BF16 is the mean of two runs. @@ -72,14 +74,14 @@ PTQ runs on **one 4-GPU GB300 node** with 512 calibration samples. ```bash torchrun --nproc_per_node 4 /opt/Model-Optimizer/examples/megatron_bridge/quantize.py \ - --hf_model_name_or_path \ + --hf_model_name_or_path nvidia/NVIDIA-Nemotron-3.5-Super-VL-120B-A12B-BF16 \ --trust_remote_code \ --recipe models/nvidia/NVIDIA-Nemotron-3.5-Super-VL-120B-A12B-BF16/ptq/nvfp4-mse \ --tp_size 1 --ep_size 4 --pp_size 1 \ --calib_dataset_name cnn_nemotron_v2_mix \ --calib_num_samples 512 \ --calib_batch_size 1 \ - --export_megatron_path /path/to/nemotron_h_omni_w4a4_megatron \ + --export_megatron_path /path/to/nemotron_3_5_super_vl_w4a4_megatron \ --skip_generate ``` @@ -108,9 +110,9 @@ We used **8 nodes × 4 GB300 (32 GPUs)** with context parallelism 4 and expert p export MASTER_ADDR=$(scontrol show hostnames "$SLURM_JOB_NODELIST" | head -n 1) MASTER_PORT=29500 srun bash -c 'export RANK=$SLURM_PROCID WORLD_SIZE=$SLURM_NTASKS LOCAL_RANK=$SLURM_LOCALID python -u /opt/Model-Optimizer/examples/megatron_bridge/distill.py \ - --teacher_hf_path \ - --student_hf_path \ - --student_megatron_path /path/to/nemotron_h_omni_w4a4_megatron \ + --teacher_hf_path nvidia/NVIDIA-Nemotron-3.5-Super-VL-120B-A12B-BF16 \ + --student_hf_path nvidia/NVIDIA-Nemotron-3.5-Super-VL-120B-A12B-BF16 \ + --student_megatron_path /path/to/nemotron_3_5_super_vl_w4a4_megatron \ --trust_remote_code \ --tp_size 1 --pp_size 1 --cp_size 4 --ep_size 32 \ --sft --sft_hf_dataset /path/to/qad_blend/train.jsonl \ @@ -145,10 +147,10 @@ Export the QAD checkpoint to a Hugging Face checkpoint on one 4-GPU node (pipeli ```bash torchrun --nproc_per_node 4 /opt/Model-Optimizer/examples/megatron_bridge/export_quantized_megatron_to_hf.py \ - --hf_model_name_or_path \ + --hf_model_name_or_path nvidia/NVIDIA-Nemotron-3.5-Super-VL-120B-A12B-BF16 \ --trust_remote_code \ --megatron_path /path/to/qad_output/checkpoints \ - --export_unified_hf_path /path/to/nemotron_h_omni_w4a4_hf \ + --export_unified_hf_path /path/to/nemotron_3_5_super_vl_w4a4_hf \ --pp_size 4 ``` @@ -167,9 +169,10 @@ Serve with vLLM on one 4-GPU GB300 node, TP=4 with expert parallelism. Quantized | Setting | Value | | --- | --- | | `--kv-cache-dtype` | `fp8` | -| `--max-model-len` | 262144 | +| `--max-model-len` | 262144 (32768 for MMLU-ProX) | | `--mamba-ssm-cache-dtype` | `float16` | -| `--mamba-conv-cache-dtype` | `float32` | -| Mamba cache rounding | stochastic rounding, 5 Philox rounds | +| `--mamba-cache-dtype` | `float32` | +| `--enable-mamba-cache-stochastic-rounding` | on | +| `--mamba-cache-philox-rounds` | 5 | | Reasoning parser | `nemotron_v3` | | Chat template | the checkpoint's `chat_template.jinja` | diff --git a/examples/megatron_bridge/tutorials/NemotronH-Omni-W4A4-QAD/data_blend.yaml b/examples/megatron_bridge/tutorials/NVIDIA-Nemotron-3.5-Super-VL-120B-A12B-BF16/data_blend.yaml similarity index 100% rename from examples/megatron_bridge/tutorials/NemotronH-Omni-W4A4-QAD/data_blend.yaml rename to examples/megatron_bridge/tutorials/NVIDIA-Nemotron-3.5-Super-VL-120B-A12B-BF16/data_blend.yaml diff --git a/examples/megatron_bridge/tutorials/README.md b/examples/megatron_bridge/tutorials/README.md index 454403083e8..ca03a85c993 100644 --- a/examples/megatron_bridge/tutorials/README.md +++ b/examples/megatron_bridge/tutorials/README.md @@ -8,5 +8,5 @@ Each one walks through a complete workflow using the scripts in [examples/megatr | Tutorial | What it covers | | --- | --- | | [NVIDIA-Nemotron-3-Nano-30B-A3B-BF16](NVIDIA-Nemotron-3-Nano-30B-A3B-BF16/README.md) | End-to-end optimization of the Nemotron-3-Nano-30B-A3B-BF16 (MoE + Mamba-Transformer hybrid) model: Minitron structured pruning (31.6B/A3.6B → 22B/A3.0B) → two-phase knowledge distillation (100B tokens, 8K then 32K seq length) → quantization → vLLM deployment. Includes data-blend preparation, evaluation setup, and detailed pruning / data-blend / long-context ablations. | -| [NemotronH-Omni-W4A4-QAD](NemotronH-Omni-W4A4-QAD/README.md) | **W4A4** quantization of the language model of a NemotronH omni vision-language checkpoint (`NemotronH_Omni_Reasoning_V3`, Mamba-Transformer MoE): mixed NVFP4/FP8 PTQ → KD-only QAD on chat data → Hugging Face export → vLLM serving. | +| [NVIDIA-Nemotron-3.5-Super-VL-120B-A12B-BF16](NVIDIA-Nemotron-3.5-Super-VL-120B-A12B-BF16/README.md) | **W4A4** quantization of the language model of NVIDIA Nemotron 3.5 Super VL (`NemotronH_Omni_Reasoning_V3`, Mamba-Transformer MoE): mixed NVFP4/FP8 PTQ → KD-only QAD on chat data → Hugging Face export → vLLM serving. | | [Qwen3.6-35B-A3B](Qwen3.6-35B-A3B/README.md) | End-to-end **W4A4 NVFP4** optimization of the Qwen3.6-35B-A3B (MoE + hybrid linear/full attention VLM) model: NVFP4 W4A4 post-training quantization → quantization-aware distillation (QAD) to recover the accuracy W4A4 costs → evaluation → vLLM deployment. | diff --git a/modelopt/torch/utils/plugins/prepare_megatron_data_blend.py b/modelopt/torch/utils/plugins/prepare_megatron_data_blend.py index 2bc7aacb8c9..86f45089a43 100644 --- a/modelopt/torch/utils/plugins/prepare_megatron_data_blend.py +++ b/modelopt/torch/utils/plugins/prepare_megatron_data_blend.py @@ -199,8 +199,9 @@ def _to_chat( if row.get(key): chat[key] = row[key] else: - text = row.get("text") or "" - if not text: + text = row.get("text") + # A non-string value (e.g. a list) would otherwise pass as list content. + if not isinstance(text, str) or not text: return None if " max_rejected: break @@ -288,7 +296,7 @@ def _take_chat_rows( if len(rows) >= want: break except Exception as e: # e.g. a gated split, or a schema the streaming reader cannot cast - raise RuntimeError(f"{name}: failed to read or convert") from e + raise RuntimeError(f"{name}: failed to read") from e if len(rows) < want: gave_up = rejected > max_rejected reason = f"gave up after {rejected} rejected rows" if gave_up else "source exhausted" diff --git a/tests/gpu_megatron/torch/utils/plugins/test_prepare_megatron_data_blend.py b/tests/gpu_megatron/torch/utils/plugins/test_prepare_megatron_data_blend.py index 6306ecef292..c03bfa489b3 100644 --- a/tests/gpu_megatron/torch/utils/plugins/test_prepare_megatron_data_blend.py +++ b/tests/gpu_megatron/torch/utils/plugins/test_prepare_megatron_data_blend.py @@ -203,6 +203,7 @@ def test_to_chat_handles_all_row_shapes(): "messages": [{"role": "assistant", "content": "def f(): pass"}] } assert blend_module._to_chat({"text": ""}, "text") is None + assert blend_module._to_chat({"text": ["x"]}, "text") is None # Markers that parse to no turns: dropped, never trained as assistant text. assert blend_module._to_chat({"text": "\nraw body\n"}, "text") is None # A role without a mapping would cut its turn out of the middle: the whole row is dropped. @@ -342,7 +343,7 @@ def iter_rows(source, output_dir, shuffle, seed): raise TypeError("cannot cast") monkeypatch.setattr(blend_module, "_iter_rows", iter_rows) - with pytest.raises(RuntimeError, match="org/a train: failed to read or convert"): + with pytest.raises(RuntimeError, match="org/a train: failed to read"): blend_module.prepare_chat_data_blend(write_config()) assert not (tmp_path / "blend" / "train.jsonl").exists() @@ -357,8 +358,10 @@ def test_write_jsonl_is_atomic(tmp_path: Path): def test_chat_blend_reads_files_sources(tmp_path: Path, monkeypatch): plain, packed = tmp_path / "a.jsonl", tmp_path / "b.jsonl.gz" lines = [json.dumps(_chat(i)) for i in range(20)] - # A malformed record is skipped with a warning, as in the tokenizing reader. - plain.write_text("\n".join([*lines[:10], '{"messages": [', *lines[10:]]) + "\n", "utf-8") + # Malformed records, unparseable or unconvertible, are skipped with a warning as in the + # tokenizing reader. + bad = ['{"messages": [', '{"messages": "hello"}'] + plain.write_text("\n".join([*lines[:10], *bad, *lines[10:]]) + "\n", "utf-8") with gzip.open(packed, "wt", encoding="utf-8") as f: f.writelines(json.dumps(_chat(i)) + "\n" for i in range(20, 40)) paths = {"data/a.jsonl": str(plain), "data/b.jsonl.gz": str(packed)}