main
py 707 lines 23.3 KB
Raw
1 # SPDX-License-Identifier: Apache-2.0
2 # SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3
4 from collections.abc import Iterable, Mapping, Sequence
5 from typing import Annotated, Literal, TypeAlias
6
7 import torch
8 import torch.nn as nn
9 import torch.nn.functional as F
10 from transformers import (
11 BatchFeature,
12 Blip2Config,
13 Blip2QFormerConfig,
14 apply_chunking_to_forward,
15 )
16
17 from vllm.config import CacheConfig, VllmConfig
18 from vllm.config.multimodal import MultiModalDummyOptions
19 from vllm.inputs import MultiModalDataDict
20 from vllm.model_executor.layers.activation import get_act_fn
21 from vllm.model_executor.layers.quantization import QuantizationConfig
22 from vllm.multimodal import MULTIMODAL_REGISTRY
23 from vllm.multimodal.inputs import (
24 MultiModalFieldConfig,
25 MultiModalKwargsItem,
26 MultiModalKwargsItems,
27 )
28 from vllm.multimodal.parse import MultiModalDataItems
29 from vllm.multimodal.processing import (
30 BaseDummyInputsBuilder,
31 BaseMultiModalProcessor,
32 BaseProcessingInfo,
33 PromptIndexTargets,
34 PromptInsertion,
35 PromptUpdate,
36 )
37 from vllm.sequence import IntermediateTensors
38 from vllm.utils.tensor_schema import TensorSchema, TensorShape
39
40 from .blip import BlipVisionModel, get_blip_num_patches
41 from .interfaces import (
42 MultiModalEmbeddings,
43 SupportsLoRA,
44 SupportsMultiModal,
45 SupportsPP,
46 SupportsQuant,
47 )
48 from .module_mapping import MultiModelKeys
49 from .utils import AutoWeightsLoader, init_vllm_registered_model, maybe_prefix
50
51
52 class Blip2ImagePixelInputs(TensorSchema):
53 """Dimensions:
54 - bn: Batch size * number of images
55 - c: Number of channels (3)
56 - h: Height of each image
57 - w: Width of each image
58 """
59
60 type: Literal["pixel_values"]
61 data: Annotated[torch.Tensor, TensorShape("bn", 3, "h", "w")]
62
63
64 class Blip2ImageEmbeddingInputs(TensorSchema):
65 """Dimensions:
66 - bn: Batch size * number of images
67 - f: Image feature size
68 - h: Hidden size (must match the hidden size of language model backbone)
69 """
70
71 type: Literal["image_embeds"]
72 data: Annotated[torch.Tensor, TensorShape("bn", "f", "h")]
73
74
75 Blip2ImageInputs: TypeAlias = Blip2ImagePixelInputs | Blip2ImageEmbeddingInputs
76 """Alias for supported BLIP-2 image input types."""
77
78
79 class Blip2QFormerMultiHeadAttention(nn.Module):
80 def __init__(
81 self,
82 config: Blip2QFormerConfig,
83 *,
84 quant_config: QuantizationConfig | None,
85 cache_config: CacheConfig | None,
86 is_cross_attention: bool = False,
87 prefix: str = "",
88 ) -> None:
89 super().__init__()
90
91 self.config = config
92
93 if config.hidden_size % config.num_attention_heads != 0:
94 raise ValueError(
95 f"The hidden size ({config.hidden_size}) is not a multiple of "
96 f"the number of attention heads ({config.num_attention_heads})"
97 )
98
99 self.num_attention_heads = config.num_attention_heads
100 self.attention_head_size = config.hidden_size // config.num_attention_heads
101 self.all_head_size = self.num_attention_heads * self.attention_head_size
102 self.scaling = self.attention_head_size**-0.5
103
104 self.query = nn.Linear(config.hidden_size, self.all_head_size)
105 if is_cross_attention:
106 kv_hidden_size = config.encoder_hidden_size
107 else:
108 kv_hidden_size = config.hidden_size
109 self.key = nn.Linear(kv_hidden_size, self.all_head_size)
110 self.value = nn.Linear(kv_hidden_size, self.all_head_size)
111
112 self.position_embedding_type = getattr(
113 config, "position_embedding_type", "absolute"
114 )
115 if self.position_embedding_type != "absolute":
116 raise NotImplementedError(
117 f"Unsupported position_embedding_type: {self.position_embedding_type}"
118 )
119
120 self.dropout = nn.Dropout(config.attention_probs_dropout_prob)
121
122 def transpose_for_scores(self, x):
123 x = x.view(*x.size()[:-1], self.num_attention_heads, self.attention_head_size)
124 return x.permute(0, 2, 1, 3)
125
126 def forward(
127 self,
128 hidden_states: torch.Tensor,
129 encoder_hidden_states: torch.FloatTensor | None = None,
130 ):
131 is_cross_attention = encoder_hidden_states is not None
132
133 if is_cross_attention:
134 key_layer = self.transpose_for_scores(self.key(encoder_hidden_states))
135 value_layer = self.transpose_for_scores(self.value(encoder_hidden_states))
136 else:
137 key_layer = self.transpose_for_scores(self.key(hidden_states))
138 value_layer = self.transpose_for_scores(self.value(hidden_states))
139
140 mixed_query_layer = self.query(hidden_states)
141
142 query_layer = self.transpose_for_scores(mixed_query_layer)
143
144 context_layer = F.scaled_dot_product_attention(
145 query_layer,
146 key_layer,
147 value_layer,
148 dropout_p=self.dropout.p if self.training else 0.0,
149 scale=self.scaling,
150 )
151
152 context_layer = context_layer.permute(0, 2, 1, 3).contiguous()
153 context_layer = context_layer.view(
154 *context_layer.size()[:-2], self.all_head_size
155 )
156
157 return context_layer
158
159
160 class Blip2QFormerSelfOutput(nn.Module):
161 def __init__(self, config: Blip2QFormerConfig, prefix: str = "") -> None:
162 super().__init__()
163
164 self.dense = nn.Linear(config.hidden_size, config.hidden_size)
165 self.LayerNorm = nn.LayerNorm(config.hidden_size, eps=config.layer_norm_eps)
166 self.dropout = nn.Dropout(config.hidden_dropout_prob)
167
168 def forward(
169 self,
170 hidden_states: torch.Tensor,
171 input_tensor: torch.Tensor,
172 ) -> torch.Tensor:
173 hidden_states = self.dense(hidden_states)
174 hidden_states = self.dropout(hidden_states)
175 hidden_states = self.LayerNorm(hidden_states + input_tensor)
176 return hidden_states
177
178
179 class Blip2QFormerAttention(nn.Module):
180 def __init__(
181 self,
182 config: Blip2QFormerConfig,
183 *,
184 quant_config: QuantizationConfig | None,
185 cache_config: CacheConfig | None,
186 is_cross_attention: bool = False,
187 prefix: str = "",
188 ) -> None:
189 super().__init__()
190
191 self.attention = Blip2QFormerMultiHeadAttention(
192 config,
193 quant_config=quant_config,
194 cache_config=cache_config,
195 is_cross_attention=is_cross_attention,
196 prefix=f"{prefix}.attention",
197 )
198
199 self.output = Blip2QFormerSelfOutput(config, prefix=f"{prefix}.output")
200
201 def forward(
202 self,
203 hidden_states: torch.Tensor,
204 encoder_hidden_states: torch.FloatTensor | None = None,
205 ) -> tuple[torch.Tensor]:
206 self_output = self.attention(
207 hidden_states,
208 encoder_hidden_states=encoder_hidden_states,
209 )
210 attention_output = self.output(self_output, hidden_states)
211
212 return attention_output
213
214
215 class Blip2QFormerIntermediate(nn.Module):
216 def __init__(self, config: Blip2QFormerConfig, prefix: str = "") -> None:
217 super().__init__()
218
219 self.dense = nn.Linear(config.hidden_size, config.intermediate_size)
220 self.intermediate_act_fn = get_act_fn(config.hidden_act)
221
222 def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
223 hidden_states = self.dense(hidden_states)
224 hidden_states = self.intermediate_act_fn(hidden_states)
225 return hidden_states
226
227
228 class Blip2QFormerOutput(nn.Module):
229 def __init__(self, config: Blip2QFormerConfig, prefix: str = "") -> None:
230 super().__init__()
231
232 self.dense = nn.Linear(config.intermediate_size, config.hidden_size)
233 self.LayerNorm = nn.LayerNorm(config.hidden_size, eps=config.layer_norm_eps)
234 self.dropout = nn.Dropout(config.hidden_dropout_prob)
235
236 def forward(
237 self,
238 hidden_states: torch.Tensor,
239 input_tensor: torch.Tensor,
240 ) -> torch.Tensor:
241 hidden_states = self.dense(hidden_states)
242 hidden_states = self.dropout(hidden_states)
243 hidden_states = self.LayerNorm(hidden_states + input_tensor)
244 return hidden_states
245
246
247 class Blip2QFormerLayer(nn.Module):
248 def __init__(
249 self,
250 config: Blip2QFormerConfig,
251 *,
252 quant_config: QuantizationConfig | None,
253 cache_config: CacheConfig | None,
254 layer_idx: int,
255 prefix: str = "",
256 ) -> None:
257 super().__init__()
258
259 self.chunk_size_feed_forward = config.chunk_size_feed_forward
260 self.seq_len_dim = 1
261 self.attention = Blip2QFormerAttention(
262 config,
263 quant_config=quant_config,
264 cache_config=cache_config,
265 prefix=f"{prefix}.attention",
266 )
267
268 self.layer_idx = layer_idx
269
270 if layer_idx % config.cross_attention_frequency == 0:
271 self.crossattention = Blip2QFormerAttention(
272 config,
273 quant_config=quant_config,
274 cache_config=cache_config,
275 is_cross_attention=True,
276 prefix=f"{prefix}.crossattention",
277 )
278 self.has_cross_attention = True
279 else:
280 self.has_cross_attention = False
281
282 self.intermediate_query = Blip2QFormerIntermediate(
283 config, prefix=f"{prefix}.intermediate_query"
284 )
285 self.output_query = Blip2QFormerOutput(config, prefix=f"{prefix}.output_query")
286
287 def forward(
288 self,
289 hidden_states: torch.FloatTensor,
290 encoder_hidden_states: torch.FloatTensor,
291 query_length: int,
292 ):
293 attention_output = self.attention(hidden_states)
294
295 if query_length > 0:
296 query_attention_output = attention_output[:, :query_length, :]
297
298 if self.has_cross_attention:
299 query_attention_output = self.crossattention(
300 query_attention_output,
301 encoder_hidden_states=encoder_hidden_states,
302 )
303
304 layer_output = apply_chunking_to_forward(
305 self.feed_forward_chunk_query,
306 self.chunk_size_feed_forward,
307 self.seq_len_dim,
308 query_attention_output,
309 )
310
311 if attention_output.shape[1] > query_length:
312 layer_output_text = apply_chunking_to_forward(
313 self.feed_forward_chunk,
314 self.chunk_size_feed_forward,
315 self.seq_len_dim,
316 attention_output[:, query_length:, :],
317 )
318 layer_output = torch.cat([layer_output, layer_output_text], dim=1)
319 else:
320 layer_output = apply_chunking_to_forward(
321 self.feed_forward_chunk,
322 self.chunk_size_feed_forward,
323 self.seq_len_dim,
324 attention_output,
325 )
326
327 return layer_output
328
329 def feed_forward_chunk(self, attention_output: torch.Tensor) -> torch.Tensor:
330 intermediate_output = self.intermediate(attention_output)
331 layer_output = self.output(intermediate_output, attention_output)
332 return layer_output
333
334 def feed_forward_chunk_query(self, attention_output: torch.Tensor) -> torch.Tensor:
335 intermediate_output = self.intermediate_query(attention_output)
336 layer_output = self.output_query(intermediate_output, attention_output)
337 return layer_output
338
339
340 class Blip2QFormerEncoder(nn.Module):
341 def __init__(
342 self,
343 config: Blip2QFormerConfig,
344 *,
345 quant_config: QuantizationConfig | None,
346 cache_config: CacheConfig | None,
347 prefix: str = "",
348 ) -> None:
349 super().__init__()
350
351 self.config = config
352
353 self.layer = nn.ModuleList(
354 [
355 Blip2QFormerLayer(
356 config,
357 quant_config=quant_config,
358 cache_config=cache_config,
359 layer_idx=layer_idx,
360 prefix=f"{prefix}.layer.{layer_idx}",
361 )
362 for layer_idx in range(config.num_hidden_layers)
363 ]
364 )
365
366 def forward(
367 self,
368 hidden_states: torch.FloatTensor,
369 encoder_hidden_states: torch.FloatTensor,
370 query_length: int,
371 ) -> torch.Tensor:
372 for i in range(self.config.num_hidden_layers):
373 layer_module = self.layer[i]
374
375 hidden_states = layer_module(
376 hidden_states,
377 encoder_hidden_states=encoder_hidden_states,
378 query_length=query_length,
379 )
380
381 return hidden_states
382
383
384 # Adapted from https://github.com/huggingface/transformers/blob/v4.41.2/src/transformers/models/blip_2/modeling_blip_2.py#L1025
385 class Blip2QFormerModel(nn.Module):
386 def __init__(
387 self,
388 config: Blip2QFormerConfig,
389 *,
390 quant_config: QuantizationConfig | None,
391 cache_config: CacheConfig | None,
392 prefix: str = "",
393 ) -> None:
394 super().__init__()
395
396 self.config = config
397
398 self.layernorm = nn.LayerNorm(config.hidden_size, eps=config.layer_norm_eps)
399 self.dropout = nn.Dropout(config.hidden_dropout_prob)
400
401 self.encoder = Blip2QFormerEncoder(
402 config,
403 quant_config=quant_config,
404 cache_config=cache_config,
405 prefix=f"{prefix}.encoder",
406 )
407
408 def forward(
409 self,
410 query_embeds: torch.FloatTensor,
411 encoder_hidden_states: torch.FloatTensor,
412 ) -> torch.Tensor:
413 query_length = query_embeds.shape[1]
414
415 embedding_output = self.layernorm(query_embeds)
416 embedding_output = self.dropout(embedding_output)
417
418 sequence_output = self.encoder(
419 embedding_output,
420 encoder_hidden_states=encoder_hidden_states,
421 query_length=query_length,
422 )
423
424 return sequence_output
425
426
427 class Blip2ProcessingInfo(BaseProcessingInfo):
428 def get_hf_config(self):
429 return self.ctx.get_hf_config(Blip2Config)
430
431 def get_supported_mm_limits(self) -> Mapping[str, int | None]:
432 return {"image": 1}
433
434 def get_num_image_tokens(self) -> int:
435 hf_config = self.get_hf_config()
436 return hf_config.num_query_tokens
437
438
439 class Blip2DummyInputsBuilder(BaseDummyInputsBuilder[Blip2ProcessingInfo]):
440 def get_dummy_text(self, mm_counts: Mapping[str, int]) -> str:
441 return ""
442
443 def get_dummy_mm_data(
444 self,
445 seq_len: int,
446 mm_counts: Mapping[str, int],
447 mm_options: MultiModalDummyOptions,
448 ) -> MultiModalDataDict:
449 hf_config = self.info.get_hf_config()
450 vision_config = hf_config.vision_config
451
452 max_image_size = vision_config.image_size
453
454 return {
455 "image": self._get_dummy_images(
456 width=max_image_size,
457 height=max_image_size,
458 num_images=mm_counts.get("image", 0),
459 overrides=mm_options.get("image"),
460 )
461 }
462
463
464 class Blip2MultiModalProcessor(BaseMultiModalProcessor[Blip2ProcessingInfo]):
465 def _get_mm_fields_config(
466 self,
467 hf_inputs: BatchFeature,
468 hf_processor_mm_kwargs: Mapping[str, object],
469 ) -> Mapping[str, MultiModalFieldConfig]:
470 return dict(
471 pixel_values=MultiModalFieldConfig.batched("image"),
472 image_embeds=MultiModalFieldConfig.batched("image"),
473 )
474
475 def _get_prompt_updates(
476 self,
477 mm_items: MultiModalDataItems,
478 hf_processor_mm_kwargs: Mapping[str, object],
479 out_mm_kwargs: MultiModalKwargsItems,
480 ) -> Sequence[PromptUpdate]:
481 tokenizer = self.info.get_tokenizer()
482 vocab = tokenizer.get_vocab()
483
484 image_token_id = vocab["<image>"]
485 num_image_tokens = self.info.get_num_image_tokens()
486 image_tokens = [image_token_id] * num_image_tokens
487
488 return [
489 PromptInsertion(
490 modality="image",
491 target=PromptIndexTargets.start(),
492 insertion=image_tokens,
493 )
494 ]
495
496
497 @MULTIMODAL_REGISTRY.register_processor(
498 Blip2MultiModalProcessor,
499 info=Blip2ProcessingInfo,
500 dummy_inputs=Blip2DummyInputsBuilder,
501 )
502 class Blip2ForConditionalGeneration(
503 nn.Module, SupportsLoRA, SupportsMultiModal, SupportsPP, SupportsQuant
504 ):
505 supports_tower_connector_lora = True
506
507 @classmethod
508 def get_placeholder_str(cls, modality: str, i: int) -> str | None:
509 if modality.startswith("image"):
510 return None
511
512 raise ValueError("Only image modality is supported")
513
514 def __init__(self, *, vllm_config: VllmConfig, prefix: str = ""):
515 super().__init__()
516 config = vllm_config.model_config.hf_config
517 cache_config = vllm_config.cache_config
518 quant_config = vllm_config.quant_config
519 multimodal_config = vllm_config.model_config.multimodal_config
520 self.config = config
521 self.multimodal_config = multimodal_config
522 vision_config = config.vision_config
523 self._vision_tokens_per_image = (
524 get_blip_num_patches(
525 image_size=vision_config.image_size,
526 patch_size=vision_config.patch_size,
527 )
528 + 1 # include class token
529 )
530
531 with self._mark_tower_model(vllm_config, "image"):
532 self.vision_model = BlipVisionModel(vision_config, quant_config)
533 self.query_tokens = nn.Parameter(
534 torch.zeros(
535 1, config.num_query_tokens, config.qformer_config.hidden_size
536 )
537 )
538 self.qformer = Blip2QFormerModel(
539 config.qformer_config,
540 cache_config=cache_config,
541 quant_config=quant_config,
542 prefix=maybe_prefix(prefix, "qformer"),
543 )
544 self.language_projection = nn.Linear(
545 config.qformer_config.hidden_size,
546 config.text_config.hidden_size,
547 bias=True,
548 )
549
550 with self._mark_language_model(vllm_config):
551 self.language_model = init_vllm_registered_model(
552 vllm_config=vllm_config,
553 hf_config=config.text_config,
554 prefix=maybe_prefix(prefix, "language_model"),
555 )
556
557 self.make_empty_intermediate_tensors = (
558 self.language_model.make_empty_intermediate_tensors
559 )
560
561 def _parse_and_validate_image_input(
562 self, **kwargs: object
563 ) -> Blip2ImageInputs | None:
564 pixel_values = kwargs.pop("pixel_values", None)
565 image_embeds = kwargs.pop("image_embeds", None)
566
567 if pixel_values is None and image_embeds is None:
568 return None
569
570 if pixel_values is not None:
571 expected_h = expected_w = self.config.vision_config.image_size
572 return Blip2ImagePixelInputs(
573 type="pixel_values",
574 data=pixel_values,
575 resolve_bindings={"h": expected_h, "w": expected_w},
576 )
577
578 if image_embeds is not None:
579 return Blip2ImageEmbeddingInputs(
580 type="image_embeds",
581 data=image_embeds,
582 )
583
584 raise AssertionError("This line should be unreachable.")
585
586 def _image_pixels_to_features(
587 self, vision_model: BlipVisionModel, pixel_values: torch.Tensor
588 ) -> torch.Tensor:
589 # NOTE: we skip the step to select the vision feature layer since
590 # this is already done inside the vision tower
591 image_features = vision_model(pixel_values)
592
593 return image_features
594
595 def _process_image_pixels(self, inputs: Blip2ImagePixelInputs) -> torch.Tensor:
596 pixel_values = inputs["data"]
597
598 return self._image_pixels_to_features(self.vision_model, pixel_values)
599
600 def _process_image_input(self, image_input: Blip2ImageInputs) -> torch.Tensor:
601 if image_input.type == "image_embeds":
602 return image_input.data
603
604 image_features = self._process_image_pixels(image_input)
605
606 query_tokens = self.query_tokens.expand(image_features.shape[0], -1, -1)
607 query_output = self.qformer(
608 query_embeds=query_tokens,
609 encoder_hidden_states=image_features,
610 )
611
612 return self.language_projection(query_output)
613
614 def embed_multimodal(self, **kwargs: object) -> MultiModalEmbeddings:
615 image_input = self._parse_and_validate_image_input(**kwargs)
616 if image_input is None:
617 return []
618 vision_embeddings = self._process_image_input(image_input)
619 return vision_embeddings
620
621 def forward(
622 self,
623 input_ids: torch.Tensor | None,
624 positions: torch.Tensor,
625 intermediate_tensors: IntermediateTensors | None = None,
626 inputs_embeds: torch.Tensor | None = None,
627 **kwargs: object,
628 ) -> IntermediateTensors:
629 """Run forward pass for BLIP-2.
630
631 One key thing to understand is the `input_ids` already accounts for the
632 positions of the to-be-inserted image embeddings.
633
634 Concretely, consider a text prompt:
635 `"Question: What's the content of the image? Answer:"`.
636
637 Tokenizer outputs:
638 `[2, 45641, 35, 653, 18, 5, 1383, 9, 5, 2274, 116, 31652, 35]`.
639
640 To reserve space in KV cache, we have to insert placeholder tokens
641 before they are inputted to the model, so the input processor prepends
642 dummy tokens (denoted as `50265`), resulting in:
643 `[50265, ..., 50265, 2, 45641, 35, ..., 31652, 35]`.
644
645 We insert 32 tokens since it corresponds to the number of query
646 embeddings outputted by the Q-Former and inputted to the language model.
647
648 This way, the `positions` and `attn_metadata` are consistent
649 with the `input_ids`.
650
651 Args:
652 input_ids: Flattened (concatenated) input_ids corresponding to a
653 batch.
654 positions: Flattened (concatenated) position ids corresponding to a
655 batch.
656 intermediate_tensors: Intermediate tensors from prior forward pass.
657 inputs_embeds: Optional tensor of input embeddings.
658
659 Info:
660 [`Blip2ImageInputs`][vllm.model_executor.models.blip2.Blip2ImageInputs]
661
662 """
663 if intermediate_tensors is not None:
664 inputs_embeds = None
665
666 hidden_states = self.language_model.model(
667 input_ids, positions, intermediate_tensors, inputs_embeds=inputs_embeds
668 )
669
670 return hidden_states
671
672 def compute_logits(
673 self,
674 hidden_states: torch.Tensor,
675 ) -> torch.Tensor | None:
676 return self.language_model.compute_logits(hidden_states)
677
678 def load_weights(self, weights: Iterable[tuple[str, torch.Tensor]]) -> set[str]:
679 loader = AutoWeightsLoader(self)
680 return loader.load_weights(weights)
681
682 def get_mm_mapping(self) -> MultiModelKeys:
683 return MultiModelKeys.from_string_field(
684 language_model="language_model",
685 connector=["qformer", "language_projection"],
686 tower_model="vision_model",
687 )
688
689 def get_mm_lora_token_counts(
690 self,
691 *,
692 modality: str,
693 mm_kwargs: MultiModalKwargsItem | None,
694 num_mm_embeds: int,
695 ) -> tuple[int, int | None]:
696 del modality, mm_kwargs
697 if num_mm_embeds <= 0:
698 return 0, 0
699 assert num_mm_embeds % self.config.num_query_tokens == 0, (
700 "The number of image tokens must be a multiple of "
701 "the number of query tokens."
702 )
703 num_images = num_mm_embeds // self.config.num_query_tokens
704 return (
705 num_images * self._vision_tokens_per_image,
706 num_images * self.config.num_query_tokens,
707 )