| |
|
|
| """ |
| ViTDet backbone adapted from Detectron2. |
| This module implements Vision Transformer (ViT) backbone for object detection. |
| |
| Rope embedding code adopted from: |
| 1. https://github.com/meta-llama/codellama/blob/main/llama/model.py |
| 2. https://github.com/naver-ai/rope-vit |
| 3. https://github.com/lucidrains/rotary-embedding-torch |
| """ |
|
|
| import math |
| from functools import partial |
| from typing import Callable, List, Optional, Tuple, Union |
|
|
| import torch |
| import torch.nn as nn |
| import torch.nn.functional as F |
| import torch.utils.checkpoint as checkpoint |
|
|
| try: |
| from timm.layers import DropPath, Mlp, trunc_normal_ |
| except ModuleNotFoundError: |
| |
| from timm.models.layers import DropPath, Mlp, trunc_normal_ |
| from torch import Tensor |
|
|
| from .model_misc import LayerScale |
|
|
|
|
| def init_t_xy( |
| end_x: int, end_y: int, scale: float = 1.0, offset: int = 0 |
| ) -> Tuple[torch.Tensor, torch.Tensor]: |
| t = torch.arange(end_x * end_y, dtype=torch.float32) |
| t_x = (t % end_x).float() |
| t_y = torch.div(t, end_x, rounding_mode="floor").float() |
| return t_x * scale + offset, t_y * scale + offset |
|
|
|
|
| def compute_axial_cis( |
| dim: int, |
| end_x: int, |
| end_y: int, |
| theta: float = 10000.0, |
| scale_pos: float = 1.0, |
| offset: int = 0, |
| ) -> torch.Tensor: |
| freqs_x = 1.0 / (theta ** (torch.arange(0, dim, 4)[: (dim // 4)].float() / dim)) |
| freqs_y = 1.0 / (theta ** (torch.arange(0, dim, 4)[: (dim // 4)].float() / dim)) |
|
|
| t_x, t_y = init_t_xy(end_x, end_y, scale_pos, offset) |
| freqs_x = torch.outer(t_x, freqs_x) |
| freqs_y = torch.outer(t_y, freqs_y) |
| freqs_cis_x = torch.polar(torch.ones_like(freqs_x), freqs_x) |
| freqs_cis_y = torch.polar(torch.ones_like(freqs_y), freqs_y) |
| return torch.cat([freqs_cis_x, freqs_cis_y], dim=-1) |
|
|
|
|
| def reshape_for_broadcast(freqs_cis: torch.Tensor, x: torch.Tensor) -> torch.Tensor: |
| ndim = x.ndim |
| assert 0 <= 1 < ndim |
| assert freqs_cis.shape == (x.shape[-2], x.shape[-1]) |
| shape = [d if i >= ndim - 2 else 1 for i, d in enumerate(x.shape)] |
| return freqs_cis.view(*shape) |
|
|
|
|
| def apply_rotary_enc( |
| xq: torch.Tensor, |
| xk: torch.Tensor, |
| freqs_cis: torch.Tensor, |
| repeat_freqs_k: bool = False, |
| ) -> Tuple[torch.Tensor, torch.Tensor]: |
| xq_ = torch.view_as_complex(xq.float().reshape(*xq.shape[:-1], -1, 2)) |
| xk_ = ( |
| torch.view_as_complex(xk.float().reshape(*xk.shape[:-1], -1, 2)) |
| if xk.shape[-2] != 0 |
| else None |
| ) |
| freqs_cis = reshape_for_broadcast(freqs_cis, xq_) |
| xq_out = torch.view_as_real(xq_ * freqs_cis).flatten(3) |
| if xk_ is None: |
| |
| return xq_out.type_as(xq).to(xq.device), xk |
| |
| if repeat_freqs_k: |
| r = xk_.shape[-2] // xq_.shape[-2] |
| freqs_cis = freqs_cis.repeat(*([1] * (freqs_cis.ndim - 2)), r, 1) |
| xk_out = torch.view_as_real(xk_ * freqs_cis).flatten(3) |
| return xq_out.type_as(xq).to(xq.device), xk_out.type_as(xk).to(xk.device) |
|
|
|
|
| def window_partition(x: Tensor, window_size: int) -> Tuple[Tensor, Tuple[int, int]]: |
| """ |
| Partition into non-overlapping windows with padding if needed. |
| Args: |
| x (tensor): input tokens with [B, H, W, C]. |
| window_size (int): window size. |
| Returns: |
| windows: windows after partition with [B * num_windows, window_size, window_size, C]. |
| (Hp, Wp): padded height and width before partition |
| """ |
| B, H, W, C = x.shape |
|
|
| pad_h = (window_size - H % window_size) % window_size |
| pad_w = (window_size - W % window_size) % window_size |
| if pad_h > 0 or pad_w > 0: |
| x = F.pad(x, (0, 0, 0, pad_w, 0, pad_h)) |
| Hp, Wp = H + pad_h, W + pad_w |
|
|
| x = x.view(B, Hp // window_size, window_size, Wp // window_size, window_size, C) |
| windows = x.permute(0, 1, 3, 2, 4, 5).reshape(-1, window_size, window_size, C) |
| return windows, (Hp, Wp) |
|
|
|
|
| def window_unpartition( |
| windows: Tensor, window_size: int, pad_hw: Tuple[int, int], hw: Tuple[int, int] |
| ) -> Tensor: |
| """ |
| Window unpartition into original sequences and removing padding. |
| Args: |
| x (tensor): input tokens with [B * num_windows, window_size, window_size, C]. |
| window_size (int): window size. |
| pad_hw (Tuple): padded height and width (Hp, Wp). |
| hw (Tuple): original height and width (H, W) before padding. |
| Returns: |
| x: unpartitioned sequences with [B, H, W, C]. |
| """ |
| Hp, Wp = pad_hw |
| H, W = hw |
| B = windows.shape[0] // (Hp * Wp // window_size // window_size) |
| x = windows.reshape( |
| B, Hp // window_size, Wp // window_size, window_size, window_size, -1 |
| ) |
| x = x.permute(0, 1, 3, 2, 4, 5).reshape(B, Hp, Wp, -1) |
|
|
| if Hp > H or Wp > W: |
| x = x[:, :H, :W, :] |
| return x |
|
|
|
|
| def get_rel_pos(q_size: int, k_size: int, rel_pos: Tensor) -> Tensor: |
| """ |
| Get relative positional embeddings according to the relative positions of |
| query and key sizes. |
| Args: |
| q_size (int): size of query q. |
| k_size (int): size of key k. |
| rel_pos (Tensor): relative position embeddings (L, C). |
| Returns: |
| Extracted positional embeddings according to relative positions. |
| """ |
| max_rel_dist = int(2 * max(q_size, k_size) - 1) |
| |
| if rel_pos.shape[0] != max_rel_dist: |
| |
| rel_pos_resized = F.interpolate( |
| rel_pos.reshape(1, rel_pos.shape[0], -1).permute(0, 2, 1), |
| size=max_rel_dist, |
| mode="linear", |
| align_corners=False, |
| ) |
| rel_pos_resized = rel_pos_resized.reshape(-1, max_rel_dist).permute(1, 0) |
| else: |
| rel_pos_resized = rel_pos |
|
|
| |
| q_coords = torch.arange(q_size)[:, None] * max(k_size / q_size, 1.0) |
| k_coords = torch.arange(k_size)[None, :] * max(q_size / k_size, 1.0) |
| relative_coords = (q_coords - k_coords) + (k_size - 1) * max(q_size / k_size, 1.0) |
|
|
| return rel_pos_resized[relative_coords.long()] |
|
|
|
|
| def get_abs_pos( |
| abs_pos: Tensor, |
| has_cls_token: bool, |
| hw: Tuple[int, int], |
| retain_cls_token: bool = False, |
| tiling: bool = False, |
| ) -> Tensor: |
| """ |
| Calculate absolute positional embeddings. If needed, resize embeddings and remove cls_token |
| dimension for the original embeddings. |
| Args: |
| abs_pos (Tensor): absolute positional embeddings with (1, num_position, C). |
| has_cls_token (bool): If true, has 1 embedding in abs_pos for cls token. |
| hw (Tuple): size of input image tokens. |
| retain_cls_token: whether to retain the cls_token |
| tiling: whether to tile the embeddings, *instead* of interpolation (a la abs_win) |
| Returns: |
| Absolute positional embeddings after processing with shape (1, H, W, C), |
| if retain_cls_token is False, otherwise (1, 1+H*W, C) |
| """ |
| if retain_cls_token: |
| assert has_cls_token |
|
|
| h, w = hw |
| if has_cls_token: |
| cls_pos = abs_pos[:, :1] |
| abs_pos = abs_pos[:, 1:] |
|
|
| xy_num = abs_pos.shape[1] |
| size = int(math.sqrt(xy_num)) |
| assert size * size == xy_num |
|
|
| if size != h or size != w: |
| new_abs_pos = abs_pos.reshape(1, size, size, -1).permute(0, 3, 1, 2) |
| if tiling: |
| new_abs_pos = new_abs_pos.tile( |
| [1, 1] + [x // y + 1 for x, y in zip((h, w), new_abs_pos.shape[2:])] |
| )[:, :, :h, :w] |
| else: |
| new_abs_pos = F.interpolate( |
| new_abs_pos, |
| size=(h, w), |
| mode="bicubic", |
| align_corners=False, |
| ) |
|
|
| if not retain_cls_token: |
| return new_abs_pos.permute(0, 2, 3, 1) |
| else: |
| |
| assert has_cls_token |
| return torch.cat( |
| [cls_pos, new_abs_pos.permute(0, 2, 3, 1).reshape(1, h * w, -1)], |
| dim=1, |
| ) |
|
|
| else: |
| if not retain_cls_token: |
| return abs_pos.reshape(1, h, w, -1) |
| else: |
| assert has_cls_token |
| return torch.cat([cls_pos, abs_pos], dim=1) |
|
|
|
|
| def concat_rel_pos( |
| q: Tensor, |
| k: Tensor, |
| q_hw: Tuple[int, int], |
| k_hw: Tuple[int, int], |
| rel_pos_h: Tensor, |
| rel_pos_w: Tensor, |
| rescale: bool = False, |
| relative_coords: Optional[Tensor] = None, |
| ) -> Tuple[Tensor, Tensor]: |
| """ |
| Concatenate rel pos coeffs to the q & k tensors, so that qk^T is now |
| effectively including rel pos biases. |
| Args: |
| q (Tensor): q tensor with shape (B, L_q, C). |
| k (Tensor): k tensor with shape (B, L_k, C). |
| q_hw, k_hw: These are spatial size of q & k tensors. |
| rel_pos_h, rel_pos_w: These are relative pos embeddings/params of height, width. |
| rescale (bool): whether to rescale. e.g. for use when using sdpa, pytorch will |
| scale by the wrong factor due to the concat. |
| Returns: |
| q, k: But, padded so that qk^T accounts for rel pos biases |
| """ |
| q_h, q_w = q_hw |
| k_h, k_w = k_hw |
|
|
| assert (q_h == q_w) and (k_h == k_w), "only square inputs supported" |
|
|
| if relative_coords is not None: |
| Rh = rel_pos_h[relative_coords] |
| Rw = rel_pos_w[relative_coords] |
| else: |
| Rh = get_rel_pos(q_h, k_h, rel_pos_h) |
| Rw = get_rel_pos(q_w, k_w, rel_pos_w) |
|
|
| B, _, dim = q.shape |
| r_q = q.reshape(B, q_h, q_w, dim) |
|
|
| old_scale = dim**0.5 |
| new_scale = (dim + k_h + k_w) ** 0.5 if rescale else old_scale |
| |
| scale_ratio = new_scale / old_scale |
|
|
| rel_h = torch.einsum("bhwc,hkc->bhwk", r_q, Rh) * new_scale |
| rel_w = torch.einsum("bhwc,wkc->bhwk", r_q, Rw) * new_scale |
|
|
| eye_h = torch.eye(k_h, dtype=q.dtype, device=q.device) |
| eye_w = torch.eye(k_w, dtype=q.dtype, device=q.device) |
|
|
| eye_h = eye_h.view(1, k_h, 1, k_h).expand([B, k_h, k_w, k_h]) |
| eye_w = eye_w.view(1, 1, k_w, k_w).expand([B, k_h, k_w, k_w]) |
|
|
| q = torch.cat([r_q * scale_ratio, rel_h, rel_w], dim=-1).view(B, q_h * q_w, -1) |
| k = torch.cat([k.view(B, k_h, k_w, -1), eye_h, eye_w], dim=-1).view( |
| B, k_h * k_w, -1 |
| ) |
|
|
| return q, k |
|
|
|
|
| class PatchEmbed(nn.Module): |
| """ |
| Image to Patch Embedding. |
| """ |
|
|
| def __init__( |
| self, |
| kernel_size: Tuple[int, int] = (16, 16), |
| stride: Tuple[int, int] = (16, 16), |
| padding: Tuple[int, int] = (0, 0), |
| in_chans: int = 3, |
| embed_dim: int = 768, |
| bias: bool = True, |
| ): |
| """ |
| Args: |
| kernel_size (Tuple): kernel size of the projection layer. |
| stride (Tuple): stride of the projection layer. |
| padding (Tuple): padding size of the projection layer. |
| in_chans (int): Number of input image channels. |
| embed_dim (int): embed_dim (int): Patch embedding dimension. |
| """ |
| super().__init__() |
|
|
| self.proj = nn.Conv2d( |
| in_chans, |
| embed_dim, |
| kernel_size=kernel_size, |
| stride=stride, |
| padding=padding, |
| bias=bias, |
| ) |
|
|
| def forward(self, x: Tensor) -> Tensor: |
| x = self.proj(x) |
| |
| x = x.permute(0, 2, 3, 1) |
| return x |
|
|
|
|
| class Attention(nn.Module): |
| """Multi-head Attention block with relative position embeddings and 2d-rope.""" |
|
|
| def __init__( |
| self, |
| dim: int, |
| num_heads: int = 8, |
| qkv_bias: bool = True, |
| use_rel_pos: bool = False, |
| rel_pos_zero_init: bool = True, |
| input_size: Optional[Tuple[int, int]] = None, |
| cls_token: bool = False, |
| use_rope: bool = False, |
| rope_theta: float = 10000.0, |
| rope_pt_size: Optional[Tuple[int, int]] = None, |
| rope_interp: bool = False, |
| ): |
| """ |
| Args: |
| dim (int): Number of input channels. |
| num_heads (int): Number of attention heads. |
| qkv_bias (bool: If True, add a learnable bias to query, key, value. |
| rel_pos (bool): If True, add relative positional embeddings to the attention map. |
| rel_pos_zero_init (bool): If True, zero initialize relative positional parameters. |
| input_size (int or None): Input resolution for calculating the relative positional |
| parameter size or rope size. |
| attn_type: Type of attention operation, e.g. "vanilla", "vanilla-xformer". |
| cls_token: whether a cls_token is present. |
| use_rope: whether to use rope 2d (indep of use_rel_pos, as it can be used together) |
| rope_theta: control frequencies of rope |
| rope_pt_size: size of rope in previous stage of training, needed for interpolation or tiling |
| rope_interp: whether to interpolate (or extrapolate) rope to match input size |
| """ |
| super().__init__() |
| self.num_heads = num_heads |
| self.head_dim = dim // num_heads |
| self.scale = self.head_dim**-0.5 |
| self.cls_token = cls_token |
|
|
| self.qkv = nn.Linear(dim, dim * 3, bias=qkv_bias) |
| self.proj = nn.Linear(dim, dim) |
|
|
| |
| self.use_rel_pos = use_rel_pos |
| self.input_size = input_size |
|
|
| self.use_rope = use_rope |
| self.rope_theta = rope_theta |
| self.rope_pt_size = rope_pt_size |
| self.rope_interp = rope_interp |
|
|
| |
| self._setup_rel_pos(rel_pos_zero_init) |
| self._setup_rope_freqs() |
|
|
| def _setup_rel_pos(self, rel_pos_zero_init: bool = True) -> None: |
| if not self.use_rel_pos: |
| self.rel_pos_h = None |
| self.rel_pos_w = None |
| return |
|
|
| assert self.input_size is not None |
| assert self.cls_token is False, "not supported" |
| |
| self.rel_pos_h = nn.Parameter( |
| torch.zeros(2 * self.input_size[0] - 1, self.head_dim) |
| ) |
| self.rel_pos_w = nn.Parameter( |
| torch.zeros(2 * self.input_size[1] - 1, self.head_dim) |
| ) |
|
|
| if not rel_pos_zero_init: |
| trunc_normal_(self.rel_pos_h, std=0.02) |
| trunc_normal_(self.rel_pos_w, std=0.02) |
|
|
| |
| H, W = self.input_size |
| q_coords = torch.arange(H)[:, None] |
| k_coords = torch.arange(W)[None, :] |
| relative_coords = (q_coords - k_coords) + (H - 1) |
| self.register_buffer("relative_coords", relative_coords.long()) |
|
|
| def _setup_rope_freqs(self) -> None: |
| if not self.use_rope: |
| self.freqs_cis = None |
| return |
|
|
| assert self.input_size is not None |
| |
| if self.rope_pt_size is None: |
| self.rope_pt_size = self.input_size |
|
|
| |
| self.compute_cis = partial( |
| compute_axial_cis, |
| dim=self.head_dim, |
| theta=self.rope_theta, |
| ) |
|
|
| |
| scale_pos = 1.0 |
| if self.rope_interp: |
| scale_pos = self.rope_pt_size[0] / self.input_size[0] |
| |
| freqs_cis = self.compute_cis( |
| end_x=self.input_size[0], |
| end_y=self.input_size[1], |
| scale_pos=scale_pos, |
| ) |
| if self.cls_token: |
| t = torch.zeros( |
| self.head_dim // 2, |
| dtype=torch.float32, |
| device=freqs_cis.device, |
| ) |
| cls_freqs_cis = torch.polar(torch.ones_like(t), t)[None, :] |
| freqs_cis = torch.cat([cls_freqs_cis, freqs_cis], dim=0) |
|
|
| self.register_buffer("freqs_cis", freqs_cis) |
|
|
| def _apply_rope(self, q, k) -> Tuple[Tensor, Tensor]: |
| if not self.use_rope: |
| return q, k |
|
|
| assert self.freqs_cis is not None |
| return apply_rotary_enc(q, k, freqs_cis=self.freqs_cis) |
|
|
| def forward(self, x: Tensor) -> Tensor: |
| s = 1 if self.cls_token else 0 |
| if x.ndim == 4: |
| B, H, W, _ = x.shape |
| assert s == 0 |
| L = H * W |
| ndim = 4 |
| else: |
| assert x.ndim == 3 |
| B, L, _ = x.shape |
| ndim = 3 |
| H = W = math.sqrt(L - s) |
|
|
| |
| qkv = self.qkv(x).reshape(B, L, 3, self.num_heads, -1) |
| |
| q, k, v = qkv.permute(2, 0, 3, 1, 4).unbind(0) |
|
|
| |
| q, k = self._apply_rope(q, k) |
| if self.use_rel_pos: |
| q, k = concat_rel_pos( |
| q.flatten(0, 1), |
| k.flatten(0, 1), |
| (H, W), |
| x.shape[1:3], |
| self.rel_pos_h, |
| self.rel_pos_w, |
| rescale=True, |
| relative_coords=self.relative_coords, |
| ) |
|
|
| |
| q = q.reshape(B, self.num_heads, H * W, -1) |
| k = k.reshape(B, self.num_heads, H * W, -1) |
|
|
| x = F.scaled_dot_product_attention(q, k, v) |
|
|
| if ndim == 4: |
| x = ( |
| x.view(B, self.num_heads, H, W, -1) |
| .permute(0, 2, 3, 1, 4) |
| .reshape(B, H, W, -1) |
| ) |
| else: |
| x = x.view(B, self.num_heads, L, -1).permute(0, 2, 1, 3).reshape(B, L, -1) |
|
|
| x = self.proj(x) |
|
|
| return x |
|
|
|
|
| class Block(nn.Module): |
| """Transformer blocks with support of window attention""" |
|
|
| def __init__( |
| self, |
| dim: int, |
| num_heads: int, |
| mlp_ratio: float = 4.0, |
| qkv_bias: bool = True, |
| drop_path: float = 0.0, |
| norm_layer: Callable[..., nn.Module] = nn.LayerNorm, |
| act_layer: Callable[..., nn.Module] = nn.GELU, |
| use_rel_pos: bool = False, |
| rel_pos_zero_init: bool = True, |
| window_size: int = 0, |
| input_size: Optional[Tuple[int, int]] = None, |
| use_rope: bool = False, |
| rope_pt_size: Optional[Tuple[int, int]] = None, |
| rope_tiled: bool = False, |
| rope_interp: bool = False, |
| use_ve_rope: bool = False, |
| cls_token: bool = False, |
| dropout: float = 0.0, |
| init_values: Optional[float] = None, |
| ): |
| """ |
| Args: |
| dim (int): Number of input channels. |
| num_heads (int): Number of attention heads in each ViT block. |
| mlp_ratio (float): Ratio of mlp hidden dim to embedding dim. |
| qkv_bias (bool): If True, add a learnable bias to query, key, value. |
| drop_path (float): Stochastic depth rate. |
| norm_layer (nn.Module): Normalization layer. |
| act_layer (nn.Module): Activation layer. |
| use_rel_pos (bool): If True, add relative positional embeddings to the attention map. |
| rel_pos_zero_init (bool): If True, zero initialize relative positional parameters. |
| window_size (int): Window size for window attention blocks. If it equals 0, then not |
| use window attention. |
| input_size (int or None): Input resolution for calculating the relative positional |
| parameter size. |
| dropout (float): Dropout rate. |
| cls_token: whether a cls_token is present. |
| use_rope: whether to use rope 2d (indep of use_rel_pos, as it can be used together) |
| rope_pt_size: size of rope in previous stage of training, needed for interpolation or tiling |
| rope_interp: whether to interpolate (or extrapolate) rope to match target input size, |
| expected to specify source size as rope_pt_size. |
| """ |
| super().__init__() |
| self.norm1 = norm_layer(dim) |
| self.attn = Attention( |
| dim, |
| num_heads=num_heads, |
| qkv_bias=qkv_bias, |
| use_rel_pos=use_rel_pos, |
| rel_pos_zero_init=rel_pos_zero_init, |
| input_size=input_size if window_size == 0 else (window_size, window_size), |
| use_rope=use_rope, |
| rope_pt_size=rope_pt_size, |
| rope_interp=rope_interp, |
| cls_token=cls_token, |
| ) |
| self.ls1 = ( |
| LayerScale(dim, init_values=init_values) if init_values else nn.Identity() |
| ) |
| self.drop_path = DropPath(drop_path) if drop_path > 0.0 else nn.Identity() |
|
|
| self.norm2 = norm_layer(dim) |
| self.mlp = Mlp( |
| in_features=dim, |
| hidden_features=int(dim * mlp_ratio), |
| act_layer=act_layer, |
| drop=(dropout, 0.0), |
| ) |
| self.ls2 = ( |
| LayerScale(dim, init_values=init_values) if init_values else nn.Identity() |
| ) |
| self.dropout = nn.Dropout(dropout) |
| self.window_size = window_size |
|
|
| def forward(self, x: Tensor) -> Tensor: |
| shortcut = x |
| x = self.norm1(x) |
| |
| if self.window_size > 0: |
| H, W = x.shape[1], x.shape[2] |
| x, pad_hw = window_partition(x, self.window_size) |
|
|
| x = self.ls1(self.attn(x)) |
| |
| if self.window_size > 0: |
| x = window_unpartition(x, self.window_size, pad_hw, (H, W)) |
|
|
| x = shortcut + self.dropout(self.drop_path(x)) |
| x = x + self.dropout(self.drop_path(self.ls2(self.mlp(self.norm2(x))))) |
|
|
| return x |
|
|
|
|
| class ViT(nn.Module): |
| """ |
| This module implements Vision Transformer (ViT) backbone in :paper:`vitdet`. |
| "Exploring Plain Vision Transformer Backbones for Object Detection", |
| https://arxiv.org/abs/2203.16527 |
| """ |
|
|
| def __init__( |
| self, |
| img_size: int = 1024, |
| patch_size: int = 16, |
| in_chans: int = 3, |
| embed_dim: int = 768, |
| depth: int = 12, |
| num_heads: int = 12, |
| mlp_ratio: float = 4.0, |
| qkv_bias: bool = True, |
| drop_path_rate: float = 0.0, |
| norm_layer: Union[Callable[..., nn.Module], str] = "LayerNorm", |
| act_layer: Callable[..., nn.Module] = nn.GELU, |
| use_abs_pos: bool = True, |
| tile_abs_pos: bool = True, |
| rel_pos_blocks: Union[Tuple[int, ...], bool] = (2, 5, 8, 11), |
| rel_pos_zero_init: bool = True, |
| window_size: int = 14, |
| global_att_blocks: Tuple[int, ...] = (2, 5, 8, 11), |
| use_rope: bool = False, |
| rope_pt_size: Optional[int] = None, |
| use_interp_rope: bool = False, |
| pretrain_img_size: int = 224, |
| pretrain_use_cls_token: bool = True, |
| retain_cls_token: bool = True, |
| dropout: float = 0.0, |
| return_interm_layers: bool = False, |
| init_values: Optional[float] = None, |
| ln_pre: bool = False, |
| ln_post: bool = False, |
| bias_patch_embed: bool = True, |
| compile_mode: Optional[str] = None, |
| use_act_checkpoint: bool = True, |
| ): |
| """ |
| Args: |
| img_size (int): Input image size. Only relevant for rel pos or rope. |
| patch_size (int): Patch size. |
| in_chans (int): Number of input image channels. |
| embed_dim (int): Patch embedding dimension. |
| depth (int): Depth of ViT. |
| num_heads (int): Number of attention heads in each ViT block. |
| mlp_ratio (float): Ratio of mlp hidden dim to embedding dim. |
| qkv_bias (bool): If True, add a learnable bias to query, key, value. |
| drop_path_rate (float): Stochastic depth rate. |
| norm_layer (nn.Module): Normalization layer. |
| act_layer (nn.Module): Activation layer. |
| use_abs_pos (bool): If True, use absolute positional embeddings. |
| tile_abs_pos (bool): If True, tile absolute positional embeddings instead of interpolation. |
| rel_pos_blocks (list): Blocks which have rel pos embeddings. |
| rel_pos_zero_init (bool): If True, zero initialize relative positional parameters. |
| window_size (int): Window size for window attention blocks. |
| global_att_blocks (list): Indexes for blocks using global attention (other blocks use window attention). |
| use_rope (bool): whether to use rope 2d (indep of rel_pos_blocks, as it can be used together). |
| rope_pt_size (int): size of rope in previous stage of training, needed for interpolation or tiling. |
| use_interp_rope: whether to interpolate (or extrapolate) rope to match target input size, |
| expected to specify source size as rope_pt_size. |
| use_act_checkpoint (bool): If True, use activation checkpointing. |
| pretrain_img_size (int): input image size for pretraining models. |
| pretrain_use_cls_token (bool): If True, pretraining models use class token. |
| retain_cls_token: whether cls_token should be retained. |
| dropout (float): Dropout rate. Applied in residual blocks of attn, mlp and inside the mlp. |
| |
| return_interm_layers (bool): Whether to return intermediate layers (all global attention blocks). |
| init_values: layer scale init, None for no layer scale. |
| |
| ln_pre (bool): If True, apply layer norm before transformer blocks. |
| ln_post (bool): If True, apply layer norm after transformer blocks. |
| bias_patch_embed (bool): bias in conv for patch embed? |
| compile_mode (str): mode to compile the forward |
| """ |
| super().__init__() |
| self.pretrain_use_cls_token = pretrain_use_cls_token |
|
|
| window_block_indexes = [i for i in range(depth) if i not in global_att_blocks] |
| self.full_attn_ids = list(global_att_blocks) |
| self.rel_pos_blocks = [False] * depth |
| if isinstance(rel_pos_blocks, bool) and rel_pos_blocks: |
| self.rel_pos_blocks = [True] * depth |
| else: |
| for i in rel_pos_blocks: |
| self.rel_pos_blocks[i] = True |
|
|
| self.retain_cls_token = retain_cls_token |
| if self.retain_cls_token: |
| assert pretrain_use_cls_token |
| assert ( |
| len(window_block_indexes) == 0 |
| ), "windowing not supported with cls token" |
|
|
| assert sum(self.rel_pos_blocks) == 0, "rel pos not supported with cls token" |
|
|
| scale = embed_dim**-0.5 |
| self.class_embedding = nn.Parameter(scale * torch.randn(1, 1, embed_dim)) |
|
|
| if isinstance(norm_layer, str): |
| norm_layer = partial(getattr(nn, norm_layer), eps=1e-5) |
|
|
| self.patch_embed = PatchEmbed( |
| kernel_size=(patch_size, patch_size), |
| stride=(patch_size, patch_size), |
| in_chans=in_chans, |
| embed_dim=embed_dim, |
| bias=bias_patch_embed, |
| ) |
|
|
| |
| self.tile_abs_pos = tile_abs_pos |
| self.use_abs_pos = use_abs_pos |
| if self.tile_abs_pos: |
| assert self.use_abs_pos |
|
|
| if self.use_abs_pos: |
| |
| num_patches = (pretrain_img_size // patch_size) * ( |
| pretrain_img_size // patch_size |
| ) |
| num_positions = (num_patches + 1) if pretrain_use_cls_token else num_patches |
| self.pos_embed = nn.Parameter(torch.zeros(1, num_positions, embed_dim)) |
| else: |
| self.pos_embed = None |
|
|
| |
| dpr = [x.item() for x in torch.linspace(0, drop_path_rate, depth)] |
|
|
| self.blocks = nn.ModuleList() |
| cur_stage = 1 |
| for i in range(depth): |
| block = Block( |
| dim=embed_dim, |
| num_heads=num_heads, |
| mlp_ratio=mlp_ratio, |
| qkv_bias=qkv_bias, |
| drop_path=dpr[i], |
| norm_layer=norm_layer, |
| act_layer=act_layer, |
| use_rel_pos=self.rel_pos_blocks[i], |
| rel_pos_zero_init=rel_pos_zero_init, |
| window_size=window_size if i in window_block_indexes else 0, |
| input_size=(img_size // patch_size, img_size // patch_size), |
| use_rope=use_rope, |
| rope_pt_size=( |
| (window_size, window_size) |
| if rope_pt_size is None |
| else (rope_pt_size, rope_pt_size) |
| ), |
| rope_interp=use_interp_rope, |
| cls_token=self.retain_cls_token, |
| dropout=dropout, |
| init_values=init_values, |
| ) |
|
|
| if i not in window_block_indexes: |
| cur_stage += 1 |
|
|
| self.use_act_checkpoint = use_act_checkpoint |
|
|
| self.blocks.append(block) |
|
|
| self.return_interm_layers = return_interm_layers |
| self.channel_list = ( |
| [embed_dim] * len(self.full_attn_ids) |
| if return_interm_layers |
| else [embed_dim] |
| ) |
|
|
| if self.pos_embed is not None: |
| trunc_normal_(self.pos_embed, std=0.02) |
|
|
| self.ln_pre = norm_layer(embed_dim) if ln_pre else nn.Identity() |
| self.ln_post = norm_layer(embed_dim) if ln_post else nn.Identity() |
|
|
| self.apply(self._init_weights) |
|
|
| if compile_mode is not None: |
| self.forward = torch.compile( |
| self.forward, mode=compile_mode, fullgraph=True |
| ) |
| if self.use_act_checkpoint and self.training: |
| torch._dynamo.config.optimize_ddp = False |
|
|
| def _init_weights(self, m: nn.Module) -> None: |
| if isinstance(m, nn.Linear): |
| trunc_normal_(m.weight, std=0.02) |
| if isinstance(m, nn.Linear) and m.bias is not None: |
| nn.init.constant_(m.bias, 0) |
| elif isinstance(m, nn.LayerNorm): |
| nn.init.constant_(m.bias, 0) |
| nn.init.constant_(m.weight, 1.0) |
|
|
| def forward(self, x: torch.Tensor) -> List[torch.Tensor]: |
| x = self.patch_embed(x) |
| h, w = x.shape[1], x.shape[2] |
|
|
| s = 0 |
| if self.retain_cls_token: |
| |
| |
| x = torch.cat([self.class_embedding, x.flatten(1, 2)], dim=1) |
| s = 1 |
|
|
| if self.pos_embed is not None: |
| x = x + get_abs_pos( |
| self.pos_embed, |
| self.pretrain_use_cls_token, |
| (h, w), |
| self.retain_cls_token, |
| tiling=self.tile_abs_pos, |
| ) |
|
|
| x = self.ln_pre(x) |
|
|
| outputs = [] |
| for i, blk in enumerate(self.blocks): |
| if self.use_act_checkpoint and self.training: |
| x = checkpoint.checkpoint(blk, x, use_reentrant=False) |
| else: |
| x = blk(x) |
| if (i == self.full_attn_ids[-1]) or ( |
| self.return_interm_layers and i in self.full_attn_ids |
| ): |
| if i == self.full_attn_ids[-1]: |
| x = self.ln_post(x) |
|
|
| feats = x[:, s:] |
| if feats.ndim == 4: |
| feats = feats.permute(0, 3, 1, 2) |
| else: |
| assert feats.ndim == 3 |
| h = w = math.sqrt(feats.shape[1]) |
| feats = feats.reshape( |
| feats.shape[0], h, w, feats.shape[-1] |
| ).permute(0, 3, 1, 2) |
|
|
| outputs.append(feats) |
|
|
| return outputs |
|
|
| def get_layer_id(self, layer_name: str) -> int: |
| |
| num_layers = self.get_num_layers() |
|
|
| if layer_name.find("rel_pos") != -1: |
| return num_layers + 1 |
| elif layer_name.find("ln_pre") != -1: |
| return 0 |
| elif layer_name.find("pos_embed") != -1 or layer_name.find("cls_token") != -1: |
| return 0 |
| elif layer_name.find("patch_embed") != -1: |
| return 0 |
| elif layer_name.find("blocks") != -1: |
| return int(layer_name.split("blocks")[1].split(".")[1]) + 1 |
| else: |
| return num_layers + 1 |
|
|
| def get_num_layers(self) -> int: |
| return len(self.blocks) |
|
|