Skip to content

Commit 178e185

Browse files
authored
add gemma4 model (#4725)
1 parent 1e3643f commit 178e185

7 files changed

Lines changed: 704 additions & 0 deletions

File tree

paddleformers/datasets/template/template.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1004,3 +1004,18 @@ def _get_gpt_oss_prefix():
10041004
chat_sep="<|assistant|>\n",
10051005
mm_plugin=get_mm_plugin(name="glm_ocr", image_token="<|image|>"),
10061006
)
1007+
1008+
1009+
register_template(
1010+
name="gemma4",
1011+
format_user=StringFormatter(slots=["<|turn>user\n{{content}}<turn|>\n<|turn>model\n"]),
1012+
format_assistant=StringFormatter(slots=["{{content}}"]),
1013+
format_system=StringFormatter(slots=["<|turn>system\n{{content}}<turn|>\n"]),
1014+
format_observation=StringFormatter(slots=["<|turn>tool\n{{content}}<turn|>\n<|turn>model\n"]),
1015+
format_prefix=EmptyFormatter(slots=[{"bos_token"}]),
1016+
chat_sep="<turn|>\n",
1017+
suffix=["<turn|>"],
1018+
stop_words=["<turn|>"],
1019+
thought_words=("<|channel>thought\n", "\n<channel|>"),
1020+
template_class=Llama2Template,
1021+
)

paddleformers/transformers/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -348,6 +348,9 @@
348348
],
349349
"glm_ocr.processor": ["Glm46VProcessor"],
350350
"glm_ocr.image_processor": ["Glm46VImageProcessor"],
351+
"gemma4_moe.configuration": ["Gemma4MoeConfig"],
352+
"gemma4_moe.modeling": ["Gemma4MoeForCausalLM"],
353+
"gemma4_moe": [],
351354
"phi4.configuration": ["Phi4Config"],
352355
"phi4.modeling": ["Phi4Model", "Phi4ForCausalLM"],
353356
"phi4.tokenizer": ["Phi4Tokenizer"],
@@ -430,6 +433,7 @@
430433
from .phi3 import *
431434
from .gemma3_text import *
432435
from .glm_ocr import *
436+
from .gemma4_moe import *
433437
from .phi4 import *
434438
else:
435439
sys.modules[__name__] = _LazyModule(

paddleformers/transformers/auto/configuration.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,9 @@
6464
("glm_ocr", "GlmOcrConfig"),
6565
("qwen3_5", "Qwen3_5Config"),
6666
("qwen3_5_moe", "Qwen3_5MoEConfig"),
67+
# TODO(VL): When Gemma4 VL is implemented, "gemma4" should point to Gemma4Config (VL wrapper)
68+
("gemma4_text", "Gemma4MoeConfig"),
69+
("gemma4", "Gemma4MoeConfig"), # Temporary: no standalone text ckpt, extract text_config in from_dict
6770
("phi4", "Phi4Config"),
6871
("phi4flash", "Phi4Config"),
6972
]
@@ -95,6 +98,9 @@
9598
("minicpm", "MiniCPM"),
9699
("qwen3_5_moe", "Qwen3_5MoEForConditionalGeneration"),
97100
("qwen3_5", "Qwen3_5ForConditionalGeneration"),
101+
("gemma4_moe", "Gemma4MoeForCausalLM"),
102+
("gemma4_text", "Gemma4MoeForCausalLM"),
103+
("gemma4", "Gemma4MoeForCausalLM"),
98104
("phi4", "Phi4ForCausalLM"),
99105
("phi4flash", "Phi4ForCausalLM"),
100106
]
@@ -110,6 +116,9 @@
110116
("qwen2_5_vl_text", "qwen2_5_vl"),
111117
("qwen3_vl_text", "qwen3_vl"),
112118
("qwen3_vl_moe_text", "qwen3_vl_moe"),
119+
# TODO(VL): Remove these when Gemma4 VL module (gemma4/) is created
120+
("gemma4_text", "gemma4_moe"),
121+
("gemma4", "gemma4_moe"),
113122
("phi4flash", "phi4"),
114123
]
115124
)

paddleformers/transformers/auto/modeling.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@
8080
("Phi3", "phi3"),
8181
("Phi4", "phi4"),
8282
("Gemma3", "gemma3_text"),
83+
("Gemma4Moe", "gemma4_moe"),
8384
("Glm4vMoe", "glm4v_moe"),
8485
("GlmOcr", "glm_ocr"),
8586
]
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
import sys
15+
from typing import TYPE_CHECKING
16+
17+
from ...utils.lazy_import import _LazyModule
18+
19+
import_structure = {
20+
"configuration": ["Gemma4MoeConfig"],
21+
"modeling": [
22+
"Gemma4MoeForCausalLM",
23+
],
24+
}
25+
26+
if TYPE_CHECKING:
27+
from .configuration import *
28+
from .modeling import *
29+
else:
30+
sys.modules[__name__] = _LazyModule(
31+
__name__,
32+
globals()["__file__"],
33+
import_structure,
34+
module_spec=__spec__,
35+
)
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
# Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
from ..configuration_utils import PretrainedConfig
16+
17+
18+
class Gemma4MoeConfig(PretrainedConfig):
19+
"""Gemma4 26B-A4B text-only MoE model configuration.
20+
21+
NOTE: Gemma4 currently only has VL checkpoints (Gemma4ForConditionalGeneration),
22+
no standalone text-only checkpoint exists. The config.json has top-level
23+
model_type="gemma4" (VL wrapper) with text params nested in text_config
24+
(model_type="gemma4_text").
25+
26+
Both "gemma4" and "gemma4_text" are registered in CONFIG_MAPPING to this class,
27+
and from_dict() automatically extracts text_config from the VL wrapper.
28+
29+
TODO(VL): When implementing full multimodal Gemma4:
30+
1. Create Gemma4Config (VL wrapper with text_config + vision_config)
31+
2. Change CONFIG_MAPPING "gemma4" to point to Gemma4Config
32+
3. Keep only "gemma4_text" -> Gemma4MoeConfig
33+
4. Remove the text_config extraction logic in from_dict() below
34+
"""
35+
36+
model_type = "gemma4_text"
37+
keys_to_ignore_at_inference = ["past_key_values"]
38+
39+
@classmethod
40+
def from_dict(cls, config_dict, **kwargs):
41+
"""Load text config from VL wrapper config.
42+
43+
Gemma4 only ships VL checkpoints, so config.json outer model_type="gemma4"
44+
with text params in text_config. This extracts it automatically.
45+
46+
TODO(VL): Move this extraction to Gemma4Config when VL model is implemented.
47+
"""
48+
if config_dict.get("model_type") == "gemma4" and "text_config" in config_dict:
49+
config_dict = config_dict["text_config"]
50+
return super().from_dict(config_dict, **kwargs)
51+
52+
def __init__(
53+
self,
54+
vocab_size=262144,
55+
hidden_size=2816,
56+
intermediate_size=2112,
57+
moe_intermediate_size=704,
58+
num_hidden_layers=30,
59+
num_attention_heads=16,
60+
num_key_value_heads=8,
61+
num_global_key_value_heads=2,
62+
head_dim=256,
63+
global_head_dim=512,
64+
hidden_act="gelu_pytorch_tanh",
65+
hidden_activation=None,
66+
max_position_embeddings=262144,
67+
rms_norm_eps=1e-6,
68+
# RoPE
69+
rope_parameters=None,
70+
sliding_window_rope_base=10000.0,
71+
full_attention_rope_base=1000000.0,
72+
full_attention_rope_partial_factor=0.25,
73+
# Attention pattern
74+
layer_types=None,
75+
sliding_window=1024,
76+
interleaved_attn_pattern=(5, 1),
77+
# MoE
78+
num_experts=128,
79+
top_k_experts=8,
80+
scoring_func="sigmoid",
81+
enable_moe_block=True,
82+
# Gemma4-specific
83+
attention_k_eq_v=True,
84+
final_logit_softcapping=30.0,
85+
scale_embeddings_by_hidden_size=True,
86+
# Pipeline
87+
pp_seg_method="layer:Gemma4TransformerLayer",
88+
tie_word_embeddings=True,
89+
**kwargs,
90+
):
91+
super().__init__(tie_word_embeddings=tie_word_embeddings, **kwargs)
92+
self.vocab_size = vocab_size
93+
self.hidden_size = hidden_size
94+
self.intermediate_size = intermediate_size
95+
self.moe_intermediate_size = moe_intermediate_size
96+
self.num_hidden_layers = num_hidden_layers
97+
self.num_attention_heads = num_attention_heads
98+
self.num_key_value_heads = num_key_value_heads
99+
self.num_global_key_value_heads = num_global_key_value_heads
100+
self.head_dim = head_dim
101+
self.global_head_dim = global_head_dim
102+
self.hidden_act = hidden_activation or hidden_act
103+
self.max_position_embeddings = max_position_embeddings
104+
self.rms_norm_eps = rms_norm_eps
105+
self.enable_moe_block = enable_moe_block
106+
self.sliding_window = sliding_window
107+
self.interleaved_attn_pattern = interleaved_attn_pattern
108+
self.num_experts = num_experts
109+
self.top_k_experts = top_k_experts
110+
self.scoring_func = scoring_func
111+
self.attention_k_eq_v = attention_k_eq_v
112+
self.final_logit_softcapping = final_logit_softcapping
113+
self.scale_embeddings_by_hidden_size = scale_embeddings_by_hidden_size
114+
self.pp_seg_method = pp_seg_method
115+
116+
# Parse rope_parameters from HF config format
117+
if rope_parameters is not None:
118+
sliding_rope = rope_parameters.get("sliding_attention", {})
119+
full_rope = rope_parameters.get("full_attention", {})
120+
self.sliding_window_rope_base = sliding_rope.get("rope_theta", sliding_window_rope_base)
121+
self.full_attention_rope_base = full_rope.get("rope_theta", full_attention_rope_base)
122+
self.full_attention_rope_partial_factor = full_rope.get(
123+
"partial_rotary_factor", full_attention_rope_partial_factor
124+
)
125+
else:
126+
self.sliding_window_rope_base = sliding_window_rope_base
127+
self.full_attention_rope_base = full_attention_rope_base
128+
self.full_attention_rope_partial_factor = full_attention_rope_partial_factor
129+
130+
# Build layer_types from interleaved pattern if not provided
131+
if layer_types is None:
132+
sliding_count, global_count = self.interleaved_attn_pattern
133+
pattern = ["sliding_attention"] * sliding_count + ["full_attention"] * global_count
134+
self.layer_types = (pattern * ((num_hidden_layers // len(pattern)) + 1))[:num_hidden_layers]
135+
else:
136+
self.layer_types = layer_types

0 commit comments

Comments
 (0)