Skip to content

Commit b70fd37

Browse files
committed
fix(model): handle GEMMA4_CONVERSION_MODE=text for gemma4 MoE checkpoints
Signed-off-by: Josephasafg <ajgard7@gmail.com>
1 parent c7e0ac6 commit b70fd37

3 files changed

Lines changed: 89 additions & 41 deletions

File tree

‎src/megatron/bridge/models/gemma/gemma4_bridge.py‎

Lines changed: 41 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -504,71 +504,75 @@ def _hf_layer_prefix(self) -> str:
504504
"""Text-only CausalLM: weights at ``model.*``; override in VL subclass."""
505505
return "model."
506506

507-
def _moe_mapping_registry(self) -> MegatronMappingRegistry:
507+
def _moe_mapping_registry(self, megatron_prefix: str = "") -> MegatronMappingRegistry:
508508
"""Parameter mappings for the MoE variant."""
509+
mp = megatron_prefix
510+
hp = self._hf_layer_prefix()
509511
param_mappings = {
510-
"embedding.word_embeddings.weight": "model.embed_tokens.weight",
511-
"decoder.final_layernorm.weight": "model.norm.weight",
512-
"decoder.layers.*.self_attention.linear_qkv.layer_norm_weight": "model.layers.*.input_layernorm.weight",
513-
"decoder.layers.*.self_attention.q_layernorm.weight": "model.layers.*.self_attn.q_norm.weight",
514-
"decoder.layers.*.self_attention.k_layernorm.weight": "model.layers.*.self_attn.k_norm.weight",
515-
"decoder.layers.*.self_attention.linear_proj.weight": "model.layers.*.self_attn.o_proj.weight",
516-
"decoder.layers.*.self_attention.linear_proj.post_layernorm.weight": (
517-
"model.layers.*.post_attention_layernorm.weight"
512+
f"{mp}embedding.word_embeddings.weight": f"{hp}embed_tokens.weight",
513+
f"{mp}decoder.final_layernorm.weight": f"{hp}norm.weight",
514+
f"{mp}decoder.layers.*.self_attention.linear_qkv.layer_norm_weight": (
515+
f"{hp}layers.*.input_layernorm.weight"
516+
),
517+
f"{mp}decoder.layers.*.self_attention.q_layernorm.weight": f"{hp}layers.*.self_attn.q_norm.weight",
518+
f"{mp}decoder.layers.*.self_attention.k_layernorm.weight": f"{hp}layers.*.self_attn.k_norm.weight",
519+
f"{mp}decoder.layers.*.self_attention.linear_proj.weight": f"{hp}layers.*.self_attn.o_proj.weight",
520+
f"{mp}decoder.layers.*.self_attention.linear_proj.post_layernorm.weight": (
521+
f"{hp}layers.*.post_attention_layernorm.weight"
518522
),
519-
"decoder.layers.*.pre_mlp_layernorm.weight": "model.layers.*.pre_feedforward_layernorm_2.weight",
520-
"decoder.layers.*.mlp.shared_experts.linear_fc2.weight": "model.layers.*.mlp.down_proj.weight",
521-
"decoder.layers.*.mlp.post_shared_expert_layernorm.weight": (
522-
"model.layers.*.post_feedforward_layernorm_1.weight"
523+
f"{mp}decoder.layers.*.pre_mlp_layernorm.weight": f"{hp}layers.*.pre_feedforward_layernorm_2.weight",
524+
f"{mp}decoder.layers.*.mlp.shared_experts.linear_fc2.weight": f"{hp}layers.*.mlp.down_proj.weight",
525+
f"{mp}decoder.layers.*.mlp.post_shared_expert_layernorm.weight": (
526+
f"{hp}layers.*.post_feedforward_layernorm_1.weight"
523527
),
524-
"decoder.layers.*.mlp.router.weight": "model.layers.*.router.proj.weight",
528+
f"{mp}decoder.layers.*.mlp.router.weight": f"{hp}layers.*.router.proj.weight",
525529
}
526530

527531
mapping_list = [AutoMapping(megatron_param=m, hf_param=h) for m, h in param_mappings.items()]
528532
mapping_list.extend(
529533
[
530534
_Gemma4QKVMapping(
531-
megatron_param="decoder.layers.*.self_attention.linear_qkv.weight",
532-
q="model.layers.*.self_attn.q_proj.weight",
533-
k="model.layers.*.self_attn.k_proj.weight",
534-
v="model.layers.*.self_attn.v_proj.weight",
535+
megatron_param=f"{mp}decoder.layers.*.self_attention.linear_qkv.weight",
536+
q=f"{hp}layers.*.self_attn.q_proj.weight",
537+
k=f"{hp}layers.*.self_attn.k_proj.weight",
538+
v=f"{hp}layers.*.self_attn.v_proj.weight",
535539
),
536540
GatedMLPMapping(
537-
megatron_param="decoder.layers.*.mlp.shared_experts.linear_fc1.weight",
538-
gate="model.layers.*.mlp.gate_proj.weight",
539-
up="model.layers.*.mlp.up_proj.weight",
541+
megatron_param=f"{mp}decoder.layers.*.mlp.shared_experts.linear_fc1.weight",
542+
gate=f"{hp}layers.*.mlp.gate_proj.weight",
543+
up=f"{hp}layers.*.mlp.up_proj.weight",
540544
),
541545
FusedGatedExpertMapping(
542-
megatron_param="decoder.layers.*.mlp.experts.linear_fc1.weight*",
543-
hf_param="model.layers.*.experts.gate_up_proj",
546+
megatron_param=f"{mp}decoder.layers.*.mlp.experts.linear_fc1.weight*",
547+
hf_param=f"{hp}layers.*.experts.gate_up_proj",
544548
),
545549
FusedExpertMapping(
546-
megatron_param="decoder.layers.*.mlp.experts.linear_fc2.weight*",
547-
hf_param="model.layers.*.experts.down_proj",
550+
megatron_param=f"{mp}decoder.layers.*.mlp.experts.linear_fc2.weight*",
551+
hf_param=f"{hp}layers.*.experts.down_proj",
548552
),
549553
ReplicatedMapping(
550-
megatron_param="decoder.layers.*.layer_scalar",
551-
hf_param="model.layers.*.layer_scalar",
554+
megatron_param=f"{mp}decoder.layers.*.layer_scalar",
555+
hf_param=f"{hp}layers.*.layer_scalar",
552556
),
553557
ReplicatedMapping(
554-
megatron_param="decoder.layers.*.mlp.router.per_expert_scale",
555-
hf_param="model.layers.*.router.per_expert_scale",
558+
megatron_param=f"{mp}decoder.layers.*.mlp.router.per_expert_scale",
559+
hf_param=f"{hp}layers.*.router.per_expert_scale",
556560
),
557561
ReplicatedMapping(
558-
megatron_param="decoder.layers.*.mlp.router.scale",
559-
hf_param="model.layers.*.router.scale",
562+
megatron_param=f"{mp}decoder.layers.*.mlp.router.scale",
563+
hf_param=f"{hp}layers.*.router.scale",
560564
),
561565
ReplicatedMapping(
562-
megatron_param="decoder.layers.*.pre_shared_expert_layernorm.weight",
563-
hf_param="model.layers.*.pre_feedforward_layernorm.weight",
566+
megatron_param=f"{mp}decoder.layers.*.pre_shared_expert_layernorm.weight",
567+
hf_param=f"{hp}layers.*.pre_feedforward_layernorm.weight",
564568
),
565569
ReplicatedMapping(
566-
megatron_param="decoder.layers.*.mlp.post_moe_layernorm.weight",
567-
hf_param="model.layers.*.post_feedforward_layernorm_2.weight",
570+
megatron_param=f"{mp}decoder.layers.*.mlp.post_moe_layernorm.weight",
571+
hf_param=f"{hp}layers.*.post_feedforward_layernorm_2.weight",
568572
),
569573
ReplicatedMapping(
570-
megatron_param="decoder.layers.*.post_ffn_layernorm.weight",
571-
hf_param="model.layers.*.post_feedforward_layernorm.weight",
574+
megatron_param=f"{mp}decoder.layers.*.post_ffn_layernorm.weight",
575+
hf_param=f"{hp}layers.*.post_feedforward_layernorm.weight",
572576
),
573577
]
574578
)

‎src/megatron/bridge/models/gemma_vl/gemma4_vl_bridge.py‎

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,14 @@
2121
2222
AutoBridge.from_hf_pretrained("google/gemma-4-E4B-it")
2323
└─ Gemma4VLBridge (registered for Gemma4ForConditionalGeneration)
24-
├─ provider_bridge() text mode → Gemma4DenseProvider (pretraining)
25-
│ auto/vl → Gemma4DenseVLProvider (full VL)
26-
└─ mapping_registry() Dense → _dense_vl_mapping_registry()
27-
MoE → _moe_vl_mapping_registry()
24+
├─ provider_bridge() Dense: text mode → Gemma4DenseProvider (pretraining)
25+
│ auto/vl → Gemma4DenseVLProvider (full VL)
26+
│ MoE: text mode → Gemma4ModelProvider (text-only training)
27+
│ auto/vl → Gemma4VLModelProvider (full VL)
28+
└─ mapping_registry() Dense: text → _dense_mapping_registry("")
29+
vl → _dense_vl_mapping_registry()
30+
MoE: text → _moe_mapping_registry()
31+
vl → _moe_vl_mapping_registry()
2832
"""
2933

3034
import os
@@ -91,6 +95,8 @@ def provider_bridge(
9195
return self._build_dense_vl_provider(hf_config, text_config, vision_config)
9296

9397
self._is_dense = False
98+
if self._conversion_mode() == "text":
99+
return self._build_moe_provider(text_config)
94100

95101
provider_kwargs = self.hf_config_to_provider_kwargs(text_config)
96102
provider = Gemma4VLModelProvider(**provider_kwargs)
@@ -226,6 +232,8 @@ def mapping_registry(self) -> MegatronMappingRegistry:
226232
if self._conversion_mode() == "text":
227233
return self._dense_mapping_registry(megatron_prefix="")
228234
return self._dense_vl_mapping_registry()
235+
if self._conversion_mode() == "text":
236+
return self._moe_mapping_registry()
229237
return self._moe_vl_mapping_registry()
230238

231239
def _dense_vl_mapping_registry(self) -> MegatronMappingRegistry:

‎tests/unit_tests/models/gemma_vl/test_gemma4_vl_bridge.py‎

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -735,6 +735,26 @@ def test_vision_config_set(self, bridge, mock_hf_pretrained_moe):
735735
assert p.vision_config is mock_hf_pretrained_moe.config.vision_config
736736
assert p.text_config is mock_hf_pretrained_moe.config.text_config
737737

738+
def test_text_mode_returns_text_moe_provider(self, bridge, mock_hf_pretrained_moe, monkeypatch):
739+
monkeypatch.setenv("GEMMA4_CONVERSION_MODE", "text")
740+
p = bridge.provider_bridge(mock_hf_pretrained_moe)
741+
assert type(p) is Gemma4ModelProvider
742+
743+
def test_text_mode_moe_provider_is_bf16(self, bridge, mock_hf_pretrained_moe, monkeypatch):
744+
monkeypatch.setenv("GEMMA4_CONVERSION_MODE", "text")
745+
p = bridge.provider_bridge(mock_hf_pretrained_moe)
746+
assert p.bf16 is True
747+
assert p.params_dtype == torch.bfloat16
748+
749+
def test_text_mode_moe_provider_config(self, bridge, mock_hf_pretrained_moe, monkeypatch):
750+
monkeypatch.setenv("GEMMA4_CONVERSION_MODE", "text")
751+
p = bridge.provider_bridge(mock_hf_pretrained_moe)
752+
assert p.num_moe_experts == 128
753+
assert p.moe_router_topk == 8
754+
assert p.moe_ffn_hidden_size == 704
755+
assert p.global_head_dim == 512
756+
assert p.num_global_key_value_heads == 2
757+
738758

739759
class TestGemma4VLBridgeProviderBridgeDense:
740760
def test_accepts_dense_with_per_layer_inputs(self, bridge, mock_hf_pretrained_dense):
@@ -947,6 +967,22 @@ def test_uses_language_model_prefix_for_vl(self, bridge, mock_hf_config_moe):
947967
lm_keys = [n for n in names if "layers" in n and "vision" not in n and "audio" not in n]
948968
assert any("language_model" in n for n in lm_keys)
949969

970+
def test_moe_text_mode_registry_targets_text_model_from_vl_checkpoint(
971+
self, bridge, mock_hf_config_moe, monkeypatch
972+
):
973+
"""MoE text mode: unprefixed Megatron params sourced from model.language_model.* HF weights."""
974+
monkeypatch.setenv("GEMMA4_CONVERSION_MODE", "text")
975+
bridge.hf_config = mock_hf_config_moe
976+
registry = bridge.mapping_registry()
977+
978+
megatron_params = [str(m.megatron_param) for m in registry.mappings if hasattr(m, "megatron_param")]
979+
assert "embedding.word_embeddings.weight" in megatron_params
980+
assert all(not n.startswith("language_model.") for n in megatron_params)
981+
assert not any("vision_tower" in n or "audio_tower" in n for n in megatron_params)
982+
983+
hf_targets = self._collect_hf_targets(registry)
984+
assert all(n.startswith("model.language_model.") for n in hf_targets)
985+
950986
def test_dense_vl_audio_tower_replicated_mappings(self, bridge, mock_hf_config_dense, monkeypatch):
951987
monkeypatch.setenv("GEMMA4_CONVERSION_MODE", "vl")
952988
bridge.hf_config = mock_hf_config_dense

0 commit comments

Comments
 (0)