| import torch |
| import torch.nn as nn |
| import torch.nn.functional as F |
|
|
| from .rope import apply_rope |
|
|
|
|
| class CausalSelfAttention(nn.Module): |
| """Full multi-head causal self-attention (Section 4.4). |
| |
| Deliberately NOT using Grouped Query Attention (GQA) — the doc is explicit |
| that at this scale, GQA's memory savings are negligible and it can quietly |
| cost quality. Every head gets its own independent K/V projections. |
| """ |
|
|
| def __init__(self, hidden_dim: int, n_heads: int, dropout: float = 0.0): |
| super().__init__() |
| assert hidden_dim % n_heads == 0 |
| self.n_heads = n_heads |
| self.head_dim = hidden_dim // n_heads |
| self.dropout = dropout |
|
|
| |
| self.q_proj = nn.Linear(hidden_dim, hidden_dim, bias=False) |
| self.k_proj = nn.Linear(hidden_dim, hidden_dim, bias=False) |
| self.v_proj = nn.Linear(hidden_dim, hidden_dim, bias=False) |
| self.out_proj = nn.Linear(hidden_dim, hidden_dim, bias=False) |
|
|
| def forward(self, x: torch.Tensor, rope_freqs: torch.Tensor) -> torch.Tensor: |
| B, T, C = x.shape |
|
|
| q = self.q_proj(x).view(B, T, self.n_heads, self.head_dim).transpose(1, 2) |
| k = self.k_proj(x).view(B, T, self.n_heads, self.head_dim).transpose(1, 2) |
| v = self.v_proj(x).view(B, T, self.n_heads, self.head_dim).transpose(1, 2) |
|
|
| q = apply_rope(q, rope_freqs[:T]) |
| k = apply_rope(k, rope_freqs[:T]) |
|
|
| |
| |
| out = F.scaled_dot_product_attention( |
| q, k, v, |
| is_causal=True, |
| dropout_p=self.dropout if self.training else 0.0, |
| ) |
|
|
| out = out.transpose(1, 2).contiguous().view(B, T, C) |
| return self.out_proj(out) |
|
|