Signed-off-by: KurodaKanbei <mistergalahad@gmail.com> Signed-off-by: Jee Jee Li <jeejeelee@inferact.ai> Co-authored-by: Jee Jee Li <jeejeelee@inferact.ai> Co-authored-by: Jee Jee Li <pandaleefree@gmail.com>
658 lines
24 KiB
Python
658 lines
24 KiB
Python
# SPDX-License-Identifier: Apache-2.0
|
|
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
|
|
# ruff: noqa: E501
|
|
import math
|
|
from dataclasses import dataclass
|
|
from typing import Any
|
|
|
|
import torch
|
|
import torch.nn.functional as F
|
|
from torch import nn
|
|
from torch.nn import LayerNorm
|
|
from transformers.configuration_utils import PretrainedConfig
|
|
from transformers.modeling_utils import PreTrainedModel
|
|
|
|
from vllm.third_party.deep_gemm import per_block_cast_to_fp8
|
|
|
|
from .vision_attention import (
|
|
VisionRMSNorm,
|
|
VisionRotaryEmbedding,
|
|
VisionRotaryPositionEmbedding,
|
|
apply_vision_attention_residual,
|
|
attn_uses_seqlens,
|
|
build_vision_attention,
|
|
prepare_rotary_pos_emb_vision,
|
|
resolve_attn_implementation,
|
|
)
|
|
from .vision_moe import note_vision_fused_moe_fp8
|
|
|
|
|
|
class DotsMoEVitConfig(PretrainedConfig):
|
|
model_type: str = "dots_moe_vit"
|
|
|
|
def __init__(
|
|
self,
|
|
embed_dim: int = 1536,
|
|
hidden_size: int = 2048,
|
|
intermediate_size: int = 4224,
|
|
moe_intermediate_size: int = 2112,
|
|
num_hidden_layers: int = 42,
|
|
num_attention_heads: int = 24,
|
|
num_channels: int = 3,
|
|
patch_size: int = 14,
|
|
spatial_merge_size: int = 2,
|
|
temporal_patch_size: int = 1,
|
|
rms_norm_eps: float = 1e-5,
|
|
use_bias: bool = False,
|
|
use_qk_norm: bool = True,
|
|
attn_implementation="flash_attention_3", # "eager","eager_v2","sdpa","flash_attention_2","flash_attention_3"
|
|
initializer_range=0.02,
|
|
is_causal=False,
|
|
post_norm=True,
|
|
gradient_checkpointing=False,
|
|
pyramid_num_routed: list[int] | None = None,
|
|
capacity_factor: float = 2.0,
|
|
router_scoring_func: str = "sigmoid",
|
|
router_scale: float = 1.0,
|
|
adapter_in_dim: int = 1536,
|
|
adapter_out_dim: int = 2048,
|
|
adapter_merge_size: int = 2,
|
|
# ``pixel_shuffle_mlp`` reshapes 2x2 spatial neighbours via NHWC
|
|
# pixel-shuffle, then runs layer norm and a two-layer MLP.
|
|
# ``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.
|
|
adapter_type: str = "pixel_shuffle_mlp",
|
|
# If True the preprocessor already emits patches in 2x2-grouped order (qwen ``merge_size=2`` flatten path), so RoPE positions must also be regrouped
|
|
# to match the in-block layout. Older checkpoints were trained with
|
|
# ``pre_pixel_shuffle=False``, so the default keeps row-major RoPE.
|
|
pre_pixel_shuffle: bool = False,
|
|
# Keep the native encoder's post-load compile behavior. Compiling in
|
|
# ``__init__`` would rename checkpoint keys through ``_orig_mod``.
|
|
enable_torch_compile: bool = True,
|
|
# If True, use FP8 MoE implementation
|
|
enable_fp8_moe: bool = True,
|
|
**kwargs: Any,
|
|
):
|
|
super().__init__(**kwargs)
|
|
self.embed_dim = embed_dim
|
|
self.hidden_size = hidden_size
|
|
self.intermediate_size = intermediate_size
|
|
self.moe_intermediate_size = moe_intermediate_size
|
|
self.num_hidden_layers = num_hidden_layers
|
|
self.num_attention_heads = num_attention_heads
|
|
self.num_channels = num_channels
|
|
self.patch_size = patch_size
|
|
self.spatial_merge_size = spatial_merge_size
|
|
self.temporal_patch_size = temporal_patch_size
|
|
self.rms_norm_eps = rms_norm_eps
|
|
self.use_bias = use_bias
|
|
self.use_qk_norm = use_qk_norm
|
|
self.attn_implementation = attn_implementation
|
|
self.initializer_range = initializer_range
|
|
self.is_causal = is_causal
|
|
self.post_norm = post_norm
|
|
self.gradient_checkpointing = gradient_checkpointing
|
|
self.pyramid_num_routed = pyramid_num_routed or []
|
|
self.capacity_factor = capacity_factor
|
|
self.router_scoring_func = router_scoring_func
|
|
self.router_scale = router_scale
|
|
self.adapter_in_dim = adapter_in_dim
|
|
self.adapter_out_dim = adapter_out_dim
|
|
self.adapter_merge_size = adapter_merge_size
|
|
if adapter_type not in ("pixel_shuffle_mlp", "patch_merger"):
|
|
raise ValueError(
|
|
f"adapter_type must be 'pixel_shuffle_mlp' or 'patch_merger', got {adapter_type!r}"
|
|
)
|
|
self.adapter_type = adapter_type
|
|
self.pre_pixel_shuffle = pre_pixel_shuffle
|
|
self.enable_torch_compile = enable_torch_compile
|
|
self.enable_fp8_moe = enable_fp8_moe
|
|
|
|
|
|
# ---- FFN modules ----
|
|
|
|
|
|
class DotsSwiGLUFFN(nn.Module):
|
|
def __init__(self, in_features, hidden_features, bias=False):
|
|
super().__init__()
|
|
self.fc1 = nn.Linear(in_features, hidden_features, bias=bias)
|
|
self.fc2 = nn.Linear(hidden_features, in_features, bias=bias)
|
|
self.fc3 = nn.Linear(in_features, hidden_features, bias=bias)
|
|
|
|
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
|
return self.fc2(F.silu(self.fc1(x)) * self.fc3(x))
|
|
|
|
|
|
class MoESwiGLUFFN(nn.Module):
|
|
"""MoE FFN with per-expert SwiGLU experts, sigmoid/softmax gating, top-k routing."""
|
|
|
|
def __init__(self, config: DotsMoEVitConfig, layer_number: int):
|
|
super().__init__()
|
|
self.config = config
|
|
self.layer_number = layer_number
|
|
self.hidden_size = config.embed_dim
|
|
self.num_routed = config.pyramid_num_routed[layer_number]
|
|
self.capacity_factor = config.capacity_factor
|
|
self.router_scoring_func = config.router_scoring_func
|
|
self.router_scale = config.router_scale
|
|
|
|
self.register_buffer(
|
|
"router_bias", torch.zeros(self.num_routed, dtype=torch.float32)
|
|
)
|
|
|
|
self.experts = nn.ModuleList(
|
|
[
|
|
DotsSwiGLUFFN(
|
|
self.hidden_size, config.moe_intermediate_size, bias=config.use_bias
|
|
)
|
|
for _ in range(self.num_routed)
|
|
]
|
|
)
|
|
|
|
self.gate_weight = nn.Parameter(
|
|
torch.empty((self.num_routed, self.hidden_size), dtype=torch.float32)
|
|
)
|
|
nn.init.kaiming_uniform_(self.gate_weight, a=math.sqrt(5))
|
|
|
|
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
|
# Mirror cybertron's ``AIMv2MoEGEMMSwiGLUFFN.forward``: keep ``gating_prob`` /
|
|
# router-bias add in fp32 and run topk in fp32. The legacy ``.type_as(x_flat)``
|
|
# path on bf16 made expert routing diverge whenever two routed-expert scores
|
|
# tied within bf16 precision, which is the dominant source of numerical drift
|
|
# observed against latest cybertron checkpoints.
|
|
epsilon = 1e-9
|
|
x_flat = x.contiguous()
|
|
num_tokens = x_flat.shape[0]
|
|
|
|
gate_logits = F.linear(x_flat.float(), self.gate_weight.float())
|
|
|
|
if self.router_scoring_func == "sigmoid":
|
|
gating_prob = torch.sigmoid(gate_logits)
|
|
else:
|
|
gating_prob = torch.softmax(gate_logits, dim=-1, dtype=torch.float32)
|
|
|
|
aggregated_output = torch.zeros_like(x_flat)
|
|
aggregated_gate = torch.zeros(num_tokens, dtype=x.dtype, device=x.device)
|
|
|
|
topk = min(int(self.capacity_factor), self.num_routed)
|
|
|
|
gating_with_bias = gating_prob + self.router_bias.to(torch.float32).unsqueeze(0)
|
|
_, topk_indices = torch.topk(gating_with_bias, k=topk, dim=-1, sorted=False)
|
|
|
|
routed_weights = gating_prob.gather(1, topk_indices)
|
|
if self.router_scoring_func == "sigmoid" and topk > 1:
|
|
routed_weights = routed_weights / (
|
|
routed_weights.sum(dim=-1, keepdim=True) + epsilon
|
|
)
|
|
routed_weights = (routed_weights * self.router_scale).to(x_flat.dtype)
|
|
|
|
for expert_idx in range(self.num_routed):
|
|
selected_mask = topk_indices == expert_idx
|
|
if selected_mask.sum() == 0:
|
|
continue
|
|
n_idx, top = torch.where(selected_mask)
|
|
# Fancy indexing can yield non-contiguous rows; cuBLAS bf16 GEMM may then fail
|
|
# with ``CUBLAS_STATUS_INVALID_VALUE`` inside ``F.linear``.
|
|
x_selected = x_flat[n_idx].contiguous()
|
|
expert_output = self.experts[expert_idx](x_selected)
|
|
contrib = expert_output * routed_weights[n_idx, top].unsqueeze(-1)
|
|
aggregated_output[n_idx] = aggregated_output[n_idx] + contrib
|
|
aggregated_gate[n_idx] = aggregated_gate[n_idx] + routed_weights[n_idx, top]
|
|
|
|
aggregated_output = aggregated_output / (
|
|
aggregated_gate.unsqueeze(-1) + epsilon
|
|
)
|
|
return aggregated_output
|
|
|
|
|
|
def _ceil_to_multiple(value: int, multiple: int) -> int:
|
|
return ((value + multiple - 1) // multiple) * multiple
|
|
|
|
|
|
def _per_block_cast_to_fp8_padded(
|
|
weight: torch.Tensor,
|
|
block_size: int = 128,
|
|
) -> tuple[torch.Tensor, torch.Tensor]:
|
|
rows, columns = weight.shape
|
|
padded = weight.new_zeros(
|
|
_ceil_to_multiple(rows, block_size),
|
|
_ceil_to_multiple(columns, block_size),
|
|
)
|
|
padded[:rows, :columns] = weight
|
|
return per_block_cast_to_fp8(
|
|
padded.contiguous(),
|
|
use_ue8m0=False,
|
|
gran_k=block_size,
|
|
)
|
|
|
|
|
|
class MoESwiGLUFFNFP8(MoESwiGLUFFN):
|
|
"""NOTE vision MoE using the checkpoint's local block-FP8 semantics."""
|
|
|
|
@torch.no_grad()
|
|
def process_weights_after_loading(self) -> None:
|
|
if hasattr(self, "_fused_w13_fp8"):
|
|
return
|
|
|
|
w13_weights = []
|
|
w13_scales = []
|
|
w2_weights = []
|
|
w2_scales = []
|
|
for expert in self.experts:
|
|
w1, s1 = _per_block_cast_to_fp8_padded(expert.fc1.weight)
|
|
w3, s3 = _per_block_cast_to_fp8_padded(expert.fc3.weight)
|
|
w2, s2 = _per_block_cast_to_fp8_padded(expert.fc2.weight)
|
|
w13_weights.append(torch.cat((w1, w3), dim=0))
|
|
w13_scales.append(torch.cat((s1, s3), dim=0))
|
|
w2_weights.append(w2)
|
|
w2_scales.append(s2)
|
|
|
|
self.register_buffer(
|
|
"_fused_w13_fp8",
|
|
torch.stack(w13_weights).contiguous(),
|
|
persistent=False,
|
|
)
|
|
self.register_buffer(
|
|
"_fused_w13_scale",
|
|
torch.stack(w13_scales).contiguous(),
|
|
persistent=False,
|
|
)
|
|
self.register_buffer(
|
|
"_fused_w2_fp8",
|
|
torch.stack(w2_weights).contiguous(),
|
|
persistent=False,
|
|
)
|
|
self.register_buffer(
|
|
"_fused_w2_scale",
|
|
torch.stack(w2_scales).contiguous(),
|
|
persistent=False,
|
|
)
|
|
del self.experts
|
|
|
|
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
|
if not hasattr(self, "_fused_w13_fp8"):
|
|
raise RuntimeError("NOTE vision FP8 weights were not initialized")
|
|
|
|
gate_logits = F.linear(x.float(), self.gate_weight.float())
|
|
if self.router_scoring_func == "sigmoid":
|
|
scores = torch.sigmoid(gate_logits)
|
|
else:
|
|
scores = torch.softmax(gate_logits, dim=-1, dtype=torch.float32)
|
|
|
|
topk = min(int(self.capacity_factor), self.num_routed)
|
|
biased_scores = scores + self.router_bias.float().unsqueeze(0)
|
|
topk_ids = torch.topk(biased_scores, k=topk, dim=-1, sorted=False).indices
|
|
topk_weights = scores.gather(1, topk_ids)
|
|
if self.router_scoring_func == "sigmoid" and topk > 1:
|
|
topk_weights = topk_weights / (
|
|
topk_weights.sum(dim=-1, keepdim=True) + 1e-9
|
|
)
|
|
topk_weights = topk_weights * self.router_scale
|
|
|
|
output = note_vision_fused_moe_fp8(
|
|
x.contiguous(),
|
|
self._fused_w13_fp8,
|
|
self._fused_w2_fp8,
|
|
topk_weights,
|
|
topk_ids.to(torch.int32),
|
|
self._fused_w13_scale,
|
|
self._fused_w2_scale,
|
|
)
|
|
denominator = topk_weights.sum(dim=-1, keepdim=True).clamp_min(1e-9)
|
|
return (output / denominator).type_as(x)
|
|
|
|
|
|
# ---- PatchEmbed ----
|
|
|
|
|
|
class DotsPatchEmbed(nn.Module):
|
|
def __init__(self, config: DotsMoEVitConfig):
|
|
super().__init__()
|
|
self.num_channels = config.num_channels
|
|
self.patch_size = config.patch_size
|
|
self.temporal_patch_size = config.temporal_patch_size
|
|
self.embed_dim = config.embed_dim
|
|
self.proj = nn.Conv2d(
|
|
config.num_channels,
|
|
config.embed_dim,
|
|
kernel_size=(config.patch_size, config.patch_size),
|
|
stride=(config.patch_size, config.patch_size),
|
|
)
|
|
self.norm = VisionRMSNorm(config.embed_dim, eps=config.rms_norm_eps)
|
|
|
|
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
|
x = x.view(
|
|
-1,
|
|
self.num_channels,
|
|
self.temporal_patch_size,
|
|
self.patch_size,
|
|
self.patch_size,
|
|
)[:, :, 0]
|
|
x = self.proj(x).view(-1, self.embed_dim)
|
|
x = self.norm(x)
|
|
return x
|
|
|
|
|
|
# ---- Block ----
|
|
|
|
|
|
class MoEVisionBlock(nn.Module):
|
|
def __init__(self, config: DotsMoEVitConfig, layer_number: int):
|
|
super().__init__()
|
|
attn_impl = resolve_attn_implementation(
|
|
config.attn_implementation, eager_fallback="eager_v2"
|
|
)
|
|
self.attn = build_vision_attention(attn_impl, config, eager_fallback="eager_v2")
|
|
self._attn_uses_seqlens = attn_uses_seqlens(attn_impl)
|
|
self.norm_1 = VisionRMSNorm(config.embed_dim, eps=config.rms_norm_eps)
|
|
self.norm_2 = VisionRMSNorm(config.embed_dim, eps=config.rms_norm_eps)
|
|
|
|
is_moe = (
|
|
config.pyramid_num_routed
|
|
and layer_number < len(config.pyramid_num_routed)
|
|
and config.pyramid_num_routed[layer_number] > 0
|
|
)
|
|
if is_moe:
|
|
mlp_cls = MoESwiGLUFFNFP8 if config.enable_fp8_moe else MoESwiGLUFFN
|
|
self.mlp = mlp_cls(config, layer_number)
|
|
else:
|
|
self.mlp = DotsSwiGLUFFN(
|
|
config.embed_dim, config.intermediate_size, bias=config.use_bias
|
|
)
|
|
|
|
def forward(
|
|
self,
|
|
hidden_states,
|
|
cu_seqlens,
|
|
rotary_pos_emb,
|
|
max_seqlen: int,
|
|
seqlens: list[int] | None = None,
|
|
) -> torch.Tensor:
|
|
hidden_states = apply_vision_attention_residual(
|
|
self.attn,
|
|
self.norm_1,
|
|
hidden_states,
|
|
cu_seqlens,
|
|
max_seqlen,
|
|
rotary_pos_emb,
|
|
seqlens=seqlens,
|
|
uses_seqlens=self._attn_uses_seqlens,
|
|
)
|
|
hidden_states = hidden_states + self.mlp(self.norm_2(hidden_states))
|
|
return hidden_states
|
|
|
|
|
|
# ---- Adapter (pixel_shuffle + MLP) ----
|
|
|
|
|
|
def _pixel_shuffle(x, scale_factor=0.5):
|
|
if x.size(1) % 2 == 1:
|
|
x = torch.cat([x[:, :1], x], dim=1)
|
|
if x.size(2) % 2 == 1:
|
|
x = torch.cat([x[:, :, :1], x], dim=2)
|
|
n, h, w, c = x.size()
|
|
x = x.reshape(n, h, int(w * scale_factor), int(c / scale_factor))
|
|
x = x.permute(0, 2, 1, 3).contiguous()
|
|
x = x.reshape(
|
|
n,
|
|
int(w * scale_factor),
|
|
int(h * scale_factor),
|
|
int(c / (scale_factor * scale_factor)),
|
|
)
|
|
x = x.permute(0, 2, 1, 3).contiguous()
|
|
return x
|
|
|
|
|
|
class PixelShuffleAdapter(nn.Module):
|
|
"""Legacy adapter: NHWC pixel-shuffle spatial merge + LayerNorm + 2-layer MLP.
|
|
|
|
Mirrors ``cybertron`` ``FCAdapter(pool_kind='pixel_shuffle', proj_kind='mlp2x_ln_gelu')``.
|
|
State-dict keys: ``proj.0`` (LayerNorm of in_dim*merge**2), ``proj.1`` / ``proj.3`` (Linear).
|
|
"""
|
|
|
|
def __init__(self, config: DotsMoEVitConfig):
|
|
super().__init__()
|
|
in_dim = config.adapter_in_dim
|
|
out_dim = config.adapter_out_dim
|
|
merge_size = config.adapter_merge_size
|
|
merged_dim = in_dim * merge_size**2
|
|
self.proj = nn.Sequential(
|
|
LayerNorm(merged_dim),
|
|
nn.Linear(merged_dim, out_dim),
|
|
nn.GELU(),
|
|
nn.Linear(out_dim, out_dim),
|
|
)
|
|
|
|
def forward(
|
|
self,
|
|
patch_embed: torch.Tensor,
|
|
grid_thw: torch.Tensor,
|
|
) -> torch.Tensor:
|
|
assert patch_embed.dim() == 2 and grid_thw is not None
|
|
image_features = []
|
|
token_index = 0
|
|
for i in range(grid_thw.shape[0]):
|
|
grid_t, grid_h, grid_w = grid_thw[i]
|
|
images_token_length = grid_t * grid_h * grid_w
|
|
_pe = patch_embed[token_index : token_index + images_token_length]
|
|
token_index += images_token_length
|
|
if grid_t == 1:
|
|
_pe = _pe.reshape(int(grid_h), int(grid_w), -1).unsqueeze(0)
|
|
else:
|
|
_pe = _pe.reshape(int(grid_t), int(grid_h), int(grid_w), -1)
|
|
_pe = _pixel_shuffle(_pe, scale_factor=0.5)
|
|
_pe = _pe.squeeze(0) if grid_t == 1 else _pe.reshape(-1, _pe.shape[-1])
|
|
image_features.append(_pe.reshape(-1, _pe.shape[-1]))
|
|
out = torch.cat(image_features, dim=0)
|
|
out = self.proj(out)
|
|
return out
|
|
|
|
|
|
class PatchMergerAdapter(nn.Module):
|
|
"""Cybertron ``PatchMerger`` (``pool_kind='patch_merger', proj_kind='identity'``).
|
|
|
|
Assumes the encoder output is already laid out in ``merge_size``x``merge_size`` groups
|
|
(qwen ``pre_pixel_shuffle`` preprocessor + RoPE grouped accordingly), so merging is a
|
|
simple ``view(-1, merge**2 * in_dim)`` of consecutive tokens. State-dict layout matches
|
|
cybertron's ``PatchMerger`` (``ln_q`` over the per-token dim, ``mlp.0`` / ``mlp.2`` Linear).
|
|
"""
|
|
|
|
def __init__(self, config: DotsMoEVitConfig):
|
|
super().__init__()
|
|
in_dim = config.adapter_in_dim
|
|
out_dim = config.adapter_out_dim
|
|
merge_size = config.adapter_merge_size
|
|
merged_dim = in_dim * merge_size**2
|
|
self.merge_size = merge_size
|
|
self.merged_dim = merged_dim
|
|
self.ln_q = LayerNorm(in_dim, eps=1e-6)
|
|
self.mlp = nn.Sequential(
|
|
nn.Linear(merged_dim, merged_dim),
|
|
nn.GELU(),
|
|
nn.Linear(merged_dim, out_dim),
|
|
)
|
|
|
|
def forward(
|
|
self,
|
|
patch_embed: torch.Tensor,
|
|
grid_thw: torch.Tensor,
|
|
) -> torch.Tensor:
|
|
assert patch_embed.dim() == 2 and grid_thw is not None
|
|
x = self.ln_q(patch_embed)
|
|
x = x.reshape(-1, self.merged_dim)
|
|
return self.mlp(x)
|
|
|
|
|
|
_ADAPTER_CLASSES: dict[str, type[nn.Module]] = {
|
|
"pixel_shuffle_mlp": PixelShuffleAdapter,
|
|
"patch_merger": PatchMergerAdapter,
|
|
}
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class VitForwardMeta:
|
|
cu_seqlens: torch.Tensor
|
|
max_seqlen: int
|
|
rotary_pos_emb: VisionRotaryPositionEmbedding
|
|
grid_thw_cpu: torch.Tensor
|
|
seqlens: list[int] | None = None
|
|
|
|
|
|
# ---- Full Model ----
|
|
|
|
|
|
class DotsMoEVitModel(PreTrainedModel):
|
|
config_class = DotsMoEVitConfig
|
|
|
|
def __init__(self, config: DotsMoEVitConfig) -> None:
|
|
super().__init__(config)
|
|
self.config = config
|
|
self.spatial_merge_size = config.spatial_merge_size
|
|
|
|
self.patch_embed = DotsPatchEmbed(config)
|
|
|
|
head_dim = config.embed_dim // config.num_attention_heads
|
|
self.rotary_pos_emb = VisionRotaryEmbedding(head_dim // 2, cache_seq_len=100000)
|
|
|
|
self.blocks = nn.ModuleList(
|
|
[MoEVisionBlock(config, i) for i in range(config.num_hidden_layers)]
|
|
)
|
|
|
|
if config.post_norm:
|
|
self.post_trunk_norm = VisionRMSNorm(
|
|
config.embed_dim, eps=config.rms_norm_eps
|
|
)
|
|
|
|
adapter_cls = _ADAPTER_CLASSES.get(config.adapter_type)
|
|
if adapter_cls is None:
|
|
raise ValueError(f"Unknown adapter_type {config.adapter_type!r}")
|
|
self.adapter = adapter_cls(config)
|
|
|
|
self.gradient_checkpointing = False
|
|
self._gradient_checkpointing_func = torch.utils.checkpoint.checkpoint
|
|
|
|
@property
|
|
def dtype(self) -> torch.dtype:
|
|
mlp = self.blocks[0].mlp
|
|
if hasattr(mlp, "fc13"):
|
|
return mlp.fc13.weight.dtype
|
|
if hasattr(mlp, "fc1"):
|
|
return mlp.fc1.weight.dtype
|
|
expert = mlp.experts[0]
|
|
if hasattr(expert, "fc13"):
|
|
return expert.fc13.weight.dtype
|
|
return expert.fc1.weight.dtype
|
|
|
|
@property
|
|
def device(self) -> torch.device:
|
|
return self.patch_embed.proj.weight.device
|
|
|
|
def process_weights_after_loading(self) -> None:
|
|
for block in self.blocks:
|
|
if isinstance(block.mlp, MoESwiGLUFFNFP8):
|
|
block.mlp.process_weights_after_loading()
|
|
if self.config.enable_torch_compile:
|
|
self.compile_block_modules()
|
|
|
|
def compile_block_modules(self, **compile_kwargs: Any) -> None:
|
|
for block in self.blocks:
|
|
block.attn = torch.compile(block.attn, **compile_kwargs)
|
|
block.norm_1 = torch.compile(block.norm_1, **compile_kwargs)
|
|
block.norm_2 = torch.compile(block.norm_2, **compile_kwargs)
|
|
|
|
def get_pos_ids_by_grid(self, grid_thw_cpu: torch.Tensor):
|
|
# Mirrors ``cybertron`` ``AIMv2NativeModel.rot_pos_emb``: when ``pre_pixel_shuffle``
|
|
# is set, RoPE positions follow the qwen ``merge_size`` grouped layout (default 2x2);
|
|
# otherwise positions are flat row-major regardless of ``spatial_merge_size``.
|
|
if self.config.pre_pixel_shuffle:
|
|
rope_merge_size = (
|
|
self.spatial_merge_size if self.spatial_merge_size > 1 else 2
|
|
)
|
|
else:
|
|
rope_merge_size = 1
|
|
pos_ids = []
|
|
for t, h, w in grid_thw_cpu:
|
|
hpos_ids = torch.arange(h).unsqueeze(1).expand(-1, w)
|
|
hpos_ids = hpos_ids.reshape(
|
|
h // rope_merge_size,
|
|
rope_merge_size,
|
|
w // rope_merge_size,
|
|
rope_merge_size,
|
|
)
|
|
hpos_ids = hpos_ids.permute(0, 2, 1, 3).flatten()
|
|
|
|
wpos_ids = torch.arange(w).unsqueeze(0).expand(h, -1)
|
|
wpos_ids = wpos_ids.reshape(
|
|
h // rope_merge_size,
|
|
rope_merge_size,
|
|
w // rope_merge_size,
|
|
rope_merge_size,
|
|
)
|
|
wpos_ids = wpos_ids.permute(0, 2, 1, 3).flatten()
|
|
pos_ids.append(torch.stack([hpos_ids, wpos_ids], dim=-1).repeat(t, 1))
|
|
return pos_ids
|
|
|
|
def rot_pos_emb(self, grid_thw_cpu: torch.Tensor):
|
|
pos_ids = torch.cat(self.get_pos_ids_by_grid(grid_thw_cpu), dim=0)
|
|
pos_ids = pos_ids.to(self.device, non_blocking=True)
|
|
max_grid_size = int(grid_thw_cpu[:, 1:].max())
|
|
rotary_pos_emb_full = self.rotary_pos_emb(max_grid_size)
|
|
return rotary_pos_emb_full[pos_ids].flatten(1)
|
|
|
|
def prepare_meta(self, grid_thw_cpu: torch.Tensor) -> VitForwardMeta:
|
|
if grid_thw_cpu.device.type != "cpu":
|
|
raise ValueError("NOTE vision grid_thw must remain on CPU")
|
|
|
|
seq_per_frame = grid_thw_cpu[:, 1] * grid_thw_cpu[:, 2]
|
|
frame_lengths = torch.repeat_interleave(seq_per_frame, grid_thw_cpu[:, 0])
|
|
cu_seqlens_cpu = F.pad(
|
|
frame_lengths.cumsum(dim=0, dtype=torch.int32), (1, 0), value=0
|
|
)
|
|
resolved_attn = resolve_attn_implementation(
|
|
self.config.attn_implementation, eager_fallback="eager_v2"
|
|
)
|
|
return VitForwardMeta(
|
|
cu_seqlens=cu_seqlens_cpu.to(self.device, non_blocking=True),
|
|
max_seqlen=int(seq_per_frame.max()),
|
|
rotary_pos_emb=prepare_rotary_pos_emb_vision(
|
|
self.rot_pos_emb(grid_thw_cpu)
|
|
),
|
|
grid_thw_cpu=grid_thw_cpu,
|
|
seqlens=(
|
|
frame_lengths.tolist() if attn_uses_seqlens(resolved_attn) else None
|
|
),
|
|
)
|
|
|
|
def forward(
|
|
self, hidden_states: torch.Tensor, meta: VitForwardMeta, bf16=True
|
|
) -> torch.Tensor:
|
|
if bf16:
|
|
hidden_states = hidden_states.to(
|
|
device=self.device, dtype=self.dtype, non_blocking=True
|
|
)
|
|
hidden_states = self.patch_embed(hidden_states)
|
|
|
|
for blk in self.blocks:
|
|
if self.gradient_checkpointing and self.training:
|
|
hidden_states = self._gradient_checkpointing_func(
|
|
blk.__call__,
|
|
hidden_states,
|
|
meta.cu_seqlens,
|
|
meta.rotary_pos_emb,
|
|
meta.max_seqlen,
|
|
meta.seqlens,
|
|
)
|
|
else:
|
|
hidden_states = blk(
|
|
hidden_states,
|
|
meta.cu_seqlens,
|
|
meta.rotary_pos_emb,
|
|
meta.max_seqlen,
|
|
seqlens=meta.seqlens,
|
|
)
|
|
|
|
if self.config.post_norm:
|
|
hidden_states = self.post_trunk_norm(hidden_states)
|
|
|
|
hidden_states = self.adapter(hidden_states, meta.grid_thw_cpu)
|
|
return hidden_states
|