main
py 862 lines 30.9 KB
Raw
1 # SPDX-License-Identifier: Apache-2.0
2 # SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3
4 # --------------------------------------------------------
5 # InternS1
6 # Copyright (c) 2025 Shanghai AI Lab
7 # Licensed under The MIT License [see LICENSE for details]
8 # --------------------------------------------------------
9 from collections.abc import Iterable, Mapping, Sequence
10 from typing import Annotated, Literal, TypeAlias, TypedDict
11
12 import regex as re
13 import torch
14 import torch.nn as nn
15 from transformers import BatchFeature, InternVLProcessor, PreTrainedConfig
16 from transformers.activations import ACT2FN
17 from transformers.models.got_ocr2.image_processing_got_ocr2 import (
18 GotOcr2ImageProcessor,
19 )
20 from transformers.models.internvl.video_processing_internvl import (
21 InternVLVideoProcessor,
22 )
23
24 from vllm.config import VllmConfig
25 from vllm.config.multimodal import MultiModalDummyOptions
26 from vllm.inputs import MultiModalDataDict
27 from vllm.model_executor.layers.quantization import QuantizationConfig
28 from vllm.model_executor.models.interns1_vit import InternS1VisionModel
29 from vllm.model_executor.models.module_mapping import MultiModelKeys
30 from vllm.multimodal import MULTIMODAL_REGISTRY
31 from vllm.multimodal.inputs import (
32 MultiModalFieldConfig,
33 MultiModalKwargsItems,
34 )
35 from vllm.multimodal.parse import (
36 ImageEmbeddingItems,
37 ImageProcessorItems,
38 ImageSize,
39 MultiModalDataItems,
40 )
41 from vllm.multimodal.processing import (
42 BaseDummyInputsBuilder,
43 BaseMultiModalProcessor,
44 BaseProcessingInfo,
45 PromptReplacement,
46 PromptUpdate,
47 PromptUpdateDetails,
48 cached_encode,
49 )
50 from vllm.sequence import IntermediateTensors
51 from vllm.transformers_utils.processor import cached_video_processor_from_config
52 from vllm.utils.tensor_schema import TensorSchema, TensorShape
53
54 from .interfaces import (
55 MultiModalEmbeddings,
56 SupportsLoRA,
57 SupportsMultiModal,
58 SupportsPP,
59 )
60 from .utils import (
61 AutoWeightsLoader,
62 WeightsMapper,
63 init_vllm_registered_model,
64 maybe_prefix,
65 )
66
67
68 class InternS1MultiModalProjector(nn.Module):
69 def __init__(self, config):
70 super().__init__()
71 self.layer_norm = nn.LayerNorm(
72 config.vision_config.hidden_size * int(1 / config.downsample_ratio) ** 2
73 )
74 self.linear_1 = nn.Linear(
75 config.vision_config.hidden_size * int(1 / config.downsample_ratio) ** 2,
76 config.text_config.hidden_size,
77 )
78 self.act = ACT2FN[config.projector_hidden_act]
79 self.linear_2 = nn.Linear(
80 config.text_config.hidden_size, config.text_config.hidden_size
81 )
82
83 def forward(self, image_features):
84 hidden_states = self.layer_norm(image_features)
85 hidden_states = self.linear_1(hidden_states)
86 hidden_states = self.act(hidden_states)
87 hidden_states = self.linear_2(hidden_states)
88 return hidden_states
89
90
91 class InternS1ImagePixelInputs(TensorSchema):
92 """Dimensions:
93 - bnp: Batch size * number of images * (1 + num_patches)
94 - c: Number of channels (3)
95 - h: Height
96 - w: Width
97 - bn: Batch size * number of images
98 """
99
100 type: Literal["pixel_values"] = "pixel_values"
101 pixel_values: Annotated[torch.Tensor, TensorShape("bnp", 3, "h", "w")]
102 num_patches: Annotated[torch.Tensor, TensorShape("bn")]
103
104
105 class InternS1ImageEmbeddingInputs(TensorSchema):
106 """Dimensions:
107 - ni: Number of images
108 - tifs: Total image feature size
109 - hs: Hidden size (must match language model backbone)
110 """
111
112 type: Literal["image_embeds"] = "image_embeds"
113 data: Annotated[torch.Tensor | list[torch.Tensor], TensorShape("ni", "tifs", "hs")]
114
115
116 InternS1ImageInputs: TypeAlias = InternS1ImagePixelInputs | InternS1ImageEmbeddingInputs
117
118
119 class InternS1VideoPixelInputs(TensorSchema):
120 """Dimensions:
121 - bnv: Batch size * number of videos * number of frames
122 - bn: Batch size * number of images
123 - c: Number of channels (3)
124 - h: Height
125 - w: Width
126 """
127
128 type: Literal["pixel_values_videos"] = "pixel_values_videos"
129 pixel_values: Annotated[torch.Tensor, TensorShape("bnv", 3, "h", "w")]
130 num_patches: Annotated[torch.Tensor, TensorShape("bn")]
131
132
133 class InternS1VideoEmbeddingInputs(TensorSchema):
134 """Dimensions:
135 - nv: Number of videos
136 - tvfs: Total video feature size
137 - hs: Hidden size (must match language model backbone)
138 """
139
140 type: Literal["video_embeds"] = "video_embeds"
141 data: Annotated[torch.Tensor | list[torch.Tensor], TensorShape("nv", "tvfs", "hs")]
142
143
144 InternS1VideoInputs: TypeAlias = InternS1VideoPixelInputs | InternS1VideoEmbeddingInputs
145
146
147 class InternS1MultiModalInputs(TypedDict, total=False):
148 images: InternS1ImageInputs | None
149 videos: InternS1VideoInputs | None
150
151
152 def resolve_interns1_min_max_num(
153 min_dynamic_patch: int,
154 max_dynamic_patch: int,
155 dynamic_image_size: bool,
156 use_thumbnail: bool,
157 ) -> tuple[int, int]:
158 min_dynamic_patch = min_dynamic_patch if dynamic_image_size else 1
159 max_dynamic_patch = max_dynamic_patch if dynamic_image_size else 1
160
161 if use_thumbnail and max_dynamic_patch != 1:
162 max_dynamic_patch += 1
163
164 return min_dynamic_patch, max_dynamic_patch
165
166
167 def get_interns1_target_ratios(
168 min_num: int,
169 max_num: int,
170 ) -> list[tuple[int, int]]:
171 target_ratios = {
172 (i, j)
173 for n in range(min_num, max_num + 1)
174 for i in range(1, n + 1)
175 for j in range(1, n + 1)
176 if min_num <= i * j <= max_num
177 }
178 return sorted(target_ratios, key=lambda x: x[0] * x[1])
179
180
181 class InternS1ProcessingInfo(BaseProcessingInfo):
182 """ProcessingInfo for InternS1-style models."""
183
184 def get_hf_processor(self, **kwargs: object) -> InternVLProcessor:
185 hf_processor = self.ctx.get_hf_processor(InternVLProcessor, **kwargs)
186 hf_processor.video_processor = cached_video_processor_from_config(
187 self.ctx.model_config,
188 processor_cls=InternVLVideoProcessor,
189 size=hf_processor.image_processor.size,
190 **kwargs,
191 )
192 return hf_processor
193
194 def get_supported_mm_limits(self) -> Mapping[str, int | None]:
195 return {"image": None, "video": None}
196
197 def get_num_image_tokens(
198 self,
199 *,
200 image_width: int,
201 image_height: int,
202 processor: InternVLProcessor,
203 mm_kwargs: Mapping[str, object],
204 ) -> int:
205 image_processor: GotOcr2ImageProcessor = processor.image_processor
206
207 num_image_patches = image_processor.get_number_of_image_patches(
208 image_height,
209 image_width,
210 self.ctx.get_merged_mm_kwargs(mm_kwargs),
211 )
212
213 return processor.image_seq_length * num_image_patches
214
215 def resolve_target_ratios(self, use_thumbnail: bool | None = None):
216 image_processor = self.get_hf_processor().image_processor
217 min_dynamic_patch = image_processor.min_patches
218 max_dynamic_patch = image_processor.max_patches
219 # HF format's InternVL processor uses `crop_to_patches` which is
220 # equivalent to `use_thumbnail` in original format.
221 use_thumbnail = image_processor.crop_to_patches
222 dynamic_image_size = True
223 min_num, max_num = resolve_interns1_min_max_num(
224 min_dynamic_patch,
225 max_dynamic_patch,
226 dynamic_image_size,
227 use_thumbnail=use_thumbnail,
228 )
229
230 return get_interns1_target_ratios(min_num, max_num)
231
232 def get_image_size_with_most_features(self) -> ImageSize:
233 processor = self.get_hf_processor()
234
235 hf_config = self.ctx.get_hf_config()
236 base_height, base_width = hf_config.vision_config.image_size
237 target_ratios = self.resolve_target_ratios()
238
239 largest_feature_size, largest_feature_pinpoint = 0, None
240 for wr, hr in target_ratios:
241 width, height = base_width * wr, base_height * hr
242
243 feat_size = self.get_num_image_tokens(
244 image_width=width,
245 image_height=height,
246 processor=processor,
247 mm_kwargs={},
248 )
249 if feat_size > largest_feature_size:
250 largest_feature_size = feat_size
251 largest_feature_pinpoint = ImageSize(width=width, height=height)
252
253 assert not (largest_feature_size == 0 or largest_feature_pinpoint is None), (
254 "Cannot have a largest feature size of 0!"
255 )
256
257 return largest_feature_pinpoint
258
259 def get_max_image_tokens(self) -> int:
260 processor = self.get_hf_processor()
261 target_width, target_height = self.get_image_size_with_most_features()
262
263 return self.get_num_image_tokens(
264 image_width=target_width,
265 image_height=target_height,
266 processor=processor,
267 mm_kwargs={},
268 )
269
270 def get_num_frames_with_most_features(
271 self,
272 seq_len: int,
273 mm_counts: Mapping[str, int],
274 ) -> int:
275 max_images = mm_counts.get("image", 0)
276 max_videos = mm_counts.get("video", 0)
277
278 processor = self.get_hf_processor()
279
280 max_image_tokens = self.get_max_image_tokens() * max_images
281 max_total_frames = (seq_len - max_image_tokens) // processor.image_seq_length
282 max_frames_per_video = max_total_frames // max(max_videos, 1)
283
284 return max(max_frames_per_video, 1)
285
286
287 class InternS1DummyInputsBuilder(BaseDummyInputsBuilder[InternS1ProcessingInfo]):
288 """DummyInputsBuilder for InternS1-style models."""
289
290 def get_dummy_text(self, mm_counts: Mapping[str, int]) -> str:
291 num_images = mm_counts.get("image", 0)
292 num_videos = mm_counts.get("video", 0)
293 image_token = self.info.get_hf_processor().image_token
294 video_token = self.info.get_hf_processor().video_token
295
296 return image_token * num_images + video_token * num_videos
297
298 def get_dummy_mm_data(
299 self,
300 seq_len: int,
301 mm_counts: Mapping[str, int],
302 mm_options: MultiModalDummyOptions,
303 ) -> MultiModalDataDict:
304 target_width, target_height = self.info.get_image_size_with_most_features()
305 target_num_frames = self.info.get_num_frames_with_most_features(
306 seq_len, mm_counts
307 )
308
309 config = self.info.get_hf_config()
310 image_size_h, image_size_w = config.vision_config.image_size
311
312 return {
313 "image": self._get_dummy_images(
314 width=target_width,
315 height=target_height,
316 num_images=mm_counts.get("image", 0),
317 overrides=mm_options.get("image"),
318 ),
319 "video": self._get_dummy_videos(
320 width=image_size_w,
321 height=image_size_h,
322 num_frames=target_num_frames,
323 num_videos=mm_counts.get("video", 0),
324 overrides=mm_options.get("video"),
325 ),
326 }
327
328
329 class InternS1MultiModalProcessor(BaseMultiModalProcessor[InternS1ProcessingInfo]):
330 """Basic image-only MultiModalProcessor for InternS1-style models."""
331
332 def _get_hf_mm_text(self, mm_counts: Mapping[str, int]) -> str:
333 return self.dummy_inputs.get_dummy_text(mm_counts)
334
335 def _apply_hf_processor_main(
336 self,
337 mm_items: MultiModalDataItems,
338 hf_kwargs: Mapping[str, object],
339 ) -> BatchFeature:
340 hf_data, hf_kwargs, passthrough_data = self._get_hf_mm_inputs(
341 mm_items, hf_kwargs
342 )
343
344 if not hf_data:
345 return self._finalize_hf_mm_data(hf_data, hf_kwargs, passthrough_data)
346
347 prompt_text = hf_data.pop("text")
348 assert isinstance(prompt_text, str)
349
350 videos = hf_data.pop("videos", [])
351 images = hf_data.pop("images", [])
352 assert isinstance(videos, list)
353 assert isinstance(images, list)
354
355 hf_processor = self.info.get_hf_processor(**hf_kwargs)
356 tokenizer = hf_processor.tokenizer
357 vocab = tokenizer.get_vocab()
358
359 video_token_id = vocab[hf_processor.video_token]
360
361 prompt_text = re.sub(
362 hf_processor.image_token, "<image_placeholder>", prompt_text
363 )
364 prompt_text = re.sub(
365 hf_processor.video_token, "<video_placeholder>", prompt_text
366 )
367
368 image_outputs = {}
369 if images:
370 image_pixel_values = []
371 for image in images:
372 processed_data = self.info.ctx.call_hf_processor(
373 self.info.get_hf_processor(**hf_kwargs),
374 dict(text=hf_processor.image_token, **{"images": image}),
375 hf_kwargs,
376 )
377 image_pixel_values.append(processed_data.pop("pixel_values"))
378
379 input_ids = processed_data.pop("input_ids")
380 image_placeholder = tokenizer.batch_decode(input_ids)[0]
381 prompt_text = prompt_text.replace(
382 "<image_placeholder>", image_placeholder, 1
383 )
384
385 num_patches = [len(item) for item in image_pixel_values]
386 image_outputs = {
387 "pixel_values": torch.concat(image_pixel_values),
388 "image_num_patches": torch.tensor(num_patches),
389 "image_token_id": torch.tensor(hf_processor.image_token_id),
390 }
391
392 video_outputs = {}
393 if videos:
394 video_pixel_values = []
395 for video in videos:
396 processed_data = self.info.ctx.call_hf_processor(
397 self.info.get_hf_processor(**hf_kwargs),
398 dict(text=hf_processor.video_token, **{"videos": video}),
399 hf_kwargs,
400 )
401 video_pixel_values.append(processed_data.pop("pixel_values"))
402
403 input_ids = processed_data.pop("input_ids")
404 input_ids[input_ids == hf_processor.image_token_id] = video_token_id
405
406 video_placeholder = tokenizer.batch_decode(input_ids)[0]
407 prompt_text = prompt_text.replace(
408 "<video_placeholder>", video_placeholder, 1
409 )
410
411 num_frames = [len(item) for item in video_pixel_values]
412 video_outputs = {
413 "pixel_values_videos": torch.concat(video_pixel_values),
414 "video_num_patches": torch.tensor(num_frames),
415 "video_token_id": torch.tensor(video_token_id),
416 }
417
418 prompt_text = re.sub(
419 "<image_placeholder>", hf_processor.image_token, prompt_text
420 )
421 prompt_text = re.sub(
422 "<video_placeholder>", hf_processor.video_token, prompt_text
423 )
424 text_outputs = tokenizer(prompt_text, return_tensors="pt")
425
426 processed_data.update(text_outputs)
427 processed_data.update(image_outputs)
428 processed_data.update(video_outputs)
429 return self._finalize_hf_mm_data(
430 hf_data, hf_kwargs, passthrough_data, processed_data
431 )
432
433 def _get_mm_fields_config(
434 self,
435 hf_inputs: BatchFeature,
436 hf_processor_mm_kwargs: Mapping[str, object],
437 ) -> Mapping[str, MultiModalFieldConfig]:
438 image_num_patches = hf_inputs.get("image_num_patches", torch.empty(0))
439 video_num_patches = hf_inputs.get("video_num_patches", torch.empty(0))
440 num_images = len(image_num_patches)
441 num_videos = len(video_num_patches)
442
443 return dict(
444 pixel_values=MultiModalFieldConfig.flat_from_sizes(
445 "image", image_num_patches
446 ),
447 image_num_patches=MultiModalFieldConfig.batched("image", keep_on_cpu=True),
448 image_embeds=MultiModalFieldConfig.batched("image"),
449 image_token_id=MultiModalFieldConfig.shared(
450 "image", num_images, keep_on_cpu=True
451 ),
452 pixel_values_videos=MultiModalFieldConfig.flat_from_sizes(
453 "video", video_num_patches
454 ),
455 video_num_patches=MultiModalFieldConfig.batched("video", keep_on_cpu=True),
456 video_token_id=MultiModalFieldConfig.shared(
457 "video", num_videos, keep_on_cpu=True
458 ),
459 )
460
461 def _get_prompt_updates(
462 self,
463 mm_items: MultiModalDataItems,
464 hf_processor_mm_kwargs: Mapping[str, object],
465 out_mm_kwargs: MultiModalKwargsItems,
466 ) -> Sequence[PromptUpdate]:
467 hf_processor = self.info.get_hf_processor(**hf_processor_mm_kwargs)
468 img_context_token = hf_processor.image_token
469 img_context_token_id = hf_processor.image_token_id
470 start_image_token = hf_processor.start_image_token
471 end_image_token = hf_processor.end_image_token
472 video_token = hf_processor.video_token
473
474 tokenizer = self.info.get_tokenizer()
475 video_token_ids = cached_encode(
476 tokenizer, video_token, add_special_tokens=False
477 )
478 assert len(video_token_ids) == 1
479 video_token_id = video_token_ids[0]
480
481 out_mm_data = out_mm_kwargs.get_data()
482 if "video_num_patches" in out_mm_data:
483 video_num_patches_tensor = out_mm_data["video_num_patches"]
484 assert isinstance(video_num_patches_tensor, torch.Tensor)
485 video_num_patches: list[int] = video_num_patches_tensor.tolist()
486 else:
487 video_num_patches = []
488
489 if "image_num_patches" in out_mm_data:
490 image_num_patches_tensor = out_mm_data["image_num_patches"]
491 assert isinstance(image_num_patches_tensor, torch.Tensor)
492 image_num_patches: list[int] = image_num_patches_tensor.tolist()
493 else:
494 image_num_patches = []
495
496 def get_replacement_interns1_image(item_idx: int):
497 images = mm_items.get_items(
498 "image", (ImageEmbeddingItems, ImageProcessorItems)
499 )
500
501 if isinstance(images, ImageEmbeddingItems):
502 feature_size = images.get_feature_size(item_idx)
503 else:
504 num_patches = image_num_patches[item_idx]
505 feature_size = num_patches * hf_processor.image_seq_length
506
507 repl_features = img_context_token * feature_size
508 repl_full = start_image_token + repl_features + end_image_token
509 repl_full_ids = cached_encode(
510 tokenizer, repl_full, add_special_tokens=False
511 )
512 return PromptUpdateDetails.select_token_id(
513 repl_full_ids, img_context_token_id
514 )
515
516 def get_replacement_interns1_video(item_idx: int):
517 num_patches = video_num_patches[item_idx]
518 repl_features = video_token * hf_processor.image_seq_length
519 repl_features_with_sep = start_image_token + repl_features + end_image_token
520 # num_patches is equal to num_frames
521 repl_full = "\n".join(
522 [f"Frame{i + 1}: {repl_features_with_sep}" for i in range(num_patches)]
523 )
524
525 repl_full_ids = cached_encode(
526 tokenizer, repl_full, add_special_tokens=False
527 )
528 return PromptUpdateDetails.select_token_id(repl_full_ids, video_token_id)
529
530 return [
531 PromptReplacement(
532 modality="image",
533 target=[img_context_token_id],
534 replacement=get_replacement_interns1_image,
535 ),
536 PromptReplacement(
537 modality="video",
538 target=[video_token_id],
539 replacement=get_replacement_interns1_video,
540 ),
541 ]
542
543
544 @MULTIMODAL_REGISTRY.register_processor(
545 InternS1MultiModalProcessor,
546 info=InternS1ProcessingInfo,
547 dummy_inputs=InternS1DummyInputsBuilder,
548 )
549 class InternS1ForConditionalGeneration(
550 nn.Module, SupportsMultiModal, SupportsPP, SupportsLoRA
551 ):
552 # To ensure correct weight loading and mapping.
553 hf_to_vllm_mapper = WeightsMapper(
554 orig_to_new_prefix={
555 "lm_head.": "language_model.lm_head.",
556 "model.language_model.": "language_model.model.",
557 "model.vision_tower.": "vision_tower.",
558 "model.multi_modal_projector.": "multi_modal_projector.",
559 }
560 )
561
562 @classmethod
563 def get_placeholder_str(cls, modality: str, i: int) -> str | None:
564 # transformers InternVLProcessor uses <IMG_CONTEXT> as the separator
565 # refer to https://github.com/huggingface/transformers/blob/f90de364c2484c7c325bbe05befdcf487bd75b63/src/transformers/models/internvl/processing_internvl.py#L116
566 if modality.startswith("image"):
567 return "<IMG_CONTEXT>"
568 if modality.startswith("video"):
569 return "<video>"
570
571 raise ValueError("Only image or video modality is supported")
572
573 def __init__(self, *, vllm_config: VllmConfig, prefix: str = "") -> None:
574 super().__init__()
575 config = vllm_config.model_config.hf_config
576 quant_config = vllm_config.quant_config
577 multimodal_config = vllm_config.model_config.multimodal_config
578
579 self.config = config
580 self.multimodal_config = multimodal_config
581
582 image_size = config.vision_config.image_size[0]
583 patch_size = config.vision_config.patch_size[0]
584 self.patch_size = patch_size
585 self.num_image_token = int(
586 (image_size // patch_size) ** 2 * (config.downsample_ratio**2)
587 )
588 self.downsample_ratio = config.downsample_ratio
589
590 with self._mark_tower_model(vllm_config, {"image", "video"}):
591 self.vision_tower = self._init_vision_model(
592 config,
593 quant_config=quant_config,
594 prefix=maybe_prefix(prefix, "vision_tower"),
595 )
596 self.multi_modal_projector = self._init_mlp1(config)
597
598 with self._mark_language_model(vllm_config):
599 self.language_model = init_vllm_registered_model(
600 vllm_config=vllm_config,
601 hf_config=config.text_config,
602 prefix=maybe_prefix(prefix, "language_model"),
603 )
604
605 self.img_context_token_id: int | None = None
606 self.video_context_token_id: int | None = None
607
608 self.visual_token_mask = None
609 self.make_empty_intermediate_tensors = (
610 self.language_model.make_empty_intermediate_tensors
611 )
612
613 def _init_vision_model(
614 self,
615 config: PreTrainedConfig,
616 quant_config: QuantizationConfig | None,
617 *,
618 prefix: str,
619 ):
620 num_hidden_layers = config.vision_config.num_hidden_layers
621 return InternS1VisionModel(
622 config.vision_config,
623 quant_config=quant_config,
624 num_hidden_layers_override=num_hidden_layers,
625 prefix=prefix,
626 )
627
628 def _init_mlp1(self, config: PreTrainedConfig) -> nn.Module:
629 return InternS1MultiModalProjector(config)
630
631 def pixel_shuffle(self, x, scale_factor=0.5):
632 n, w, h, c = x.size()
633 # N, W, H, C --> N, W, H * scale, C // scale
634 x = x.view(n, w, int(h * scale_factor), int(c / scale_factor))
635 # N, W, H * scale, C // scale --> N, H * scale, W, C // scale
636 x = x.permute(0, 2, 1, 3).contiguous()
637 x = x.view(
638 n,
639 int(h * scale_factor),
640 int(w * scale_factor),
641 int(c / (scale_factor * scale_factor)),
642 )
643 x = x.permute(0, 2, 1, 3).contiguous()
644 return x
645
646 def extract_feature(self, pixel_values: torch.Tensor) -> torch.Tensor:
647 vit_embeds = self.vision_tower(pixel_values=pixel_values)
648 vit_embeds = vit_embeds[:, 1:, :]
649
650 h = w = int(vit_embeds.shape[1] ** 0.5)
651 vit_embeds = vit_embeds.reshape(vit_embeds.shape[0], h, w, -1)
652 vit_embeds = self.pixel_shuffle(vit_embeds, scale_factor=self.downsample_ratio)
653 vit_embeds = vit_embeds.reshape(vit_embeds.shape[0], -1, vit_embeds.shape[-1])
654
655 vit_embeds = self.multi_modal_projector(vit_embeds)
656 return vit_embeds
657
658 def _parse_and_validate_image_input(
659 self, **kwargs: object
660 ) -> InternS1ImageInputs | None:
661 pixel_values = kwargs.pop("pixel_values", None)
662 image_num_patches = kwargs.pop("image_num_patches", None)
663 image_embeds = kwargs.pop("image_embeds", None)
664
665 if pixel_values is None and image_embeds is None:
666 return None
667
668 if image_embeds is not None:
669 return InternS1ImageEmbeddingInputs(
670 type="image_embeds",
671 data=image_embeds,
672 )
673
674 image_token_id = kwargs["image_token_id"]
675 if isinstance(image_token_id, torch.Tensor):
676 image_token_id = image_token_id.flatten().unique().item()
677
678 assert isinstance(image_token_id, int)
679 self.img_context_token_id = image_token_id
680
681 if pixel_values is not None:
682 h, w = self.config.vision_config.image_size
683 return InternS1ImagePixelInputs(
684 type="pixel_values",
685 pixel_values=pixel_values,
686 num_patches=image_num_patches,
687 resolve_bindings={
688 "h": h,
689 "w": w,
690 },
691 )
692
693 raise AssertionError("This line should be unreachable.")
694
695 def _parse_and_validate_video_input(
696 self, **kwargs: object
697 ) -> InternS1VideoInputs | None:
698 pixel_values_flat_video = kwargs.pop("pixel_values_videos", None)
699 video_num_patches = kwargs.pop("video_num_patches", None)
700 video_embeds = kwargs.pop("video_embeds", None)
701
702 if pixel_values_flat_video is None and video_embeds is None:
703 return None
704
705 if video_embeds is not None:
706 return InternS1VideoEmbeddingInputs(
707 type="video_embeds",
708 data=video_embeds,
709 )
710
711 video_token_id = kwargs["video_token_id"]
712 if isinstance(video_token_id, torch.Tensor):
713 video_token_id = video_token_id.flatten().unique().item()
714
715 assert isinstance(video_token_id, int)
716 self.video_context_token_id = video_token_id
717
718 if pixel_values_flat_video is not None:
719 h, w = self.config.vision_config.image_size
720 return InternS1VideoPixelInputs(
721 type="pixel_values_videos",
722 num_patches=video_num_patches,
723 pixel_values=pixel_values_flat_video,
724 resolve_bindings={
725 "h": h,
726 "w": w,
727 },
728 )
729
730 raise AssertionError("This line should be unreachable.")
731
732 def _process_vision_input(
733 self,
734 image_input: InternS1ImageInputs | InternS1VideoInputs,
735 ) -> tuple[torch.Tensor, ...]:
736 if (
737 image_input["type"] == "image_embeds"
738 or image_input["type"] == "video_embeds"
739 ):
740 return image_input["data"]
741
742 image_embeds = self.extract_feature(image_input["pixel_values"])
743
744 num_patches = image_input["num_patches"]
745
746 # Only one image in the current batch
747 if len(num_patches) == 1:
748 return (image_embeds.view(-1, self.config.text_config.hidden_size),)
749
750 # NOTE: Image embeddings are split into separate tensors for each image
751 # by the size of each embedding.
752 feature_size = image_embeds.shape[1]
753 image_embeds = image_embeds.view(-1, self.config.text_config.hidden_size)
754 image_feature_sizes = [
755 num_patches * feature_size for num_patches in num_patches
756 ]
757 return image_embeds.split(image_feature_sizes)
758
759 def _parse_and_validate_multimodal_inputs(
760 self, **kwargs: object
761 ) -> InternS1MultiModalInputs:
762 modalities: InternS1MultiModalInputs = {}
763
764 # Preserve the order of modalities if there are multiple of them
765 # from the order of kwargs.
766 for input_key in kwargs:
767 if (
768 input_key in ("pixel_values", "image_embeds")
769 and "images" not in modalities
770 ):
771 modalities["images"] = self._parse_and_validate_image_input(**kwargs)
772 if input_key in ("pixel_values_videos",) and "videos" not in modalities:
773 modalities["videos"] = self._parse_and_validate_video_input(**kwargs)
774
775 return modalities
776
777 def _set_visual_token_mask(self, input_ids: torch.Tensor) -> None:
778 self.visual_token_mask = None
779
780 def embed_multimodal(self, **kwargs: object) -> MultiModalEmbeddings:
781 modalities = self._parse_and_validate_multimodal_inputs(**kwargs)
782 if not modalities:
783 return []
784
785 # The result multimodal_embeddings is tuple of tensors, with each
786 # tensor corresponding to a multimodal data item (image or video).
787 multimodal_embeddings: tuple[torch.Tensor, ...] = ()
788
789 # NOTE: It is important to iterate over the keys in this dictionary
790 # to preserve the order of the modalities.
791 for modality in modalities:
792 if modality == "images":
793 image_input = modalities["images"]
794 if image_input is not None:
795 image_embeddings = self._process_vision_input(image_input)
796 multimodal_embeddings += tuple(image_embeddings)
797 if modality == "videos":
798 video_input = modalities["videos"]
799 if video_input is not None:
800 video_embeddings = self._process_vision_input(video_input)
801 multimodal_embeddings += tuple(video_embeddings)
802
803 return multimodal_embeddings
804
805 def embed_input_ids(
806 self,
807 input_ids: torch.Tensor,
808 multimodal_embeddings: MultiModalEmbeddings | None = None,
809 *,
810 is_multimodal: torch.Tensor | None = None,
811 ) -> torch.Tensor:
812 if multimodal_embeddings is not None and len(multimodal_embeddings) > 0:
813 self._set_visual_token_mask(input_ids)
814
815 # This is to satisfy the type checker for each overload
816 if multimodal_embeddings is None or is_multimodal is None:
817 return super().embed_input_ids(input_ids)
818
819 return super().embed_input_ids(
820 input_ids,
821 multimodal_embeddings=multimodal_embeddings,
822 is_multimodal=is_multimodal,
823 )
824
825 def forward(
826 self,
827 input_ids: torch.Tensor | None,
828 positions: torch.Tensor,
829 intermediate_tensors: IntermediateTensors | None = None,
830 inputs_embeds: torch.Tensor | None = None,
831 **kwargs: object,
832 ) -> IntermediateTensors:
833 if intermediate_tensors is not None:
834 inputs_embeds = None
835
836 forward_kwargs = {
837 "input_ids": input_ids,
838 "positions": positions,
839 "intermediate_tensors": intermediate_tensors,
840 "inputs_embeds": inputs_embeds,
841 }
842
843 hidden_states = self.language_model.model(**forward_kwargs)
844 return hidden_states
845
846 def compute_logits(
847 self,
848 hidden_states: torch.Tensor,
849 ) -> torch.Tensor | None:
850 return self.language_model.compute_logits(hidden_states)
851
852 def load_weights(self, weights: Iterable[tuple[str, torch.Tensor]]) -> set[str]:
853 loader = AutoWeightsLoader(self)
854 return loader.load_weights(weights, mapper=self.hf_to_vllm_mapper)
855
856 def get_mm_mapping(self) -> MultiModelKeys:
857 """Get the module prefix in multimodal models."""
858 return MultiModelKeys.from_string_field(
859 language_model="language_model",
860 connector="multi_modal_projector",
861 tower_model="vision_tower",
862 )