K-Means Memory
Streaming k-means over keys — exact attention below capacity, graceful compression above it.
K-Means Memory
Motivated by the Lattice Memory findings (the no-dynamics ablation was the fidelity ceiling; the best spring constant trended to zero): keep the mass-weighted centroid read, drop the topology entirely, and let the partition be data-adaptive streaming k-means over keys.
Mechanics: greedy fill — while a slot is free, every token births its own centroid, so short contexts are exact attention. Once full, tokens merge into the Euclidean-nearest centroid; a deferred scale factor makes the leak O(1) per token; centroids dying below a mass floor free their slots so births resume as old content fades. The leak doubles as a linear recency bias through the \beta\log(\text{mass}) score term.
How we know this
| impl | spec | max_dlogit |
|---|---|---|
| sliding | sliding:256 | 0.000000 |
| kmeans-exact | kmeans:l=0 | 0.000000 |
| tiered-exact | tiered:e=1024;l=0;nerode=0 | 0.000000 |
| sink-exact | sink:s=16;w=256 | 0.000000 |
Findings
- Dropping the lattice topology lost nothing and (attested) doubled streaming speed, with equal-or-better lexical stats at 16× compression (2048 tokens in 128 centroids per head).
- Birth policy matters more than capacity. A DP-means-style relative threshold self-throttled — the mean-distance EMA anchors high and almost nothing births, making capacity settings indistinguishable. Greedy fill fixed it instantly. (The idea returns, rehabilitated, as the surprise gate.)
- Deep-stream degradation (~position 1900) persists and is not explained
by memory blur alone or RoPE extrapolation alone: position-free scoring
(
rope=none) made it worse, so positional scoring is protective. - Re-indexed relative RoPE is implemented, verified, and — frozen — refuted. Each centroid tracks a decaying mean insertion position; at read time its key is rotated by its clamped integer age, so scoring uses relative positions that never leave the trained range — StreamingLLM's rolling-position trick generalized to clusters. The read path is exact to float noise (0.000014 max |Δlogit| at zero leak), but deep streams got worse. Diagnosis: raw phase interference is a feature — a position-incoherent cluster's rotated keys partially cancel, automatically suppressing blurry blobs; re-indexing restores their full magnitude at one fake age and lets stale mush outcompete fresh tokens. Position handling and forgetting are coupled. The story reverses with training in the loop — see Training in the Loop.
Measured behaviour
Short-range fidelity on the standard probe, and 3-seed stream health at 2048 tokens (8× trained context):
| impl | spec | agree | total | worst_dlogit |
|---|---|---|---|---|
| kmeans128 | kmeans:m=128 | 67 | 70 | 1.459785 |
| sink4-124 | sink:s=4;w=124 | 70 | 70 | 0.000000 |
| kmeans64 | kmeans:m=64 | 67 | 70 | 15.235278 |
| tiered-nerode | tiered:m=64;e=16;nerode=64;tau=0.2;tv=0.05 | 68 | 70 | 0.979404 |
| tiered-gate | tiered:m=64;e=16;nerode=0;g=0.5 | 67 | 70 | 0.993266 |
| tiered32-gate | tiered:m=32;e=16;g=1.0 | 63 | 70 | 5.868399 |
| lattice-binning | lattice:g=32;it=0 | 49 | 70 | 9.130353 |
| lattice-best | lattice:rope=raw;k=0.01;l=0.01 | 54 | 70 | 9.767433 |
| lattice-underdamped | lattice:d=0.12 | 14 | 70 | 24.241472 |
| impl | seed | first | last | min |
|---|---|---|---|---|
| tiered-nerode-s1 | 1 | 0.58 | 0.50 | 0.38 |
| tiered-nerode-s2 | 2 | 0.49 | 0.50 | 0.49 |
| tiered-nerode-s3 | 3 | 0.54 | 0.52 | 0.38 |
| sink4-124-s1 | 1 | 0.56 | 0.22 | 0.22 |
| sink4-124-s2 | 2 | 0.54 | 0.28 | 0.28 |
| sink4-124-s3 | 3 | 0.57 | 0.40 | 0.32 |
| kmeans128-s1 | 1 | 0.63 | 0.52 | 0.31 |
| kmeans128-s2 | 2 | 0.55 | 0.49 | 0.43 |
| kmeans128-s3 | 3 | 0.48 | 0.53 | 0.35 |
| kmeans64-s1 | 1 | 0.58 | 0.50 | 0.36 |
| kmeans64-s2 | 2 | 0.43 | 0.36 | 0.29 |
| kmeans64-s3 | 3 | 0.52 | 0.50 | 0.44 |
| tiered32-gate-s1 | 1 | 0.60 | 0.45 | 0.40 |
| tiered32-gate-s2 | 2 | 0.60 | 0.43 | 0.35 |
| tiered32-gate-s3 | 3 | 0.45 | 0.54 | 0.38 |
| lattice-best-s1 | 1 | 0.55 | 0.60 | 0.52 |
| lattice-best-s2 | 2 | 0.55 | 0.56 | 0.54 |
| lattice-best-s3 | 3 | 0.39 | 0.67 | 0.39 |
kmeans:m=128 holds the distinct-token ratio roughly flat across the
stream; at half that budget (m=64) the worst seed sags — the problem
Tiered Memory fixes at half the m=128 budget. Frozen (no tuning), the
compressed memory pays a real perplexity tax at 2× context: validation CE
4.2283 for raw
k-means vs 2.6080 for
exact attention with RoPE extrapolation (see Training in the Loop for
how tuning closes this).