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 15, 09:45 UTC
[Frontend] decrease import time of vllm.multimodal (#18031)
Co-authored-by: Aaron Pham <Aaronpham0103@gmail.com>
David Xia committed
May 14, 2025 at 18:43 UTC
749f792553d48dad68855f32910fafa61a28297e
3 files changed
+45
-34
vllm/multimodal/inputs.py
+24
-21
@@ -10,40 +10,43 @@ from typing import (TYPE_CHECKING, Any, Literal, Optional, TypedDict, TypeVar,
10
Union, cast, final)
11
12
import numpy as np
13
-import torch
14
-import torch.types
15
-from PIL.Image import Image
16
-from transformers import BatchFeature
13
from typing_extensions import NotRequired, TypeAlias
14
15
from vllm.jsontree import JSONTree, json_map_leaves
20
-from vllm.utils import full_groupby, is_list_of
16
+from vllm.utils import LazyLoader, full_groupby, is_list_of
17
18
if TYPE_CHECKING:
19
+ import torch
20
+ import torch.types
21
+ from PIL.Image import Image
22
+ from transformers.feature_extraction_utils import BatchFeature
23
+
24
from .hasher import MultiModalHashDict
25
+else:
26
+ torch = LazyLoader("torch", globals(), "torch")
27
28
_T = TypeVar("_T")
29
27
-HfImageItem: TypeAlias = Union[Image, np.ndarray, torch.Tensor]
30
+HfImageItem: TypeAlias = Union["Image", np.ndarray, "torch.Tensor"]
31
"""
32
A {class}`transformers.image_utils.ImageInput` representing a single image
33
item, which can be passed to a HuggingFace `ImageProcessor`.
34
"""
35
33
-HfVideoItem: TypeAlias = Union[list[Image], np.ndarray, torch.Tensor,
34
- list[np.ndarray], list[torch.Tensor]]
36
+HfVideoItem: TypeAlias = Union[list["Image"], np.ndarray, "torch.Tensor",
37
+ list[np.ndarray], list["torch.Tensor"]]
38
"""
39
A {class}`transformers.image_utils.VideoInput` representing a single video
40
item, which can be passed to a HuggingFace `VideoProcessor`.
41
"""
42
40
-HfAudioItem: TypeAlias = Union[list[float], np.ndarray, torch.Tensor]
43
+HfAudioItem: TypeAlias = Union[list[float], np.ndarray, "torch.Tensor"]
44
"""
45
Represents a single audio
46
item, which can be passed to a HuggingFace `AudioProcessor`.
47
"""
48
46
-ImageItem: TypeAlias = Union[HfImageItem, torch.Tensor]
49
+ImageItem: TypeAlias = Union[HfImageItem, "torch.Tensor"]
50
"""
51
A {class}`transformers.image_utils.ImageInput` representing a single image
52
item, which can be passed to a HuggingFace `ImageProcessor`.
@@ -53,7 +56,7 @@ which are treated as image embeddings;
56
these are directly passed to the model without HF processing.
57
"""
58
56
-VideoItem: TypeAlias = Union[HfVideoItem, torch.Tensor]
59
+VideoItem: TypeAlias = Union[HfVideoItem, "torch.Tensor"]
60
"""
61
A {class}`transformers.image_utils.VideoInput` representing a single video
62
item, which can be passed to a HuggingFace `VideoProcessor`.
@@ -64,7 +67,7 @@ these are directly passed to the model without HF processing.
67
"""
68
69
AudioItem: TypeAlias = Union[HfAudioItem, tuple[np.ndarray, float],
67
- torch.Tensor]
70
+ "torch.Tensor"]
71
"""
72
Represents a single audio
73
item, which can be passed to a HuggingFace `AudioProcessor`.
@@ -132,7 +135,7 @@ class PlaceholderRange:
135
length: int
136
"""The length of the placeholder."""
137
135
- is_embed: Optional[torch.Tensor] = None
138
+ is_embed: Optional["torch.Tensor"] = None
139
"""
140
A boolean mask of shape `(length,)` indicating which positions
141
between `offset` and `offset + length` to assign embeddings to.
@@ -158,8 +161,8 @@ class PlaceholderRange:
161
return nested_tensors_equal(self.is_embed, other.is_embed)
162
163
161
-NestedTensors = Union[list["NestedTensors"], list[torch.Tensor], torch.Tensor,
162
- tuple[torch.Tensor, ...]]
164
+NestedTensors: TypeAlias = Union[list["NestedTensors"], list["torch.Tensor"],
165
+ "torch.Tensor", tuple["torch.Tensor", ...]]
166
"""
167
Uses a list instead of a tensor if the dimensions of each element do not match.
168
"""
@@ -261,7 +264,7 @@ class BaseMultiModalField(ABC):
264
"""
265
Construct {class}`MultiModalFieldElem` instances to represent
266
the provided data.
264
-
267
+
268
This is the inverse of {meth}`reduce_data`.
269
"""
270
raise NotImplementedError
@@ -422,7 +425,7 @@ class MultiModalFieldConfig:
425
modality: The modality of the multi-modal item that uses this
426
keyword argument.
427
slices: For each multi-modal item, a slice (dim=0) or a tuple of
425
- slices (dim>0) that is used to extract the data corresponding
428
+ slices (dim>0) that is used to extract the data corresponding
429
to it.
430
dim: The dimension to extract data, default to 0.
431
@@ -465,7 +468,7 @@ class MultiModalFieldConfig:
468
469
@staticmethod
470
def flat_from_sizes(modality: str,
468
- size_per_item: torch.Tensor,
471
+ size_per_item: "torch.Tensor",
472
dim: int = 0):
473
"""
474
Defines a field where an element in the batch is obtained by
@@ -602,7 +605,7 @@ class MultiModalKwargs(UserDict[str, NestedTensors]):
605
606
@staticmethod
607
def from_hf_inputs(
605
- hf_inputs: BatchFeature,
608
+ hf_inputs: "BatchFeature",
609
config_by_key: Mapping[str, MultiModalFieldConfig],
610
):
611
# NOTE: This skips fields in `hf_inputs` that are not in `config_by_key`
@@ -792,7 +795,7 @@ class MultiModalKwargs(UserDict[str, NestedTensors]):
795
return self._items_by_modality[modality]
796
797
795
-MultiModalPlaceholderDict = Mapping[str, Sequence[PlaceholderRange]]
798
+MultiModalPlaceholderDict: TypeAlias = Mapping[str, Sequence[PlaceholderRange]]
799
"""
800
A dictionary containing placeholder ranges for each modality.
801
"""
@@ -823,7 +826,7 @@ class MultiModalInputs(TypedDict):
826
mm_hashes: Optional["MultiModalHashDict"]
827
"""The hashes of the multi-modal data."""
828
826
- mm_placeholders: MultiModalPlaceholderDict
829
+ mm_placeholders: "MultiModalPlaceholderDict"
830
"""
831
For each modality, information about the placeholder tokens in
832
`prompt_token_ids`.
vllm/multimodal/parse.py
+13
-8
@@ -8,11 +8,9 @@ from typing import (TYPE_CHECKING, Any, Generic, Literal, NamedTuple, Optional,
8
9
import numpy as np
10
import torch
11
-from PIL.Image import Image
12
-from transformers import BatchFeature
11
from typing_extensions import TypeAlias, TypeGuard, assert_never
12
15
-from vllm.utils import is_list_of
13
+from vllm.utils import LazyLoader, is_list_of
14
15
from .audio import AudioResampler
16
from .inputs import (AudioItem, HfAudioItem, HfImageItem, HfVideoItem,
@@ -22,6 +20,11 @@ from .inputs import (AudioItem, HfAudioItem, HfImageItem, HfVideoItem,
20
_T = TypeVar("_T")
21
_I = TypeVar("_I")
22
23
+if TYPE_CHECKING:
24
+ import PIL.Image as PILImage
25
+else:
26
+ PILImage = LazyLoader("PILImage", globals(), "PIL.Image")
27
+
28
29
class ModalityDataItems(ABC, Generic[_T, _I]):
30
"""
@@ -131,6 +134,8 @@ class DictEmbeddingItems(ModalityDataItems[Mapping[str, torch.Tensor],
134
Mapping[str, MultiModalFieldConfig],
135
],
136
) -> None:
137
+ from transformers.feature_extraction_utils import BatchFeature
138
+
139
super().__init__(data, modality)
140
141
missing_required_data_keys = required_fields - data.keys()
@@ -200,7 +205,7 @@ class ImageProcessorItems(ProcessorBatchItems[HfImageItem]):
205
def get_image_size(self, item_idx: int) -> ImageSize:
206
image = self.get(item_idx)
207
203
- if isinstance(image, Image):
208
+ if isinstance(image, PILImage.Image):
209
return ImageSize(*image.size)
210
if isinstance(image, (np.ndarray, torch.Tensor)):
211
_, h, w = image.shape
@@ -226,7 +231,7 @@ class VideoProcessorItems(ProcessorBatchItems[HfVideoItem]):
231
def get_frame_size(self, item_idx: int) -> ImageSize:
232
image = self.get(item_idx)[0] # Assume that the video isn't empty
233
229
- if isinstance(image, Image):
234
+ if isinstance(image, PILImage.Image):
235
return ImageSize(*image.size)
236
if isinstance(image, (np.ndarray, torch.Tensor)):
237
_, h, w = image.shape
@@ -253,7 +258,7 @@ class MultiModalDataItems(UserDict[str, ModalityDataItems[Any, Any]]):
258
def get_count(self, modality: str, *, strict: bool = True) -> int:
259
"""
260
Get the number of data items belonging to a modality.
256
-
261
+
262
If `strict=False`, return `0` instead of raising {exc}`KeyError`
263
even if the modality is not found.
264
"""
@@ -399,7 +404,7 @@ class MultiModalDataParser:
404
if self._is_embeddings(data):
405
return ImageEmbeddingItems(data)
406
402
- if (isinstance(data, Image)
407
+ if (isinstance(data, PILImage.Image)
408
or isinstance(data,
409
(np.ndarray, torch.Tensor)) and data.ndim == 3):
410
data_items = [data]
@@ -420,7 +425,7 @@ class MultiModalDataParser:
425
if self._is_embeddings(data):
426
return VideoEmbeddingItems(data)
427
423
- if (is_list_of(data, Image)
428
+ if (is_list_of(data, PILImage.Image)
429
or isinstance(data,
430
(np.ndarray, torch.Tensor)) and data.ndim == 4):
431
data_items = [data]
vllm/multimodal/processing.py
+8
-5
@@ -13,7 +13,6 @@ from typing import (TYPE_CHECKING, Generic, NamedTuple, Optional, Protocol,
13
TypeVar, Union, cast)
14
15
import torch
16
-from transformers import BatchFeature, PretrainedConfig, ProcessorMixin
16
from typing_extensions import assert_never
17
18
from vllm.inputs import InputProcessingContext
@@ -31,6 +30,10 @@ from .parse import (DictEmbeddingItems, EmbeddingItems, MultiModalDataItems,
30
MultiModalDataParser)
31
32
if TYPE_CHECKING:
33
+ from transformers.configuration_utils import PretrainedConfig
34
+ from transformers.feature_extraction_utils import BatchFeature
35
+ from transformers.processing_utils import ProcessorMixin
36
+
37
from .profiling import BaseDummyInputsBuilder
38
39
logger = init_logger(__name__)
@@ -1047,10 +1050,10 @@ class BaseProcessingInfo:
1050
def get_tokenizer(self) -> AnyTokenizer:
1051
return self.ctx.tokenizer
1052
1050
- def get_hf_config(self) -> PretrainedConfig:
1053
+ def get_hf_config(self) -> "PretrainedConfig":
1054
return self.ctx.get_hf_config()
1055
1053
- def get_hf_processor(self, **kwargs: object) -> ProcessorMixin:
1056
+ def get_hf_processor(self, **kwargs: object) -> "ProcessorMixin":
1057
"""
1058
Subclasses can override this method to handle
1059
specific kwargs from model config or user inputs.
@@ -1165,7 +1168,7 @@ class BaseMultiModalProcessor(ABC, Generic[_I]):
1168
@abstractmethod
1169
def _get_mm_fields_config(
1170
self,
1168
- hf_inputs: BatchFeature,
1171
+ hf_inputs: "BatchFeature",
1172
hf_processor_mm_kwargs: Mapping[str, object],
1173
) -> Mapping[str, MultiModalFieldConfig]:
1174
"""Given the HF-processed data, output the metadata of each field."""
@@ -1222,7 +1225,7 @@ class BaseMultiModalProcessor(ABC, Generic[_I]):
1225
# This refers to the data to be passed to HF processor.
1226
mm_data: Mapping[str, object],
1227
mm_kwargs: Mapping[str, object],
1225
- ) -> BatchFeature:
1228
+ ) -> "BatchFeature":
1229
"""
1230
Call the HF processor on the prompt text and
1231
associated multi-modal data.