A high-throughput and memory-efficient inference and serving engine for LLMs
Mirrored from https://github.com/tduncan-lum/Nvidia-Optimization-Stack---VLLM.git
Synced Nov 14, 05:30 UTC
[Model] Use `merge_by_field_config` for MM models (H-L) (#26230)
Signed-off-by: DarkLight1337 <tlleungac@connect.ust.hk>
Cyrus Leung committed
Oct 5, 2025 at 11:54 UTC
59a85c366ef3666d22b57f952979a3f74ee50f61
6 files changed
+29
-161
examples/offline_inference/vision_language_multi_image.py
+1
-1
@@ -548,7 +548,7 @@ def load_keye_vl1_5(question: str, image_urls: list[str]) -> ModelRequestData:
548
engine_args = EngineArgs(
549
model=model_name,
550
trust_remote_code=True,
551
- max_model_len=8192,
551
+ max_model_len=32768,
552
max_num_seqs=5,
553
limit_mm_per_prompt={"image": len(image_urls)},
554
)
vllm/model_executor/models/idefics3.py
+9
-23
@@ -53,7 +53,7 @@ from .idefics2_vision_model import (
53
# yapf: enable
54
from .interfaces import MultiModalEmbeddings, SupportsLoRA, SupportsMultiModal
55
from .llama import LlamaModel
56
-from .utils import AutoWeightsLoader, flatten_bn, maybe_prefix
56
+from .utils import AutoWeightsLoader, maybe_prefix
57
58
59
class Idefics3ImagePixelInputs(TensorSchema):
@@ -67,7 +67,7 @@ class Idefics3ImagePixelInputs(TensorSchema):
67
"""
68
type: Literal["pixel_values"]
69
pixel_values: Annotated[torch.Tensor, TensorShape("bnp", 3, "h", "w")]
70
- pixel_attention_mask: torch.Tensor
70
+ pixel_attention_mask: Annotated[torch.Tensor, TensorShape("bnp", "h", "w")]
71
num_patches: Annotated[torch.Tensor, TensorShape("bn")]
72
73
@@ -569,6 +569,8 @@ class Idefics3Model(nn.Module):
569
dummy_inputs=Idefics3DummyInputsBuilder)
570
class Idefics3ForConditionalGeneration(nn.Module, SupportsMultiModal,
571
SupportsLoRA):
572
+ merge_by_field_config = True
573
+
574
packed_modules_mapping = {
575
"qkv_proj": [
576
"q_proj",
@@ -621,37 +623,21 @@ class Idefics3ForConditionalGeneration(nn.Module, SupportsMultiModal,
623
return None
624
625
if image_embeds is not None:
624
- if not isinstance(image_embeds, (torch.Tensor, list)):
625
- raise ValueError("Incorrect type of image embeddings. "
626
- f"Got type: {type(image_embeds)}")
627
-
626
return Idefics3ImageEmbeddingInputs(
627
type="image_embeds",
630
- data=flatten_bn(image_embeds, concat=True),
628
+ data=image_embeds,
629
)
630
631
if pixel_values is not None:
634
- if not isinstance(pixel_values, (torch.Tensor, list)):
635
- raise ValueError("Incorrect type of pixel values. "
636
- f"Got type: {type(pixel_values)}")
637
-
632
pixel_attention_mask = kwargs.pop("pixel_attention_mask")
639
- if not isinstance(pixel_attention_mask, (torch.Tensor, list)):
640
- raise ValueError("Incorrect type of pixel_attention_mask. "
641
- f"Got type: {type(pixel_attention_mask)}")
642
-
633
num_patches = kwargs.pop("num_patches")
644
- if not isinstance(num_patches, (torch.Tensor, list)):
645
- raise ValueError("Incorrect type of num_patches. "
646
- f"Got type: {type(num_patches)}")
647
-
634
expected_h = expected_w = self.config.vision_config.image_size
635
+
636
return Idefics3ImagePixelInputs(
637
type="pixel_values",
651
- pixel_values=flatten_bn(pixel_values, concat=True),
652
- pixel_attention_mask=flatten_bn(pixel_attention_mask,
653
- concat=True),
654
- num_patches=flatten_bn(num_patches, concat=True),
638
+ pixel_values=pixel_values,
639
+ pixel_attention_mask=pixel_attention_mask,
640
+ num_patches=num_patches,
641
resolve_bindings={
642
"h": expected_h,
643
"w": expected_w
vllm/model_executor/models/keye.py
+7
-52
@@ -30,7 +30,7 @@ from vllm.model_executor.layers.quantization import QuantizationConfig
30
from vllm.model_executor.model_loader.weight_utils import (
31
default_weight_loader, maybe_remap_kv_scale_name)
32
from vllm.model_executor.models.module_mapping import MultiModelKeys
33
-from vllm.multimodal import MULTIMODAL_REGISTRY, NestedTensors
33
+from vllm.multimodal import MULTIMODAL_REGISTRY
34
from vllm.multimodal.inputs import (ImageItem, ModalityData,
35
MultiModalDataDict, MultiModalFieldConfig,
36
MultiModalKwargsItems, VideoItem)
@@ -42,7 +42,6 @@ from vllm.multimodal.processing import (BaseMultiModalProcessor,
42
PromptUpdate)
43
from vllm.multimodal.profiling import BaseDummyInputsBuilder
44
from vllm.sequence import IntermediateTensors
45
-from vllm.utils import is_list_of
45
from vllm.utils.tensor_schema import TensorSchema, TensorShape
46
47
from .interfaces import (MultiModalEmbeddings, SupportsLoRA,
@@ -100,8 +99,7 @@ def smart_resize(
99
class KeyeImagePixelInputs(TensorSchema):
100
"""
101
Dimensions:
103
- - b: Batch size
104
- - np: Number of patches
102
+ - bnp: Batch size * Number of patches
103
- c: Number of channels
104
- ps: Patch size
105
- ni: Number of images
@@ -110,7 +108,7 @@ class KeyeImagePixelInputs(TensorSchema):
108
type: Literal["pixel_values"]
109
pixel_values: Annotated[
110
torch.Tensor,
113
- TensorShape("b", "np", 3, "ps", "ps", dynamic_dims={"np"})]
111
+ TensorShape("bnp", 3, "ps", "ps", dynamic_dims={"bnp"})]
112
image_grid_thw: Annotated[torch.Tensor, TensorShape("ni", 3)]
113
114
@@ -134,8 +132,7 @@ KeyeImageInputs = Union[KeyeImagePixelInputs, KeyeImageEmbeddingInputs]
132
class KeyeVideoPixelInputs(TensorSchema):
133
"""
134
Dimensions:
137
- - b: Batch size
138
- - np: Number of patches
135
+ - bnp: Batch size * Number of patches
136
- c: Number of channels
137
- ps: Patch size
138
- ni: Number of images
@@ -144,7 +141,7 @@ class KeyeVideoPixelInputs(TensorSchema):
141
type: Literal["pixel_values_videos"]
142
pixel_values_videos: Annotated[
143
torch.Tensor,
147
- TensorShape("b", "np", 3, "ps", "ps", dynamic_dims={"np"})]
144
+ TensorShape("bnp", 3, "ps", "ps", dynamic_dims={"bnp"})]
145
video_grid_thw: Annotated[torch.Tensor, TensorShape("nv", 3)]
146
147
@@ -1258,6 +1255,8 @@ class KeyeMultiModalProcessor(BaseMultiModalProcessor[KeyeProcessingInfo]):
1255
1256
1257
class BaseKeyeModule(nn.Module):
1258
+ merge_by_field_config = True
1259
+
1260
packed_modules_mapping = {
1261
"qkv_proj": [
1262
"q_proj",
@@ -1524,28 +1523,6 @@ class KeyeForConditionalGeneration(BaseKeyeModule, SupportsMultiModal,
1523
prefix: str = "") -> nn.Module:
1524
return Projector(text_config, vision_config, quant_config, prefix)
1525
1527
- def _validate_and_reshape_mm_tensor(
1528
- self, mm_input: NestedTensors,
1529
- name: str) -> Union[torch.Tensor, list[torch.Tensor]]:
1530
- if not isinstance(mm_input, (torch.Tensor, list)):
1531
- raise ValueError(f"Incorrect type of {name}. "
1532
- f"Got type: {type(mm_input)}")
1533
- if isinstance(mm_input, torch.Tensor):
1534
- if mm_input.ndim == 2:
1535
- return mm_input
1536
- if mm_input.ndim == 5:
1537
- return mm_input
1538
- if mm_input.ndim != 3:
1539
- raise ValueError(f"{name} should be 2D or batched 3D tensor. "
1540
- f"Got ndim: {mm_input.ndim} "
1541
- f"(shape={mm_input.shape})")
1542
- return mm_input.reshape(-1, mm_input.shape[-1])
1543
- elif is_list_of(mm_input, torch.Tensor):
1544
- if all(p.dim() == 4 for p in mm_input) or all(p.dim() == 2
1545
- for p in mm_input):
1546
- return mm_input
1547
- return torch.concat(mm_input)
1548
-
1526
def _parse_and_validate_image_input(
1527
self, **kwargs: object) -> Optional[KeyeImageInputs]:
1528
pixel_values = kwargs.pop("pixel_values", None)
@@ -1556,11 +1533,6 @@ class KeyeForConditionalGeneration(BaseKeyeModule, SupportsMultiModal,
1533
return None
1534
1535
if pixel_values is not None:
1559
- pixel_values = self._validate_and_reshape_mm_tensor(
1560
- pixel_values, "image pixel values")
1561
- image_grid_thw = self._validate_and_reshape_mm_tensor(
1562
- image_grid_thw, "image grid_thw")
1563
-
1536
return KeyeImagePixelInputs(
1537
type="pixel_values",
1538
pixel_values=pixel_values,
@@ -1568,11 +1540,6 @@ class KeyeForConditionalGeneration(BaseKeyeModule, SupportsMultiModal,
1540
)
1541
1542
if image_embeds is not None:
1571
- image_embeds = self._validate_and_reshape_mm_tensor(
1572
- image_embeds, "image embeds")
1573
- image_grid_thw = self._validate_and_reshape_mm_tensor(
1574
- image_grid_thw, "image grid_thw")
1575
-
1543
return KeyeImageEmbeddingInputs(
1544
type="image_embeds",
1545
image_embeds=image_embeds,
@@ -1589,13 +1556,6 @@ class KeyeForConditionalGeneration(BaseKeyeModule, SupportsMultiModal,
1556
return None
1557
1558
if pixel_values_videos is not None:
1592
- pixel_values_videos = self._validate_and_reshape_mm_tensor(
1593
- pixel_values_videos,
1594
- "video pixel values",
1595
- )
1596
- video_grid_thw = self._validate_and_reshape_mm_tensor(
1597
- video_grid_thw, "video grid_thw")
1598
-
1559
return KeyeVideoPixelInputs(
1560
type="pixel_values_videos",
1561
pixel_values_videos=pixel_values_videos,
@@ -1603,11 +1563,6 @@ class KeyeForConditionalGeneration(BaseKeyeModule, SupportsMultiModal,
1563
)
1564
1565
if video_embeds is not None:
1606
- video_embeds = self._validate_and_reshape_mm_tensor(
1607
- video_embeds, "video embeds")
1608
- video_grid_thw = self._validate_and_reshape_mm_tensor(
1609
- video_grid_thw, "video grid_thw")
1610
-
1566
return KeyeVideoEmbeddingInputs(
1567
type="video_embeds",
1568
video_embeds=video_embeds,
vllm/model_executor/models/keye_vl1_5.py
+5
-51
@@ -18,7 +18,7 @@ from vllm.logger import init_logger
18
from vllm.model_executor.layers.linear import (ColumnParallelLinear,
19
RowParallelLinear)
20
from vllm.model_executor.layers.quantization import QuantizationConfig
21
-from vllm.multimodal import MULTIMODAL_REGISTRY, NestedTensors
21
+from vllm.multimodal import MULTIMODAL_REGISTRY
22
from vllm.multimodal.inputs import (ImageItem, ModalityData,
23
MultiModalFieldConfig,
24
MultiModalKwargsItems, VideoItem)
@@ -100,8 +100,7 @@ def get_num_patches(grid_thw: torch.Tensor,
100
class KeyeVL1_5ImagePixelInputs(TensorSchema):
101
"""
102
Dimensions:
103
- - b: Batch size
104
- - np: Number of patches
103
+ - bnp: Batch size * Number of patches
104
- c: Number of channels
105
- ps: Patch size
106
- ni: Number of images
@@ -111,7 +110,7 @@ class KeyeVL1_5ImagePixelInputs(TensorSchema):
110
111
pixel_values: Annotated[
112
torch.Tensor,
114
- TensorShape("np", 3, "ps", "ps", dynamic_dims={"np"})]
113
+ TensorShape("bnp", 3, "ps", "ps", dynamic_dims={"bnp"})]
114
115
image_grid_thw: Annotated[torch.Tensor, TensorShape("ni", 3)]
116
@@ -137,8 +136,7 @@ KeyeVL1_5ImageInputs = Union[KeyeVL1_5ImagePixelInputs,
136
class KeyeVL1_5VideoPixelInputs(TensorSchema):
137
"""
138
Dimensions:
140
- - b: Batch size
141
- - np: Number of patches
139
+ - bnp: Batch size * Number of patches
140
- c: Number of channels
141
- ps: Patch size
142
- ni: Number of images
@@ -147,7 +145,7 @@ class KeyeVL1_5VideoPixelInputs(TensorSchema):
145
type: Literal["pixel_values_videos"]
146
pixel_values_videos: Annotated[
147
torch.Tensor,
150
- TensorShape("np", 3, "ps", "ps", dynamic_dims={"np"})]
148
+ TensorShape("bnp", 3, "ps", "ps", dynamic_dims={"bnp"})]
149
video_grid_thw: Annotated[torch.Tensor, TensorShape("nv", 3)]
150
151
num_frames: torch.Tensor
@@ -483,24 +481,6 @@ class KeyeVL1_5ForConditionalGeneration(BaseKeyeModule, SupportsMultiModal,
481
self.merge_size = config.vision_config.spatial_merge_size
482
super().__init__(vllm_config=vllm_config, prefix=prefix)
483
486
- def _validate_and_reshape_mm_tensor(self, mm_input: NestedTensors,
487
- expected_dim: int, name: str):
488
- if not isinstance(mm_input, (torch.Tensor, list)):
489
- raise ValueError(f"Incorrect type of {name}. "
490
- f"Got type: {type(mm_input)}")
491
- if isinstance(mm_input, torch.Tensor):
492
- if mm_input.ndim == expected_dim:
493
- return mm_input
494
- elif mm_input.ndim == expected_dim + 1:
495
- return mm_input.reshape(-1, *mm_input.shape[2:])
496
- else:
497
- raise ValueError(
498
- f"{name} should be {expected_dim}D or "
499
- f"batched {expected_dim}D tensor."
500
- f"Got ndim: {mm_input.ndim} (shape={mm_input.shape})")
501
- else:
502
- return torch.concat(mm_input)
503
-
484
def _parse_and_validate_image_input(
485
self, **kwargs: object) -> Optional[KeyeVL1_5ImageInputs]:
486
pixel_values = kwargs.pop("pixel_values", None)
@@ -511,11 +491,6 @@ class KeyeVL1_5ForConditionalGeneration(BaseKeyeModule, SupportsMultiModal,
491
return None
492
493
if pixel_values is not None:
514
- pixel_values = self._validate_and_reshape_mm_tensor(
515
- pixel_values, expected_dim=4, name="image pixel values")
516
- image_grid_thw = self._validate_and_reshape_mm_tensor(
517
- image_grid_thw, expected_dim=2, name="image grid_thw")
518
-
494
return KeyeVL1_5ImagePixelInputs(
495
type="pixel_values",
496
pixel_values=pixel_values,
@@ -523,11 +498,6 @@ class KeyeVL1_5ForConditionalGeneration(BaseKeyeModule, SupportsMultiModal,
498
)
499
500
if image_embeds is not None:
526
- image_embeds = self._validate_and_reshape_mm_tensor(
527
- image_embeds, expected_dim=2, name="image embeds")
528
- image_grid_thw = self._validate_and_reshape_mm_tensor(
529
- image_grid_thw, expected_dim=2, name="image grid_thw")
530
-
501
return KeyeVL1_5ImageEmbeddingInputs(
502
type="image_embeds",
503
image_embeds=image_embeds,
@@ -545,17 +515,6 @@ class KeyeVL1_5ForConditionalGeneration(BaseKeyeModule, SupportsMultiModal,
515
return None
516
517
if pixel_values_videos is not None:
548
- pixel_values_videos = self._validate_and_reshape_mm_tensor(
549
- pixel_values_videos,
550
- expected_dim=4,
551
- name="video pixel values",
552
- )
553
- video_grid_thw = self._validate_and_reshape_mm_tensor(
554
- video_grid_thw, expected_dim=2, name="video grid_thw")
555
-
556
- num_frames = self._validate_and_reshape_mm_tensor(
557
- num_frames, expected_dim=1, name="video num frames")
558
-
518
return KeyeVL1_5VideoPixelInputs(
519
type="pixel_values_videos",
520
pixel_values_videos=pixel_values_videos,
@@ -563,11 +522,6 @@ class KeyeVL1_5ForConditionalGeneration(BaseKeyeModule, SupportsMultiModal,
522
num_frames=num_frames)
523
524
if video_embeds is not None:
566
- video_embeds = self._validate_and_reshape_mm_tensor(
567
- video_embeds, expected_dim=2, name="video embeds")
568
- video_grid_thw = self._validate_and_reshape_mm_tensor(
569
- video_grid_thw, expected_dim=2, name="video grid_thw")
570
-
525
return KeyeVL1_5VideoEmbeddingInputs(type="video_embeds",
526
video_embeds=video_embeds,
527
video_grid_thw=video_grid_thw,
vllm/model_executor/models/kimi_vl.py
+1
-32
@@ -283,6 +283,7 @@ class KimiVLMultiModalProcessor(BaseMultiModalProcessor[KimiVLProcessingInfo]):
283
dummy_inputs=KimiVLDummyInputsBuilder)
284
class KimiVLForConditionalGeneration(nn.Module, SupportsMultiModal,
285
SupportsPP):
286
+ merge_by_field_config = True
287
288
supports_encoder_tp_data = True
289
@@ -342,23 +343,6 @@ class KimiVLForConditionalGeneration(nn.Module, SupportsMultiModal,
343
config.vocab_size, logit_scale)
344
self.media_placeholder: int = self.config.media_placeholder_token_id
345
345
- # ref: qwen2_vl.py
346
- def _validate_and_reshape_mm_tensor(self, mm_input: object,
347
- name: str) -> torch.Tensor:
348
- if not isinstance(mm_input, (torch.Tensor, list)):
349
- raise ValueError(f"Incorrect type of {name}. "
350
- f"Got type: {type(mm_input)}")
351
- if isinstance(mm_input, torch.Tensor):
352
- if mm_input.ndim == 2:
353
- return mm_input
354
- if mm_input.ndim != 3:
355
- raise ValueError(f"{name} should be 2D or batched 3D tensor. "
356
- f"Got ndim: {mm_input.ndim} "
357
- f"(shape={mm_input.shape})")
358
- return mm_input.reshape(-1, mm_input.shape[-1])
359
- else:
360
- return torch.concat(mm_input)
361
-
346
def _parse_and_validate_image_input(
347
self, **kwargs: object) -> Optional[KimiVLImageInputs]:
348
# image input type must be pixel values now
@@ -368,21 +352,6 @@ class KimiVLForConditionalGeneration(nn.Module, SupportsMultiModal,
352
if pixel_values is None:
353
return None
354
371
- image_grid_hws = self._validate_and_reshape_mm_tensor(
372
- image_grid_hws, "image grid hws")
373
- # pixel_values may have complex shapes
374
- num_channels = 3
375
- patch_size = self.config.vision_config.patch_size
376
- if isinstance(pixel_values, list):
377
- pixel_values = torch.cat([
378
- x.reshape(-1, num_channels, patch_size, patch_size)
379
- for x in pixel_values
380
- ])
381
- else:
382
- pixel_values = pixel_values.reshape(-1, num_channels, patch_size,
383
- patch_size)
384
- pixel_values = pixel_values.to(self.vision_tower.dtype)
385
-
355
return KimiVLImagePixelInputs(
356
type="pixel_values",
357
pixel_values=pixel_values,
vllm/utils/tensor_schema.py
+6
-2
@@ -164,7 +164,9 @@ class TensorSchema:
164
165
if len(actual_shape) != len(expected_shape):
166
raise ValueError(f"{field_name} has rank {len(actual_shape)} "
167
- f"but expected {len(expected_shape)}")
167
+ f"but expected {len(expected_shape)}. "
168
+ f"Expected shape: {expected_shape}, "
169
+ f"but got {actual_shape}")
170
171
for i, dim in enumerate(expected_shape):
172
if dim in dynamic_dims:
@@ -172,7 +174,9 @@ class TensorSchema:
174
elif isinstance(dim, int):
175
if actual_shape[i] != dim:
176
raise ValueError(f"{field_name} dim[{i}] expected "
175
- f"{dim}, got {actual_shape[i]}")
177
+ f"{dim}, got {actual_shape[i]}. "
178
+ f"Expected shape: {expected_shape}, "
179
+ f"but got {actual_shape}")
180
elif isinstance(dim, str):
181
if dim in shape_env:
182
if actual_shape[i] != shape_env[dim]: