main
py 844 lines 28.5 KB
Raw
1 # SPDX-License-Identifier: Apache-2.0
2 # SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3
4 # Copyright 2024 the HuggingFace Inc. team. All rights reserved.
5 #
6 # Licensed under the Apache License, Version 2.0 (the "License");
7 # you may not use this file except in compliance with the License.
8 # You may obtain a copy of the License at
9 #
10 # http://www.apache.org/licenses/LICENSE-2.0
11 #
12 # Unless required by applicable law or agreed to in writing, software
13 # distributed under the License is distributed on an "AS IS" BASIS,
14 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 # See the License for the specific language governing permissions and
16 # limitations under the License.
17 """Inference-only Idefics3 model compatible with HuggingFace weights."""
18
19 from collections.abc import Hashable, Iterable, Mapping, Sequence
20 from typing import Annotated, Any, Literal, TypeAlias
21
22 import torch
23 from torch import nn
24 from transformers import (
25 BatchFeature,
26 Idefics3Config,
27 Idefics3ImageProcessor,
28 Idefics3Processor,
29 )
30
31 from vllm.config import VllmConfig
32 from vllm.config.multimodal import MultiModalDummyOptions
33 from vllm.inputs import MultiModalDataDict
34 from vllm.model_executor.layers.linear import ReplicatedLinear
35 from vllm.model_executor.layers.logits_processor import LogitsProcessor
36 from vllm.model_executor.layers.quantization import QuantizationConfig
37 from vllm.model_executor.layers.vocab_parallel_embedding import ParallelLMHead
38 from vllm.model_executor.models.module_mapping import MultiModelKeys
39 from vllm.multimodal import MULTIMODAL_REGISTRY
40 from vllm.multimodal.inputs import (
41 MultiModalFieldConfig,
42 MultiModalKwargsItem,
43 MultiModalKwargsItems,
44 )
45 from vllm.multimodal.parse import ImageProcessorItems, MultiModalDataItems
46 from vllm.multimodal.processing import (
47 BaseDummyInputsBuilder,
48 BaseMultiModalProcessor,
49 BaseProcessingInfo,
50 PromptReplacement,
51 PromptUpdate,
52 PromptUpdateDetails,
53 cached_encode,
54 )
55 from vllm.multimodal.processing.processor import HFMultiModalInputs
56 from vllm.sequence import IntermediateTensors
57 from vllm.utils.gpu_sync_debug import gpu_sync_allowed
58 from vllm.utils.tensor_schema import TensorSchema, TensorShape
59
60 from .idefics2_vision_model import (
61 Idefics2VisionTransformer as Idefics3VisionTransformer,
62 )
63 from .interfaces import (
64 MultiModalEmbeddings,
65 SupportsEncoderCudaGraph,
66 SupportsLoRA,
67 SupportsMultiModal,
68 )
69 from .llama import LlamaModel
70 from .utils import AutoWeightsLoader, maybe_prefix
71
72
73 class Idefics3ImagePixelInputs(TensorSchema):
74 """Dimensions:
75 - bn: Batch size * number of images
76 - bnp: Batch size * number of images * number of patches
77 - c: Number of channels (3)
78 - h: Height
79 - w: Width
80 """
81
82 type: Literal["pixel_values"]
83 pixel_values: Annotated[torch.Tensor, TensorShape("bnp", 3, "h", "w")]
84 pixel_attention_mask: Annotated[torch.Tensor, TensorShape("bnp", "h", "w")]
85 num_patches: Annotated[torch.Tensor, TensorShape("bn")]
86
87
88 class Idefics3ImageEmbeddingInputs(TensorSchema):
89 """Dimensions:
90 - bn: Batch size * number of images
91 - f: Image feature size
92 - h: Hidden size (must match the hidden size of language model backbone)
93 """
94
95 type: Literal["image_embeds"]
96 data: Annotated[torch.Tensor, TensorShape("bn", "f", "h")]
97
98
99 ImageInputs: TypeAlias = Idefics3ImagePixelInputs | Idefics3ImageEmbeddingInputs
100
101
102 class Idefics3ProcessingInfo(BaseProcessingInfo):
103 def get_hf_processor(self, **kwargs: object) -> Idefics3Processor:
104 return self.ctx.get_hf_processor(Idefics3Processor, **kwargs)
105
106 def get_supported_mm_limits(self) -> Mapping[str, int | None]:
107 return {"image": None}
108
109 def _get_image_feature_grid_size(
110 self,
111 *,
112 image_width: int,
113 image_height: int,
114 processor: Idefics3Processor,
115 mm_kwargs: Mapping[str, object],
116 ) -> tuple[int, int, int]:
117 image_processor: Idefics3ImageProcessor = processor.image_processor
118
119 return image_processor.get_number_of_image_patches(
120 image_height,
121 image_width,
122 self.ctx.get_merged_mm_kwargs(mm_kwargs),
123 )
124
125 def get_num_patches(
126 self,
127 *,
128 image_width: int,
129 image_height: int,
130 processor: Idefics3Processor,
131 mm_kwargs: Mapping[str, object],
132 ) -> int:
133 num_patches, _, _ = self._get_image_feature_grid_size(
134 image_width=image_width,
135 image_height=image_height,
136 processor=processor,
137 mm_kwargs=mm_kwargs,
138 )
139
140 return max(num_patches, 1)
141
142 def _get_image_token(self, processor: Idefics3Processor) -> tuple[str, str, str]:
143 image_token = processor.image_token
144 fake_image_token = processor.fake_image_token
145 global_image_token = processor.global_image_tag
146 return image_token, fake_image_token, global_image_token
147
148 def get_image_repl(
149 self,
150 *,
151 image_width: int,
152 image_height: int,
153 processor: Idefics3Processor,
154 mm_kwargs: Mapping[str, object],
155 ) -> str:
156 image_token, fake_image_token, global_img_token = self._get_image_token(
157 processor
158 )
159 image_seq_len = processor.image_seq_len
160 grid_placeholder = "<row_{n_h}_col_{n_w}>"
161
162 p_img = image_token * image_seq_len
163 global_img_placeholder = fake_image_token + global_img_token + p_img
164 tile_img_placeholder = fake_image_token + grid_placeholder + p_img
165
166 _, grid_h, grid_w = self._get_image_feature_grid_size(
167 image_width=image_width,
168 image_height=image_height,
169 processor=processor,
170 mm_kwargs=mm_kwargs,
171 )
172 if grid_w == 0 and grid_h == 0:
173 return global_img_placeholder + fake_image_token
174
175 tiles_placeholder = list[str]()
176 for i in range(grid_h):
177 for j in range(grid_w):
178 placeholder_per_tile = tile_img_placeholder.format(n_h=i + 1, n_w=j + 1)
179 tiles_placeholder.append(placeholder_per_tile)
180 # Add line break if it is the last tile in the row
181 if j == grid_w - 1:
182 tiles_placeholder.append("\n")
183
184 return "".join(
185 [
186 *tiles_placeholder,
187 "\n",
188 global_img_placeholder,
189 fake_image_token,
190 ]
191 )
192
193 def get_num_image_tokens(
194 self,
195 *,
196 image_width: int,
197 image_height: int,
198 processor: Idefics3Processor,
199 mm_kwargs: Mapping[str, object],
200 ) -> int:
201 num_patches = self.get_num_patches(
202 image_width=image_width,
203 image_height=image_height,
204 processor=processor,
205 mm_kwargs=mm_kwargs,
206 )
207
208 return num_patches * processor.image_seq_len
209
210
211 class Idefics3DummyInputsBuilder(BaseDummyInputsBuilder[Idefics3ProcessingInfo]):
212 def get_dummy_text(self, mm_counts: Mapping[str, int]) -> str:
213 num_images = mm_counts.get("image", 0)
214
215 processor = self.info.get_hf_processor()
216 image_token, _, _ = self.info._get_image_token(processor)
217
218 return image_token * num_images
219
220 def get_dummy_mm_data(
221 self,
222 seq_len: int,
223 mm_counts: Mapping[str, int],
224 mm_options: MultiModalDummyOptions,
225 ) -> MultiModalDataDict:
226 hf_processor = self.info.get_hf_processor()
227 image_processor: Idefics3ImageProcessor = hf_processor.image_processor
228 longest_edge = image_processor.max_image_size["longest_edge"]
229
230 return {
231 "image": self._get_dummy_images(
232 width=longest_edge,
233 height=longest_edge,
234 num_images=mm_counts.get("image", 0),
235 overrides=mm_options.get("image"),
236 )
237 }
238
239
240 class Idefics3MultiModalProcessor(BaseMultiModalProcessor[Idefics3ProcessingInfo]):
241 def _get_hf_mm_inputs(
242 self,
243 mm_items: MultiModalDataItems,
244 hf_kwargs: Mapping[str, object],
245 ) -> HFMultiModalInputs:
246 hf_inputs = super()._get_hf_mm_inputs(mm_items, hf_kwargs)
247
248 image_processor = self.info.get_hf_processor().image_processor
249 if getattr(image_processor, "backend", "pil") == "pil":
250 hf_inputs = hf_inputs._replace(
251 hf_kwargs={
252 "input_data_format": "channels_last",
253 **hf_inputs.hf_kwargs,
254 }
255 )
256
257 return hf_inputs
258
259 def _postprocess_hf_mm_data(
260 self,
261 mm_data: Mapping[str, object],
262 hf_processor_mm_kwargs: Mapping[str, object],
263 processed_data: BatchFeature,
264 ) -> BatchFeature:
265 if not mm_data:
266 return processed_data
267
268 images = mm_data.get("images", [])
269 mm_items = self.info.parse_mm_data({"image": images}, validate=False)
270 parsed_images = mm_items.get_items("image", ImageProcessorItems)
271 image_sizes = [
272 parsed_images.get_image_size(i) for i in range(len(parsed_images))
273 ]
274 hf_processor = self.info.get_hf_processor(**hf_processor_mm_kwargs)
275
276 num_patches = [
277 self.info.get_num_patches(
278 image_width=size.width,
279 image_height=size.height,
280 processor=hf_processor,
281 mm_kwargs=hf_processor_mm_kwargs,
282 )
283 for size in image_sizes
284 ]
285 processed_data["num_patches"] = torch.tensor(num_patches)
286
287 # Remove the extra batch dimension
288 processed_data["pixel_values"].squeeze_(0)
289 processed_data["pixel_attention_mask"].squeeze_(0)
290
291 return processed_data
292
293 def _get_mm_fields_config(
294 self,
295 hf_inputs: BatchFeature,
296 hf_processor_mm_kwargs: Mapping[str, object],
297 ) -> Mapping[str, MultiModalFieldConfig]:
298 num_patches = hf_inputs.get("num_patches", torch.empty(0))
299
300 return dict(
301 pixel_values=MultiModalFieldConfig.flat_from_sizes("image", num_patches),
302 pixel_attention_mask=MultiModalFieldConfig.flat_from_sizes(
303 "image", num_patches
304 ),
305 image_embeds=MultiModalFieldConfig.batched("image"),
306 num_patches=MultiModalFieldConfig.batched("image", keep_on_cpu=True),
307 )
308
309 def _get_prompt_updates(
310 self,
311 mm_items: MultiModalDataItems,
312 hf_processor_mm_kwargs: Mapping[str, object],
313 out_mm_kwargs: MultiModalKwargsItems,
314 ) -> Sequence[PromptUpdate]:
315 hf_processor = self.info.get_hf_processor(**hf_processor_mm_kwargs)
316 image_token_id = hf_processor.image_token_id
317 tokenizer = self.info.get_tokenizer()
318
319 def get_replacement_idefics3(item_idx: int) -> PromptUpdateDetails:
320 images = mm_items.get_items("image", ImageProcessorItems)
321
322 image_size = images.get_image_size(item_idx)
323
324 image_repl = self.info.get_image_repl(
325 image_width=image_size.width,
326 image_height=image_size.height,
327 processor=hf_processor,
328 mm_kwargs=hf_processor_mm_kwargs,
329 )
330
331 image_repl_ids = cached_encode(
332 tokenizer, image_repl, add_special_tokens=False
333 )
334 return PromptUpdateDetails.select_token_id(
335 image_repl_ids,
336 image_token_id,
337 )
338
339 return [
340 PromptReplacement(
341 modality="image",
342 target=[image_token_id],
343 replacement=get_replacement_idefics3,
344 )
345 ]
346
347
348 class Idefics3SimpleMLP(nn.Module):
349 def __init__(
350 self,
351 config: Idefics3Config,
352 quant_config: QuantizationConfig | None = None,
353 prefix: str = "",
354 ):
355 super().__init__()
356 input_size = config.vision_config.hidden_size * (config.scale_factor**2)
357 output_size = config.text_config.hidden_size
358 self.proj = ReplicatedLinear(
359 input_size,
360 output_size,
361 bias=False,
362 quant_config=quant_config,
363 prefix=maybe_prefix(prefix, "proj"),
364 )
365
366 def forward(self, x: torch.Tensor) -> torch.Tensor:
367 out, _ = self.proj(x)
368 return out
369
370
371 class Idefics3Connector(nn.Module):
372 def __init__(
373 self,
374 config: Idefics3Config,
375 quant_config: QuantizationConfig | None = None,
376 prefix: str = "",
377 ):
378 super().__init__()
379 self.scale_factor = config.scale_factor
380 self.modality_projection = Idefics3SimpleMLP(
381 config,
382 quant_config,
383 prefix=maybe_prefix(prefix, "modality_projection"),
384 )
385
386 def pixel_shuffle(self, x: torch.Tensor, scale_factor: int = 2) -> torch.Tensor:
387 bsz, seq, embed_dim = x.size()
388 height = width = int(seq**0.5)
389 x = x.view(bsz, height, width, embed_dim)
390 x = x.view(bsz, height, int(width / scale_factor), embed_dim * scale_factor)
391 x = x.permute(0, 2, 1, 3)
392 x = x.reshape(
393 bsz,
394 int(width / scale_factor),
395 int(height / scale_factor),
396 embed_dim * (scale_factor**2),
397 )
398 x = x.permute(0, 2, 1, 3)
399 x = x.reshape(bsz, int(seq / (scale_factor**2)), embed_dim * (scale_factor**2))
400 return x
401
402 def forward(self, image_hidden_states: torch.Tensor) -> torch.Tensor:
403 image_hidden_states = self.pixel_shuffle(image_hidden_states, self.scale_factor)
404 image_hidden_states = self.modality_projection(image_hidden_states)
405 return image_hidden_states
406
407
408 class Idefics3Model(nn.Module):
409 def __init__(self, *, vllm_config: VllmConfig, prefix: str = ""):
410 super().__init__()
411
412 config: Idefics3Config = vllm_config.model_config.hf_config
413 quant_config = vllm_config.quant_config
414
415 self.config = config
416 self.vocab_size = self.config.text_config.vocab_size
417 self.vision_model = Idefics3VisionTransformer(
418 config.vision_config,
419 quant_config=quant_config,
420 prefix=maybe_prefix(prefix, "vision_model"),
421 )
422 self.connector = Idefics3Connector(
423 config,
424 quant_config,
425 prefix=maybe_prefix(prefix, "connector"),
426 )
427 self.text_model = LlamaModel(
428 vllm_config=vllm_config.with_hf_config(config.text_config),
429 prefix=maybe_prefix(prefix, "text_model"),
430 )
431
432 self.image_seq_len = int(
433 ((config.vision_config.image_size // config.vision_config.patch_size) ** 2)
434 / (config.scale_factor**2)
435 )
436 self.image_token_id = self.config.image_token_id
437
438 def image_pixels_to_features(
439 self,
440 pixel_values: torch.Tensor,
441 pixel_attention_mask: torch.Tensor,
442 *,
443 remove_padding: bool = True,
444 position_ids: torch.Tensor | None = None,
445 ) -> torch.Tensor:
446 # NOTE: we skip the step to select the vision feature layer since
447 # this is already done inside the vision tower
448 pixel_values = pixel_values.to(
449 dtype=self.vision_model.embeddings.patch_embedding.weight.dtype
450 ) # fp16 compatibility
451
452 if remove_padding:
453 # Remove padding images - padding images are full 0.
454 nb_values_per_image = pixel_values.shape[1:].numel()
455 real_images_inds = (pixel_values == 0.0).sum(
456 dim=(-1, -2, -3)
457 ) != nb_values_per_image
458 with gpu_sync_allowed():
459 pixel_values = pixel_values[real_images_inds].contiguous()
460
461 # Handle the vision attention mask
462 # Remove padding images from the mask
463 pixel_attention_mask = pixel_attention_mask[
464 real_images_inds
465 ].contiguous()
466
467 patch_attention_mask = self.get_patch_attention_mask(pixel_attention_mask)
468
469 # Get sequence from the vision encoder
470 image_hidden_states = self.vision_model(
471 pixel_values=pixel_values,
472 patch_attention_mask=patch_attention_mask,
473 position_ids=position_ids,
474 )
475
476 return image_hidden_states
477
478 def get_patch_attention_mask(
479 self,
480 pixel_attention_mask: torch.Tensor,
481 ) -> torch.Tensor:
482 patch_size = self.config.vision_config.patch_size
483 patches_subgrid = pixel_attention_mask.unfold(
484 dimension=1, size=patch_size, step=patch_size
485 )
486 patches_subgrid = patches_subgrid.unfold(
487 dimension=2, size=patch_size, step=patch_size
488 )
489 return (patches_subgrid.sum(dim=(-1, -2)) > 0).bool()
490
491 def get_position_ids(
492 self,
493 pixel_attention_mask: torch.Tensor,
494 ) -> torch.Tensor:
495 patch_attention_mask = self.get_patch_attention_mask(pixel_attention_mask)
496 return self.vision_model.embeddings.get_position_ids(patch_attention_mask)
497
498 def embed_input_ids(self, input_ids: torch.Tensor) -> torch.Tensor:
499 return self.text_model.embed_input_ids(input_ids)
500
501 def forward(
502 self,
503 input_ids: torch.Tensor | None,
504 positions: torch.Tensor,
505 intermediate_tensors: IntermediateTensors | None = None,
506 inputs_embeds: torch.Tensor | None = None,
507 ) -> torch.Tensor | IntermediateTensors:
508 hidden_states = self.text_model(
509 input_ids,
510 positions,
511 intermediate_tensors,
512 inputs_embeds=inputs_embeds,
513 )
514 return hidden_states
515
516
517 @MULTIMODAL_REGISTRY.register_processor(
518 Idefics3MultiModalProcessor,
519 info=Idefics3ProcessingInfo,
520 dummy_inputs=Idefics3DummyInputsBuilder,
521 )
522 class Idefics3ForConditionalGeneration(
523 nn.Module, SupportsMultiModal, SupportsLoRA, SupportsEncoderCudaGraph
524 ):
525 supports_encoder_cudagraph = True
526
527 packed_modules_mapping = {
528 "qkv_proj": [
529 "q_proj",
530 "k_proj",
531 "v_proj",
532 ],
533 "gate_up_proj": [
534 "gate_proj",
535 "up_proj",
536 ],
537 }
538
539 supports_tower_connector_lora = True
540
541 @classmethod
542 def get_placeholder_str(cls, modality: str, i: int) -> str | None:
543 if modality.startswith("image"):
544 return "<image>"
545
546 raise ValueError("Only image modality is supported")
547
548 def __init__(self, *, vllm_config: VllmConfig, prefix: str = ""):
549 super().__init__()
550
551 config = vllm_config.model_config.hf_config
552 quant_config = vllm_config.quant_config
553 multimodal_config = vllm_config.model_config.multimodal_config
554
555 self.config = config
556 self.multimodal_config = multimodal_config
557
558 with self._mark_composite_model(
559 vllm_config,
560 language_targets=LlamaModel,
561 tower_targets={"image": (Idefics3VisionTransformer, Idefics3Connector)},
562 ):
563 self.model = Idefics3Model(
564 vllm_config=vllm_config,
565 prefix=maybe_prefix(prefix, "model"),
566 )
567
568 self.image_token_id = self.config.image_token_id
569
570 self.lm_head = ParallelLMHead(
571 config.text_config.vocab_size,
572 config.text_config.hidden_size,
573 quant_config=quant_config,
574 prefix=maybe_prefix(prefix, "lm_head"),
575 )
576 if self.config.text_config.tie_word_embeddings:
577 self.lm_head = self.lm_head.tie_weights(self.model.text_model.embed_tokens)
578 self.logits_processor = LogitsProcessor(config.text_config.vocab_size)
579
580 def _parse_and_validate_image_input(self, **kwargs: object) -> ImageInputs | None:
581 pixel_values = kwargs.pop("pixel_values", None)
582 image_embeds = kwargs.pop("image_embeds", None)
583
584 if pixel_values is None and image_embeds is None:
585 return None
586
587 if image_embeds is not None:
588 return Idefics3ImageEmbeddingInputs(
589 type="image_embeds",
590 data=image_embeds,
591 )
592
593 if pixel_values is not None:
594 pixel_attention_mask = kwargs.pop("pixel_attention_mask")
595 num_patches = kwargs.pop("num_patches")
596 expected_h = expected_w = self.config.vision_config.image_size
597
598 return Idefics3ImagePixelInputs(
599 type="pixel_values",
600 pixel_values=pixel_values,
601 pixel_attention_mask=pixel_attention_mask,
602 num_patches=num_patches,
603 resolve_bindings={"h": expected_h, "w": expected_w},
604 )
605
606 raise AssertionError("This line should be unreachable.")
607
608 def _process_image_pixels(self, inputs: Idefics3ImagePixelInputs) -> torch.Tensor:
609 pixel_values = inputs["pixel_values"]
610 pixel_attention_mask = inputs["pixel_attention_mask"]
611
612 return self.model.image_pixels_to_features(
613 pixel_values,
614 pixel_attention_mask=pixel_attention_mask,
615 )
616
617 def _process_image_input(
618 self,
619 image_input: ImageInputs,
620 ) -> torch.Tensor | list[torch.Tensor]:
621 if image_input["type"] == "image_embeds":
622 return image_input["data"]
623
624 assert isinstance(image_input, Idefics3ImagePixelInputs)
625 image_features = self._process_image_pixels(image_input)
626 image_features = self.model.connector(image_features)
627
628 num_patches = image_input["num_patches"]
629 return [e.flatten(0, 1) for e in image_features.split(num_patches.tolist())]
630
631 # -- SupportsEncoderCudaGraph protocol methods --
632
633 def get_encoder_cudagraph_config(self):
634 from vllm.v1.worker.encoder_cudagraph_defs import EncoderCudaGraphConfig
635
636 return EncoderCudaGraphConfig(
637 modalities=["image"],
638 buffer_keys=["pixel_values", "pixel_attention_mask", "position_ids"],
639 out_hidden_size=self.config.text_config.hidden_size,
640 )
641
642 def get_encoder_cudagraph_budget_range(
643 self,
644 vllm_config: VllmConfig,
645 ) -> tuple[int, int]:
646 min_budget = self.model.image_seq_len
647 max_budget = min(
648 vllm_config.scheduler_config.max_num_batched_tokens,
649 vllm_config.model_config.max_model_len,
650 )
651 return (min_budget, max_budget)
652
653 def _get_num_patches_list(self, mm_kwargs: dict[str, Any]) -> list[int]:
654 num_patches = mm_kwargs["num_patches"]
655 if isinstance(num_patches, torch.Tensor):
656 return num_patches.tolist()
657 return [int(n) for n in num_patches]
658
659 def get_encoder_cudagraph_item_specs(
660 self,
661 mm_kwargs: dict[str, Any],
662 ):
663 from vllm.v1.worker.encoder_cudagraph_defs import EncoderItemSpec
664
665 return [
666 EncoderItemSpec(
667 input_size=num_patches,
668 output_tokens=num_patches * self.model.image_seq_len,
669 )
670 for num_patches in self._get_num_patches_list(mm_kwargs)
671 ]
672
673 def select_encoder_cudagraph_items(
674 self,
675 mm_kwargs: dict[str, Any],
676 indices: list[int],
677 ) -> dict[str, Any]:
678 pixel_values = mm_kwargs["pixel_values"]
679 pixel_attention_mask = mm_kwargs["pixel_attention_mask"]
680 num_patches_list = self._get_num_patches_list(mm_kwargs)
681
682 if len(indices) == 0:
683 return {
684 "pixel_values": pixel_values[:0],
685 "pixel_attention_mask": pixel_attention_mask[:0],
686 "num_patches": torch.empty(
687 (0,), dtype=torch.long, device=pixel_values.device
688 ),
689 }
690
691 cum_patches = [0]
692 for num_patches in num_patches_list:
693 cum_patches.append(cum_patches[-1] + num_patches)
694
695 selected_pixel_values = torch.cat(
696 [pixel_values[cum_patches[i] : cum_patches[i + 1]] for i in indices]
697 )
698 selected_attention_mask = torch.cat(
699 [pixel_attention_mask[cum_patches[i] : cum_patches[i + 1]] for i in indices]
700 )
701 selected_num_patches = torch.tensor(
702 [num_patches_list[i] for i in indices],
703 dtype=torch.long,
704 device=pixel_values.device,
705 )
706
707 return {
708 "pixel_values": selected_pixel_values,
709 "pixel_attention_mask": selected_attention_mask,
710 "num_patches": selected_num_patches,
711 }
712
713 def prepare_encoder_cudagraph_capture_inputs(
714 self,
715 token_budget: int,
716 max_batch_size: int,
717 max_frames_per_batch: int,
718 device: torch.device,
719 dtype: torch.dtype,
720 path: str = "default",
721 axis_keys: tuple[Hashable, ...] | None = None,
722 ):
723 from vllm.v1.worker.encoder_cudagraph_defs import (
724 EncoderCudaGraphCaptureInputs,
725 )
726
727 image_size = self.config.vision_config.image_size
728 num_tiles = max(
729 (token_budget + self.model.image_seq_len - 1) // self.model.image_seq_len,
730 1,
731 )
732 dummy_pixel_values = torch.randn(
733 num_tiles, 3, image_size, image_size, device=device, dtype=dtype
734 )
735 dummy_pixel_attention_mask = torch.ones(
736 num_tiles, image_size, image_size, device=device, dtype=torch.bool
737 )
738 dummy_position_ids = self.model.get_position_ids(dummy_pixel_attention_mask)
739
740 return EncoderCudaGraphCaptureInputs(
741 values={
742 "pixel_values": dummy_pixel_values,
743 "pixel_attention_mask": dummy_pixel_attention_mask,
744 "position_ids": dummy_position_ids,
745 }
746 )
747
748 def prepare_encoder_cudagraph_replay_buffers(
749 self,
750 mm_kwargs: dict[str, Any],
751 max_batch_size: int,
752 max_frames_per_batch: int,
753 path: str = "default",
754 ):
755 from vllm.v1.worker.encoder_cudagraph_defs import (
756 EncoderCudaGraphReplayBuffers,
757 )
758
759 return EncoderCudaGraphReplayBuffers(
760 values={
761 "pixel_values": mm_kwargs["pixel_values"],
762 "pixel_attention_mask": mm_kwargs["pixel_attention_mask"],
763 "position_ids": self.model.get_position_ids(
764 mm_kwargs["pixel_attention_mask"]
765 ),
766 }
767 )
768
769 def encoder_cudagraph_forward(
770 self,
771 inputs: dict[str, torch.Tensor],
772 path: str = "default",
773 ) -> torch.Tensor:
774 image_features = self.model.image_pixels_to_features(
775 inputs["pixel_values"],
776 pixel_attention_mask=inputs["pixel_attention_mask"],
777 remove_padding=False,
778 position_ids=inputs["position_ids"],
779 )
780 image_features = self.model.connector(image_features)
781 return image_features.flatten(0, 1)
782
783 def encoder_eager_forward(
784 self,
785 mm_kwargs: dict[str, Any],
786 path: str = "default",
787 ) -> torch.Tensor:
788 image_input = self._parse_and_validate_image_input(**mm_kwargs)
789 assert isinstance(image_input, Idefics3ImagePixelInputs)
790 image_features = self._process_image_pixels(image_input)
791 image_features = self.model.connector(image_features)
792 return image_features.flatten(0, 1)
793
794 def embed_multimodal(self, **kwargs: object) -> MultiModalEmbeddings:
795 image_input = self._parse_and_validate_image_input(**kwargs)
796 if image_input is None:
797 return []
798
799 return self._process_image_input(image_input)
800
801 def forward(
802 self,
803 input_ids: torch.Tensor | None,
804 positions: torch.Tensor,
805 intermediate_tensors: IntermediateTensors | None = None,
806 inputs_embeds: torch.Tensor | None = None,
807 **kwargs: object,
808 ) -> torch.Tensor | IntermediateTensors:
809 if intermediate_tensors is not None:
810 inputs_embeds = None
811
812 hidden_states = self.model.text_model(
813 input_ids, positions, intermediate_tensors, inputs_embeds=inputs_embeds
814 )
815
816 return hidden_states
817
818 def compute_logits(self, hidden_states: torch.Tensor) -> torch.Tensor:
819 logits = self.logits_processor(self.lm_head, hidden_states)
820 return logits
821
822 def load_weights(self, weights: Iterable[tuple[str, torch.Tensor]]) -> set[str]:
823 loader = AutoWeightsLoader(self)
824 return loader.load_weights(weights)
825
826 def get_mm_mapping(self) -> MultiModelKeys:
827 """Get the module prefix in multimodal models."""
828 return MultiModelKeys.from_string_field(
829 language_model="model.text_model",
830 connector="model.connector",
831 tower_model="model.vision_model",
832 )
833
834 def get_mm_lora_token_counts(
835 self,
836 *,
837 modality: str,
838 mm_kwargs: MultiModalKwargsItem | None,
839 num_mm_embeds: int,
840 ) -> tuple[int, int | None]:
841 del modality, mm_kwargs
842 hf_config = self.config
843 scale_factor = hf_config.scale_factor
844 return num_mm_embeds * scale_factor**2, num_mm_embeds