Image Feature Extraction
Transformers
Safetensors
monkeyocrv2_vitae_encoder
feature-extraction
custom_code
Instructions to use zenosai/MonkeyOCRv2-AS with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use zenosai/MonkeyOCRv2-AS with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("image-feature-extraction", model="zenosai/MonkeyOCRv2-AS", trust_remote_code=True)# Load model directly from transformers import AutoModel model = AutoModel.from_pretrained("zenosai/MonkeyOCRv2-AS", trust_remote_code=True, dtype="auto") - Notebooks
- Google Colab
- Kaggle
| import math | |
| import torch | |
| import torch.nn as nn | |
| import torch.nn.functional as F | |
| import torch.utils.checkpoint | |
| from transformers.modeling_utils import PreTrainedModel | |
| flash_attn_available = True | |
| try: | |
| from flash_attn import flash_attn_varlen_func | |
| except ImportError: | |
| flash_attn_available = False | |
| from .configuration_monkeyocrv2_vitae import MonkeyOCRv2ViTAEEncoderConfig | |
| # --------------------------------------------------------------------------- | |
| # 2D-RoPE helpers | |
| # --------------------------------------------------------------------------- | |
| def rotate_half(x): | |
| x1 = x[..., : x.shape[-1] // 2] | |
| x2 = x[..., x.shape[-1] // 2:] | |
| return torch.cat((-x2, x1), dim=-1) | |
| def apply_rotary_pos_emb_vision(tensor: torch.Tensor, freqs: torch.Tensor) -> torch.Tensor: | |
| orig_dtype = tensor.dtype | |
| tensor = tensor.float() | |
| cos = freqs.cos() | |
| sin = freqs.sin() | |
| cos = cos.unsqueeze(1).repeat(1, 1, 2).unsqueeze(0).float() | |
| sin = sin.unsqueeze(1).repeat(1, 1, 2).unsqueeze(0).float() | |
| output = (tensor * cos) + (rotate_half(tensor) * sin) | |
| return output.to(orig_dtype) | |
| class VisionRotaryEmbedding(nn.Module): | |
| def __init__(self, dim: int, theta: float = 10000.0) -> None: | |
| super().__init__() | |
| if dim % 2 != 0: | |
| raise ValueError("2D-RoPE requires an even per-axis rotary dimension.") | |
| inv_freq = 1.0 / (theta ** (torch.arange(0, dim, 2, dtype=torch.float) / dim)) | |
| self.register_buffer("inv_freq", inv_freq, persistent=False) | |
| def forward(self, seqlen: int) -> torch.Tensor: | |
| seq = torch.arange(seqlen, device=self.inv_freq.device, dtype=self.inv_freq.dtype) | |
| freqs = torch.outer(seq, self.inv_freq) | |
| return freqs | |
| # --------------------------------------------------------------------------- | |
| # Norms | |
| # --------------------------------------------------------------------------- | |
| class RMSNorm(nn.Module): | |
| def __init__(self, dim: int, eps: float = 1e-6): | |
| super().__init__() | |
| self.weight = nn.Parameter(torch.ones(dim)) | |
| self.eps = eps | |
| def forward(self, x: torch.Tensor) -> torch.Tensor: | |
| return self._norm(x.float()).type_as(x) * self.weight | |
| def _norm(self, x: torch.Tensor) -> torch.Tensor: | |
| return x * torch.rsqrt(x.pow(2).mean(-1, keepdim=True) + self.eps) | |
| class RMSNorm2d(nn.Module): | |
| def __init__(self, dim: int, eps: float = 1e-6): | |
| super().__init__() | |
| self.weight = nn.Parameter(torch.ones(dim)) | |
| self.bias = nn.Parameter(torch.zeros(dim)) | |
| self.eps = eps | |
| def forward(self, x: torch.Tensor) -> torch.Tensor: | |
| orig_dtype = x.dtype | |
| x = x.float() | |
| x = x * torch.rsqrt(x.pow(2).mean(1, keepdim=True) + self.eps) | |
| x = x * self.weight[:, None, None] + self.bias[:, None, None] | |
| return x.to(orig_dtype) | |
| # --------------------------------------------------------------------------- | |
| # Attention | |
| # --------------------------------------------------------------------------- | |
| class VisionAttentionSdpa(nn.Module): | |
| def __init__(self, dim: int, num_heads: int, bias: bool = False): | |
| super().__init__() | |
| self.num_heads = num_heads | |
| self.head_dim = dim // num_heads | |
| self.qkv = nn.Linear(dim, dim * 3, bias=bias) | |
| self.proj = nn.Linear(dim, dim, bias=bias) | |
| def forward(self, x: torch.Tensor, cu_seqlens: torch.Tensor, | |
| rotary_pos_emb: torch.Tensor = None) -> torch.Tensor: | |
| S = x.shape[0] | |
| q, k, v = self.qkv(x).reshape(S, 3, self.num_heads, self.head_dim).permute(1, 0, 2, 3).unbind(0) | |
| if rotary_pos_emb is not None: | |
| q = apply_rotary_pos_emb_vision(q.unsqueeze(0), rotary_pos_emb).squeeze(0) | |
| k = apply_rotary_pos_emb_vision(k.unsqueeze(0), rotary_pos_emb).squeeze(0) | |
| mask = torch.zeros([1, S, S], device=q.device, dtype=torch.bool) | |
| for i in range(1, len(cu_seqlens)): | |
| mask[..., cu_seqlens[i - 1]:cu_seqlens[i], cu_seqlens[i - 1]:cu_seqlens[i]] = True | |
| q = q.transpose(0, 1).unsqueeze(0) | |
| k = k.transpose(0, 1).unsqueeze(0) | |
| v = v.transpose(0, 1).unsqueeze(0) | |
| if mask.stride(-1) != 1: | |
| mask = torch.empty_like(mask, memory_format=torch.contiguous_format).copy_(mask) | |
| out = F.scaled_dot_product_attention(q, k, v, mask, dropout_p=0.0) | |
| out = out.squeeze(0).transpose(0, 1).reshape(S, -1) | |
| return self.proj(out) | |
| class VisionFlashAttention2(nn.Module): | |
| def __init__(self, dim: int, num_heads: int, is_causal: bool = False, bias: bool = False): | |
| super().__init__() | |
| self.num_heads = num_heads | |
| self.head_dim = dim // num_heads | |
| self.is_causal = is_causal | |
| self.qkv = nn.Linear(dim, dim * 3, bias=bias) | |
| self.proj = nn.Linear(dim, dim, bias=bias) | |
| def forward(self, x: torch.Tensor, cu_seqlens: torch.Tensor, | |
| rotary_pos_emb: torch.Tensor = None) -> torch.Tensor: | |
| S = x.shape[0] | |
| q, k, v = self.qkv(x).reshape(S, 3, self.num_heads, self.head_dim).permute(1, 0, 2, 3).unbind(0) | |
| if rotary_pos_emb is not None: | |
| q = apply_rotary_pos_emb_vision(q.unsqueeze(0), rotary_pos_emb).squeeze(0) | |
| k = apply_rotary_pos_emb_vision(k.unsqueeze(0), rotary_pos_emb).squeeze(0) | |
| max_seqlen = (cu_seqlens[1:] - cu_seqlens[:-1]).max().item() | |
| out = flash_attn_varlen_func(q, k, v, cu_seqlens, cu_seqlens, max_seqlen, max_seqlen, | |
| causal=self.is_causal) | |
| out = out.reshape(S, -1) | |
| return self.proj(out) | |
| class VisionAttentionEager(nn.Module): | |
| def __init__(self, dim: int, num_heads: int, bias: bool = False): | |
| super().__init__() | |
| self.num_heads = num_heads | |
| self.head_dim = dim // num_heads | |
| self.qkv = nn.Linear(dim, dim * 3, bias=bias) | |
| self.proj = nn.Linear(dim, dim, bias=bias) | |
| def forward(self, x: torch.Tensor, cu_seqlens: torch.Tensor, | |
| rotary_pos_emb: torch.Tensor = None) -> torch.Tensor: | |
| S = x.shape[0] | |
| q, k, v = self.qkv(x).reshape(S, 3, self.num_heads, self.head_dim).permute(1, 0, 2, 3).unbind(0) | |
| if rotary_pos_emb is not None: | |
| q = apply_rotary_pos_emb_vision(q.unsqueeze(0), rotary_pos_emb).squeeze(0) | |
| k = apply_rotary_pos_emb_vision(k.unsqueeze(0), rotary_pos_emb).squeeze(0) | |
| mask = torch.full([1, S, S], torch.finfo(q.dtype).min, device=q.device, dtype=q.dtype) | |
| for i in range(1, len(cu_seqlens)): | |
| mask[..., cu_seqlens[i - 1]:cu_seqlens[i], cu_seqlens[i - 1]:cu_seqlens[i]] = 0 | |
| q = q.transpose(0, 1); k = k.transpose(0, 1); v = v.transpose(0, 1) | |
| w = torch.matmul(q, k.transpose(-2, -1)) / math.sqrt(self.head_dim) + mask | |
| w = F.softmax(w, dim=-1, dtype=torch.float32).to(q.dtype) | |
| out = torch.matmul(w, v).transpose(0, 1).reshape(S, -1) | |
| return self.proj(out) | |
| def _build_attn(dim: int, num_heads: int, attn_impl: str, is_causal: bool, bias: bool) -> nn.Module: | |
| if attn_impl == "flash_attention_2": | |
| if flash_attn_available: | |
| return VisionFlashAttention2(dim, num_heads, is_causal=is_causal, bias=bias) | |
| attn_impl = "sdpa" | |
| if attn_impl == "sdpa": | |
| return VisionAttentionSdpa(dim, num_heads, bias=bias) | |
| return VisionAttentionEager(dim, num_heads, bias=bias) | |
| # --------------------------------------------------------------------------- | |
| # MLP (standard 2-layer GELU, matching official ViTAEv2) | |
| # --------------------------------------------------------------------------- | |
| class Mlp(nn.Module): | |
| def __init__(self, dim: int, hidden_dim: int, bias: bool = False): | |
| super().__init__() | |
| self.fc1 = nn.Linear(dim, hidden_dim, bias=bias) | |
| self.fc2 = nn.Linear(hidden_dim, dim, bias=bias) | |
| def forward(self, x: torch.Tensor) -> torch.Tensor: | |
| return self.fc2(F.gelu(self.fc1(x))) | |
| class SwiGLU(nn.Module): | |
| def __init__(self, dim: int, hidden_dim: int, bias: bool = False): | |
| super().__init__() | |
| self.gate_proj = nn.Linear(dim, hidden_dim, bias=bias) | |
| self.up_proj = nn.Linear(dim, hidden_dim, bias=bias) | |
| self.down_proj = nn.Linear(hidden_dim, dim, bias=bias) | |
| def forward(self, x: torch.Tensor) -> torch.Tensor: | |
| return self.down_proj(F.silu(self.gate_proj(x)) * self.up_proj(x)) | |
| # --------------------------------------------------------------------------- | |
| # Window-attention helpers | |
| # --------------------------------------------------------------------------- | |
| def window_partition(x: torch.Tensor, window_size: int): | |
| """x: [B, H, W, C] → [nW*B, ws, ws, C]""" | |
| B, H, W, C = x.shape | |
| x = x.view(B, H // window_size, window_size, W // window_size, window_size, C) | |
| return x.permute(0, 1, 3, 2, 4, 5).contiguous().view(-1, window_size, window_size, C) | |
| def window_reverse(windows: torch.Tensor, window_size: int, H: int, W: int) -> torch.Tensor: | |
| """windows: [nW*B, ws, ws, C] → [B, H, W, C]""" | |
| B = int(windows.shape[0] / (H * W / window_size / window_size)) | |
| x = windows.view(B, H // window_size, W // window_size, window_size, window_size, -1) | |
| return x.permute(0, 1, 3, 2, 4, 5).contiguous().view(B, H, W, -1) | |
| # --------------------------------------------------------------------------- | |
| # NormalCell with 2D-RoPE | |
| # --------------------------------------------------------------------------- | |
| class NormalCell2DRoPE(nn.Module): | |
| """ViTAEv2 NormalCell with windowed or global attention + 2D-RoPE + PCM.""" | |
| def __init__(self, dim: int, num_heads: int, mlp_ratio: float = 4.0, | |
| window_size: int = 7, window_attn: bool = True, | |
| bias: bool = False, norm_eps: float = 1e-6, | |
| attn_impl: str = "sdpa", is_causal: bool = False, | |
| pcm_group: int = None): | |
| super().__init__() | |
| self.dim = dim | |
| self.num_heads = num_heads | |
| self.head_dim = dim // num_heads | |
| if self.head_dim % 4 != 0: | |
| raise ValueError("2D-RoPE requires attention head_dim divisible by 4.") | |
| self.window_attn = window_attn | |
| self.window_size = window_size | |
| self.norm1 = RMSNorm(dim, eps=norm_eps) | |
| self.norm2 = RMSNorm(dim, eps=norm_eps) | |
| self.qkv = nn.Linear(dim, dim * 3, bias=bias) | |
| self.proj = nn.Linear(dim, dim, bias=bias) | |
| hidden_dim = int(dim * mlp_ratio) | |
| swiglu_hidden_dim = math.ceil((2 * hidden_dim) / 3) | |
| self.mlp = SwiGLU(dim, swiglu_hidden_dim, bias=bias) | |
| g = pcm_group if pcm_group is not None else max(1, dim // 64) | |
| self.pcm = nn.Sequential( | |
| nn.Conv2d(dim, hidden_dim, 3, 1, 1, groups=g), | |
| RMSNorm2d(hidden_dim, eps=norm_eps), | |
| nn.SiLU(inplace=True), | |
| nn.Conv2d(hidden_dim, dim, 3, 1, 1, groups=g), | |
| RMSNorm2d(dim, eps=norm_eps), | |
| nn.SiLU(inplace=True), | |
| nn.Conv2d(dim, dim, 3, 1, 1, groups=g), | |
| ) | |
| self._attn_impl = attn_impl | |
| self._is_causal = is_causal | |
| half_d = self.head_dim // 2 | |
| ws = window_size | |
| h_ids = torch.arange(ws).unsqueeze(1).expand(ws, ws).reshape(-1) | |
| w_ids = torch.arange(ws).unsqueeze(0).expand(ws, ws).reshape(-1) | |
| theta = 1.0 / (10000.0 ** (torch.arange(0, half_d, 2, dtype=torch.float32) / half_d)) | |
| freq_h = torch.outer(h_ids.float(), theta) # [ws*ws, half_d//2] | |
| freq_w = torch.outer(w_ids.float(), theta) | |
| freqs = torch.stack([freq_h, freq_w], dim=2).reshape(ws * ws, half_d) # [ws*ws, half_d] | |
| cos_emb = freqs.cos().repeat(1, 2)[None, None] # [1, 1, ws*ws, head_dim] | |
| sin_emb = freqs.sin().repeat(1, 2)[None, None] | |
| self.register_buffer("_local_rope_cos", cos_emb, persistent=False) | |
| self.register_buffer("_local_rope_sin", sin_emb, persistent=False) | |
| def _apply_local_rope_window(self, q: torch.Tensor, ws: int) -> torch.Tensor: | |
| q_f = q.float() | |
| q_out = q_f * self._local_rope_cos + rotate_half(q_f) * self._local_rope_sin | |
| return q_out.to(q.dtype) | |
| def _window_attn(self, x: torch.Tensor, H: int, W: int) -> torch.Tensor: | |
| ws = self.window_size | |
| pad_h = (ws - H % ws) % ws | |
| pad_w = (ws - W % ws) % ws | |
| x2d = x.view(H, W, self.dim).unsqueeze(0) | |
| if pad_h + pad_w > 0: | |
| x2d = x2d.permute(0, 3, 1, 2) | |
| x2d = F.pad(x2d, (0, pad_w, 0, pad_h)) | |
| x2d = x2d.permute(0, 2, 3, 1) | |
| Hp, Wp = H + pad_h, W + pad_w | |
| wins = window_partition(x2d, ws) # [nW, ws, ws, dim] | |
| nWB = wins.shape[0] | |
| wins = wins.view(nWB, ws * ws, self.dim) | |
| q, k, v = self.qkv(wins).reshape(nWB, ws * ws, 3, self.num_heads, self.head_dim).permute(2, 0, 3, 1, 4).unbind(0) | |
| q = self._apply_local_rope_window(q, ws) | |
| k = self._apply_local_rope_window(k, ws) | |
| attn = torch.matmul(q, k.transpose(-2, -1)) / math.sqrt(self.head_dim) | |
| attn = F.softmax(attn, dim=-1, dtype=torch.float32).to(q.dtype) | |
| out = torch.matmul(attn, v).transpose(1, 2).reshape(nWB, ws * ws, self.dim) | |
| out = self.proj(out) | |
| out = out.view(nWB, ws, ws, self.dim) | |
| out = window_reverse(out, ws, Hp, Wp) | |
| if pad_h + pad_w > 0: | |
| out = out[:, :H, :W, :].contiguous() | |
| return out.view(-1, self.dim) | |
| def _global_attn(self, x: torch.Tensor, cu_seqlens: torch.Tensor, | |
| rotary_pos_emb: torch.Tensor) -> torch.Tensor: | |
| S = x.shape[0] | |
| q, k, v = self.qkv(x).reshape(S, 3, self.num_heads, self.head_dim).permute(1, 0, 2, 3).unbind(0) | |
| if rotary_pos_emb is not None: | |
| q = apply_rotary_pos_emb_vision(q.unsqueeze(0), rotary_pos_emb).squeeze(0) | |
| k = apply_rotary_pos_emb_vision(k.unsqueeze(0), rotary_pos_emb).squeeze(0) | |
| if flash_attn_available and self._attn_impl == "flash_attention_2": | |
| max_sl = (cu_seqlens[1:] - cu_seqlens[:-1]).max().item() | |
| out = flash_attn_varlen_func(q, k, v, cu_seqlens, cu_seqlens, max_sl, max_sl, causal=False) | |
| else: | |
| mask = torch.zeros([1, S, S], device=q.device, dtype=torch.bool) | |
| for i in range(1, len(cu_seqlens)): | |
| mask[..., cu_seqlens[i - 1]:cu_seqlens[i], cu_seqlens[i - 1]:cu_seqlens[i]] = True | |
| qt = q.transpose(0, 1).unsqueeze(0) | |
| kt = k.transpose(0, 1).unsqueeze(0) | |
| vt = v.transpose(0, 1).unsqueeze(0) | |
| if mask.stride(-1) != 1: | |
| mask = torch.empty_like(mask, memory_format=torch.contiguous_format).copy_(mask) | |
| out = F.scaled_dot_product_attention(qt, kt, vt, mask, dropout_p=0.0) | |
| out = out.squeeze(0).transpose(0, 1) | |
| out = out.reshape(S, -1) | |
| return self.proj(out) | |
| def _pcm_single(self, x_flat: torch.Tensor, H: int, W: int) -> torch.Tensor: | |
| x2d = x_flat.view(1, H, W, self.dim).permute(0, 3, 1, 2) # [1, dim, H, W] | |
| return self.pcm(x2d).permute(0, 2, 3, 1).reshape(H * W, self.dim) | |
| def _pcm(self, x: torch.Tensor, cu_seqlens: torch.Tensor, grid_hw: torch.Tensor) -> torch.Tensor: | |
| seqlens = (cu_seqlens[1:] - cu_seqlens[:-1]).tolist() | |
| outs, token_idx = [], 0 | |
| for img_idx, sl in enumerate(seqlens): | |
| H, W = int(grid_hw[img_idx, 0]), int(grid_hw[img_idx, 1]) | |
| outs.append(self._pcm_single(x[token_idx: token_idx + sl], H, W)) | |
| token_idx += sl | |
| return torch.cat(outs, dim=0) | |
| def forward(self, hidden_states: torch.Tensor, cu_seqlens: torch.Tensor, | |
| rotary_pos_emb: torch.Tensor, grid_hw: torch.Tensor = None) -> torch.Tensor: | |
| shortcut = hidden_states | |
| x_norm = self.norm1(hidden_states) | |
| if self.window_attn and grid_hw is not None: | |
| seqlens = (cu_seqlens[1:] - cu_seqlens[:-1]).tolist() | |
| attn_outs, pcm_outs = [], [] | |
| token_idx = 0 | |
| for img_idx, sl in enumerate(seqlens): | |
| H, W = int(grid_hw[img_idx, 0]), int(grid_hw[img_idx, 1]) | |
| attn_outs.append(self._window_attn(x_norm[token_idx: token_idx + sl], H, W)) | |
| pcm_outs.append(self._pcm_single(shortcut[token_idx: token_idx + sl], H, W)) | |
| token_idx += sl | |
| attn_out = torch.cat(attn_outs, dim=0) | |
| pcm_out = torch.cat(pcm_outs, dim=0) | |
| else: | |
| attn_out = self._global_attn(x_norm, cu_seqlens, rotary_pos_emb) | |
| pcm_out = self._pcm(shortcut, cu_seqlens, grid_hw) if grid_hw is not None else 0.0 | |
| hidden_states = shortcut + attn_out + pcm_out | |
| hidden_states = hidden_states + self.mlp(self.norm2(hidden_states)) | |
| return hidden_states | |
| # --------------------------------------------------------------------------- | |
| # ReductionCell | |
| # --------------------------------------------------------------------------- | |
| class ReductionCell(nn.Module): | |
| """ | |
| ViTAEv2 ReductionCell: downsamples tokens via PRM (4 dilation branches) + | |
| single Transformer attention block + 3-conv PCM shortcut. | |
| RC_tokens_type in ViTAEv2-S: RC0&RC1 -> window attention (W), RC2&RC3 -> global attention (F). | |
| RC_group in ViTAEv2-S: [1, 16, 32, 64] for RC0, RC1, RC2, RC3. | |
| """ | |
| def __init__(self, in_dim: int, out_dim: int, | |
| num_heads: int = 1, | |
| window_attn: bool = True, | |
| window_size: int = 7, | |
| pcm_group: int = 1, | |
| prm_embed_dim: int = 64, | |
| downsample_ratio: int = 2, | |
| kernel_size: int = 3, | |
| norm_eps: float = 1e-6, | |
| bias: bool = False, | |
| attn_impl: str = "sdpa"): | |
| super().__init__() | |
| self.in_dim = in_dim | |
| self.out_dim = out_dim | |
| self.num_heads = num_heads | |
| self.head_dim = out_dim // num_heads | |
| self.window_attn = window_attn | |
| self.window_size = window_size | |
| self.prm_embed_dim = prm_embed_dim | |
| self.downsample_ratio = downsample_ratio | |
| self.kernel_size = kernel_size | |
| self._attn_impl = attn_impl | |
| dilations = [1, 2, 3, 4] | |
| self.prm_convs = nn.ModuleList() | |
| for d in dilations: | |
| pad = math.ceil(((kernel_size - 1) * d + 1 - downsample_ratio) / 2) | |
| self.prm_convs.append(nn.Sequential( | |
| nn.Conv2d(in_dim, prm_embed_dim, kernel_size=kernel_size, stride=downsample_ratio, padding=pad, dilation=d), | |
| nn.GELU(), | |
| )) | |
| self.prm_proj = nn.Linear(prm_embed_dim * 4, out_dim, bias=bias) | |
| pcm_strides = [] | |
| residual = downsample_ratio // 2 | |
| for _ in range(3): | |
| pcm_strides.append((residual > 0) + 1) | |
| residual //= 2 | |
| assert residual == 0 | |
| g = pcm_group | |
| self.pcm = nn.Sequential( | |
| nn.Conv2d(in_dim, prm_embed_dim, 3, stride=pcm_strides[0], padding=1, groups=g), | |
| RMSNorm2d(prm_embed_dim, eps=norm_eps), | |
| nn.SiLU(inplace=True), | |
| nn.Conv2d(prm_embed_dim, prm_embed_dim, 3, stride=pcm_strides[1], padding=1, groups=g), | |
| RMSNorm2d(prm_embed_dim, eps=norm_eps), | |
| nn.SiLU(inplace=True), | |
| nn.Conv2d(prm_embed_dim, out_dim, 3, stride=pcm_strides[2], padding=1, groups=g), | |
| ) | |
| self.norm_attn = RMSNorm(out_dim, eps=norm_eps) | |
| self.qkv = nn.Linear(out_dim, out_dim * 3, bias=bias) | |
| self.attn_proj = nn.Linear(out_dim, out_dim, bias=bias) | |
| self.norm_ffn = RMSNorm(out_dim, eps=norm_eps) | |
| self.mlp = Mlp(out_dim, out_dim, bias=bias) | |
| def _window_attn_rc(self, y: torch.Tensor, H: int, W: int) -> torch.Tensor: | |
| """Window self-attention on flat tokens [H*W, out_dim]. Returns [H*W, out_dim].""" | |
| ws = self.window_size | |
| y2d = y.view(1, H, W, self.out_dim) | |
| pad_h = (ws - H % ws) % ws | |
| pad_w = (ws - W % ws) % ws | |
| if pad_h + pad_w > 0: | |
| y2d = F.pad(y2d.permute(0, 3, 1, 2), (0, pad_w, 0, pad_h)).permute(0, 2, 3, 1) | |
| Hp, Wp = H + pad_h, W + pad_w | |
| wins = window_partition(y2d, ws) # [nW, ws, ws, out_dim] | |
| nW = wins.shape[0] | |
| wins_flat = wins.view(nW, ws * ws, self.out_dim) | |
| q, k, v = self.qkv(wins_flat).reshape(nW, ws * ws, 3, self.num_heads, self.head_dim).permute(2, 0, 3, 1, 4).unbind(0) | |
| attn = torch.matmul(q, k.transpose(-2, -1)) / math.sqrt(self.head_dim) | |
| attn = F.softmax(attn, dim=-1, dtype=torch.float32).to(q.dtype) | |
| out = torch.matmul(attn, v).transpose(1, 2).reshape(nW, ws * ws, self.out_dim) | |
| out = self.attn_proj(out) | |
| out = window_reverse(out.view(nW, ws, ws, self.out_dim), ws, Hp, Wp) | |
| if pad_h + pad_w > 0: | |
| out = out[:, :H, :W, :].contiguous() | |
| return out.view(H * W, self.out_dim) | |
| def _global_attn_rc(self, y: torch.Tensor, cu_seqlens: torch.Tensor, | |
| attn_impl: str = "sdpa") -> torch.Tensor: | |
| """Global attention over packed tokens using cu_seqlens. Returns same shape.""" | |
| S = y.shape[0] | |
| q, k, v = self.qkv(y).reshape(S, 3, self.num_heads, self.head_dim).permute(1, 0, 2, 3).unbind(0) | |
| if flash_attn_available and attn_impl == "flash_attention_2": | |
| max_sl = (cu_seqlens[1:] - cu_seqlens[:-1]).max().item() | |
| out = flash_attn_varlen_func(q, k, v, cu_seqlens, cu_seqlens, max_sl, max_sl, causal=False) | |
| else: | |
| mask = torch.zeros([1, S, S], device=q.device, dtype=torch.bool) | |
| for i in range(1, len(cu_seqlens)): | |
| mask[..., cu_seqlens[i - 1]:cu_seqlens[i], cu_seqlens[i - 1]:cu_seqlens[i]] = True | |
| qt = q.transpose(0, 1).unsqueeze(0) | |
| kt = k.transpose(0, 1).unsqueeze(0) | |
| vt = v.transpose(0, 1).unsqueeze(0) | |
| if mask.stride(-1) != 1: | |
| mask = torch.empty_like(mask, memory_format=torch.contiguous_format).copy_(mask) | |
| out = F.scaled_dot_product_attention(qt, kt, vt, mask, dropout_p=0.0) | |
| out = out.squeeze(0).transpose(0, 1) | |
| return self.attn_proj(out.reshape(S, self.out_dim)) | |
| def forward(self, x: torch.Tensor, cu_seqlens: torch.Tensor, grid_hw: torch.Tensor): | |
| """ | |
| x: [total_tokens, in_dim], cu_seqlens: [N+1], grid_hw: [N, 2] | |
| Returns: (x_out [total_new, out_dim], new_cu_seqlens [N+1], new_grid_hw [N, 2]) | |
| """ | |
| seqlens = (cu_seqlens[1:] - cu_seqlens[:-1]).tolist() | |
| prm_list, pcm_list, new_hw_list = [], [], [] | |
| token_idx = 0 | |
| for img_idx, sl in enumerate(seqlens): | |
| H, W = int(grid_hw[img_idx, 0]), int(grid_hw[img_idx, 1]) | |
| tok = x[token_idx: token_idx + sl] | |
| x2d = tok.view(1, H, W, self.in_dim).permute(0, 3, 1, 2) # [1, in_dim, H, W] | |
| ys = [conv(x2d) for conv in self.prm_convs] # each [1, prm_embed_dim, H//2, W//2] | |
| Hn, Wn = ys[0].shape[2], ys[0].shape[3] | |
| y = torch.cat(ys, dim=1) # [1, prm_embed_dim*4, Hn, Wn] | |
| y = y.permute(0, 2, 3, 1).reshape(Hn * Wn, self.prm_embed_dim * 4) | |
| prm_list.append(self.prm_proj(y)) # [Hn*Wn, out_dim] | |
| pcm_list.append(self.pcm(x2d).permute(0, 2, 3, 1).reshape(Hn * Wn, self.out_dim)) | |
| new_hw_list.append((Hn, Wn)) | |
| token_idx += sl | |
| prm_out = torch.cat(prm_list, dim=0) | |
| pcm_out = torch.cat(pcm_list, dim=0) | |
| new_lens = torch.tensor([h * w for h, w in new_hw_list], dtype=torch.long, device=x.device) | |
| new_cu_seqlens = F.pad(new_lens.cumsum(0, dtype=torch.int32), (1, 0), value=0) | |
| new_grid_hw = torch.tensor(new_hw_list, dtype=torch.long, device=x.device) | |
| y = prm_out + pcm_out | |
| y_norm = self.norm_attn(y) | |
| if self.window_attn: | |
| attn_outs = [] | |
| idx = 0 | |
| for Hn, Wn in new_hw_list: | |
| sl_n = Hn * Wn | |
| attn_outs.append(self._window_attn_rc(y_norm[idx: idx + sl_n], Hn, Wn)) | |
| idx += sl_n | |
| attn_out = torch.cat(attn_outs, dim=0) | |
| else: | |
| attn_out = self._global_attn_rc(y_norm, new_cu_seqlens, self._attn_impl) | |
| y = y + attn_out | |
| y = y + self.mlp(self.norm_ffn(y)) | |
| return y, new_cu_seqlens, new_grid_hw | |
| class ConvStem(nn.Module): | |
| def __init__(self, num_channels: int = 3, out_dim: int = 64, | |
| patch_size: int = 32, temporal_patch_size: int = 1): | |
| super().__init__() | |
| self.num_channels = num_channels | |
| self.patch_size = patch_size | |
| self.temporal_patch_size = temporal_patch_size | |
| inter = out_dim // 2 | |
| self.conv1 = nn.Sequential( | |
| nn.Conv2d(num_channels, inter, 3, stride=2, padding=1, bias=False), | |
| RMSNorm2d(inter), | |
| nn.GELU(), | |
| ) | |
| self.conv2 = nn.Sequential( | |
| nn.Conv2d(inter, out_dim, 3, stride=2, padding=1, bias=False), | |
| RMSNorm2d(out_dim), | |
| nn.GELU(), | |
| ) | |
| self.proj = nn.Conv2d(out_dim, out_dim, 3, stride=1, padding=1) | |
| self.norm = RMSNorm(out_dim) | |
| def forward(self, pixel_values: torch.Tensor, grid_thw: torch.Tensor): | |
| """ | |
| pixel_values: [total_tiles, C * temporal * ph * pw] — flat from Qwen2VLImageProcessor | |
| OR [total_tiles, C, ph, pw] — already spatial | |
| grid_thw: [N, 3] (t, h_tiles, w_tiles) | |
| Returns: | |
| x: [total_tokens, out_dim] packed | |
| cu_seqlens: [N+1] | |
| grid_hw: [N, 2] (H_feat, W_feat) after 4x downsampling | |
| """ | |
| # Reshape flat pixel_values [N, C*T*ph*pw] → [N, C, ph, pw] | |
| if pixel_values.ndim == 2: | |
| C = self.num_channels | |
| T = self.temporal_patch_size | |
| hw = int(math.sqrt(pixel_values.shape[1] / (C * T))) | |
| pixel_values = pixel_values.view(-1, C, T, hw, hw)[:, :, 0] # [N, C, ph, pw] | |
| N = grid_thw.shape[0] | |
| outputs = [] | |
| hw_list = [] | |
| token_idx = 0 | |
| for img_idx in range(N): | |
| t, h, w = int(grid_thw[img_idx, 0]), int(grid_thw[img_idx, 1]), int(grid_thw[img_idx, 2]) | |
| num_tiles = t * h * w | |
| tiles = pixel_values[token_idx: token_idx + num_tiles] # [h*w, C, ph, pw] | |
| token_idx += num_tiles | |
| ph = tiles.shape[-2] | |
| pw = tiles.shape[-1] | |
| img = tiles.view(h, w, -1, ph, pw).permute(2, 0, 3, 1, 4).reshape(-1, h * ph, w * pw).unsqueeze(0) | |
| out = self.conv1(img) # [1, inter, H/2, W/2] | |
| out = self.conv2(out) # [1, dim, H/4, W/4] | |
| out = self.proj(out) # [1, dim, H/4, W/4] | |
| Hn, Wn = out.shape[2], out.shape[3] | |
| out = out.squeeze(0).permute(1, 2, 0).reshape(Hn * Wn, -1) # [Hn*Wn, dim] | |
| out = self.norm(out) | |
| outputs.append(out) | |
| hw_list.append((Hn, Wn)) | |
| x = torch.cat(outputs, dim=0) | |
| grid_hw = torch.tensor(hw_list, dtype=torch.long, device=x.device) | |
| lens = torch.tensor([h * w for h, w in hw_list], dtype=torch.long, device=x.device) | |
| cu_seqlens = F.pad(lens.cumsum(0, dtype=torch.int32), (1, 0), value=0) | |
| return x, cu_seqlens, grid_hw | |
| class StemReductionCell(nn.Module): | |
| """ | |
| First-stage reduction cell replacing ConvStem: reads raw pixel tiles and applies | |
| 4x PRM (kernel=7, dilations=[1,2,3,4], stride=4) + PCM + | |
| window-attention block. Matches the official ViTAEv2-S RC0 (downsample_ratio=4). | |
| Forward interface is identical to the old ConvStem: | |
| forward(pixel_values, grid_thw) -> (packed_tokens, cu_seqlens, grid_hw) | |
| """ | |
| def __init__(self, num_channels: int = 3, out_dim: int = 64, num_heads: int = 1, | |
| window_size: int = 7, prm_embed_dim: int = 64, | |
| downsample_ratio: int = 4, kernel_size: int = 7, pcm_group: int = 1, | |
| patch_size: int = 32, temporal_patch_size: int = 1, | |
| norm_eps: float = 1e-6, bias: bool = False, attn_impl: str = "sdpa"): | |
| super().__init__() | |
| self.num_channels = num_channels | |
| self.patch_size = patch_size | |
| self.temporal_patch_size = temporal_patch_size | |
| self.out_dim = out_dim | |
| self.num_heads = num_heads | |
| self.head_dim = out_dim // num_heads | |
| self.window_size = window_size | |
| self.prm_embed_dim = prm_embed_dim | |
| self.downsample_ratio = downsample_ratio | |
| self.kernel_size = kernel_size | |
| self._attn_impl = attn_impl | |
| in_dim = num_channels | |
| dilations = [1, 2, 3, 4] | |
| self.prm_convs = nn.ModuleList() | |
| for d in dilations: | |
| pad = math.ceil(((kernel_size - 1) * d + 1 - downsample_ratio) / 2) | |
| self.prm_convs.append(nn.Sequential( | |
| nn.Conv2d(in_dim, prm_embed_dim, kernel_size=kernel_size, | |
| stride=downsample_ratio, padding=pad, dilation=d), | |
| nn.GELU(), | |
| )) | |
| self.prm_proj = nn.Linear(prm_embed_dim * 4, out_dim, bias=bias) | |
| pcm_strides = [] | |
| residual = downsample_ratio // 2 | |
| for _ in range(3): | |
| pcm_strides.append((residual > 0) + 1) | |
| residual //= 2 | |
| assert residual == 0 | |
| inter = prm_embed_dim | |
| self.pcm = nn.Sequential( | |
| nn.Conv2d(in_dim, inter, 3, stride=pcm_strides[0], padding=1, groups=pcm_group), | |
| RMSNorm2d(inter, eps=norm_eps), | |
| nn.SiLU(inplace=True), | |
| nn.Conv2d(inter, inter, 3, stride=pcm_strides[1], padding=1, groups=pcm_group), | |
| RMSNorm2d(inter, eps=norm_eps), | |
| nn.SiLU(inplace=True), | |
| nn.Conv2d(inter, out_dim, 3, stride=pcm_strides[2], padding=1, groups=pcm_group), | |
| ) | |
| self.norm_attn = RMSNorm(out_dim, eps=norm_eps) | |
| self.qkv = nn.Linear(out_dim, out_dim * 3, bias=bias) | |
| self.attn_proj = nn.Linear(out_dim, out_dim, bias=bias) | |
| self.norm_ffn = RMSNorm(out_dim, eps=norm_eps) | |
| self.mlp = Mlp(out_dim, out_dim, bias=bias) | |
| def _window_attn(self, y: torch.Tensor, H: int, W: int) -> torch.Tensor: | |
| """Window self-attention on flat tokens [H*W, out_dim].""" | |
| ws = self.window_size | |
| y2d = y.view(1, H, W, self.out_dim) | |
| pad_h = (ws - H % ws) % ws | |
| pad_w = (ws - W % ws) % ws | |
| if pad_h + pad_w > 0: | |
| y2d = F.pad(y2d.permute(0, 3, 1, 2), (0, pad_w, 0, pad_h)).permute(0, 2, 3, 1) | |
| Hp, Wp = H + pad_h, W + pad_w | |
| wins = window_partition(y2d, ws) # [nW, ws, ws, out_dim] | |
| nW = wins.shape[0] | |
| wins_flat = wins.view(nW, ws * ws, self.out_dim) | |
| q, k, v = (self.qkv(wins_flat) | |
| .reshape(nW, ws * ws, 3, self.num_heads, self.head_dim) | |
| .permute(2, 0, 3, 1, 4).unbind(0)) | |
| attn = torch.matmul(q, k.transpose(-2, -1)) / math.sqrt(self.head_dim) | |
| attn = F.softmax(attn, dim=-1, dtype=torch.float32).to(q.dtype) | |
| out = torch.matmul(attn, v).transpose(1, 2).reshape(nW, ws * ws, self.out_dim) | |
| out = self.attn_proj(out) | |
| out = window_reverse(out.view(nW, ws, ws, self.out_dim), ws, Hp, Wp) | |
| if pad_h + pad_w > 0: | |
| out = out[:, :H, :W, :].contiguous() | |
| return out.view(H * W, self.out_dim) | |
| def forward(self, pixel_values: torch.Tensor, grid_thw: torch.Tensor): | |
| """ | |
| pixel_values : [total_tiles, C, ph, pw] or [total_tiles, C*T*ph*pw] | |
| grid_thw : [N, 3] (t, h_tiles, w_tiles) | |
| Returns : (x [total_tokens, out_dim], cu_seqlens [N+1], grid_hw [N, 2]) | |
| """ | |
| # Reshape flat tiles if preprocessor returned [N, C*T*ph*pw] | |
| if pixel_values.ndim == 2: | |
| C = self.num_channels | |
| T = self.temporal_patch_size | |
| hw_px = int(math.sqrt(pixel_values.shape[1] / (C * T))) | |
| pixel_values = pixel_values.view(-1, C, T, hw_px, hw_px)[:, :, 0] | |
| N = grid_thw.shape[0] | |
| prm_list, pcm_list, hw_list = [], [], [] | |
| tile_idx = 0 | |
| for img_idx in range(N): | |
| t, h, w = (int(grid_thw[img_idx, 0]), | |
| int(grid_thw[img_idx, 1]), | |
| int(grid_thw[img_idx, 2])) | |
| num_tiles = t * h * w | |
| tiles = pixel_values[tile_idx: tile_idx + num_tiles] # [h*w, C, ph, pw] | |
| tile_idx += num_tiles | |
| ph, pw = tiles.shape[-2], tiles.shape[-1] | |
| img = (tiles.view(h, w, -1, ph, pw) | |
| .permute(2, 0, 3, 1, 4) | |
| .reshape(-1, h * ph, w * pw) | |
| .unsqueeze(0)) | |
| ys = [conv(img) for conv in self.prm_convs] | |
| Hn, Wn = ys[0].shape[2], ys[0].shape[3] | |
| y_cat = torch.cat(ys, dim=1) # [1, emb*4, Hn, Wn] | |
| y_flat = y_cat.permute(0, 2, 3, 1).reshape(Hn * Wn, self.prm_embed_dim * 4) | |
| prm_list.append(self.prm_proj(y_flat)) # [Hn*Wn, out_dim] | |
| pcm_list.append( | |
| self.pcm(img).permute(0, 2, 3, 1).reshape(Hn * Wn, self.out_dim) | |
| ) | |
| hw_list.append((Hn, Wn)) | |
| prm_out = torch.cat(prm_list, dim=0) # [total_tokens, out_dim] | |
| pcm_out = torch.cat(pcm_list, dim=0) | |
| new_lens = torch.tensor([h * w for h, w in hw_list], | |
| dtype=torch.long, device=prm_out.device) | |
| cu_seqlens = F.pad(new_lens.cumsum(0, dtype=torch.int32), (1, 0), value=0) | |
| grid_hw = torch.tensor(hw_list, dtype=torch.long, device=prm_out.device) | |
| x = prm_out + pcm_out | |
| x_norm = self.norm_attn(x) | |
| attn_outs, idx = [], 0 | |
| for Hn, Wn in hw_list: | |
| sl = Hn * Wn | |
| attn_outs.append(self._window_attn(x_norm[idx: idx + sl], Hn, Wn)) | |
| idx += sl | |
| x = x + torch.cat(attn_outs, dim=0) | |
| x = x + self.mlp(self.norm_ffn(x)) | |
| return x, cu_seqlens, grid_hw | |
| # --------------------------------------------------------------------------- | |
| # 2D-RoPE for global attention | |
| # --------------------------------------------------------------------------- | |
| def compute_2d_rope(grid_hw: torch.Tensor, rotary_emb: VisionRotaryEmbedding): | |
| emb_device = rotary_emb.inv_freq.device | |
| pos_ids_list = [] | |
| for h, w in grid_hw.tolist(): | |
| h, w = int(h), int(w) | |
| hpos = torch.arange(h, device=emb_device).unsqueeze(1).expand(h, w).flatten() | |
| wpos = torch.arange(w, device=emb_device).unsqueeze(0).expand(h, w).flatten() | |
| pos_ids_list.append(torch.stack([hpos, wpos], dim=-1)) | |
| pos_ids = torch.cat(pos_ids_list, dim=0) | |
| max_size = grid_hw.max().item() | |
| full_emb = rotary_emb(int(max_size)) | |
| emb = full_emb[pos_ids] | |
| return torch.stack([emb[:, 0], emb[:, 1]], dim=2).reshape(emb.shape[0], -1) | |
| # --------------------------------------------------------------------------- | |
| # PatchMerger | |
| # --------------------------------------------------------------------------- | |
| class PatchMerger(nn.Module): | |
| def __init__(self, dim: int, context_dim: int, init_std: float = 0.02): | |
| super().__init__() | |
| self.norm = RMSNorm(context_dim) | |
| self.mlp = nn.Sequential( | |
| nn.Linear(context_dim, context_dim), | |
| nn.GELU(), | |
| nn.Linear(context_dim, dim), | |
| ) | |
| nn.init.normal_(self.mlp[0].weight, std=init_std) | |
| nn.init.zeros_(self.mlp[0].bias) | |
| nn.init.normal_(self.mlp[2].weight, std=init_std) | |
| nn.init.zeros_(self.mlp[2].bias) | |
| def forward(self, x: torch.Tensor) -> torch.Tensor: | |
| return self.mlp(self.norm(x)) | |
| # --------------------------------------------------------------------------- | |
| # Main ViTAEv2 Vision Transformer | |
| # --------------------------------------------------------------------------- | |
| class MonkeyOCRv2ViTAEVisionTransformer(PreTrainedModel): | |
| _supports_flash_attn = True | |
| _supports_sdpa = True | |
| _no_split_modules = ["StemReductionCell", "ReductionCell", "NormalCell2DRoPE"] | |
| config_class = MonkeyOCRv2ViTAEEncoderConfig | |
| def __init__(self, config: MonkeyOCRv2ViTAEEncoderConfig) -> None: | |
| super().__init__(config) | |
| self.config = config | |
| sd = config.stage_dims # [64, 128, 256, 512] (ViTAEv2-S) | |
| sdp = config.stage_depths # [2, 2, 8, 2] | |
| sh = config.stage_heads # [1, 2, 4, 8] (head_dim=64 at all stages) | |
| dsr = getattr(config, 'downsample_ratios', [4, 2, 2, 2]) | |
| ksz = getattr(config, 'kernel_sizes', [7, 3, 3, 3]) | |
| rct = getattr(config, 'rc_tokens_type', ["window", "window", "transformer", "transformer"]) | |
| nct = getattr(config, 'nc_tokens_type', ["window", "window", "transformer", "transformer"]) | |
| ncg = getattr(config, 'nc_groups', [1, 32, 64, 128]) | |
| rcg = list(getattr(config, 'rc_groups', [1, 16, 32, 64])) | |
| rcpem = list(getattr(config, 'rc_embed_dims', [64, 64, 128, 256])) | |
| rch = list(getattr(config, 'rc_heads', [1, 1, 2, 4])) | |
| if len(rcg) == 3: | |
| rcg = [1] + rcg | |
| if len(rch) == 3: | |
| rch = [1] + rch | |
| if len(rcpem) == 3: | |
| rcpem = [getattr(config, 'prm_embed_dim', 64)] + rcpem | |
| ws = config.window_size | |
| mr = config.mlp_ratio | |
| bias = config.use_bias | |
| eps = config.rms_norm_eps | |
| attn_impl = config.attn_implementation | |
| is_causal = config.is_causal | |
| self.stem = StemReductionCell( | |
| num_channels=config.num_channels, | |
| out_dim=sd[0], | |
| num_heads=rch[0], | |
| window_size=ws, | |
| prm_embed_dim=rcpem[0], | |
| downsample_ratio=dsr[0], | |
| kernel_size=ksz[0], | |
| pcm_group=rcg[0], | |
| patch_size=config.patch_size, | |
| temporal_patch_size=config.temporal_patch_size, | |
| norm_eps=eps, | |
| bias=bias, | |
| attn_impl=attn_impl, | |
| ) | |
| self.stage1 = nn.ModuleList([ | |
| NormalCell2DRoPE(sd[0], sh[0], mr, ws, nct[0] == "window", bias, eps, attn_impl, is_causal, ncg[0]) | |
| for _ in range(sdp[0])]) | |
| self.rc1 = ReductionCell(sd[0], sd[1], num_heads=rch[1], window_attn=rct[1] == "window", window_size=ws, pcm_group=rcg[1], prm_embed_dim=rcpem[1], downsample_ratio=dsr[1], kernel_size=ksz[1], norm_eps=eps, bias=bias, attn_impl=attn_impl) | |
| self.stage2 = nn.ModuleList([ | |
| NormalCell2DRoPE(sd[1], sh[1], mr, ws, nct[1] == "window", bias, eps, attn_impl, is_causal, ncg[1]) | |
| for _ in range(sdp[1])]) | |
| self.rc2 = ReductionCell(sd[1], sd[2], num_heads=rch[2], window_attn=rct[2] == "window", window_size=ws, pcm_group=rcg[2], prm_embed_dim=rcpem[2], downsample_ratio=dsr[2], kernel_size=ksz[2], norm_eps=eps, bias=bias, attn_impl=attn_impl) | |
| head_dim_s3 = sd[2] // sh[2] | |
| self.rotary_pos_emb_s3 = VisionRotaryEmbedding(head_dim_s3 // 2) | |
| self.stage3 = nn.ModuleList([ | |
| NormalCell2DRoPE(sd[2], sh[2], mr, ws, nct[2] == "window", bias, eps, attn_impl, is_causal, ncg[2]) | |
| for _ in range(sdp[2])]) | |
| self.rc3 = ReductionCell(sd[2], sd[3], num_heads=rch[3], window_attn=rct[3] == "window", window_size=ws, pcm_group=rcg[3], prm_embed_dim=rcpem[3], downsample_ratio=dsr[3], kernel_size=ksz[3], norm_eps=eps, bias=bias, attn_impl=attn_impl) | |
| head_dim_s4 = sd[3] // sh[3] | |
| self.rotary_pos_emb = VisionRotaryEmbedding(head_dim_s4 // 2) | |
| self.stage4 = nn.ModuleList([ | |
| NormalCell2DRoPE(sd[3], sh[3], mr, ws, nct[3] == "window", bias, eps, attn_impl, is_causal, ncg[3]) | |
| for _ in range(sdp[3])]) | |
| if config.post_norm: | |
| self.post_trunk_norm = RMSNorm(sd[3], eps=eps) | |
| self.gradient_checkpointing = False | |
| self._gradient_checkpointing_func = torch.utils.checkpoint.checkpoint | |
| self.apply(self._init_weights) | |
| def _init_weights(self, module): | |
| std = self.config.initializer_range | |
| if isinstance(module, nn.Linear): | |
| module.weight.data.normal_(mean=0.0, std=std) | |
| if module.bias is not None: | |
| module.bias.data.zero_() | |
| elif isinstance(module, nn.Conv2d): | |
| module.weight.data.normal_(mean=0.0, std=std) | |
| if module.bias is not None: | |
| module.bias.data.zero_() | |
| elif isinstance(module, (RMSNorm, RMSNorm2d)): | |
| module.weight.data.fill_(1.0) | |
| if hasattr(module, "bias") and module.bias is not None: | |
| module.bias.data.zero_() | |
| def dtype(self): | |
| return self.stem.prm_proj.weight.dtype | |
| def device(self): | |
| return self.stem.prm_proj.weight.device | |
| def _run_stage(self, cells, x, cu_seqlens, rotary_pos_emb, grid_hw): | |
| for cell in cells: | |
| if self.gradient_checkpointing and self.training: | |
| x = self._gradient_checkpointing_func(cell.__call__, x, cu_seqlens, rotary_pos_emb, grid_hw) | |
| else: | |
| x = cell(x, cu_seqlens, rotary_pos_emb, grid_hw) | |
| return x | |
| def forward(self, pixel_values: torch.Tensor, grid_thw: torch.Tensor) -> tuple: | |
| pixel_values = pixel_values.to(dtype=self.dtype) | |
| all_hidden_states =[] | |
| all_grid_hw = [] | |
| # --- Stage 1 (1/4) --- | |
| x, cu_seqlens, grid_hw = self.stem(pixel_values, grid_thw) | |
| x = self._run_stage(self.stage1, x, cu_seqlens, None, grid_hw) | |
| all_hidden_states.append(x.clone()) | |
| all_grid_hw.append(grid_hw.clone()) | |
| # --- Stage 2 (1/8) --- | |
| x, cu_seqlens, grid_hw = self.rc1(x, cu_seqlens, grid_hw) | |
| x = self._run_stage(self.stage2, x, cu_seqlens, None, grid_hw) | |
| all_hidden_states.append(x.clone()) | |
| all_grid_hw.append(grid_hw.clone()) | |
| # --- Stage 3 (1/16) → global attention + 2D-RoPE --- | |
| x, cu_seqlens, grid_hw = self.rc2(x, cu_seqlens, grid_hw) | |
| rotary_pos_emb_s3 = compute_2d_rope(grid_hw, self.rotary_pos_emb_s3) | |
| x = self._run_stage(self.stage3, x, cu_seqlens, rotary_pos_emb_s3, grid_hw) | |
| all_hidden_states.append(x.clone()) | |
| all_grid_hw.append(grid_hw.clone()) | |
| # --- Stage 4 (1/32) → global attention --- | |
| x, cu_seqlens, grid_hw = self.rc3(x, cu_seqlens, grid_hw) | |
| rotary_pos_emb = compute_2d_rope(grid_hw, self.rotary_pos_emb) | |
| x = self._run_stage(self.stage4, x, cu_seqlens, rotary_pos_emb, grid_hw) | |
| if self.config.post_norm: | |
| x = self.post_trunk_norm(x) | |
| all_hidden_states.append(x) | |
| all_grid_hw.append(grid_hw) | |
| return all_hidden_states, all_grid_hw | |