Skip to content

Commit a38eef8

Browse files
committed
feat(model): add GLM-5.3-Flash support
Signed-off-by: Hollow Man <hollowman@opensuse.org>
1 parent 1a45bc2 commit a38eef8

16 files changed

Lines changed: 1576 additions & 242 deletions

File tree

‎src/megatron/bridge/models/__init__.py‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,11 @@
7373
GLM45Bridge,
7474
GLM47FlashBridge,
7575
)
76+
from megatron.bridge.models.glm5next import (
77+
GLM53FlashBridge,
78+
GLM53FlashModel,
79+
GLM53FlashModelProvider,
80+
)
7681
from megatron.bridge.models.glm_moe_dsa import (
7782
GLM5Bridge,
7883
)
@@ -236,6 +241,9 @@
236241
"GLM45Bridge",
237242
"GLM47FlashBridge",
238243
"GLM5Bridge",
244+
"GLM53FlashBridge",
245+
"GLM53FlashModel",
246+
"GLM53FlashModelProvider",
239247
"GLM45VBridge",
240248
"GLM45VModelProvider",
241249
"GPTModelProvider",

‎src/megatron/bridge/models/conversion/__init__.py‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
FusedExpertMapping,
2323
FusedGatedExpertMapping,
2424
GatedMLPMapping,
25+
HCAlphaMapping,
26+
InitOnlyMapping,
2527
MegatronParamMapping,
2628
QKVMapping,
2729
ReplicatedMapping,
@@ -43,5 +45,7 @@
4345
"ReplicatedMapping",
4446
"RowParallelMapping",
4547
"AutoMapping",
48+
"HCAlphaMapping",
49+
"InitOnlyMapping",
4650
"weights_verification_table",
4751
]

‎src/megatron/bridge/models/conversion/model_bridge.py‎

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -974,6 +974,35 @@ def mapping_registry(self):
974974
"""
975975
raise NotImplementedError("Subclass must implement mapping_registry method")
976976

977+
@staticmethod
978+
def _prefix_mapping_registry(
979+
registry: MegatronMappingRegistry, megatron_prefix: str, *extra: MegatronParamMapping
980+
) -> MegatronMappingRegistry:
981+
"""Re-key a text-model mapping registry for a VLM wrapper.
982+
983+
VLM bridges typically reuse a text bridge's ``mapping_registry`` but must
984+
re-prefix every Megatron parameter (e.g. ``embedding.*`` →
985+
``language_model.embedding.*``) because the language model is a submodule of
986+
the VLM wrapper. Concatenation/delegation mappings that hold a nested
987+
``_tp_mapping`` keep it in sync with the parent ``megatron_param``.
988+
989+
Args:
990+
registry: The text-model registry returned by ``super().mapping_registry()``.
991+
megatron_prefix: Prefix prepended to each ``megatron_param``.
992+
*extra: Additional mappings appended verbatim (e.g. replicated vision
993+
weights); they are NOT prefixed.
994+
995+
Returns:
996+
A new ``MegatronMappingRegistry`` with prefixed text mappings + extras.
997+
"""
998+
mappings = list(registry.mappings)
999+
for mapping in mappings:
1000+
mapping.megatron_param = megatron_prefix + mapping.megatron_param
1001+
nested = getattr(mapping, "_tp_mapping", None)
1002+
if nested is not None:
1003+
nested.megatron_param = mapping.megatron_param
1004+
return MegatronMappingRegistry(*mappings, *extra)
1005+
9771006
def _megatron_global_param_names_all_pp_ranks(
9781007
self, megatron_model: Union[MegatronModel, List[MegatronModel]]
9791008
) -> List[str]:

‎src/megatron/bridge/models/conversion/param_mapping.py‎

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3917,3 +3917,84 @@ def split_kv_weights(provider: TransformerConfig, kv: torch.Tensor) -> Tuple[tor
39173917
k = kv_reshaped[k_slice].reshape(-1, hidden_size)
39183918
v = kv_reshaped[v_slice].reshape(-1, hidden_size)
39193919
return k, v
3920+
3921+
3922+
class HCAlphaMapping(MegatronParamMapping):
3923+
"""Map a 3-element HF hyper-connection scale tensor to/from Megatron's
3924+
three separate ``alpha_pre`` / ``alpha_post`` / ``alpha_res`` parameters.
3925+
3926+
The HF checkpoint stores the three scaling coefficients stacked along dim 0
3927+
as ``<layer>.hc_{kind}_scale`` (shape ``[3]``); Megatron keeps them as three
3928+
independent ``nn.Parameter([1])`` tensors on a single module.
3929+
3930+
One :class:`HCAlphaMapping` is registered **per alpha parameter** (index 0, 1,
3931+
or 2). Import extracts element ``[index:index+1]`` from the HF scale tensor.
3932+
Only the ``index == 0`` (``alpha_pre``) mapping exports: it gathers all three
3933+
alphas from the same module and concatenates them back into the ``[3]`` tensor.
3934+
The ``index != 0`` mappings return ``{}`` on export (the primary already wrote
3935+
all three), so they exist only to drive the import side and suppress export
3936+
"no mapping found" warnings.
3937+
3938+
This serves both models with a single wrapped ``hyper_connection`` submodule
3939+
(e.g. GLM-5.3-Flash) and models with separate ``self_attention_hyper_connection`` /
3940+
``mlp_hyper_connection`` submodules (e.g. DeepSeek-V4); the caller chooses the
3941+
``megatron_param`` path accordingly.
3942+
"""
3943+
3944+
def __init__(self, megatron_param: str, hf_param: str, index: int):
3945+
super().__init__(megatron_param=megatron_param, hf_param=hf_param)
3946+
self._index = index
3947+
# index != 0 imports from the shared HF scale tensor but never exports
3948+
# (the index-0 mapping writes all three alphas). Its HF key collides with
3949+
# the index-0 key, so skip the strict hf_keys presence check.
3950+
self.allow_hf_name_mismatch = index != 0
3951+
3952+
def resolve(self, captures):
3953+
resolved_mg, resolved_hf = self._resolve_names(captures)
3954+
return HCAlphaMapping(resolved_mg, resolved_hf, self._index)
3955+
3956+
def hf_to_megatron(self, hf_weights, megatron_module):
3957+
attr = ("alpha_pre", "alpha_post", "alpha_res")[self._index]
3958+
return hf_weights[self._index : self._index + 1].to(getattr(megatron_module, attr).device)
3959+
3960+
def megatron_to_hf(self, megatron_weights, megatron_module):
3961+
if self._index != 0:
3962+
return {}
3963+
parts = []
3964+
for attr, weight in (
3965+
("alpha_pre", megatron_weights),
3966+
("alpha_post", getattr(megatron_module, "alpha_post", None)),
3967+
("alpha_res", getattr(megatron_module, "alpha_res", None)),
3968+
):
3969+
parts.append(self.broadcast_from_pp_rank(weight, cache_key=f"{self.hf_param}_{attr}"))
3970+
if parts[0] is None:
3971+
return {}
3972+
return {self.hf_param: torch.cat([self.maybe_dequantize(part).float() for part in parts])}
3973+
3974+
3975+
class InitOnlyMapping(MegatronParamMapping[torch.Tensor]):
3976+
"""Keep Megatron initialization for parameters that have no HF counterpart.
3977+
3978+
Used for hyper-connection / MTP parameters that MCore creates but the source
3979+
checkpoint does not store. The mapping declares a synthetic HF key ending in
3980+
``__init_only__`` (which never exists in any checkpoint) and returns ``None``
3981+
on import so the random init is preserved. Export is a no-op.
3982+
3983+
Bridges that want this behaviour should also short-circuit the HF state-dict
3984+
lookup for the synthetic key (e.g. in ``maybe_modify_loaded_hf_weight``).
3985+
"""
3986+
3987+
def __init__(self, megatron_param: str):
3988+
super().__init__(megatron_param=megatron_param, hf_param=megatron_param + ".__init_only__")
3989+
self.allow_hf_name_mismatch = True
3990+
3991+
def resolve(self, captures):
3992+
resolved_mg, _ = self._resolve_names(captures)
3993+
return InitOnlyMapping(resolved_mg)
3994+
3995+
def hf_to_megatron(self, hf_weights, megatron_module):
3996+
# hf_weights is None when the (synthetic) HF key is absent -> keep init.
3997+
return None
3998+
3999+
def megatron_to_hf(self, megatron_weights, megatron_module):
4000+
return {}

‎src/megatron/bridge/models/conversion/peft_bridge.py‎

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -368,10 +368,16 @@ def _split_qkv_linear_out_weight(
368368
"""Split a fused LoRA linear_out tensor for QKV adapters."""
369369

370370
model = megatron_model[0] if isinstance(megatron_model, list) else megatron_model
371-
# Pass the LoRA rank as feature_dim so split_qkv_weights doesn't
372-
# mistake it for an FP8 compressed hidden_size.
373-
feature_dim = linear_out_weight.shape[-1] if linear_out_weight.ndim == 2 else None
374-
q_out, k_out, v_out = split_qkv_weights(model.config, linear_out_weight, feature_dim=feature_dim)
371+
config = model.config
372+
if getattr(config, "kda_two_stage_gates", False):
373+
# Low-rank KDA gates leave a contiguous Q/K/V-only input projection.
374+
q_dim = config.linear_num_key_heads * config.linear_key_head_dim
375+
v_dim = config.linear_num_value_heads * config.linear_value_head_dim
376+
q_out, k_out, v_out = linear_out_weight.split((q_dim, q_dim, v_dim), dim=0)
377+
else:
378+
# LoRA rank is the feature width, not an FP8-compressed hidden size.
379+
feature_dim = linear_out_weight.shape[-1] if linear_out_weight.ndim == 2 else None
380+
q_out, k_out, v_out = split_qkv_weights(config, linear_out_weight, feature_dim=feature_dim)
375381
return {"q_proj": q_out, "k_proj": k_out, "v_proj": v_out}
376382

377383
def _split_gdn_in_proj_linear_out_weight(

‎src/megatron/bridge/models/deepseek/deepseek_v4_bridge.py‎

Lines changed: 24 additions & 127 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@
7676
AutoMapping,
7777
ColumnParallelMapping,
7878
GatedMLPMapping,
79-
MegatronParamMapping,
79+
HCAlphaMapping,
8080
ReplicatedMapping,
8181
)
8282
from megatron.bridge.models.hf_pretrained.causal_lm import PreTrainedCausalLM
@@ -236,93 +236,6 @@ def _dsv4_use_mxfp4_export(hf_param: str, weight: torch.Tensor, source_scale: to
236236
# ---------------------------------------------------------------------------
237237

238238

239-
class _HCAlphaMapping(MegatronParamMapping):
240-
"""Map Megatron's three scalar HC alpha parameters to/from the V4 checkpoint's
241-
3-element hc_*_scale tensor.
242-
243-
V4 checkpoint : layers.N.hc_attn_scale shape [3] = [alpha_pre, alpha_post, alpha_res]
244-
Megatron : three separate nn.Parameter([1]) tensors
245-
"""
246-
247-
def __init__(self, megatron_pre: str, megatron_post: str, megatron_res: str, hf_param: str):
248-
# We register under the alpha_pre path; the others are handled inside hf_to_megatron.
249-
super().__init__(megatron_param=megatron_pre, hf_param=hf_param)
250-
self._megatron_post = megatron_post
251-
self._megatron_res = megatron_res
252-
253-
@staticmethod
254-
def _resolve_single(pattern: str, captures) -> str:
255-
result = pattern
256-
ci = 0
257-
while "**" in result and ci < len(captures):
258-
result = result.replace("**", captures[ci], 1)
259-
ci += 1
260-
ci = 0
261-
while "*" in result and ci < len(captures):
262-
result = result.replace("*", captures[ci], 1)
263-
ci += 1
264-
return result
265-
266-
def resolve(self, captures):
267-
resolved_mg, resolved_hf = self._resolve_names(captures)
268-
resolved_post = self._resolve_single(self._megatron_post, captures)
269-
resolved_res = self._resolve_single(self._megatron_res, captures)
270-
return _HCAlphaMapping(
271-
megatron_pre=resolved_mg,
272-
megatron_post=resolved_post,
273-
megatron_res=resolved_res,
274-
hf_param=resolved_hf,
275-
)
276-
277-
def hf_to_megatron(self, hf_weights, megatron_module):
278-
# hf_weights is hc_*_scale [3]; we write alpha_pre here (index 0).
279-
# alpha_post and alpha_res are handled by their own mappings when registered.
280-
target = hf_weights.to(megatron_module.alpha_pre.device)
281-
return target[0:1]
282-
283-
def megatron_to_hf(self, megatron_weights, megatron_module):
284-
# megatron_weights is alpha_pre [1]; gather all 3 from the same module.
285-
# With PP > 1, megatron_module may be None on non-owning ranks,
286-
# so we broadcast alpha_post and alpha_res alongside alpha_pre.
287-
post_tensor = megatron_module.alpha_post.detach() if megatron_module is not None else None
288-
res_tensor = megatron_module.alpha_res.detach() if megatron_module is not None else None
289-
megatron_weights = self.broadcast_from_pp_rank(megatron_weights, cache_key=str(self.hf_param))
290-
post = self.broadcast_from_pp_rank(post_tensor, cache_key=str(self.hf_param) + "_post")
291-
res = self.broadcast_from_pp_rank(res_tensor, cache_key=str(self.hf_param) + "_res")
292-
if megatron_weights is None:
293-
return {}
294-
megatron_weights = self.maybe_dequantize(megatron_weights)
295-
return {self.hf_param: torch.cat([megatron_weights.float(), post.float(), res.float()])}
296-
297-
298-
class _HCAlphaSecondaryMapping(MegatronParamMapping):
299-
"""Secondary mapping for alpha_post (index=1) or alpha_res (index=2).
300-
301-
Import: extracts element [index] from the 3-element hc_*_scale tensor.
302-
Export: returns {} because the primary _HCAlphaMapping (alpha_pre) already
303-
exports all three alpha values together. This mapping just suppresses the
304-
"No mapping found" warning for the secondary Megatron params during export.
305-
"""
306-
307-
def __init__(self, megatron_param: str, hf_scale_param: str, index: int):
308-
super().__init__(megatron_param=megatron_param, hf_param=hf_scale_param)
309-
self._index = index
310-
self.allow_hf_name_mismatch = True # export is no-op; skip hf_keys check
311-
312-
def hf_to_megatron(self, hf_weights, megatron_module):
313-
attr = "alpha_post" if self._index == 1 else "alpha_res"
314-
target = hf_weights.to(getattr(megatron_module, attr).device)
315-
return target[self._index : self._index + 1]
316-
317-
def resolve(self, captures):
318-
resolved_mg, resolved_hf = self._resolve_names(captures)
319-
return _HCAlphaSecondaryMapping(resolved_mg, resolved_hf, self._index)
320-
321-
def megatron_to_hf(self, megatron_weights, megatron_module):
322-
# Already handled by the primary alpha_pre _HCAlphaMapping
323-
return {}
324-
325-
326239
class _ReplicatedOptional(ReplicatedMapping):
327240
"""ReplicatedMapping for CSA-optional weights (compressor / indexer).
328241
@@ -768,41 +681,36 @@ def mapping_registry(self) -> MegatronMappingRegistry: # noqa: C901
768681
),
769682
]
770683

771-
# HC alpha scalars need custom concatenation mapping (per-layer, both attn and ffn)
684+
# HC alpha scalars: one HCAlphaMapping per alpha (index 0 exports all three;
685+
# index 1/2 import only and suppress export "no mapping found" warnings).
772686
# These are wildcarded across all layers.
773687
mappings += [
774-
_HCAlphaMapping(
775-
megatron_pre="decoder.layers.*.self_attention_hyper_connection.alpha_pre",
776-
megatron_post="decoder.layers.*.self_attention_hyper_connection.alpha_post",
777-
megatron_res="decoder.layers.*.self_attention_hyper_connection.alpha_res",
778-
hf_param="layers.*.hc_attn_scale",
779-
),
780-
_HCAlphaMapping(
781-
megatron_pre="decoder.layers.*.mlp_hyper_connection.alpha_pre",
782-
megatron_post="decoder.layers.*.mlp_hyper_connection.alpha_post",
783-
megatron_res="decoder.layers.*.mlp_hyper_connection.alpha_res",
784-
hf_param="layers.*.hc_ffn_scale",
688+
HCAlphaMapping(
689+
"decoder.layers.*.self_attention_hyper_connection.alpha_pre",
690+
"layers.*.hc_attn_scale",
691+
0,
785692
),
786-
]
787-
788-
# HC alpha secondary: register alpha_post and alpha_res to suppress export warnings
789-
mappings += [
790-
_HCAlphaSecondaryMapping(
693+
HCAlphaMapping(
791694
"decoder.layers.*.self_attention_hyper_connection.alpha_post",
792695
"layers.*.hc_attn_scale",
793696
1,
794697
),
795-
_HCAlphaSecondaryMapping(
698+
HCAlphaMapping(
796699
"decoder.layers.*.self_attention_hyper_connection.alpha_res",
797700
"layers.*.hc_attn_scale",
798701
2,
799702
),
800-
_HCAlphaSecondaryMapping(
703+
HCAlphaMapping(
704+
"decoder.layers.*.mlp_hyper_connection.alpha_pre",
705+
"layers.*.hc_ffn_scale",
706+
0,
707+
),
708+
HCAlphaMapping(
801709
"decoder.layers.*.mlp_hyper_connection.alpha_post",
802710
"layers.*.hc_ffn_scale",
803711
1,
804712
),
805-
_HCAlphaSecondaryMapping(
713+
HCAlphaMapping(
806714
"decoder.layers.*.mlp_hyper_connection.alpha_res",
807715
"layers.*.hc_ffn_scale",
808716
2,
@@ -896,34 +804,23 @@ def mapping_registry(self) -> MegatronMappingRegistry: # noqa: C901
896804
),
897805
]
898806

899-
# MTP HC alpha scalars
900-
mappings += [
901-
_HCAlphaMapping(
902-
megatron_pre=f"{mg_pfx}.mtp_model_layer.self_attention_hyper_connection.alpha_pre",
903-
megatron_post=f"{mg_pfx}.mtp_model_layer.self_attention_hyper_connection.alpha_post",
904-
megatron_res=f"{mg_pfx}.mtp_model_layer.self_attention_hyper_connection.alpha_res",
905-
hf_param=f"{ck_pfx}.hc_attn_scale",
906-
),
907-
_HCAlphaMapping(
908-
megatron_pre=f"{mg_pfx}.mtp_model_layer.mlp_hyper_connection.alpha_pre",
909-
megatron_post=f"{mg_pfx}.mtp_model_layer.mlp_hyper_connection.alpha_post",
910-
megatron_res=f"{mg_pfx}.mtp_model_layer.mlp_hyper_connection.alpha_res",
911-
hf_param=f"{ck_pfx}.hc_ffn_scale",
912-
),
913-
]
914-
915-
# MTP HC alpha secondary: suppress export warnings for post/res
807+
# MTP HC alpha scalars (one HCAlphaMapping per alpha; index 0 exports all three)
916808
for _hc_mg_sub, _hc_hf_key in [
917809
("self_attention_hyper_connection", "hc_attn_scale"),
918810
("mlp_hyper_connection", "hc_ffn_scale"),
919811
]:
920812
mappings += [
921-
_HCAlphaSecondaryMapping(
813+
HCAlphaMapping(
814+
f"{mg_pfx}.mtp_model_layer.{_hc_mg_sub}.alpha_pre",
815+
f"{ck_pfx}.{_hc_hf_key}",
816+
0,
817+
),
818+
HCAlphaMapping(
922819
f"{mg_pfx}.mtp_model_layer.{_hc_mg_sub}.alpha_post",
923820
f"{ck_pfx}.{_hc_hf_key}",
924821
1,
925822
),
926-
_HCAlphaSecondaryMapping(
823+
HCAlphaMapping(
927824
f"{mg_pfx}.mtp_model_layer.{_hc_mg_sub}.alpha_res",
928825
f"{ck_pfx}.{_hc_hf_key}",
929826
2,

0 commit comments

Comments
 (0)