Skip to content

Commit fb5ee38

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

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
@@ -85,6 +85,11 @@
8585
GLM45Bridge,
8686
GLM47FlashBridge,
8787
)
88+
from megatron.bridge.models.glm5next import (
89+
GLM53FlashBridge,
90+
GLM53FlashModel,
91+
GLM53FlashModelProvider,
92+
)
8893
from megatron.bridge.models.glm_moe_dsa import (
8994
GLM5Bridge,
9095
)
@@ -248,6 +253,9 @@
248253
"GLM45Bridge",
249254
"GLM47FlashBridge",
250255
"GLM5Bridge",
256+
"GLM53FlashBridge",
257+
"GLM53FlashModel",
258+
"GLM53FlashModelProvider",
251259
"GLM45VBridge",
252260
"GLM45VModelProvider",
253261
"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
@@ -1010,6 +1010,35 @@ def mapping_registry(self):
10101010
"""
10111011
raise NotImplementedError("Subclass must implement mapping_registry method")
10121012

1013+
@staticmethod
1014+
def _prefix_mapping_registry(
1015+
registry: MegatronMappingRegistry, megatron_prefix: str, *extra: MegatronParamMapping
1016+
) -> MegatronMappingRegistry:
1017+
"""Re-key a text-model mapping registry for a VLM wrapper.
1018+
1019+
VLM bridges typically reuse a text bridge's ``mapping_registry`` but must
1020+
re-prefix every Megatron parameter (e.g. ``embedding.*`` →
1021+
``language_model.embedding.*``) because the language model is a submodule of
1022+
the VLM wrapper. Concatenation/delegation mappings that hold a nested
1023+
``_tp_mapping`` keep it in sync with the parent ``megatron_param``.
1024+
1025+
Args:
1026+
registry: The text-model registry returned by ``super().mapping_registry()``.
1027+
megatron_prefix: Prefix prepended to each ``megatron_param``.
1028+
*extra: Additional mappings appended verbatim (e.g. replicated vision
1029+
weights); they are NOT prefixed.
1030+
1031+
Returns:
1032+
A new ``MegatronMappingRegistry`` with prefixed text mappings + extras.
1033+
"""
1034+
mappings = list(registry.mappings)
1035+
for mapping in mappings:
1036+
mapping.megatron_param = megatron_prefix + mapping.megatron_param
1037+
nested = getattr(mapping, "_tp_mapping", None)
1038+
if nested is not None:
1039+
nested.megatron_param = mapping.megatron_param
1040+
return MegatronMappingRegistry(*mappings, *extra)
1041+
10131042
def _megatron_global_param_names_all_pp_ranks(
10141043
self, megatron_model: Union[MegatronModel, List[MegatronModel]]
10151044
) -> List[str]:

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

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4340,3 +4340,84 @@ def split_kv_weights(provider: TransformerConfig, kv: torch.Tensor) -> Tuple[tor
43404340
k = kv_reshaped[k_slice].reshape(-1, hidden_size)
43414341
v = kv_reshaped[v_slice].reshape(-1, hidden_size)
43424342
return k, v
4343+
4344+
4345+
class HCAlphaMapping(MegatronParamMapping):
4346+
"""Map a 3-element HF hyper-connection scale tensor to/from Megatron's
4347+
three separate ``alpha_pre`` / ``alpha_post`` / ``alpha_res`` parameters.
4348+
4349+
The HF checkpoint stores the three scaling coefficients stacked along dim 0
4350+
as ``<layer>.hc_{kind}_scale`` (shape ``[3]``); Megatron keeps them as three
4351+
independent ``nn.Parameter([1])`` tensors on a single module.
4352+
4353+
One :class:`HCAlphaMapping` is registered **per alpha parameter** (index 0, 1,
4354+
or 2). Import extracts element ``[index:index+1]`` from the HF scale tensor.
4355+
Only the ``index == 0`` (``alpha_pre``) mapping exports: it gathers all three
4356+
alphas from the same module and concatenates them back into the ``[3]`` tensor.
4357+
The ``index != 0`` mappings return ``{}`` on export (the primary already wrote
4358+
all three), so they exist only to drive the import side and suppress export
4359+
"no mapping found" warnings.
4360+
4361+
This serves both models with a single wrapped ``hyper_connection`` submodule
4362+
(e.g. GLM-5.3-Flash) and models with separate ``self_attention_hyper_connection`` /
4363+
``mlp_hyper_connection`` submodules (e.g. DeepSeek-V4); the caller chooses the
4364+
``megatron_param`` path accordingly.
4365+
"""
4366+
4367+
def __init__(self, megatron_param: str, hf_param: str, index: int):
4368+
super().__init__(megatron_param=megatron_param, hf_param=hf_param)
4369+
self._index = index
4370+
# index != 0 imports from the shared HF scale tensor but never exports
4371+
# (the index-0 mapping writes all three alphas). Its HF key collides with
4372+
# the index-0 key, so skip the strict hf_keys presence check.
4373+
self.allow_hf_name_mismatch = index != 0
4374+
4375+
def resolve(self, captures):
4376+
resolved_mg, resolved_hf = self._resolve_names(captures)
4377+
return HCAlphaMapping(resolved_mg, resolved_hf, self._index)
4378+
4379+
def hf_to_megatron(self, hf_weights, megatron_module):
4380+
attr = ("alpha_pre", "alpha_post", "alpha_res")[self._index]
4381+
return hf_weights[self._index : self._index + 1].to(getattr(megatron_module, attr).device)
4382+
4383+
def megatron_to_hf(self, megatron_weights, megatron_module):
4384+
if self._index != 0:
4385+
return {}
4386+
parts = []
4387+
for attr, weight in (
4388+
("alpha_pre", megatron_weights),
4389+
("alpha_post", getattr(megatron_module, "alpha_post", None)),
4390+
("alpha_res", getattr(megatron_module, "alpha_res", None)),
4391+
):
4392+
parts.append(self.broadcast_from_pp_rank(weight, cache_key=f"{self.hf_param}_{attr}"))
4393+
if parts[0] is None:
4394+
return {}
4395+
return {self.hf_param: torch.cat([self.maybe_dequantize(part).float() for part in parts])}
4396+
4397+
4398+
class InitOnlyMapping(MegatronParamMapping[torch.Tensor]):
4399+
"""Keep Megatron initialization for parameters that have no HF counterpart.
4400+
4401+
Used for hyper-connection / MTP parameters that MCore creates but the source
4402+
checkpoint does not store. The mapping declares a synthetic HF key ending in
4403+
``__init_only__`` (which never exists in any checkpoint) and returns ``None``
4404+
on import so the random init is preserved. Export is a no-op.
4405+
4406+
Bridges that want this behaviour should also short-circuit the HF state-dict
4407+
lookup for the synthetic key (e.g. in ``maybe_modify_loaded_hf_weight``).
4408+
"""
4409+
4410+
def __init__(self, megatron_param: str):
4411+
super().__init__(megatron_param=megatron_param, hf_param=megatron_param + ".__init_only__")
4412+
self.allow_hf_name_mismatch = True
4413+
4414+
def resolve(self, captures):
4415+
resolved_mg, _ = self._resolve_names(captures)
4416+
return InitOnlyMapping(resolved_mg)
4417+
4418+
def hf_to_megatron(self, hf_weights, megatron_module):
4419+
# hf_weights is None when the (synthetic) HF key is absent -> keep init.
4420+
return None
4421+
4422+
def megatron_to_hf(self, megatron_weights, megatron_module):
4423+
return {}

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

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

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

379385
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
@@ -77,7 +77,7 @@
7777
AutoMapping,
7878
ColumnParallelMapping,
7979
GatedMLPMapping,
80-
MegatronParamMapping,
80+
HCAlphaMapping,
8181
ReplicatedMapping,
8282
)
8383
from megatron.bridge.models.hf_pretrained.causal_lm import PreTrainedCausalLM
@@ -243,93 +243,6 @@ def _dsv4_use_mxfp4_export(hf_param: str, weight: torch.Tensor, source_scale: to
243243
# ---------------------------------------------------------------------------
244244

245245

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

811-
# HC alpha scalars need custom concatenation mapping (per-layer, both attn and ffn)
724+
# HC alpha scalars: one HCAlphaMapping per alpha (index 0 exports all three;
725+
# index 1/2 import only and suppress export "no mapping found" warnings).
812726
# These are wildcarded across all layers.
813727
mappings += [
814-
_HCAlphaMapping(
815-
megatron_pre="decoder.layers.*.self_attention_hyper_connection.alpha_pre",
816-
megatron_post="decoder.layers.*.self_attention_hyper_connection.alpha_post",
817-
megatron_res="decoder.layers.*.self_attention_hyper_connection.alpha_res",
818-
hf_param="layers.*.hc_attn_scale",
819-
),
820-
_HCAlphaMapping(
821-
megatron_pre="decoder.layers.*.mlp_hyper_connection.alpha_pre",
822-
megatron_post="decoder.layers.*.mlp_hyper_connection.alpha_post",
823-
megatron_res="decoder.layers.*.mlp_hyper_connection.alpha_res",
824-
hf_param="layers.*.hc_ffn_scale",
728+
HCAlphaMapping(
729+
"decoder.layers.*.self_attention_hyper_connection.alpha_pre",
730+
"layers.*.hc_attn_scale",
731+
0,
825732
),
826-
]
827-
828-
# HC alpha secondary: register alpha_post and alpha_res to suppress export warnings
829-
mappings += [
830-
_HCAlphaSecondaryMapping(
733+
HCAlphaMapping(
831734
"decoder.layers.*.self_attention_hyper_connection.alpha_post",
832735
"layers.*.hc_attn_scale",
833736
1,
834737
),
835-
_HCAlphaSecondaryMapping(
738+
HCAlphaMapping(
836739
"decoder.layers.*.self_attention_hyper_connection.alpha_res",
837740
"layers.*.hc_attn_scale",
838741
2,
839742
),
840-
_HCAlphaSecondaryMapping(
743+
HCAlphaMapping(
744+
"decoder.layers.*.mlp_hyper_connection.alpha_pre",
745+
"layers.*.hc_ffn_scale",
746+
0,
747+
),
748+
HCAlphaMapping(
841749
"decoder.layers.*.mlp_hyper_connection.alpha_post",
842750
"layers.*.hc_ffn_scale",
843751
1,
844752
),
845-
_HCAlphaSecondaryMapping(
753+
HCAlphaMapping(
846754
"decoder.layers.*.mlp_hyper_connection.alpha_res",
847755
"layers.*.hc_ffn_scale",
848756
2,
@@ -936,34 +844,23 @@ def mapping_registry(self) -> MegatronMappingRegistry: # noqa: C901
936844
),
937845
]
938846

939-
# MTP HC alpha scalars
940-
mappings += [
941-
_HCAlphaMapping(
942-
megatron_pre=f"{mg_pfx}.mtp_model_layer.self_attention_hyper_connection.alpha_pre",
943-
megatron_post=f"{mg_pfx}.mtp_model_layer.self_attention_hyper_connection.alpha_post",
944-
megatron_res=f"{mg_pfx}.mtp_model_layer.self_attention_hyper_connection.alpha_res",
945-
hf_param=f"{ck_pfx}.hc_attn_scale",
946-
),
947-
_HCAlphaMapping(
948-
megatron_pre=f"{mg_pfx}.mtp_model_layer.mlp_hyper_connection.alpha_pre",
949-
megatron_post=f"{mg_pfx}.mtp_model_layer.mlp_hyper_connection.alpha_post",
950-
megatron_res=f"{mg_pfx}.mtp_model_layer.mlp_hyper_connection.alpha_res",
951-
hf_param=f"{ck_pfx}.hc_ffn_scale",
952-
),
953-
]
954-
955-
# MTP HC alpha secondary: suppress export warnings for post/res
847+
# MTP HC alpha scalars (one HCAlphaMapping per alpha; index 0 exports all three)
956848
for _hc_mg_sub, _hc_hf_key in [
957849
("self_attention_hyper_connection", "hc_attn_scale"),
958850
("mlp_hyper_connection", "hc_ffn_scale"),
959851
]:
960852
mappings += [
961-
_HCAlphaSecondaryMapping(
853+
HCAlphaMapping(
854+
f"{mg_pfx}.mtp_model_layer.{_hc_mg_sub}.alpha_pre",
855+
f"{ck_pfx}.{_hc_hf_key}",
856+
0,
857+
),
858+
HCAlphaMapping(
962859
f"{mg_pfx}.mtp_model_layer.{_hc_mg_sub}.alpha_post",
963860
f"{ck_pfx}.{_hc_hf_key}",
964861
1,
965862
),
966-
_HCAlphaSecondaryMapping(
863+
HCAlphaMapping(
967864
f"{mg_pfx}.mtp_model_layer.{_hc_mg_sub}.alpha_res",
968865
f"{ck_pfx}.{_hc_hf_key}",
969866
2,

0 commit comments

Comments
 (0)