Skip to content

Commit 8f43506

Browse files
authored
[data] Add openmathinstruct and gsm8k datasets (#2264)
Signed-off-by: Chen Cui <chcui@nvidia.com>
1 parent c6b5368 commit 8f43506

12 files changed

Lines changed: 1442 additions & 49 deletions

File tree

‎scripts/training/run_recipe.py‎

Lines changed: 112 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -18,42 +18,72 @@
1818
1919
This script works with any model family that uses GPT-style training
2020
(Llama, Gemma, Qwen, GPT, etc.). It dynamically loads recipes and supports
21-
CLI overrides.
21+
CLI overrides. The --dataset flag selects the dataset type and automatically
22+
infers pretrain vs finetune mode.
2223
2324
Usage:
24-
Pretrain (single-GPU):
25-
uv run python run_recipe.py \
26-
--recipe llama32_1b_pretrain_config
27-
28-
Pretrain (multi-GPU):
29-
uv run torchrun --nproc_per_node=8 run_recipe.py \
30-
--recipe llama32_1b_pretrain_config
31-
32-
SFT (full finetuning):
33-
uv run torchrun --nproc_per_node=8 run_recipe.py \
34-
--recipe llama32_1b_sft_config
35-
36-
PEFT (LoRA/DoRA):
37-
uv run torchrun --nproc_per_node=8 run_recipe.py \
38-
--recipe llama32_1b_peft_config
39-
40-
With CLI overrides:
41-
uv run torchrun --nproc_per_node=8 run_recipe.py \
42-
--recipe llama32_1b_pretrain_config \
43-
train.train_iters=5000 \
25+
Pretrain (mock data):
26+
uv run torchrun --nproc_per_node=8 run_recipe.py \\
27+
--recipe llama32_1b_pretrain_config \\
28+
--dataset llm-pretrain-mock
29+
30+
Pretrain (real data):
31+
uv run torchrun --nproc_per_node=8 run_recipe.py \\
32+
--recipe llama32_1b_pretrain_config \\
33+
--dataset llm-pretrain \\
34+
'dataset.blend=[[/data/my_dataset_text_document],null]'
35+
36+
Finetune (SQuAD, default):
37+
uv run torchrun --nproc_per_node=8 run_recipe.py \\
38+
--recipe llama32_1b_sft_config \\
39+
--dataset llm-finetune
40+
41+
Finetune (GSM8K):
42+
uv run torchrun --nproc_per_node=8 run_recipe.py \\
43+
--recipe llama32_1b_sft_config \\
44+
--dataset llm-finetune \\
45+
dataset.dataset_name=gsm8k
46+
47+
Finetune (user-supplied JSONL):
48+
uv run torchrun --nproc_per_node=8 run_recipe.py \\
49+
--recipe llama32_1b_sft_config \\
50+
--dataset llm-finetune-preloaded \\
51+
dataset.dataset_root=/data/my_finetune_data
52+
53+
VLM with HF dataset:
54+
uv run torchrun --nproc_per_node=8 run_recipe.py \\
55+
--recipe qwen3_vl_8b_peft_config \\
56+
--dataset vlm-hf \\
57+
--step_func qwen3_vl_step \\
58+
dataset.maker_name=cord_v2 \\
59+
dataset.hf_processor_path=Qwen/Qwen3-VL-8B-Instruct \\
60+
checkpoint.pretrained_checkpoint=/path/to/checkpoint
61+
62+
VLM with Energon dataset:
63+
uv run torchrun --nproc_per_node=8 run_recipe.py \\
64+
--recipe qwen3_vl_8b_peft_energon_config \\
65+
--dataset vlm-energon \\
66+
--step_func qwen3_vl_step \\
67+
dataset.path=/data/energon \\
68+
checkpoint.pretrained_checkpoint=/path/to/checkpoint
69+
70+
VLM with preloaded JSON:
71+
uv run torchrun --nproc_per_node=8 run_recipe.py \\
72+
--recipe qwen3_vl_8b_peft_config \\
73+
--dataset vlm-preloaded \\
74+
--step_func qwen3_vl_step \\
75+
dataset.train_data_path=/data/vlm_train.json \\
76+
dataset.image_folder=/data/vlm_images \\
77+
dataset.hf_processor_path=Qwen/Qwen3-VL-8B-Instruct \\
78+
checkpoint.pretrained_checkpoint=/path/to/checkpoint
79+
80+
With CLI overrides (Hydra-style, works for any config field):
81+
uv run torchrun --nproc_per_node=8 run_recipe.py \\
82+
--recipe llama32_1b_pretrain_config \\
83+
--dataset llm-pretrain-mock \\
84+
train.train_iters=5000 \\
4485
optimizer.lr=0.0003
4586
46-
With VLM step function:
47-
uv run torchrun --nproc_per_node=8 run_recipe.py \
48-
--recipe qwen25_vl_finetune_config \
49-
--step_func vlm_step
50-
51-
With packed sequences and custom sequence length:
52-
uv run torchrun --nproc_per_node=8 run_recipe.py \
53-
--recipe llama32_1b_pretrain_config \
54-
--packed_sequence \
55-
--seq_length 2048
56-
5787
Recipe Arguments:
5888
Generic scripts call recipes with no arguments: recipe().
5989
@@ -67,6 +97,11 @@
6797

6898
import megatron.bridge.recipes as recipes
6999
from megatron.bridge.models.qwen_vl.qwen3_vl_step import forward_step as qwen3_vl_forward_step
100+
from megatron.bridge.recipes.utils.dataset_utils import (
101+
DATASET_TYPES,
102+
apply_dataset_override,
103+
infer_mode_from_dataset,
104+
)
70105
from megatron.bridge.training.config import ConfigContainer
71106
from megatron.bridge.training.finetune import finetune
72107
from megatron.bridge.training.gpt_step import forward_step as gpt_forward_step
@@ -83,16 +118,16 @@
83118
"llava_step": llava_forward_step,
84119
}
85120

86-
TRAIN_MODES = {
121+
TRAIN_FUNCTIONS = {
87122
"pretrain": pretrain,
88123
"finetune": finetune,
89124
}
90125

91-
# Error message constants
92126
ERR_UNKNOWN_STEP = "Unknown step type: {step_type}. Choose from: {choices}"
93127
ERR_INFER_MODE_FAILED = (
94-
"Unable to infer training mode from recipe name. "
95-
"Please include 'pretrain', 'sft', 'peft', or 'finetune' in the recipe name or pass --mode explicitly."
128+
"Unable to infer training mode. "
129+
"Pass --dataset to specify the dataset type, or include 'pretrain' or 'finetune' "
130+
"(or 'sft'/'peft') in the recipe name."
96131
)
97132

98133

@@ -102,19 +137,30 @@ def parse_args() -> tuple[argparse.Namespace, list[str]]:
102137
description="Generic training script for GPT-based models",
103138
formatter_class=argparse.RawTextHelpFormatter,
104139
)
105-
parser.add_argument(
106-
"--mode",
107-
type=str,
108-
default=None,
109-
choices=sorted(TRAIN_MODES.keys()),
110-
help="Training mode (optional). If omitted, inferred from recipe name.",
111-
)
112140
parser.add_argument(
113141
"--recipe",
114142
type=str,
115143
required=True,
116144
help="Recipe function name (e.g., llama32_1b_pretrain_config, gemma3_1b_sft_config, gemma3_1b_peft_config)",
117145
)
146+
parser.add_argument(
147+
"--dataset",
148+
type=str,
149+
default=None,
150+
choices=DATASET_TYPES,
151+
help=(
152+
"Dataset type. Training mode (pretrain/finetune) is inferred from this.\n"
153+
"LLM datasets:\n"
154+
" llm-pretrain GPT pretrain data (set dataset.blend=<path>)\n"
155+
" llm-pretrain-mock Mock pretrain data for testing\n"
156+
" llm-finetune HF finetune dataset (set dataset.dataset_name=squad|gsm8k|openmathinstruct2)\n"
157+
" llm-finetune-preloaded User-supplied JSONL (set dataset.dataset_root=<path>)\n"
158+
"VLM datasets:\n"
159+
" vlm-energon Energon multimodal (set dataset.path=<path>)\n"
160+
" vlm-hf HF VLM dataset (set dataset.maker_name=<name>)\n"
161+
" vlm-preloaded User-supplied VLM JSON (set dataset.train_data_path=<path>)"
162+
),
163+
)
118164
parser.add_argument(
119165
"--step_func",
120166
type=str,
@@ -227,7 +273,7 @@ def load_forward_step(step_type: str) -> Callable:
227273

228274

229275
def infer_train_mode(recipe_name: str) -> str:
230-
"""Infer training mode from the recipe name."""
276+
"""Infer training mode from the recipe name (fallback when --dataset is not passed)."""
231277
lowered = recipe_name.lower()
232278
has_pretrain = "pretrain" in lowered
233279
has_finetune = "finetune" in lowered or "sft" in lowered or "peft" in lowered
@@ -248,15 +294,35 @@ def main() -> None:
248294
args.hf_path,
249295
)
250296

297+
if args.dataset is not None:
298+
mode = infer_mode_from_dataset(args.dataset)
299+
config = apply_dataset_override(
300+
config,
301+
dataset_type=args.dataset,
302+
packed_sequence=args.packed_sequence,
303+
seq_length=args.seq_length,
304+
cli_overrides=cli_overrides,
305+
)
306+
else:
307+
mode = infer_train_mode(args.recipe)
308+
251309
config = process_config_with_overrides(
252310
config,
253311
cli_overrides=cli_overrides or None,
254312
)
255313

256-
mode = args.mode or infer_train_mode(args.recipe)
314+
# Ensure dataset.seq_length and model.seq_length stay in sync after CLI overrides
315+
if (
316+
hasattr(config, "model")
317+
and config.model is not None
318+
and hasattr(config, "dataset")
319+
and config.dataset is not None
320+
):
321+
if hasattr(config.dataset, "seq_length") and config.model.seq_length != config.dataset.seq_length:
322+
config.model.seq_length = config.dataset.seq_length
257323

258324
forward_step = load_forward_step(args.step_func)
259-
train_func = TRAIN_MODES[mode]
325+
train_func = TRAIN_FUNCTIONS[mode]
260326
train_func(config=config, forward_step_func=forward_step)
261327

262328

‎src/megatron/bridge/data/builders/hf_dataset.py‎

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -358,5 +358,18 @@ def _load_dataset(self) -> DatasetDict:
358358
)
359359
else:
360360
raise ValueError("Expected `dataset_name` to be str, got " + str(type(self.dataset_name)))
361+
# If a specific split was requested, load_dataset returns a Dataset, not DatasetDict.
362+
# We need to wrap it in a DatasetDict for downstream processing.
363+
if isinstance(dataset, Dataset):
364+
normalized_split = (self.split or "train").split("[", 1)[0]
365+
if normalized_split.startswith("train"):
366+
split_key = "train"
367+
elif normalized_split.startswith(("validation", "valid", "val", "eval")):
368+
split_key = "validation"
369+
elif normalized_split.startswith("test"):
370+
split_key = "test"
371+
else:
372+
split_key = "train"
373+
dataset = DatasetDict({split_key: dataset})
361374

362375
return dataset

‎src/megatron/bridge/data/hf_processors/__init__.py‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,13 @@
1919
and are designed to work with the HFDatasetConfig and HFDatasetBuilder classes.
2020
"""
2121

22+
from .gsm8k import process_gsm8k_example
23+
from .openmathinstruct2 import process_openmathinstruct2_example
2224
from .squad import process_squad_example
2325

2426

2527
__all__ = [
28+
"process_gsm8k_example",
29+
"process_openmathinstruct2_example",
2630
"process_squad_example",
2731
]
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
# Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
"""Processing functions for GSM8K (Grade School Math 8K) dataset.
16+
17+
Dataset: https://huggingface.co/datasets/openai/gsm8k
18+
19+
GSM8K contains 8.5K grade school math word problems. Each example has a
20+
``question`` and an ``answer`` field where the answer contains chain-of-thought
21+
reasoning followed by ``####`` and the final numerical answer.
22+
"""
23+
24+
from typing import Any, Optional
25+
26+
from megatron.bridge.data.builders.hf_dataset import ProcessExampleOutput
27+
from megatron.bridge.training.tokenizers.tokenizer import MegatronTokenizer
28+
29+
30+
def _extract_final_answer(answer: str) -> str:
31+
"""Extract the final numerical answer after the ``####`` delimiter."""
32+
if "####" in answer:
33+
return answer.split("####")[-1].strip()
34+
return answer.strip()
35+
36+
37+
def process_gsm8k_example(
38+
example: dict[str, Any], _tokenizer: Optional[MegatronTokenizer] = None
39+
) -> ProcessExampleOutput:
40+
"""Process a single GSM8K example into the required format.
41+
42+
Transforms a raw GSM8K dataset example into the standard format expected by
43+
the HFDatasetBuilder for fine-tuning.
44+
45+
Args:
46+
example: Raw GSM8K example containing 'question' and 'answer'
47+
tokenizer: Optional tokenizer (not used in this processor)
48+
49+
Returns:
50+
ProcessExampleOutput with formatted input/output and original answers
51+
52+
Example:
53+
>>> example = {
54+
... "question": "Janet has 3 apples. She buys 2 more. How many does she have?",
55+
... "answer": "Janet starts with 3 apples and buys 2 more. 3 + 2 = <<3+2=5>>5.\\n#### 5",
56+
... }
57+
>>> result = process_gsm8k_example(example)
58+
>>> print(result["input"])
59+
Question: Janet has 3 apples. She buys 2 more. How many does she have? Answer:
60+
"""
61+
_input = f"Question: {example['question']} Answer:"
62+
_output = example["answer"]
63+
final_answer = _extract_final_answer(example["answer"])
64+
65+
return ProcessExampleOutput(input=_input, output=_output, original_answers=[final_answer])
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
# Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
"""Processing functions for OpenMathInstruct-2 dataset.
16+
17+
Dataset: https://huggingface.co/datasets/nvidia/OpenMathInstruct-2
18+
19+
OpenMathInstruct-2 contains math problems with generated solutions. Each example
20+
has ``problem``, ``generated_solution``, and ``expected_answer`` fields.
21+
"""
22+
23+
from typing import Any, Optional
24+
25+
from megatron.bridge.data.builders.hf_dataset import ProcessExampleOutput
26+
from megatron.bridge.training.tokenizers.tokenizer import MegatronTokenizer
27+
28+
29+
def process_openmathinstruct2_example(
30+
example: dict[str, Any], _tokenizer: Optional[MegatronTokenizer] = None
31+
) -> ProcessExampleOutput:
32+
"""Process a single OpenMathInstruct-2 example into the required format.
33+
34+
Transforms a raw OpenMathInstruct-2 dataset example into the standard format
35+
expected by the HFDatasetBuilder for fine-tuning.
36+
37+
Args:
38+
example: Raw example containing 'problem', 'generated_solution', and 'expected_answer'
39+
tokenizer: Optional tokenizer (not used in this processor)
40+
41+
Returns:
42+
ProcessExampleOutput with formatted input/output and original answers
43+
44+
Example:
45+
>>> example = {
46+
... "problem": "What is 2 + 3?",
47+
... "generated_solution": "We add 2 and 3 to get 5.",
48+
... "expected_answer": "5",
49+
... }
50+
>>> result = process_openmathinstruct2_example(example)
51+
>>> print(result["input"])
52+
Problem: What is 2 + 3? Solution:
53+
"""
54+
_input = f"Problem: {example['problem']} Solution:"
55+
_output = example["generated_solution"]
56+
expected_answer = example["expected_answer"]
57+
58+
return ProcessExampleOutput(input=_input, output=_output, original_answers=[expected_answer])

0 commit comments

Comments
 (0)