Skip to content

Commit 5a75bc8

Browse files
committed
feat: add PaliGemma2 multimodal model
Add PaliGemma2 modeling, processor registration, automatic checkpoint class resolution, and validation coverage for multimodal inference and training.
1 parent ab92762 commit 5a75bc8

13 files changed

Lines changed: 1377 additions & 0 deletions

File tree

paddleformers/transformers/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,8 @@
9292
"TruncationStrategy",
9393
],
9494
"auto.processing": ["AutoProcessor"],
95+
"paligemma2.image_processor": ["PaliGemmaImageProcessor"],
96+
"paligemma2.processor": ["PaliGemmaProcessor"],
9597
"auto.tokenizer": ["AutoTokenizer", "TOKENIZER_MAPPING"],
9698
"auto.video_processing": ["AutoVideoProcessor", "VIDEO_PROCESSOR_MAPPING"],
9799
"auto.feature_extraction": ["AutoFeatureExtractor"],

paddleformers/transformers/auto/configuration.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@
3535
[
3636
("deepseek_v3", "DeepseekV3Config"),
3737
("deepseek_v32", "DeepseekV32Config"),
38+
("paligemma", "PaliGemma2Config"),
39+
("paligemma2", "PaliGemma2Config"),
3840
("ernie4_5", "Ernie4_5Config"),
3941
("ernie4_5_moe", "Ernie4_5_MoeConfig"),
4042
("ernie4_5_moe_vl", "Ernie4_5_VLConfig"),
@@ -79,6 +81,8 @@
7981
[
8082
("deepseek_v2", "DeepseekV2"),
8183
("deepseek_v3", "DeepseekV3"),
84+
("paligemma", "PaliGemma2"),
85+
("paligemma2", "PaliGemma2"),
8286
("ernie4_5", "Ernie4_5"),
8387
("ernie4_5_moe", "Ernie4_5_Moe"),
8488
("ernie4_5_moe_vl", "Ernie4_5_VLMoeForConditionalGeneration"),
@@ -115,6 +119,7 @@
115119

116120
SPECIAL_MODEL_TYPE_TO_MODULE_NAME = OrderedDict(
117121
[
122+
("paligemma", "paligemma2"),
118123
("qwen2_5_vl_text", "qwen2_5_vl"),
119124
("qwen3_vl_text", "qwen3_vl"),
120125
("qwen3_vl_moe_text", "qwen3_vl_moe"),

paddleformers/transformers/auto/image_processing.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@
5959
"qwen2_vl": ("Qwen2VLImageProcessor", "Qwen2VLImageProcessorFast"),
6060
"qwen3_vl": ("Qwen3VLImageProcessor", "Qwen3VLImageProcessorFast"),
6161
"glm_ocr": ("Glm46VImageProcessor"),
62+
"paligemma": ("PaliGemmaImageProcessor"),
6263
}
6364
)
6465

@@ -301,6 +302,8 @@ def from_pretrained(cls, pretrained_model_name_or_path, *inputs, **kwargs):
301302
raise initial_exception
302303

303304
image_processor_type = config_dict.get("image_processor_type", None)
305+
if image_processor_type == "SiglipImageProcessor" and config_dict.get("processor_class") == "PaliGemmaProcessor":
306+
image_processor_type = "PaliGemmaImageProcessor"
304307
image_processor_auto_map = None
305308
if "AutoImageProcessor" in config_dict.get("auto_map", {}):
306309
image_processor_auto_map = config_dict["auto_map"]["AutoImageProcessor"]

paddleformers/transformers/auto/modeling.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
[
5656
("DeepseekV3", "deepseek_v3"),
5757
("DeepseekV32", "deepseek_v32"),
58+
("PaliGemma2", "paligemma2"),
5859
("Ernie4_5", "ernie4_5"),
5960
("Ernie4_5_Moe", "ernie4_5_moe"),
6061
("Ernie4_5_VLMoe", "ernie4_5_moe_vl"),
@@ -186,12 +187,17 @@ def _get_model_class_from_config(cls, pretrained_model_name_or_path, config_file
186187
if model_flag in init_class:
187188
model_name = model_flag + "Model"
188189
break
190+
if model_name is None and init_class == "PaliGemmaForConditionalGeneration":
191+
model_name = "PaliGemma2ForConditionalGenerationModel"
189192
else:
190193
# From pretrained_model_name_or_path
191194
for model_flag, name in SORTED_MAPPING_NAMES.items():
192195
if type(pretrained_model_name_or_path) is str and name in pretrained_model_name_or_path.lower():
193196
model_name = model_flag + "Model"
194197
break
198+
if init_class == "PaliGemmaForConditionalGeneration":
199+
import_class = importlib.import_module("paddleformers.transformers.paligemma2.modeling")
200+
return getattr(import_class, "PaliGemma2ForConditionalGeneration")
195201
if model_name is None:
196202
# Try to get model class from config class
197203
if not isinstance(config, PretrainedConfig) and pretrained_model_name_or_path is not None:

paddleformers/transformers/auto/processing.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@
5757
("ernie4_5_moe_vl", "Ernie4_5_VLProcessor"),
5858
("glm4v_moe", "Glm4vProcessor"),
5959
("glm_ocr", "Glm46VProcessor"),
60+
("paligemma2", "PaliGemmaProcessor"),
6061
]
6162
)
6263

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
# Copyright (c) 2026 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+
import sys
16+
from typing import TYPE_CHECKING
17+
18+
from ...utils.lazy_import import _LazyModule
19+
20+
import_structure = {
21+
"configuration": ["PaliGemma2Config", "SiglipVisionConfig", "Gemma2TextConfig"],
22+
"image_processor": ["PaliGemmaImageProcessor"],
23+
"processor": ["PaliGemmaProcessor"],
24+
"modeling": [
25+
"PaliGemma2PreTrainedModel",
26+
"PaliGemma2ForConditionalGeneration",
27+
"PaliGemma2ForCausalLM",
28+
],
29+
}
30+
31+
if TYPE_CHECKING:
32+
from .configuration import Gemma2TextConfig, PaliGemma2Config, SiglipVisionConfig
33+
from .image_processor import PaliGemmaImageProcessor
34+
from .processor import PaliGemmaProcessor
35+
from .modeling import (
36+
PaliGemma2ForCausalLM,
37+
PaliGemma2ForConditionalGeneration,
38+
PaliGemma2PreTrainedModel,
39+
)
40+
else:
41+
sys.modules[__name__] = _LazyModule(
42+
__name__,
43+
globals()["__file__"],
44+
import_structure,
45+
module_spec=__spec__,
46+
)
Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
# Copyright (c) 2026 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+
"""PaliGemma2 model configuration"""
16+
17+
from typing import Optional, Union
18+
19+
from paddleformers.transformers.configuration_utils import PretrainedConfig
20+
21+
22+
class SiglipVisionConfig(PretrainedConfig):
23+
"""Configuration for the SigLIP vision encoder used in PaliGemma2."""
24+
25+
model_type = "siglip_vision_model"
26+
27+
def __init__(
28+
self,
29+
hidden_size: int = 1152,
30+
image_size: int = 448,
31+
intermediate_size: int = 4304,
32+
num_hidden_layers: int = 27,
33+
num_attention_heads: int = 16,
34+
num_channels: int = 3,
35+
patch_size: int = 14,
36+
projection_dim: int = 3584,
37+
num_image_tokens: int = 1024,
38+
num_positions: int = 1024,
39+
layer_norm_eps: float = 1e-6,
40+
vision_use_head: bool = False,
41+
**kwargs,
42+
):
43+
super().__init__(**kwargs)
44+
self.hidden_size = hidden_size
45+
self.image_size = image_size
46+
self.intermediate_size = intermediate_size
47+
self.num_hidden_layers = num_hidden_layers
48+
self.num_attention_heads = num_attention_heads
49+
self.num_channels = num_channels
50+
self.patch_size = patch_size
51+
self.projection_dim = projection_dim
52+
self.num_image_tokens = num_image_tokens
53+
self.num_positions = num_positions
54+
self.layer_norm_eps = layer_norm_eps
55+
self.vision_use_head = vision_use_head
56+
57+
58+
class Gemma2TextConfig(PretrainedConfig):
59+
"""Configuration for the Gemma2 text decoder used in PaliGemma2."""
60+
61+
model_type = "gemma2"
62+
keys_to_ignore_at_inference = ["past_key_values"]
63+
64+
def __init__(
65+
self,
66+
vocab_size: int = 257216,
67+
hidden_size: int = 3584,
68+
intermediate_size: int = 14336,
69+
num_hidden_layers: int = 42,
70+
num_attention_heads: int = 16,
71+
num_key_value_heads: int = 8,
72+
head_dim: Optional[int] = None,
73+
hidden_activation: str = "gelu_pytorch_tanh",
74+
max_position_embeddings: int = 8192,
75+
initializer_range: float = 0.02,
76+
rms_norm_eps: float = 1e-6,
77+
use_cache: bool = True,
78+
pad_token_id: int = 0,
79+
bos_token_id: int = 2,
80+
eos_token_id: int = 1,
81+
tie_word_embeddings: bool = True,
82+
rope_theta: float = 10000.0,
83+
attention_bias: bool = False,
84+
attention_dropout: float = 0.0,
85+
query_pre_attn_scalar: float = 256.0,
86+
sliding_window: int = 4096,
87+
final_logit_softcapping: float = 30.0,
88+
attn_logit_softcapping: float = 50.0,
89+
**kwargs,
90+
):
91+
super().__init__(
92+
pad_token_id=pad_token_id,
93+
bos_token_id=bos_token_id,
94+
eos_token_id=eos_token_id,
95+
tie_word_embeddings=tie_word_embeddings,
96+
**kwargs,
97+
)
98+
self.vocab_size = vocab_size
99+
self.hidden_size = hidden_size
100+
self.intermediate_size = intermediate_size
101+
self.num_hidden_layers = num_hidden_layers
102+
self.num_attention_heads = num_attention_heads
103+
self.num_key_value_heads = num_key_value_heads
104+
self.head_dim = 256 if head_dim is None else head_dim
105+
self.hidden_activation = hidden_activation
106+
self.max_position_embeddings = max_position_embeddings
107+
self.initializer_range = initializer_range
108+
self.rms_norm_eps = rms_norm_eps
109+
self.use_cache = use_cache
110+
self.rope_theta = rope_theta
111+
self.attention_bias = attention_bias
112+
self.attention_dropout = attention_dropout
113+
self.query_pre_attn_scalar = query_pre_attn_scalar
114+
self.sliding_window = sliding_window
115+
self.final_logit_softcapping = final_logit_softcapping
116+
self.attn_logit_softcapping = attn_logit_softcapping
117+
118+
119+
class PaliGemma2Config(PretrainedConfig):
120+
"""Configuration for the PaliGemma2 multimodal model.
121+
122+
PaliGemma2 combines a SigLIP vision encoder with a Gemma2 text decoder.
123+
124+
Example:
125+
```python
126+
>>> from paddleformers.transformers.paligemma2 import PaliGemma2Config
127+
>>> config = PaliGemma2Config()
128+
```
129+
"""
130+
131+
model_type = "paligemma2"
132+
attribute_map = {"image_token_id": "image_token_index"}
133+
sub_configs = {"text_config": Gemma2TextConfig, "vision_config": SiglipVisionConfig}
134+
keys_to_ignore_at_inference = ["past_key_values"]
135+
136+
def __init__(
137+
self,
138+
vision_config: Optional[Union[SiglipVisionConfig, dict]] = None,
139+
text_config: Optional[Union[Gemma2TextConfig, dict]] = None,
140+
image_token_index: int = 257152,
141+
projection_dim: int = 3584,
142+
hidden_size: int = 3584,
143+
vocab_size: int = 257152,
144+
tie_word_embeddings: bool = True,
145+
**kwargs,
146+
):
147+
if vision_config is None:
148+
vision_config = SiglipVisionConfig()
149+
elif isinstance(vision_config, dict):
150+
vision_config = SiglipVisionConfig(**vision_config)
151+
152+
if text_config is None:
153+
text_config = Gemma2TextConfig()
154+
elif isinstance(text_config, dict):
155+
text_config = Gemma2TextConfig(**text_config)
156+
157+
self.vision_config = vision_config
158+
self.text_config = text_config
159+
self.image_token_index = image_token_index
160+
self.projection_dim = projection_dim
161+
self.hidden_size = hidden_size
162+
self.vocab_size = vocab_size
163+
self.tie_word_embeddings = tie_word_embeddings
164+
165+
super().__init__(tie_word_embeddings=tie_word_embeddings, **kwargs)
166+
167+
168+
__all__ = ["PaliGemma2Config", "SiglipVisionConfig", "Gemma2TextConfig"]
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# Copyright (c) 2026 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+
6+
from transformers import SiglipImageProcessor
7+
8+
from ..image_processing_utils import PaddleImageProcessingMixin
9+
10+
11+
class PaliGemmaImageProcessor(PaddleImageProcessingMixin, SiglipImageProcessor):
12+
"""SigLIP preprocessing with PaliGemma image-token metadata."""
13+
14+
def __init__(self, image_seq_length=1024, **kwargs):
15+
self.image_seq_length = image_seq_length
16+
super().__init__(**kwargs)
17+
18+
19+
__all__ = ["PaliGemmaImageProcessor"]

0 commit comments

Comments
 (0)