main
py 657 lines 24.2 KB
Raw
1 # SPDX-License-Identifier: Apache-2.0
2 # SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3 # ruff: noqa: E501
4 import math
5 from dataclasses import dataclass
6 from typing import Any
7
8 import torch
9 import torch.nn.functional as F
10 from torch import nn
11 from torch.nn import LayerNorm
12 from transformers.configuration_utils import PreTrainedConfig
13 from transformers.modeling_utils import PreTrainedModel
14
15 from vllm.third_party.deep_gemm import per_block_cast_to_fp8
16
17 from .vision_attention import (
18 VisionRMSNorm,
19 VisionRotaryEmbedding,
20 VisionRotaryPositionEmbedding,
21 apply_vision_attention_residual,
22 attn_uses_seqlens,
23 build_vision_attention,
24 prepare_rotary_pos_emb_vision,
25 resolve_attn_implementation,
26 )
27 from .vision_moe import note_vision_fused_moe_fp8
28
29
30 class DotsMoEVitConfig(PreTrainedConfig):
31 model_type: str = "dots_moe_vit"
32
33 def __init__(
34 self,
35 embed_dim: int = 1536,
36 hidden_size: int = 2048,
37 intermediate_size: int = 4224,
38 moe_intermediate_size: int = 2112,
39 num_hidden_layers: int = 42,
40 num_attention_heads: int = 24,
41 num_channels: int = 3,
42 patch_size: int = 14,
43 spatial_merge_size: int = 2,
44 temporal_patch_size: int = 1,
45 rms_norm_eps: float = 1e-5,
46 use_bias: bool = False,
47 use_qk_norm: bool = True,
48 attn_implementation="flash_attention_3", # "eager","eager_v2","sdpa","flash_attention_2","flash_attention_3"
49 initializer_range=0.02,
50 is_causal=False,
51 post_norm=True,
52 gradient_checkpointing=False,
53 pyramid_num_routed: list[int] | None = None,
54 capacity_factor: float = 2.0,
55 router_scoring_func: str = "sigmoid",
56 router_scale: float = 1.0,
57 adapter_in_dim: int = 1536,
58 adapter_out_dim: int = 2048,
59 adapter_merge_size: int = 2,
60 # ``pixel_shuffle_mlp`` reshapes 2x2 spatial neighbours via NHWC
61 # pixel-shuffle, then runs layer norm and a two-layer MLP.
62 # ``patch_merger`` (cybertron PatchMerger, e.g. fireall_iter02275) skips the pixel-shuffle permutation and instead views every 4 consecutive 2x2-grouped tokens as one row.
63 adapter_type: str = "pixel_shuffle_mlp",
64 # If True the preprocessor already emits patches in 2x2-grouped order (qwen ``merge_size=2`` flatten path), so RoPE positions must also be regrouped
65 # to match the in-block layout. Older checkpoints were trained with
66 # ``pre_pixel_shuffle=False``, so the default keeps row-major RoPE.
67 pre_pixel_shuffle: bool = False,
68 # Keep the native encoder's post-load compile behavior. Compiling in
69 # ``__init__`` would rename checkpoint keys through ``_orig_mod``.
70 enable_torch_compile: bool = True,
71 # If True, use FP8 MoE implementation
72 enable_fp8_moe: bool = True,
73 **kwargs: Any,
74 ):
75 super().__init__(**kwargs)
76 self.embed_dim = embed_dim
77 self.hidden_size = hidden_size
78 self.intermediate_size = intermediate_size
79 self.moe_intermediate_size = moe_intermediate_size
80 self.num_hidden_layers = num_hidden_layers
81 self.num_attention_heads = num_attention_heads
82 self.num_channels = num_channels
83 self.patch_size = patch_size
84 self.spatial_merge_size = spatial_merge_size
85 self.temporal_patch_size = temporal_patch_size
86 self.rms_norm_eps = rms_norm_eps
87 self.use_bias = use_bias
88 self.use_qk_norm = use_qk_norm
89 self.attn_implementation = attn_implementation
90 self.initializer_range = initializer_range
91 self.is_causal = is_causal
92 self.post_norm = post_norm
93 self.gradient_checkpointing = gradient_checkpointing
94 self.pyramid_num_routed = pyramid_num_routed or []
95 self.capacity_factor = capacity_factor
96 self.router_scoring_func = router_scoring_func
97 self.router_scale = router_scale
98 self.adapter_in_dim = adapter_in_dim
99 self.adapter_out_dim = adapter_out_dim
100 self.adapter_merge_size = adapter_merge_size
101 if adapter_type not in ("pixel_shuffle_mlp", "patch_merger"):
102 raise ValueError(
103 f"adapter_type must be 'pixel_shuffle_mlp' or 'patch_merger', got {adapter_type!r}"
104 )
105 self.adapter_type = adapter_type
106 self.pre_pixel_shuffle = pre_pixel_shuffle
107 self.enable_torch_compile = enable_torch_compile
108 self.enable_fp8_moe = enable_fp8_moe
109
110
111 # ---- FFN modules ----
112
113
114 class DotsSwiGLUFFN(nn.Module):
115 def __init__(self, in_features, hidden_features, bias=False):
116 super().__init__()
117 self.fc1 = nn.Linear(in_features, hidden_features, bias=bias)
118 self.fc2 = nn.Linear(hidden_features, in_features, bias=bias)
119 self.fc3 = nn.Linear(in_features, hidden_features, bias=bias)
120
121 def forward(self, x: torch.Tensor) -> torch.Tensor:
122 return self.fc2(F.silu(self.fc1(x)) * self.fc3(x))
123
124
125 class MoESwiGLUFFN(nn.Module):
126 """MoE FFN with per-expert SwiGLU experts, sigmoid/softmax gating, top-k routing."""
127
128 def __init__(self, config: DotsMoEVitConfig, layer_number: int):
129 super().__init__()
130 self.config = config
131 self.layer_number = layer_number
132 self.hidden_size = config.embed_dim
133 self.num_routed = config.pyramid_num_routed[layer_number]
134 self.capacity_factor = config.capacity_factor
135 self.router_scoring_func = config.router_scoring_func
136 self.router_scale = config.router_scale
137
138 self.register_buffer(
139 "router_bias", torch.zeros(self.num_routed, dtype=torch.float32)
140 )
141
142 self.experts = nn.ModuleList(
143 [
144 DotsSwiGLUFFN(
145 self.hidden_size, config.moe_intermediate_size, bias=config.use_bias
146 )
147 for _ in range(self.num_routed)
148 ]
149 )
150
151 self.gate_weight = nn.Parameter(
152 torch.empty((self.num_routed, self.hidden_size), dtype=torch.float32)
153 )
154 nn.init.kaiming_uniform_(self.gate_weight, a=math.sqrt(5))
155
156 def forward(self, x: torch.Tensor) -> torch.Tensor:
157 # Mirror cybertron's ``AIMv2MoEGEMMSwiGLUFFN.forward``: keep ``gating_prob`` /
158 # router-bias add in fp32 and run topk in fp32. The legacy ``.type_as(x_flat)``
159 # path on bf16 made expert routing diverge whenever two routed-expert scores
160 # tied within bf16 precision, which is the dominant source of numerical drift
161 # observed against latest cybertron checkpoints.
162 epsilon = 1e-9
163 x_flat = x.contiguous()
164 num_tokens = x_flat.shape[0]
165
166 gate_logits = F.linear(x_flat.float(), self.gate_weight.float())
167
168 if self.router_scoring_func == "sigmoid":
169 gating_prob = torch.sigmoid(gate_logits)
170 else:
171 gating_prob = torch.softmax(gate_logits, dim=-1, dtype=torch.float32)
172
173 aggregated_output = torch.zeros_like(x_flat)
174 aggregated_gate = torch.zeros(num_tokens, dtype=x.dtype, device=x.device)
175
176 topk = min(int(self.capacity_factor), self.num_routed)
177
178 gating_with_bias = gating_prob + self.router_bias.to(torch.float32).unsqueeze(0)
179 _, topk_indices = torch.topk(gating_with_bias, k=topk, dim=-1, sorted=False)
180
181 routed_weights = gating_prob.gather(1, topk_indices)
182 if self.router_scoring_func == "sigmoid" and topk > 1:
183 routed_weights = routed_weights / (
184 routed_weights.sum(dim=-1, keepdim=True) + epsilon
185 )
186 routed_weights = (routed_weights * self.router_scale).to(x_flat.dtype)
187
188 for expert_idx in range(self.num_routed):
189 selected_mask = topk_indices == expert_idx
190 if selected_mask.sum() == 0:
191 continue
192 n_idx, top = torch.where(selected_mask)
193 # Fancy indexing can yield non-contiguous rows; cuBLAS bf16 GEMM may then fail
194 # with ``CUBLAS_STATUS_INVALID_VALUE`` inside ``F.linear``.
195 x_selected = x_flat[n_idx].contiguous()
196 expert_output = self.experts[expert_idx](x_selected)
197 contrib = expert_output * routed_weights[n_idx, top].unsqueeze(-1)
198 aggregated_output[n_idx] = aggregated_output[n_idx] + contrib
199 aggregated_gate[n_idx] = aggregated_gate[n_idx] + routed_weights[n_idx, top]
200
201 aggregated_output = aggregated_output / (
202 aggregated_gate.unsqueeze(-1) + epsilon
203 )
204 return aggregated_output
205
206
207 def _ceil_to_multiple(value: int, multiple: int) -> int:
208 return ((value + multiple - 1) // multiple) * multiple
209
210
211 def _per_block_cast_to_fp8_padded(
212 weight: torch.Tensor,
213 block_size: int = 128,
214 ) -> tuple[torch.Tensor, torch.Tensor]:
215 rows, columns = weight.shape
216 padded = weight.new_zeros(
217 _ceil_to_multiple(rows, block_size),
218 _ceil_to_multiple(columns, block_size),
219 )
220 padded[:rows, :columns] = weight
221 return per_block_cast_to_fp8(
222 padded.contiguous(),
223 use_ue8m0=False,
224 gran_k=block_size,
225 )
226
227
228 class MoESwiGLUFFNFP8(MoESwiGLUFFN):
229 """NOTE vision MoE using the checkpoint's local block-FP8 semantics."""
230
231 @torch.no_grad()
232 def process_weights_after_loading(self) -> None:
233 if hasattr(self, "_fused_w13_fp8"):
234 return
235
236 w13_weights = []
237 w13_scales = []
238 w2_weights = []
239 w2_scales = []
240 for expert in self.experts:
241 w1, s1 = _per_block_cast_to_fp8_padded(expert.fc1.weight)
242 w3, s3 = _per_block_cast_to_fp8_padded(expert.fc3.weight)
243 w2, s2 = _per_block_cast_to_fp8_padded(expert.fc2.weight)
244 w13_weights.append(torch.cat((w1, w3), dim=0))
245 w13_scales.append(torch.cat((s1, s3), dim=0))
246 w2_weights.append(w2)
247 w2_scales.append(s2)
248
249 self.register_buffer(
250 "_fused_w13_fp8",
251 torch.stack(w13_weights).contiguous(),
252 persistent=False,
253 )
254 self.register_buffer(
255 "_fused_w13_scale",
256 torch.stack(w13_scales).contiguous(),
257 persistent=False,
258 )
259 self.register_buffer(
260 "_fused_w2_fp8",
261 torch.stack(w2_weights).contiguous(),
262 persistent=False,
263 )
264 self.register_buffer(
265 "_fused_w2_scale",
266 torch.stack(w2_scales).contiguous(),
267 persistent=False,
268 )
269 del self.experts
270
271 def forward(self, x: torch.Tensor) -> torch.Tensor:
272 if not hasattr(self, "_fused_w13_fp8"):
273 raise RuntimeError("NOTE vision FP8 weights were not initialized")
274
275 gate_logits = F.linear(x.float(), self.gate_weight.float())
276 if self.router_scoring_func == "sigmoid":
277 scores = torch.sigmoid(gate_logits)
278 else:
279 scores = torch.softmax(gate_logits, dim=-1, dtype=torch.float32)
280
281 topk = min(int(self.capacity_factor), self.num_routed)
282 biased_scores = scores + self.router_bias.float().unsqueeze(0)
283 topk_ids = torch.topk(biased_scores, k=topk, dim=-1, sorted=False).indices
284 topk_weights = scores.gather(1, topk_ids)
285 if self.router_scoring_func == "sigmoid" and topk > 1:
286 topk_weights = topk_weights / (
287 topk_weights.sum(dim=-1, keepdim=True) + 1e-9
288 )
289 topk_weights = topk_weights * self.router_scale
290
291 output = note_vision_fused_moe_fp8(
292 x.contiguous(),
293 self._fused_w13_fp8,
294 self._fused_w2_fp8,
295 topk_weights,
296 topk_ids.to(torch.int32),
297 self._fused_w13_scale,
298 self._fused_w2_scale,
299 )
300 denominator = topk_weights.sum(dim=-1, keepdim=True).clamp_min(1e-9)
301 return (output / denominator).type_as(x)
302
303
304 # ---- PatchEmbed ----
305
306
307 class DotsPatchEmbed(nn.Module):
308 def __init__(self, config: DotsMoEVitConfig):
309 super().__init__()
310 self.num_channels = config.num_channels
311 self.patch_size = config.patch_size
312 self.temporal_patch_size = config.temporal_patch_size
313 self.embed_dim = config.embed_dim
314 self.proj = nn.Conv2d(
315 config.num_channels,
316 config.embed_dim,
317 kernel_size=(config.patch_size, config.patch_size),
318 stride=(config.patch_size, config.patch_size),
319 )
320 self.norm = VisionRMSNorm(config.embed_dim, eps=config.rms_norm_eps)
321
322 def forward(self, x: torch.Tensor) -> torch.Tensor:
323 x = x.view(
324 -1,
325 self.num_channels,
326 self.temporal_patch_size,
327 self.patch_size,
328 self.patch_size,
329 )[:, :, 0]
330 x = self.proj(x).view(-1, self.embed_dim)
331 x = self.norm(x)
332 return x
333
334
335 # ---- Block ----
336
337
338 class MoEVisionBlock(nn.Module):
339 def __init__(self, config: DotsMoEVitConfig, layer_number: int):
340 super().__init__()
341 attn_impl = resolve_attn_implementation(
342 config.attn_implementation, eager_fallback="eager_v2"
343 )
344 self.attn = build_vision_attention(attn_impl, config, eager_fallback="eager_v2")
345 self._attn_uses_seqlens = attn_uses_seqlens(attn_impl)
346 self.norm_1 = VisionRMSNorm(config.embed_dim, eps=config.rms_norm_eps)
347 self.norm_2 = VisionRMSNorm(config.embed_dim, eps=config.rms_norm_eps)
348
349 is_moe = (
350 config.pyramid_num_routed
351 and layer_number < len(config.pyramid_num_routed)
352 and config.pyramid_num_routed[layer_number] > 0
353 )
354 if is_moe:
355 mlp_cls = MoESwiGLUFFNFP8 if config.enable_fp8_moe else MoESwiGLUFFN
356 self.mlp = mlp_cls(config, layer_number)
357 else:
358 self.mlp = DotsSwiGLUFFN(
359 config.embed_dim, config.intermediate_size, bias=config.use_bias
360 )
361
362 def forward(
363 self,
364 hidden_states,
365 cu_seqlens,
366 rotary_pos_emb,
367 max_seqlen: int,
368 seqlens: list[int] | None = None,
369 ) -> torch.Tensor:
370 hidden_states = apply_vision_attention_residual(
371 self.attn,
372 self.norm_1,
373 hidden_states,
374 cu_seqlens,
375 max_seqlen,
376 rotary_pos_emb,
377 seqlens=seqlens,
378 uses_seqlens=self._attn_uses_seqlens,
379 )
380 hidden_states = hidden_states + self.mlp(self.norm_2(hidden_states))
381 return hidden_states
382
383
384 # ---- Adapter (pixel_shuffle + MLP) ----
385
386
387 def _pixel_shuffle(x, scale_factor=0.5):
388 if x.size(1) % 2 == 1:
389 x = torch.cat([x[:, :1], x], dim=1)
390 if x.size(2) % 2 == 1:
391 x = torch.cat([x[:, :, :1], x], dim=2)
392 n, h, w, c = x.size()
393 x = x.reshape(n, h, int(w * scale_factor), int(c / scale_factor))
394 x = x.permute(0, 2, 1, 3).contiguous()
395 x = x.reshape(
396 n,
397 int(w * scale_factor),
398 int(h * scale_factor),
399 int(c / (scale_factor * scale_factor)),
400 )
401 x = x.permute(0, 2, 1, 3).contiguous()
402 return x
403
404
405 class PixelShuffleAdapter(nn.Module):
406 """Legacy adapter: NHWC pixel-shuffle spatial merge + LayerNorm + 2-layer MLP.
407
408 Mirrors ``cybertron`` ``FCAdapter(pool_kind='pixel_shuffle', proj_kind='mlp2x_ln_gelu')``.
409 State-dict keys: ``proj.0`` (LayerNorm of in_dim*merge**2), ``proj.1`` / ``proj.3`` (Linear).
410 """
411
412 def __init__(self, config: DotsMoEVitConfig):
413 super().__init__()
414 in_dim = config.adapter_in_dim
415 out_dim = config.adapter_out_dim
416 merge_size = config.adapter_merge_size
417 merged_dim = in_dim * merge_size**2
418 self.proj = nn.Sequential(
419 LayerNorm(merged_dim),
420 nn.Linear(merged_dim, out_dim),
421 nn.GELU(),
422 nn.Linear(out_dim, out_dim),
423 )
424
425 def forward(
426 self,
427 patch_embed: torch.Tensor,
428 grid_thw: torch.Tensor,
429 ) -> torch.Tensor:
430 assert patch_embed.dim() == 2 and grid_thw is not None
431 image_features = []
432 token_index = 0
433 for i in range(grid_thw.shape[0]):
434 grid_t, grid_h, grid_w = grid_thw[i]
435 images_token_length = grid_t * grid_h * grid_w
436 _pe = patch_embed[token_index : token_index + images_token_length]
437 token_index += images_token_length
438 if grid_t == 1:
439 _pe = _pe.reshape(int(grid_h), int(grid_w), -1).unsqueeze(0)
440 else:
441 _pe = _pe.reshape(int(grid_t), int(grid_h), int(grid_w), -1)
442 _pe = _pixel_shuffle(_pe, scale_factor=0.5)
443 _pe = _pe.squeeze(0) if grid_t == 1 else _pe.reshape(-1, _pe.shape[-1])
444 image_features.append(_pe.reshape(-1, _pe.shape[-1]))
445 out = torch.cat(image_features, dim=0)
446 out = self.proj(out)
447 return out
448
449
450 class PatchMergerAdapter(nn.Module):
451 """Cybertron ``PatchMerger`` (``pool_kind='patch_merger', proj_kind='identity'``).
452
453 Assumes the encoder output is already laid out in ``merge_size``x``merge_size`` groups
454 (qwen ``pre_pixel_shuffle`` preprocessor + RoPE grouped accordingly), so merging is a
455 simple ``view(-1, merge**2 * in_dim)`` of consecutive tokens. State-dict layout matches
456 cybertron's ``PatchMerger`` (``ln_q`` over the per-token dim, ``mlp.0`` / ``mlp.2`` Linear).
457 """
458
459 def __init__(self, config: DotsMoEVitConfig):
460 super().__init__()
461 in_dim = config.adapter_in_dim
462 out_dim = config.adapter_out_dim
463 merge_size = config.adapter_merge_size
464 merged_dim = in_dim * merge_size**2
465 self.merge_size = merge_size
466 self.merged_dim = merged_dim
467 self.ln_q = LayerNorm(in_dim, eps=1e-6)
468 self.mlp = nn.Sequential(
469 nn.Linear(merged_dim, merged_dim),
470 nn.GELU(),
471 nn.Linear(merged_dim, out_dim),
472 )
473
474 def forward(
475 self,
476 patch_embed: torch.Tensor,
477 grid_thw: torch.Tensor,
478 ) -> torch.Tensor:
479 assert patch_embed.dim() == 2 and grid_thw is not None
480 x = self.ln_q(patch_embed)
481 x = x.reshape(-1, self.merged_dim)
482 return self.mlp(x)
483
484
485 _ADAPTER_CLASSES: dict[str, type[nn.Module]] = {
486 "pixel_shuffle_mlp": PixelShuffleAdapter,
487 "patch_merger": PatchMergerAdapter,
488 }
489
490
491 @dataclass(frozen=True)
492 class VitForwardMeta:
493 cu_seqlens: torch.Tensor
494 max_seqlen: int
495 rotary_pos_emb: VisionRotaryPositionEmbedding
496 grid_thw_cpu: torch.Tensor
497 seqlens: list[int] | None = None
498
499
500 # ---- Full Model ----
501
502
503 class DotsMoEVitModel(PreTrainedModel):
504 config_class = DotsMoEVitConfig
505
506 def __init__(self, config: DotsMoEVitConfig) -> None:
507 super().__init__(config)
508 self.config = config
509 self.spatial_merge_size = config.spatial_merge_size
510
511 self.patch_embed = DotsPatchEmbed(config)
512
513 head_dim = config.embed_dim // config.num_attention_heads
514 self.rotary_pos_emb = VisionRotaryEmbedding(head_dim // 2, cache_seq_len=100000)
515
516 self.blocks = nn.ModuleList(
517 [MoEVisionBlock(config, i) for i in range(config.num_hidden_layers)]
518 )
519
520 if config.post_norm:
521 self.post_trunk_norm = VisionRMSNorm(
522 config.embed_dim, eps=config.rms_norm_eps
523 )
524
525 adapter_cls = _ADAPTER_CLASSES.get(config.adapter_type)
526 if adapter_cls is None:
527 raise ValueError(f"Unknown adapter_type {config.adapter_type!r}")
528 self.adapter = adapter_cls(config)
529
530 self.gradient_checkpointing = False
531 self._gradient_checkpointing_func = torch.utils.checkpoint.checkpoint
532
533 @property
534 def dtype(self) -> torch.dtype:
535 mlp = self.blocks[0].mlp
536 if hasattr(mlp, "fc13"):
537 return mlp.fc13.weight.dtype
538 if hasattr(mlp, "fc1"):
539 return mlp.fc1.weight.dtype
540 expert = mlp.experts[0]
541 if hasattr(expert, "fc13"):
542 return expert.fc13.weight.dtype
543 return expert.fc1.weight.dtype
544
545 @property
546 def device(self) -> torch.device:
547 return self.patch_embed.proj.weight.device
548
549 def process_weights_after_loading(self) -> None:
550 for block in self.blocks:
551 if isinstance(block.mlp, MoESwiGLUFFNFP8):
552 block.mlp.process_weights_after_loading()
553 if self.config.enable_torch_compile:
554 self.compile_block_modules()
555
556 def compile_block_modules(self, **compile_kwargs: Any) -> None:
557 for block in self.blocks:
558 block.attn = torch.compile(block.attn, **compile_kwargs)
559 block.norm_1 = torch.compile(block.norm_1, **compile_kwargs)
560 block.norm_2 = torch.compile(block.norm_2, **compile_kwargs)
561
562 def get_pos_ids_by_grid(self, grid_thw_cpu: torch.Tensor):
563 # Mirrors ``cybertron`` ``AIMv2NativeModel.rot_pos_emb``: when ``pre_pixel_shuffle``
564 # is set, RoPE positions follow the qwen ``merge_size`` grouped layout (default 2x2);
565 # otherwise positions are flat row-major regardless of ``spatial_merge_size``.
566 if self.config.pre_pixel_shuffle:
567 rope_merge_size = (
568 self.spatial_merge_size if self.spatial_merge_size > 1 else 2
569 )
570 else:
571 rope_merge_size = 1
572 pos_ids = []
573 for t, h, w in grid_thw_cpu:
574 hpos_ids = torch.arange(h).unsqueeze(1).expand(-1, w)
575 hpos_ids = hpos_ids.reshape(
576 h // rope_merge_size,
577 rope_merge_size,
578 w // rope_merge_size,
579 rope_merge_size,
580 )
581 hpos_ids = hpos_ids.permute(0, 2, 1, 3).flatten()
582
583 wpos_ids = torch.arange(w).unsqueeze(0).expand(h, -1)
584 wpos_ids = wpos_ids.reshape(
585 h // rope_merge_size,
586 rope_merge_size,
587 w // rope_merge_size,
588 rope_merge_size,
589 )
590 wpos_ids = wpos_ids.permute(0, 2, 1, 3).flatten()
591 pos_ids.append(torch.stack([hpos_ids, wpos_ids], dim=-1).repeat(t, 1))
592 return pos_ids
593
594 def rot_pos_emb(self, grid_thw_cpu: torch.Tensor):
595 pos_ids = torch.cat(self.get_pos_ids_by_grid(grid_thw_cpu), dim=0)
596 pos_ids = pos_ids.to(self.device, non_blocking=True)
597 max_grid_size = int(grid_thw_cpu[:, 1:].max())
598 rotary_pos_emb_full = self.rotary_pos_emb(max_grid_size)
599 return rotary_pos_emb_full[pos_ids].flatten(1)
600
601 def prepare_meta(self, grid_thw_cpu: torch.Tensor) -> VitForwardMeta:
602 if grid_thw_cpu.device.type != "cpu":
603 raise ValueError("NOTE vision grid_thw must remain on CPU")
604
605 seq_per_frame = grid_thw_cpu[:, 1] * grid_thw_cpu[:, 2]
606 frame_lengths = torch.repeat_interleave(seq_per_frame, grid_thw_cpu[:, 0])
607 cu_seqlens_cpu = F.pad(
608 frame_lengths.cumsum(dim=0, dtype=torch.int32), (1, 0), value=0
609 )
610 resolved_attn = resolve_attn_implementation(
611 self.config.attn_implementation, eager_fallback="eager_v2"
612 )
613 return VitForwardMeta(
614 cu_seqlens=cu_seqlens_cpu.to(self.device, non_blocking=True),
615 max_seqlen=int(seq_per_frame.max()),
616 rotary_pos_emb=prepare_rotary_pos_emb_vision(
617 self.rot_pos_emb(grid_thw_cpu)
618 ),
619 grid_thw_cpu=grid_thw_cpu,
620 seqlens=(
621 frame_lengths.tolist() if attn_uses_seqlens(resolved_attn) else None
622 ),
623 )
624
625 def forward(
626 self, hidden_states: torch.Tensor, meta: VitForwardMeta, bf16=True
627 ) -> torch.Tensor:
628 if bf16:
629 hidden_states = hidden_states.to(
630 device=self.device, dtype=self.dtype, non_blocking=True
631 )
632 hidden_states = self.patch_embed(hidden_states)
633
634 for blk in self.blocks:
635 if self.gradient_checkpointing and self.training:
636 hidden_states = self._gradient_checkpointing_func(
637 blk.__call__,
638 hidden_states,
639 meta.cu_seqlens,
640 meta.rotary_pos_emb,
641 meta.max_seqlen,
642 meta.seqlens,
643 )
644 else:
645 hidden_states = blk(
646 hidden_states,
647 meta.cu_seqlens,
648 meta.rotary_pos_emb,
649 meta.max_seqlen,
650 seqlens=meta.seqlens,
651 )
652
653 if self.config.post_norm:
654 hidden_states = self.post_trunk_norm(hidden_states)
655
656 hidden_states = self.adapter(hidden_states, meta.grid_thw_cpu)
657 return hidden_states