Skip to content
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
676eac4
MSE & mixed precision in mcore
jenchen13 May 22, 2026
9143e02
lint
jenchen13 May 22, 2026
57e5f26
MSE calibration: universal MseCalibrator fallback for non-NVFP4 quant…
jenchen13 May 26, 2026
2dea94a
update mse test for mixed precision
jenchen13 May 26, 2026
918ed6a
fix gpu test, no duplicate backend registration
jenchen13 May 26, 2026
05a436f
Merge branch 'main' into feature/mcore_mse_mixed_precision
jenchen13 May 27, 2026
ff20eca
fix unit tests
jenchen13 May 27, 2026
985da85
lazy init autoquant register
jenchen13 May 27, 2026
d88e54a
revert breaking change on MSECalibrator
jenchen13 May 27, 2026
5f291a6
revert autoquant and gptq changes
jenchen13 May 27, 2026
5c9cd43
fallback to copy HF remote code if no dir
jenchen13 May 27, 2026
c5c7a2e
Merge branch 'main' into feature/mcore_mse_mixed_precision
jenchen13 May 28, 2026
d63bf70
fix if else
jenchen13 May 28, 2026
2ba29fd
autoquant and gptq in mcore
jenchen13 May 28, 2026
e14fa62
fix logic again
jenchen13 May 29, 2026
e985e93
Merge branch 'main' into feature/mcore_mse_mixed_precision
jenchen13 May 29, 2026
9088966
Merge branch 'feature/mcore_mse_mixed_precision' into jennifchen/mcor…
jenchen13 Jun 2, 2026
68e6837
revert tensor quantizer changes
jenchen13 Jun 2, 2026
967d5ef
Fix MCore layerwise discovery mutation
jenchen13 Jun 15, 2026
5b141b5
Merge branch 'main' into jennifchen/mcore_autoquant_gptq
jenchen13 Jun 16, 2026
55ed710
Fix AutoQuant budget denominator for restored stats
jenchen13 Jun 16, 2026
2acbc03
Update changelog for MCore quantization support
jenchen13 Jun 16, 2026
f40849e
Clean up Megatron parallel state in layerwise test
jenchen13 Jun 22, 2026
a59eecd
Merge branch 'main' into jennifchen/mcore_autoquant_gptq
jenchen13 Jun 24, 2026
f68bab8
fix: sum AutoQuantize cost/score across EP for MCore MoE experts
jenchen13 Jun 25, 2026
8f94cf8
test: strengthen GPTQ mamba hybrid assertions and increase timeout
jenchen13 Jun 25, 2026
fbaa4a3
test: move update_hessian zero-token noop test to CPU unit tests
jenchen13 Jun 25, 2026
d4d91c8
separate megatron calib dataloader out into helper
jenchen13 Jun 25, 2026
bbf7f35
increase timeout for onnx test
jenchen13 Jun 25, 2026
fe80756
test: raise unit test timeout to 120 s on Windows
jenchen13 Jun 25, 2026
6c2d446
update comment
jenchen13 Jun 25, 2026
29475fe
move cpu only gptq tests to cpu unit tests
jenchen13 Jun 26, 2026
850799b
move autoquant registration to plugins/megatron.py; fix circular impo…
jenchen13 Jun 26, 2026
470b48f
remove padding hardcode
jenchen13 Jun 26, 2026
b9a1a6b
Merge branch 'main' into jennifchen/mcore_autoquant_gptq
jenchen13 Jun 26, 2026
0608daf
Increase timeout for Megatron sharded state dict test
jenchen13 Jun 26, 2026
289dbdc
Increase GPU Megatron CI timeout
jenchen13 Jun 29, 2026
35bd94f
Adjust GPU Megatron CI timeout
jenchen13 Jun 29, 2026
f0d1c55
Speed up Mamba GPU quantization tests
jenchen13 Jun 29, 2026
351c6c6
test: tighten timeouts for mamba-hybrid quant tests
jenchen13 Jun 29, 2026
14cd2df
test: prebuild quant CUDA extensions in a session fixture
jenchen13 Jun 29, 2026
9749a49
test: drop redundant quant CUDA extension prebuild fixture
jenchen13 Jun 29, 2026
20a888d
Fix gpu_megatron quant extension timeout
jenchen13 Jun 30, 2026
d6b01ac
test: prebuild quant CUDA extensions via session fixture
jenchen13 Jul 1, 2026
6d75cbc
Merge branch 'main' into jennifchen/mcore_autoquant_gptq
jenchen13 Jul 1, 2026
51ba351
Reduce compressed gpu_megatron state dict matrix
jenchen13 Jul 1, 2026
f518c1a
Address docs and gpu_megatron feedback
jenchen13 Jul 1, 2026
848c317
Add Nemotron Nano AutoQuantize launcher example
jenchen13 Jul 1, 2026
c976a23
Restore quantization docs cross references
jenchen13 Jul 1, 2026
485750b
Update launcher Megatron submodule for AutoQuantize
jenchen13 Jul 1, 2026
8c307de
bring gpu_megatron tests back to 60min
jenchen13 Jul 1, 2026
eccabc8
respond to comments fix tests
jenchen13 Jul 1, 2026
961abac
explain conftest change
jenchen13 Jul 1, 2026
2e5100c
rename example
jenchen13 Jul 1, 2026
054e353
refactor: drop dead AutoQuantize denominator fallback path
jenchen13 Jul 1, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.rst
Comment thread
kevalmorabia97 marked this conversation as resolved.
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,14 @@ Changelog
- The ``nemotron-sft-agentic-v2`` registered dataset (added in #1498) now uses only the ``search`` split. The previously configured ``interactive_agent`` and ``tool_calling`` splits contain content-level defects (heterogeneous schema and a malformed JSON row, respectively) that cause pyarrow's streaming JSON reader to fail deterministically.
- Add shared Megatron-Core calibration forward loop: ``modelopt.torch.utils.plugins.megatron_calibration.get_megatron_calibration_forward_loop`` produces the ``forward_loop`` callable expected by ``mtq.quantize`` / ``mtp.prune``. Replaces the bespoke calibration loops in Megatron-LM and Megatron-Bridge for quantization and pruning with a single canonical implementation.
- Add ``pack=True`` mode to ``get_dataset_dataloader`` (Megatron-LM pretraining-style global-stream document packing): all raw samples concatenated EOS-separated into one token stream, sliced into uniform ``max_sample_length`` rows. Used by the shared megatron calibration loop.
- Support Megatron-Core checkpoint restore and export for MSE ``NVFP4StaticQuantizer``.
- Add mixed-precision FP8 + NVFP4 export for Megatron-Core: per-layer ``quant_algo`` recorded under ``quantized_layers`` in ``hf_quant_config.json``, PP-aware ``kv_cache_dtype`` gather, fused-QKV exclude split into per-HF-name ``q/k/v_proj`` entries.
- Add Nemotron-3-Super-120B-A12B PTQ recipes ``modelopt_recipes/models/Nemotron-3-Super-120B-A12B/super-nvfp4.yaml`` (MSE-mixed) and ``super-nvfp4-max-calib.yaml`` (max-calib mixed): NVFP4 W4A4 routed experts + FP8 per-tensor shared experts / Mamba in/out_proj + FP8 KV cache.
- Add quantized ``nn.Embedding`` support. ``nn.Embedding`` is now registered in ``QuantModuleRegistry`` and exposes ``weight_quantizer`` (embedding table), ``output_quantizer`` (lookup activations), and a permanently disabled ``input_quantizer`` placeholder — embedding inputs are integer indices and cannot be fake-quantized, so direct ``enable*()`` calls raise. ``export_hf_checkpoint`` packs quantized embedding weights alongside Linear layers. Embedding quantizers are opt-in (``parent_class: nn.Embedding`` disabled by default).

**Bug Fixes**

- In Megatron-Core only do EP amax sync for routed expert weights if ``sync_expert_weight_amax=True``. Previously EP amax sync would sync routed expert weights across EP ranks even when ``sync_expert_weight_amax`` was False.
- Fix Megatron-Core HF importer to load fused ``TELayerNormColumnParallelLinear.layer_norm_weight`` from HF for GPT-family models (Qwen3 etc.) under ``--export-default-te-spec``. Importer now prefers per-context keys ``fused_input_layernorm`` / ``fused_pre_mlp_layernorm`` (fallback ``fused_norm`` for Nemotron-H backward compatibility); ``mcore_qwen.py`` provides the new rules. Without this fix, post-prune MMLU sat at chance.

0.44 (2026-05-14)
Expand Down
6 changes: 1 addition & 5 deletions examples/specdec_bench/specdec_bench/datasets/speed.py
Original file line number Diff line number Diff line change
Expand Up @@ -730,11 +730,7 @@ def _load_dataset(self, config_name_or_dataset_path: config_type | str) -> "Data
# Strip HF metadata from the schema to avoid Feature parsing errors
schema = table.schema
if schema.metadata and b"huggingface" in schema.metadata:
new_meta = {
k: v
for k, v in schema.metadata.items()
if k != b"huggingface"
}
new_meta = {k: v for k, v in schema.metadata.items() if k != b"huggingface"}
table = table.replace_schema_metadata(new_meta or None)
dataset = HFDataset(table)
if self.num_samples is not None and self.num_samples < len(dataset):
Expand Down
69 changes: 61 additions & 8 deletions modelopt/torch/export/plugins/hf_checkpoint_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,21 @@

import torch
from huggingface_hub import snapshot_download
from huggingface_hub.errors import LocalEntryNotFoundError
from safetensors.torch import safe_open
from tqdm import tqdm

_HF_HUB_OFFLINE_TRUE_VALUES = {"1", "ON", "YES", "TRUE"}


def _is_hf_hub_offline() -> bool:
return os.environ.get("HF_HUB_OFFLINE", "").strip().upper() in _HF_HUB_OFFLINE_TRUE_VALUES


def _copy_python_files(source_dir: Path, save_dir: Path) -> None:
for py_file in source_dir.glob("*.py"):
shutil.copy2(py_file, save_dir / py_file.name)


def copy_hf_ckpt_remote_code(
pretrained_model_path: str | os.PathLike, save_directory: str | os.PathLike
Expand All @@ -36,7 +48,10 @@ def copy_hf_ckpt_remote_code(
frameworks.

If ``pretrained_model_path`` is a local directory, Python files are copied directly.
If it's a HF Hub model ID (e.g. ``nvidia/NVIDIA-Nemotron-Nano-12B-v2``), files are downloaded from the Hub.
If it's a HF Hub model ID (e.g. ``nvidia/NVIDIA-Nemotron-Nano-12B-v2``), the Hub
snapshot is resolved first and Python files are copied from that snapshot. When
``HF_HUB_OFFLINE`` is set, the snapshot must already be available in the local
Hugging Face cache.

Args:
pretrained_model_path: Local path to the pretrained model or HuggingFace Hub model ID.
Expand All @@ -47,14 +62,28 @@ def copy_hf_ckpt_remote_code(
save_dir.mkdir(parents=True, exist_ok=True)

if hf_checkpoint_path.is_dir():
for py_file in hf_checkpoint_path.glob("*.py"):
shutil.copy2(py_file, save_dir / py_file.name)
_copy_python_files(hf_checkpoint_path, save_dir)
else:
snapshot_download(
repo_id=str(pretrained_model_path),
local_dir=str(save_dir),
allow_patterns=["*.py"],
)
local_files_only = _is_hf_hub_offline()
try:
source_dir = Path(
snapshot_download(
repo_id=str(pretrained_model_path),
allow_patterns=["*.py"],
local_files_only=local_files_only,
)
)
except LocalEntryNotFoundError as exc:
if local_files_only:
raise RuntimeError(
f"Could not copy Python sidecar files for {pretrained_model_path!r} because "
"HF_HUB_OFFLINE is enabled and the files are not available in the local "
"Hugging Face cache. Populate the cache with the model's *.py files or pass "
"a local pretrained model directory."
) from exc
raise

_copy_python_files(source_dir, save_dir)


def load_multimodal_components(
Expand Down Expand Up @@ -123,3 +152,27 @@ def load_multimodal_components(

print(f"Successfully loaded {len(multimodal_state_dict)} multimodal tensors")
return multimodal_state_dict


def copy_non_safetensor_files_from_ckpt(src: str | os.PathLike, dst: str | os.PathLike):
"""Copy every non-safetensors file from a local HF checkpoint dir verbatim.

Use as a baseline so tokenizer files, remote_code ``*.py``, README, LICENSE, etc.
are preserved from the source. The caller is expected to overwrite the files
modelopt owns (``config.json``, ``generation_config.json``, ``hf_quant_config.json``,
``preprocessor_config.json``) after this step.

Args:
src: Source HF checkpoint directory. Must be a local path.
dst: Destination directory; created if missing.
"""
if not os.path.isdir(src):
raise ValueError(f"Invalid source path: {src}. It should be a directory.")
os.makedirs(dst, exist_ok=True)
for entry in os.listdir(src):
sp = os.path.join(src, entry)
if not os.path.isfile(sp):
continue
if entry.endswith(".safetensors") or entry == "model.safetensors.index.json":
continue
shutil.copy2(sp, dst)
5 changes: 4 additions & 1 deletion modelopt/torch/export/plugins/mcore_nemotron.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,10 @@
"input_layernorm": NameRemapping("backbone.layers.{}.norm."),
"linear_qkv": QKVSlicing("backbone.layers.{}.mixer."),
"linear_proj": NameRemapping("backbone.layers.{}.mixer.o_proj."),
"core_attention": SelfAttentionScaling("backbone.layers.{}.mixer."),
"core_attention": SelfAttentionScaling(
"backbone.layers.{}.mixer.",
func_kwargs={"k_scale_name": "k_proj.k_scale", "v_scale_name": "v_proj.v_scale"},
),
# MLP
"pre_mlp_layernorm": NameRemapping("backbone.layers.{}.norm."),
"linear_fc1": NameRemapping("backbone.layers.{}.mixer.up_proj."),
Expand Down
20 changes: 18 additions & 2 deletions modelopt/torch/export/quant_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -288,9 +288,25 @@ def _ensure_weight_quantizer_calibrated(
module_name: Optional module name for better warning messages
"""
if isinstance(weight_quantizer, NVFP4StaticQuantizer):
need_per_block = not hasattr(weight_quantizer, "_amax") or weight_quantizer._amax is None

def _amax_is_invalid(t: torch.Tensor | None) -> bool:
# MCore distcp may register but not fill amax — treat missing/non-finite/negative as recompute.
if t is None:
return True
t = t.detach()
if not torch.is_floating_point(t):
return False
return bool((~torch.isfinite(t) | (t < 0)).any().item())

need_per_block = (
not hasattr(weight_quantizer, "_amax")
or weight_quantizer._amax is None
or _amax_is_invalid(weight_quantizer._amax)
)
need_global = (
not hasattr(weight_quantizer, "_global_amax") or weight_quantizer.global_amax is None
not hasattr(weight_quantizer, "_global_amax")
or weight_quantizer.global_amax is None
or _amax_is_invalid(weight_quantizer.global_amax)
)
if not (need_per_block or need_global):
return
Expand Down
Loading
Loading