Skip to content

Commit 84d0636

Browse files
PR-K1.F: SDPA attention path for K1.E long-context (>= 16k) NIAH validation
The 2026-06-08 vast.ai multi-context scan (commit 4c95975 on PR #75 branch) hit OOM at the 4500-line / 7000-line ladder rungs (~88k / ~140k tokens). Diagnosis: HF transformers default-loads Gemma 3-1B with attn_implementation='eager' which materialises the full [B, H, T, T] attention matrix per layer. At T=88k bf16 with H=4 Q heads, that's 62 GB just for ONE layer's attention matrix — H200's 141 GB is insufficient (62 GB * (2x for v0.4 verifier+proposer) + weights + activations + KV cache > 141 GB). The K1.D patched forward already supports SDPA dispatch via ALL_ATTENTION_FUNCTIONS[impl] when impl != 'eager' — no v0.4 code change needed. The fix is purely in the K1.E harness: expose the attention implementation as a configurable knob. Files changed: scripts/research/k1e_niah_validation.py * New --attn-impl {eager, sdpa} flag (default eager). Threaded into AutoModelForCausalLM.from_pretrained's attn_implementation kwarg. * Reported in the JSON config block as 'attn_impl' so future analysis can disambiguate eager-baseline runs from SDPA-long-context runs. scripts/review_pr_k1e_on_vast.sh * New ATTN_IMPL env knob (default 'sdpa' on vast — the whole point of the GPU runner is to reach the 100k gate (a) target which requires the memory-efficient path). * Documents the eager-vs-sdpa trade-off inline. * Updated default ladder note: line ≈ 20 tokens (with chat template), not the 14 originally estimated. Documents a long-context ladder example reaching the canonical 100k gate (a) target: '70 280 1100 3200 5000' for ~1.4k / 5.6k / 22k / 64k / 100k. scripts/review_pr_k1e_on_mac.sh * Same ATTN_IMPL env knob (default 'eager' on Mac for reproducibility with the 2026-06-08 baseline recorded in ADR 0008 §11.11). Documents the trade-off: eager preserves bit-exact baseline; sdpa is needed for >= 16k context even on Mac but introduces small bf16 reduction-order numerical differences that may shift recall by a few percent. Memory math (Gemma 3-1B at 88k tokens, bf16): attention matrix [1, 4, 88000, 88000] = 62 GB per layer H200 has 141 GB usable; eager OOMs. SDPA chunks the [T, T] matrix; never materialises full block. Risk assessment: * SDPA reduction order may differ from eager at bf16 precision. Recall shifts of <= few % are possible at any context length. For the K1.E gate (a) check ('v04 within 5pp of oracle'), this is acceptable because BOTH oracle and v0.4 use the same SDPA backend — so the v04-vs-oracle delta should remain ~0. The v0.3 baseline (which also uses SDPA) might shift its absolute recall slightly, but the qualitative regression (~ 0/N) is structural to sink+window=4+64 and won't change. What this PR does NOT do: * No code change to K1.D (the patched forward already routes through ALL_ATTENTION_FUNCTIONS for non-eager impls). * No change to the v04 unit test suite (163 cases still pass — they don't exercise model loading). * No change to ADR 0008 §11 (a follow-up postscript will record the resulting long-context evidence when the user re-runs vast). Re-run instructions for vast (after this PR + the K1 stack land): git checkout main && git pull MULTI_CONTEXT=1 \ CONTEXT_LADDER='70 280 1100 3200 5000' \ N_SAMPLES=20 \ bash scripts/review_pr_k1e_on_vast.sh Expected outcome: 5 JSONs covering ~1.4k / 5.6k / 22k / 64k / 100k. At 100k context, expect: * Oracle recall: depends on Gemma 3-1B's intrinsic long-context ability (likely ~0.3-0.7 based on the 21k=0.65 data point). * v0.3 recall: ~0 (structural). * v0.4 recall: == oracle (architectural claim from the K1 same-model identity scope). The architectural claim is 'v04 == oracle in K1 identity case', not 'v04 >= 0.95 absolute'. If oracle's absolute recall at 100k is 0.5 and v04 also gets 0.5, that's a PASS for the architecture even though the original gate predicate 'v04_recall_ge_0_95' is False — Gemma 3-1B-it's intrinsic 100k limitation is not the architecture's fault. The right gate after seeing the 21k data is 'v04 within 5pp of oracle at every measured context'. Stacking notes: Logical base is PR #75 (K1.E vast runner). After #71 -> #72 -> #73 -> #74 -> #75 land on main, this PR's diff shrinks to just the three modified files. Co-authored-by: FluffyAIcode <FluffyAIcode@users.noreply.github.com>
1 parent 4c95975 commit 84d0636

3 files changed

Lines changed: 65 additions & 5 deletions

File tree

scripts/research/k1e_niah_validation.py

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,21 @@ def parse_args() -> argparse.Namespace:
7474
help="Skip the full-attention oracle. Not recommended; the "
7575
"oracle is the upper-bound reference.",
7676
)
77+
ap.add_argument(
78+
"--attn-impl",
79+
choices=["eager", "sdpa"],
80+
default="eager",
81+
help="HF transformers attention implementation for the wrapped model. "
82+
"'eager' (default) materialises the full [B, H, T, T] attention "
83+
"matrix per layer — fits comfortably at <= 16k context but OOMs "
84+
"long-context oracle/v0.3/v0.4 forwards on a single H200 at >= 88k "
85+
"tokens (62 GB just for one layer's attention matrix at 88k bf16). "
86+
"'sdpa' uses HF's memory-efficient scaled-dot-product-attention path; "
87+
"the K1.D patched forward already dispatches through ALL_ATTENTION_"
88+
"FUNCTIONS[impl] when impl != 'eager', so v0.4 K/V Restoration also "
89+
"works under SDPA. Use 'sdpa' for the 64k+ context rungs that "
90+
"validate ADR 0008 §11.8 gate (a) at canonical scale.",
91+
)
7792
ap.add_argument(
7893
"--output", default=None,
7994
help="JSON report path. Default: results/research/k1e_niah_<stamp>.json",
@@ -110,8 +125,9 @@ def main() -> int:
110125
tokenizer.pad_token_id = tokenizer.eos_token_id
111126

112127
dtype = torch.bfloat16 if device.type != "cpu" else torch.float32
128+
print(f"[k1e] attn_implementation={args.attn_impl}", file=sys.stderr)
113129
model = AutoModelForCausalLM.from_pretrained(
114-
args.model, dtype=dtype, attn_implementation="eager",
130+
args.model, dtype=dtype, attn_implementation=args.attn_impl,
115131
).to(device)
116132
model.eval()
117133
for p in model.parameters():
@@ -273,6 +289,7 @@ def v04_decode(sample) -> Tuple[str, float]:
273289
"model": args.model,
274290
"device": str(device),
275291
"dtype": str(dtype),
292+
"attn_impl": args.attn_impl,
276293
"n_samples": args.n_samples,
277294
"haystack_min_lines": args.haystack_min_lines,
278295
"haystack_max_lines": args.haystack_max_lines,

scripts/review_pr_k1e_on_mac.sh

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,14 @@
3535
# HAYSTACK_MAX (default 80) max padding-line count
3636
# SINK (default 4) sink size
3737
# WINDOW (default 64) window size
38+
# ATTN_IMPL (default eager) 'eager' (matches the 2026-06-08
39+
# Mac M4 baseline recorded in ADR
40+
# 0008 §11.11) vs 'sdpa' (memory-
41+
# efficient; needed for >= 16k context
42+
# even on Mac, but introduces small
43+
# bf16 reduction-order numerical
44+
# differences vs eager — slightly
45+
# different recall numbers possible).
3846
# SKIP_V03=1 skip the v0.3 baseline (saves ~5 min)
3947
# SKIP_V04=1 skip v0.4 (smoke-only the oracle path)
4048
#
@@ -58,6 +66,7 @@ HAYSTACK_MIN="${HAYSTACK_MIN:-60}"
5866
HAYSTACK_MAX="${HAYSTACK_MAX:-80}"
5967
SINK="${SINK:-4}"
6068
WINDOW="${WINDOW:-64}"
69+
ATTN_IMPL="${ATTN_IMPL:-eager}"
6170
SKIP_V03="${SKIP_V03:-0}"
6271
SKIP_V04="${SKIP_V04:-0}"
6372

@@ -82,6 +91,7 @@ echo
8291
flags=(
8392
--model google/gemma-3-1b-it
8493
--device auto
94+
--attn-impl "$ATTN_IMPL"
8595
--n-samples "$N_SAMPLES"
8696
--haystack-min-lines "$HAYSTACK_MIN"
8797
--haystack-max-lines "$HAYSTACK_MAX"

scripts/review_pr_k1e_on_vast.sh

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -69,14 +69,34 @@
6969
# WINDOW (default 64)
7070
# MAX_NEW_TOKENS (default 24)
7171
# SEED (default 42)
72+
# ATTN_IMPL (default sdpa) 'eager' (full [B,H,T,T] matrix, OOMs
73+
# at >= 88k tokens on H200) vs 'sdpa'
74+
# (memory-efficient, fits 100k+).
7275
# SKIP_V03=1 skip the v0.3 baseline
7376
# SKIP_V04=1 skip v0.4 (oracle-only smoke)
7477
# SKIP_ORACLE=1 skip the oracle (not recommended)
7578
# MULTI_CONTEXT=1 enable multi-context scan
76-
# CONTEXT_LADDER='40 80 320 1280' (only used when MULTI_CONTEXT=1)
79+
# CONTEXT_LADDER='70 280 1100' (only used when MULTI_CONTEXT=1)
7780
# space-separated padding-line counts;
78-
# each entry yields a haystack range of
79-
# [n × 0.85, n × 1.15] for variability.
81+
# line ≈ 20 tokens with chat template
82+
# (empirical from 2026-06-08 run);
83+
# each entry yields a haystack range
84+
# of [n × 0.85, n × 1.15] for variability.
85+
#
86+
# Examples:
87+
#
88+
# # Default scan (1k / 4k / 16k tokens) with SDPA — fits H100/H200:
89+
# bash scripts/review_pr_k1e_on_vast.sh
90+
#
91+
# # Long-context scan reaching the canonical 100k gate (a) target:
92+
# MULTI_CONTEXT=1 \
93+
# CONTEXT_LADDER='70 280 1100 3200 5000' \
94+
# bash scripts/review_pr_k1e_on_vast.sh
95+
# # Lines × 20 tok/line ≈ 1.4k / 5.6k / 22k / 64k / 100k tokens.
96+
#
97+
# # Force eager (reproducibility with the 2026-06-08 short-context
98+
# # baseline; do NOT use at long context — will OOM):
99+
# ATTN_IMPL=eager bash scripts/review_pr_k1e_on_vast.sh
80100

81101
set -euo pipefail
82102

@@ -94,7 +114,19 @@ SKIP_V03="${SKIP_V03:-0}"
94114
SKIP_V04="${SKIP_V04:-0}"
95115
SKIP_ORACLE="${SKIP_ORACLE:-0}"
96116
MULTI_CONTEXT="${MULTI_CONTEXT:-0}"
97-
# Default ladder: ~1k, ~4k, ~16k tokens (line ≈ 14 tokens)
117+
# K1.F: HF attention implementation. 'eager' materialises [B, H, T, T]
118+
# per layer — at 88k tokens that's 62 GB just for one layer's attention
119+
# matrix in bf16, which OOMs even an H200's 141 GB. 'sdpa' uses HF's
120+
# memory-efficient scaled-dot-product-attention path; the K1.D patched
121+
# forward already dispatches through ALL_ATTENTION_FUNCTIONS[impl] when
122+
# impl != 'eager', so v0.4 K/V Restoration also works under SDPA.
123+
# Default 'sdpa' on vast because the whole point is to push past the
124+
# 88k OOM and reach the canonical 100k gate (a) target.
125+
ATTN_IMPL="${ATTN_IMPL:-sdpa}"
126+
# Default ladder: ~1k, ~4k, ~16k tokens (line ≈ 20 tokens with chat
127+
# template — empirically observed in the 2026-06-08 run, not 14 as
128+
# initially estimated, so the previous 4500/7000 ladder produced
129+
# ~88k / ~140k token prompts, both of which OOM'd under eager).
98130
CONTEXT_LADDER="${CONTEXT_LADDER:-70 280 1100}"
99131

100132
stamp="$(date +%s)"
@@ -105,6 +137,7 @@ mkdir -p "$out_dir" "$log_dir"
105137
flags_common=(
106138
--model google/gemma-3-1b-it
107139
--device cuda
140+
--attn-impl "$ATTN_IMPL"
108141
--n-samples "$N_SAMPLES"
109142
--sink-size "$SINK"
110143
--window-size "$WINDOW"

0 commit comments

Comments
 (0)