| |
| |
| |
| |
|
|
| import math |
| from typing import List, Tuple |
|
|
| import torch |
| import torch.nn.functional as F |
| from dinov3.utils import cat_keep_shapes, uncat_with_shapes |
| from torch import Tensor, nn |
|
|
|
|
| |
| def rope_rotate_half(x: Tensor) -> Tensor: |
| |
| |
| x1, x2 = x.chunk(2, dim=-1) |
| return torch.cat([-x2, x1], dim=-1) |
|
|
|
|
| def rope_apply(x: Tensor, sin: Tensor, cos: Tensor) -> Tensor: |
| |
| |
| |
| return (x * cos) + (rope_rotate_half(x) * sin) |
|
|
|
|
| class LinearKMaskedBias(nn.Linear): |
| def __init__(self, *args, **kwargs): |
| super().__init__(*args, **kwargs) |
| o = self.out_features |
| assert o % 3 == 0 |
| if self.bias is not None: |
| self.register_buffer("bias_mask", torch.full_like(self.bias, fill_value=math.nan)) |
|
|
| def forward(self, input: Tensor) -> Tensor: |
| masked_bias = self.bias * self.bias_mask.to(self.bias.dtype) if self.bias is not None else None |
| return F.linear(input, self.weight, masked_bias) |
|
|
|
|
| class SelfAttention(nn.Module): |
| def __init__( |
| self, |
| dim: int, |
| num_heads: int = 8, |
| qkv_bias: bool = False, |
| proj_bias: bool = True, |
| attn_drop: float = 0.0, |
| proj_drop: float = 0.0, |
| mask_k_bias: bool = False, |
| device=None, |
| ) -> None: |
| super().__init__() |
| self.num_heads = num_heads |
| head_dim = dim // num_heads |
| self.scale = head_dim**-0.5 |
|
|
| linear_class = LinearKMaskedBias if mask_k_bias else nn.Linear |
| self.qkv = linear_class(dim, dim * 3, bias=qkv_bias, device=device) |
| self.attn_drop = nn.Dropout(attn_drop) |
| self.proj = nn.Linear(dim, dim, bias=proj_bias, device=device) |
| self.proj_drop = nn.Dropout(proj_drop) |
|
|
| def apply_rope(self, q: Tensor, k: Tensor, rope: Tensor | Tuple[Tensor, Tensor]) -> Tuple[Tensor, Tensor]: |
| |
| q_dtype = q.dtype |
| k_dtype = k.dtype |
| sin, cos = rope |
| rope_dtype = sin.dtype |
| q = q.to(dtype=rope_dtype) |
| k = k.to(dtype=rope_dtype) |
| N = q.shape[-2] |
| prefix = N - sin.shape[-2] |
| assert prefix >= 0 |
| q_prefix = q[:, :, :prefix, :] |
| q = rope_apply(q[:, :, prefix:, :], sin, cos) |
| q = torch.cat((q_prefix, q), dim=-2) |
| k_prefix = k[:, :, :prefix, :] |
| k = rope_apply(k[:, :, prefix:, :], sin, cos) |
| k = torch.cat((k_prefix, k), dim=-2) |
| q = q.to(dtype=q_dtype) |
| k = k.to(dtype=k_dtype) |
| return q, k |
|
|
| def forward(self, x: Tensor, attn_bias=None, rope: Tensor = None) -> Tensor: |
| qkv = self.qkv(x) |
| attn_v = self.compute_attention(qkv=qkv, attn_bias=attn_bias, rope=rope) |
| x = self.proj(attn_v) |
| x = self.proj_drop(x) |
| return x |
|
|
| def forward_list(self, x_list, attn_bias=None, rope_list=None) -> List[Tensor]: |
| assert len(x_list) == len(rope_list) |
| x_flat, shapes, num_tokens = cat_keep_shapes(x_list) |
| qkv_flat = self.qkv(x_flat) |
| qkv_list = uncat_with_shapes(qkv_flat, shapes, num_tokens) |
| att_out = [] |
| for _, (qkv, _, rope) in enumerate(zip(qkv_list, shapes, rope_list)): |
| att_out.append(self.compute_attention(qkv, attn_bias=attn_bias, rope=rope)) |
| x_flat, shapes, num_tokens = cat_keep_shapes(att_out) |
| x_flat = self.proj(x_flat) |
| return uncat_with_shapes(x_flat, shapes, num_tokens) |
|
|
| def compute_attention(self, qkv: Tensor, attn_bias=None, rope=None) -> Tensor: |
| assert attn_bias is None |
| B, N, _ = qkv.shape |
| C = self.qkv.in_features |
|
|
| qkv = qkv.reshape(B, N, 3, self.num_heads, C // self.num_heads) |
| q, k, v = torch.unbind(qkv, 2) |
| q, k, v = [t.transpose(1, 2) for t in [q, k, v]] |
| if rope is not None: |
| q, k = self.apply_rope(q, k, rope) |
| x = torch.nn.functional.scaled_dot_product_attention(q, k, v) |
| x = x.transpose(1, 2) |
| return x.reshape([B, N, C]) |
|
|
|
|
| class CausalSelfAttention(nn.Module): |
| def __init__( |
| self, |
| dim: int, |
| num_heads: int = 8, |
| qkv_bias: bool = False, |
| proj_bias: bool = True, |
| attn_drop: float = 0.0, |
| proj_drop: float = 0.0, |
| ) -> None: |
| super().__init__() |
| self.dim = dim |
| self.num_heads = num_heads |
| head_dim = dim // num_heads |
| self.scale = head_dim**-0.5 |
|
|
| self.qkv = nn.Linear(dim, dim * 3, bias=qkv_bias) |
| self.attn_drop = attn_drop |
| self.proj = nn.Linear(dim, dim, bias=proj_bias) |
| self.proj_drop = nn.Dropout(proj_drop) |
|
|
| def init_weights( |
| self, init_attn_std: float | None = None, init_proj_std: float | None = None, factor: float = 1.0 |
| ) -> None: |
| init_attn_std = init_attn_std or (self.dim**-0.5) |
| init_proj_std = init_proj_std or init_attn_std * factor |
| nn.init.normal_(self.qkv.weight, std=init_attn_std) |
| nn.init.normal_(self.proj.weight, std=init_proj_std) |
| if self.qkv.bias is not None: |
| nn.init.zeros_(self.qkv.bias) |
| if self.proj.bias is not None: |
| nn.init.zeros_(self.proj.bias) |
|
|
| def forward(self, x: Tensor, is_causal: bool = True) -> Tensor: |
| B, N, C = x.shape |
| qkv = self.qkv(x).reshape(B, N, 3, self.num_heads, C // self.num_heads) |
| q, k, v = torch.unbind(qkv, 2) |
| q, k, v = [t.transpose(1, 2) for t in [q, k, v]] |
| x = torch.nn.functional.scaled_dot_product_attention( |
| q, k, v, attn_mask=None, dropout_p=self.attn_drop if self.training else 0, is_causal=is_causal |
| ) |
| x = x.transpose(1, 2).contiguous().view(B, N, C) |
| x = self.proj_drop(self.proj(x)) |
| return x |
|
|