Skip to content

Commit a80ab57

Browse files
committed
Merge remote-tracking branch 'origin/main' into codex/pr5479-hybrid-model
Signed-off-by: yaoyu-33 <yaoyu.094@gmail.com>
2 parents e6b9dc2 + 74749b4 commit a80ab57

10 files changed

Lines changed: 327 additions & 28 deletions

File tree

docs/bridge-rl-integration.md

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -76,24 +76,46 @@ Translate your RL framework config into Megatron Bridge's `ConfigContainer` for
7676

7777
```python
7878
import torch
79+
from megatron.bridge import AutoBridge
7980
from megatron.bridge.training.config import (
8081
ConfigContainer,
8182
TrainingConfig,
8283
OptimizerConfig,
8384
SchedulerConfig,
8485
DistributedDataParallelConfig,
8586
CheckpointConfig,
87+
LoggerConfig,
8688
TokenizerConfig,
8789
)
8890
from nemo_rl.models.policy import PolicyConfig # or your own policy cfg type
8991

9092
# Example: map your RL config to Megatron config
9193
def build_megatron_config(rl_cfg: PolicyConfig, pretrained_ckpt_dir: str) -> ConfigContainer:
92-
model_cfg = rl_cfg["megatron_cfg"].copy()
94+
bridge = AutoBridge.from_hf_pretrained(rl_cfg["model_name"])
95+
model_cfg = bridge.to_megatron_provider(load_weights=False)
96+
97+
# Keep framework-owned optimizer/scheduler/DDP mappings out of the model
98+
# provider. Apply only provider fields, with Bridge validating each name.
99+
provider_fields = (
100+
"tensor_model_parallel_size",
101+
"pipeline_model_parallel_size",
102+
"context_parallel_size",
103+
"expert_model_parallel_size",
104+
"expert_tensor_parallel_size",
105+
"sequence_parallel",
106+
"recompute_granularity",
107+
"recompute_method",
108+
"recompute_num_layers",
109+
)
110+
model_overrides = {name: rl_cfg["megatron_cfg"][name] for name in provider_fields if name in rl_cfg["megatron_cfg"]}
111+
93112
# Precision
94-
dtype = rl_cfg["precision"]
95-
model_cfg["bf16"] = dtype == "bfloat16"
96-
model_cfg["fp16"] = dtype == "float16"
113+
dtype = {
114+
"float32": torch.float32,
115+
"bfloat16": torch.bfloat16,
116+
"float16": torch.float16,
117+
}[rl_cfg["precision"]]
118+
model_cfg.apply_overrides_and_finalize(dtype=dtype, overrides=model_overrides)
97119

98120
checkpoint = CheckpointConfig(
99121
save_interval=100,
@@ -130,17 +152,19 @@ def build_megatron_config(rl_cfg: PolicyConfig, pretrained_ckpt_dir: str) -> Con
130152
tokenizer_model=rl_cfg["model_name"],
131153
)
132154

133-
return ConfigContainer(
155+
cfg = ConfigContainer(
134156
model=model_cfg,
135157
checkpoint=checkpoint,
136-
logger=None,
158+
logger=LoggerConfig(),
137159
train=train,
138160
optimizer=opt,
139161
ddp=ddp,
140162
scheduler=sch,
141163
dataset=None,
142164
tokenizer=tokenizer,
143165
)
166+
cfg.validate()
167+
return cfg
144168
```
145169

146170
Initialize Megatron-Core using a helper similar to `setup_megatron_model` from NeMo-RL:

docs/fern/versions/0.4.2/pages/bridge-rl-integration.mdx

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -72,24 +72,46 @@ Translate your RL framework config into Megatron Bridge's `ConfigContainer` for
7272

7373
```python
7474
import torch
75+
from megatron.bridge import AutoBridge
7576
from megatron.bridge.training.config import (
7677
ConfigContainer,
7778
TrainingConfig,
7879
OptimizerConfig,
7980
SchedulerConfig,
8081
DistributedDataParallelConfig,
8182
CheckpointConfig,
83+
LoggerConfig,
8284
TokenizerConfig,
8385
)
8486
from nemo_rl.models.policy import PolicyConfig # or your own policy cfg type
8587

8688
# Example: map your RL config to Megatron config
8789
def build_megatron_config(rl_cfg: PolicyConfig, pretrained_ckpt_dir: str) -> ConfigContainer:
88-
model_cfg = rl_cfg["megatron_cfg"].copy()
90+
bridge = AutoBridge.from_hf_pretrained(rl_cfg["model_name"])
91+
model_cfg = bridge.to_megatron_provider(load_weights=False)
92+
93+
# Keep framework-owned optimizer/scheduler/DDP mappings out of the model
94+
# provider. Apply only provider fields, with Bridge validating each name.
95+
provider_fields = (
96+
"tensor_model_parallel_size",
97+
"pipeline_model_parallel_size",
98+
"context_parallel_size",
99+
"expert_model_parallel_size",
100+
"expert_tensor_parallel_size",
101+
"sequence_parallel",
102+
"recompute_granularity",
103+
"recompute_method",
104+
"recompute_num_layers",
105+
)
106+
model_overrides = {name: rl_cfg["megatron_cfg"][name] for name in provider_fields if name in rl_cfg["megatron_cfg"]}
107+
89108
# Precision
90-
dtype = rl_cfg["precision"]
91-
model_cfg["bf16"] = dtype == "bfloat16"
92-
model_cfg["fp16"] = dtype == "float16"
109+
dtype = {
110+
"float32": torch.float32,
111+
"bfloat16": torch.bfloat16,
112+
"float16": torch.float16,
113+
}[rl_cfg["precision"]]
114+
model_cfg.apply_overrides_and_finalize(dtype=dtype, overrides=model_overrides)
93115

94116
checkpoint = CheckpointConfig(
95117
save_interval=100,
@@ -126,17 +148,19 @@ def build_megatron_config(rl_cfg: PolicyConfig, pretrained_ckpt_dir: str) -> Con
126148
tokenizer_model=rl_cfg["model_name"],
127149
)
128150

129-
return ConfigContainer(
151+
cfg = ConfigContainer(
130152
model=model_cfg,
131153
checkpoint=checkpoint,
132-
logger=None,
154+
logger=LoggerConfig(),
133155
train=train,
134156
optimizer=opt,
135157
ddp=ddp,
136158
scheduler=sch,
137159
dataset=None,
138160
tokenizer=tokenizer,
139161
)
162+
cfg.validate()
163+
return cfg
140164
```
141165

142166
Initialize Megatron-Core using a helper similar to `setup_megatron_model` from NeMo-RL:

docs/fern/versions/nightly/pages/bridge-rl-integration.mdx

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -72,24 +72,46 @@ Translate your RL framework config into Megatron Bridge's `ConfigContainer` for
7272

7373
```python
7474
import torch
75+
from megatron.bridge import AutoBridge
7576
from megatron.bridge.training.config import (
7677
ConfigContainer,
7778
TrainingConfig,
7879
OptimizerConfig,
7980
SchedulerConfig,
8081
DistributedDataParallelConfig,
8182
CheckpointConfig,
83+
LoggerConfig,
8284
TokenizerConfig,
8385
)
8486
from nemo_rl.models.policy import PolicyConfig # or your own policy cfg type
8587

8688
# Example: map your RL config to Megatron config
8789
def build_megatron_config(rl_cfg: PolicyConfig, pretrained_ckpt_dir: str) -> ConfigContainer:
88-
model_cfg = rl_cfg["megatron_cfg"].copy()
90+
bridge = AutoBridge.from_hf_pretrained(rl_cfg["model_name"])
91+
model_cfg = bridge.to_megatron_provider(load_weights=False)
92+
93+
# Keep framework-owned optimizer/scheduler/DDP mappings out of the model
94+
# provider. Apply only provider fields, with Bridge validating each name.
95+
provider_fields = (
96+
"tensor_model_parallel_size",
97+
"pipeline_model_parallel_size",
98+
"context_parallel_size",
99+
"expert_model_parallel_size",
100+
"expert_tensor_parallel_size",
101+
"sequence_parallel",
102+
"recompute_granularity",
103+
"recompute_method",
104+
"recompute_num_layers",
105+
)
106+
model_overrides = {name: rl_cfg["megatron_cfg"][name] for name in provider_fields if name in rl_cfg["megatron_cfg"]}
107+
89108
# Precision
90-
dtype = rl_cfg["precision"]
91-
model_cfg["bf16"] = dtype == "bfloat16"
92-
model_cfg["fp16"] = dtype == "float16"
109+
dtype = {
110+
"float32": torch.float32,
111+
"bfloat16": torch.bfloat16,
112+
"float16": torch.float16,
113+
}[rl_cfg["precision"]]
114+
model_cfg.apply_overrides_and_finalize(dtype=dtype, overrides=model_overrides)
93115

94116
checkpoint = CheckpointConfig(
95117
save_interval=100,
@@ -126,17 +148,19 @@ def build_megatron_config(rl_cfg: PolicyConfig, pretrained_ckpt_dir: str) -> Con
126148
tokenizer_model=rl_cfg["model_name"],
127149
)
128150

129-
return ConfigContainer(
151+
cfg = ConfigContainer(
130152
model=model_cfg,
131153
checkpoint=checkpoint,
132-
logger=None,
154+
logger=LoggerConfig(),
133155
train=train,
134156
optimizer=opt,
135157
ddp=ddp,
136158
scheduler=sch,
137159
dataset=None,
138160
tokenizer=tokenizer,
139161
)
162+
cfg.validate()
163+
return cfg
140164
```
141165

142166
Initialize Megatron-Core using a helper similar to `setup_megatron_model` from NeMo-RL:

examples/model_verification_cards/nemotron-3-super-120b-a12b/card.yaml

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,13 @@
44
title: nemotron_3_super_120b_a12b
55
summary: >
66
Performance scope: pretrain_performance.H100 and pretrain_performance.GB200
7-
track tuned canonical 64-GPU performance recipes. Timing and throughput from
7+
and pretrain_performance.GB300 track tuned canonical 64-GPU performance
8+
recipes, with NVFP4 on GB300. Timing and throughput from
89
functional H100 and GB200 training are support-verification sanity checks
910
rather than optimized performance results. Hardware evidence is scoped
1011
strictly by accelerator. Nemotron 3 Super 120B-A12B verification covers
1112
conversion, inference, bounded real-data training, checkpoint resume, and
12-
canonical H100 and GB200 benchmarks.
13+
canonical H100, GB200, and GB300 benchmarks.
1314
verification_index:
1415
model_level:
1516
verified:
@@ -30,6 +31,7 @@ verification_index:
3031
performance:
3132
H100: verified
3233
GB200: verified
34+
GB300: verified
3335
model:
3436
hf_id: nvidia/NVIDIA-Nemotron-3-Super-120B-A12B-BF16
3537
hf_revision: d51eab0d1f979ebc26b546e634a04f450d99158e # pragma: allowlist secret
@@ -702,4 +704,30 @@ items:
702704
11000 ms and at least 500 TFLOP/s/GPU, and the resolved configuration
703705
is persisted. Mock data and forced routing make the loss a finiteness
704706
check rather than convergence evidence. These numbers are GB200-only
705-
evidence and make no GB300 performance claim.
707+
evidence.
708+
709+
GB300:
710+
status: verified
711+
precision: nvfp4
712+
bridge_commit: 0480586879f2513958fd8634fc529693ae13e536 # pragma: allowlist secret
713+
command: >
714+
./scripts/training/train.sh --wait --nodes 16 --gpus-per-node 4
715+
--recipe nemotron_3_super_pretrain_64gpu_gb300_nvfp4_config
716+
--mode pretrain --max_steps 50 --seq_length 8192
717+
logger.save_config_filepath=work/model-verification/nemotron-3-super-120b-a12b/gb300-performance/ConfigContainer.yaml
718+
last_verified: 2026-08-18
719+
metrics:
720+
initial_loss: 12.17656
721+
final_loss: 0.01398012
722+
last_10_steps_step_time_ms_avg: 6357.940
723+
last_10_steps_model_tflops_per_gpu_avg: 873.330
724+
last_10_steps_tokens_per_second_per_gpu_avg: 10307.741
725+
expected_result: >
726+
On exactly 64 GB300s, the canonical NVFP4 mock-data recipe completes
727+
exactly 50 optimizer steps at TP1/PP1/CP1/EP64/ETP1, GBS/MBS 512/1,
728+
and sequence length 8192. All 50 keyed rows have finite loss with zero
729+
skipped or NaN iterations. Loss moves from 12.17656 to 0.01398012; the
730+
final ten steps average 6357.940 ms, 873.330 TFLOP/s/GPU, and
731+
10307.741 tokens/s/GPU. The resolved configuration persists. Mock data
732+
and forced routing make the loss a finiteness check rather than
733+
convergence evidence.

src/megatron/bridge/data/sft_processing.py

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -142,18 +142,21 @@ class TokenizedPromptCompletion:
142142
_CONVERSATION_KEYS = ("messages", "conversation", "conversations")
143143
_CANONICAL_PROMPT_KEY = "prompt"
144144
_CANONICAL_COMPLETION_KEY = "completion"
145+
_PLURAL_MEDIA_KEYS = (
146+
"images",
147+
"image_paths",
148+
"videos",
149+
"video_paths",
150+
"audio_paths",
151+
)
145152
_MEDIA_KEYS = (
146153
"image",
147-
"images",
148154
"image_path",
149-
"image_paths",
150155
"video",
151-
"videos",
152156
"video_path",
153-
"video_paths",
154157
"audio",
155158
"audio_path",
156-
"audio_paths",
159+
*_PLURAL_MEDIA_KEYS,
157160
)
158161

159162

@@ -275,7 +278,10 @@ def is_text_only_prompt_completion_example(
275278
preprocessing: PromptCompletionSFTPreprocessingConfig,
276279
) -> bool:
277280
"""Return whether a row is a text-only prompt-completion example."""
278-
if any(example.get(key) is not None for key in _MEDIA_KEYS):
281+
for key in _MEDIA_KEYS:
282+
value = example.get(key)
283+
if value is None or (key in _PLURAL_MEDIA_KEYS and isinstance(value, list) and not value):
284+
continue
279285
return False
280286
try:
281287
normalize_sft_example(example, preprocessing)

src/megatron/bridge/models/conversion/param_mapping.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -815,7 +815,7 @@ def gather_from_ep_ranks(
815815
else:
816816
weights_dict[param_name] = gathered_weights[i].unsqueeze(0)
817817
for param_name in weights_dict:
818-
weights_dict[param_name] = weights_dict[param_name].squeeze()
818+
weights_dict[param_name] = weights_dict[param_name].squeeze(0)
819819
return weights_dict
820820

821821
def gather_from_ep_ranks_scale(

tests/unit_tests/data/builders/test_direct_hf_sft.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import importlib
44

55
import pytest
6+
from datasets import Dataset
67
from megatron.training.config.instantiate_utils import instantiate
78

89
from megatron.bridge.data.base import DatasetBuildContext
@@ -576,6 +577,29 @@ def test_builder_uses_prompt_completion_without_chat_template(monkeypatch):
576577
assert batch["loss_mask"].sum().item() == 2
577578

578579

580+
def test_builder_treats_empty_optional_media_as_text_only(monkeypatch):
581+
row = Dataset.from_list([{"prompt": "Q", "completion": "A", "images": []}])[0]
582+
monkeypatch.setattr(builder_module, "load_and_adapt_hf_dataset", lambda source: [row])
583+
config = DirectHFSFTDatasetConfig(
584+
seq_length=16,
585+
source=HFDatasetSourceConfig(path_or_dataset="org/paired"),
586+
preprocessing=PromptCompletionSFTPreprocessingConfig(),
587+
pad_to_multiple_of=1,
588+
do_validation=False,
589+
do_test=False,
590+
)
591+
592+
train, _, _ = DirectHFSFTDatasetBuilder(config).build(DatasetBuildContext(1, 0, 0, tokenizer=_Tokenizer()))
593+
594+
assert train is not None
595+
assert train[0]["images"] == []
596+
with pytest.raises(ValueError, match="supports text-only examples"):
597+
select_direct_hf_sft_collate(
598+
[{"prompt": "Q", "completion": "A", "images": [object()]}],
599+
config.preprocessing,
600+
)
601+
602+
579603
def test_direct_hf_sft_config_resolves_canonical_builder(monkeypatch):
580604
from megatron.bridge.data import utils as data_utils
581605

0 commit comments

Comments
 (0)