From df6040d7904c589b3b4ae66ecdd529ef206ebac7 Mon Sep 17 00:00:00 2001 From: Chenjie Luo Date: Sun, 17 May 2026 10:56:56 -0700 Subject: [PATCH 1/6] =?UTF-8?q?Add=20DATASET=5FCOMBOS=20=E2=80=94=20named?= =?UTF-8?q?=20groups=20of=20registered=20calibration=20datasets?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Lets a single --dataset token fan out to several entries in SUPPORTED_DATASET_CONFIG, with that entry's num_samples split evenly across the members. Initial combos: - ``default``: cnn_dailymail + nemotron-post-training-dataset-v2. hf_ptq.py now uses this when --dataset is omitted, replacing a hardcoded two-element list. --calib_size default bumped 512 -> 1024 so the per-dataset sample count after the even split matches the prior behavior. - ``nemotron-sft-mix``: the seven nvidia/Nemotron-* SFT datasets registered in #1498. get_supported_datasets() now lists combo names so they appear in --dataset --help. Expansion happens inside get_dataset_dataloader after list normalization. Co-Authored-By: Claude Opus 4.7 (1M context) Signed-off-by: Chenjie Luo --- CHANGELOG.rst | 1 + examples/llm_ptq/hf_ptq.py | 7 +++--- modelopt/torch/utils/dataset_utils.py | 32 ++++++++++++++++++++++++++- 3 files changed, 36 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 7dd63c29ecd..793ab412f54 100755 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -25,6 +25,7 @@ Changelog - Add ``--cast_mxfp4_to_nvfp4`` flag to ``examples/llm_ptq/hf_ptq.py`` for closed-form, bit-exact MXFP4 → NVFP4 weight conversion. Supports the GPT-OSS family (``openai/gpt-oss-20b``, ``openai/gpt-oss-120b``). See `examples/llm_ptq/README.md `__ for usage. - DeepSeek PTQ (``examples/deepseek/ptq.py``) now defaults to native top-k calibration with post-hoc per-layer peer-max sync of expert ``input_quantizer.amax``; the all-experts path is preserved behind ``--calib_all_experts``. - Add NVFP4 W4A16 weight-only quantization (``w4a16_nvfp4``): FP4 weights with group_size=16, BF16 activations, no calibration forward pass required. Use ``mtq.W4A16_NVFP4_CFG`` or ``--qformat w4a16_nvfp4`` in ``hf_ptq.py``. vLLM deployment support is in progress. +- Add ``DATASET_COMBOS`` to ``modelopt.torch.utils.dataset_utils`` — single ``--dataset`` tokens that fan out to multiple registered datasets; per-entry ``num_samples`` is split evenly across the members. Initial combos: ``default`` (``cnn_dailymail`` + ``nemotron-post-training-dataset-v2``, used by ``hf_ptq.py`` when no ``--dataset`` is provided) and ``nemotron-sft-mix`` (the seven ``nvidia/Nemotron-*`` SFT datasets added in #1498). Combo names are listed by ``get_supported_datasets()`` and surfaced in ``--dataset`` help. The ``hf_ptq.py`` default ``--calib_size`` is bumped from ``512`` to ``1024`` so the new ``default`` combo preserves the previous total sample count. 0.44 (2026-05-18) ^^^^^^^^^^^^^^^^^ diff --git a/examples/llm_ptq/hf_ptq.py b/examples/llm_ptq/hf_ptq.py index d52c3ee40bb..c92219a91ce 100755 --- a/examples/llm_ptq/hf_ptq.py +++ b/examples/llm_ptq/hf_ptq.py @@ -531,9 +531,10 @@ def load_model(args: argparse.Namespace): language_model = full_model else: if args.dataset is None: - args.dataset = ["cnn_dailymail", "nemotron-post-training-dataset-v2"] + args.dataset = ["default"] warnings.warn( - "No dataset specified. Defaulting to cnn_dailymail and nemotron-post-training-dataset-v2." + "No dataset specified. Defaulting to the 'default' combo " + "(cnn_dailymail + nemotron-post-training-dataset-v2)." ) # Adjust calib_size to match dataset length by extending or truncating as needed args.calib_size = (args.calib_size + [args.calib_size[-1]] * len(args.dataset))[ @@ -1238,7 +1239,7 @@ def parse_args() -> argparse.Namespace: "This argument will be parsed and converted as a list of ints." ), type=str, - default="512", + default="1024", ) parser.add_argument( "--calib_seq", diff --git a/modelopt/torch/utils/dataset_utils.py b/modelopt/torch/utils/dataset_utils.py index 4329f15488f..814ba72c379 100644 --- a/modelopt/torch/utils/dataset_utils.py +++ b/modelopt/torch/utils/dataset_utils.py @@ -195,6 +195,22 @@ def _join_messages_content(sample: dict) -> str: }, } +# Named groups of registered datasets, expanded in ``get_dataset_dataloader``. +# Useful when callers want a single ``--dataset`` token that fans out to several +# entries; per-dataset ``num_samples`` is split evenly across the members. +DATASET_COMBOS: dict[str, list[str]] = { + "default": ["cnn_dailymail", "nemotron-post-training-dataset-v2"], + "nemotron-sft-mix": [ + "nemotron-sft-instruction-following-chat-v2", + "nemotron-science-v1", + "nemotron-competitive-programming-v1", + "nemotron-sft-agentic-v2", + "nemotron-math-v2", + "nemotron-sft-swe-v2", + "nemotron-sft-multilingual-v1", + ], +} + __all__ = [ "create_forward_loop", "download_hf_dataset_as_jsonl", @@ -560,6 +576,20 @@ def get_dataset_dataloader( "dataset_name and num_samples must be the same length" ) + expanded_names: list[str] = [] + expanded_num_samples: list[int] = [] + for ds_name, n in zip(dataset_name, num_samples): + if ds_name in DATASET_COMBOS: + members = DATASET_COMBOS[ds_name] + base, remainder = divmod(n, len(members)) + for i, member in enumerate(members): + expanded_names.append(member) + expanded_num_samples.append(base + (1 if i < remainder else 0)) + else: + expanded_names.append(ds_name) + expanded_num_samples.append(n) + dataset_name, num_samples = expanded_names, expanded_num_samples + all_samples = [] for ds_name, num_sample in zip(dataset_name, num_samples): samples = get_dataset_samples( @@ -617,7 +647,7 @@ def get_supported_datasets() -> list[str]: print("Supported datasets:", get_supported_datasets()) """ - return list(SUPPORTED_DATASET_CONFIG.keys()) + return list(SUPPORTED_DATASET_CONFIG.keys()) + list(DATASET_COMBOS.keys()) @contextmanager From 10f3cfd860864c41720120359a769c84932daeaa Mon Sep 17 00:00:00 2001 From: Chenjie Luo Date: Mon, 18 May 2026 21:37:28 +0000 Subject: [PATCH 2/6] Fix nemotron-sft-agentic-v2 splits in DATASET_COMBOS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit End-to-end probing of the nemotron-sft-mix combo showed both ``interactive_agent`` and ``tool_calling`` fail under pyarrow's streaming JSON reader: the former at JSONL row 4 (``Column(.../member_id/type) changed from string to array``), the latter deterministically at sample ~885 (``Missing a closing quotation mark in string``). Both failures reproduce after a full HF cache wipe with ``force_redownload``, so they are content defects in the published JSONL — not local cache corruption. ``search``, which the previous comment incorrectly described as the skipped split, is the only one that streams cleanly (verified to 2500 samples). Switch the config to use only ``search`` and update the comment to describe the actual failure modes of the dropped splits. Co-Authored-By: Claude Opus 4.7 (1M context) Signed-off-by: Chenjie Luo --- modelopt/torch/utils/dataset_utils.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/modelopt/torch/utils/dataset_utils.py b/modelopt/torch/utils/dataset_utils.py index 814ba72c379..8cd6814b2c0 100644 --- a/modelopt/torch/utils/dataset_utils.py +++ b/modelopt/torch/utils/dataset_utils.py @@ -109,10 +109,12 @@ def _join_messages_content(sample: dict) -> str: "chat_key": "messages", }, "nemotron-sft-agentic-v2": { - # Skips ``search`` split: heterogeneous messages schema fails streaming cast. + # Only ``search`` streams cleanly: ``interactive_agent`` has a heterogeneous + # tools schema (string vs list) that breaks pyarrow JSON inference, and + # ``tool_calling`` contains at least one malformed JSON row in a later shard. "config": { "path": "nvidia/Nemotron-SFT-Agentic-v2", - "split": ["interactive_agent", "tool_calling"], + "split": ["search"], }, "preprocess": _join_messages_content, "chat_key": "messages", From 0202e738595e071dfae0d44c42df409264df4e41 Mon Sep 17 00:00:00 2001 From: Chenjie Luo Date: Mon, 18 May 2026 21:54:49 +0000 Subject: [PATCH 3/6] Reject combo + member overlap in --dataset MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Passing both a combo and one of its member datasets (e.g. ``--dataset cnn_dailymail,default``) silently double-samples the member — the combo expansion would take a per-member quota from cnn_dailymail in addition to the explicit entry. Raise ValueError in ``get_dataset_dataloader`` listing the combo and the overlapping member(s) so the user can drop one. Co-Authored-By: Claude Opus 4.7 (1M context) Signed-off-by: Chenjie Luo --- modelopt/torch/utils/dataset_utils.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/modelopt/torch/utils/dataset_utils.py b/modelopt/torch/utils/dataset_utils.py index 8cd6814b2c0..724cd9f51aa 100644 --- a/modelopt/torch/utils/dataset_utils.py +++ b/modelopt/torch/utils/dataset_utils.py @@ -578,6 +578,20 @@ def get_dataset_dataloader( "dataset_name and num_samples must be the same length" ) + # Reject inputs that include both a combo and one of its member datasets + # (e.g. ``["cnn_dailymail", "default"]``), since the combo would sample the + # plain entry a second time with a smaller per-member quota. + plain_inputs = {n for n in dataset_name if n not in DATASET_COMBOS} + for ds_name in dataset_name: + if ds_name in DATASET_COMBOS: + overlap = plain_inputs & set(DATASET_COMBOS[ds_name]) + if overlap: + raise ValueError( + f"--dataset includes both combo '{ds_name}' and its " + f"member(s) {sorted(overlap)}; remove one to avoid " + "double-sampling." + ) + expanded_names: list[str] = [] expanded_num_samples: list[int] = [] for ds_name, n in zip(dataset_name, num_samples): From 936a374d05e8a7b67c6e83a2b9404d74f04977c4 Mon Sep 17 00:00:00 2001 From: Chenjie Luo Date: Mon, 18 May 2026 22:10:05 +0000 Subject: [PATCH 4/6] Harden DATASET_COMBOS: validation, safety, tests, CHANGELOG MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Validate DATASET_COMBOS at import time: empty member lists, name collisions with SUPPORTED_DATASET_CONFIG, and references to unregistered datasets now raise ValueError up front so typos surface immediately rather than as confusing HF lookup failures. - Reject combo names in ``get_dataset_samples`` with a message pointing callers to ``get_dataset_dataloader``. Previously a combo name would fall through to the auto-detected HF path and fail opaquely. - Add five unit tests in ``TestDatasetCombosExpansion`` covering even split, divmod remainder distribution (n=10 over 7 members → [2,2,2,1,1,1,1]), plain + combo composition, overlap rejection, and the new ``get_dataset_samples`` combo-name guard. Tests mock ``get_dataset_samples`` so they don't touch the HF Hub. - Split the CHANGELOG entry: document the combo + member overlap and combo-in-``get_dataset_samples`` rejections, and add a separate bullet for the ``nemotron-sft-agentic-v2`` split swap so it isn't buried inside the combo entry. Co-Authored-By: Claude Opus 4.7 (1M context) Signed-off-by: Chenjie Luo --- CHANGELOG.rst | 3 +- modelopt/torch/utils/dataset_utils.py | 27 +++++++ tests/unit/torch/utils/test_dataset_utils.py | 83 ++++++++++++++++++++ 3 files changed, 112 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 793ab412f54..89beada25b3 100755 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -25,7 +25,8 @@ Changelog - Add ``--cast_mxfp4_to_nvfp4`` flag to ``examples/llm_ptq/hf_ptq.py`` for closed-form, bit-exact MXFP4 → NVFP4 weight conversion. Supports the GPT-OSS family (``openai/gpt-oss-20b``, ``openai/gpt-oss-120b``). See `examples/llm_ptq/README.md `__ for usage. - DeepSeek PTQ (``examples/deepseek/ptq.py``) now defaults to native top-k calibration with post-hoc per-layer peer-max sync of expert ``input_quantizer.amax``; the all-experts path is preserved behind ``--calib_all_experts``. - Add NVFP4 W4A16 weight-only quantization (``w4a16_nvfp4``): FP4 weights with group_size=16, BF16 activations, no calibration forward pass required. Use ``mtq.W4A16_NVFP4_CFG`` or ``--qformat w4a16_nvfp4`` in ``hf_ptq.py``. vLLM deployment support is in progress. -- Add ``DATASET_COMBOS`` to ``modelopt.torch.utils.dataset_utils`` — single ``--dataset`` tokens that fan out to multiple registered datasets; per-entry ``num_samples`` is split evenly across the members. Initial combos: ``default`` (``cnn_dailymail`` + ``nemotron-post-training-dataset-v2``, used by ``hf_ptq.py`` when no ``--dataset`` is provided) and ``nemotron-sft-mix`` (the seven ``nvidia/Nemotron-*`` SFT datasets added in #1498). Combo names are listed by ``get_supported_datasets()`` and surfaced in ``--dataset`` help. The ``hf_ptq.py`` default ``--calib_size`` is bumped from ``512`` to ``1024`` so the new ``default`` combo preserves the previous total sample count. +- Add ``DATASET_COMBOS`` to ``modelopt.torch.utils.dataset_utils`` — single ``--dataset`` tokens that fan out to multiple registered datasets; per-entry ``num_samples`` is split evenly across the members. Initial combos: ``default`` (``cnn_dailymail`` + ``nemotron-post-training-dataset-v2``, used by ``hf_ptq.py`` when no ``--dataset`` is provided) and ``nemotron-sft-mix`` (the seven ``nvidia/Nemotron-*`` SFT datasets added in #1498). Combo names are listed by ``get_supported_datasets()`` and surfaced in ``--dataset`` help. ``get_dataset_dataloader`` rejects inputs that mix a combo with one of its member datasets (e.g. ``cnn_dailymail,default``) to avoid double-sampling, and ``get_dataset_samples`` rejects combo names so callers route through the dataloader. ``hf_ptq.py`` default ``--calib_size`` is bumped from ``512`` to ``1024`` so the total calibration sample count under the new ``default`` combo matches the previous two-dataset fallback. +- 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. 0.44 (2026-05-18) ^^^^^^^^^^^^^^^^^ diff --git a/modelopt/torch/utils/dataset_utils.py b/modelopt/torch/utils/dataset_utils.py index 724cd9f51aa..e3b26039bd4 100644 --- a/modelopt/torch/utils/dataset_utils.py +++ b/modelopt/torch/utils/dataset_utils.py @@ -213,6 +213,26 @@ def _join_messages_content(sample: dict) -> str: ], } + +def _validate_dataset_combos() -> None: + """Validate DATASET_COMBOS at import time: fail loud on typos / collisions.""" + overlap = set(DATASET_COMBOS) & set(SUPPORTED_DATASET_CONFIG) + if overlap: + raise ValueError( + f"DATASET_COMBOS keys collide with SUPPORTED_DATASET_CONFIG: {sorted(overlap)}" + ) + for combo_name, members in DATASET_COMBOS.items(): + if not members: + raise ValueError(f"DATASET_COMBOS['{combo_name}'] must contain at least one dataset.") + unknown = [m for m in members if m not in SUPPORTED_DATASET_CONFIG] + if unknown: + raise ValueError( + f"DATASET_COMBOS['{combo_name}'] references unknown datasets: {unknown}" + ) + + +_validate_dataset_combos() + __all__ = [ "create_forward_loop", "download_hf_dataset_as_jsonl", @@ -367,6 +387,13 @@ def get_dataset_samples( Returns: Samples: The list of samples. """ + if dataset_name in DATASET_COMBOS: + raise ValueError( + f"'{dataset_name}' is a DATASET_COMBOS entry, not a single dataset. " + "Use ``get_dataset_dataloader`` to expand combos, or pass one of " + f"its members: {DATASET_COMBOS[dataset_name]}" + ) + # Local JSONL: load via HF's ``json`` builder and route through the same # auto-preprocess path as unregistered HF datasets so chat / prompt / text # columns are handled consistently with a downloaded HF dataset. Never diff --git a/tests/unit/torch/utils/test_dataset_utils.py b/tests/unit/torch/utils/test_dataset_utils.py index 7566088a1ad..db38512e90c 100644 --- a/tests/unit/torch/utils/test_dataset_utils.py +++ b/tests/unit/torch/utils/test_dataset_utils.py @@ -610,6 +610,89 @@ def test_length_mismatch_raises(self, tmp_path, pad_tokenizer): ) +class TestDatasetCombosExpansion: + """Combo names in ``--dataset`` fan out to their registered members. + + The combo branch in ``get_dataset_dataloader`` is exercised by mocking + ``get_dataset_samples`` so we can assert the post-expansion (name, count) + sequence without hitting HF. + """ + + @staticmethod + def _record_calls(monkeypatch): + calls: list[tuple[str, int]] = [] + + def _fake(name, num_sample, **_kwargs): + calls.append((name, num_sample)) + return [f"{name}-{i}" for i in range(num_sample)] + + from modelopt.torch.utils import dataset_utils + + monkeypatch.setattr(dataset_utils, "get_dataset_samples", _fake) + return calls + + def test_combo_expands_evenly(self, monkeypatch, pad_tokenizer): + from modelopt.torch.utils.dataset_utils import DATASET_COMBOS + + calls = self._record_calls(monkeypatch) + get_dataset_dataloader( + dataset_name="default", + tokenizer=pad_tokenizer, + num_samples=8, + batch_size=1, + max_sample_length=16, + ) + members = DATASET_COMBOS["default"] + assert calls == [(members[0], 4), (members[1], 4)] + + def test_combo_remainder_distributed_to_earlier_members(self, monkeypatch, pad_tokenizer): + from modelopt.torch.utils.dataset_utils import DATASET_COMBOS + + calls = self._record_calls(monkeypatch) + get_dataset_dataloader( + dataset_name="nemotron-sft-mix", + tokenizer=pad_tokenizer, + num_samples=10, + batch_size=1, + max_sample_length=16, + ) + members = DATASET_COMBOS["nemotron-sft-mix"] + # 10 / 7 = 1 base, remainder 3 -> first 3 get +1 + expected_counts = [2, 2, 2, 1, 1, 1, 1] + assert calls == list(zip(members, expected_counts)) + + def test_plain_and_combo_compose(self, monkeypatch, pad_tokenizer): + from modelopt.torch.utils.dataset_utils import DATASET_COMBOS + + calls = self._record_calls(monkeypatch) + get_dataset_dataloader( + dataset_name=["cnn_dailymail", "nemotron-sft-mix"], + tokenizer=pad_tokenizer, + num_samples=[3, 7], + batch_size=1, + max_sample_length=16, + ) + members = DATASET_COMBOS["nemotron-sft-mix"] + assert calls == [("cnn_dailymail", 3)] + [(m, 1) for m in members] + + def test_combo_overlapping_with_member_raises(self, monkeypatch, pad_tokenizer): + self._record_calls(monkeypatch) + with pytest.raises(ValueError, match="combo 'default'"): + get_dataset_dataloader( + dataset_name=["cnn_dailymail", "default"], + tokenizer=pad_tokenizer, + num_samples=[2, 4], + batch_size=1, + max_sample_length=16, + ) + + def test_get_dataset_samples_rejects_combo_name(self): + from modelopt.torch.utils.dataset_utils import get_dataset_samples + + with pytest.raises(ValueError, match="DATASET_COMBOS"): + get_dataset_samples("default", num_samples=1) + + # --------------------------------------------------------------------------- # Live HF dataset round-trips. ``hf-internal-testing/dataset_with_data_files`` # is a 10-row x {train,test} fixture maintained by HF for their own CI — tiny From fb3247667fca2e6c7f0cff798f2464cbc805bcf5 Mon Sep 17 00:00:00 2001 From: Chenjie Luo Date: Tue, 19 May 2026 18:00:27 +0000 Subject: [PATCH 5/6] Rename combo nemotron-sft-mix -> nemotron-post-training-v3 The combo's seven member datasets are exactly the ones in NVIDIA's ``nemotron-post-training-v3`` HuggingFace collection (https://huggingface.co/collections/nvidia/nemotron-post-training-v3), so reuse the upstream name to make the relationship explicit. Co-Authored-By: Claude Opus 4.7 (1M context) Signed-off-by: Chenjie Luo --- CHANGELOG.rst | 2 +- modelopt/torch/utils/dataset_utils.py | 2 +- tests/unit/torch/utils/test_dataset_utils.py | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 89beada25b3..0f2e4a95449 100755 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -25,7 +25,7 @@ Changelog - Add ``--cast_mxfp4_to_nvfp4`` flag to ``examples/llm_ptq/hf_ptq.py`` for closed-form, bit-exact MXFP4 → NVFP4 weight conversion. Supports the GPT-OSS family (``openai/gpt-oss-20b``, ``openai/gpt-oss-120b``). See `examples/llm_ptq/README.md `__ for usage. - DeepSeek PTQ (``examples/deepseek/ptq.py``) now defaults to native top-k calibration with post-hoc per-layer peer-max sync of expert ``input_quantizer.amax``; the all-experts path is preserved behind ``--calib_all_experts``. - Add NVFP4 W4A16 weight-only quantization (``w4a16_nvfp4``): FP4 weights with group_size=16, BF16 activations, no calibration forward pass required. Use ``mtq.W4A16_NVFP4_CFG`` or ``--qformat w4a16_nvfp4`` in ``hf_ptq.py``. vLLM deployment support is in progress. -- Add ``DATASET_COMBOS`` to ``modelopt.torch.utils.dataset_utils`` — single ``--dataset`` tokens that fan out to multiple registered datasets; per-entry ``num_samples`` is split evenly across the members. Initial combos: ``default`` (``cnn_dailymail`` + ``nemotron-post-training-dataset-v2``, used by ``hf_ptq.py`` when no ``--dataset`` is provided) and ``nemotron-sft-mix`` (the seven ``nvidia/Nemotron-*`` SFT datasets added in #1498). Combo names are listed by ``get_supported_datasets()`` and surfaced in ``--dataset`` help. ``get_dataset_dataloader`` rejects inputs that mix a combo with one of its member datasets (e.g. ``cnn_dailymail,default``) to avoid double-sampling, and ``get_dataset_samples`` rejects combo names so callers route through the dataloader. ``hf_ptq.py`` default ``--calib_size`` is bumped from ``512`` to ``1024`` so the total calibration sample count under the new ``default`` combo matches the previous two-dataset fallback. +- Add ``DATASET_COMBOS`` to ``modelopt.torch.utils.dataset_utils`` — single ``--dataset`` tokens that fan out to multiple registered datasets; per-entry ``num_samples`` is split evenly across the members. Initial combos: ``default`` (``cnn_dailymail`` + ``nemotron-post-training-dataset-v2``, used by ``hf_ptq.py`` when no ``--dataset`` is provided) and ``nemotron-post-training-v3`` (the seven ``nvidia/Nemotron-*`` SFT datasets added in #1498, mirroring the `nemotron-post-training-v3 collection `_). Combo names are listed by ``get_supported_datasets()`` and surfaced in ``--dataset`` help. ``get_dataset_dataloader`` rejects inputs that mix a combo with one of its member datasets (e.g. ``cnn_dailymail,default``) to avoid double-sampling, and ``get_dataset_samples`` rejects combo names so callers route through the dataloader. ``hf_ptq.py`` default ``--calib_size`` is bumped from ``512`` to ``1024`` so the total calibration sample count under the new ``default`` combo matches the previous two-dataset fallback. - 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. 0.44 (2026-05-18) diff --git a/modelopt/torch/utils/dataset_utils.py b/modelopt/torch/utils/dataset_utils.py index e3b26039bd4..9cf8692c472 100644 --- a/modelopt/torch/utils/dataset_utils.py +++ b/modelopt/torch/utils/dataset_utils.py @@ -202,7 +202,7 @@ def _join_messages_content(sample: dict) -> str: # entries; per-dataset ``num_samples`` is split evenly across the members. DATASET_COMBOS: dict[str, list[str]] = { "default": ["cnn_dailymail", "nemotron-post-training-dataset-v2"], - "nemotron-sft-mix": [ + "nemotron-post-training-v3": [ "nemotron-sft-instruction-following-chat-v2", "nemotron-science-v1", "nemotron-competitive-programming-v1", diff --git a/tests/unit/torch/utils/test_dataset_utils.py b/tests/unit/torch/utils/test_dataset_utils.py index db38512e90c..b54f9e909d1 100644 --- a/tests/unit/torch/utils/test_dataset_utils.py +++ b/tests/unit/torch/utils/test_dataset_utils.py @@ -650,13 +650,13 @@ def test_combo_remainder_distributed_to_earlier_members(self, monkeypatch, pad_t calls = self._record_calls(monkeypatch) get_dataset_dataloader( - dataset_name="nemotron-sft-mix", + dataset_name="nemotron-post-training-v3", tokenizer=pad_tokenizer, num_samples=10, batch_size=1, max_sample_length=16, ) - members = DATASET_COMBOS["nemotron-sft-mix"] + members = DATASET_COMBOS["nemotron-post-training-v3"] # 10 / 7 = 1 base, remainder 3 -> first 3 get +1 expected_counts = [2, 2, 2, 1, 1, 1, 1] assert calls == list(zip(members, expected_counts)) @@ -666,13 +666,13 @@ def test_plain_and_combo_compose(self, monkeypatch, pad_tokenizer): calls = self._record_calls(monkeypatch) get_dataset_dataloader( - dataset_name=["cnn_dailymail", "nemotron-sft-mix"], + dataset_name=["cnn_dailymail", "nemotron-post-training-v3"], tokenizer=pad_tokenizer, num_samples=[3, 7], batch_size=1, max_sample_length=16, ) - members = DATASET_COMBOS["nemotron-sft-mix"] + members = DATASET_COMBOS["nemotron-post-training-v3"] assert calls == [("cnn_dailymail", 3)] + [(m, 1) for m in members] def test_combo_overlapping_with_member_raises(self, monkeypatch, pad_tokenizer): From 6a752cea09526209172c66a4a465bf72782f5aa2 Mon Sep 17 00:00:00 2001 From: Chenjie Luo Date: Tue, 19 May 2026 20:18:10 +0000 Subject: [PATCH 6/6] Rename combo default -> cnn_nemotron_v2_mix MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ``default`` is too generic for a registry key — it doesn't say what the combo contains and clashes with the common English sense ("the default thing"). Rename it to ``cnn_nemotron_v2_mix`` so the combo's contents (cnn_dailymail + nemotron-post-training-dataset-v2) are obvious from the name, matching the descriptive style of the other combo (``nemotron-post-training-v3``). ``hf_ptq.py``'s no-``--dataset`` fallback now uses the new key. Co-Authored-By: Claude Opus 4.7 (1M context) Signed-off-by: Chenjie Luo --- CHANGELOG.rst | 2 +- examples/llm_ptq/hf_ptq.py | 4 ++-- modelopt/torch/utils/dataset_utils.py | 4 ++-- tests/unit/torch/utils/test_dataset_utils.py | 10 +++++----- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 0f2e4a95449..c0c18e797f5 100755 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -25,7 +25,7 @@ Changelog - Add ``--cast_mxfp4_to_nvfp4`` flag to ``examples/llm_ptq/hf_ptq.py`` for closed-form, bit-exact MXFP4 → NVFP4 weight conversion. Supports the GPT-OSS family (``openai/gpt-oss-20b``, ``openai/gpt-oss-120b``). See `examples/llm_ptq/README.md `__ for usage. - DeepSeek PTQ (``examples/deepseek/ptq.py``) now defaults to native top-k calibration with post-hoc per-layer peer-max sync of expert ``input_quantizer.amax``; the all-experts path is preserved behind ``--calib_all_experts``. - Add NVFP4 W4A16 weight-only quantization (``w4a16_nvfp4``): FP4 weights with group_size=16, BF16 activations, no calibration forward pass required. Use ``mtq.W4A16_NVFP4_CFG`` or ``--qformat w4a16_nvfp4`` in ``hf_ptq.py``. vLLM deployment support is in progress. -- Add ``DATASET_COMBOS`` to ``modelopt.torch.utils.dataset_utils`` — single ``--dataset`` tokens that fan out to multiple registered datasets; per-entry ``num_samples`` is split evenly across the members. Initial combos: ``default`` (``cnn_dailymail`` + ``nemotron-post-training-dataset-v2``, used by ``hf_ptq.py`` when no ``--dataset`` is provided) and ``nemotron-post-training-v3`` (the seven ``nvidia/Nemotron-*`` SFT datasets added in #1498, mirroring the `nemotron-post-training-v3 collection `_). Combo names are listed by ``get_supported_datasets()`` and surfaced in ``--dataset`` help. ``get_dataset_dataloader`` rejects inputs that mix a combo with one of its member datasets (e.g. ``cnn_dailymail,default``) to avoid double-sampling, and ``get_dataset_samples`` rejects combo names so callers route through the dataloader. ``hf_ptq.py`` default ``--calib_size`` is bumped from ``512`` to ``1024`` so the total calibration sample count under the new ``default`` combo matches the previous two-dataset fallback. +- Add ``DATASET_COMBOS`` to ``modelopt.torch.utils.dataset_utils`` — single ``--dataset`` tokens that fan out to multiple registered datasets; per-entry ``num_samples`` is split evenly across the members. Initial combos: ``cnn_nemotron_v2_mix`` (``cnn_dailymail`` + ``nemotron-post-training-dataset-v2``, used by ``hf_ptq.py`` when no ``--dataset`` is provided) and ``nemotron-post-training-v3`` (the seven ``nvidia/Nemotron-*`` SFT datasets added in #1498, mirroring the `nemotron-post-training-v3 collection `_). Combo names are listed by ``get_supported_datasets()`` and surfaced in ``--dataset`` help. ``get_dataset_dataloader`` rejects inputs that mix a combo with one of its member datasets (e.g. ``cnn_dailymail,cnn_nemotron_v2_mix``) to avoid double-sampling, and ``get_dataset_samples`` rejects combo names so callers route through the dataloader. ``hf_ptq.py`` default ``--calib_size`` is bumped from ``512`` to ``1024`` so the total calibration sample count under the new default combo matches the previous two-dataset fallback. - 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. 0.44 (2026-05-18) diff --git a/examples/llm_ptq/hf_ptq.py b/examples/llm_ptq/hf_ptq.py index c92219a91ce..0c17558d2e3 100755 --- a/examples/llm_ptq/hf_ptq.py +++ b/examples/llm_ptq/hf_ptq.py @@ -531,9 +531,9 @@ def load_model(args: argparse.Namespace): language_model = full_model else: if args.dataset is None: - args.dataset = ["default"] + args.dataset = ["cnn_nemotron_v2_mix"] warnings.warn( - "No dataset specified. Defaulting to the 'default' combo " + "No dataset specified. Defaulting to the 'cnn_nemotron_v2_mix' combo " "(cnn_dailymail + nemotron-post-training-dataset-v2)." ) # Adjust calib_size to match dataset length by extending or truncating as needed diff --git a/modelopt/torch/utils/dataset_utils.py b/modelopt/torch/utils/dataset_utils.py index 9cf8692c472..80ed8f9abdd 100644 --- a/modelopt/torch/utils/dataset_utils.py +++ b/modelopt/torch/utils/dataset_utils.py @@ -201,7 +201,7 @@ def _join_messages_content(sample: dict) -> str: # Useful when callers want a single ``--dataset`` token that fans out to several # entries; per-dataset ``num_samples`` is split evenly across the members. DATASET_COMBOS: dict[str, list[str]] = { - "default": ["cnn_dailymail", "nemotron-post-training-dataset-v2"], + "cnn_nemotron_v2_mix": ["cnn_dailymail", "nemotron-post-training-dataset-v2"], "nemotron-post-training-v3": [ "nemotron-sft-instruction-following-chat-v2", "nemotron-science-v1", @@ -606,7 +606,7 @@ def get_dataset_dataloader( ) # Reject inputs that include both a combo and one of its member datasets - # (e.g. ``["cnn_dailymail", "default"]``), since the combo would sample the + # (e.g. ``["cnn_dailymail", "cnn_nemotron_v2_mix"]``), since the combo would sample the # plain entry a second time with a smaller per-member quota. plain_inputs = {n for n in dataset_name if n not in DATASET_COMBOS} for ds_name in dataset_name: diff --git a/tests/unit/torch/utils/test_dataset_utils.py b/tests/unit/torch/utils/test_dataset_utils.py index b54f9e909d1..812d2cd9c3b 100644 --- a/tests/unit/torch/utils/test_dataset_utils.py +++ b/tests/unit/torch/utils/test_dataset_utils.py @@ -636,13 +636,13 @@ def test_combo_expands_evenly(self, monkeypatch, pad_tokenizer): calls = self._record_calls(monkeypatch) get_dataset_dataloader( - dataset_name="default", + dataset_name="cnn_nemotron_v2_mix", tokenizer=pad_tokenizer, num_samples=8, batch_size=1, max_sample_length=16, ) - members = DATASET_COMBOS["default"] + members = DATASET_COMBOS["cnn_nemotron_v2_mix"] assert calls == [(members[0], 4), (members[1], 4)] def test_combo_remainder_distributed_to_earlier_members(self, monkeypatch, pad_tokenizer): @@ -677,9 +677,9 @@ def test_plain_and_combo_compose(self, monkeypatch, pad_tokenizer): def test_combo_overlapping_with_member_raises(self, monkeypatch, pad_tokenizer): self._record_calls(monkeypatch) - with pytest.raises(ValueError, match="combo 'default'"): + with pytest.raises(ValueError, match="combo 'cnn_nemotron_v2_mix'"): get_dataset_dataloader( - dataset_name=["cnn_dailymail", "default"], + dataset_name=["cnn_dailymail", "cnn_nemotron_v2_mix"], tokenizer=pad_tokenizer, num_samples=[2, 4], batch_size=1, @@ -690,7 +690,7 @@ def test_get_dataset_samples_rejects_combo_name(self): from modelopt.torch.utils.dataset_utils import get_dataset_samples with pytest.raises(ValueError, match="DATASET_COMBOS"): - get_dataset_samples("default", num_samples=1) + get_dataset_samples("cnn_nemotron_v2_mix", num_samples=1) # ---------------------------------------------------------------------------