Kye Gomez commited on
Commit ·
7d78ebe
1
Parent(s): eae0f04
flash attn
Browse files- README.md +6 -0
- open_mythos/main.py +42 -16
- pyproject.toml +7 -0
- requirements.txt +3 -0
README.md
CHANGED
|
@@ -40,6 +40,12 @@ pip install open-mythos
|
|
| 40 |
#uv pip install open-mythos
|
| 41 |
```
|
| 42 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 43 |
## Usage
|
| 44 |
|
| 45 |
```python
|
|
|
|
| 40 |
#uv pip install open-mythos
|
| 41 |
```
|
| 42 |
|
| 43 |
+
To enable Flash Attention 2 in `GQAttention` (requires CUDA and build tools):
|
| 44 |
+
|
| 45 |
+
```bash
|
| 46 |
+
pip install open-mythos[flash]
|
| 47 |
+
```
|
| 48 |
+
|
| 49 |
## Usage
|
| 50 |
|
| 51 |
```python
|
open_mythos/main.py
CHANGED
|
@@ -5,6 +5,13 @@ import torch
|
|
| 5 |
import torch.nn as nn
|
| 6 |
import torch.nn.functional as F
|
| 7 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 8 |
|
| 9 |
@dataclass
|
| 10 |
class MythosConfig:
|
|
@@ -169,12 +176,17 @@ def apply_rope(x: torch.Tensor, freqs_cis: torch.Tensor) -> torch.Tensor:
|
|
| 169 |
|
| 170 |
class GQAttention(nn.Module):
|
| 171 |
"""
|
| 172 |
-
Grouped Query Attention (Ainslie et al., 2023).
|
| 173 |
|
| 174 |
Uses fewer KV heads than Q heads (n_kv_heads < n_heads). Each KV head is
|
| 175 |
shared across n_heads // n_kv_heads query heads, reducing the KV cache size
|
| 176 |
by that factor while keeping full query expressiveness.
|
| 177 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 178 |
RoPE is applied to both Q and K. K and V are stored in kv_cache after
|
| 179 |
RoPE application so that cached values are already positionally encoded and
|
| 180 |
do not need to be re-rotated on retrieval.
|
|
@@ -195,7 +207,7 @@ class GQAttention(nn.Module):
|
|
| 195 |
self.wk = nn.Linear(cfg.dim, cfg.n_kv_heads * self.head_dim, bias=False)
|
| 196 |
self.wv = nn.Linear(cfg.dim, cfg.n_kv_heads * self.head_dim, bias=False)
|
| 197 |
self.wo = nn.Linear(cfg.n_heads * self.head_dim, cfg.dim, bias=False)
|
| 198 |
-
self.
|
| 199 |
|
| 200 |
def forward(
|
| 201 |
self,
|
|
@@ -230,21 +242,35 @@ class GQAttention(nn.Module):
|
|
| 230 |
v = torch.cat([kv_cache[cache_key]["v"], v], dim=1)
|
| 231 |
kv_cache[cache_key] = {"k": k.detach(), "v": v.detach()}
|
| 232 |
|
| 233 |
-
|
| 234 |
-
|
| 235 |
-
|
| 236 |
-
|
| 237 |
-
|
| 238 |
-
|
| 239 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 240 |
|
| 241 |
-
scale = self.head_dim**-0.5
|
| 242 |
-
attn = torch.matmul(q, k.transpose(-2, -1)) * scale
|
| 243 |
-
if mask is not None:
|
| 244 |
-
attn = attn + mask
|
| 245 |
-
attn = self.attn_drop(F.softmax(attn, dim=-1))
|
| 246 |
-
out = torch.matmul(attn, v)
|
| 247 |
-
out = out.transpose(1, 2).contiguous().view(B, T, -1)
|
| 248 |
return self.wo(out)
|
| 249 |
|
| 250 |
|
|
|
|
| 5 |
import torch.nn as nn
|
| 6 |
import torch.nn.functional as F
|
| 7 |
|
| 8 |
+
try:
|
| 9 |
+
from flash_attn import flash_attn_func
|
| 10 |
+
|
| 11 |
+
_HAS_FLASH_ATTN = True
|
| 12 |
+
except ImportError:
|
| 13 |
+
_HAS_FLASH_ATTN = False
|
| 14 |
+
|
| 15 |
|
| 16 |
@dataclass
|
| 17 |
class MythosConfig:
|
|
|
|
| 176 |
|
| 177 |
class GQAttention(nn.Module):
|
| 178 |
"""
|
| 179 |
+
Grouped Query Attention (Ainslie et al., 2023) with Flash Attention 2 (Dao et al., 2023).
|
| 180 |
|
| 181 |
Uses fewer KV heads than Q heads (n_kv_heads < n_heads). Each KV head is
|
| 182 |
shared across n_heads // n_kv_heads query heads, reducing the KV cache size
|
| 183 |
by that factor while keeping full query expressiveness.
|
| 184 |
|
| 185 |
+
When flash-attn is installed, uses flash_attn_func which handles GQA natively
|
| 186 |
+
(no KV head expansion needed) and is IO-bound-optimal. Inputs are cast to
|
| 187 |
+
bfloat16 for flash_attn and restored to the original dtype afterward.
|
| 188 |
+
Falls back to manual scaled dot-product attention when flash-attn is absent.
|
| 189 |
+
|
| 190 |
RoPE is applied to both Q and K. K and V are stored in kv_cache after
|
| 191 |
RoPE application so that cached values are already positionally encoded and
|
| 192 |
do not need to be re-rotated on retrieval.
|
|
|
|
| 207 |
self.wk = nn.Linear(cfg.dim, cfg.n_kv_heads * self.head_dim, bias=False)
|
| 208 |
self.wv = nn.Linear(cfg.dim, cfg.n_kv_heads * self.head_dim, bias=False)
|
| 209 |
self.wo = nn.Linear(cfg.n_heads * self.head_dim, cfg.dim, bias=False)
|
| 210 |
+
self.dropout_p = cfg.dropout
|
| 211 |
|
| 212 |
def forward(
|
| 213 |
self,
|
|
|
|
| 242 |
v = torch.cat([kv_cache[cache_key]["v"], v], dim=1)
|
| 243 |
kv_cache[cache_key] = {"k": k.detach(), "v": v.detach()}
|
| 244 |
|
| 245 |
+
if _HAS_FLASH_ATTN:
|
| 246 |
+
# flash_attn_func expects (B, T, H, head_dim) — GQA is handled natively
|
| 247 |
+
# (n_kv_heads < n_heads is supported without repeat_interleave).
|
| 248 |
+
# causal=True when mask is present (full-sequence prefill/training);
|
| 249 |
+
# causal=False for single-token decode where T=1 and mask is None.
|
| 250 |
+
orig_dtype = q.dtype
|
| 251 |
+
q = q.to(torch.bfloat16)
|
| 252 |
+
k = k.to(torch.bfloat16)
|
| 253 |
+
v = v.to(torch.bfloat16)
|
| 254 |
+
dropout_p = self.dropout_p if self.training else 0.0
|
| 255 |
+
out = flash_attn_func(q, k, v, dropout_p=dropout_p, causal=(mask is not None))
|
| 256 |
+
out = out.to(orig_dtype).contiguous().view(B, T, -1)
|
| 257 |
+
else:
|
| 258 |
+
# Fallback: manual scaled dot-product with explicit KV head expansion.
|
| 259 |
+
k = k.repeat_interleave(self.groups, dim=2)
|
| 260 |
+
v = v.repeat_interleave(self.groups, dim=2)
|
| 261 |
+
q = q.transpose(1, 2) # (B, H, T, head_dim)
|
| 262 |
+
k = k.transpose(1, 2)
|
| 263 |
+
v = v.transpose(1, 2)
|
| 264 |
+
scale = self.head_dim**-0.5
|
| 265 |
+
attn = torch.matmul(q, k.transpose(-2, -1)) * scale
|
| 266 |
+
if mask is not None:
|
| 267 |
+
attn = attn + mask
|
| 268 |
+
attn = F.dropout(
|
| 269 |
+
F.softmax(attn, dim=-1), p=self.dropout_p, training=self.training
|
| 270 |
+
)
|
| 271 |
+
out = torch.matmul(attn, v)
|
| 272 |
+
out = out.transpose(1, 2).contiguous().view(B, T, -1)
|
| 273 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 274 |
return self.wo(out)
|
| 275 |
|
| 276 |
|
pyproject.toml
CHANGED
|
@@ -42,6 +42,13 @@ torch = "2.11.0"
|
|
| 42 |
transformers = ">=4.40.0"
|
| 43 |
datasets = ">=2.18.0"
|
| 44 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 45 |
|
| 46 |
[tool.poetry.group.lint.dependencies]
|
| 47 |
black = ">=23.1,<27.0"
|
|
|
|
| 42 |
transformers = ">=4.40.0"
|
| 43 |
datasets = ">=2.18.0"
|
| 44 |
|
| 45 |
+
[tool.poetry.extras]
|
| 46 |
+
flash = ["flash-attn"]
|
| 47 |
+
|
| 48 |
+
[tool.poetry.dependencies.flash-attn]
|
| 49 |
+
version = ">=2.8.3"
|
| 50 |
+
optional = true
|
| 51 |
+
|
| 52 |
|
| 53 |
[tool.poetry.group.lint.dependencies]
|
| 54 |
black = ">=23.1,<27.0"
|
requirements.txt
CHANGED
|
@@ -2,3 +2,6 @@ torch>=2.1.0
|
|
| 2 |
transformers>=4.40.0
|
| 3 |
datasets>=2.18.0
|
| 4 |
pytest>=7.0.0
|
|
|
|
|
|
|
|
|
|
|
|
| 2 |
transformers>=4.40.0
|
| 3 |
datasets>=2.18.0
|
| 4 |
pytest>=7.0.0
|
| 5 |
+
|
| 6 |
+
# optional — enables Flash Attention 2 in GQAttention (requires CUDA + build tools)
|
| 7 |
+
# flash-attn>=2.8.3
|