User01110 commited on
Commit
9fc8d4e
·
verified ·
1 Parent(s): c54bab7

uncompiled attention: chunked over visible keys instead of eager flex

Browse files
Files changed (2) hide show
  1. README.md +1 -1
  2. modeling_swa.py +22 -6
README.md CHANGED
@@ -142,7 +142,7 @@ A broad, quality-filtered web mix, an educational slice, long documents for the
142
 
143
  The model uses custom code, so `trust_remote_code=True` is required.
144
 
145
- **Requirements:** PyTorch 2.5 or newer (attention runs on FlexAttention); tested with transformers 5.14. On an Ampere or newer GPU (compute capability 8.0+) with triton, attention is compiled; on CPU, older GPUs (T4, V100), or if compiling fails, it falls back to eager attention (same results, slower; set `SWA_NO_COMPILE=1` to force it). GPUs without bf16 (T4, V100) and CPUs: load with `dtype=torch.float32`. Generation uses a KV cache. Batches must not be padded: generate one prompt at a time, or batch only prompts of equal length.
146
 
147
  ```python
148
  import torch
 
142
 
143
  The model uses custom code, so `trust_remote_code=True` is required.
144
 
145
+ **Requirements:** PyTorch 2.5 or newer (attention runs on FlexAttention); tested with transformers 5.14. On an Ampere or newer GPU (compute capability 8.0+) with triton, attention is compiled; on CPU, older GPUs (T4, V100), or if compiling fails, it falls back to chunked attention: the same results, with sliding-window layers scoring only the 512 keys each query can see, so memory grows with length instead of length squared (set `SWA_NO_COMPILE=1` to force it). GPUs without bf16 (T4, V100) and CPUs: load with `dtype=torch.float32`. Generation uses a KV cache. Batches must not be padded: generate one prompt at a time, or batch only prompts of equal length.
146
 
147
  ```python
148
  import torch
modeling_swa.py CHANGED
@@ -56,9 +56,9 @@ def _can_compile(): # compiled flex kernels need triton and a CUDA GPU of compu
56
 
57
 
58
  class SafeFlex:
59
- """flex_attention compiled where it can be (fused, memory-light); on CPU, pre-Ampere GPUs (T4, V100), a missing
60
- triton, or the first compile failure it runs eagerly for the rest of the process: the same attention, slower,
61
- and materializing the full score matrix."""
62
  def __init__(s): s.compiled = torch.compile(flex_attention) if _can_compile() else None
63
 
64
  def give_up(s, e):
@@ -96,6 +96,17 @@ def cached_attention(q, k, v, qpos, kpos, qdoc, kdoc, window, sink):
96
  return torch.einsum("bhtl,blhd->bthd", sc.softmax(-1)[..., :-1], v.float()).to(q.dtype)
97
 
98
 
 
 
 
 
 
 
 
 
 
 
 
99
  class SWACache:
100
  """KV cache for generation. Per layer: keys/values after norms, HoPE and value residual (sliding-window layers keep
101
  only the last `window` tokens, full layers keep all), their positions and document ids, and the last conv - 1 raw
@@ -129,12 +140,16 @@ class Block(nn.Module):
129
  if v1 is None: v1 = v # block 0 hands its values to every later block
130
  else: lam = s.lam.to(v.dtype); v = lam[0] * v + lam[1] * v1
131
  q, k = rope(s.qn(q), cos, sin).type_as(v), rope(s.kn(k), cos, sin).type_as(v)
132
- if cache is None or "k" not in cache: # training, eval, and the prompt pass
133
- flex = flex_attention if torch.compiler.is_compiling() else _flex
134
  a, lse = flex(*(t.transpose(1, 2) for t in (q, k, v)), block_mask=masks[s.kind], enable_gqa=True, return_lse=True)
135
  a = a * torch.sigmoid(lse - s.sink.view(1, -1, 1)).unsqueeze(-1).type_as(a) # softmax with sink: Z / (Z + e^sink)
136
  o = a.transpose(1, 2).reshape(B, T, c.h * c.hd)
137
  K, V, P, D = k, v, pos, docs
 
 
 
 
138
  else: # decode: new queries over cached + new keys
139
  K, V = torch.cat([cache["k"], k], 1), torch.cat([cache["v"], v], 1)
140
  P, D = torch.cat([cache["pos"], pos]), torch.cat([cache["doc"], docs], 1)
@@ -164,7 +179,8 @@ class SWA(nn.Module):
164
  if docs is None:
165
  docs = (x == c.bos_token_id).cumsum(1)
166
  if off: docs = docs + cache.last_doc[:, None] # continue the cached document ids
167
- masks = block_masks(c, docs) if not off else None # decode steps use cached_attention instead
 
168
  x = s.emb(x)
169
  if torch.is_autocast_enabled(): x = x.to(torch.get_autocast_dtype("cuda"))
170
  inv = 1 / c.theta ** (torch.arange(0, c.hd, 2, device=x.device) / c.hd) # rotary frequency per q/k dimension pair
 
56
 
57
 
58
  class SafeFlex:
59
+ """flex_attention compiled where it can be (fused, memory-light). On CPU, pre-Ampere GPUs (T4, V100), a missing
60
+ triton, or the first compile failure, `compiled` is None and the model uses chunked_attention instead (the call
61
+ that hit the failure finishes on eager flex_attention)."""
62
  def __init__(s): s.compiled = torch.compile(flex_attention) if _can_compile() else None
63
 
64
  def give_up(s, e):
 
96
  return torch.einsum("bhtl,blhd->bthd", sc.softmax(-1)[..., :-1], v.float()).to(q.dtype)
97
 
98
 
99
+ def chunked_attention(q, k, v, pos, docs, window, sink, chunk=512):
100
+ """The flex path without compiling: queries in chunks, each over only the keys it can see (the last `window`
101
+ positions for sliding-window layers, everything before it for full layers), so no T x T score matrix is built.
102
+ q (B, T, H, D); k, v (B, T, KV, D); pos (T,); docs (B, T). Same result as flex with the block masks."""
103
+ out = []
104
+ for i in range(0, q.shape[1], chunk):
105
+ j = min(i + chunk, q.shape[1]); a = max(0, i - window + 1) if window else 0
106
+ out.append(cached_attention(q[:, i:j], k[:, a:j], v[:, a:j], pos[i:j], pos[a:j], docs[:, i:j], docs[:, a:j], window, sink))
107
+ return torch.cat(out, 1)
108
+
109
+
110
  class SWACache:
111
  """KV cache for generation. Per layer: keys/values after norms, HoPE and value residual (sliding-window layers keep
112
  only the last `window` tokens, full layers keep all), their positions and document ids, and the last conv - 1 raw
 
140
  if v1 is None: v1 = v # block 0 hands its values to every later block
141
  else: lam = s.lam.to(v.dtype); v = lam[0] * v + lam[1] * v1
142
  q, k = rope(s.qn(q), cos, sin).type_as(v), rope(s.kn(k), cos, sin).type_as(v)
143
+ if (cache is None or "k" not in cache) and masks is not None and (torch.compiler.is_compiling() or _flex.compiled is not None):
144
+ flex = flex_attention if torch.compiler.is_compiling() else _flex # training, eval, and the prompt pass (fused)
145
  a, lse = flex(*(t.transpose(1, 2) for t in (q, k, v)), block_mask=masks[s.kind], enable_gqa=True, return_lse=True)
146
  a = a * torch.sigmoid(lse - s.sink.view(1, -1, 1)).unsqueeze(-1).type_as(a) # softmax with sink: Z / (Z + e^sink)
147
  o = a.transpose(1, 2).reshape(B, T, c.h * c.hd)
148
  K, V, P, D = k, v, pos, docs
149
+ elif cache is None or "k" not in cache: # the same, where flex cannot compile
150
+ P = pos if pos is not None else torch.arange(T, device=x.device)
151
+ o = chunked_attention(q, k, v, P, docs, c.window if s.kind == "swa" else 0, s.sink).reshape(B, T, c.h * c.hd)
152
+ K, V, D = k, v, docs
153
  else: # decode: new queries over cached + new keys
154
  K, V = torch.cat([cache["k"], k], 1), torch.cat([cache["v"], v], 1)
155
  P, D = torch.cat([cache["pos"], pos]), torch.cat([cache["doc"], docs], 1)
 
179
  if docs is None:
180
  docs = (x == c.bos_token_id).cumsum(1)
181
  if off: docs = docs + cache.last_doc[:, None] # continue the cached document ids
182
+ fused = not off and (torch.compiler.is_compiling() or _flex.compiled is not None)
183
+ masks = block_masks(c, docs) if fused else None # decode steps and uncompiled runs use cached_attention
184
  x = s.emb(x)
185
  if torch.is_autocast_enabled(): x = x.to(torch.get_autocast_dtype("cuda"))
186
  inv = 1 / c.theta ** (torch.arange(0, c.hd, 2, device=x.device) / c.hd) # rotary frequency per q/k dimension pair