main
py 1,477 lines 48.4 KB
Raw
1 # SPDX-License-Identifier: Apache-2.0
2 # SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3
4 import math
5 from collections.abc import Iterable, Mapping, Sequence
6 from dataclasses import dataclass
7 from functools import partial
8 from itertools import islice
9 from typing import Annotated
10
11 import numpy as np
12 import torch
13 import torch.nn as nn
14 import torch.nn.functional as F
15 from einops import rearrange
16 from transformers import (
17 BaseImageProcessor,
18 BatchFeature,
19 PreTrainedConfig,
20 )
21
22 from vllm.compilation.decorators import support_torch_compile
23 from vllm.config import CacheConfig, VllmConfig
24 from vllm.config.multimodal import MultiModalDummyOptions
25 from vllm.distributed import (
26 get_pp_group,
27 get_tensor_model_parallel_rank,
28 get_tensor_model_parallel_world_size,
29 split_tensor_along_last_dim,
30 tensor_model_parallel_all_gather,
31 )
32 from vllm.inputs import MultiModalDataDict
33 from vllm.model_executor.layers.activation import MulAndSilu, QuickGELU, SiluAndMul
34 from vllm.model_executor.layers.attention import Attention, MMEncoderAttention
35 from vllm.model_executor.layers.layernorm import RMSNorm
36 from vllm.model_executor.layers.linear import (
37 ColumnParallelLinear,
38 MergedColumnParallelLinear,
39 QKVParallelLinear,
40 RowParallelLinear,
41 )
42 from vllm.model_executor.layers.logits_processor import LogitsProcessor
43 from vllm.model_executor.layers.quantization import QuantizationConfig
44 from vllm.model_executor.layers.rotary_embedding import get_rope
45 from vllm.model_executor.layers.vocab_parallel_embedding import (
46 ParallelLMHead,
47 VocabParallelEmbedding,
48 )
49 from vllm.model_executor.models.module_mapping import MultiModelKeys
50 from vllm.multimodal import MULTIMODAL_REGISTRY
51 from vllm.multimodal.inputs import (
52 MultiModalFieldConfig,
53 MultiModalKwargsItems,
54 )
55 from vllm.multimodal.parse import ImageProcessorItems, ImageSize, MultiModalDataItems
56 from vllm.multimodal.processing import (
57 BaseDummyInputsBuilder,
58 BaseMultiModalProcessor,
59 BaseProcessingInfo,
60 PromptIndexTargets,
61 PromptInsertion,
62 PromptUpdate,
63 PromptUpdateDetails,
64 cached_encode,
65 )
66 from vllm.sequence import IntermediateTensors
67 from vllm.utils.tensor_schema import TensorSchema, TensorShape
68
69 from .interfaces import (
70 MultiModalEmbeddings,
71 SupportsLoRA,
72 SupportsMultiModal,
73 SupportsPP,
74 SupportsQuant,
75 )
76 from .utils import (
77 AutoWeightsLoader,
78 WeightsMapper,
79 make_empty_intermediate_tensors_factory,
80 make_layers,
81 maybe_prefix,
82 )
83
84 # TODO: hard-coded for now. Consider making it configurable.
85 VIT_LAYERS = [-2, -9]
86 NUM_PREFIX_TOKENS = 1
87 ADDITIONAL_VOCAB_SIZE = 128
88 IMAGE_PATCH_TOKEN = "<im_patch>"
89 IM_COL_TOKEN = "<im_col>"
90 IM_START_TOKEN = "<im_start>"
91 IM_END_TOKEN = "<im_end>"
92 POOLING_SIZE = 2
93
94
95 class MolmoImageInputs(TensorSchema):
96 """Dimensions:
97 - bn: Batch size * number of images
98 - bnc: Batch size * number of images * number of crops (dynamic)
99 - np: Number of patches
100 - tp: Token sequence positions
101 - pd: Patch dimension
102 """
103
104 images: Annotated[torch.Tensor, TensorShape("bnc", "np", "pd")]
105
106 image_masks: Annotated[torch.Tensor | None, TensorShape("bnc", "np")]
107
108 image_input_idx: Annotated[torch.Tensor, TensorShape("bnc", "tp")]
109 """An index tensor that maps image features to their corresponding patch tokens."""
110
111 num_crops: Annotated[torch.Tensor, TensorShape("bn")]
112
113
114 @dataclass
115 class VisionBackboneConfig:
116 image_default_input_size: tuple[int, int] = (336, 336)
117 image_patch_size: int = 14
118 image_pos_patch_size: int = 14
119 image_emb_dim: int = 1024
120 image_num_heads: int = 16
121 image_num_key_value_heads: int = 16
122 image_num_layers: int = 23
123 image_mlp_dim: int = 4096
124 image_mlp_activations: str = "quick_gelu"
125 image_num_pos: int = 577
126 image_norm_eps: float = 1e-5
127
128 def __post_init__(self):
129 self.image_default_input_size = tuple(self.image_default_input_size) # type: ignore[assignment]
130
131 @property
132 def image_num_patch(self):
133 h, w = self.image_default_input_size
134 return h // self.image_patch_size, w // self.image_patch_size
135
136
137 class ViTMLP(nn.Module):
138 """MLP used in Vision Transformer."""
139
140 def __init__(
141 self,
142 config: VisionBackboneConfig,
143 quant_config: QuantizationConfig | None = None,
144 prefix: str = "",
145 ):
146 super().__init__()
147 self.w1 = ColumnParallelLinear(
148 config.image_emb_dim,
149 config.image_mlp_dim,
150 bias=True,
151 quant_config=quant_config,
152 prefix=f"{prefix}.w1",
153 )
154 # Activation function.
155 assert config.image_mlp_activations == "quick_gelu"
156 self.act = QuickGELU()
157 self.w2 = RowParallelLinear(
158 config.image_mlp_dim,
159 config.image_emb_dim,
160 bias=True,
161 quant_config=quant_config,
162 prefix=f"{prefix}.w2",
163 )
164
165 def forward(self, x: torch.Tensor) -> torch.Tensor:
166 x, _ = self.w1(x)
167 x = self.act(x)
168 x, _ = self.w2(x)
169 return x
170
171
172 class MultiHeadDotProductAttention(nn.Module):
173 """Multi-head attention used in Vision Transformer."""
174
175 def __init__(
176 self,
177 config: VisionBackboneConfig,
178 use_bias: bool = True,
179 nlayers: int = 1,
180 quant_config: QuantizationConfig | None = None,
181 prefix: str = "",
182 ):
183 super().__init__()
184
185 self.hidden_size = config.image_emb_dim
186 self.total_num_heads = config.image_num_heads
187 tp_size = get_tensor_model_parallel_world_size()
188
189 assert self.hidden_size % self.total_num_heads == 0
190 assert self.total_num_heads % tp_size == 0
191
192 self.num_heads = self.total_num_heads // tp_size
193 self.head_dim = self.hidden_size // self.total_num_heads
194
195 self.total_num_kv_heads = config.image_num_key_value_heads
196 if self.total_num_kv_heads >= tp_size:
197 assert self.total_num_kv_heads % tp_size == 0
198 else:
199 assert tp_size % self.total_num_kv_heads == 0
200
201 self.num_kv_heads = max(1, self.total_num_kv_heads // tp_size)
202
203 self.wq = ColumnParallelLinear(
204 nlayers * self.hidden_size,
205 self.total_num_heads * self.head_dim,
206 bias=use_bias,
207 quant_config=quant_config,
208 prefix=f"{prefix}.wq",
209 )
210 self.wk = ColumnParallelLinear(
211 nlayers * self.hidden_size,
212 self.total_num_kv_heads * self.head_dim,
213 bias=use_bias,
214 quant_config=quant_config,
215 prefix=f"{prefix}.wk",
216 )
217 self.wv = ColumnParallelLinear(
218 nlayers * self.hidden_size,
219 self.total_num_kv_heads * self.head_dim,
220 bias=use_bias,
221 quant_config=quant_config,
222 prefix=f"{prefix}.wv",
223 )
224 self.wo = RowParallelLinear(
225 self.total_num_heads * self.head_dim,
226 self.hidden_size,
227 bias=use_bias,
228 quant_config=quant_config,
229 prefix=f"{prefix}.wo",
230 )
231
232 self.scale = self.head_dim**-0.5
233 self.attn = MMEncoderAttention(
234 self.num_heads,
235 self.head_dim,
236 self.scale,
237 num_kv_heads=self.num_kv_heads,
238 prefix=f"{prefix}.attn",
239 )
240
241 def forward(
242 self, inputs_q: torch.Tensor, inputs_kv: torch.Tensor | None = None
243 ) -> torch.Tensor:
244 if inputs_kv is not None:
245 inputs_k = inputs_kv
246 inputs_v = inputs_kv
247 else:
248 inputs_k = inputs_q
249 inputs_v = inputs_q
250
251 xq, _ = self.wq(inputs_q)
252 xk, _ = self.wk(inputs_k)
253 xv, _ = self.wv(inputs_v)
254
255 output = self.attn(xq, xk, xv)
256 output, _ = self.wo(output)
257
258 return output
259
260
261 class ResidualAttentionBlock(nn.Module):
262 """Residual attention block used in Vision Transformer."""
263
264 def __init__(
265 self,
266 config: VisionBackboneConfig,
267 quant_config: QuantizationConfig | None = None,
268 prefix: str = "",
269 ):
270 super().__init__()
271 self.attention = MultiHeadDotProductAttention(
272 config, quant_config=quant_config, prefix=f"{prefix}.attention"
273 )
274 self.feed_forward = ViTMLP(
275 config, quant_config, prefix=f"{prefix}.feed_forward"
276 )
277 self.attention_norm = nn.LayerNorm(
278 config.image_emb_dim,
279 eps=config.image_norm_eps,
280 )
281 self.ffn_norm = nn.LayerNorm(
282 config.image_emb_dim,
283 eps=config.image_norm_eps,
284 )
285
286 def forward(self, x: torch.Tensor) -> torch.Tensor:
287 x = x + self.attention(self.attention_norm(x))
288 x = x + self.feed_forward(self.ffn_norm(x))
289 return x
290
291
292 class BlockCollection(nn.Module):
293 """Collection of residual attention blocks used in Vision Transformer."""
294
295 def __init__(
296 self,
297 config: VisionBackboneConfig,
298 quant_config: QuantizationConfig | None = None,
299 prefix: str = "",
300 ):
301 super().__init__()
302 self.resblocks = nn.ModuleList(
303 [
304 ResidualAttentionBlock(
305 config, quant_config, prefix=f"{prefix}.resblocks.{i}"
306 )
307 for i in range(config.image_num_layers)
308 ]
309 )
310
311 def forward(self, x: torch.Tensor) -> list[torch.Tensor]:
312 hidden_states = []
313 for r in self.resblocks:
314 x = r(x)
315 hidden_states.append(x)
316 return hidden_states
317
318
319 def _expand_token(token: torch.Tensor, batch_size: int) -> torch.Tensor:
320 return token.view(1, 1, -1).expand(batch_size, -1, -1)
321
322
323 class VisionTransformer(nn.Module):
324 """Vision Transformer used in Vision Backbone."""
325
326 def __init__(
327 self,
328 config: VisionBackboneConfig,
329 quant_config: QuantizationConfig | None = None,
330 prefix: str = "",
331 ):
332 super().__init__()
333 scale = config.image_emb_dim**-0.5
334 self.patch_num: tuple[int, int] = config.image_num_patch
335 self.class_embedding = nn.Parameter(torch.randn(config.image_emb_dim) * scale)
336 self.num_prefix_tokens: int = NUM_PREFIX_TOKENS
337 self.positional_embedding = nn.Parameter(
338 torch.randn(config.image_num_pos, config.image_emb_dim) * scale
339 )
340 image_patch_size = config.image_patch_size
341 self.patch_embedding = nn.Linear(
342 image_patch_size * image_patch_size * 3,
343 config.image_emb_dim,
344 bias=False,
345 )
346 self.pre_ln = nn.LayerNorm(config.image_emb_dim, eps=config.image_norm_eps)
347 self.transformer = BlockCollection(
348 config, quant_config, prefix=f"{prefix}.transformer"
349 )
350
351 def add_pos_emb(self, x: torch.Tensor, patch_num: tuple[int, int]) -> torch.Tensor:
352 cls_emb = self.positional_embedding[0:1]
353 pos_emb = self.positional_embedding[1:]
354
355 pos_emb = pos_emb.reshape(
356 (
357 int(math.sqrt(pos_emb.shape[0])),
358 int(math.sqrt(pos_emb.shape[0])),
359 pos_emb.shape[1],
360 )
361 )
362
363 (patch_num_0, patch_num_1) = patch_num
364
365 if pos_emb.shape[0] != patch_num_0 or pos_emb.shape[1] != patch_num_1:
366 # from https://github.com/facebookresearch/mae/blob/main/util/pos_embed.py
367 pos_emb = pos_emb.unsqueeze(0).permute(0, 3, 1, 2)
368 pos_emb = F.interpolate(
369 pos_emb,
370 size=(patch_num_0, patch_num_1),
371 mode="bicubic",
372 align_corners=False,
373 antialias=True,
374 )
375 pos_emb = pos_emb.permute(0, 2, 3, 1).squeeze(0)
376
377 pos_emb = pos_emb.reshape(-1, pos_emb.shape[-1])
378 x = x + torch.cat([cls_emb[None, :, :], pos_emb[None, :, :]], dim=1).to(x.dtype)
379 return x
380
381 def forward(
382 self, x: torch.Tensor, patch_num: tuple[int, int] | None = None
383 ) -> list[torch.Tensor]:
384 """: param x: (batch_size, num_patch, n_pixels)."""
385 if patch_num is None:
386 patch_num = self.patch_num
387 B, N, D = x.shape
388
389 x = self.patch_embedding(x)
390
391 # class embeddings and positional embeddings
392 x = torch.cat(
393 [_expand_token(self.class_embedding, x.shape[0]).to(x.dtype), x], dim=1
394 )
395 x = self.add_pos_emb(x, patch_num)
396
397 x = self.pre_ln(x)
398
399 hidden_states = self.transformer(x)
400 return hidden_states
401
402
403 class MolmoAttention(nn.Module):
404 """Molmo's LLM attention."""
405
406 def __init__(
407 self,
408 config: PreTrainedConfig,
409 cache_config: CacheConfig | None = None,
410 quant_config: QuantizationConfig | None = None,
411 prefix: str = "",
412 ) -> None:
413 super().__init__()
414 self.hidden_size = config.hidden_size
415 self.tp_size = get_tensor_model_parallel_world_size()
416 self.total_num_heads = config.num_attention_heads
417
418 assert self.hidden_size % self.total_num_heads == 0
419 assert self.total_num_heads % self.tp_size == 0
420
421 self.num_heads = self.total_num_heads // self.tp_size
422 self.total_num_kv_heads = config.num_key_value_heads or self.total_num_heads
423 if self.total_num_kv_heads >= self.tp_size:
424 assert self.total_num_kv_heads % self.tp_size == 0
425 else:
426 assert self.tp_size % self.total_num_kv_heads == 0
427
428 self.num_kv_heads = max(1, self.total_num_kv_heads // self.tp_size)
429 self.head_dim = self.hidden_size // self.total_num_heads
430 self.q_size = self.num_heads * self.head_dim
431 self.kv_size = self.num_kv_heads * self.head_dim
432 self.max_position_embeddings = config.max_position_embeddings
433
434 # Attention input projection. Projects x -> (q, k, v)
435 self.qkv_proj = QKVParallelLinear(
436 self.hidden_size,
437 self.head_dim,
438 self.total_num_heads,
439 self.total_num_kv_heads,
440 bias=config.qkv_bias,
441 quant_config=quant_config,
442 prefix=f"{prefix}.qkv_proj",
443 )
444
445 self.tp_rank: int | None = None
446 self.k_norm: nn.Module | None = None
447 self.q_norm: nn.Module | None = None
448 if config.attention_layer_norm:
449 self.tp_rank = get_tensor_model_parallel_rank()
450 self.k_norm = RMSNorm(
451 self.total_num_kv_heads * self.head_dim, eps=config.layer_norm_eps
452 )
453 self.q_norm = RMSNorm(config.hidden_size, eps=config.layer_norm_eps)
454
455 # Rotary embeddings.
456 self.rotary_emb = get_rope(
457 self.head_dim,
458 max_position=self.max_position_embeddings,
459 rope_parameters=config.rope_parameters,
460 )
461 self.scaling = self.head_dim**-0.5
462 self.attn = Attention(
463 self.num_heads,
464 self.head_dim,
465 self.scaling,
466 num_kv_heads=self.num_kv_heads,
467 cache_config=cache_config,
468 quant_config=quant_config,
469 prefix=f"{prefix}.attn",
470 )
471
472 # Attention output projection.
473 self.o_proj = RowParallelLinear(
474 self.total_num_heads * self.head_dim,
475 self.hidden_size,
476 bias=False,
477 quant_config=quant_config,
478 prefix=f"{prefix}.o_proj",
479 )
480
481 def _apply_qk_norm(
482 self, q: torch.Tensor, k: torch.Tensor
483 ) -> tuple[torch.Tensor, torch.Tensor]:
484 assert self.q_norm is not None
485 assert self.k_norm is not None
486 if self.tp_size > 1:
487 q = tensor_model_parallel_all_gather(q.contiguous())
488 k = tensor_model_parallel_all_gather(k.contiguous())
489 q = self.q_norm(q)
490 k = self.k_norm(k)
491 if self.tp_size > 1:
492 assert self.tp_rank is not None
493 splitter = partial(split_tensor_along_last_dim, num_partitions=self.tp_size)
494 q = splitter(q)[self.tp_rank]
495 k = splitter(k)[self.tp_rank]
496 return q, k
497
498 def forward(
499 self,
500 positions: torch.Tensor,
501 hidden_states: torch.Tensor,
502 ) -> torch.Tensor:
503 qkv, _ = self.qkv_proj(hidden_states)
504 q, k, v = qkv.split([self.q_size, self.kv_size, self.kv_size], dim=-1)
505 if self.q_norm is not None and self.k_norm is not None:
506 q, k = self._apply_qk_norm(q, k)
507 q, k = self.rotary_emb(positions, q, k)
508 attn_output = self.attn(q, k, v)
509 output, _ = self.o_proj(attn_output)
510 return output
511
512
513 class LanguageModelMLP(nn.Module):
514 """Molmo's LLM mlp."""
515
516 def __init__(
517 self,
518 config: PreTrainedConfig,
519 input_dim: int | None = None,
520 quant_config: QuantizationConfig | None = None,
521 prefix: str = "",
522 ) -> None:
523 super().__init__()
524 self.hidden_size = config.hidden_size
525 self.intermediate_size = config.intermediate_size // 2
526
527 self.gate_up_proj = MergedColumnParallelLinear(
528 input_dim or self.hidden_size,
529 [self.intermediate_size] * 2,
530 bias=False,
531 quant_config=quant_config,
532 prefix=f"{prefix}.gate_up_proj",
533 )
534 # Activation function.
535 self.act_fn = MulAndSilu()
536 # Feed-forward output projection.
537 self.down_proj = RowParallelLinear(
538 self.intermediate_size,
539 self.hidden_size,
540 bias=False,
541 quant_config=quant_config,
542 prefix=f"{prefix}.down_proj",
543 )
544
545 def forward(
546 self,
547 x: torch.Tensor,
548 ) -> torch.Tensor:
549 gate_up, _ = self.gate_up_proj(x)
550 x = self.act_fn(gate_up)
551 x, _ = self.down_proj(x)
552 return x
553
554
555 class ImageProjectorMLP(nn.Module):
556 """Molmo's image_projector mlp."""
557
558 def __init__(
559 self,
560 config: PreTrainedConfig,
561 input_dim: int | None = None,
562 quant_config: QuantizationConfig | None = None,
563 prefix: str = "",
564 ) -> None:
565 super().__init__()
566 self.hidden_size = config.hidden_size
567 self.intermediate_size = config.intermediate_size // 2
568
569 self.merged_linear = MergedColumnParallelLinear(
570 input_dim or self.hidden_size,
571 [self.intermediate_size] * 2,
572 bias=False,
573 quant_config=quant_config,
574 prefix=f"{prefix}.merged_linear",
575 )
576 # Activation function.
577 self.act_fn = SiluAndMul()
578
579 # Feed-forward output projection.
580 self.down_proj = RowParallelLinear(
581 self.intermediate_size,
582 self.hidden_size,
583 bias=False,
584 quant_config=quant_config,
585 prefix=f"{prefix}.down_proj",
586 )
587
588 def forward(
589 self,
590 x: torch.Tensor,
591 ) -> torch.Tensor:
592 gate_up, _ = self.merged_linear(x)
593 x = self.act_fn(gate_up)
594 x, _ = self.down_proj(x)
595 return x
596
597
598 class MolmoDecoderLayer(nn.Module):
599 def __init__(
600 self,
601 config: PreTrainedConfig,
602 cache_config: CacheConfig | None = None,
603 quant_config: QuantizationConfig | None = None,
604 prefix: str = "",
605 ) -> None:
606 super().__init__()
607 # Attention block.
608 self.self_attn = MolmoAttention(
609 config, cache_config, quant_config, prefix=f"{prefix}.self_attn"
610 )
611
612 # MLP block.
613 self.mlp = LanguageModelMLP(
614 config, quant_config=quant_config, prefix=f"{prefix}.mlp"
615 )
616
617 # LayerNorm
618 assert config.layer_norm_type == "rms"
619 self.input_layernorm = RMSNorm(config.hidden_size, eps=config.layer_norm_eps)
620 self.post_attention_layernorm = RMSNorm(
621 config.hidden_size, eps=config.layer_norm_eps
622 )
623
624 def forward(
625 self,
626 positions: torch.Tensor,
627 hidden_states: torch.Tensor,
628 residual: torch.Tensor | None,
629 ) -> tuple[torch.Tensor, tuple[torch.Tensor, torch.Tensor] | None]:
630 # Self Attention
631 if residual is None:
632 residual = hidden_states
633 hidden_states = self.input_layernorm(hidden_states)
634 else:
635 hidden_states, residual = self.input_layernorm(hidden_states, residual)
636 hidden_states = self.self_attn(
637 positions=positions,
638 hidden_states=hidden_states,
639 )
640
641 hidden_states, residual = self.post_attention_layernorm(hidden_states, residual)
642 hidden_states = self.mlp(hidden_states)
643 return hidden_states, residual
644
645
646 class MolmoDecoderNormAfterLayer(MolmoDecoderLayer):
647 def forward(
648 self,
649 positions: torch.Tensor,
650 hidden_states: torch.Tensor,
651 residual: torch.Tensor | None,
652 ) -> tuple[torch.Tensor, tuple[torch.Tensor, torch.Tensor] | None]:
653 # Self Attention
654 residual = hidden_states
655 hidden_states = self.self_attn(
656 positions=positions,
657 hidden_states=hidden_states,
658 )
659
660 hidden_states = self.input_layernorm(hidden_states)
661 hidden_states = hidden_states + residual
662 residual = hidden_states
663
664 hidden_states = self.mlp(hidden_states)
665 hidden_states = self.post_attention_layernorm(hidden_states)
666 hidden_states = hidden_states + residual
667 residual = None
668 return hidden_states, residual
669
670
671 class MolmoVisionBackbone(nn.Module, SupportsQuant):
672 packed_modules_mapping = {"merged_linear": ["gate_proj", "up_proj"]}
673
674 hf_to_vllm_mapper = WeightsMapper(
675 orig_to_new_stacked={
676 # image_projector gate_up merge
677 "gate_proj": ("merged_linear", 0),
678 "up_proj": ("merged_linear", 1),
679 }
680 )
681
682 def __init__(
683 self,
684 config: PreTrainedConfig,
685 vision_config: VisionBackboneConfig,
686 quant_config: QuantizationConfig | None = None,
687 prefix: str = "",
688 ) -> None:
689 super().__init__()
690 self.vit_layers = VIT_LAYERS
691 self.image_num_patch = vision_config.image_num_patch
692 self.llm_patches_per_crop = (
693 (self.image_num_patch[0] + 1) // POOLING_SIZE,
694 (self.image_num_patch[1] + 1) // POOLING_SIZE,
695 )
696 self.image_vit = VisionTransformer(
697 vision_config, quant_config=quant_config, prefix=f"{prefix}.image_vit"
698 )
699 self.num_prefix_tokens = self.image_vit.num_prefix_tokens
700 assert self.num_prefix_tokens in {0, 1}, (
701 "Only 0 or 1 prefix tokens are supported"
702 )
703 self.image_pooling_2d = MultiHeadDotProductAttention(
704 vision_config,
705 nlayers=len(self.vit_layers),
706 quant_config=quant_config,
707 prefix=f"{prefix}.image_pooling_2d",
708 )
709 self.image_projector = ImageProjectorMLP(
710 config,
711 input_dim=vision_config.image_emb_dim,
712 quant_config=quant_config,
713 prefix=f"{prefix}.image_projector",
714 )
715
716 image_dim = vision_config.image_emb_dim * len(self.vit_layers)
717 self.pad_embed = nn.Parameter(torch.zeros((2, image_dim)))
718
719 @property
720 def dtype(self) -> torch.dtype:
721 return self.image_vit.patch_embedding.weight.dtype
722
723 @property
724 def device(self) -> torch.device:
725 return self.image_vit.patch_embedding.weight.device
726
727 def encode_image(self, images: torch.Tensor) -> torch.Tensor:
728 """: param images: (batch_size, num_crops, num_patch, n_pixels)."""
729 B, T, N, D = images.shape
730
731 mask = ~torch.all(images.view(B * T, N, D) == -1, dim=(1, 2), keepdim=True)
732
733 images = images.view(B * T, N, D)
734 image_features = self.image_vit(images)
735
736 if self.vit_layers is not None:
737 features = []
738 for layer in self.vit_layers:
739 features.append(image_features[layer])
740 image_features = torch.cat(features, dim=-1)
741 else:
742 image_features = image_features[-1]
743
744 if self.num_prefix_tokens > 0:
745 image_features = image_features[:, 1:]
746
747 image_features = image_features * mask
748 image_features = image_features.view(B, T, N, -1)
749
750 return image_features
751
752 def forward(
753 self,
754 images: torch.Tensor,
755 image_masks: torch.Tensor,
756 ) -> torch.Tensor:
757 # image_features: (batch_size, num_crops(=num_image), num_patch, nximage_emb_dim) # noqa: E501
758 batch_size, num_image = images.shape[:2]
759 images = images.to(device=self.device, dtype=self.dtype)
760 image_features = self.encode_image(images)
761
762 og_dtype = image_features.dtype
763 assert image_masks is not None
764 pad_embed = self.pad_embed[:, None, None, None, :]
765 all_pad = image_masks == 0
766 partial_pad = torch.logical_and(image_masks < 1, torch.logical_not(all_pad)).to(
767 dtype=torch.float32
768 )
769 all_pad = all_pad.to(dtype=torch.float32)
770 image_features = image_features + pad_embed[0] * torch.unsqueeze(all_pad, -1)
771 image_features = image_features + pad_embed[1] * torch.unsqueeze(
772 partial_pad, -1
773 )
774
775 image_features = image_features.to(og_dtype)
776
777 image_features = image_features.reshape(
778 (batch_size, num_image) + self.image_num_patch + (-1,),
779 )
780
781 if missing_w := self.image_num_patch[0] % POOLING_SIZE:
782 # Padding for image pooling (see below)
783 image_features = F.pad(
784 image_features,
785 (0, 0, 0, missing_w, 0, missing_w, 0, 0, 0, 0),
786 )
787
788 # image pooling
789 image_features = rearrange(
790 image_features,
791 "b n (h dh) (w dw) c -> (b n h w) (dh dw) c",
792 dh=POOLING_SIZE,
793 dw=POOLING_SIZE,
794 )
795
796 query = image_features.mean(-2, keepdim=True)
797 image_features = self.image_pooling_2d(query, image_features)
798
799 h, w = self.llm_patches_per_crop
800 image_features = image_features.view(batch_size, num_image, h * w, -1)
801
802 image_features = self.image_projector(image_features)
803
804 # image_features: (batch_size, num_image, num_patch, d_model)
805 return image_features
806
807 def load_weights(self, weights: Iterable[tuple[str, torch.Tensor]]) -> set[str]:
808 loader = AutoWeightsLoader(self)
809 return loader.load_weights(weights, mapper=self.hf_to_vllm_mapper)
810
811
812 @support_torch_compile
813 class MolmoModel(nn.Module, SupportsQuant):
814 def __init__(self, *, vllm_config: VllmConfig, prefix: str = ""):
815 super().__init__()
816
817 config = vllm_config.model_config.hf_config
818 cache_config = vllm_config.cache_config
819 quant_config = vllm_config.quant_config
820
821 self.config = config
822
823 self.embedding_size = config.embedding_size or config.vocab_size
824 self.embedding_size += ADDITIONAL_VOCAB_SIZE
825 self.embed_tokens = VocabParallelEmbedding(
826 self.embedding_size,
827 config.hidden_size,
828 quant_config=quant_config,
829 )
830
831 decoder_layer = (
832 MolmoDecoderNormAfterLayer if config.norm_after else MolmoDecoderLayer
833 )
834 self.start_layer, self.end_layer, self.layers = make_layers(
835 config.num_hidden_layers,
836 lambda prefix: decoder_layer(
837 config, cache_config, quant_config, prefix=prefix
838 ),
839 prefix=f"{prefix}.layers",
840 )
841
842 assert config.layer_norm_type == "rms"
843 self.norm = RMSNorm(config.hidden_size, config.layer_norm_eps)
844
845 self.make_empty_intermediate_tensors = make_empty_intermediate_tensors_factory(
846 ["hidden_states", "residual"], config.hidden_size
847 )
848
849 def embed_input_ids(self, input_ids: torch.Tensor) -> torch.Tensor:
850 return self.embed_tokens(input_ids)
851
852 def forward(
853 self,
854 input_ids: torch.Tensor | None,
855 positions: torch.Tensor,
856 intermediate_tensors: IntermediateTensors | None = None,
857 inputs_embeds: torch.Tensor | None = None,
858 ) -> torch.Tensor:
859 if get_pp_group().is_first_rank:
860 if inputs_embeds is not None:
861 hidden_states = inputs_embeds
862 else:
863 hidden_states = self.embed_tokens(input_ids)
864 residual = None
865 else:
866 assert intermediate_tensors is not None
867 hidden_states = intermediate_tensors["hidden_states"]
868 residual = intermediate_tensors["residual"]
869
870 # Apply blocks one-by-one.
871 for layer in islice(self.layers, self.start_layer, self.end_layer):
872 hidden_states, residual = layer(
873 positions,
874 hidden_states,
875 residual,
876 )
877 if not get_pp_group().is_last_rank:
878 return IntermediateTensors(
879 {"hidden_states": hidden_states, "residual": residual}
880 )
881 if residual is not None:
882 hidden_states, _ = self.norm(hidden_states, residual)
883 else:
884 hidden_states = self.norm(hidden_states)
885 return hidden_states
886
887 def load_weights(self, weights: Iterable[tuple[str, torch.Tensor]]) -> set[str]:
888 return AutoWeightsLoader(self).load_weights(weights)
889
890
891 def _lowest_multiple(x: int, k: int) -> int:
892 return (x // k) * k
893
894
895 def get_num_patches(
896 num_tiles: int,
897 *,
898 crop_patches: int,
899 left_margin: int,
900 right_margin: int,
901 pooling_size: int,
902 ) -> int:
903 if num_tiles == 1:
904 return _lowest_multiple(crop_patches + pooling_size - 1, pooling_size)
905
906 crop_window_patches = crop_patches - (left_margin + right_margin)
907
908 left_num = _lowest_multiple(
909 crop_window_patches + left_margin + pooling_size - 1,
910 pooling_size,
911 )
912 middle_num = _lowest_multiple(
913 crop_window_patches + pooling_size - 1,
914 pooling_size,
915 )
916 right_num = _lowest_multiple(
917 crop_window_patches + right_margin + pooling_size - 1,
918 pooling_size,
919 )
920
921 return left_num + (num_tiles - 2) * middle_num + right_num
922
923
924 def get_patches_grid_size(
925 *,
926 tiling_h: int,
927 tiling_w: int,
928 crop_patches: int,
929 left_margin: int,
930 right_margin: int,
931 pooling_size: int,
932 ) -> tuple[int, int]:
933 nrows = get_num_patches(
934 tiling_h,
935 crop_patches=crop_patches,
936 left_margin=left_margin,
937 right_margin=right_margin,
938 pooling_size=pooling_size,
939 )
940 ncols = get_num_patches(
941 tiling_w,
942 crop_patches=crop_patches,
943 left_margin=left_margin,
944 right_margin=right_margin,
945 pooling_size=pooling_size,
946 )
947
948 return nrows, ncols
949
950
951 def get_candidate_tilings(max_num: int) -> list[tuple[int, int]]:
952 tilings = [
953 (i, j)
954 for i in range(1, max_num + 1)
955 for j in range(1, max_num + 1)
956 if i * j <= max_num
957 ]
958 return sorted(tilings, key=lambda x: x[0] * x[1])
959
960
961 def select_tiling(
962 *,
963 height: int,
964 width: int,
965 patch_size: int,
966 max_num_patches: int,
967 ):
968 tilings = get_candidate_tilings(max_num_patches)
969 candidate_tilings = np.array(tilings, dtype=np.int32)
970 candidate_resolutions = candidate_tilings * patch_size
971
972 original_size = np.array([height, width], dtype=np.float32)
973 required_scale_d = candidate_resolutions.astype(np.float32) / original_size
974 required_scale = required_scale_d.min(axis=-1, keepdims=True)
975
976 if (required_scale < 1).all():
977 ix = required_scale.argmax()
978 else:
979 ix = np.where(required_scale < 1.0, 10e9, required_scale).argmin()
980
981 return candidate_tilings[ix]
982
983
984 def _as_2tuple(x: int | tuple[int, int]) -> tuple[int, int]:
985 if isinstance(x, int):
986 return x, x
987
988 return x
989
990
991 class MolmoProcessingInfo(BaseProcessingInfo):
992 def get_supported_mm_limits(self) -> Mapping[str, int | None]:
993 return {"image": None}
994
995 def select_tiling(
996 self,
997 *,
998 image_width: int,
999 image_height: int,
1000 image_processor: BaseImageProcessor,
1001 ) -> tuple[int, int]:
1002 max_crops = image_processor.max_crops
1003 left_margin, right_margin = image_processor.overlap_margins
1004 base_image_input_size = _as_2tuple(image_processor.base_image_input_size)
1005 base_image_input_d = image_processor.image_patch_size
1006
1007 total_margin_pixels = base_image_input_d * (right_margin + left_margin)
1008 crop_patches = base_image_input_size[0] // base_image_input_d
1009 crop_window_patches = crop_patches - (right_margin + left_margin)
1010 crop_window_size = crop_window_patches * base_image_input_d
1011 tiling_h, tiling_w = select_tiling(
1012 height=image_height - total_margin_pixels,
1013 width=image_width - total_margin_pixels,
1014 patch_size=crop_window_size,
1015 max_num_patches=max_crops,
1016 )
1017
1018 return tiling_w, tiling_h
1019
1020 def get_patches_grid_size(
1021 self,
1022 *,
1023 image_width: int,
1024 image_height: int,
1025 image_processor: BaseImageProcessor,
1026 ) -> tuple[int, int]:
1027 left_margin, right_margin = image_processor.overlap_margins
1028 base_image_input_size = _as_2tuple(image_processor.base_image_input_size)
1029 base_image_input_d = image_processor.image_patch_size
1030 pooling_size = POOLING_SIZE
1031
1032 crop_patches = base_image_input_size[0] // base_image_input_d
1033 tiling_w, tiling_h = self.select_tiling(
1034 image_height=image_height,
1035 image_width=image_width,
1036 image_processor=image_processor,
1037 )
1038
1039 nrows, ncols = get_patches_grid_size(
1040 tiling_h=tiling_h,
1041 tiling_w=tiling_w,
1042 crop_patches=crop_patches,
1043 left_margin=left_margin,
1044 right_margin=right_margin,
1045 pooling_size=pooling_size,
1046 )
1047
1048 return ncols, nrows
1049
1050 def get_num_image_tokens(
1051 self,
1052 *,
1053 image_width: int,
1054 image_height: int,
1055 image_processor: BaseImageProcessor,
1056 ) -> int:
1057 ncols, nrows = self.get_patches_grid_size(
1058 image_width=image_width,
1059 image_height=image_height,
1060 image_processor=image_processor,
1061 )
1062 pooling_size = POOLING_SIZE
1063
1064 image_token_length_w = image_processor.image_token_length_w
1065 image_token_length_h = image_processor.image_token_length_h
1066
1067 # Calculate total tokens: 2 for start/end + (w+1)*h for column separators
1068 extra = 2 + (image_token_length_w + 1) * image_token_length_h
1069 joint = 2 + ((ncols + 1) // pooling_size + 1) * ((nrows + 1) // pooling_size)
1070
1071 return extra + joint
1072
1073 def get_image_size_with_most_features(self) -> ImageSize:
1074 processor = self.get_hf_processor()
1075 image_processor = processor.image_processor
1076
1077 tilings = get_candidate_tilings(image_processor.max_crops)
1078 base_h, base_w = _as_2tuple(image_processor.base_image_input_size)
1079
1080 largest_feature_size, largest_feature_pinpoint = 0, None
1081 for wr, hr in tilings:
1082 width, height = base_w * wr, base_h * hr
1083
1084 feat_size = self.get_num_image_tokens(
1085 image_width=width,
1086 image_height=height,
1087 image_processor=image_processor,
1088 )
1089 if feat_size > largest_feature_size:
1090 largest_feature_size = feat_size
1091 largest_feature_pinpoint = ImageSize(width=width, height=height)
1092
1093 if largest_feature_size == 0 or largest_feature_pinpoint is None:
1094 raise ValueError("Cannot have a largest feature size of 0!")
1095
1096 return largest_feature_pinpoint
1097
1098
1099 class MolmoDummyInputsBuilder(BaseDummyInputsBuilder[MolmoProcessingInfo]):
1100 def get_dummy_text(self, mm_counts: Mapping[str, int]) -> str:
1101 return ""
1102
1103 def get_dummy_mm_data(
1104 self,
1105 seq_len: int,
1106 mm_counts: Mapping[str, int],
1107 mm_options: MultiModalDummyOptions,
1108 ) -> MultiModalDataDict:
1109 target_width, target_height = self.info.get_image_size_with_most_features()
1110
1111 return {
1112 "image": self._get_dummy_images(
1113 width=target_width,
1114 height=target_height,
1115 num_images=mm_counts.get("image", 0),
1116 overrides=mm_options.get("image"),
1117 )
1118 }
1119
1120
1121 class MolmoMultiModalProcessor(BaseMultiModalProcessor[MolmoProcessingInfo]):
1122 def _get_hf_mm_text(self, mm_counts: Mapping[str, int]) -> str:
1123 return self.dummy_inputs.get_dummy_text(mm_counts)
1124
1125 def _postprocess_prompt(self, prompt: list[int]) -> list[int]:
1126 processor = self.info.get_hf_processor()
1127
1128 # The chat template is already applied to the prompt tokens
1129 # Use message_format="none" to avoid applying it again
1130 # Prepend an empty space if `always_start_with_space` is True
1131 tokens = processor.get_tokens_input(
1132 self.info.get_tokenizer().decode(prompt),
1133 message_format="none",
1134 always_start_with_space=True,
1135 )
1136
1137 # Prepend a BOS token id to the tokens
1138 return self.info.ctx.call_hf_processor(
1139 processor.process,
1140 dict(tokens=tokens),
1141 )["input_ids"].tolist()
1142
1143 def _call_hf_processor(
1144 self,
1145 hf_data: Mapping[str, object],
1146 hf_kwargs: Mapping[str, object],
1147 ) -> BatchFeature:
1148 hf_processor = self.info.get_hf_processor(**hf_kwargs)
1149 return self.info.ctx.call_hf_processor(
1150 hf_processor.process,
1151 hf_data,
1152 hf_kwargs,
1153 )
1154
1155 def _postprocess_hf_mm_data(
1156 self,
1157 hf_data: Mapping[str, object],
1158 hf_kwargs: Mapping[str, object],
1159 processed_data: BatchFeature,
1160 ) -> BatchFeature:
1161 if not hf_data:
1162 return processed_data
1163
1164 hf_processor = self.info.get_hf_processor(**hf_kwargs)
1165 tokenizer = hf_processor.tokenizer
1166 image_patch_id = tokenizer.vocab[IMAGE_PATCH_TOKEN]
1167
1168 image_processor = hf_processor.image_processor
1169
1170 processed_data.pop("input_ids")
1171
1172 if (images := hf_data.get("images")) is not None:
1173 mm_items = self.info.parse_mm_data({"image": images}, validate=False)
1174 parsed_images = mm_items.get_items("image", ImageProcessorItems)
1175 image_sizes = [
1176 parsed_images.get_image_size(i) for i in range(len(parsed_images))
1177 ]
1178
1179 feat_is_patch = processed_data["image_input_idx"] >= 0
1180
1181 tilings = [
1182 self.info.select_tiling(
1183 image_width=image_size.width,
1184 image_height=image_size.height,
1185 image_processor=image_processor,
1186 )
1187 for image_size in image_sizes
1188 ]
1189 # For each image: tiling_h * tiling_w + extra
1190 num_crops = torch.tensor(tilings).prod(-1) + 1
1191 assert num_crops.sum() == len(feat_is_patch)
1192
1193 processed_data["num_crops"] = num_crops
1194 processed_data["img_patch_id"] = image_patch_id
1195
1196 return processed_data
1197
1198 def _get_mm_fields_config(
1199 self,
1200 hf_inputs: BatchFeature,
1201 hf_processor_mm_kwargs: Mapping[str, object],
1202 ) -> Mapping[str, MultiModalFieldConfig]:
1203 num_crops = hf_inputs.get("num_crops", torch.empty(0))
1204 num_images = len(num_crops)
1205
1206 return dict(
1207 images=MultiModalFieldConfig.flat_from_sizes("image", num_crops),
1208 image_masks=MultiModalFieldConfig.flat_from_sizes("image", num_crops),
1209 image_input_idx=MultiModalFieldConfig.flat_from_sizes("image", num_crops),
1210 num_crops=MultiModalFieldConfig.batched("image", keep_on_cpu=True),
1211 img_patch_id=MultiModalFieldConfig.shared(
1212 "image", num_images, keep_on_cpu=True
1213 ),
1214 )
1215
1216 def _get_prompt_updates(
1217 self,
1218 mm_items: MultiModalDataItems,
1219 hf_processor_mm_kwargs: Mapping[str, object],
1220 out_mm_kwargs: MultiModalKwargsItems,
1221 ) -> Sequence[PromptUpdate]:
1222 tokenizer = self.info.get_tokenizer()
1223 vocab = tokenizer.get_vocab()
1224 img_patch_id = vocab[IMAGE_PATCH_TOKEN]
1225 img_col_id = vocab[IM_COL_TOKEN]
1226 img_start_id = vocab[IM_START_TOKEN]
1227 img_end_id = vocab[IM_END_TOKEN]
1228
1229 processor = self.info.get_hf_processor(**hf_processor_mm_kwargs)
1230 image_processor = processor.image_processor
1231 image_token_length_w = image_processor.image_token_length_w
1232 image_token_length_h = image_processor.image_token_length_h
1233 pooling_size = POOLING_SIZE
1234
1235 extra_row = [img_patch_id] * image_token_length_w + [img_col_id]
1236 extra_joint = [img_start_id] + extra_row * image_token_length_h + [img_end_id]
1237
1238 def get_insertion_molmo(item_idx: int):
1239 images = mm_items.get_items("image", ImageProcessorItems)
1240 image_size = images.get_image_size(item_idx)
1241
1242 ncols, nrows = self.info.get_patches_grid_size(
1243 image_width=image_size.width,
1244 image_height=image_size.height,
1245 image_processor=image_processor,
1246 )
1247
1248 joint_row = [img_patch_id] * ((ncols + 1) // pooling_size) + [img_col_id]
1249 joint = (
1250 [img_start_id]
1251 + joint_row * ((nrows + 1) // pooling_size)
1252 + [img_end_id]
1253 )
1254
1255 return PromptUpdateDetails.select_token_id(
1256 extra_joint + joint,
1257 embed_token_id=img_patch_id,
1258 )
1259
1260 return [
1261 PromptInsertion(
1262 modality="image",
1263 target=PromptIndexTargets.prefix(
1264 cached_encode(tokenizer, "<|endoftext|>", add_special_tokens=False),
1265 ),
1266 insertion=get_insertion_molmo,
1267 )
1268 ]
1269
1270
1271 @MULTIMODAL_REGISTRY.register_processor(
1272 MolmoMultiModalProcessor,
1273 info=MolmoProcessingInfo,
1274 dummy_inputs=MolmoDummyInputsBuilder,
1275 )
1276 class MolmoForCausalLM(
1277 nn.Module, SupportsMultiModal, SupportsPP, SupportsLoRA, SupportsQuant
1278 ):
1279 hf_to_vllm_mapper = WeightsMapper(
1280 orig_to_new_substr={
1281 # vision backbone mapping
1282 "image_projector.w1.": "image_projector.gate_proj.",
1283 "image_projector.w3.": "image_projector.up_proj.",
1284 "image_projector.w2.": "image_projector.down_proj.",
1285 # language backbone mapping
1286 "att_proj": "self_attn.qkv_proj",
1287 "attn_out": "self_attn.o_proj",
1288 "q_norm": "self_attn.q_norm",
1289 "k_norm": "self_attn.k_norm",
1290 "ff_proj": "mlp.gate_up_proj",
1291 "ff_out": "mlp.down_proj",
1292 "attn_norm": "input_layernorm",
1293 "ff_norm": "post_attention_layernorm",
1294 },
1295 orig_to_new_prefix={
1296 # vision backbone mapping
1297 "model.vision_backbone.": "vision_backbone.",
1298 # language backbone mapping
1299 "model.transformer.blocks.": "model.layers.",
1300 "model.transformer.ln_f.": "model.norm.",
1301 # lm_head is renamed to model.transformer.mlp.down_proj firstly,
1302 # we need to run a second renaming for it
1303 "model.transformer.mlp.down_proj.": "lm_head.",
1304 },
1305 )
1306
1307 packed_modules_mapping = {
1308 "qkv_proj": ["qkv_proj"],
1309 "gate_up_proj": ["gate_up_proj"], # language model
1310 "merged_linear": ["gate_proj", "up_proj"], # image_projector
1311 }
1312
1313 @classmethod
1314 def get_placeholder_str(cls, modality: str, i: int) -> str | None:
1315 if modality.startswith("image"):
1316 return None
1317
1318 raise ValueError("Only image modality is supported")
1319
1320 def __init__(self, *, vllm_config: VllmConfig, prefix: str = ""):
1321 super().__init__()
1322 config = vllm_config.model_config.hf_config
1323 quant_config = vllm_config.quant_config
1324 multimodal_config = vllm_config.model_config.multimodal_config
1325
1326 self.config = config
1327 self.multimodal_config = multimodal_config
1328
1329 vision_config = VisionBackboneConfig()
1330
1331 with self._mark_tower_model(vllm_config, "image"):
1332 self.vision_backbone = MolmoVisionBackbone(
1333 config,
1334 vision_config,
1335 quant_config,
1336 prefix=maybe_prefix(prefix, "vision_backbone"),
1337 )
1338
1339 with self._mark_language_model(vllm_config):
1340 self.model = MolmoModel(
1341 vllm_config=vllm_config, prefix=maybe_prefix(prefix, "model")
1342 )
1343
1344 self.img_patch_id: int | None = None
1345
1346 self.lm_head = ParallelLMHead(
1347 config.embedding_size or config.vocab_size,
1348 config.hidden_size,
1349 quant_config=quant_config,
1350 prefix=maybe_prefix(prefix, "lm_head"),
1351 )
1352 if self.config.weight_tying:
1353 self.lm_head = self.lm_head.tie_weights(self.model.transformer.wte)
1354
1355 self.logits_processor = LogitsProcessor(
1356 config.embedding_size or config.vocab_size
1357 )
1358
1359 self.make_empty_intermediate_tensors = (
1360 self.model.make_empty_intermediate_tensors
1361 )
1362
1363 def _parse_and_validate_image_input(
1364 self,
1365 **kwargs: object,
1366 ) -> MolmoImageInputs | None:
1367 images = kwargs.pop("images", None)
1368 image_masks = kwargs.pop("image_masks", None)
1369 image_input_idx = kwargs.pop("image_input_idx", None)
1370 num_crops = kwargs.pop("num_crops", None)
1371
1372 if images is None:
1373 return None
1374
1375 img_patch_id = kwargs.pop("img_patch_id", None)
1376 if isinstance(img_patch_id, torch.Tensor):
1377 img_patch_id = img_patch_id.item()
1378
1379 assert isinstance(img_patch_id, int)
1380 self.img_patch_id = img_patch_id
1381
1382 return MolmoImageInputs(
1383 images=images,
1384 image_masks=image_masks,
1385 image_input_idx=image_input_idx,
1386 num_crops=num_crops,
1387 )
1388
1389 def _process_image_input(
1390 self,
1391 image_input: MolmoImageInputs,
1392 ) -> list[torch.Tensor]:
1393 images = image_input["images"]
1394 image_masks = image_input["image_masks"]
1395 image_input_idx = image_input["image_input_idx"]
1396 num_crops = image_input["num_crops"]
1397
1398 # Call the vision backbone on the whole batch at once
1399 image_features = self.vision_backbone(
1400 images=images.unsqueeze(0),
1401 image_masks=None if image_masks is None else image_masks.unsqueeze(0),
1402 ).squeeze(0)
1403
1404 # Only the features corresponding to patch tokens are relevant
1405 # Re-order the features using the image_input_idx tensor
1406 results = []
1407 num_crops_list = num_crops.tolist()
1408 for feats, img_idx in zip(
1409 image_features.split(num_crops_list),
1410 image_input_idx.split(num_crops_list),
1411 ):
1412 is_valid = img_idx >= 0
1413 valid_img_idx = img_idx[is_valid]
1414 order = torch.argsort(valid_img_idx)
1415 results.append(feats[is_valid][order])
1416 return results
1417
1418 def embed_multimodal(self, **kwargs: object) -> MultiModalEmbeddings:
1419 image_input = self._parse_and_validate_image_input(**kwargs)
1420 if image_input is None:
1421 return []
1422
1423 return self._process_image_input(image_input)
1424
1425 def forward(
1426 self,
1427 input_ids: torch.LongTensor,
1428 positions: torch.LongTensor,
1429 intermediate_tensors: IntermediateTensors | None = None,
1430 inputs_embeds: torch.Tensor | None = None,
1431 **kwargs: object,
1432 ) -> torch.Tensor:
1433 if intermediate_tensors is not None:
1434 inputs_embeds = None
1435
1436 hidden_states = self.model(
1437 input_ids, positions, intermediate_tensors, inputs_embeds=inputs_embeds
1438 )
1439
1440 return hidden_states
1441
1442 def compute_logits(self, hidden_states: torch.Tensor) -> torch.Tensor:
1443 logits = self.logits_processor(self.lm_head, hidden_states)
1444 return logits
1445
1446 def load_weights(self, weights: Iterable[tuple[str, torch.Tensor]]):
1447 loader = AutoWeightsLoader(self)
1448 weights = _get_weights_with_merged_embedding(weights)
1449 return loader.load_weights(weights, mapper=self.hf_to_vllm_mapper)
1450
1451 def get_mm_mapping(self) -> MultiModelKeys:
1452 """Get the module prefix in multimodal models."""
1453 return MultiModelKeys.from_string_field(
1454 language_model="model",
1455 connector="vision_backbone.image_projector",
1456 tower_model="vision_backbone",
1457 )
1458
1459
1460 def _get_weights_with_merged_embedding(
1461 weights: Iterable[tuple[str, torch.Tensor]],
1462 ) -> Iterable[tuple[str, torch.Tensor]]:
1463 embedding_weights = {}
1464 for name, weight in weights:
1465 if "wte.embedding" in name:
1466 embedding_weights["embedding"] = weight
1467 elif "wte.new_embedding" in name:
1468 embedding_weights["new_embedding"] = weight
1469 else:
1470 yield (name, weight)
1471 # this is compatible with most of quantization,
1472 # because they won't quantize embed_tokens
1473 embedding_weights = torch.cat(
1474 [embedding_weights["embedding"], embedding_weights["new_embedding"]],
1475 dim=0,
1476 )
1477 yield ("model.embed_tokens.weight", embedding_weights)