Skip to content

Commit 7dbd1da

Browse files
Address review: refuse a Megatron-built GLM-5 MTP, drop the KPool indexer export
- GLM-5's MTP is only copied from the source checkpoint; raise on every rank if Megatron built one instead of exporting it under mtp.* names, and drop the unused mtp.* rules. - Leave the KPool indexer parameters to the GLM-5.3-Flash PR that adds their rules. - Check the restored DSA K/V quantizers stay enabled (V is unused in the forward). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Signed-off-by: Keval Morabia <28916987+kevalmorabia97@users.noreply.github.com>
1 parent 3e9bdf9 commit 7dbd1da

4 files changed

Lines changed: 37 additions & 13 deletions

File tree

‎modelopt/torch/export/plugins/mcore_glm.py‎

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@
1616
"""Custom mappings from Megatron Core models to GLM-5.x Hugging Face models.
1717
1818
GLM-5 / GLM-5.2 (``glm_moe_dsa``) is DeepSeek-V3-style MLA with a DSA indexer. It keeps the MTP
19-
layer at HF index ``num_hidden_layers`` under the decoder's names.
19+
layer at HF index ``num_hidden_layers`` under the decoder's names; Megatron-Bridge does not build
20+
it, so the exporter copies it from the source checkpoint.
2021
"""
2122

2223
from .mcore_custom import (
@@ -37,8 +38,4 @@
3738
"indexer.linear_weights_proj": NameRemapping("model.layers.{}.self_attn.indexer.weights_proj."),
3839
"experts.linear_fc1": GroupedGatedMLPSlicing("model.layers.{}.mlp.experts.{{}}"),
3940
"experts.linear_fc2": GroupedMLPSlicing("model.layers.{}.mlp.experts.{{}}.down_proj"),
40-
"mtp.enorm": NameRemapping("model.layers.{}.enorm."),
41-
"mtp.hnorm": NameRemapping("model.layers.{}.hnorm."),
42-
"mtp.eh_proj": NameRemapping("model.layers.{}.eh_proj."),
43-
"mtp.final_layernorm": NameRemapping("model.layers.{}.shared_head.norm."),
4441
}

‎modelopt/torch/export/unified_export_megatron.py‎

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,11 @@ def __init__(
203203
self.all_rules = self._populate_rule_book()
204204
self.rules = self.all_rules[self.arch]
205205
self.all_mcore_mappings = all_mcore_hf_export_mapping[self.arch]
206+
if self.rules.get("mtp_in_decoder_layers", False) and language_model.config.mtp_num_layers:
207+
# Only the copy from the source checkpoint is supported (the bridge builds no MTP).
208+
raise NotImplementedError(
209+
f"Exporting a Megatron-built MTP for {self.arch} is not supported yet."
210+
)
206211
self.exclude_modules = []
207212
self.layer_config_dict = {}
208213

@@ -965,18 +970,11 @@ def _get_gated_delta_net_state_dict(self, layer, layer_id, is_mtp=False):
965970
self.rules["linear_attn.out_proj"](gdn.out_proj, layer_id, is_mtp=is_mtp)
966971

967972
def _get_dsa_indexer_state_dict(self, indexer, layer_id, is_mtp=False):
968-
"""Export the DSA kpool indexer of a sparse MLA layer."""
973+
"""Export the DSA indexer of a sparse MLA layer."""
969974
if "indexer.linear_wq_b" not in self.rules:
970975
raise NotImplementedError(f"No export rule for the DSA indexer of {self.arch}.")
971976
for name in ("linear_wq_b", "linear_wk", "k_norm", "linear_weights_proj"):
972977
self.rules[f"indexer.{name}"](getattr(indexer, name), layer_id, is_mtp=is_mtp)
973-
# KPool (GLM-5.3-Flash) only; plain DSA indexers (GLM-5.2) have no compression params.
974-
for name in ("index_kpool_compress_ape", "index_kpool_compress_gate"):
975-
param = getattr(indexer, name, None)
976-
if param is not None:
977-
self.rules[f"indexer.{name}"](
978-
param.detach().to(self.dtype), layer_id, is_mtp=is_mtp
979-
)
980978

981979
def _get_mamba_layer_state_dict(self, layer, layer_id, is_mtp=False):
982980
if not isinstance(layer.norm, IdentityOp):

‎tests/gpu_megatron/torch/export/test_unified_export_megatron.py‎

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -977,6 +977,30 @@ def test_mtp_state_dict_index_file(tmp_path):
977977
assert "mtp*" in exporter.exclude_modules
978978

979979

980+
def _test_live_decoder_mtp_export_rejected(model_dir, rank, size):
981+
model = get_mcore_gpt_model(
982+
tensor_model_parallel_size=size,
983+
pipeline_model_parallel_size=1,
984+
initialize_megatron=True,
985+
num_layers=2,
986+
hidden_size=64,
987+
num_attention_heads=4,
988+
vocab_size=128,
989+
max_sequence_length=32,
990+
mtp_num_layers=1,
991+
).cuda()
992+
with pytest.raises(NotImplementedError, match="Megatron-built MTP"):
993+
GPTModelExporter(model, str(model_dir))
994+
995+
996+
def test_live_decoder_mtp_export_rejected(dist_workers_size_1, tmp_path):
997+
"""GLM-5 MTP is only copied from the source; a Megatron-built one must not export misnamed."""
998+
transformers.GlmMoeDsaConfig(
999+
num_hidden_layers=2, architectures=["GlmMoeDsaForCausalLM"]
1000+
).save_pretrained(tmp_path)
1001+
dist_workers_size_1.run(partial(_test_live_decoder_mtp_export_rejected, tmp_path))
1002+
1003+
9801004
def test_mtp_state_dict_copies_decoder_mtp_layers(tmp_path):
9811005
"""GLM-5 keeps MTP as an extra decoder layer; copy it dequantized when Megatron did not build it."""
9821006
model_dir = tmp_path / "fake_glm5"

‎tests/gpu_megatron/torch/quantization/plugins/test_megatron.py‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1958,6 +1958,11 @@ def _test_dsa_kv_cache_quant_helper(tmp_path, rank, size):
19581958
if ".indexer." in name:
19591959
param.requires_grad_(False)
19601960
sharded_state_dict_test_helper(tmp_path, model, model_test, forward)
1961+
# The helper compares every restored tensor (incl. K/V amax), but V is unused in the forward
1962+
# pass, so also check both quantizers came back enabled.
1963+
restored = [m for m in model_test.modules() if isinstance(m, DSAttention)]
1964+
assert len(restored) == len(dsa_modules)
1965+
assert all(m.k_bmm_quantizer.is_enabled and m.v_bmm_quantizer.is_enabled for m in restored)
19611966

19621967

19631968
def test_dsa_kv_cache_quant(dist_workers_size_1, tmp_path):

0 commit comments

Comments
 (0)