main
py 707 lines 24.3 KB
Raw
1 # SPDX-License-Identifier: Apache-2.0
2 # SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3 import itertools
4 from collections.abc import Iterator, Mapping, Sequence
5 from functools import partial
6 from typing import Annotated, Any, Literal, TypeAlias
7
8 import numpy as np
9 import torch
10 import torch.nn as nn
11 from einops import rearrange
12 from transformers import PreTrainedConfig
13 from transformers.activations import GELUActivation
14 from transformers.feature_extraction_utils import BatchFeature
15
16 from vllm.config import VllmConfig
17 from vllm.inputs import ModalityData
18 from vllm.logger import init_logger
19 from vllm.model_executor.layers.linear import ColumnParallelLinear, RowParallelLinear
20 from vllm.model_executor.layers.quantization import QuantizationConfig
21 from vllm.multimodal import MULTIMODAL_REGISTRY
22 from vllm.multimodal.inputs import (
23 ImageItem,
24 MultiModalFeatureSpec,
25 MultiModalFieldConfig,
26 MultiModalKwargsItems,
27 VideoItem,
28 )
29 from vllm.multimodal.parse import (
30 DictEmbeddingItems,
31 ModalityDataItems,
32 MultiModalDataItems,
33 MultiModalDataParser,
34 )
35 from vllm.multimodal.processing import (
36 PromptReplacement,
37 PromptUpdate,
38 PromptUpdateDetails,
39 )
40 from vllm.utils.tensor_schema import TensorSchema, TensorShape
41
42 from .interfaces import SupportsLoRA, SupportsMRoPE, SupportsMultiModal, SupportsPP
43 from .keye import (
44 BaseKeyeModule,
45 BaseMultiModalProcessor,
46 KeyeBaseDummyInputsBuilder,
47 KeyeProcessingInfo,
48 )
49
50 logger = init_logger(__name__)
51
52
53 def split_thw(grid_thw: torch.Tensor) -> torch.Tensor:
54 """Split grid_thw in t dimension.
55
56 Args:
57 grid_thw: [N, 3] tensor of [t, h, w]
58
59 Returns:
60 [Σt, 3] tensor where each row is [1, h, w]
61
62 Example:
63 >>> grid_thw = torch.tensor([[2, 3, 4], [1, 5, 6]])
64 >>> split_thw(grid_thw)
65 tensor([[1, 3, 4],
66 [1, 3, 4],
67 [1, 5, 6]])
68
69 """
70 t = grid_thw[:, 0]
71 h_w = grid_thw[:, 1:]
72 ones = torch.ones_like(h_w[:, :1])
73 return torch.cat([ones, h_w], dim=1).repeat_interleave(t, dim=0)
74
75
76 def get_num_patches(
77 grid_thw: torch.Tensor, num_frames: list[int] | torch.Tensor
78 ) -> torch.Tensor:
79 """Return num_patches per video.
80
81 Args:
82 grid_thw: Tensor with shape [N, 3] containing temporal, height, width
83 dimensions
84 num_frames: List or tensor indicating the number of frames per video
85
86 Returns:
87 Tensor of the number of patches for each video
88
89 Examples:
90 >>> # Suppose there are 2 videos with a total of 3 grids
91 >>> grid_thw = torch.tensor(
92 ... [
93 ... [2, 2, 2], # grid 0: 2*2*2=8 patches
94 ... [2, 2, 2], # grid 1: 2*2*2=8 patches
95 ... [1, 1, 1],
96 ... ]
97 ... ) # grid 2: 1*1*1=1 patches
98 >>> num_frames = [2, 1] # The first video contains 2 grids,
99 the second contains 1 grid.
100 >>> get_num_patches(grid_thw, num_frames)
101 tensor([16, 1]) # Total patches for first video: 8+8=16,
102 second video: 1.
103
104 """
105 assert len(grid_thw.shape) == 2
106 if isinstance(num_frames, torch.Tensor):
107 num_frames = num_frames.clone().tolist()
108
109 num_grids_per_frame = grid_thw.prod(dim=1)
110 start_idx_per_video = [0, *itertools.accumulate(num_frames)]
111 num_patches = [
112 num_grids_per_frame[start_idx_per_video[i] : start_idx_per_video[i + 1]].sum()
113 for i in range(len(num_frames))
114 ]
115 return (
116 torch.stack(num_patches)
117 if num_patches
118 else torch.zeros(0, dtype=grid_thw.dtype, device=grid_thw.device)
119 )
120
121
122 class KeyeVL1_5ImagePixelInputs(TensorSchema):
123 """Dimensions:
124 - bnp: Batch size * Number of patches
125 - c: Number of channels
126 - ps: Patch size
127 - ni: Number of images
128 - g: Grid dimensions (3 for t, h, w)
129 """
130
131 type: Literal["pixel_values"]
132
133 pixel_values: Annotated[
134 torch.Tensor, TensorShape("bnp", 3, "ps", "ps", dynamic_dims={"bnp"})
135 ]
136
137 image_grid_thw: Annotated[torch.Tensor, TensorShape("ni", 3)]
138
139
140 class KeyeVL1_5ImageEmbeddingInputs(TensorSchema):
141 """Dimensions:
142 - nf: Number of image features
143 - hs: Hidden size (must match the hidden size of language model
144 backbone)
145 - ni: Number of images
146 - g: Grid dimensions (3 for t, h, w)
147 """
148
149 type: Literal["image_embeds"]
150 image_embeds: Annotated[torch.Tensor, TensorShape("nf", "hs")]
151 image_grid_thw: Annotated[torch.Tensor, TensorShape("ni", 3)]
152
153
154 KeyeVL1_5ImageInputs: TypeAlias = (
155 KeyeVL1_5ImagePixelInputs | KeyeVL1_5ImageEmbeddingInputs
156 )
157
158
159 class KeyeVL1_5VideoPixelInputs(TensorSchema):
160 """Dimensions:
161 - bnp: Batch size * Number of patches
162 - c: Number of channels
163 - ps: Patch size
164 - ni: Number of images
165 - g: Grid dimensions (3 for t, h, w)
166 """
167
168 type: Literal["pixel_values_videos"]
169 pixel_values_videos: Annotated[
170 torch.Tensor, TensorShape("bnp", 3, "ps", "ps", dynamic_dims={"bnp"})
171 ]
172 video_grid_thw: Annotated[torch.Tensor, TensorShape("nv", 3)]
173
174 num_frames: torch.Tensor
175
176
177 class KeyeVL1_5VideoEmbeddingInputs(TensorSchema):
178 """Dimensions:
179 - nf: Number of video features
180 - hs: Hidden size (must match the hidden size of language model
181 backbone)
182 - nv: Number of videos
183 - g: Grid dimensions (3 for t, h, w)
184 """
185
186 type: Literal["video_embeds"]
187 video_embeds: Annotated[torch.Tensor, TensorShape("nf", "hs")]
188 video_grid_thw: Annotated[torch.Tensor, TensorShape("nv", 3)]
189 num_frames: torch.Tensor
190
191
192 KeyeVL1_5VideoInputs: TypeAlias = (
193 KeyeVL1_5VideoPixelInputs | KeyeVL1_5VideoEmbeddingInputs
194 )
195
196
197 class KeyeVL1_5Projector(nn.Module):
198 def __init__(
199 self,
200 text_config: PreTrainedConfig,
201 vision_config: PreTrainedConfig,
202 quant_config: QuantizationConfig | None = None,
203 prefix: str = "",
204 ):
205 super().__init__()
206 self.text_config = text_config
207 self.vision_config = vision_config
208 self.merge_kernel_size = (2, 2)
209
210 self.hidden_size = (
211 self.vision_config.hidden_size
212 * self.merge_kernel_size[0]
213 * self.merge_kernel_size[1]
214 )
215
216 self.pre_norm = torch.nn.LayerNorm(self.hidden_size, eps=1e-05)
217 self.act = GELUActivation()
218
219 self.linear_1 = ColumnParallelLinear(
220 self.hidden_size,
221 self.hidden_size,
222 bias=True,
223 quant_config=quant_config,
224 prefix=f"{prefix}.linear_1",
225 )
226 self.linear_2 = RowParallelLinear(
227 self.hidden_size,
228 self.text_config.hidden_size,
229 bias=True,
230 quant_config=quant_config,
231 prefix=f"{prefix}.linear_2",
232 )
233
234 def forward(
235 self,
236 image_features: torch.Tensor | tuple[torch.Tensor] | list[torch.Tensor],
237 image_grid_thw: list[tuple[int, int, int]],
238 ) -> torch.Tensor | list[torch.Tensor]:
239 m1, m2 = self.merge_kernel_size
240 if isinstance(image_features, (list, tuple)):
241 processed_features = list()
242 for image_feature, image_grid in zip(image_features, image_grid_thw):
243 t, h, w = image_grid
244 image_feature = rearrange(
245 image_feature,
246 "(t h p1 w p2) d -> (t h w) (p1 p2 d)",
247 t=t,
248 h=h // m1,
249 p1=m1,
250 w=w // m2,
251 p2=m2,
252 )
253 image_feature = self.pre_norm(image_feature)
254 hidden_states, _ = self.linear_1(image_feature)
255 hidden_states = self.act(hidden_states)
256 hidden_states, _ = self.linear_2(hidden_states)
257 processed_features.append(hidden_states)
258
259 return processed_features
260
261 dims = image_features.shape[:-1]
262 dim = image_features.shape[-1]
263 image_features = image_features.view(np.prod(dims), dim)
264 hidden_states = self.pre_norm(image_features.view(-1, self.hidden_size))
265 hidden_states = self.linear_1(hidden_states)
266 hidden_states = self.act(hidden_states)
267 hidden_states = self.linear_2(hidden_states)
268
269 return hidden_states.view(*dims, -1)
270
271
272 def _keye_field_config(
273 hf_inputs: Mapping[str, torch.Tensor],
274 ):
275 image_grid_thw = hf_inputs.get(
276 "image_grid_thw", torch.empty((0, 3), dtype=torch.int64)
277 )
278 image_grid_sizes = image_grid_thw.prod(-1)
279
280 video_grid_thw = hf_inputs.get(
281 "video_grid_thw", torch.empty((0, 3), dtype=torch.int64)
282 )
283 video_grid_thw = split_thw(video_grid_thw)
284 num_frames = hf_inputs.get("num_frames", video_grid_thw[:, 0]).clone().tolist()
285
286 video_num_patches = get_num_patches(video_grid_thw, num_frames)
287
288 video_num_grids = []
289 if len(num_frames) > 0:
290 i = 0
291 j = 1
292 cur_frames = num_frames[i]
293 for t, _, _ in video_grid_thw.tolist():
294 cur_frames -= t
295 if cur_frames == 0:
296 video_num_grids.append(j)
297 i += 1
298 if i < len(num_frames):
299 cur_frames = num_frames[i]
300 j = 1
301 else:
302 j += 1
303 video_num_grids = torch.tensor(video_num_grids)
304 return dict(
305 pixel_values=MultiModalFieldConfig.flat_from_sizes("image", image_grid_sizes),
306 image_embeds=MultiModalFieldConfig.flat_from_sizes("image", image_grid_sizes),
307 image_grid_thw=MultiModalFieldConfig.batched("image", keep_on_cpu=True),
308 pixel_values_videos=MultiModalFieldConfig.flat_from_sizes(
309 "video", video_num_patches
310 ),
311 video_embeds=MultiModalFieldConfig.flat_from_sizes("video", video_num_patches),
312 video_grid_thw=MultiModalFieldConfig.flat_from_sizes(
313 "video", video_num_grids, keep_on_cpu=True
314 ),
315 num_frames=MultiModalFieldConfig.batched("video", keep_on_cpu=True),
316 )
317
318
319 class KeyeVL1_5MultiModalDataParser(MultiModalDataParser):
320 # The patch grid is what sizes the placeholder range.
321 embedding_fields = {
322 "image": {"image_embeds": "values", "image_grid_thw": "metadata"},
323 "video": {"video_embeds": "values", "video_grid_thw": "metadata"},
324 }
325
326 def _parse_image_data(
327 self,
328 data: dict[str, torch.Tensor] | ModalityData[ImageItem],
329 ) -> ModalityDataItems[Any, Any] | None:
330 if isinstance(data, dict):
331 required, optional = self.embedding_field_sets("image")
332 return DictEmbeddingItems(
333 data,
334 modality="image",
335 required_fields=required,
336 optional_fields=optional,
337 fields_factory=_keye_field_config,
338 )
339
340 return super()._parse_image_data(data)
341
342 def _parse_video_data(
343 self,
344 data: dict[str, torch.Tensor] | ModalityData[VideoItem],
345 ) -> ModalityDataItems[Any, Any] | None:
346 if isinstance(data, dict):
347 required, optional = self.embedding_field_sets("video")
348 return DictEmbeddingItems(
349 data,
350 modality="video",
351 required_fields=required,
352 optional_fields=optional,
353 fields_factory=_keye_field_config,
354 )
355
356 return super()._parse_video_data(data)
357
358
359 class KeyeVL1_5ProcessingInfo(KeyeProcessingInfo):
360 def get_data_parser(self):
361 return KeyeVL1_5MultiModalDataParser(
362 expected_hidden_size=self._get_expected_hidden_size(),
363 allow_missing_mm_embeddings=self.allow_missing_mm_embeddings,
364 )
365
366 def get_max_frame_per_video(self) -> int:
367 return 2048
368
369 def get_supported_mm_limits(
370 self,
371 ) -> Mapping[str, int | None]:
372 return {"image": None, "video": 1}
373
374
375 class KeyeVL1_5MultiModalProcessor(BaseMultiModalProcessor[KeyeVL1_5ProcessingInfo]):
376 def _get_hf_mm_text(self, mm_counts: Mapping[str, int]) -> str:
377 return self.dummy_inputs.get_dummy_text(mm_counts)
378
379 def _get_prompt_updates(
380 self,
381 mm_items: MultiModalDataItems,
382 hf_processor_mm_kwargs: Mapping[str, Any],
383 out_mm_kwargs: MultiModalKwargsItems,
384 ) -> Sequence[PromptUpdate]:
385 hf_processor = self.info.get_hf_processor(**hf_processor_mm_kwargs)
386 image_processor = self.info.get_image_processor(**hf_processor_mm_kwargs)
387 tokenizer = self.info.get_tokenizer()
388 vocab = tokenizer.get_vocab()
389 image_token_id = vocab[hf_processor.image_token]
390 video_token_id = vocab[hf_processor.video_token]
391 placeholder = {"image": image_token_id, "video": video_token_id}
392 merge_length = image_processor.merge_size**2
393
394 out_mm_kwargs_data = out_mm_kwargs.get_data()
395 frame_types: list[torch.Tensor] = hf_processor_mm_kwargs.get(
396 "frame_types", None
397 )
398 timestamps: list[torch.Tensor] = hf_processor_mm_kwargs.get("timestamps", None)
399 num_videos = mm_items.get_count("video", strict=False)
400
401 if frame_types is None:
402 frame_types = [None] * num_videos
403 assert len(frame_types) == num_videos, (
404 f"Number of frame_types={len(frame_types)} "
405 f"doesn't equal to number of videos={num_videos}"
406 )
407 if timestamps is None:
408 timestamps = [None] * num_videos
409 assert len(timestamps) == num_videos, (
410 f"Number of timestamps={len(timestamps)} "
411 f"doesn't equal to number of videos={num_videos}"
412 )
413
414 video_grid_thw = out_mm_kwargs_data.get(
415 "video_grid_thw", torch.empty((0, 3), dtype=torch.int64)
416 )
417 num_frames = out_mm_kwargs_data.get(
418 "num_frames", torch.tensor([], dtype=torch.int64)
419 )
420
421 assert len(num_frames) == num_videos, (
422 f"Size of num_frames={len(num_frames)} "
423 f"doesn't equal to number of videos={num_videos}"
424 )
425
426 video_grid_hws = split_thw(video_grid_thw)
427 assert int(num_frames.sum().tolist()) == video_grid_hws.shape[0], (
428 f"The first dimension of `video_grid_hws`={video_grid_hws.shape[0]}"
429 f"doesn't equal to num of frames."
430 )
431
432 cu_seqlens = torch.cumsum(torch.tensor([0] + num_frames.tolist()), dim=-1)
433
434 def get_replacement_keye(item_idx: int, modality: str):
435 """Args:
436 item_idx(int): The item index of modality to replace
437 modality(str): The modality
438
439 """
440 if modality == "image":
441 out_item = out_mm_kwargs[modality][item_idx]
442 grid_thw = out_item[f"{modality}_grid_thw"].data
443 assert isinstance(grid_thw, torch.Tensor)
444
445 num_tokens = int(grid_thw.prod()) // merge_length
446 return [image_token_id] * num_tokens
447 elif modality == "video":
448 placeholders = []
449 video_timestamps = timestamps[item_idx]
450 video_frame_types = frame_types[item_idx]
451 grid_thw = video_grid_hws[
452 cu_seqlens[item_idx] : cu_seqlens[item_idx + 1]
453 ]
454
455 nframes = grid_thw.shape[0]
456
457 if video_timestamps is None:
458 video_timestamps = [""] * nframes
459 else:
460 video_timestamps = [format(ts, ".1f") for ts in video_timestamps]
461
462 if video_frame_types is None:
463 video_frame_types = [0] * nframes
464 for i, sub_thw in enumerate(grid_thw):
465 s = f"{hf_processor.frame_token}{video_timestamps[i]}"
466 if video_frame_types[i] == 1:
467 s += hf_processor.fast_start
468 placeholders.extend(tokenizer.encode(s))
469 num_frame_tokens = int(sub_thw.prod()) // merge_length
470 placeholders.extend([video_token_id] * num_frame_tokens)
471 if video_frame_types[i] == 1:
472 placeholders.append(vocab[hf_processor.fast_end])
473
474 return PromptUpdateDetails.select_token_id(
475 placeholders, embed_token_id=video_token_id
476 )
477 else:
478 raise ValueError(f"Unsupported modality {modality}")
479
480 return [
481 PromptReplacement(
482 modality=modality,
483 target=[placeholder[modality]],
484 replacement=partial(get_replacement_keye, modality=modality),
485 )
486 for modality in ("image", "video")
487 ]
488
489 def _get_mm_fields_config(
490 self,
491 hf_inputs: BatchFeature,
492 hf_processor_mm_kwargs: Mapping[str, object],
493 ) -> Mapping[str, MultiModalFieldConfig]:
494 return _keye_field_config(hf_inputs)
495
496
497 class KeyeVL1_5DummyInputsBuilder(
498 KeyeBaseDummyInputsBuilder[KeyeVL1_5ProcessingInfo]
499 ): ...
500
501
502 @MULTIMODAL_REGISTRY.register_processor(
503 KeyeVL1_5MultiModalProcessor,
504 info=KeyeVL1_5ProcessingInfo,
505 dummy_inputs=KeyeVL1_5DummyInputsBuilder,
506 )
507 class KeyeVL1_5ForConditionalGeneration(
508 BaseKeyeModule, SupportsMultiModal, SupportsLoRA, SupportsPP, SupportsMRoPE
509 ):
510 def _build_projector(
511 self,
512 text_config: PreTrainedConfig,
513 vision_config: PreTrainedConfig,
514 quant_config: QuantizationConfig | None = None,
515 prefix: str = "",
516 ) -> nn.Module:
517 return KeyeVL1_5Projector(text_config, vision_config, quant_config, prefix)
518
519 def __init__(self, *, vllm_config: VllmConfig, prefix: str = ""):
520 config: PreTrainedConfig = vllm_config.model_config.hf_config
521 self.merge_size = config.vision_config.spatial_merge_size
522 super().__init__(vllm_config=vllm_config, prefix=prefix)
523
524 def _parse_and_validate_image_input(
525 self, **kwargs: object
526 ) -> KeyeVL1_5ImageInputs | None:
527 pixel_values = kwargs.pop("pixel_values", None)
528 image_embeds = kwargs.pop("image_embeds", None)
529 image_grid_thw = kwargs.pop("image_grid_thw", None)
530
531 if pixel_values is None and image_embeds is None:
532 return None
533
534 if pixel_values is not None:
535 return KeyeVL1_5ImagePixelInputs(
536 type="pixel_values",
537 pixel_values=pixel_values,
538 image_grid_thw=image_grid_thw,
539 )
540
541 return KeyeVL1_5ImageEmbeddingInputs(
542 type="image_embeds",
543 image_embeds=image_embeds,
544 image_grid_thw=image_grid_thw,
545 )
546
547 def _parse_and_validate_video_input(
548 self, **kwargs: object
549 ) -> KeyeVL1_5VideoInputs | None:
550 pixel_values_videos = kwargs.pop("pixel_values_videos", None)
551 video_embeds = kwargs.pop("video_embeds", None)
552 video_grid_thw = kwargs.pop("video_grid_thw", None)
553 num_frames = kwargs.pop("num_frames", None)
554
555 if pixel_values_videos is None and video_embeds is None:
556 return None
557
558 if pixel_values_videos is not None:
559 return KeyeVL1_5VideoPixelInputs(
560 type="pixel_values_videos",
561 pixel_values_videos=pixel_values_videos,
562 video_grid_thw=video_grid_thw,
563 num_frames=num_frames,
564 )
565
566 return KeyeVL1_5VideoEmbeddingInputs(
567 type="video_embeds",
568 video_embeds=video_embeds,
569 video_grid_thw=video_grid_thw,
570 num_frames=num_frames,
571 )
572
573 def _process_video_input(
574 self, video_input: KeyeVL1_5VideoInputs
575 ) -> tuple[torch.Tensor, ...]:
576 video_type = video_input["type"]
577 video_grid_thw = split_thw(video_input["video_grid_thw"])
578 pixel_values_videos = video_input.get("pixel_values_videos", None)
579
580 video_embeds = self._process_video_embeds(
581 video_type, video_grid_thw, pixel_values_videos
582 )
583 video_embeds = torch.concat(video_embeds, dim=0)
584
585 num_frames = video_input["num_frames"].clone().tolist()
586
587 num_patches = get_num_patches(video_grid_thw, num_frames).tolist()
588
589 patch_cu_seqlens = torch.cumsum(
590 torch.tensor([0] + num_patches).detach().clone(), dim=-1
591 )
592 patch_cu_seqlens = torch.div(
593 patch_cu_seqlens, self.merge_size**2, rounding_mode="floor"
594 )
595
596 new_video_embeds = []
597 for idx in range(patch_cu_seqlens.shape[0] - 1):
598 start = patch_cu_seqlens[idx]
599 end = patch_cu_seqlens[idx + 1]
600 new_video_embeds.append(video_embeds[start:end])
601 return tuple(new_video_embeds)
602
603 def iter_mm_grid_thw(
604 self, mm_features: list[MultiModalFeatureSpec]
605 ) -> Iterator[tuple[int, int, int, int]]:
606 spatial_merge_size = self.config.vision_config.spatial_merge_size
607
608 for mm_feature in sorted(mm_features, key=lambda f: f.mm_position.offset):
609 if mm_feature.data is None:
610 raise ValueError("M-RoPE calculation requires multimodal feature data")
611
612 embed_ranges = mm_feature.mm_position.extract_embeds_range()
613 if mm_feature.modality == "image":
614 assert len(embed_ranges) == 1
615 grid_thw = mm_feature.data["image_grid_thw"].data
616 if isinstance(grid_thw, torch.Tensor):
617 if grid_thw.ndim == 2:
618 assert grid_thw.shape[0] == 1
619 t, h, w = grid_thw[0].tolist()
620 else:
621 t, h, w = grid_thw.tolist()
622 else:
623 if isinstance(grid_thw[0], list):
624 assert len(grid_thw) == 1
625 t, h, w = grid_thw[0]
626 else:
627 t, h, w = grid_thw
628
629 yield (
630 embed_ranges[0][0],
631 t,
632 h // spatial_merge_size,
633 w // spatial_merge_size,
634 )
635 elif mm_feature.modality == "video":
636 split_video_grids = split_thw(mm_feature.data["video_grid_thw"].data)
637 assert len(embed_ranges) == split_video_grids.shape[0]
638 for (start_idx, end_idx), (t, h, w) in zip(
639 embed_ranges, split_video_grids.tolist()
640 ):
641 llm_grid_h = h // spatial_merge_size
642 llm_grid_w = w // spatial_merge_size
643 num_mm_tokens = t * llm_grid_h * llm_grid_w
644 assert end_idx - start_idx + 1 == num_mm_tokens
645 yield (start_idx, t, llm_grid_h, llm_grid_w)
646 else:
647 raise ValueError(f"Unsupported modality: {mm_feature.modality}")
648
649 def get_mrope_input_positions(
650 self,
651 input_tokens: list[int],
652 mm_features: list[MultiModalFeatureSpec],
653 ) -> tuple[torch.Tensor, int]:
654 llm_pos_ids_list: list = []
655 st = 0
656
657 for (
658 offset,
659 llm_grid_t,
660 llm_grid_h,
661 llm_grid_w,
662 ) in self.iter_mm_grid_thw(mm_features):
663 text_len = offset - st
664
665 st_idx = llm_pos_ids_list[-1].max() + 1 if len(llm_pos_ids_list) > 0 else 0
666 llm_pos_ids_list.append(
667 torch.arange(text_len).view(1, -1).expand(3, -1) + st_idx
668 )
669
670 t_index = (
671 (
672 torch.arange(llm_grid_t)
673 .view(-1, 1)
674 .expand(-1, llm_grid_h * llm_grid_w)
675 )
676 .long()
677 .flatten()
678 )
679
680 h_index = (
681 torch.arange(llm_grid_h)
682 .view(1, -1, 1)
683 .expand(llm_grid_t, -1, llm_grid_w)
684 .flatten()
685 )
686 w_index = (
687 torch.arange(llm_grid_w)
688 .view(1, 1, -1)
689 .expand(llm_grid_t, llm_grid_h, -1)
690 .flatten()
691 )
692 llm_pos_ids_list.append(
693 torch.stack([t_index, h_index, w_index]) + text_len + st_idx
694 )
695 st = offset + llm_grid_t * llm_grid_h * llm_grid_w
696
697 if st < len(input_tokens):
698 st_idx = llm_pos_ids_list[-1].max() + 1 if len(llm_pos_ids_list) > 0 else 0
699 text_len = len(input_tokens) - st
700 llm_pos_ids_list.append(
701 torch.arange(text_len).view(1, -1).expand(3, -1) + st_idx
702 )
703
704 llm_positions = torch.cat(llm_pos_ids_list, dim=1).reshape(3, -1)
705 mrope_position_delta = (llm_positions.max() + 1 - len(input_tokens)).item()
706
707 return llm_positions, mrope_position_delta