Skip to content

Commit ce43b81

Browse files
Merge branch 'main' into rob-luke/training/mlflow-status
2 parents 63fe8d6 + 2bb60cc commit ce43b81

130 files changed

Lines changed: 11710 additions & 861 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎.main.commit‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
b2a8ec7458e90216e3a8d147d8ddf81cfb895f7c
1+
38986a98aae6a0cc4c8ae7b435db3288a890b0cb

‎docs/models/llm/deepseek-v4.md‎

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# DeepSeek V4
2+
3+
[DeepSeek-V4](https://github.com/deepseek-ai/DeepSeek-V4) is the next-generation Mixture-of-Experts language model from DeepSeek-AI. It extends the V3 design with **Hyper-Connections (mHC)** for multi-stream residual mixing, **Compressed Sparse Attention (CSA)** with a learned token-importance indexer (DSA), **hash-routed MoE layers** for the first few decoder blocks, and a refined **Multi-Token Prediction (MTP)** head with separate `e_proj` / `h_proj` projections.
4+
5+
DeepSeek V4 models are supported via the Bridge system with auto-detected configuration and weight mapping.
6+
7+
## Model Architecture Features
8+
9+
- **Hybrid Attention (DSv4HybridSelfAttention)**: Per-layer mix of dense MLA and Compressed Sparse Attention selected by `compress_ratios`
10+
- **Compressed Sparse Attention (CSA)** with **DSA Indexer**: Top-k token selection over windowed keys; `index_n_heads`, `index_head_dim`, `index_topk` control the indexer
11+
- **Hyper-Connections (mHC)**: 4-stream residual mixing per layer (`hc_mult = 4`) with sinkhorn-iterated attention; per-MTP-layer `hc_head_*` learns output contraction
12+
- **Hash-Routed MoE**: First few decoder layers use a deterministic vocab → expert mapping (`tid2eid`) instead of softmax routing
13+
- **Multi-Token Prediction (MTP)**: One MTP layer with separate `e_proj` and `h_proj` projections (post-MCore #4518)
14+
- **YaRN RoPE**: `rotary_scaling_factor=16`, `original_max_position_embeddings=65536`; `mscale=mscale_all_dim=1.0` for V4
15+
- **Sigmoid Gating with Expert Bias**: `noaux_tc` load balancing, `sqrtsoftplus` scoring, expert bias enabled
16+
- **`o_groups` Output Projection**: `o_lora_rank` low-rank output projection split into `o_groups` parallel groups
17+
18+
## Examples, Parallelism, and Limitations
19+
20+
For checkpoint conversion and inference scripts, recommended parallelism settings, and current known limitations, see the [DeepSeek V4 examples README](https://github.com/NVIDIA-NeMo/Megatron-Bridge/tree/main/examples/models/deepseek_v4).
21+
22+
## Hugging Face Model Cards & References
23+
24+
### Hugging Face Model Cards
25+
- DeepSeek-V4-Flash: https://huggingface.co/deepseek-ai/DeepSeek-V4-Flash
26+
- DeepSeek-V4-Flash-Base: https://huggingface.co/deepseek-ai/DeepSeek-V4-Flash-Base
27+
- DeepSeek-V4-Pro: https://huggingface.co/deepseek-ai/DeepSeek-V4-Pro
28+
- DeepSeek-V4-Pro-Base: https://huggingface.co/deepseek-ai/DeepSeek-V4-Pro-Base
29+
30+
### Additional Resources
31+
- GitHub Repository: https://github.com/deepseek-ai/DeepSeek-V4
32+
33+
## Related Docs
34+
- DeepSeek V4 examples: [examples/models/deepseek_v4/README.md](https://github.com/NVIDIA-NeMo/Megatron-Bridge/tree/main/examples/models/deepseek_v4)

‎docs/models/llm/index.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ This section documents Large Language Models supported by Megatron Bridge, with
77
88
deepseek-v2.md
99
deepseek-v3.md
10+
deepseek-v4.md
1011
gemma2.md
1112
gemma3.md
1213
glm45.md

‎docs/models/llm/nemotron3-nano.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,5 +91,5 @@ A LoRA checkpoint only contains the learnable adapter weights. In order to conve
9191
uv run python examples/peft/merge_lora.py \
9292
--hf-model-path $HF_MODEL_ID \
9393
--lora-checkpoint /path/to/lora/ckpt/iter_xxxxxxx
94-
--output /path/to/merged/ckpt
94+
--output /path/to/merged/hf/ckpt
9595
```

‎docs/models/vlm/nemotron-nano-v2-vl.md‎

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -149,10 +149,9 @@ format for downstream evaluation, it is necessary to merge the LoRA adapters bac
149149
uv run python examples/peft/merge_lora.py \
150150
--hf-model-path $HF_MODEL_PATH \
151151
--lora-checkpoint <trained LoRA checkpoint>/iter_N \
152-
--output <LoRA checkpoint merged>
152+
--output <merged HF checkpoint>
153153
```
154-
You can now run in-framework inference with `hf_to_megatron_generate_vlm.py` by supplying the merged LoRA checkpoint.
155-
You can also export the merged LoRA checkpoint to Hugging Face format.
154+
The output is a merged Hugging Face checkpoint for downstream evaluation.
156155

157156

158157
## Example Datasets

‎examples/conversion/convert_checkpoints_multi_gpu.py‎

Lines changed: 53 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
"""
5454

5555
import argparse
56+
import datetime
5657
import os
5758
import sys
5859

@@ -84,6 +85,20 @@ def _check_distributed():
8485
sys.exit(1)
8586

8687

88+
def _ensure_distributed_initialized(timeout_minutes: int | None):
89+
_check_distributed()
90+
if timeout_minutes is None:
91+
return
92+
if torch.distributed.is_initialized():
93+
return
94+
95+
torch.cuda.set_device(int(os.environ.get("LOCAL_RANK", "0")))
96+
torch.distributed.init_process_group(
97+
"nccl",
98+
timeout=datetime.timedelta(minutes=timeout_minutes),
99+
)
100+
101+
87102
@torchrun_main
88103
def import_hf_to_megatron(
89104
hf_model: str,
@@ -94,9 +109,10 @@ def import_hf_to_megatron(
94109
etp: int = 1,
95110
torch_dtype: str = "bfloat16",
96111
trust_remote_code: bool = False,
112+
distributed_timeout_minutes: int | None = None,
97113
) -> None:
98114
"""Import a HuggingFace model and save it as a distributed Megatron checkpoint."""
99-
_check_distributed()
115+
_ensure_distributed_initialized(distributed_timeout_minutes)
100116
dtype = _parse_dtype(torch_dtype)
101117

102118
print_rank_0(f"Importing: {hf_model} -> {megatron_path}")
@@ -115,6 +131,15 @@ def import_hf_to_megatron(
115131
model_provider.expert_tensor_parallel_size = etp
116132
model_provider.pipeline_dtype = dtype
117133
model_provider.params_dtype = dtype
134+
# Auto-generate pipeline layout for models that need it (e.g. DSv4 hash MoE)
135+
if pp > 1 and hasattr(bridge._model_bridge, "generate_pipeline_layout"):
136+
hf_config = bridge.hf_pretrained.config
137+
num_layers = hf_config.num_hidden_layers
138+
mtp = getattr(hf_config, "num_nextn_predict_layers", 0) or 0
139+
model_provider.pipeline_model_parallel_layout = bridge._model_bridge.generate_pipeline_layout(
140+
num_layers, pp, mtp
141+
)
142+
print_rank_0(f" Auto-generated pipeline layout for PP={pp} ({num_layers} layers, {mtp} MTP)")
118143
model_provider.finalize()
119144
model_provider.initialize_model_parallel(seed=0)
120145

@@ -151,9 +176,10 @@ def export_megatron_to_hf(
151176
show_progress: bool = True,
152177
distributed_save: bool = False,
153178
save_every_n_ranks: int = 1,
179+
distributed_timeout_minutes: int | None = None,
154180
) -> None:
155181
"""Export a distributed Megatron checkpoint to HuggingFace format."""
156-
_check_distributed()
182+
_ensure_distributed_initialized(distributed_timeout_minutes)
157183
dtype = _parse_dtype(torch_dtype)
158184

159185
print_rank_0(f"Exporting: {megatron_path} -> {hf_path}")
@@ -173,6 +199,23 @@ def export_megatron_to_hf(
173199
model_provider.expert_tensor_parallel_size = etp
174200
model_provider.pipeline_dtype = dtype
175201
model_provider.params_dtype = dtype
202+
# For PP > 1 export, read pipeline layout from checkpoint
203+
if pp > 1:
204+
from pathlib import Path
205+
206+
import yaml
207+
208+
ckpt_path = Path(megatron_path)
209+
for candidate in [ckpt_path, *ckpt_path.glob("iter_*")]:
210+
rc = candidate / "run_config.yaml"
211+
if rc.exists():
212+
with open(rc) as f:
213+
cfg = yaml.safe_load(f)
214+
saved_layout = cfg.get("model", {}).get("pipeline_model_parallel_layout")
215+
if isinstance(saved_layout, list):
216+
model_provider.pipeline_model_parallel_layout = saved_layout
217+
print_rank_0(f" Read pipeline layout from checkpoint ({len(saved_layout)} stages)")
218+
break
176219
model_provider.finalize()
177220
model_provider.initialize_model_parallel(seed=0)
178221

@@ -218,6 +261,12 @@ def _add_common_args(parser: argparse.ArgumentParser) -> None:
218261
help="Model precision (default: bfloat16)",
219262
)
220263
parser.add_argument("--trust-remote-code", action="store_true", help="Allow custom model code execution")
264+
parser.add_argument(
265+
"--distributed-timeout-minutes",
266+
type=int,
267+
default=None,
268+
help="Initialize the distributed process group with this timeout before model setup",
269+
)
221270

222271

223272
def main():
@@ -255,7 +304,6 @@ def main():
255304
default=1,
256305
help="Only every N-th rank writes files (reduces I/O, only with --distributed-save)",
257306
)
258-
259307
args = parser.parse_args()
260308

261309
if not args.command:
@@ -272,6 +320,7 @@ def main():
272320
etp=args.etp,
273321
torch_dtype=args.torch_dtype,
274322
trust_remote_code=args.trust_remote_code,
323+
distributed_timeout_minutes=args.distributed_timeout_minutes,
275324
)
276325
elif args.command == "export":
277326
export_megatron_to_hf(
@@ -288,6 +337,7 @@ def main():
288337
show_progress=not args.no_progress,
289338
distributed_save=args.distributed_save,
290339
save_every_n_ranks=args.save_every_n_ranks,
340+
distributed_timeout_minutes=args.distributed_timeout_minutes,
291341
)
292342

293343

‎examples/conversion/hf_to_megatron_generate_text.py‎

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,23 @@ def main(args) -> None:
128128
model_provider.expert_tensor_parallel_size = etp
129129
model_provider.pipeline_dtype = torch.bfloat16
130130

131+
# Read pipeline layout from checkpoint for PP > 1
132+
if pp > 1:
133+
from pathlib import Path
134+
135+
import yaml
136+
137+
ckpt_path = Path(args.megatron_model_path)
138+
for candidate in [ckpt_path, *ckpt_path.glob("iter_*")]:
139+
rc = candidate / "run_config.yaml"
140+
if rc.exists():
141+
with open(rc) as f:
142+
cfg = yaml.safe_load(f)
143+
saved_layout = cfg.get("model", {}).get("pipeline_model_parallel_layout")
144+
if isinstance(saved_layout, list):
145+
model_provider.pipeline_model_parallel_layout = saved_layout
146+
break
147+
131148
# Once all overrides are set, finalize the model provider to ensure the post initialization logic is run
132149
model_provider.finalize()
133150
model_provider.initialize_model_parallel(seed=0)

‎examples/conversion/hf_to_megatron_generate_vlm.py‎

Lines changed: 61 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@
3838
pad_input_ids_to_tp_multiple,
3939
patch_kimi_vision_processor,
4040
process_image_inputs,
41+
process_multi_image_inputs,
42+
process_video_inputs,
4143
to_cuda,
4244
)
4345

@@ -64,6 +66,8 @@ def __init__(
6466
image_grid_thw=None,
6567
image_sizes=None,
6668
mm_token_type_ids=None,
69+
pixel_values_videos=None,
70+
video_grid_thw=None,
6771
image_position_ids=None,
6872
):
6973
self.batch = dict(
@@ -79,6 +83,10 @@ def __init__(
7983
self.batch["image_sizes"] = image_sizes
8084
if mm_token_type_ids is not None:
8185
self.batch["mm_token_type_ids"] = mm_token_type_ids
86+
if pixel_values_videos is not None:
87+
self.batch["pixel_values_videos"] = pixel_values_videos
88+
if video_grid_thw is not None:
89+
self.batch["video_grid_thw"] = video_grid_thw
8290
if image_position_ids is not None:
8391
self.batch["image_position_ids"] = image_position_ids
8492
self._yielded = False
@@ -101,7 +109,15 @@ def vlm_forward_step(data_iterator, model, **kwargs) -> torch.Tensor:
101109
"position_ids": batch["position_ids"],
102110
"attention_mask": batch.get("attention_mask"),
103111
}
104-
for key in ("pixel_values", "image_grid_thw", "image_sizes", "mm_token_type_ids", "image_position_ids"):
112+
for key in (
113+
"pixel_values",
114+
"image_grid_thw",
115+
"image_sizes",
116+
"mm_token_type_ids",
117+
"pixel_values_videos",
118+
"video_grid_thw",
119+
"image_position_ids",
120+
):
105121
if key in batch:
106122
forward_args[key] = batch[key]
107123

@@ -215,27 +231,41 @@ def _disable_mtp(m):
215231
# ------------------------------------------------------------------
216232
# Process inputs
217233
# ------------------------------------------------------------------
234+
pixel_values = image_grid_thw = image_sizes = mm_token_type_ids = image_position_ids = None
235+
pixel_values_videos = video_grid_thw = None
236+
237+
if args.video_path:
238+
input_ids_raw, pixel_values_videos, video_grid_thw = process_video_inputs(
239+
processor, args.video_path, args.prompt, fps=args.video_fps
240+
)
241+
elif args.image_paths:
242+
input_ids_raw, pixel_values, image_grid_thw = process_multi_image_inputs(
243+
processor, args.image_paths, args.prompt
244+
)
245+
else:
246+
(
247+
input_ids_raw,
248+
pixel_values,
249+
image_grid_thw,
250+
image_sizes,
251+
mm_token_type_ids,
252+
image_position_ids,
253+
) = process_image_inputs(
254+
processor,
255+
args.image_path,
256+
args.prompt,
257+
is_gemma4=is_gemma4,
258+
is_kimi=is_kimi,
259+
image_token_id=image_token_id,
260+
)
218261

219-
(
220-
input_ids_raw,
221-
pixel_values,
222-
image_grid_thw,
223-
image_sizes,
224-
mm_token_type_ids,
225-
image_position_ids,
226-
) = process_image_inputs(
227-
processor,
228-
args.image_path,
229-
args.prompt,
230-
is_gemma4=is_gemma4,
231-
is_kimi=is_kimi,
232-
image_token_id=image_token_id,
233-
)
234262
input_ids_raw = input_ids_raw.cuda()
235263
pixel_values = to_cuda(pixel_values)
236264
image_grid_thw = to_cuda(image_grid_thw)
237265
image_sizes = to_cuda(image_sizes)
238266
mm_token_type_ids = to_cuda(mm_token_type_ids)
267+
pixel_values_videos = to_cuda(pixel_values_videos)
268+
video_grid_thw = to_cuda(video_grid_thw)
239269
image_position_ids = to_cuda(image_position_ids)
240270

241271
# ------------------------------------------------------------------
@@ -270,7 +300,9 @@ def _disable_mtp(m):
270300
image_grid_thw,
271301
image_sizes,
272302
mm_ids_padded,
273-
image_position_ids,
303+
pixel_values_videos=pixel_values_videos,
304+
video_grid_thw=video_grid_thw,
305+
image_position_ids=image_position_ids,
274306
)
275307

276308
output = fwd_bwd_function(
@@ -343,7 +375,18 @@ def _disable_mtp(m):
343375
"--pp_layout", type=str, default=None, help="Pipeline model parallel layout (e.g. 'Et*15|t*15|t*16|t*15L')"
344376
)
345377
parser.add_argument("--megatron_model_path", type=str, default=None, help="Path to Megatron model checkpoint")
346-
parser.add_argument("--image_path", type=str, default=None, help="Path or URL to image (optional).")
378+
parser.add_argument("--image_path", type=str, default=None, help="Path or URL to a single image (optional).")
379+
parser.add_argument(
380+
"--image_paths",
381+
type=str,
382+
nargs="+",
383+
default=None,
384+
help="Paths to N image files in order (multi-image; Qwen-family only).",
385+
)
386+
parser.add_argument("--video_path", type=str, default=None, help="Path to a video file (Qwen-family only).")
387+
parser.add_argument(
388+
"--video_fps", type=float, default=2.0, help="Frames per second to sample from the video (default: 2.0)."
389+
)
347390
parser.add_argument("--trust_remote_code", action="store_true", help="Trust remote code for HF model loading")
348391
args = parser.parse_args()
349392

0 commit comments

Comments
 (0)