Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
75 commits
Select commit Hold shift + click to select a range
bd4703c
docs(model): draft model support card with Qwen3 example
yaoyu-33 Jul 16, 2026
f4092cc
docs(model): simplify Qwen3 support card draft
yaoyu-33 Jul 16, 2026
8adf113
docs(model): add verification commands to support card
yaoyu-33 Jul 16, 2026
fdd4351
docs(model): record enabled training features
yaoyu-33 Jul 16, 2026
0dbc4bd
docs(model): narrow enabled feature highlights
yaoyu-33 Jul 16, 2026
4a99fd4
docs(model): document enabled feature allowlist
yaoyu-33 Jul 16, 2026
7c56faa
docs(model): add Slurm long-context validation
yaoyu-33 Jul 16, 2026
d1291b8
fix(data): normalize packed Parquet loss masks
yaoyu-33 Jul 16, 2026
9c7aab1
docs(model): record Qwen3 long-context validation
yaoyu-33 Jul 16, 2026
53d4196
docs(model): allowlist immutable HF revision
yaoyu-33 Jul 16, 2026
a21a917
docs(model): verify Qwen3 support card
yaoyu-33 Jul 17, 2026
52b3d7b
docs(model): split CPU and GPU conversion verification
yaoyu-33 Jul 17, 2026
d24a7ad
docs(model): use Qwen3 model ID in support commands
yaoyu-33 Jul 17, 2026
2a38f7b
Merge remote-tracking branch 'origin/main' into yuya/mb-575-model-sup…
yaoyu-33 Jul 17, 2026
c94ff80
docs(model): stabilize Qwen3 training verification
yaoyu-33 Jul 17, 2026
e8d68f7
docs(model): verify Qwen3 training support
yaoyu-33 Jul 17, 2026
55c2962
docs(model): record exact Qwen3 inference output
yaoyu-33 Jul 17, 2026
acf3483
docs(model): resume Qwen3 from pretrain checkpoint
yaoyu-33 Jul 17, 2026
5c56eab
docs(model): verify direct Qwen3 resume
yaoyu-33 Jul 17, 2026
da04718
docs(model): add verification card skill
yaoyu-33 Jul 17, 2026
a5d1ff2
docs(model): keep verification commands workload-only
yaoyu-33 Jul 17, 2026
971aa9d
docs(model): simplify verification card skill layout
yaoyu-33 Jul 17, 2026
26994d2
docs(model): verify SFT export inference
yaoyu-33 Jul 17, 2026
24ca038
docs(model): use public Slurm launchers
yaoyu-33 Jul 17, 2026
c002cf0
docs(model): shorten verification data paths
yaoyu-33 Jul 17, 2026
89375d3
docs(model): address verification card review
yaoyu-33 Jul 17, 2026
b417456
docs(model): scope precision per verification item
yaoyu-33 Jul 17, 2026
70d94f7
docs(model): trim checkpoint overrides
yaoyu-33 Jul 17, 2026
9d09256
fix(conversion): preserve Qwen3 MoE top-k normalization
yaoyu-33 Jul 17, 2026
1703387
docs(model): add Qwen3 30B A3B support card
yaoyu-33 Jul 17, 2026
3ee9602
docs(model): update Qwen3 performance results
yaoyu-33 Jul 18, 2026
26c4ca0
docs(model): make performance verification optional
yaoyu-33 Jul 18, 2026
4e3a01a
Merge branch 'yuya/qwen3-verification-prerequisites' into yuya/mb-575…
yaoyu-33 Jul 19, 2026
c2e3aed
feat(misc): add model verification card tooling
yaoyu-33 Jul 19, 2026
aabb29d
Merge branch 'yuya/qwen3-verification-prerequisites' into yuya/mb-575…
yaoyu-33 Jul 19, 2026
596ef50
docs(model): tune Qwen3 30B A3B verification
yaoyu-33 Jul 19, 2026
222e4c7
docs(model): accept bounded resume loss drift
yaoyu-33 Jul 19, 2026
0cbd718
docs(model): align Qwen3 pretrain with perf recipe
yaoyu-33 Jul 19, 2026
3e991b4
feat(model-card): support trusted HF inference code
yaoyu-33 Jul 19, 2026
7426364
fix(recipe): align Moonlight finetuning architecture
yaoyu-33 Jul 19, 2026
11da3cc
fix(data): infer Moonlight assistant spans
yaoyu-33 Jul 19, 2026
93c6930
fix(conversion): preserve Moonlight model semantics
yaoyu-33 Jul 19, 2026
139a8ed
docs(model): add Moonlight support card
yaoyu-33 Jul 19, 2026
e0d0c12
fix(model-card): support Moonlight verification workflows
yaoyu-33 Jul 19, 2026
f2ec0a5
docs(model): record Moonlight verification results
yaoyu-33 Jul 19, 2026
7cad9d2
docs(model): verify Moonlight long-context SFT
yaoyu-33 Jul 19, 2026
04eb4f8
feat(recipes): promote Qwen3 30B pretrain defaults
yaoyu-33 Jul 19, 2026
0566160
fix(model-card): validate resume provenance
yaoyu-33 Jul 19, 2026
45d6f0b
docs(model): verify Moonlight parity provenance
yaoyu-33 Jul 19, 2026
36f7ee4
docs(model): refresh Qwen3 30B pretrain verification
yaoyu-33 Jul 19, 2026
1c914c5
docs(model): verify Moonlight pretraining
yaoyu-33 Jul 19, 2026
cc7fcfc
docs(model): verify Moonlight checkpoint resume
yaoyu-33 Jul 19, 2026
5b9d9cf
perf(recipe): enable Qwen3 precision-aware optimizer
yaoyu-33 Jul 19, 2026
d1a71e1
docs(model): refresh Qwen3 optimizer verification
yaoyu-33 Jul 20, 2026
cd75540
docs(skill): document bounded pretrain resume verification
yaoyu-33 Jul 20, 2026
ba19ea9
docs(skill): define comparable training contracts
yaoyu-33 Jul 20, 2026
9937013
docs(model): verify bounded Qwen3 8B resume
yaoyu-33 Jul 20, 2026
71073db
docs(skill): define Qwen3 MoE convergence cohort
yaoyu-33 Jul 20, 2026
13c02ed
feat(recipes): align Qwen and Moonlight convergence configs
yaoyu-33 Jul 20, 2026
691b8c3
perf(recipe): optimize Qwen3 MoE SFT defaults
yaoyu-33 Jul 20, 2026
5e26c13
docs(model): refresh Qwen3 MoE SFT verification
yaoyu-33 Jul 20, 2026
e2cfa77
fix(data): align batch sampling across pipeline stages
yaoyu-33 Jul 20, 2026
f3ae276
feat(recipes): align fine-tuning packing contracts
yaoyu-33 Jul 20, 2026
23013aa
docs(model): align convergence verification cards
yaoyu-33 Jul 20, 2026
abaf107
docs(model): restore exact Qwen3 MoE pretrain evidence
yaoyu-33 Jul 20, 2026
5871065
docs(model): refresh Qwen3 MoE parity evidence
yaoyu-33 Jul 20, 2026
501e321
feat(recipes): add Nemotron 3 Nano 4B verification configs
yaoyu-33 Jul 20, 2026
4180ea8
feat(conversion): support pinned HF revisions
yaoyu-33 Jul 20, 2026
9cb1160
fix(ci): allowlist immutable revision fixtures
yaoyu-33 Jul 20, 2026
4b980e2
fix(conversion): prefix Slurm job names
yaoyu-33 Jul 20, 2026
4650ef4
fix(conversion): preserve source generation config
yaoyu-33 Jul 20, 2026
76bafdd
docs(model-card): refine qwen3 moe parity disposition
yaoyu-33 Jul 20, 2026
5c51d7b
docs(model-card): preserve parity provenance
yaoyu-33 Jul 20, 2026
d8c50ab
fix(model): align Nemotron-H inference parity
yaoyu-33 Jul 20, 2026
a340262
feat(training): add synchronous Slurm wait option
yaoyu-33 Jul 20, 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
12 changes: 6 additions & 6 deletions docs/fern/versions/nightly/pages/models/qwen/qwen.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -218,9 +218,9 @@ uv run python examples/conversion/hf_to_megatron_generate_text.py \

| Model | Mode | TP | PP | EP | Total GPUs | Use Case |
|-------|------|----|----|----|-----------:|----------|
| **Qwen3-30B-A3B** | Pretrain | 1 | 1 | 8 | 8 | Pre-training (single node) |
| **Qwen3-30B-A3B** | Full SFT | 1 | 1 | 8 | 8 | Full supervised finetuning |
| **Qwen3-30B-A3B** | LoRA/DoRA | 1 | 1 | 8 | 8 | PEFT finetuning (single node) |
| **Qwen3-30B-A3B** | Pretrain | 1 | 1 | 16 | 16 | Pre-training (2 nodes) |
| **Qwen3-30B-A3B** | Full SFT | 1 | 1 | 16 | 16 | Full supervised finetuning (2 nodes) |
| **Qwen3-30B-A3B** | LoRA/DoRA | 4 | 1 | 4 | 4 | PEFT finetuning |
| **Qwen3-235B-A22B** | Pretrain | 2 | 8 | 32 | 512 | Pre-training (64 nodes) |
| **Qwen3-235B-A22B** | Full SFT | 2 | 8 | 32 | 512 | Full supervised finetuning (64 nodes) |
| **Qwen3-235B-A22B** | LoRA/DoRA | 2 | 8 | 32 | 512 | PEFT finetuning (64 nodes) |
Expand All @@ -239,7 +239,7 @@ config = qwen3_30b_a3b_pretrain_config(
train_iters=500_000,
global_batch_size=2048,
seq_length=4096,
# Uses TP=1, PP=1, EP=8 (8 GPUs) automatically
# Uses TP=1, PP=1, EP=16 (16 GPUs) automatically
)
```

Expand Down Expand Up @@ -272,7 +272,7 @@ config = qwen3_30b_a3b_sft_config(
train_iters=1000,
global_batch_size=64,
finetune_lr=5e-6,
# Uses TP=1, PP=1, EP=8 (8 GPUs) automatically
# Uses TP=1, PP=1, EP=16 (16 GPUs) automatically
)
```

Expand All @@ -288,7 +288,7 @@ config = qwen3_30b_a3b_peft_config(
train_iters=1000,
global_batch_size=128,
finetune_lr=1e-4,
# Uses TP=1, PP=1, EP=8 (8 GPUs) automatically
# Uses TP=4, PP=1, EP=4 (4 GPUs) automatically
)
```

Expand Down
12 changes: 6 additions & 6 deletions docs/models/qwen/qwen.md
Original file line number Diff line number Diff line change
Expand Up @@ -218,9 +218,9 @@ uv run python examples/conversion/hf_to_megatron_generate_text.py \

| Model | Mode | TP | PP | EP | Total GPUs | Use Case |
|-------|------|----|----|----|-----------:|----------|
| **Qwen3-30B-A3B** | Pretrain | 1 | 1 | 8 | 8 | Pre-training (single node) |
| **Qwen3-30B-A3B** | Full SFT | 1 | 1 | 8 | 8 | Full supervised finetuning |
| **Qwen3-30B-A3B** | LoRA/DoRA | 1 | 1 | 8 | 8 | PEFT finetuning (single node) |
| **Qwen3-30B-A3B** | Pretrain | 1 | 1 | 16 | 16 | Pre-training (2 nodes) |
| **Qwen3-30B-A3B** | Full SFT | 1 | 1 | 16 | 16 | Full supervised finetuning (2 nodes) |
| **Qwen3-30B-A3B** | LoRA/DoRA | 4 | 1 | 4 | 4 | PEFT finetuning |
| **Qwen3-235B-A22B** | Pretrain | 2 | 8 | 32 | 512 | Pre-training (64 nodes) |
| **Qwen3-235B-A22B** | Full SFT | 2 | 8 | 32 | 512 | Full supervised finetuning (64 nodes) |
| **Qwen3-235B-A22B** | LoRA/DoRA | 2 | 8 | 32 | 512 | PEFT finetuning (64 nodes) |
Expand All @@ -239,7 +239,7 @@ config = qwen3_30b_a3b_pretrain_config(
train_iters=500_000,
global_batch_size=2048,
seq_length=4096,
# Uses TP=1, PP=1, EP=8 (8 GPUs) automatically
# Uses TP=1, PP=1, EP=16 (16 GPUs) automatically
)
```

Expand Down Expand Up @@ -272,7 +272,7 @@ config = qwen3_30b_a3b_sft_config(
train_iters=1000,
global_batch_size=64,
finetune_lr=5e-6,
# Uses TP=1, PP=1, EP=8 (8 GPUs) automatically
# Uses TP=1, PP=1, EP=16 (16 GPUs) automatically
)
```

Expand All @@ -288,7 +288,7 @@ config = qwen3_30b_a3b_peft_config(
train_iters=1000,
global_batch_size=128,
finetune_lr=1e-4,
# Uses TP=1, PP=1, EP=8 (8 GPUs) automatically
# Uses TP=4, PP=1, EP=4 (4 GPUs) automatically
)
```

Expand Down
162 changes: 123 additions & 39 deletions examples/conversion/compare_hf_and_megatron/compare.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,8 @@
import torch
import torch.distributed as dist
from megatron.core import parallel_state
from megatron.core.inference.contexts import StaticInferenceContext
from megatron.core.inference.utils import InferenceMode
from megatron.core.pipeline_parallel.schedules import get_forward_backward_func
from transformers import AutoConfig, AutoModelForCausalLM, AutoProcessor, AutoTokenizer

Expand Down Expand Up @@ -264,11 +266,20 @@ class SingleBatchIterator:
then raises StopIteration. Used for single-step inference in the forward pass.
"""

def __init__(self, input_ids, position_ids, attention_mask, pixel_values=None, image_grid_thw=None):
def __init__(
self,
input_ids,
position_ids,
attention_mask,
pixel_values=None,
image_grid_thw=None,
inference_context=None,
):
self.batch = dict(
tokens=input_ids,
position_ids=position_ids,
attention_mask=attention_mask,
inference_context=inference_context,
)

# Add vision inputs if provided
Expand Down Expand Up @@ -329,6 +340,47 @@ def loss_func(x, **kwargs):
return output_tensor, loss_func


def inference_forward_step(data_iterator, model, **kwargs) -> torch.Tensor:
"""Run a text-model forward step with an explicit inference context."""
batch = next(data_iterator)

def loss_func(x, **kwargs):
return x

model_output = model(
input_ids=batch["tokens"],
position_ids=batch["position_ids"],
attention_mask=batch.get("attention_mask"),
inference_context=batch["inference_context"],
runtime_gather_output=True,
)
if isinstance(model_output, tuple):
model_output = model_output[0]
return model_output, loss_func


def _run_megatron_forward(fwd_bwd_function, **kwargs):
"""Run a Megatron forward pass with the inference execution paths active."""
with InferenceMode.active():
return fwd_bwd_function(**kwargs)


def _maybe_gather_tensor_parallel_logits(megatron_output, hf_vocab_size: int, world_size: int, group):
"""Gather sharded TP logits while preserving an already gathered full-vocabulary tensor."""
if megatron_output.size(-1) >= hf_vocab_size:
return megatron_output

gathered_tensors = [torch.zeros_like(megatron_output) for _ in range(world_size)]
dist.all_gather(gathered_tensors, megatron_output, group=group)
gathered_output = torch.cat(gathered_tensors, dim=2)
if gathered_output.size(-1) < hf_vocab_size:
raise ValueError(
f"Gathered Megatron vocabulary ({gathered_output.size(-1)}) is smaller than "
f"the Hugging Face vocabulary ({hf_vocab_size})."
)
return gathered_output


def load_image(image_path: str) -> Image.Image:
"""Load an image from URL or file path.

Expand Down Expand Up @@ -671,6 +723,29 @@ def _setup_tokenizer_and_processor(args, is_vl_model: bool):
return tokenizer, processor


def _broadcast_hf_results(hf_logits, hf_next_token, device):
"""Broadcast rank-0 HF results using the model's actual output vocabulary size."""
if hf_logits is not None:
hf_logits = hf_logits.float()

hf_logits_size = torch.tensor(
[hf_logits.numel() if hf_logits is not None else 0],
device=device,
dtype=torch.long,
)
torch.distributed.broadcast(hf_logits_size, 0)

if hf_next_token is None:
hf_next_token = torch.zeros(1, device=device, dtype=torch.long)
if hf_logits is None:
hf_logits = torch.zeros(hf_logits_size.item(), device=device, dtype=torch.float32)

torch.distributed.broadcast(hf_next_token, 0)
torch.distributed.broadcast(hf_logits, 0)
torch.distributed.barrier()
return hf_logits, hf_next_token


def compare_models_one_step(args) -> None:
"""Compare 1-step generation between HF and Megatron models with debugging.

Expand Down Expand Up @@ -733,28 +808,10 @@ def compare_models_one_step(args) -> None:

# Broadcast HF results to all ranks
if torch.distributed.is_initialized():
# Ensure consistent dtype across ranks: rank 0 has bfloat16 logits from the HF model,
# so all ranks must use the same dtype for NCCL broadcast to work correctly.
if hf_logits is not None:
hf_logits = hf_logits.float()

# Create tensors for broadcasting if they don't exist on non-rank-0
if hf_next_token is None:
hf_next_token = torch.zeros(1, device=input_ids.device, dtype=torch.long)
if hf_logits is None:
# Get vocab size from tokenizer for proper tensor size
vocab_size = getattr(
tokenizer, "vocab_size", len(tokenizer.vocab) if hasattr(tokenizer, "vocab") else 32000
)
hf_logits = torch.zeros(vocab_size, device=input_ids.device, dtype=torch.float32)

# Ensure consistent dtype across ranks before broadcast
hf_logits = hf_logits.float()

# Broadcast from rank 0 to all ranks
torch.distributed.broadcast(hf_next_token, 0)
torch.distributed.broadcast(hf_logits, 0)
torch.distributed.barrier()
# The model's output vocabulary can be larger than the tokenizer vocabulary.
# Broadcast the actual logits length before allocating receive buffers so every
# rank participates in the logits broadcast with the same tensor shape.
hf_logits, hf_next_token = _broadcast_hf_results(hf_logits, hf_next_token, input_ids.device)
print_rank_0("HF results broadcast complete.")

# Run Megatron model forward pass
Expand All @@ -773,18 +830,44 @@ def compare_models_one_step(args) -> None:
attention_mask = None

fwd_bwd_function = get_forward_backward_func()
iterator = SingleBatchIterator(input_ids, position_ids, attention_mask, pixel_values, image_grid_thw)

megatron_output = fwd_bwd_function(
forward_step_func=vlm_forward_step,
data_iterator=iterator,
model=megatron_model,
num_microbatches=1,
forward_only=True,
seq_length=input_ids.size(1),
micro_batch_size=1,
collect_non_loss_data=True,
)
forward_kwargs = {
"model": megatron_model,
"num_microbatches": 1,
"forward_only": True,
"seq_length": input_ids.size(1),
"micro_batch_size": 1,
"collect_non_loss_data": True,
}
if is_vl_model:
iterator = SingleBatchIterator(
input_ids,
position_ids,
attention_mask,
pixel_values,
image_grid_thw,
)
megatron_output = fwd_bwd_function(
forward_step_func=vlm_forward_step,
data_iterator=iterator,
**forward_kwargs,
)
else:
inference_context = StaticInferenceContext(
max_batch_size=input_ids.size(0),
max_sequence_length=input_ids.size(1),
)
iterator = SingleBatchIterator(
input_ids,
position_ids,
attention_mask,
inference_context=inference_context,
)
megatron_output = _run_megatron_forward(
fwd_bwd_function,
forward_step_func=inference_forward_step,
data_iterator=iterator,
**forward_kwargs,
)

if isinstance(megatron_output, list) and len(megatron_output) > 0:
megatron_output = megatron_output[0]
Expand All @@ -796,11 +879,12 @@ def compare_models_one_step(args) -> None:
# Gather tensor parallel results if using TP
if torch.distributed.is_initialized() and parallel_state.get_tensor_model_parallel_world_size() > 1:
world_size = parallel_state.get_tensor_model_parallel_world_size()
gathered_tensors = [torch.zeros_like(megatron_output) for _ in range(world_size)]
dist.all_gather(
gathered_tensors, megatron_output, group=parallel_state.get_tensor_model_parallel_group()
megatron_output = _maybe_gather_tensor_parallel_logits(
megatron_output,
hf_logits.size(0),
world_size,
parallel_state.get_tensor_model_parallel_group(),
)
megatron_output = torch.cat(gathered_tensors, dim=2)

megatron_logits = megatron_output[0, -1, :]
megatron_next_token = torch.argmax(megatron_logits, dim=-1)
Expand Down
Loading
Loading