Tabby / basic.py
XSF0528's picture
Upload 14 files
a5d67fc verified
Raw
History Blame Contribute Delete
11.3 kB
from typing import Optional, Type
import torch
import torch.nn as nn
import torch.nn.functional as F
def make_attn_mask(query_pad: torch.Tensor, key_pad: torch.Tensor) -> torch.Tensor:
"""
Build an additive attention mask of shape (B, Q, K) from
query/key padding masks.
Args:
query_pad: (B, Q) bool or 0/1 tensor. 1/True = padded query position.
key_pad: (B, K) bool or 0/1 tensor. 1/True = padded key position.
Returns:
attn_mask: (B, Q, K) float tensor, where masked positions are -inf
and valid positions are 0.0 (for use with SDPA).
"""
# Ensure boolean
q_pad = query_pad.bool() # (B, Q)
k_pad = key_pad.bool() # (B, K)
# A position (q, k) is invalid if *either* the query or key is padded
# Shape: (B, Q, K)
pad = q_pad.unsqueeze(-1) | k_pad.unsqueeze(-2)
# Build float mask with -inf on padded positions, 0 elsewhere
attn_mask = torch.zeros_like(pad, dtype=torch.float32)
attn_mask.masked_fill_(pad, float("-inf"))
return attn_mask
class MLP(nn.Module):
def __init__(
self,
in_dim,
out_dim,
hidden_dim=256,
num_hidden_layers=1,
dropout=0,
norm=False,
activation=nn.GELU(approximate="tanh"),
output_activation=nn.Identity(),
norm_layer=nn.LayerNorm,
):
super().__init__()
layers = []
layers.append(nn.Linear(in_dim, hidden_dim))
# layers.append(norm_layer(hidden_dim) if norm else nn.Identity())
layers.append(activation)
for _ in range(num_hidden_layers - 1):
layers.append(nn.Dropout(dropout))
layers.append(norm_layer(hidden_dim) if norm else nn.Identity())
layers.append(nn.Linear(hidden_dim, hidden_dim))
layers.append(activation)
layers.append(nn.Dropout(dropout))
layers.append(norm_layer(hidden_dim) if norm else nn.Identity())
layers.append(nn.Linear(hidden_dim, out_dim))
layers.append(output_activation)
self.layers = nn.Sequential(*layers)
# self.init_weights()
def forward(self, x):
return self.layers(x)
class SwiGLU(nn.Module):
def __init__(self, in_dim, out_dim, hidden_dim=384, dropout=0):
super().__init__()
hidden_dim = round(hidden_dim * 2 / 3)
self.fc1 = nn.Linear(in_dim, hidden_dim)
self.fc2 = nn.Linear(in_dim, hidden_dim)
self.fc3 = nn.Linear(hidden_dim, out_dim)
self.activation = nn.SiLU()
self.dropout = nn.Dropout(dropout)
def forward(self, x):
x = self.fc1(x) * self.activation(self.fc2(x))
return self.dropout(self.fc3(x))
class Attention(nn.Module):
def __init__(
self,
dim: int,
num_heads: int = 8,
qkv_bias: bool = False,
qk_norm: bool = False,
proj_bias: bool = True,
attn_drop: float = 0.0,
proj_drop: float = 0.0,
norm_layer: Type[nn.Module] = nn.LayerNorm,
) -> None:
super().__init__()
assert dim % num_heads == 0, "dim should be divisible by num_heads"
self.num_heads = num_heads
self.head_dim = dim // num_heads
self.scale = self.head_dim**-0.5
self.qkv = nn.Linear(dim, dim * 3, bias=qkv_bias)
self.q_norm = norm_layer(self.head_dim) if qk_norm else nn.Identity()
self.k_norm = norm_layer(self.head_dim) if qk_norm else nn.Identity()
self.attn_drop = nn.Dropout(attn_drop)
self.proj = nn.Linear(dim, dim, bias=proj_bias)
self.proj_drop = nn.Dropout(proj_drop)
def forward(self, x: torch.Tensor, attn_mask: torch.Tensor | None = None) -> torch.Tensor:
if x.ndim == 3:
B, N, C = x.shape
qkv = self.qkv(x).reshape(B, N, 3, self.num_heads, self.head_dim).permute(2, 0, 3, 1, 4)
q, k, v = qkv.unbind(0) # (B, num_heads, N, head_dim)
q, k = self.q_norm(q), self.k_norm(k)
x = F.scaled_dot_product_attention(
q,
k,
v,
dropout_p=self.attn_drop.p if self.training else 0.0,
attn_mask=attn_mask,
)
x = x.transpose(1, 2).reshape(B, N, C)
elif x.ndim == 4:
B, M, N, C = x.shape
qkv = self.qkv(x).reshape(B, M, N, 3, self.num_heads, self.head_dim).permute(3, 0, 4, 1, 2, 5)
q, k, v = qkv.unbind(0) # (B, num_heads, M, N, head_dim)
q, k = self.q_norm(q), self.k_norm(k)
# print('q', q.shape, 'k', k.shape, 'v', v.shape, 'attn_mask', attn_mask.shape if attn_mask is not None else "None")
x = F.scaled_dot_product_attention(
q,
k,
v,
dropout_p=self.attn_drop.p if self.training else 0.0,
attn_mask=attn_mask.unsqueeze(1) if attn_mask is not None else None,
)
x = x.permute(0, 2, 3, 1, 4).reshape(B, M, N, C)
else:
raise ValueError(f"Unsupported input dimension: {x.ndim}")
x = self.proj(x)
x = self.proj_drop(x)
return x
class CrossAttention(nn.Module):
def __init__(
self,
q_dim: int, # dim of x
kv_dim: Optional[int] = None, # dim of m (defaults to q_dim)
num_heads: int = 8,
qkv_bias: bool = False,
qk_norm: bool = False,
proj_bias: bool = True,
attn_drop: float = 0.0,
proj_drop: float = 0.0,
norm_layer: Type[nn.Module] = nn.LayerNorm,
) -> None:
super().__init__()
kv_dim = kv_dim if kv_dim is not None else q_dim
assert q_dim % num_heads == 0, "q_dim must be divisible by num_heads"
self.num_heads = num_heads
self.head_dim = q_dim // num_heads
self.q = nn.Linear(q_dim, q_dim, bias=qkv_bias)
self.kv = nn.Linear(kv_dim, 2 * q_dim, bias=qkv_bias) # produce k and v in the SAME head dim as q
self.q_norm = norm_layer(self.head_dim) if qk_norm else nn.Identity()
self.k_norm = norm_layer(self.head_dim) if qk_norm else nn.Identity()
self.attn_drop = nn.Dropout(attn_drop)
self.proj = nn.Linear(q_dim, q_dim, bias=proj_bias)
self.proj_drop = nn.Dropout(proj_drop)
def forward(
self,
x: torch.Tensor, # (B, Nq, q_dim)
m: torch.Tensor, # (B, Nk, kv_dim)
attn_mask: Optional[torch.Tensor] = None, # broadcastable to (B, num_heads, Nq, Nk) or (Nq, Nk)
is_causal: bool = False,
) -> torch.Tensor:
if x.ndim == 3:
B, Nq, Cq = x.shape
_, Nk, _ = m.shape
q = self.q(x).reshape(B, Nq, self.num_heads, self.head_dim).permute(0, 2, 1, 3) # (B, H, Nq, Hd)
kv = self.kv(m).reshape(B, Nk, 2, self.num_heads, self.head_dim).permute(2, 0, 3, 1, 4)
k, v = kv.unbind(0) # (B, H, Nk, Hd)
q, k = self.q_norm(q), self.k_norm(k)
x = F.scaled_dot_product_attention(
q,
k,
v,
attn_mask=attn_mask,
dropout_p=self.attn_drop.p if self.training else 0.0,
is_causal=is_causal,
) # (B, H, Nq, Hd)
x = x.transpose(1, 2).reshape(B, Nq, Cq) # back to (B, Nq, q_dim)
elif x.ndim == 4:
B, M, Nq, Cq = x.shape
_, Nk, _ = m.shape
q = self.q(x).reshape(B, M, Nq, self.num_heads, self.head_dim).permute(0, 3, 1, 2, 4) # (B, H, M, Nq, Hd)
kv = self.kv(m).reshape(B, Nk, 2, self.num_heads, self.head_dim).permute(2, 0, 3, 1, 4)
k, v = kv.unbind(0) # (B, H, Nk, Hd)
q, k = self.q_norm(q), self.k_norm(k)
x = F.scaled_dot_product_attention(
q,
k.unsqueeze(2),
v.unsqueeze(2),
attn_mask=attn_mask.unsqueeze(1) if attn_mask is not None else None,
dropout_p=self.attn_drop.p if self.training else 0.0,
is_causal=is_causal,
) # (B, H, M, Nq, Hd)
x = x.permute(0, 2, 3, 1, 4).reshape(B, M, Nq, Cq)
else:
raise ValueError(f"Unsupported input dimension: {x.ndim}")
x = self.proj_drop(self.proj(x))
return x
class TransformerBlock(nn.Module):
"""
A standard Transformer block.
"""
def __init__(
self,
d_model,
num_heads,
mlp_ratio=4.0,
dropout=0.1,
norm_first=True,
norm_layer=nn.LayerNorm,
mlp_type="mlp",
):
super().__init__()
self.norm_first = norm_first
self.norm1 = norm_layer(d_model, elementwise_affine=True, eps=1e-6)
self.attn = Attention(d_model, num_heads, qkv_bias=True, attn_drop=dropout, proj_drop=dropout)
self.norm2 = norm_layer(d_model, elementwise_affine=True, eps=1e-6)
if mlp_type == "swiglu":
self.mlp = SwiGLU(d_model, d_model, hidden_dim=int(mlp_ratio * d_model), dropout=dropout)
elif mlp_type == "mlp":
self.mlp = MLP(
in_dim=d_model,
out_dim=d_model,
hidden_dim=int(mlp_ratio * d_model),
dropout=dropout,
)
else:
raise ValueError(f"Unsupported MLP type: {mlp_type}")
self.dropout = nn.Dropout(dropout)
def forward(self, x, attn_mask=None):
if self.norm_first:
x = x + self.attn(self.norm1(x), attn_mask)
x = x + self.dropout(self.mlp(self.norm2(x)))
else:
x = self.norm1(x + self.attn(x, attn_mask))
x = self.norm2(x + self.dropout(self.mlp(x)))
return x
class TransformerBlockCrossAttention(nn.Module):
def __init__(
self,
d_model,
num_heads,
d_cond=None,
mlp_ratio=4.0,
dropout=0.1,
norm_first=True,
norm_layer=nn.LayerNorm,
mlp_type="mlp",
):
super().__init__()
d_cond = d_cond if d_cond is not None else d_model
self.norm_first = norm_first
self.norm1 = norm_layer(d_model, elementwise_affine=True, eps=1e-6)
self.attn = CrossAttention(
d_model,
d_cond,
num_heads,
qkv_bias=True,
attn_drop=dropout,
proj_drop=dropout,
)
self.norm2 = norm_layer(d_model, elementwise_affine=True, eps=1e-6)
if mlp_type == "swiglu":
self.mlp = SwiGLU(d_model, d_model, hidden_dim=int(mlp_ratio * d_model), dropout=dropout)
elif mlp_type == "mlp":
self.mlp = MLP(
in_dim=d_model,
out_dim=d_model,
hidden_dim=int(mlp_ratio * d_model),
dropout=dropout,
)
else:
raise ValueError(f"Unsupported MLP type: {mlp_type}")
self.dropout = nn.Dropout(dropout)
def forward(self, x, m, attn_mask=None):
if self.norm_first:
x = x + self.attn(self.norm1(x), m, attn_mask)
x = x + self.dropout(self.mlp(self.norm2(x)))
else:
x = self.norm1(x + self.attn(x, m, attn_mask))
x = self.norm2(x + self.dropout(self.mlp(x)))
return x