Skip to content

Commit 5bec7a1

Browse files
authored
Support GLM-5.3 Flash training and bump Docker image (#2478)
1 parent 4ac368b commit 5bec7a1

33 files changed

Lines changed: 1758 additions & 29 deletions

‎.github/workflows/pr-test.yml‎

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,14 @@ jobs:
305305
"num_gpus": 1,
306306
"test_file": "test_sglang_top_p_copy.py"
307307
},
308+
{
309+
"num_gpus": 1,
310+
"test_file": "test_glm5_next_gpu.py"
311+
},
312+
{
313+
"num_gpus": 4,
314+
"test_file": "test_glm5_next_cp.py"
315+
},
308316
{
309317
"enable_eval": "0",
310318
"num_gpus": 8,
@@ -1164,6 +1172,10 @@ jobs:
11641172
"num_gpus": 0,
11651173
"test_file": "test_glm5_indexer_short_context.py"
11661174
},
1175+
{
1176+
"num_gpus": 0,
1177+
"test_file": "test_glm5_next_ops.py"
1178+
},
11671179
{
11681180
"num_gpus": 0,
11691181
"test_file": "test_model_provider_freeze.py"
@@ -1509,6 +1521,14 @@ jobs:
15091521
"num_gpus": 1,
15101522
"test_file": "test_sglang_top_p_copy.py"
15111523
},
1524+
{
1525+
"num_gpus": 1,
1526+
"test_file": "test_glm5_next_gpu.py"
1527+
},
1528+
{
1529+
"num_gpus": 4,
1530+
"test_file": "test_glm5_next_cp.py"
1531+
},
15121532
{
15131533
"enable_eval": "0",
15141534
"num_gpus": 8,

‎.github/workflows/pr-test.yml.j2‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
{'test_file': 'test_qwen3_4B_ppo_train_critic_only.py', 'num_gpus': 8, 'enable_eval': '0'},
1616
{'test_file': 'test_ppo_logprob_entropy_gpu.py', 'num_gpus': 2},
1717
{'test_file': 'test_sglang_top_p_copy.py', 'num_gpus': 1},
18+
{'test_file': 'test_glm5_next_gpu.py', 'num_gpus': 1},
19+
{'test_file': 'test_glm5_next_cp.py', 'num_gpus': 4},
1820
{'test_file': 'test_moonlight_16B_A3B.py', 'num_gpus': 8, 'enable_eval': '0'},
1921
{'test_file': 'test_moonlight_16B_A3B_r3.py', 'num_gpus': 8, 'enable_eval': '0', 'straw': True},
2022
{'test_file': 'test_mimo_7B_mtp_only_grad.py', 'num_gpus': 8, 'straw': True},
@@ -163,6 +165,7 @@
163165
{'test_file': 'test_empty_colocated_weight_bucket.py', 'num_gpus': 0},
164166
{'test_file': 'test_expert_routing.py', 'num_gpus': 0},
165167
{'test_file': 'test_glm5_indexer_short_context.py', 'num_gpus': 0},
168+
{'test_file': 'test_glm5_next_ops.py', 'num_gpus': 0},
166169
{'test_file': 'test_model_provider_freeze.py', 'num_gpus': 0},
167170
{'test_file': 'test_glm52_layerwise_comparison.py', 'num_gpus': 0},
168171
{'test_file': 'test_layerwise_alignment.py', 'num_gpus': 0},

‎docker/README.md‎

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ Current build configuration:
1313

1414
- sglang v0.5.21 (e00930c5489053f26d86b179cee0d087f846acbb), Megatron Core v0.19.2 (4b4acac9a1d28ea6829c8d4f566d75698a21249d)
1515
- Base: `lmsysorg/sglang:v0.5.21`, CUDA 13.0.3, PyTorch 2.13.0.
16-
- Build version: `nightly-dev-20261016a` in `docker/version.txt` (Slime `0.4.0`).
16+
- Build version: `nightly-dev-20261016b` in `docker/version.txt` (Slime `0.4.0`).
1717

1818
Previous build configurations:
1919

@@ -37,7 +37,7 @@ just build
3737
Or from the repository root:
3838

3939
```bash
40-
docker build -f docker/Dockerfile . -t slimerl/slime:nightly-dev-20261016a-cu130
40+
docker build -f docker/Dockerfile . -t slimerl/slime:nightly-dev-20261016b-cu130
4141
```
4242

4343
The local checkout is installed into the image. The existing
@@ -70,7 +70,7 @@ not need to change to 13.0 when compatibility is active.
7070
Run commands through the image's default entrypoint:
7171

7272
```bash
73-
docker run --rm --gpus all --ipc=host slimerl/slime:nightly-dev-20261016a-cu130 \
73+
docker run --rm --gpus all --ipc=host slimerl/slime:nightly-dev-20261016b-cu130 \
7474
python -c 'import torch; print(torch.__version__, torch.version.cuda); print(torch.ones(1, device="cuda"))'
7575
```
7676

@@ -89,6 +89,12 @@ The following components are pinned in the image:
8989
- Megatron-LM / NVIDIA Megatron Core `0.19.2`
9090
(`4b4acac9a1d28ea6829c8d4f566d75698a21249d`), plus
9191
`docker/patch/<version>/megatron.patch` and `megatron-sglang-aligned.patch`.
92+
The `latest` and `v0.5.21` patches refresh all HybridDeviceOptimizer copies
93+
after model checkpoint loading, including parameters that are already FP32
94+
and offloaded to CPU. This prevents the first optimizer step from replacing
95+
loaded GLM5-next mHC/KDA parameters with their initial values. Optimizer-state
96+
restore uses the same complete parameter mapping, avoiding a KeyError for
97+
already-FP32 parameters when resuming training.
9298
- Transformer Engine `2.18.0`: the official CUDA core wheel matching the base
9399
image, with PyTorch bindings compiled locally. Optional NCCL EP bindings are
94100
disabled; Slime uses DeepEP.

‎docker/patch/latest/megatron.patch‎

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -454,3 +454,33 @@ index 57b8efe93..e7376a7f8 100644
454454
torch_memory_saver.hook_mode = "torch"
455455
HAVE_TORCH_MEMORY_SAVER = True
456456
except ImportError:
457+
diff --git a/megatron/core/optimizer/cpu_offloading/hybrid_optimizer.py b/megatron/core/optimizer/cpu_offloading/hybrid_optimizer.py
458+
index c87ccd5ff..1ecc861bd 100644
459+
--- a/megatron/core/optimizer/cpu_offloading/hybrid_optimizer.py
460+
+++ b/megatron/core/optimizer/cpu_offloading/hybrid_optimizer.py
461+
@@ -370,15 +370,21 @@ class HybridDeviceOptimizer(torch.optim.Optimizer):
462+
if not self.param_update_in_fp32:
463+
return
464+
for param, v in self.state.items():
465+
- fp32_param = self.param_to_fp32_param[param]
466+
+ fp32_param = self.param_to_inner_param[param]
467+
fp32_param.data.copy_(v["master_param"])
468+
469+
+ @torch.no_grad()
470+
def update_fp32_param_by_new_param(self):
471+
"""
472+
- Update the fp32 parameters by the new parameters.
473+
+ Refresh optimizer copies after loading new model parameters.
474+
+
475+
+ Already-FP32 parameters can also have a separate CPU copy, even though
476+
+ they are absent from param_to_fp32_param. Leaving that copy stale would
477+
+ overwrite the loaded model parameter on the first optimizer step.
478+
"""
479+
- for param, fp32_param in self.param_to_fp32_param.items():
480+
- fp32_param.data.copy_(param)
481+
+ for param, inner_param in self.param_to_inner_param.items():
482+
+ if inner_param is not param:
483+
+ inner_param.copy_(param)
484+
485+
def _register_load_state_dict_hooks(self):
486+
def pre_load_state_dict_hook(self, state_dict):

‎docker/patch/v0.5.21/megatron.patch‎

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -454,3 +454,33 @@ index 57b8efe93..e7376a7f8 100644
454454
torch_memory_saver.hook_mode = "torch"
455455
HAVE_TORCH_MEMORY_SAVER = True
456456
except ImportError:
457+
diff --git a/megatron/core/optimizer/cpu_offloading/hybrid_optimizer.py b/megatron/core/optimizer/cpu_offloading/hybrid_optimizer.py
458+
index c87ccd5ff..1ecc861bd 100644
459+
--- a/megatron/core/optimizer/cpu_offloading/hybrid_optimizer.py
460+
+++ b/megatron/core/optimizer/cpu_offloading/hybrid_optimizer.py
461+
@@ -370,15 +370,21 @@ class HybridDeviceOptimizer(torch.optim.Optimizer):
462+
if not self.param_update_in_fp32:
463+
return
464+
for param, v in self.state.items():
465+
- fp32_param = self.param_to_fp32_param[param]
466+
+ fp32_param = self.param_to_inner_param[param]
467+
fp32_param.data.copy_(v["master_param"])
468+
469+
+ @torch.no_grad()
470+
def update_fp32_param_by_new_param(self):
471+
"""
472+
- Update the fp32 parameters by the new parameters.
473+
+ Refresh optimizer copies after loading new model parameters.
474+
+
475+
+ Already-FP32 parameters can also have a separate CPU copy, even though
476+
+ they are absent from param_to_fp32_param. Leaving that copy stale would
477+
+ overwrite the loaded model parameter on the first optimizer step.
478+
"""
479+
- for param, fp32_param in self.param_to_fp32_param.items():
480+
- fp32_param.data.copy_(param)
481+
+ for param, inner_param in self.param_to_inner_param.items():
482+
+ if inner_param is not param:
483+
+ inner_param.copy_(param)
484+
485+
def _register_load_state_dict_hooks(self):
486+
def pre_load_state_dict_hook(self, state_dict):

‎docker/version.txt‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
nightly-dev-20261016a
1+
nightly-dev-20261016b
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
#!/usr/bin/env bash
2+
3+
# GLM-5.3 Flash (checkpoint model_type: glm5_next).
4+
# KDA / no-RoPE MLA, 4-token pooled DSA, 4-stream mHC, and CogViT.
5+
MODEL_ARGS=(
6+
--spec slime_plugins.models.glm5_next.model get_glm5_next_model_provider
7+
--model-name glm5_next
8+
--num-layers 45
9+
--hidden-size 4096
10+
--ffn-hidden-size 12288
11+
--num-attention-heads 64
12+
--num-query-groups 64
13+
--kv-channels 256
14+
--q-lora-rank 1536
15+
--kv-lora-rank 512
16+
--vocab-size 154880
17+
--make-vocab-size-divisible-by 64
18+
--disable-bias-linear
19+
--swiglu
20+
--untie-embeddings-and-output-weights
21+
--position-embedding-type none
22+
--normalization RMSNorm
23+
--norm-epsilon 1e-5
24+
--rotary-base 800000
25+
--num-experts 288
26+
--moe-layer-freq '[0]*3+[1]*42'
27+
--moe-ffn-hidden-size 2048
28+
--moe-shared-expert-intermediate-size 2048
29+
--moe-router-topk 8
30+
--moe-router-score-function sigmoid
31+
--moe-router-pre-softmax
32+
--moe-router-enable-expert-bias
33+
--moe-router-bias-update-rate 0
34+
--moe-router-load-balancing-type seq_aux_loss
35+
--moe-router-topk-scaling-factor 2.5
36+
--moe-aux-loss-coeff 0
37+
--moe-router-dtype fp32
38+
--moe-grouped-gemm
39+
--moe-permute-fusion
40+
--freeze-indexer
41+
--enable-experimental
42+
)

‎scripts/run-glm5.3-flash-300B.sh‎

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
unset http_proxy https_proxy HTTP_PROXY HTTPS_PROXY ALL_PROXY all_proxy
4+
export no_proxy="${no_proxy:-localhost,127.0.0.1}"
5+
export NO_PROXY="${no_proxy}"
6+
7+
# Submit to an existing 8-node x 8-H100 Ray cluster. All paths must be shared.
8+
# Assume the BF16 GLM-5.3-Flash checkpoint and dapo-math-17k are already
9+
# downloaded under BASE_DIR, which must be mounted on every Ray node.
10+
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
11+
REPO_ROOT="$(cd -- "${SCRIPT_DIR}/.." && pwd)"
12+
source "${SCRIPT_DIR}/models/glm5.3-flash-300B.sh"
13+
BASE_DIR="${BASE_DIR:-/data}"
14+
HF_CHECKPOINT="${HF_CHECKPOINT:-${BASE_DIR}/GLM-5.3-Flash}"
15+
PROMPT_DATA="${PROMPT_DATA:-${BASE_DIR}/dapo-math-17k/dapo-math-17k.jsonl}"
16+
RUN_DIR="${RUN_DIR:-${REPO_ROOT}/local/glm53/$(date -u +%Y%m%dT%H%M%SZ)}"
17+
mkdir -p "${RUN_DIR}"
18+
export REPO_ROOT HF_CHECKPOINT
19+
# Let NCCL/Gloo choose their interfaces unless the cluster needs an override.
20+
export SOCKET_IFNAME="${SOCKET_IFNAME:-}"
21+
export MEGATRON_PATH="${MEGATRON_PATH:-/root/Megatron-LM}"
22+
23+
RUNTIME_ENV_JSON="$(python - <<'PY'
24+
import json, os
25+
env = {
26+
'PYTHONPATH': os.environ['REPO_ROOT'] + ':' + os.environ['MEGATRON_PATH'],
27+
'PYTHONUNBUFFERED': '1', 'OMP_NUM_THREADS': '4',
28+
'CUDA_DEVICE_MAX_CONNECTIONS': '1',
29+
'NCCL_IB_TIMEOUT': '22', 'NCCL_IB_QPS_PER_CONNECTION': '2',
30+
'NCCL_NVLS_ENABLE': '0', 'NCCL_CUMEM_ENABLE': '0',
31+
# Ray workers inherit the head/worker daemon environment as well.
32+
'http_proxy': '', 'https_proxy': '', 'HTTP_PROXY': '', 'HTTPS_PROXY': '',
33+
'all_proxy': '', 'ALL_PROXY': '',
34+
'no_proxy': os.environ['no_proxy'],
35+
'NO_PROXY': os.environ['NO_PROXY'],
36+
'TOKENIZERS_PARALLELISM': 'false',
37+
}
38+
if os.environ['SOCKET_IFNAME']:
39+
env['NCCL_SOCKET_IFNAME'] = env['GLOO_SOCKET_IFNAME'] = os.environ['SOCKET_IFNAME']
40+
print(json.dumps({'env_vars': env}))
41+
PY
42+
)"
43+
44+
cd "${REPO_ROOT}"
45+
# Populate remote-code caches once per node before concurrent worker startup.
46+
python - <<'PY'
47+
import os
48+
import ray
49+
from ray.util.scheduling_strategies import NodeAffinitySchedulingStrategy
50+
51+
ray.init(address="auto", log_to_driver=False)
52+
53+
@ray.remote
54+
def prepare_processor(path):
55+
from transformers import AutoProcessor, AutoTokenizer
56+
AutoProcessor.from_pretrained(path, trust_remote_code=True)
57+
AutoTokenizer.from_pretrained(path, trust_remote_code=True)
58+
59+
ray.get([
60+
prepare_processor.options(scheduling_strategy=NodeAffinitySchedulingStrategy(node["NodeID"], soft=False)).remote(os.environ["HF_CHECKPOINT"])
61+
for node in ray.nodes() if node["Alive"]
62+
])
63+
ray.shutdown()
64+
PY
65+
66+
ray job submit --address="${RAY_DASHBOARD:-http://127.0.0.1:8265}" \
67+
--runtime-env-json="${RUNTIME_ENV_JSON}" \
68+
-- python train.py \
69+
--actor-num-nodes 8 --actor-num-gpus-per-node 8 --colocate \
70+
"${MODEL_ARGS[@]}" \
71+
--hf-checkpoint "${HF_CHECKPOINT}" --load "${HF_CHECKPOINT}" \
72+
--megatron-to-hf-mode raw \
73+
--prompt-data "${PROMPT_DATA}" --input-key prompt --label-key label --apply-chat-template \
74+
--rm-type "${RM_TYPE:-deepscaler}" \
75+
--num-rollout "${NUM_ROLLOUT:-3}" --rollout-batch-size 16 --n-samples-per-prompt 8 \
76+
--global-batch-size 128 --micro-batch-size 1 \
77+
--rollout-max-response-len "${RESPONSE_LENGTH:-32768}" \
78+
--rollout-temperature 1.0 --rollout-top-p 0.95 \
79+
--use-rollout-routing-replay --use-score-centering --pg-loss-type reinforce \
80+
--advantage-estimator grpo \
81+
--use-tis --tis-clip-low 0.5 --tis-clip 2.0 \
82+
--tensor-model-parallel-size 1 --pipeline-model-parallel-size 4 \
83+
--decoder-first-pipeline-num-layers 12 --decoder-last-pipeline-num-layers 11 \
84+
--context-parallel-size "${CP_SIZE:-2}" --expert-model-parallel-size 16 --expert-tensor-parallel-size 1 \
85+
--allgather-cp \
86+
--moe-token-dispatcher-type alltoall \
87+
--recompute-granularity full --recompute-method uniform --recompute-num-layers 1 \
88+
--seq-length 65536 --max-position-embeddings 65536 \
89+
--use-dynamic-batch-size --max-tokens-per-gpu 2048 --data-pad-size-multiplier 128 \
90+
--log-probs-chunk-size 256 \
91+
--optimizer adam --lr 1e-6 --lr-decay-style constant \
92+
--weight-decay 0.1 --adam-beta1 0.9 --adam-beta2 0.98 \
93+
--optimizer-cpu-offload --overlap-cpu-optimizer-d2h-h2d --use-precision-aware-optimizer \
94+
--no-load-optim --no-load-rng --no-save-optim \
95+
--freeze-params-name-list 'model.visual.*' \
96+
--rollout-num-gpus-per-engine 16 \
97+
--sglang-dp-size 16 --sglang-ep-size 16 --sglang-enable-dp-attention \
98+
--sglang-enable-dp-lm-head --sglang-moe-dense-tp-size 1 \
99+
--sglang-mm-enable-dp-encoder --sglang-disable-shared-experts-fusion \
100+
--sglang-dsa-prefill-backend fa3 --sglang-dsa-decode-backend fa3 \
101+
--sglang-mem-fraction-static 0.75 --sglang-context-length 65536 \
102+
--sglang-max-running-requests 32 --sglang-chunked-prefill-size 4096 \
103+
--sglang-disable-overlap-schedule --sglang-cuda-graph-max-bs-decode 8 \
104+
--sglang-mamba-ssm-dtype float32 --sglang-page-size 64 \
105+
--sglang-kv-cache-dtype bfloat16 \
106+
--sglang-watchdog-timeout 3600 \
107+
--attention-dropout 0 --hidden-dropout 0 --bf16 \
108+
--accumulate-allreduce-grads-in-fp32 --attention-softmax-in-fp32 \
109+
--distributed-timeout-minutes 60 \
110+
--update-weight-buffer-size 1073741824 \
111+
--save-debug-train-data "${RUN_DIR}/train-{rollout_id}.pt" \
112+
"$@"

‎slime/backends/megatron_utils/actor.py‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
import torch.distributed as dist
1111
from megatron.core import mpu
1212
from torch_memory_saver import torch_memory_saver
13-
from transformers import AutoConfig, AutoTokenizer
13+
from transformers import AutoTokenizer
1414

1515
from slime.data.tensor import TensorRef
1616
from slime.observability import train_data_utils, train_metric_utils
@@ -23,6 +23,7 @@
2323
from slime.utils.distributed_utils import get_gloo_group
2424
from slime.utils.memory_utils import clear_memory, print_memory, reset_cuda_stack_size
2525
from slime.utils.misc import Box
26+
from slime.utils.model_config import load_hf_config
2627
from slime.utils.reloadable_process_group import (
2728
destroy_process_groups,
2829
monkey_patch_torch_dist,
@@ -93,7 +94,7 @@ def init(
9394
# read config and tokenizer serialized to prevent concurrent writing bug.
9495
for i in range(args.num_gpus_per_node):
9596
if i == dist.get_rank() % args.num_gpus_per_node:
96-
self.hf_config = AutoConfig.from_pretrained(args.hf_checkpoint, trust_remote_code=True)
97+
self.hf_config = load_hf_config(args.hf_checkpoint, trust_remote_code=True)
9798
self.tokenizer = AutoTokenizer.from_pretrained(self.args.hf_checkpoint, trust_remote_code=True)
9899
dist.barrier(group=get_gloo_group())
99100

‎slime/backends/megatron_utils/arguments.py‎

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33

44
from megatron.training.arguments import parse_args as _megatron_parse_args
55
from megatron.training.arguments import validate_args as _megatron_validate_args
6-
from transformers import AutoConfig
6+
7+
from slime.utils.model_config import load_hf_config
78

89
try:
910
from megatron.core.tokenizers.utils.build_tokenizer import vocab_size_with_padding as _vocab_size_with_padding
@@ -18,6 +19,8 @@
1819
_ALLGATHER_CP_DSA_ARCHITECTURES = {
1920
"DeepseekV32ForCausalLM",
2021
"GlmMoeDsaForCausalLM",
22+
"Glm5NextForCausalLM",
23+
"Glm5NextForConditionalGeneration",
2124
}
2225

2326

@@ -38,7 +41,7 @@ def _validate_allgather_cp_supported(args, hf_config=None):
3841

3942
raise ValueError(
4043
"--allgather-cp with --context-parallel-size > 1 is currently only supported for "
41-
"DSA attention models (DeepSeek-V3.2 and GLM-5.1). Non-DSA models still use the "
44+
"DSA attention models (DeepSeek-V3.2, GLM-5.1 and GLM5-next). Other models still use the "
4245
"zigzag CP layout and would silently scramble token order under allgather CP. "
4346
"Please remove --allgather-cp, set --context-parallel-size 1, or use a supported DSA model."
4447
)
@@ -204,7 +207,7 @@ def megatron_parse_args(extra_args_provider, skip_hf_validate=False):
204207

205208
hf_config = None
206209
if args.hf_checkpoint and not skip_hf_validate:
207-
hf_config = AutoConfig.from_pretrained(args.hf_checkpoint, trust_remote_code=True)
210+
hf_config = load_hf_config(args.hf_checkpoint, trust_remote_code=True)
208211
_hf_validate_args(args, hf_config)
209212

210213
if not skip_hf_validate:

0 commit comments

Comments
 (0)