main
py 512 lines 18.5 KB
Raw
1 # SPDX-License-Identifier: Apache-2.0
2 # SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3
4 # adapted from https://github.com/huggingface/transformers/blob/v4.43.2/src/transformers/models/idefics2/modeling_idefics2.py
5 # Copyright 2024 The vLLM team.
6 # Copyright 2024 the HuggingFace Inc. team. All rights reserved.
7 #
8 # Licensed under the Apache License, Version 2.0 (the "License");
9 # you may not use this file except in compliance with the License.
10 # You may obtain a copy of the License at
11 #
12 # http://www.apache.org/licenses/LICENSE-2.0
13 #
14 # Unless required by applicable law or agreed to in writing, software
15 # distributed under the License is distributed on an "AS IS" BASIS,
16 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 # See the License for the specific language governing permissions and
18 # limitations under the License.
19 """PyTorch Idefics2 model."""
20
21 from collections.abc import Iterable
22
23 import torch
24 from torch import nn
25 from torch.nn import functional as F
26 from transformers.models.idefics2.configuration_idefics2 import (
27 Idefics2Config,
28 Idefics2VisionConfig,
29 )
30
31 from vllm.distributed import get_tensor_model_parallel_world_size
32 from vllm.model_executor.layers.activation import get_act_fn
33 from vllm.model_executor.layers.attention import MMEncoderAttention
34 from vllm.model_executor.layers.conv import Conv2dLayer
35 from vllm.model_executor.layers.linear import (
36 ColumnParallelLinear,
37 QKVParallelLinear,
38 RowParallelLinear,
39 )
40 from vllm.model_executor.layers.quantization import QuantizationConfig
41 from vllm.utils.torch_utils import async_tensor_h2d
42
43 from .utils import AutoWeightsLoader, WeightsMapper
44 from .vision import is_vit_use_data_parallel, run_dp_sharded_vision_model
45
46
47 class Idefics2VisionEmbeddings(nn.Module):
48 """This is a modified version of `siglip.modelign_siglip.SiglipVisionEmbeddings
49 ` to enable images of variable
50 resolution.
51
52 The modifications are adapted from [Patch n' Pack: NaViT, a Vision
53 Transformer for any Aspect Ratio and Resolution](https://arxiv.org/abs/2307.06304)
54 which allows treating images in their native aspect ratio and without the
55 need to resize them to the same fixed size. In particular, we start from the
56 original pre-trained SigLIP model(which uses images of fixed-size square
57 images) and adapt it by training on images of variable resolutions.
58 """
59
60 def __init__(self, config: Idefics2VisionConfig):
61 super().__init__()
62 self.embed_dim = config.hidden_size
63 self.image_size = config.image_size
64 self.patch_size = config.patch_size
65 self.patch_embedding = Conv2dLayer(
66 in_channels=config.num_channels,
67 out_channels=self.embed_dim,
68 kernel_size=self.patch_size,
69 stride=self.patch_size,
70 padding="valid",
71 )
72 self.num_patches_per_side = self.image_size // self.patch_size
73 self.num_patches = self.num_patches_per_side**2
74 self.num_positions = self.num_patches
75 self.position_embedding = nn.Embedding(self.num_positions, self.embed_dim)
76
77 def get_position_ids(
78 self,
79 patch_attention_mask: torch.BoolTensor,
80 tgt_sizes: torch.IntTensor | None = None,
81 ) -> torch.Tensor:
82 batch_size = patch_attention_mask.shape[0]
83 device = patch_attention_mask.device
84 boundaries = torch.arange(
85 1 / self.num_patches_per_side,
86 1.0,
87 1 / self.num_patches_per_side,
88 device=device,
89 )
90
91 flat_patch_attention_mask = patch_attention_mask.view(batch_size, -1)
92 max_patches = flat_patch_attention_mask.shape[-1]
93
94 if tgt_sizes is not None:
95 # tgt_sizes may come from CPU-side mm_kwargs; use a pinned async
96 # copy to stay clean under VLLM_GPU_SYNC_CHECK.
97 if tgt_sizes.device != device:
98 tgt_sizes = async_tensor_h2d(tgt_sizes, device)
99 nb_patches_h = tgt_sizes[:, 0]
100 nb_patches_w = tgt_sizes[:, 1]
101 else:
102 if patch_attention_mask.dim() == 3:
103 nb_patches_h = patch_attention_mask[:, :, 0].sum(dim=1)
104 nb_patches_w = patch_attention_mask[:, 0, :].sum(dim=1)
105 else:
106 # Fallback for unexpected 2D masks without tgt_sizes
107 nb_patches_h = torch.ones(batch_size, device=device)
108 nb_patches_w = patch_attention_mask.sum(dim=1)
109
110 nb_h = nb_patches_h.clamp(min=1).unsqueeze(1)
111 nb_w = nb_patches_w.clamp(min=1).unsqueeze(1)
112
113 i = torch.arange(max_patches, device=device)
114
115 if patch_attention_mask.dim() == 3 and patch_attention_mask.shape[1] > 1:
116 stride_w = patch_attention_mask.shape[2] # padded 2D grid width
117 else:
118 stride_w = nb_w # (batch, 1) — per-item actual patch width
119
120 h_idx = i.unsqueeze(0) // stride_w
121 w_idx = i.unsqueeze(0) % stride_w
122
123 fractional_h = h_idx / nb_h
124 fractional_w = w_idx / nb_w
125
126 bucket_h = torch.bucketize(fractional_h, boundaries, right=True)
127 bucket_w = torch.bucketize(fractional_w, boundaries, right=True)
128
129 pos_ids = bucket_h * self.num_patches_per_side + bucket_w
130
131 position_ids = torch.where(flat_patch_attention_mask, pos_ids.to(torch.long), 0)
132
133 target_device = self.position_embedding.weight.device
134 if position_ids.device != target_device:
135 position_ids = async_tensor_h2d(position_ids, target_device)
136 return position_ids
137
138 def forward(
139 self,
140 pixel_values: torch.FloatTensor,
141 patch_attention_mask: torch.BoolTensor,
142 tgt_sizes: torch.IntTensor | None = None,
143 position_ids: torch.Tensor | None = None,
144 ) -> torch.Tensor:
145 target_dtype = self.patch_embedding.weight.dtype
146 patch_embeds = self.patch_embedding(pixel_values.to(target_dtype))
147 embeddings = patch_embeds.flatten(2).transpose(1, 2)
148 if position_ids is None:
149 position_ids = self.get_position_ids(patch_attention_mask, tgt_sizes)
150 embeddings += self.position_embedding(position_ids)
151 return embeddings
152
153
154 class Idefics2VisionAttention(nn.Module):
155 """Multi-headed attention from 'Attention Is All You Need' paper."""
156
157 def __init__(
158 self,
159 config: Idefics2VisionConfig,
160 quant_config: QuantizationConfig | None = None,
161 prefix: str = "",
162 ) -> None:
163 super().__init__()
164 use_data_parallel = is_vit_use_data_parallel()
165 self.config = config
166 self.embed_dim = config.hidden_size
167 self.num_heads = config.num_attention_heads
168 self.head_dim = self.embed_dim // self.num_heads
169 if self.head_dim * self.num_heads != self.embed_dim:
170 raise ValueError(
171 f"embed_dim must be divisible by num_heads (got `embed_dim`: {self.embed_dim} and `num_heads`:" # noqa: E501
172 f" {self.num_heads})."
173 )
174 self.scale = self.head_dim**-0.5
175 self.dropout = config.attention_dropout
176
177 tp_size = 1 if use_data_parallel else get_tensor_model_parallel_world_size()
178 assert self.num_heads % tp_size == 0
179 self.num_heads_per_partition = self.num_heads // tp_size
180
181 self.qkv_proj = QKVParallelLinear(
182 self.embed_dim,
183 self.head_dim,
184 self.num_heads,
185 quant_config=quant_config,
186 prefix=f"{prefix}.qkv_proj",
187 disable_tp=use_data_parallel,
188 )
189 self.out_proj = RowParallelLinear(
190 self.embed_dim,
191 self.embed_dim,
192 bias=True,
193 quant_config=quant_config,
194 prefix=f"{prefix}.out_proj",
195 disable_tp=use_data_parallel,
196 )
197 # Use unified MMEncoderAttention with Flash Attention support
198 self.attn = MMEncoderAttention(
199 self.num_heads_per_partition,
200 self.head_dim,
201 self.scale,
202 prefix=f"{prefix}.attn",
203 )
204
205 def forward(
206 self,
207 hidden_states: torch.Tensor,
208 attention_mask: torch.Tensor | None = None,
209 ) -> torch.Tensor:
210 qkv, _ = self.qkv_proj(
211 hidden_states
212 ) # batch_size, q_len, 3 * num_heads_per_partition * head_dim
213 query_states, key_states, value_states = qkv.chunk(3, dim=-1)
214
215 # If attention_mask is provided, prefer Torch SDPA so the mask is
216 # correctly applied (aligns with HuggingFace NaViT SigLIP behavior).
217 if attention_mask is None:
218 # Use unified MMEncoderAttention implementation
219 out = self.attn(query_states, key_states, value_states)
220 else:
221 bsz, q_len = query_states.size()[:2]
222 kv_len = key_states.size(1)
223
224 query = query_states.view(
225 bsz, q_len, self.num_heads_per_partition, self.head_dim
226 ).transpose(1, 2)
227 key = key_states.view(
228 bsz, kv_len, self.num_heads_per_partition, self.head_dim
229 ).transpose(1, 2)
230 value = value_states.view(
231 bsz, kv_len, self.num_heads_per_partition, self.head_dim
232 ).transpose(1, 2)
233
234 out = F.scaled_dot_product_attention(
235 query,
236 key,
237 value,
238 attn_mask=attention_mask,
239 dropout_p=0.0,
240 scale=self.scale,
241 )
242 out = out.transpose(1, 2).reshape(bsz, q_len, -1)
243 attn_output, _ = self.out_proj(out)
244 return attn_output
245
246
247 class Idefics2VisionMLP(nn.Module):
248 def __init__(
249 self,
250 config: Idefics2VisionConfig,
251 quant_config: QuantizationConfig | None = None,
252 prefix: str = "",
253 ) -> None:
254 super().__init__()
255 self.config = config
256 self.activation_fn = get_act_fn(config.hidden_act)
257
258 use_data_parallel = is_vit_use_data_parallel()
259 self.fc1 = ColumnParallelLinear(
260 config.hidden_size,
261 config.intermediate_size,
262 bias=True,
263 quant_config=quant_config,
264 prefix=f"{prefix}.fc1",
265 disable_tp=use_data_parallel,
266 )
267 self.fc2 = RowParallelLinear(
268 config.intermediate_size,
269 config.hidden_size,
270 bias=True,
271 quant_config=quant_config,
272 prefix=f"{prefix}.fc2",
273 disable_tp=use_data_parallel,
274 )
275
276 def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
277 hidden_states, _ = self.fc1(hidden_states)
278 hidden_states = self.activation_fn(hidden_states)
279 hidden_states, _ = self.fc2(hidden_states)
280 return hidden_states
281
282
283 class Idefics2EncoderLayer(nn.Module):
284 def __init__(
285 self,
286 config: Idefics2Config,
287 quant_config: QuantizationConfig | None = None,
288 prefix: str = "",
289 ) -> None:
290 super().__init__()
291 self.embed_dim = config.hidden_size
292 self.self_attn = Idefics2VisionAttention(
293 config,
294 quant_config=quant_config,
295 prefix=f"{prefix}.self_attn",
296 )
297 self.layer_norm1 = nn.LayerNorm(self.embed_dim, eps=config.layer_norm_eps)
298 self.mlp = Idefics2VisionMLP(
299 config,
300 quant_config=quant_config,
301 prefix=f"{prefix}.mlp",
302 )
303 self.layer_norm2 = nn.LayerNorm(self.embed_dim, eps=config.layer_norm_eps)
304
305 def forward(
306 self,
307 hidden_states: torch.Tensor,
308 attention_mask: torch.Tensor | None = None,
309 ) -> torch.Tensor:
310 """Args:
311 hidden_states (`torch.FloatTensor`):
312 Input to the layer of shape `(batch, seq_len, embed_dim)`.
313
314 """
315 residual = hidden_states
316 hidden_states = self.layer_norm1(hidden_states)
317 hidden_states = self.self_attn(hidden_states, attention_mask=attention_mask)
318 hidden_states += residual
319 residual = hidden_states
320 hidden_states = self.layer_norm2(hidden_states)
321 hidden_states = self.mlp(hidden_states)
322 hidden_states += residual
323 return hidden_states
324
325
326 class Idefics2Encoder(nn.Module):
327 """Transformer encoder consisting of `config.num_hidden_layers` self attention
328 layers. Each layer is a
329 [`Idefics2EncoderLayer`].
330
331 Args:
332 config: Idefics2Config
333
334 """
335
336 def __init__(
337 self,
338 config: Idefics2Config,
339 quant_config: QuantizationConfig | None = None,
340 *,
341 num_hidden_layers_override: int | None = None,
342 prefix: str = "",
343 ) -> None:
344 super().__init__()
345
346 self.config = config
347
348 if num_hidden_layers_override is None:
349 num_hidden_layers = config.num_hidden_layers
350 else:
351 num_hidden_layers = num_hidden_layers_override
352
353 self.layers = nn.ModuleList(
354 [
355 Idefics2EncoderLayer(
356 config,
357 quant_config=quant_config,
358 prefix=f"{prefix}.layers.{layer_idx}",
359 )
360 for layer_idx in range(num_hidden_layers)
361 ]
362 )
363
364 def forward(
365 self,
366 inputs_embeds: torch.Tensor,
367 attention_mask: torch.Tensor | None = None,
368 ) -> torch.Tensor:
369 r"""Args:
370 inputs_embeds (torch.Tensor):
371 Optionally, instead of passing `input_ids` you can choose to
372 directly pass an embedded representation.
373 This is useful if you want more control over how to convert
374 `input_ids` indices into associated vectorsthan the model's
375 internal embedding lookup matrix.
376
377 """
378 hidden_states = inputs_embeds
379 for encoder_layer in self.layers:
380 layer_outputs = encoder_layer(hidden_states, attention_mask=attention_mask)
381 hidden_states = layer_outputs
382 return hidden_states
383
384
385 class Idefics2VisionTransformer(nn.Module):
386 hf_to_vllm_mapper = WeightsMapper(
387 orig_to_new_stacked={
388 ".q_proj": (".qkv_proj", "q"),
389 ".k_proj": (".qkv_proj", "k"),
390 ".v_proj": (".qkv_proj", "v"),
391 }
392 )
393
394 def __init__(
395 self,
396 config: Idefics2VisionConfig,
397 quant_config: QuantizationConfig | None = None,
398 *,
399 num_hidden_layers_override: int | None = None,
400 require_post_norm: bool = True,
401 apply_encoder_attention_mask: bool = False,
402 prefix: str = "",
403 ) -> None:
404 super().__init__()
405
406 embed_dim = config.hidden_size
407 self.config = config
408 self.use_data_parallel = is_vit_use_data_parallel()
409 self.apply_encoder_attention_mask = apply_encoder_attention_mask
410 self.embeddings = Idefics2VisionEmbeddings(config)
411 self.encoder = Idefics2Encoder(
412 config,
413 quant_config=quant_config,
414 num_hidden_layers_override=num_hidden_layers_override,
415 prefix=f"{prefix}.encoder",
416 )
417
418 num_hidden_layers = config.num_hidden_layers
419 if len(self.encoder.layers) > config.num_hidden_layers:
420 raise ValueError(
421 f"The original encoder only has {num_hidden_layers} "
422 f"layers, but you requested {len(self.encoder.layers)} layers."
423 )
424
425 self.require_post_norm = require_post_norm
426 self.post_layernorm = (
427 nn.LayerNorm(
428 embed_dim,
429 eps=config.layer_norm_eps,
430 )
431 if require_post_norm
432 else nn.Identity()
433 )
434 # head is a pooling header absent from this model.
435 orig_to_new_prefix: dict[str, str | None] = {"head.": None}
436 if not require_post_norm:
437 orig_to_new_prefix["post_layernorm."] = None
438 self.hf_to_vllm_mapper = self.hf_to_vllm_mapper | WeightsMapper(
439 orig_to_new_prefix=orig_to_new_prefix
440 )
441
442 def get_input_embeddings(self):
443 return self.embeddings
444
445 def forward(
446 self,
447 pixel_values,
448 patch_attention_mask: torch.BoolTensor | None = None,
449 tgt_sizes: torch.IntTensor | None = None,
450 position_ids: torch.Tensor | None = None,
451 ) -> torch.Tensor:
452 batch_size = pixel_values.size(0)
453
454 if patch_attention_mask is None:
455 # No mask provided - create default all-ones mask for embeddings
456 # and skip attention masking (no padding to mask)
457 patch_attention_mask = torch.ones(
458 size=(
459 batch_size,
460 pixel_values.size(2) // self.config.patch_size,
461 pixel_values.size(3) // self.config.patch_size,
462 ),
463 dtype=torch.bool,
464 device=pixel_values.device,
465 )
466 flat_patch_mask = None
467 else:
468 flat_patch_mask = patch_attention_mask.view(batch_size, -1)
469
470 hidden_states = self.embeddings(
471 pixel_values=pixel_values,
472 patch_attention_mask=patch_attention_mask,
473 tgt_sizes=tgt_sizes,
474 position_ids=position_ids,
475 )
476
477 # Align with HuggingFace NaViT SigLIP in MiniCPMV/O:
478 # - if apply_encoder_attention_mask is False, skip (not all models
479 # sharing this encoder apply masking in attention, e.g. Aria, Phi4)
480 # - if patch_attention_mask was None, skip attention masking
481 if not self.apply_encoder_attention_mask or flat_patch_mask is None:
482 attention_mask = None
483 else:
484 # Additive mask: masked positions receive a large negative value.
485 # Shape: (B, 1, 1, L) broadcastable to (B, H, Q, K).
486 min_val = torch.finfo(hidden_states.dtype).min
487 attention_mask = (~flat_patch_mask).to(dtype=hidden_states.dtype) * min_val
488 attention_mask = attention_mask[:, None, None, :]
489
490 if self.use_data_parallel:
491 encoder_outputs = run_dp_sharded_vision_model(hidden_states, self.encoder)
492 else:
493 encoder_outputs = self.encoder(hidden_states, attention_mask=attention_mask)
494 last_hidden_state = self.post_layernorm(encoder_outputs)
495 return last_hidden_state
496
497 def load_weights(self, weights: Iterable[tuple[str, torch.Tensor]]) -> set[str]:
498 loader = AutoWeightsLoader(self)
499
500 layer_count = len(self.encoder.layers)
501
502 def _filter(ws: Iterable[tuple[str, torch.Tensor]]):
503 # Drop layers beyond num_hidden_layers_override.
504 for name, w in ws:
505 if (
506 name.startswith("encoder.layers.")
507 and int(name.split(".")[2]) >= layer_count
508 ):
509 continue
510 yield name, w
511
512 return loader.load_weights(_filter(weights), mapper=self.hf_to_vllm_mapper)