Download scripts/quant_longctx.py from poolside-laguna-hackathon/kv-landlords: direct link, hf CLI and curl.
- Browser
- Download file 6.21 kB
-
https://huggingface.co/poolside-laguna-hackathon/kv-landlords/resolve/main/scripts/quant_longctx.py
- Command line
-
hf download hf://poolside-laguna-hackathon/kv-landlords/scripts/quant_longctx.py
-
curl -L -o quant_longctx.py https://huggingface.co/poolside-laguna-hackathon/kv-landlords/resolve/main/scripts/quant_longctx.py
6.21 kB
| """Long-context test: does the INT4-KIVI vs NVFP4-baseline gap grow with context? | |
| Scores long in-distribution sequences (real source code, in-distribution for a | |
| code model) in a SINGLE forward per scheme. KV quantization is injected by | |
| patching scaled_dot_product_attention so position t attends to a quantized | |
| prefix. Per-position KL(bf16||scheme) and top-1 agreement are binned by sequence | |
| position, revealing whether quantization error compounds as context lengthens. | |
| All cached K/V are quantized (the <16-token bf16 hot page is omitted — negligible | |
| at long context, and a slightly conservative choice). | |
| Usage: | |
| python -m scripts.quant_longctx [--ctx 8000] [--windows 3] | |
| """ | |
| from __future__ import annotations | |
| import argparse | |
| import glob | |
| import sys | |
| import torch | |
| import torch.nn.functional as F | |
| from transformers import AutoModelForCausalLM, AutoTokenizer | |
| sys.path.insert(0, "/home/alex/poolside-hackathon-kv-quant") | |
| from kv_quant import roundtrip | |
| MODEL = "poolside/Laguna-XS.2" | |
| N_ALPHAS = 32 | |
| SCHEMES = { | |
| "nvfp4-baseline": {"k": ("nvfp4", "headdim", "absmax"), "v": ("nvfp4", "headdim", "absmax")}, | |
| "int4-kivi": {"k": ("int4", "channel", "mse"), "v": ("int4", "headdim", "mse")}, | |
| "int3-kivi": {"k": ("int3", "channel", "mse"), "v": ("int3", "headdim", "mse")}, | |
| "int3-naive": {"k": ("int3", "headdim", "absmax"), "v": ("int3", "headdim", "absmax")}, | |
| } | |
| BASELINE = "nvfp4-baseline" | |
| BINS = [(0, 512), (512, 1024), (1024, 2048), (2048, 4096), (4096, 8192)] | |
| _ORIG_SDPA = F.scaled_dot_product_attention | |
| _SCHEME = None | |
| _HITS = 0 | |
| def _q_per_head(x, cell): | |
| """Quantize [H, S, D] head-by-head (caps the MSE-search peak memory).""" | |
| return torch.stack([roundtrip(x[h:h + 1], *cell, n_alphas=N_ALPHAS)[0] | |
| for h in range(x.shape[0])]) | |
| def _patched_sdpa(query, key, value, *a, **kw): | |
| global _HITS | |
| if _SCHEME is not None: | |
| _HITS += 1 | |
| key = _q_per_head(key[0], _SCHEME["k"]).unsqueeze(0) | |
| value = _q_per_head(value[0], _SCHEME["v"]).unsqueeze(0) | |
| return _ORIG_SDPA(query, key, value, *a, **kw) | |
| def token_pool(tok): | |
| files = sorted(glob.glob( | |
| "/home/alex/poolside-hackathon-kv-quant/.venv/**/transformers/**/modeling_*.py", | |
| recursive=True)) | |
| texts, total = [], 0 | |
| for f in files: | |
| try: | |
| t = open(f).read() | |
| except OSError: | |
| continue | |
| texts.append(t) | |
| total += len(t) | |
| if total > 600_000: | |
| break | |
| ids = tok("\n\n".join(texts), return_tensors="pt").input_ids[0] | |
| return ids | |
| def logits_of(model, ids): | |
| return model(input_ids=ids.unsqueeze(0)).logits[0] # [S, V], bf16 | |
| def kl_top1(ref, lg, ref_arg, chunk=512): | |
| """Per-position KL(bf16||scheme) and top-1 match. ref/lg bf16 [S,V].""" | |
| S = ref.shape[0] | |
| kls, t1 = [], [] | |
| for i in range(0, S, chunk): | |
| r, q = ref[i:i + chunk].float(), lg[i:i + chunk].float() | |
| rlp, qlp = torch.log_softmax(r, -1), torch.log_softmax(q, -1) | |
| kls.append((rlp.exp() * (rlp - qlp)).sum(-1)) | |
| t1.append((q.argmax(-1) == ref_arg[i:i + chunk]).float()) | |
| return torch.cat(kls), torch.cat(t1) | |
| def main(): | |
| ap = argparse.ArgumentParser() | |
| ap.add_argument("--ctx", type=int, default=8000) | |
| ap.add_argument("--windows", type=int, default=3) | |
| args = ap.parse_args() | |
| global _SCHEME, _HITS | |
| F.scaled_dot_product_attention = _patched_sdpa | |
| torch.nn.functional.scaled_dot_product_attention = _patched_sdpa | |
| print(f"[load] {MODEL}", flush=True) | |
| tok = AutoTokenizer.from_pretrained(MODEL) | |
| model = AutoModelForCausalLM.from_pretrained( | |
| MODEL, torch_dtype=torch.bfloat16, device_map="auto", attn_implementation="sdpa") | |
| model.eval() | |
| device = next(model.parameters()).device | |
| pool = token_pool(tok) | |
| ctx = (args.ctx // 16) * 16 | |
| nwin = min(args.windows, pool.shape[0] // ctx) | |
| print(f"[seq] {nwin} windows x {ctx} tokens (pool={pool.shape[0]})", flush=True) | |
| bins = [(lo, hi) for lo, hi in BINS if lo < ctx] | |
| acc = {n: {b: {"kl": [], "t1": []} for b in bins} for n in SCHEMES} | |
| for w in range(nwin): | |
| ids = pool[w * ctx:(w + 1) * ctx].to(device) | |
| _SCHEME = None | |
| ref = logits_of(model, ids) | |
| ref_arg = ref.argmax(-1) | |
| for name, sch in SCHEMES.items(): | |
| _HITS = 0 | |
| _SCHEME = sch | |
| lg = logits_of(model, ids) | |
| _SCHEME = None | |
| assert _HITS > 0, f"SDPA patch never fired for {name} — wrong attn path" | |
| kl, t1 = kl_top1(ref, lg, ref_arg) | |
| for lo, hi in bins: | |
| acc[name][(lo, hi)]["kl"].append(kl[lo:min(hi, ctx)].mean().item()) | |
| acc[name][(lo, hi)]["t1"].append(t1[lo:min(hi, ctx)].mean().item()) | |
| del lg | |
| print(f"[window {w}] done", flush=True) | |
| avg = lambda xs: sum(xs) / max(len(xs), 1) | |
| print("\n" + "=" * 74) | |
| print(f"PER-POSITION KL(bf16||scheme), avg over {nwin} windows") | |
| print(f" {'position':<12}" + "".join(f"{n[:13]:>14}" for n in SCHEMES)) | |
| print(f" {'-'*12}" + "".join(f" {'-'*13}" for _ in SCHEMES)) | |
| for lo, hi in bins: | |
| row = f" {f'{lo}-{min(hi,ctx)}':<12}" | |
| for n in SCHEMES: | |
| row += f"{avg(acc[n][(lo,hi)]['kl']):>14.5f}" | |
| print(row) | |
| print(f"\nKIVI vs baseline: KL reduction by position (does the win grow?)") | |
| print(f" {'position':<12}{'int4-kivi':>14}{'int3-kivi':>14}") | |
| print(f" {'-'*12}{' '+'-'*13}{' '+'-'*13}") | |
| for lo, hi in bins: | |
| b = avg(acc[BASELINE][(lo, hi)]["kl"]) | |
| i4 = avg(acc["int4-kivi"][(lo, hi)]["kl"]) | |
| i3 = avg(acc["int3-kivi"][(lo, hi)]["kl"]) | |
| row = f" {f'{lo}-{min(hi,ctx)}':<12}" | |
| row += f"{100*(b-i4)/max(b,1e-12):>13.0f}%" | |
| row += f"{100*(b-i3)/max(b,1e-12):>13.0f}%" | |
| print(row) | |
| print(f"\nTOP-1 agreement vs bf16 by position") | |
| print(f" {'position':<12}" + "".join(f"{n[:13]:>14}" for n in SCHEMES)) | |
| for lo, hi in bins: | |
| row = f" {f'{lo}-{min(hi,ctx)}':<12}" | |
| for n in SCHEMES: | |
| row += f"{100*avg(acc[n][(lo,hi)]['t1']):>13.1f}%" | |
| print(row) | |
| if __name__ == "__main__": | |
| main() | |