Repository navigation
torchtitan: miles-side model packages, with GLM-4.7-Flash as the first - #3252
Merged
Zhichenzzz merged 15 commits intoOct 2, 2026
Merged
Conversation
Zhichenzzz
requested review from
Shi-Dong,
fzyzcjy,
maocheng23 and
yueming-yuan
as code owners
September 14, 2026 22:39
There was a problem hiding this comment.
Claude Code Review
This repository is configured for manual code reviews. Comment @claude review for a one-time review, or @claude review always to subscribe this PR to a review on every future push.
Tip: disable this comment in your organization's Code Review settings.
Zhichenzzz
added this pull request to stack #3250
September 14, 2026 22:46
Zhichenzzz
force-pushed
the
zhichen/titan-glm4-moe-lite
branch
from
September 14, 2026 23:19
fec4ad1 to
6eb0a0d
Compare
Zhichenzzz
requested review from
Rockdu,
guapisolo,
jybsuper and
yushengsu-thu
as code owners
September 15, 2026 00:59
Zhichenzzz
force-pushed
the
zhichen/titan-glm4-moe-lite
branch
from
September 15, 2026 00:59
615f557 to
0e30519
Compare
Zhichenzzz
force-pushed
the
zhichen/titan-glm4-moe-lite
branch
from
September 15, 2026 01:01
0e30519 to
b0a0a20
Compare
Zhichenzzz
force-pushed
the
zhichen/titan-glm4-moe-lite
branch
2 times, most recently
from
September 16, 2026 19:27
f0b7ad7 to
d18f46d
Compare
Zhichenzzz
force-pushed
the
zhichen/titan-glm4-moe-lite
branch
from
September 16, 2026 20:46
b527c4b to
afe55f0
Compare
Zhichenzzz
force-pushed
the
zhichen/titan-glm4-moe-lite
branch
from
September 17, 2026 23:30
afe55f0 to
708585c
Compare
Zhichenzzz
force-pushed
the
zhichen/titan-glm4-moe-lite
branch
from
September 17, 2026 23:46
708585c to
d8997ef
Compare
Zhichenzzz
force-pushed
the
zhichen/titan-glm4-moe-lite
branch
from
September 17, 2026 23:52
d8997ef to
88f8b95
Compare
Zhichenzzz
force-pushed
the
zhichen/titan-glm4-moe-lite
branch
from
September 18, 2026 03:27
434f4f4 to
1ed7ecf
Compare
Zhichenzzz
force-pushed
the
zhichen/titan-glm4-moe-lite
branch
from
September 18, 2026 03:44
1ed7ecf to
626b0bf
Compare
Zhichenzzz
force-pushed
the
zhichen/titan-glm4-moe-lite
branch
from
September 18, 2026 04:48
626b0bf to
4425bf9
Compare
Zhichenzzz
force-pushed
the
zhichen/titan-glm4-moe-lite
branch
from
September 25, 2026 19:57
4425bf9 to
ee64d82
Compare
resolve_model_spec only knew torchtitan.models.<name>, so a model torchtitan does not ship had nowhere to live. It now imports miles.backends.torchtitan_utils.models.<name> first and falls back to torchtitan.models.<name>; both roots expose the same model_registry(flavor, attn_backend) -> ModelSpec, so nothing downstream distinguishes the two. A ModuleNotFoundError for anything other than the package being looked up still propagates, so a broken import inside a model package is not misreported as an unknown name.
… config SGLang's DeepSeek family loads q_a_proj and kv_a_proj_with_mqa into one fused parameter and writes it only when both halves arrive in the same load call, so miles keeps the pair in one bucket through get_hf_atomic_update_groups(). The torchtitan bridge asked the torchtitan model config for q_lora_rank, which DeepSeek-V3-shaped configs keep on layers[0].attention, not at the top level; the lookup returned None, no group was registered, and whichever layer's pair straddled a bucket boundary was silently never updated in the engine. The HF config carries q_lora_rank for every MLA model, so read it there. Found by the GLM-4.7-Flash case: layer 10's fused_qkv_a_proj_with_mqa failed the post-update weight check while every other tensor matched.
GLM-4.7-Flash (HF glm4_moe_lite) is DeepSeek-V3's layer with different sizes: MLA with q_lora_rank 768 / kv_lora_rank 512 / nope 192 / rope 64 / v 256, a sigmoid top-4 router with the correction bias, route scale 1.8 with normalisation, one shared expert, a dense first layer, RoPE theta 1e6 with no YaRN. Its checkpoint uses DeepSeek-V3's parameter names verbatim. The package therefore builds the layer list with torchtitan's DeepSeek-V3 helpers, sets the GLM dimensions, and reuses parallelize_deepseekv3 and DeepSeekV3StateDictAdapter unchanged. The checkpoint's MTP layer (layers.47) is not built: the HF load only requests the keys the model's own to_hf() produces, so the extra tensors are never read, and the weight stream to SGLang does not carry them. The fast test pins the flavor to the values in the model's config.json and checks the adapter's map covers every key pattern in the checkpoint index. The e2e case runs it colocated on 4 GPUs with expert parallelism and routing replay, against SGLang's own glm4_moe_lite implementation.
The export ran to_hf() over DTensor state, so the adapter split every GroupedExperts tensor into its rank-local experts and each of the resulting 8.8k per-expert HF tensors went through its own redistribute and its own broadcast in the owners pass. The grouped tensor is now gathered once per layer and split locally, so every rank holds the full expert set and the stream takes the uniform-keys fast path: 138 collectives instead of 8.8k. Dense weights keep the existing path, and the set of grouped keys is agreed across ranks first so pipeline stages issue the same number of collectives. GLM-4.7-Flash on 4x GB300 (TP2 EP2, colocated): update_weights_time 4.8-5.0 s -> 4.1-4.2 s with the post-update weight check clean.
Zhichenzzz
force-pushed
the
zhichen/titan-glm4-moe-lite
branch
from
September 25, 2026 20:11
ee64d82 to
406266e
Compare
yueming-yuan
approved these changes
Oct 2, 2026
…titan-glm4-moe-lite
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
ci-image-tag: pr-3171
Stacked on #3248 (which is stacked on #3171). Review only the top four commits.
What
A place for models torchtitan does not ship, and the first one: GLM-4.7-Flash.
--titan-model-name <name>now resolvesmiles.backends.torchtitan_utils.models.<name>beforetorchtitan.models.<name>. Both roots expose the samemodel_registry(flavor, attn_backend) -> ModelSpec, so nothing downstream ofresolve_model_specdistinguishes a miles package from a torchtitan one.Why this shape
torchtitan's own extension point is the
ModelSpec: model config,parallelize_fn,pipelining_fn,state_dict_adapter. Most new architectures are a recombination of blocks torchtitan already has (MLA/GQA attention, sigmoid or softmax token-choice router, grouped experts, shared experts), so a miles-side package is usually a flavor table plus amodel_registrypointing at an existingparallelize_fnand adapter. When the architecture genuinely differs, the package grows amodel.py, then astate_dict_adapter.py, then aparallelize.py, in that order.GLM-4.7-Flash
HF
glm4_moe_liteis DeepSeek-V3's layer with different sizes: MLA (q_lora 768 / kv_lora 512 / nope 192 / rope 64 / v 256), sigmoid top-4 router with the correction bias, route scale 1.8 with normalisation, one shared expert, a dense first layer, RoPE theta 1e6 without YaRN, 47 layers, vocab 154880. Its checkpoint uses DeepSeek-V3's parameter names verbatim, somodels/glm4_moe_lite/builds the layers with torchtitan's DeepSeek-V3 helpers and reusesparallelize_deepseekv3andDeepSeekV3StateDictAdapterunchanged.The checkpoint's MTP layer (
layers.47) is not built. The HF load requests only the keys the model'sto_hf()produces, so those tensors are never read, and the weight stream to SGLang does not carry them.A bug in #3171 this surfaced
SGLang's DeepSeek family loads
q_a_projandkv_a_proj_with_mqainto one fused parameter and writes it only when both halves arrive in the same load call; miles keeps the pair in one bucket throughget_hf_atomic_update_groups(). The torchtitan bridge asked the torchtitan model config forq_lora_rank, which DeepSeek-V3-shaped configs keep onlayers[0].attention, so it readNone, registered no group, and whichever layer's pair straddled a bucket boundary was silently never updated in the engine. The first GLM run failed the post-update weight check on exactly one tensor (layers.10...fused_qkv_a_proj_with_mqa). The bridge now readsq_lora_rankfrom the HF config. Separate commit.Tests
config.json, and the adapter's map must cover every key pattern in the checkpoint'smodel.safetensors.index.json; miles-first resolution and the two-root error message.tests/e2e/torchtitan/test_glm47_flash_tp_ep_r3.py, colocated on 8 GPUs, TP2 EP4 with 4-GPU engines, routing replay, SGLang's ownglm4_moe_lite. SGLang attention is pinned to triton: flashinfer's SM100 prefill kernel has no head_dim 256 instantiation (MLA's 192 + 64 query head pads to it).Validated on 4x GB300 (run: https://wandb.ai/miles_training/backend_unify/runs/1m82oxpk): weight check clean on both syncs, 2 rollouts, 2 training steps. That pod's 626 GiB cgroup cannot hold the 4-rank CPU offload of a 30B-A3B trainer, so the run there used
--no-offload-trainwith--sglang-mem-fraction-static 0.35; the committed case keeps the default colocate offload, which fits the 2 TB H200 CI nodes.Docs
docs/user-guide/training-backend.md: the model lookup order, the Limits warning, and a "Going deeper: a model torchtitan does not ship" section.Megatron vs torchtitan on GLM-4.7-Flash, same setting
4x GB300, colocated, TP2 EP2, routing replay, 8k context (4096 prompt + 4096 response), 32 samples per step,
--use-dynamic-batch-size --max-tokens-per-gpu 8192on both, trainer kept on GPU, the same SGLang configuration (triton attention and MoE runner,mem_fraction_static 0.35). Steady-state steps 1-5, 6 rollouts each. Rollout time is the same on both (42-48 s), so the table is the training side.torchtitan's dynamic batching needed no code: the shared
get_batchalready packs samples into one row with per-sample position ids, and torchtitan'spost_dataloading_processbuilds the packed-document mask from those positions. The remaining gap is kernels (TE fused attention and grouped GEMM against flex attention and torch grouped_mm) and 2 s of weight sync.Runs: torchtitan, Megatron.
The same comparison on 8x H200
TP2 EP4, colocated, two 4-GPU SGLang engines (20 attention heads do not divide by 8), otherwise the same flags as above. Steady-state steps 1-5.
Runs: torchtitan group
cmp_glm47_h200_titan_tp2ep4_r3_8k, Megatron groupcmp_glm47_h200_megatron_tp2ep4_r3_8k.What was tried to close the gap, and what stuck
compile.enable=True(per-block torch.compile, R3 router kept eager)flex_flash)head_dim=256 does not support block sparsityvarlenattention (torch 2.13 routes to FA4)On Blackwell, MLA's 192 + 64 query head is the wall for every flex/FA path torchtitan exposes; Megatron's advantage on this model is TE fused attention and grouped GEMM, which support head_dim 256.