siglino-30M / attention.py
SofianChay's picture
Update SigLino siglino-30M (full content push)
e5f4b9e verified
Raw
History Blame Contribute Delete
5.42 kB
# Attention module for Falcon Vision
# Supports FlexAttention for efficient vision-only attention patterns
import torch
import torch.nn.functional as F
from torch import nn
from torch.nn.attention.flex_attention import (
BlockMask,
AuxRequest,
create_block_mask,
flex_attention,
)
import einops as E
from .rope import apply_rotary_emb, apply_3d_rotary_emb
def repeat_kv(x: torch.Tensor, n_rep: int) -> torch.Tensor:
"""Repeat key/value heads to match query heads."""
bs, slen, n_kv_heads, head_dim = x.shape
if n_rep == 1:
return x
return (
x.unsqueeze(3)
.expand(bs, slen, n_kv_heads, n_rep, head_dim)
.reshape(bs, slen, n_kv_heads * n_rep, head_dim)
)
class FlexAttentionWrapper(nn.Module):
"""Wrapper for flex_attention with optional compilation and aux outputs."""
_compiled = None
def forward(
self,
q: torch.Tensor,
k: torch.Tensor,
v: torch.Tensor,
block_mask: BlockMask,
compile: bool = True,
return_aux: bool = False,
):
# Choose compiled or eager function
fn = flex_attention
if compile:
if FlexAttentionWrapper._compiled is None:
FlexAttentionWrapper._compiled = torch.compile(
flex_attention,
mode="max-autotune-no-cudagraphs",
)
fn = FlexAttentionWrapper._compiled
if return_aux:
# Request log-sum-exp aux for sink attention
return fn(q, k, v, block_mask=block_mask, return_aux=AuxRequest(lse=True))
else:
return fn(q, k, v, block_mask=block_mask)
class SDPAttentionWrapper(nn.Module):
"""Fallback SDPA attention when flex_attention is not available."""
def forward(
self,
q: torch.Tensor,
k: torch.Tensor,
v: torch.Tensor,
attn_mask: torch.Tensor | None = None,
) -> torch.Tensor:
return F.scaled_dot_product_attention(q, k, v, attn_mask=attn_mask)
class Attention(nn.Module):
def __init__(
self,
dim: int,
n_heads: int,
n_kv_heads: int | None = None,
head_dim: int | None = None,
use_qk_norm: bool = False,
enable_3d_rope: bool = False,
use_flex_attn: bool = True,
use_sink_attn: bool = True,
):
super().__init__()
self.n_heads = n_heads
self.n_kv_heads = n_kv_heads or n_heads
self.n_rep = self.n_heads // self.n_kv_heads
self.head_dim = head_dim or dim // n_heads
self.q_dim = self.n_heads * self.head_dim
self.kv_dim = self.n_kv_heads * self.head_dim
self.wq = nn.Linear(dim, self.q_dim, bias=False)
self.wk = nn.Linear(dim, self.kv_dim, bias=False)
self.wv = nn.Linear(dim, self.kv_dim, bias=False)
self.wo = nn.Linear(self.n_heads * self.head_dim, dim, bias=False)
self.use_qk_norm = use_qk_norm
self.enable_3d_rope = enable_3d_rope
self.use_flex_attn = use_flex_attn
self.sink_attn = use_sink_attn
if self.sink_attn:
self.sinks = nn.Parameter(torch.empty(n_heads))
self.inner_attention = FlexAttentionWrapper()
def init_weights(self, init_std: float):
for linear in (self.wq, self.wk, self.wv):
nn.init.trunc_normal_(linear.weight, mean=0.0, std=init_std)
nn.init.zeros_(self.wo.weight)
if self.sink_attn:
nn.init.trunc_normal_(self.sinks, mean=0.0, std=init_std)
def forward(
self,
x: torch.Tensor,
freqs_cis: torch.Tensor,
freqs_cis_2d: torch.Tensor | None = None,
pos_thw: torch.Tensor | None = None,
attention_masks: BlockMask | torch.Tensor | None = None,
compile: bool = True,
) -> torch.Tensor:
bs, seqlen, _ = x.shape
xq, xk, xv = self.wq(x), self.wk(x), self.wv(x)
xq = xq.view(bs, seqlen, -1, self.head_dim)
xk = xk.view(bs, seqlen, -1, self.head_dim)
xv = xv.view(bs, seqlen, -1, self.head_dim)
if self.use_qk_norm:
xq = F.rms_norm(xq, (xq.size(-1),))
xk = F.rms_norm(xk, (xk.size(-1),))
xk = repeat_kv(xk, self.n_rep)
xv = repeat_kv(xv, self.n_rep)
xq, xk = apply_3d_rotary_emb(xq, xk, freqs_cis, freqs_cis_2d, pos_thw)
xq = xq.transpose(1, 2)
xk = xk.transpose(1, 2)
xv = xv.transpose(1, 2)
output, aux = self.inner_attention(
xq,
xk,
xv,
block_mask=attention_masks,
compile=compile,
return_aux=True,
)
# aux.lse: (B, H, S) log-sum-exp per head & position
sinks_BHL = E.rearrange(self.sinks, "h -> 1 h 1")
sink_scale = torch.sigmoid(aux.lse - sinks_BHL)
output = (output * sink_scale.unsqueeze(-1)).to(output.dtype)
output = E.rearrange(output, "b h s d -> b s (h d)").contiguous()
return self.wo(output)
def create_attention_mask(
mask_mod,
B: int | None,
H: int | None,
Q_LEN: int,
KV_LEN: int,
BLOCK_SIZE: tuple[int, int] = (64, 64),
) -> BlockMask:
"""Create a BlockMask for flex_attention."""
return create_block_mask(
mask_mod,
B=B,
H=H,
Q_LEN=Q_LEN,
KV_LEN=KV_LEN,
BLOCK_SIZE=BLOCK_SIZE,
)