Kye Gomez commited on
Commit
7d78ebe
·
1 Parent(s): eae0f04

flash attn

Browse files
Files changed (4) hide show
  1. README.md +6 -0
  2. open_mythos/main.py +42 -16
  3. pyproject.toml +7 -0
  4. 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.attn_drop = nn.Dropout(cfg.dropout)
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
- # expand KV to match Q heads
234
- k = k.repeat_interleave(self.groups, dim=2)
235
- v = v.repeat_interleave(self.groups, dim=2)
236
-
237
- q = q.transpose(1, 2) # (B, H, T, head_dim)
238
- k = k.transpose(1, 2)
239
- v = v.transpose(1, 2)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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