Skip to content

Commit 75ad1ee

Browse files
authored
fix qwen35 ckpt convert (#1231)
fix qwen35 ckpt convert and add loss_mask
1 parent 2a72872 commit 75ad1ee

21 files changed

Lines changed: 2383 additions & 3592 deletions

flagscale/models/megatron/qwen35/qwen35_model.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,7 @@ def forward(
191191
video_input_mask: torch.Tensor = None,
192192
attention_mask: torch.Tensor = None,
193193
labels: torch.Tensor = None,
194+
loss_mask: torch.Tensor | None = None,
194195
inference_params: InferenceParams = None,
195196
packed_seq_params: PackedSeqParams = None,
196197
extra_block_kwargs: dict = None,
@@ -274,6 +275,7 @@ def forward(
274275
attention_mask=attention_mask,
275276
decoder_input=combined_embeddings,
276277
labels=labels,
278+
loss_mask=loss_mask,
277279
inference_params=inference_params,
278280
packed_seq_params=packed_seq_params,
279281
visual_pos_masks=visual_pos_masks,

flagscale/train/megatron/train_qwen35.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -372,6 +372,7 @@ def forward_step(data_iterator, model: Qwen35Model):
372372
video_input_mask=video_input_mask,
373373
attention_mask=attention_mask,
374374
labels=labels,
375+
loss_mask=loss_mask,
375376
)
376377

377378
return output_tensor, partial(loss_func, loss_mask, model=model)

tools/checkpoint/qwen35/README.md

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
# Qwen3.5 Checkpoint Converter
2+
3+
This directory provides a unified entry point for bidirectional Qwen3.5 checkpoint conversion between HuggingFace and Megatron.
4+
5+
## Main Files
6+
7+
| File/Directory | Description |
8+
| --- | --- |
9+
| [convert_qwen35.py](convert_qwen35.py) | Unified conversion entry point, supports `hf2meg` and `meg2hf` |
10+
| [run.sh](run.sh) | Shell wrapper for `convert_qwen35.py` |
11+
| [qwen35/](qwen35/) | Shared conversion library (Config, GDN/Attention/Vision/MTP/MLP, TP/PP/EP sharding, IO, validation) |
12+
13+
## Usage
14+
15+
### HF → Megatron
16+
17+
```bash
18+
./run.sh hf2meg \
19+
--yaml /workspace/FlagScale/examples/qwen35/conf/train/4b_nv_baseline.yaml \
20+
--hf-path Qwen/Qwen3.5-4B \
21+
--meg-path /path/to/output \
22+
[--ref-path /path/to/ref/megatron]
23+
```
24+
25+
For `hf2meg`, `--hf-path` can be either a local HF checkpoint directory or a ModelScope model ID (e.g. `Qwen/Qwen3.5-4B`). When the path is not found locally, the converter automatically downloads it from ModelScope.
26+
27+
### Megatron → HF
28+
29+
```bash
30+
./run.sh meg2hf \
31+
--yaml /workspace/FlagScale/examples/qwen35/conf/train/4b_nv_baseline.yaml \
32+
--meg-path /path/to/megatron/checkpoint \
33+
--hf-path /path/to/output \
34+
[--ref-path /path/to/ref/hf]
35+
```
36+
37+
### Direct Python Invocation
38+
39+
```bash
40+
python convert_qwen35.py --direction hf2meg --hf-path ... --meg-path ... --yaml ...
41+
python convert_qwen35.py --direction meg2hf --meg-path ... --hf-path ... --yaml ...
42+
```
43+
44+
## Parameters
45+
46+
- `--direction {hf2meg,meg2hf}`: **required**, conversion direction
47+
- `--hf-path PATH|ID`: **required**, HF checkpoint directory (input for hf2meg, output for meg2hf). For `hf2meg`, a ModelScope model ID such as `Qwen/Qwen3.5-4B` is also accepted; the checkpoint will be downloaded automatically if it is not found locally.
48+
- `--meg-path PATH`: **required**, Megatron checkpoint directory (output for hf2meg, input for meg2hf)
49+
- `--yaml PATH`: **required**, training config yaml (provides TP/PP/EP and model structure)
50+
- `--ref-path PATH`: optional, reference checkpoint path for post-conversion comparison
51+
- `--tp N` / `--pp N` / `--ep N`: optional, override parallel sizes from yaml
52+
- `--adjust-embedding`: during hf2meg, adjust vocab size to match the reference checkpoint
53+
- `--adjust-ln`: enable legacy layer norm adjustment (off by default; usually not needed for Qwen3.5)
54+
55+
## Output Format
56+
57+
- **Megatron**: `{save_dir}/release/mp_rank_*/model_optim_rng.pt`, plus `latest_checkpointed_iteration.txt`
58+
- **HF**: `{save_dir}/model.safetensors`
59+
60+
## Validation
61+
62+
- If `--ref-path` is provided, the converter automatically compares keys and shapes with the reference checkpoint
63+
- Use `compare_two_ckpts.py` for stricter per-tensor value comparison:
64+
65+
```bash
66+
python compare_two_ckpts.py --ref /path/to/ref/release --gen /path/to/gen/release --tp 2
67+
```
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
#!/usr/bin/env python3
2+
"""Unified Qwen3.5 HF <-> Megatron checkpoint converter.
3+
4+
Supports both dense and MoE models. Model type is auto-detected from the
5+
input checkpoint / YAML; users do not need to specify it.
6+
7+
Examples:
8+
# HF -> Megatron
9+
python convert_qwen35.py --direction hf2meg \
10+
--hf-path /path/to/hf \
11+
--meg-path /path/to/output \
12+
--yaml /path/to/train.yaml \
13+
[--ref-path /path/to/ref]
14+
15+
# Megatron -> HF
16+
python convert_qwen35.py --direction meg2hf \
17+
--meg-path /path/to/meg/checkpoint \
18+
--hf-path /path/to/output \
19+
--yaml /path/to/train.yaml \
20+
[--ref-path /path/to/hf/ref]
21+
"""
22+
23+
import argparse
24+
import sys
25+
26+
from qwen35.config import Config, detect_model_type
27+
from qwen35.constants import LN_ADJUSTMENT
28+
from qwen35.converter import DenseConverter, MoEConverter
29+
30+
31+
def parse_args():
32+
p = argparse.ArgumentParser(description="Unified Qwen3.5 checkpoint converter")
33+
p.add_argument(
34+
"--direction",
35+
required=True,
36+
choices=["hf2meg", "meg2hf"],
37+
help="Conversion direction",
38+
)
39+
p.add_argument("--hf-path", required=True, help="Path to HF checkpoint directory")
40+
p.add_argument(
41+
"--meg-path",
42+
required=True,
43+
help="For hf2meg: output Megatron checkpoint directory. "
44+
"For meg2hf: input Megatron checkpoint directory.",
45+
)
46+
p.add_argument(
47+
"--yaml",
48+
required=True,
49+
help="Path to training YAML config (provides TP/PP/EP and model shapes)",
50+
)
51+
p.add_argument(
52+
"--ref-path",
53+
default=None,
54+
help="Reference checkpoint path for validation (Megatron ref for hf2meg, "
55+
"HF ref for meg2hf).",
56+
)
57+
p.add_argument(
58+
"--adjust-embedding",
59+
action="store_true",
60+
help="Adjust embedding vocab size to match reference checkpoint (hf2meg only)",
61+
)
62+
p.add_argument(
63+
"--adjust-ln",
64+
action="store_true",
65+
help="Enable legacy layer norm adjustment (add/subtract 1.0). "
66+
"Only use this if the model stores raw gamma values instead of "
67+
"zero-centered weights (default: disabled for Qwen3.5).",
68+
)
69+
p.add_argument(
70+
"--no-adjust-ln",
71+
action="store_true",
72+
help=argparse.SUPPRESS,
73+
)
74+
p.add_argument(
75+
"--tp",
76+
type=int,
77+
default=None,
78+
help="Override tensor model parallel size from YAML",
79+
)
80+
p.add_argument(
81+
"--pp",
82+
type=int,
83+
default=None,
84+
help="Override pipeline model parallel size from YAML",
85+
)
86+
p.add_argument(
87+
"--ep",
88+
type=int,
89+
default=None,
90+
help="Override expert model parallel size from YAML (MoE only)",
91+
)
92+
return p.parse_args()
93+
94+
95+
def main():
96+
args = parse_args()
97+
98+
# Apply CLI override for layer norm adjustment
99+
if args.adjust_ln:
100+
import qwen35.constants
101+
102+
qwen35.constants.LN_ADJUSTMENT = True
103+
if args.no_adjust_ln:
104+
import qwen35.constants
105+
106+
qwen35.constants.LN_ADJUSTMENT = False
107+
108+
cfg = Config(args.yaml)
109+
if args.tp is not None:
110+
cfg.tp = args.tp
111+
if args.pp is not None:
112+
cfg.pp = args.pp
113+
if args.ep is not None:
114+
cfg.ep = args.ep
115+
116+
# Auto-detect model type from whichever input is available
117+
hf_input = args.hf_path if args.direction == "hf2meg" else None
118+
meg_input = args.meg_path if args.direction == "meg2hf" else None
119+
model_type = detect_model_type(
120+
hf_dir=hf_input,
121+
meg_dir=meg_input,
122+
yaml_path=args.yaml,
123+
)
124+
converter_cls = MoEConverter if model_type == "moe" else DenseConverter
125+
converter = converter_cls(cfg, adjust_embedding=args.adjust_embedding)
126+
127+
print(f"Direction: {args.direction}")
128+
print(f"Model type: {model_type}")
129+
print(f"TP={cfg.tp}, PP={cfg.pp}, EP={cfg.ep}")
130+
print(f"Layers={cfg.num_layers}, hidden={cfg.hidden_size}")
131+
print(f"LN adjustment: {LN_ADJUSTMENT}")
132+
133+
if args.direction == "hf2meg":
134+
success = converter.run_hf2meg(args.hf_path, args.meg_path, args.ref_path)
135+
else:
136+
success = converter.run_meg2hf(args.meg_path, args.hf_path, args.ref_path)
137+
138+
sys.exit(0 if success else 1)
139+
140+
141+
if __name__ == "__main__":
142+
main()

0 commit comments

Comments
 (0)