142 lines
6.2 KiB
Python
142 lines
6.2 KiB
Python
from typing import *
|
|
import torch
|
|
import torch.nn as nn
|
|
import torch.nn.functional as F
|
|
from .. import VarLenTensor, SparseTensor
|
|
from .full_attn import sparse_scaled_dot_product_attention
|
|
from .windowed_attn import sparse_windowed_scaled_dot_product_self_attention
|
|
from .rope import SparseRotaryPositionEmbedder
|
|
|
|
|
|
class SparseMultiHeadRMSNorm(nn.Module):
|
|
def __init__(self, dim: int, heads: int):
|
|
super().__init__()
|
|
self.scale = dim ** 0.5
|
|
self.gamma = nn.Parameter(torch.ones(heads, dim))
|
|
|
|
def forward(self, x: Union[VarLenTensor, torch.Tensor]) -> Union[VarLenTensor, torch.Tensor]:
|
|
x_type = x.dtype
|
|
x = x.float()
|
|
if isinstance(x, VarLenTensor):
|
|
x = x.replace(F.normalize(x.feats, dim=-1) * self.gamma * self.scale)
|
|
else:
|
|
x = F.normalize(x, dim=-1) * self.gamma * self.scale
|
|
return x.to(x_type)
|
|
|
|
|
|
class SparseMultiHeadAttention(nn.Module):
|
|
def __init__(
|
|
self,
|
|
channels: int,
|
|
num_heads: int,
|
|
ctx_channels: Optional[int] = None,
|
|
type: Literal["self", "cross"] = "self",
|
|
attn_mode: Literal["full", "windowed", "double_windowed"] = "full",
|
|
window_size: Optional[int] = None,
|
|
shift_window: Optional[Tuple[int, int, int]] = None,
|
|
qkv_bias: bool = True,
|
|
use_rope: bool = False,
|
|
rope_freq: Tuple[int, int] = (1.0, 10000.0),
|
|
qk_rms_norm: bool = False,
|
|
):
|
|
super().__init__()
|
|
assert channels % num_heads == 0
|
|
assert type in ["self", "cross"], f"Invalid attention type: {type}"
|
|
assert attn_mode in ["full", "windowed", "double_windowed"], f"Invalid attention mode: {attn_mode}"
|
|
assert type == "self" or attn_mode == "full", "Cross-attention only supports full attention"
|
|
assert type == "self" or use_rope is False, "Rotary position embeddings only supported for self-attention"
|
|
if attn_mode == 'double_windowed':
|
|
assert window_size % 2 == 0, "Window size must be even for double windowed attention"
|
|
assert num_heads % 2 == 0, "Number of heads must be even for double windowed attention"
|
|
self.channels = channels
|
|
self.head_dim = channels // num_heads
|
|
self.ctx_channels = ctx_channels if ctx_channels is not None else channels
|
|
self.num_heads = num_heads
|
|
self._type = type
|
|
self.attn_mode = attn_mode
|
|
self.window_size = window_size
|
|
self.shift_window = shift_window
|
|
self.use_rope = use_rope
|
|
self.qk_rms_norm = qk_rms_norm
|
|
|
|
if self._type == "self":
|
|
self.to_qkv = nn.Linear(channels, channels * 3, bias=qkv_bias)
|
|
else:
|
|
self.to_q = nn.Linear(channels, channels, bias=qkv_bias)
|
|
self.to_kv = nn.Linear(self.ctx_channels, channels * 2, bias=qkv_bias)
|
|
|
|
if self.qk_rms_norm:
|
|
self.q_rms_norm = SparseMultiHeadRMSNorm(self.head_dim, num_heads)
|
|
self.k_rms_norm = SparseMultiHeadRMSNorm(self.head_dim, num_heads)
|
|
|
|
self.to_out = nn.Linear(channels, channels)
|
|
|
|
if use_rope:
|
|
self.rope = SparseRotaryPositionEmbedder(self.head_dim, rope_freq=rope_freq)
|
|
|
|
@staticmethod
|
|
def _linear(module: nn.Linear, x: Union[VarLenTensor, torch.Tensor]) -> Union[VarLenTensor, torch.Tensor]:
|
|
if isinstance(x, VarLenTensor):
|
|
return x.replace(module(x.feats))
|
|
else:
|
|
return module(x)
|
|
|
|
@staticmethod
|
|
def _reshape_chs(x: Union[VarLenTensor, torch.Tensor], shape: Tuple[int, ...]) -> Union[VarLenTensor, torch.Tensor]:
|
|
if isinstance(x, VarLenTensor):
|
|
return x.reshape(*shape)
|
|
else:
|
|
return x.reshape(*x.shape[:2], *shape)
|
|
|
|
def _fused_pre(self, x: Union[VarLenTensor, torch.Tensor], num_fused: int) -> Union[VarLenTensor, torch.Tensor]:
|
|
if isinstance(x, VarLenTensor):
|
|
x_feats = x.feats.unsqueeze(0)
|
|
else:
|
|
x_feats = x
|
|
x_feats = x_feats.reshape(*x_feats.shape[:2], num_fused, self.num_heads, -1)
|
|
return x.replace(x_feats.squeeze(0)) if isinstance(x, VarLenTensor) else x_feats
|
|
|
|
def forward(self, x: SparseTensor, context: Optional[Union[VarLenTensor, torch.Tensor]] = None) -> SparseTensor:
|
|
if self._type == "self":
|
|
qkv = self._linear(self.to_qkv, x)
|
|
qkv = self._fused_pre(qkv, num_fused=3)
|
|
if self.qk_rms_norm or self.use_rope:
|
|
q, k, v = qkv.unbind(dim=-3)
|
|
if self.qk_rms_norm:
|
|
q = self.q_rms_norm(q)
|
|
k = self.k_rms_norm(k)
|
|
if self.use_rope:
|
|
q, k = self.rope(q, k)
|
|
qkv = qkv.replace(torch.stack([q.feats, k.feats, v.feats], dim=1))
|
|
if self.attn_mode == "full":
|
|
h = sparse_scaled_dot_product_attention(qkv)
|
|
elif self.attn_mode == "windowed":
|
|
h = sparse_windowed_scaled_dot_product_self_attention(
|
|
qkv, self.window_size, shift_window=self.shift_window
|
|
)
|
|
elif self.attn_mode == "double_windowed":
|
|
qkv0 = qkv.replace(qkv.feats[:, :, self.num_heads//2:])
|
|
qkv1 = qkv.replace(qkv.feats[:, :, :self.num_heads//2])
|
|
h0 = sparse_windowed_scaled_dot_product_self_attention(
|
|
qkv0, self.window_size, shift_window=(0, 0, 0)
|
|
)
|
|
h1 = sparse_windowed_scaled_dot_product_self_attention(
|
|
qkv1, self.window_size, shift_window=tuple([self.window_size//2] * 3)
|
|
)
|
|
h = qkv.replace(torch.cat([h0.feats, h1.feats], dim=1))
|
|
else:
|
|
q = self._linear(self.to_q, x)
|
|
q = self._reshape_chs(q, (self.num_heads, -1))
|
|
kv = self._linear(self.to_kv, context)
|
|
kv = self._fused_pre(kv, num_fused=2)
|
|
if self.qk_rms_norm:
|
|
q = self.q_rms_norm(q)
|
|
k, v = kv.unbind(dim=-3)
|
|
k = self.k_rms_norm(k)
|
|
h = sparse_scaled_dot_product_attention(q, k, v)
|
|
else:
|
|
h = sparse_scaled_dot_product_attention(q, kv)
|
|
h = self._reshape_chs(h, (-1,))
|
|
h = self._linear(self.to_out, h)
|
|
return h
|