main
py 770 lines 25.2 KB
Raw
1 # SPDX-License-Identifier: Apache-2.0
2 # SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3 from collections.abc import Iterable, Mapping
4 from typing import Annotated, Literal, TypeAlias
5
6 import torch
7 import torch.nn as nn
8 from torch.nn import LayerNorm
9 from transformers.models.qwen2_vl import Qwen2VLProcessor
10
11 from vllm.config import VllmConfig
12 from vllm.config.multimodal import MultiModalDummyOptions
13 from vllm.distributed import utils as dist_utils
14 from vllm.distributed.parallel_state import (
15 get_tensor_model_parallel_rank,
16 get_tensor_model_parallel_world_size,
17 )
18 from vllm.inputs import MultiModalDataDict
19 from vllm.model_executor.layers.activation import SiluAndMul
20 from vllm.model_executor.layers.attention import (
21 MMEncoderAttention,
22 )
23 from vllm.model_executor.layers.conv import Conv2dLayer
24 from vllm.model_executor.layers.layernorm import RMSNorm
25 from vllm.model_executor.layers.linear import (
26 ColumnParallelLinear,
27 MergedColumnParallelLinear,
28 QKVParallelLinear,
29 RowParallelLinear,
30 )
31 from vllm.model_executor.layers.quantization import QuantizationConfig
32 from vllm.model_executor.layers.rotary_embedding.common import (
33 ApplyRotaryEmb,
34 )
35 from vllm.model_executor.models.interfaces import (
36 MultiModalEmbeddings,
37 SupportsLoRA,
38 SupportsMultiModal,
39 SupportsPP,
40 )
41 from vllm.model_executor.models.module_mapping import MultiModelKeys
42 from vllm.model_executor.models.qwen2 import Qwen2ForCausalLM
43 from vllm.model_executor.models.qwen2_vl import (
44 Qwen2VisionAttention,
45 Qwen2VLDummyInputsBuilder,
46 Qwen2VLMultiModalProcessor,
47 Qwen2VLProcessingInfo,
48 )
49 from vllm.model_executor.models.utils import (
50 AutoWeightsLoader,
51 WeightsMapper,
52 init_vllm_registered_model,
53 maybe_prefix,
54 )
55 from vllm.model_executor.models.vision import get_vit_attn_backend
56 from vllm.multimodal import MULTIMODAL_REGISTRY
57 from vllm.multimodal.inputs import MultiModalKwargsItem
58 from vllm.sequence import IntermediateTensors
59 from vllm.transformers_utils.configs.dotsocr import DotsOCRConfig, DotsVisionConfig
60 from vllm.utils.tensor_schema import TensorSchema, TensorShape
61 from vllm.v1.attention.backends.registry import AttentionBackendEnum
62
63 from .vision import is_vit_use_data_parallel, run_dp_sharded_mrope_vision_model
64
65 IMAGE_TOKEN = "<|imgpad|>"
66
67
68 class DotsOCRImagePixelInputs(TensorSchema):
69 """Dimensions:
70 - np: The total number of patches over each image over each prompt in
71 the batch
72 - ni: Number of images
73 - cps: Number of channels * patch_size * patch_size
74 """
75
76 type: Literal["pixel_values"]
77
78 pixel_values: Annotated[torch.Tensor, TensorShape("np", "cps")]
79 image_grid_thw: Annotated[torch.Tensor, TensorShape("ni", 3)]
80
81
82 class DotsOCRImageEmbeddingInputs(TensorSchema):
83 """Dimensions:
84 - nf: Number of image features
85 - hs: Hidden size
86 - ni: Number of images
87 """
88
89 type: Literal["image_embeds"]
90
91 image_embeds: Annotated[torch.Tensor, TensorShape("nf", "hs")]
92 image_grid_thw: Annotated[torch.Tensor, TensorShape("ni", 3)]
93
94
95 DotsOCRImageInputs: TypeAlias = DotsOCRImagePixelInputs | DotsOCRImageEmbeddingInputs
96
97
98 class DotsOCRDummyInputsBuilder(Qwen2VLDummyInputsBuilder):
99 def get_dummy_text(self, mm_counts: Mapping[str, int]) -> str:
100 num_images = mm_counts.get("image", 0)
101 return IMAGE_TOKEN * num_images
102
103 def get_dummy_mm_data(
104 self,
105 seq_len: int,
106 mm_counts: Mapping[str, int],
107 mm_options: MultiModalDummyOptions,
108 ) -> MultiModalDataDict:
109 target_width, target_height = self.info.get_image_size_with_most_features()
110
111 return {
112 "image": self._get_dummy_images(
113 width=target_width,
114 height=target_height,
115 num_images=mm_counts.get("image", 0),
116 overrides=mm_options.get("image"),
117 ),
118 }
119
120
121 class DotsOCRProcessingInfo(Qwen2VLProcessingInfo):
122 def get_hf_config(self) -> DotsOCRConfig:
123 config = self.ctx.get_hf_config()
124 if not config.__class__.__name__ == "DotsOCRConfig":
125 raise TypeError(f"Expected DotsOCRConfig, got {type(config)}")
126
127 if hasattr(config, "vision_config") and isinstance(config.vision_config, dict):
128 config.vision_config = DotsVisionConfig(**config.vision_config)
129
130 return config
131
132 def get_supported_mm_limits(self) -> Mapping[str, int | None]:
133 return {"image": None}
134
135 def get_mm_max_tokens_per_item(
136 self,
137 seq_len: int,
138 mm_counts: Mapping[str, int],
139 ) -> Mapping[str, int]:
140 max_image_tokens = self.get_max_image_tokens()
141 return {"image": max_image_tokens}
142
143 def get_hf_processor(
144 self,
145 **kwargs: object,
146 ) -> Qwen2VLProcessor:
147 setattr(self.get_tokenizer(), "image_token", IMAGE_TOKEN) # noqa: B010
148 processor = self.ctx.get_hf_processor(
149 Qwen2VLProcessor,
150 **kwargs,
151 )
152 processor.image_token = IMAGE_TOKEN
153 processor.video_token = "<|video_pad|>"
154 return processor
155
156
157 class VisionRotaryEmbedding(nn.Module):
158 def __init__(self, dim: int, theta: float = 10000.0) -> None:
159 super().__init__()
160 inv_freq = 1.0 / (theta ** (torch.arange(0, dim, 2, dtype=torch.float) / dim))
161 self.register_buffer("inv_freq", inv_freq, persistent=False)
162
163 def forward(self, seqlen: int) -> torch.Tensor:
164 seq = torch.arange(
165 seqlen, device=self.inv_freq.device, dtype=self.inv_freq.dtype
166 )
167 freqs = torch.outer(seq, self.inv_freq)
168 return freqs
169
170
171 class PatchMerger(nn.Module):
172 def __init__(
173 self,
174 dim: int,
175 context_dim: int,
176 spatial_merge_size: int = 2,
177 pre_norm="layernorm",
178 prefix: str = "",
179 ) -> None:
180 super().__init__()
181 use_data_parallel = is_vit_use_data_parallel()
182 self.hidden_size = context_dim * (spatial_merge_size**2)
183 self.pre_norm = pre_norm
184 if self.pre_norm == "layernorm":
185 self.ln_q = LayerNorm(context_dim, eps=1e-6)
186 elif self.pre_norm == "rmsnorm":
187 self.ln_q = RMSNorm(context_dim, eps=1e-6)
188
189 self.mlp = nn.Sequential(
190 ColumnParallelLinear(
191 self.hidden_size,
192 self.hidden_size,
193 bias=True,
194 return_bias=False,
195 prefix=f"{prefix}.0",
196 disable_tp=use_data_parallel,
197 ),
198 nn.GELU(),
199 RowParallelLinear(
200 self.hidden_size,
201 dim,
202 bias=True,
203 return_bias=False,
204 prefix=f"{prefix}.2",
205 disable_tp=use_data_parallel,
206 ),
207 )
208
209 def forward(self, x: torch.Tensor) -> torch.Tensor:
210 if self.pre_norm:
211 x = self.mlp(self.ln_q(x).view(-1, self.hidden_size))
212 else:
213 x = self.mlp(x.view(-1, self.hidden_size))
214 return x
215
216
217 class DotsVisionAttention(nn.Module):
218 def __init__(
219 self,
220 config,
221 dim: int,
222 num_heads: int = 16,
223 bias: bool = True,
224 *,
225 quant_config: QuantizationConfig | None = None,
226 prefix: str = "",
227 ) -> None:
228 super().__init__()
229 use_data_parallel = is_vit_use_data_parallel()
230
231 self.embed_dim = dim
232 self.tp_size = (
233 1 if use_data_parallel else get_tensor_model_parallel_world_size()
234 )
235 self.tp_rank = 0 if use_data_parallel else get_tensor_model_parallel_rank()
236 self.hidden_size_per_attention_head = dist_utils.divide(dim, num_heads)
237 self.num_attention_heads_per_partition = dist_utils.divide(
238 num_heads, self.tp_size
239 )
240 # qkv/proj follow Qwen2-VL style; bias controlled by arg
241 self.qkv = QKVParallelLinear(
242 hidden_size=dim,
243 head_size=self.hidden_size_per_attention_head,
244 total_num_heads=num_heads,
245 bias=bias,
246 quant_config=quant_config,
247 prefix=f"{prefix}.qkv",
248 disable_tp=use_data_parallel,
249 )
250 self.proj = RowParallelLinear(
251 input_size=dim,
252 output_size=dim,
253 bias=bias,
254 quant_config=quant_config,
255 prefix=f"{prefix}.proj",
256 disable_tp=use_data_parallel,
257 )
258
259 self.attn = MMEncoderAttention(
260 num_heads=self.num_attention_heads_per_partition,
261 head_size=self.hidden_size_per_attention_head,
262 scale=self.hidden_size_per_attention_head**-0.5,
263 prefix=f"{prefix}.attn",
264 )
265
266 self.apply_rotary_emb = ApplyRotaryEmb(
267 enforce_enable=True,
268 enable_fp32_compute=True,
269 )
270
271 def forward(
272 self,
273 hidden_states: torch.Tensor,
274 cu_seqlens: torch.Tensor,
275 rotary_pos_emb: torch.Tensor | None = None,
276 *,
277 max_seqlen: torch.Tensor | None = None,
278 ) -> torch.Tensor:
279 # [S, C] -> [S, B=1, C]
280 x = hidden_states.unsqueeze(1)
281 x, _ = self.qkv(x)
282 q, k, v = Qwen2VisionAttention.split_qkv(self, x)
283 bs = q.shape[1]
284 # [S,B,H,D] -> [B,S,H,D]
285 q = q.permute(1, 0, 2, 3).contiguous()
286 k = k.permute(1, 0, 2, 3).contiguous()
287 v = v.permute(1, 0, 2, 3).contiguous()
288
289 if rotary_pos_emb is not None:
290 qk_concat = torch.cat([q, k], dim=0)
291 qk_rotated = self.apply_rotary_emb(
292 qk_concat,
293 rotary_pos_emb.cos(),
294 rotary_pos_emb.sin(),
295 )
296 q, k = torch.chunk(qk_rotated, 2, dim=0)
297
298 context_layer = self.attn(
299 query=q,
300 key=k,
301 value=v,
302 cu_seqlens=cu_seqlens,
303 max_seqlen=max_seqlen,
304 )
305
306 # [B,S,H,D] -> [S,B,H*D] -> [S, C]
307 context_layer = context_layer.permute(1, 0, 2, 3).contiguous()
308 context_layer = context_layer.view(context_layer.shape[0], bs, -1)
309 out, _ = self.proj(context_layer)
310 return out.squeeze(1)
311
312
313 class DotsSwiGLUFFN(nn.Module):
314 def __init__(
315 self,
316 config,
317 *,
318 quant_config: QuantizationConfig | None = None,
319 prefix: str = "",
320 ):
321 super().__init__()
322 hidden_features = config.intermediate_size
323 in_features = config.embed_dim
324 bias = config.use_bias
325
326 use_data_parallel = is_vit_use_data_parallel()
327 # Referenced aimv2.py AIMv2SwiGLUFFN
328 self.fc13 = MergedColumnParallelLinear(
329 in_features,
330 [hidden_features] * 2,
331 bias=bias,
332 quant_config=quant_config,
333 prefix=f"{prefix}.fc13",
334 disable_tp=use_data_parallel,
335 )
336 self.fc2 = RowParallelLinear(
337 hidden_features,
338 in_features,
339 bias=bias,
340 quant_config=quant_config,
341 prefix=f"{prefix}.fc2",
342 disable_tp=use_data_parallel,
343 )
344 self.act_fn = SiluAndMul()
345
346 def forward(self, x: torch.Tensor) -> torch.Tensor:
347 x, _ = self.fc13(x)
348 x = self.act_fn(x)
349 x, _ = self.fc2(x)
350 return x
351
352
353 class DotsPatchEmbed(nn.Module):
354 def __init__(self, config):
355 super().__init__()
356 self.num_channels = config.num_channels
357 self.patch_size = config.patch_size
358 self.temporal_patch_size = config.temporal_patch_size
359 self.embed_dim = config.embed_dim
360 self.config = config
361 self.proj = Conv2dLayer(
362 config.num_channels,
363 config.embed_dim,
364 kernel_size=(config.patch_size, config.patch_size),
365 stride=(config.patch_size, config.patch_size),
366 )
367 self.norm = RMSNorm(config.embed_dim, eps=config.rms_norm_eps)
368
369 def forward(self, x: torch.Tensor, grid_thw=None) -> torch.Tensor:
370 x = x.view(
371 -1,
372 self.num_channels,
373 self.temporal_patch_size,
374 self.patch_size,
375 self.patch_size,
376 )[:, :, 0]
377 x = self.proj(x).view(-1, self.embed_dim)
378 x = self.norm(x)
379 return x
380
381
382 class DotsViTPreprocessor(nn.Module):
383 def __init__(self, config):
384 super().__init__()
385 self.patch_h = config.patch_size
386 self.patch_w = config.patch_size
387 self.embed_dim = config.embed_dim
388 self.config = config
389 self.patchifier = DotsPatchEmbed(config)
390
391 def forward(self, x: torch.Tensor, grid_thw=None) -> torch.Tensor:
392 tokens = self.patchifier(x, grid_thw)
393 return tokens
394
395
396 class DotsVisionBlock(nn.Module):
397 def __init__(
398 self,
399 config,
400 *,
401 quant_config: QuantizationConfig | None = None,
402 prefix: str = "",
403 ):
404 super().__init__()
405
406 self.attn = DotsVisionAttention(
407 config,
408 config.embed_dim,
409 num_heads=config.num_attention_heads,
410 bias=config.use_bias,
411 quant_config=quant_config,
412 prefix=f"{prefix}.attn",
413 )
414 self.norm1 = RMSNorm(config.embed_dim, eps=config.rms_norm_eps)
415 self.mlp = DotsSwiGLUFFN(
416 config,
417 quant_config=quant_config,
418 prefix=f"{prefix}.mlp",
419 )
420 self.norm2 = RMSNorm(config.embed_dim, eps=config.rms_norm_eps)
421
422 def forward(
423 self,
424 hidden_states: torch.Tensor,
425 *,
426 cu_seqlens: torch.Tensor,
427 rotary_pos_emb: torch.Tensor,
428 max_seqlen: torch.Tensor | None = None,
429 ) -> torch.Tensor:
430 hidden_states = hidden_states + self.attn(
431 self.norm1(hidden_states),
432 cu_seqlens=cu_seqlens,
433 rotary_pos_emb=rotary_pos_emb,
434 max_seqlen=max_seqlen,
435 )
436 hidden_states = hidden_states + self.mlp(self.norm2(hidden_states))
437 return hidden_states
438
439
440 class DotsVisionTransformer(nn.Module):
441 def __init__(
442 self,
443 config: DotsVisionConfig,
444 quant_config: QuantizationConfig | None = None,
445 *,
446 num_hidden_layers_override: int | None = None,
447 require_post_norm: bool | None = None,
448 prefix: str = "",
449 ) -> None:
450 super().__init__()
451 self.config = config
452 self.spatial_merge_size = config.spatial_merge_size
453
454 self.patch_embed = DotsViTPreprocessor(config)
455
456 head_dim = config.embed_dim // config.num_attention_heads
457 self.rotary_pos_emb = VisionRotaryEmbedding(head_dim // 2)
458 self.attn_backend = get_vit_attn_backend(
459 head_size=head_dim,
460 dtype=torch.get_default_dtype(),
461 )
462 self.out_hidden_size = config.hidden_size
463 # Keep blocks for compatibility with other vision towers
464 num_layers = (
465 config.num_hidden_layers
466 if num_hidden_layers_override is None
467 else num_hidden_layers_override
468 )
469 self.blocks = nn.ModuleList(
470 [
471 DotsVisionBlock(
472 config,
473 quant_config=quant_config,
474 prefix=f"{prefix}.blocks.{i}",
475 )
476 for i in range(num_layers)
477 ]
478 )
479 if require_post_norm is None:
480 require_post_norm = len(self.blocks) == config.num_hidden_layers
481 self.post_trunk_norm: RMSNorm | None
482 if require_post_norm and self.config.post_norm:
483 self.post_trunk_norm = RMSNorm(config.embed_dim, eps=config.rms_norm_eps)
484 else:
485 self.post_trunk_norm = None
486
487 self.merger = PatchMerger(
488 dim=config.hidden_size,
489 context_dim=config.embed_dim,
490 spatial_merge_size=config.spatial_merge_size,
491 )
492
493 @property
494 def dtype(self) -> torch.dtype:
495 return self.patch_embed.patchifier.proj.weight.dtype
496
497 @property
498 def device(self) -> torch.device:
499 return self.patch_embed.patchifier.proj.weight.device
500
501 def get_pos_ids_by_grid(self, grid_thw: list[list[int]]) -> list[torch.Tensor]:
502 pos_ids = []
503 for t, h, w in grid_thw:
504 hpos_ids = torch.arange(h).unsqueeze(1).expand(-1, w)
505 hpos_ids = hpos_ids.reshape(
506 h // self.spatial_merge_size,
507 self.spatial_merge_size,
508 w // self.spatial_merge_size,
509 self.spatial_merge_size,
510 )
511 hpos_ids = hpos_ids.permute(0, 2, 1, 3)
512 hpos_ids = hpos_ids.flatten()
513
514 wpos_ids = torch.arange(w).unsqueeze(0).expand(h, -1)
515 wpos_ids = wpos_ids.reshape(
516 h // self.spatial_merge_size,
517 self.spatial_merge_size,
518 w // self.spatial_merge_size,
519 self.spatial_merge_size,
520 )
521 wpos_ids = wpos_ids.permute(0, 2, 1, 3)
522 wpos_ids = wpos_ids.flatten()
523 pos_ids.append(torch.stack([hpos_ids, wpos_ids], dim=-1).repeat(t, 1))
524
525 return pos_ids
526
527 def rot_pos_emb(self, grid_thw: list[list[int]]) -> torch.Tensor:
528 pos_ids = self.get_pos_ids_by_grid(grid_thw)
529 pos_ids = torch.cat(pos_ids, dim=0)
530 max_grid_size = max(max(h, w) for _, h, w in grid_thw)
531 rotary_pos_emb_full = self.rotary_pos_emb(max_grid_size)
532 rotary_pos_emb = rotary_pos_emb_full[pos_ids].flatten(1)
533 return rotary_pos_emb
534
535 def compute_attn_mask_seqlen(self, cu_seqlens: torch.Tensor) -> torch.Tensor | None:
536 max_seqlen = None
537 if self.attn_backend in {
538 AttentionBackendEnum.FLASH_ATTN,
539 AttentionBackendEnum.ROCM_AITER_FA,
540 AttentionBackendEnum.TRITON_ATTN,
541 }:
542 max_seqlen = (cu_seqlens[1:] - cu_seqlens[:-1]).max()
543 return max_seqlen
544
545 def forward(
546 self, hidden_states: torch.Tensor, grid_thw: list[list[int]]
547 ) -> torch.Tensor:
548 rotary_pos_emb = self.rot_pos_emb(grid_thw)
549
550 # Convert grid_thw to tensor (always expecting list format now)
551 grid_thw_tensor = torch.tensor(
552 grid_thw, device=hidden_states.device, dtype=torch.long
553 )
554 hidden_states = hidden_states.to(self.dtype)
555 hidden_states = self.patch_embed(hidden_states, grid_thw_tensor)
556
557 cu_seqlens = torch.repeat_interleave(
558 grid_thw_tensor[:, 1] * grid_thw_tensor[:, 2], grid_thw_tensor[:, 0]
559 ).cumsum(
560 dim=0,
561 dtype=grid_thw_tensor.dtype if torch.jit.is_tracing() else torch.int32,
562 )
563 cu_seqlens = torch.cat([cu_seqlens.new_zeros(1), cu_seqlens])
564
565 max_seqlen = self.compute_attn_mask_seqlen(cu_seqlens)
566 for blk in self.blocks:
567 hidden_states = blk(
568 hidden_states,
569 cu_seqlens=cu_seqlens,
570 rotary_pos_emb=rotary_pos_emb,
571 max_seqlen=max_seqlen,
572 )
573
574 if self.post_trunk_norm is not None:
575 hidden_states = self.post_trunk_norm(hidden_states)
576
577 hidden_states = self.merger(hidden_states)
578 return hidden_states
579
580
581 @MULTIMODAL_REGISTRY.register_processor(
582 Qwen2VLMultiModalProcessor,
583 info=DotsOCRProcessingInfo,
584 dummy_inputs=DotsOCRDummyInputsBuilder,
585 )
586 class DotsOCRForCausalLM(nn.Module, SupportsMultiModal, SupportsPP, SupportsLoRA):
587 hf_to_vllm_mapper = WeightsMapper(
588 orig_to_new_substr={
589 ".attn.qkv_proj.": ".attn.qkv.",
590 ".attn.out_proj.": ".attn.proj.",
591 },
592 orig_to_new_stacked={
593 ".fc1.": (".fc13.", 0),
594 ".fc3.": (".fc13.", 1),
595 },
596 orig_to_new_prefix={
597 "lm_head.": "language_model.lm_head.",
598 "model.": "language_model.model.",
599 },
600 )
601
602 packed_modules_mapping = {
603 "qkv_proj": [
604 "q_proj",
605 "k_proj",
606 "v_proj",
607 ],
608 "gate_up_proj": [
609 "gate_proj",
610 "up_proj",
611 ],
612 ".attn.qkv": [".attn.qkv"],
613 "fc13": ["fc1", "fc3"],
614 }
615 supports_encoder_tp_data = True
616 supports_tower_connector_lora = True
617
618 @classmethod
619 def get_placeholder_str(cls, modality: str, i: int) -> str | None:
620 if modality.startswith("image"):
621 return "<|img|><|imgpad|><|endofimg|>"
622 return None
623
624 def __init__(self, *, vllm_config: VllmConfig, prefix: str = ""):
625 super().__init__()
626
627 self.config: DotsOCRConfig = vllm_config.model_config.hf_config
628 self.quant_config = vllm_config.quant_config
629 multimodal_config = vllm_config.model_config.multimodal_config
630 assert multimodal_config is not None
631 self.use_data_parallel = multimodal_config.mm_encoder_tp_mode == "data"
632 if isinstance(self.config.vision_config, dict):
633 vision_config = DotsVisionConfig(**self.config.vision_config)
634 self.config.vision_config = vision_config
635 else:
636 vision_config = self.config.vision_config
637
638 with self._mark_tower_model(vllm_config, "image"):
639 self.vision_tower = DotsVisionTransformer(
640 vision_config,
641 quant_config=self.quant_config,
642 prefix=maybe_prefix(prefix, "vision_tower"),
643 )
644
645 with self._mark_language_model(vllm_config):
646 self.language_model: Qwen2ForCausalLM = init_vllm_registered_model(
647 vllm_config=vllm_config,
648 hf_config=self.config,
649 prefix=maybe_prefix(prefix, "language_model"),
650 architectures=["Qwen2ForCausalLM"],
651 )
652
653 self.make_empty_intermediate_tensors = (
654 self.language_model.make_empty_intermediate_tensors
655 )
656
657 def _parse_and_validate_image_input(
658 self, **kwargs: object
659 ) -> DotsOCRImageInputs | None:
660 pixel_values = kwargs.pop("pixel_values", None)
661 image_embeds = kwargs.pop("image_embeds", None)
662 image_grid_thw = kwargs.pop("image_grid_thw", None)
663
664 if pixel_values is None and image_embeds is None:
665 return None
666
667 if pixel_values is not None:
668 return DotsOCRImagePixelInputs(
669 type="pixel_values",
670 pixel_values=pixel_values,
671 image_grid_thw=image_grid_thw,
672 )
673
674 if image_embeds is not None:
675 return DotsOCRImageEmbeddingInputs(
676 type="image_embeds",
677 image_embeds=image_embeds,
678 image_grid_thw=image_grid_thw,
679 )
680
681 raise AssertionError
682
683 def _process_image_input(
684 self, image_input: DotsOCRImageInputs
685 ) -> tuple[torch.Tensor, ...]:
686 grid_thw = image_input["image_grid_thw"]
687 assert grid_thw.ndim == 2
688 grid_thw_list = grid_thw.tolist()
689
690 if image_input["type"] == "image_embeds":
691 image_embeds = image_input["image_embeds"].type(self.vision_tower.dtype)
692 else:
693 pixel_values = image_input["pixel_values"].type(self.vision_tower.dtype)
694
695 if self.use_data_parallel:
696 return run_dp_sharded_mrope_vision_model(
697 self.vision_tower,
698 pixel_values,
699 grid_thw_list,
700 rope_type="rope_3d",
701 )
702 else:
703 image_embeds = self.vision_tower(pixel_values, grid_thw_list)[
704 :, : self.config.hidden_size
705 ]
706
707 # Split concatenated embeddings for each image item.
708 merge_size = self.vision_tower.spatial_merge_size
709 sizes = (
710 torch.tensor(grid_thw_list, dtype=torch.long).prod(-1)
711 // (merge_size * merge_size)
712 ).tolist()
713
714 return image_embeds.split(sizes)
715
716 def get_mm_lora_token_counts(
717 self,
718 *,
719 modality: str,
720 mm_kwargs: MultiModalKwargsItem | None,
721 num_mm_embeds: int,
722 ) -> tuple[int, int | None]:
723 del modality, mm_kwargs
724 merge_size = self.vision_tower.spatial_merge_size
725 return num_mm_embeds * (merge_size**2), num_mm_embeds
726
727 def embed_multimodal(self, **kwargs: object) -> MultiModalEmbeddings:
728 image_input = self._parse_and_validate_image_input(**kwargs)
729 if image_input is None:
730 return []
731 vision_embeddings = self._process_image_input(image_input)
732 return vision_embeddings
733
734 def forward(
735 self,
736 input_ids: torch.Tensor | None,
737 positions: torch.Tensor,
738 intermediate_tensors: IntermediateTensors | None = None,
739 inputs_embeds: torch.Tensor | None = None,
740 **kwargs,
741 ) -> torch.Tensor | IntermediateTensors:
742 if intermediate_tensors is not None:
743 inputs_embeds = None
744
745 hidden_states = self.language_model(
746 input_ids=input_ids,
747 positions=positions,
748 intermediate_tensors=intermediate_tensors,
749 inputs_embeds=inputs_embeds,
750 )
751
752 return hidden_states
753
754 def compute_logits(
755 self,
756 hidden_states: torch.Tensor,
757 ) -> torch.Tensor | None:
758 return self.language_model.compute_logits(hidden_states)
759
760 def load_weights(self, weights: Iterable[tuple[str, torch.Tensor]]) -> set[str]:
761 loader = AutoWeightsLoader(self)
762 return loader.load_weights(weights, mapper=self.hf_to_vllm_mapper)
763
764 def get_mm_mapping(self) -> MultiModelKeys:
765 """Get the module prefix in multimodal models."""
766 return MultiModelKeys.from_string_field(
767 language_model="language_model",
768 connector="vision_tower.merger",
769 tower_model="vision_tower.",
770 )