|
Download LAB.md from skyuu72/kibble-pythia-1b: direct link, hf CLI and curl.
- Browser
- Download file 38.6 kB
-
https://huggingface.co/skyuu72/kibble-pythia-1b/resolve/main/LAB.md
- Command line
-
hf download hf://skyuu72/kibble-pythia-1b/LAB.md
-
curl -L -o LAB.md https://huggingface.co/skyuu72/kibble-pythia-1b/resolve/main/LAB.md
38.6 kB
| # LAB.md β writing facts into pythia-1b without training it | |
| ## STATE | |
| - phase: DONE | |
| - next action: none. read REPORT.md | |
| - experiments run: 21 of 30 (E00 = setup, not counted; E12 slot unused) | |
| - best method: Kibble = FD values + gated key neurons (E13). scale 0.763, seq 0.743, all-1000 with held-out 0.763. stop condition met at E13 | |
| - harness: FROZEN at the phase-0 commit. do not edit evals/. | |
| ## Ground rules I set for myself (assumptions, since nobody's around to ask) | |
| - **Model**: `EleutherAI/pythia-1b` (1012M params, fp16). Picked by bake-off (results/E00_model_select.json): | |
| TinyLlama_v1.1 (1.1B) knew almost nothing (QA 0.30, real-fact probes 0.16), gemma-3-1b is gated (401, and | |
| I'm not clicking through a licence for you), Qwen2.5-0.5B is half the size. pythia won on the things the | |
| judge needs: real-fact recall 0.72, in-context multi-hop 0.89. | |
| - **GPU**: RTX 4060 Laptop 8GB, CUDA, torch 2.14.1+cu126. ~1.8GB is already eaten by other apps, so the | |
| process is capped at 7GB via set_per_process_memory_fraction (OOM -> back off, never spill silently). | |
| - **"1000 facts, hold out 200"**: facts.json has 800 `eval` + 200 `heldout` = the 1000, plus 200 extra `tune` | |
| facts with different names. ALL tuning (hparams, hypernet training, probing) happens on `tune`. Scale sweep | |
| on eval is 1/10/100/800; the true 1000 point (800+200) only happens once, in the final report. | |
| - Tune facts only use ~70% of each answer pool, so anything fitted on tune can be checked on unseen answers | |
| (`target_seen_in_tune`). | |
| - **"No gradient training at edit time"**: I read it strictly for our own methods: no backprop through the | |
| target model when an edit is requested. Offline, one-time training of a small hypernetwork is allowed | |
| (the brief says so). The MEMIT baseline does optimise its value vectors by gradient descent, that's how it's | |
| published, and it's a baseline. | |
| - Edits are held as fp32 side deltas next to the fp16 weights (fp16 would round small updates away). Merging | |
| back into fp16 is tested as an attack in phase 4. | |
| - Eval batch size is 16 (measured peak 3.4GB at that size, start-at-4 was pointless for 20-token prompts). | |
| OOM handler halves it. | |
| - Experiment numbering: E00 is setup. Counting toward the 30 cap starts at E01. | |
| ## Phase 0 β the judge (done, frozen) | |
| Files: evals/spec.py (templates + pools), evals/build_data.py, evals/core.py (scoring), evals/harness.py (Judge). | |
| - 7 relations (birth city, citizenship, native language, company HQ, town->country, device maker, mineral | |
| element), 5 subject kinds, all subjects are syllable-soup names. Answer pools were filtered so the base model | |
| knows the second hop (city->country, country->capital, language->country, company->country, element->symbol). | |
| - Per fact: 1 direct prompt, 5 paraphrases (3 direct rewordings + 2 indirect with the subject buried in a | |
| story), 1 multi-hop prompt, 2 near-miss-name neighbours (a: same first word, b: same last word), 1 | |
| cross-relation probe on the same subject. | |
| - Global: 500 wikitext-2 prefixes for KL(base||edited), 68 QA items and 180 real same-relation facts the base | |
| model gets right (exact-match retention), perplexity on 10k wikitext-2 test tokens, 200 ARC-Easy questions. | |
| - Scoring: teacher-forced log-likelihood; "exact match" = greedy decode equals the target, which is the same | |
| as every teacher-forced argmax matching. Nothing reads generations. | |
| - Gotcha found while building: pythia answers "The capital of France is" with " the". Capital templates had to | |
| end in "is the city of". (0/74 -> 37/74 known.) | |
| **Sanity (results/E00_sanity.json, tune split, n=200)** | |
| | | eff | para | hop | loc KL | QA | real nbr | name bleed | ppl | ARC | | |
| |---|---|---|---|---|---|---|---|---|---| | |
| | no edit | 0.010 | 0.007 | 0.020 | 0 | 1.00 | 1.00 | 0.007 | 26.17 | 0.475 | | |
| | fact in context | 1.000 | 0.783 | 0.880 | 0 | 1.00 | 1.00 | 0.675 | 26.17 | 0.475 | | |
| Passes. Note the in-context baseline here is "oracle retrieval": the fact is prepended only to prompts about | |
| that fact (and to its near-miss neighbours, like a retriever fooled by a similar name would do β hence 0.675 | |
| bleed). Peak VRAM 3390MB. | |
| ## Experiment log | |
| ### E01 β causal tracing (results/E01_causal_trace.json) β phase 1 | |
| Plan: ROME-style tracing on 42 real facts pythia knows (6 per relation). Noise the subject embeddings | |
| (3x embedding std, 3 draws), restore one clean state, measure recovered p(answer). Residual per layer; | |
| mlp/attn outputs over a +-1 layer window. | |
| Result (clean p=0.47, corrupted p=0.03; numbers are recovered probability): | |
| - Residual at the **last subject token** carries the fact through layers 0-10 (0.28-0.37), then drops to | |
| 0.01 at layer 11. Residual at the **last prompt token** is ~0 until layer 10 and 0.33-0.43 from 11 on. | |
| It's a cliff, not a slope: the handoff happens in one place. | |
| - Attention at the last token does the moving: 0.31 at layer 10, 0.21 at 11-12, nothing elsewhere. | |
| - MLPs at the last subject token matter early: 0.23-0.24 at layers 0-1, 0.08-0.09 at 2-3, gone by ~6. | |
| - Other positions (first/middle subject tokens, tokens after the subject) barely register. | |
| Surprises: how early the MLP site is (layers 0-3 of 16; in GPT-2 XL it's the middle third), and how sharp | |
| the layer 10->11 handoff is. Caveat I believe: embedding noise inflates layer-0/1 MLP effects. | |
| Take-away for editing: write at the last subject token, somewhere in layers ~2-8. Anything written at or | |
| after layer 10 at the subject is too late to be read. | |
| (First run had a span bug for subjects not at the start of the prompt; fixed and rerun, numbers above are the rerun.) | |
| ### E02 β spectra (results/E02_spectra.json) β phase 1 | |
| Plan: singular values of every mlp/attn matrix; second moments of mlp keys (post-GELU, 8192-d) and mlp | |
| inputs (2048-d) over 200k wikitext-2 train tokens (cached in ckpt/ for the editors). | |
| Result: | |
| - Weights are close to full rank everywhere: mlp_out effective rank 1450-1780 of 2048, attention V/O about | |
| 1000-1200. There's no spare low-rank room sitting in the weights. | |
| - Key covariance: 99% of the energy needs ~7400 of 8192 directions (layer 0 is the odd one: 4700). So there | |
| is no real null space either. BUT by AlphaEdit's absolute rule (eigenvalue < 0.02) 66-91% of directions in | |
| layers 0-10 count as "null". Those two statements are both true: the spectrum is flat-ish and small, not | |
| empty. A null-space projector built that way throws away most of the key, not just the noise. | |
| - The mlp *input* covariance is absurdly lopsided in layers 0-1 (effective rank 27-29 of 2048) and opens up | |
| later (130 at L2, 300+ at L10-13). One or two directions (the mean) dominate, so anything keyed on the raw | |
| mlp input has to whiten first. | |
| Surprise: I expected a big dead subspace to hide edits in. There isn't one; "null space" here is a threshold. | |
| Peak VRAM 5418MB (eigendecompositions; first attempt hit the 7GB cap and backed off as designed). | |
| ## Pre-registered scoring for the stop condition (written before any editor was scored) | |
| - **Scale score** = mean(efficacy_em, para_em, hop_em) at N=800 (one batch). Also reported at 1/10/100. | |
| - **Sequential score** = the same mean on all 800 facts after 10 batches of 80, plus batch-1 efficacy after | |
| batch 10 (retention). | |
| - **"Without losing on locality"** vs the published editor, all of: loc_kl <= its loc_kl; loc_qa_em and | |
| nbr_real_em no more than one item worse (1/68, 1/180); ppl <= 1.005x its ppl; near-miss name bleed no more | |
| than 0.02 worse. | |
| - "The published editor" = MEMIT (E05). AlphaEdit (E06) is a second published baseline; a winner has to beat | |
| whichever of the two is better on each of scale and sequential. | |
| - Protocol (methods/run.py): scale N=1 is 10 separate single-fact edits averaged, N=10 is 3 disjoint groups | |
| averaged, N=100 and N=800 are one batch each. Sequential is 10 x 80 in the fixed eval order. | |
| - Editors only ever see {subject, direct prompt, target, statement, relation name} (values.req). Paraphrases, | |
| hops and neighbours are judge-only. | |
| ## Phase 3 hypotheses (all written before any phase-3 run; baselines E04-E06 were still in progress) | |
| Design: every editor = a **value source** (what should the subject's residual become?) x a **writer** (how do | |
| the weights produce it?), in methods/inventions.py. "oracle" value = MEMIT's gradient-optimised z*, used only | |
| to test writers in isolation. Everything else is forward-only at edit time. Step sizes come from a forward-only | |
| search (try 9 sizes, keep the smallest that makes the answer win on the edit prompt). | |
| - **M1 Echo (late-site unembedding write).** Skip subject enrichment. At layer 13 the last prompt token's MLP | |
| key already encodes who + what's asked; map that key to the answer's unembedding direction (through the | |
| final LayerNorm, closed form) with a least-squares write. *Predict:* direct-prompt efficacy >= 0.9 at N<=10; | |
| paraphrase < 0.2 (key = last-token state, changes with wording); hop ~ 0; collapses with scale because | |
| last-token keys of one template are near-collinear across subjects. | |
| - **M2 Graft (token-identity superposition).** Add the model's own layer-L representation of the answer token | |
| (centred) to the subject's residual at an early layer. Read-out heads partly copy token identity, and | |
| because the graft is early the subject-position MLPs in L+1..9 should enrich it with the answer's own | |
| attributes -> multi-hop for free. *Predict:* efficacy 0.3-0.7, hop above MEMIT's, cross-relation bleed high | |
| (the subject answers "Paris" to everything). | |
| - **M3 OV back-projection.** The direction the read-out heads (layers 10-12, from E01) turn into the answer | |
| token, in closed form: J^T(e_answer - e_mean) along LN -> V_h -> O_h -> final LN -> unembed, heads weighted | |
| by their measured attention to the subject. *Predict:* efficacy 0.6-0.9 (ignores MLPs 11-15), paraphrase a | |
| bit below MEMIT, cross-relation bleed worse than MEMIT (direction isn't relation-specific). | |
| - **M4 Gated key neurons (LCMV memory block).** One new neuron per fact beside layer L's MLP. Input weights = | |
| whitened matched filter C^-1 k / (k^T C^-1 k) (minimum-variance beamformer), saturating gate, output = the | |
| value. Pretrained weights untouched, every fact has its own slot. *Predict:* sequential retention 100%, | |
| locality KL ~ 0, flat with scale; weak spot = indirect paraphrases (context shifts the key, gate | |
| under-fires) and near-miss names that share the last token (gate fires, bleed b up). | |
| - **M5 HyperZ (amortised values).** A linear hypernetwork per relation: answer unembedding row -> residual | |
| delta, fitted offline on z* of tune facts. Forward-only at edit time. *Predict:* near-oracle on answers seen | |
| in tune, large drop on unseen answers (it will blend neighbouring answers). | |
| - **M6 Many tiny vs few concentrated.** Same value written at 1 layer vs spread over 5 vs 8. *Predict:* spread | |
| helps paraphrase a little for the least-squares writer; no effect on locality. | |
| - **M7 In-context transplant.** Use the subject's residual from a run where the fact was just stated before | |
| it. *Predict:* fails (efficacy <= 0.1). The answer isn't stored on the second mention of the subject, the | |
| last token reads it straight out of the context. | |
| Kill rule: tune-split score mean(eff, para, hop) under 0.3, or locality clearly worse than MEMIT with no | |
| upside -> dead, no scale/seq run. | |
| ### E03 β baselines (a) no edit and (b) fact in context β phase 2 | |
| Files: results/E03_NoEdit.json, E03_InContext_oracle.json, E03_InContext_all.json. | |
| Plan: run the standard protocol on the two "no weights touched" baselines. | |
| | N=800, one batch | eff | para | hop | loc KL | QA | real nbr | name bleed | ppl | ARC | | |
| |---|---|---|---|---|---|---|---|---|---| | |
| | no edit | 0.004 | 0.002 | 0.006 | 0 | 1.00 | 1.00 | 0.003 | 26.17 | 0.475 | | |
| | in context, oracle retrieval | 0.998 | 0.762 | 0.858 | 0 | 1.00 | 1.00 | 0.716 | 26.17 | 0.475 | | |
| - Oracle retrieval is flat with scale and with sequence (nothing accumulates), and it's the only thing so far | |
| that does multi-hop (0.86). Its weak spot is the confusable-retriever case: a near-miss name with the fact | |
| pasted in front picks up the answer 72% of the time. | |
| - **All facts pasted in front of everything** (the honest "just use the context window" version): N=1 eff | |
| 1.00 / para 0.76 / hop 0.80 but loc KL 0.17, QA 0.84, real-nbr 0.62 (one stray sentence already bends | |
| unrelated answers). N=10: eff 0.70, para 0.25, hop 0.10, QA 0.91. N=800 is 12.7k tokens and does not fit | |
| pythia's 2048 window. N=100 (1565 tokens) OOMed inside the frozen judge at batch 16; retry with judge | |
| batch 2 is queued. So context stuffing collapses between 1 and 10 facts on this model. | |
| Peak VRAM 3390MB (oracle), 4860MB (all-in-context, N<=10). | |
| ### E04 β baseline (c) LoRA β phase 2 | |
| Files: results/E04_LoRA.json (+ E04_LoRA_tune_*.json). | |
| Plan: hand-rolled LoRA r=8 on every attention + mlp linear in all 16 layers (4.6M params), AdamW. Tune lr x | |
| epochs on the tune split, then scale + sequential (same adapters keep training on each new batch, no replay). | |
| What happened while tuning (11 configs, tune split only): | |
| - Full-statement LM loss wrecked locality at every setting (KL 1.0-2.1 nats, QA retention 0.28-0.72), and | |
| *lower* learning rates were worse. I blamed fp16 gradient underflow; retrained in fp32 and got the same | |
| numbers to three digits. So that theory was wrong and the damage is real: 100 templated sentences are enough | |
| to drag the whole model toward the template. (Side effect of the detour: LoRA now trains in fp32 by | |
| upcasting in place, which is lossless both ways. MEMIT's z* was checked too: fp16 with loss scaling matches | |
| fp32 at cosine 0.998-1.000, so that stays in fp16.) | |
| - Switched to the standard editing-baseline loss (answer tokens only). Much better: tune eff 1.00 / | |
| para 0.93 / hop 0.34, but still loc KL 0.37, QA 0.90, real-nbr 0.65. Picked lr 3e-4, 10 epochs. | |
| Result (eval facts): | |
| | | eff | para | hop | loc KL | QA | real nbr | name bleed | ppl | ARC | | |
| |---|---|---|---|---|---|---|---|---|---| | |
| | N=1 | 1.00 | 0.98 | 0.00 | 0.47 | 0.69 | 0.41 | 1.00 | 28.5 | 0.46 | | |
| | N=10 | 1.00 | 0.92 | 0.30 | 0.36 | 0.86 | 0.49 | 0.92 | 28.6 | 0.455 | | |
| | N=100 | 1.00 | 0.92 | 0.35 | 0.29 | 0.88 | 0.66 | 0.65 | 32.0 | 0.475 | | |
| | N=800 | 1.00 | 0.95 | 0.20 | 0.75 | 0.68 | 0.42 | 0.68 | 48.2 | 0.44 | | |
| | sequential 10x80 | 0.24 | 0.20 | 0.02 | 1.23 | 0.40 | 0.06 | 0.16 | 89.5 | 0.395 | | |
| - Best paraphrase generalisation of anything so far (it beats the fact in context, 0.95 vs 0.76) and it never | |
| fails to memorise. But it has no notion of *which* subject: one edited fact and every near-miss name gets | |
| the same answer (bleed 1.00), and real same-relation facts drop to 0.41. | |
| - Sequentially it's catastrophic forgetting: batch-1 efficacy is 0.000 after batch 10, perplexity 26 -> 89. | |
| Peak VRAM 4160MB (fp32 training). Edit time 446s for the sequential 800. | |
| ### E05 β baseline (d) MEMIT β phase 2 | |
| Files: results/E05_MEMIT.json (+ 15 tune files E05_MEMIT_tune_A..O). | |
| Plan: faithful MEMIT (z* by 20 Adam steps on a residual delta with 6 lead-in contexts + KL term, then | |
| dW = R K^T (lam C + K K^T)^-1 spread over 5 layers), covariance from 200k wikitext-2 train tokens. Tune | |
| layers / lam on the tune split. | |
| Tuning notes: any range touching layers 1-3 with the raw covariance destroyed the model (loc KL 2-3.5 nats). | |
| Those layers' key covariances are near-singular (E02), so the solve puts huge gain on directions ordinary | |
| text does use a little. A 1% ridge on C fixes it. Lower lam helped efficacy. Winner: layers 2-6, lam 1500, | |
| ridge 0.01 (tune: eff 1.00 / para 0.83 / hop 0.35 / KL 0.005). | |
| | | eff | para | hop | loc KL | QA | real nbr | name bleed | ppl | ARC | | |
| |---|---|---|---|---|---|---|---|---|---| | |
| | N=1 | 1.000 | 0.840 | 0.300 | 0.0001 | 0.987 | 0.997 | 0.500 | 26.18 | 0.475 | | |
| | N=10 | 1.000 | 0.833 | 0.567 | 0.0006 | 0.980 | 0.991 | 0.533 | 26.17 | 0.465 | | |
| | N=100 | 0.990 | 0.774 | 0.420 | 0.0055 | 0.956 | 0.950 | 0.465 | 26.37 | 0.470 | | |
| | N=800 | 0.974 | 0.666 | 0.309 | 0.0491 | 0.941 | 0.822 | 0.397 | 27.65 | 0.440 | | |
| | sequential 10x80 | 0.664 | 0.416 | 0.237 | 0.0387 | 0.941 | 0.822 | 0.289 | 27.30 | 0.460 | | |
| - **Scale score 0.650, sequential score 0.439.** Batch-1 efficacy over time: 0.99, 0.78, 0.75, 0.70, 0.66, | |
| 0.62, 0.59, 0.61, 0.60, 0.61. Later batches overwrite earlier ones (final efficacy by batch: 0.61 ... 0.95). | |
| - Surprise: multi-hop is not zero (0.31-0.57). I predicted ~0 for every weight editor. | |
| - Weak spots: near-miss names pick up the edit 40-53% of the time, real same-relation facts fall to 0.82 at 800. | |
| - z* costs 0.5 s/fact (402 s for 800), peak VRAM 4001MB. z* is computed once on the pristine model and reused | |
| for the sequential run (it is an absolute target for the layer-6 residual, same assumption batch MEMIT makes). | |
| ### E06 β second published baseline: AlphaEdit β phase 2 | |
| Files: results/E06_AlphaEdit.json (+ 4 tune files). | |
| Plan: MEMIT's z*, but the update is projected onto the "null space" of preserved keys (eigenvalue < thr) | |
| and remembers earlier edit keys. Same layers 2-6. Tuned thr x L2 on tune; the published defaults (thr 0.02, | |
| L2 10) won. A tighter null space (thr 0.005 / 0.001) killed efficacy (0.75 / 0.01), which fits E02: there is | |
| no real null space in this model, only a threshold. | |
| | | eff | para | hop | loc KL | QA | real nbr | name bleed | ppl | ARC | | |
| |---|---|---|---|---|---|---|---|---|---| | |
| | N=100 | 0.990 | 0.780 | 0.460 | 0.006 | 0.985 | 0.950 | 0.460 | 26.36 | 0.465 | | |
| | N=800 | 0.969 | 0.686 | 0.309 | 0.057 | 0.897 | 0.806 | 0.399 | 27.88 | 0.445 | | |
| | sequential 10x80 | 0.950 | 0.680 | 0.319 | 0.054 | 0.912 | 0.839 | 0.391 | 27.84 | 0.470 | | |
| - **Scale score 0.655, sequential score 0.650.** Batch-1 efficacy after batch 10: 0.912. It really does fix | |
| MEMIT's sequential problem. Locality is slightly worse than MEMIT's. | |
| - Peak VRAM 5025MB. | |
| **THE BAR (from E05/E06):** scale score > 0.655, sequential score > 0.650, and locality no worse than the | |
| better of the two: loc KL <= 0.049 (scale) / 0.039 (seq), QA >= 0.926, real-nbr >= 0.833 (seq) / 0.816 | |
| (scale), ppl <= 27.44 (seq) / 27.79 (scale), name bleed <= 0.42. And for honesty: fact-in-context with a | |
| perfect retriever (0.873 on both, zero locality cost) is above all of them. Nothing in phase 2 beats (b). | |
| Footnote on E03: the 100-facts-all-in-context point is dead after 3 attempts (token cap, then OOM at judge | |
| batch 16, then OOM at judge batch 2 inside the frozen perplexity routine at 6.8GB). Logged as too big for | |
| the box, moving on. | |
| ### E07 β M1 Echo (late-site unembedding write) β KILLED | |
| File: results/E07_Composite_echo_tune.json. Run: value=unembed, site=last, L=13, least-squares writer. | |
| Result (tune, N=100): eff 0.65, para 0.20, hop 0.06, loc KL 0.002, real-nbr 0.81. | |
| vs prediction: paraphrase < 0.2 (got 0.20, right), hop ~ 0 (right). Efficacy I said >= 0.9 at N<=10; at N=100 | |
| it is already down to 0.65, which is the predicted scale collapse arriving early. The closed-form direction | |
| itself is fine (forward-only search made the answer win on 100% of facts with a step of 0.21|h|); the key is | |
| the problem. Last-token keys of one template are too alike to address 100 facts. Dead. | |
| ### E08 β M7 in-context transplant β KILLED | |
| File: results/E08_Composite_ctx_tune.json. Result: eff 0.01, para 0.008, hop 0.02. Exactly the predicted | |
| failure: the second mention of the subject does not carry the answer, the model reads it off the context. | |
| ### E09 β M2 Graft (token-identity superposition) β KEPT as an ingredient | |
| Files: results/E09_Composite_graft_L6_tune.json, _L3_tune.json, diag_rel_graft_L6.json, diag_rel_graft_stmt_L6.json. | |
| Result (tune, N=100): eff 0.57-0.59, para 0.34-0.35, **hop 0.66-0.68**, loc KL 0.016-0.026, bleed 0.26-0.29. | |
| vs prediction: efficacy 0.3-0.7 (right), hop above MEMIT's (right, and by a lot: 0.68 vs 0.35), cross-relation | |
| bleed high (WRONG, it's 0.00: the subject does not blurt the answer to unrelated questions). | |
| Per relation: works where the read-out can copy an entity (citizenship 0.85, HQ city 0.80, town 0.80, element | |
| 0.75), fails on native language (0.15) and device maker (0.15). Multi-hop is high everywhere (0.45-0.90). | |
| Layer 3 vs 6 makes no difference. Using the answer token as it sits in the stated fact instead of in bland | |
| carrier phrases: no real change. Ceiling check: MEMIT's own z* plus a graft of size 1.0|h| keeps efficacy | |
| ~0.94 and lifts hop from 0.34 to 0.62. | |
| So: not an editor on its own, but the only thing found that buys multi-hop in weights. It goes into the mix. | |
| ### E10 β M3 OV back-projection β MUTATED | |
| Files: results/E10_Composite_ov_L6_tune.json, _L8_tune.json. | |
| Result (tune): eff 0.43 / para 0.18 / hop 0.30 (L6); 0.39 / 0.17 / 0.27 (L8). | |
| vs prediction: I said 0.6-0.9 efficacy; it's 0.43. One attention-weighted direction is too crude. The | |
| directions are not useless though, which led to the mutation below. | |
| ### M8 (mutation of M2+M3, hypothesis written after E09/E10, before its harness runs) β FD value | |
| Honest timeline: I ran two tune-split diagnostics of this idea (diag_rel_pursuit_a.json, diag_rel_fd_a.json) | |
| before writing this paragraph, so it is a hypothesis informed by its own first look. | |
| Idea: MEMIT finds its value by backprop. Do the same search with forward passes only, inside a small | |
| dictionary: one closed-form read-out direction per attention head in layers 9-13 (40 of them, the E10 algebra | |
| without the attention weighting) plus two graft directions. Start at 0.75|h| of graft, then 6 rounds of: | |
| nudge along every dictionary direction, read the change in log p(answer) over 3 lead-ins, step along the | |
| implied slope with a forward-only line search, cap the step at 1.5|h|. | |
| Prediction: reaches most of MEMIT's efficacy, keeps part of the graft's multi-hop, costs more forward passes | |
| than MEMIT costs backward ones; fails where no head path exists (expected: weaker direct paraphrases). | |
| First look: log p(answer) -2.67 -> -0.12, 96% of facts above p=0.5; with MEMIT's writer per-relation direct | |
| 0.90-1.00, paraphrase ~0.76, hop ~0.48. Greedy one-direction-at-a-time pursuit was clearly worse | |
| (log p -1.10) and is dropped. Cost 1.4-1.5 s/fact vs 0.5 for z*. | |
| ### E11 β M4 gated key neurons (writer test with oracle z*) β tuning on tune split | |
| Files: results/E11_Composite_gate_*.json, E11_Composite_joint_*.json, diag_gate_L6_*.json. | |
| Diagnostics first (how hard does a fact's filter fire, layer 6): own key 1.00, paraphrases median 0.94-0.96, | |
| ordinary text max ~0.13 per neuron (95th pct 0.42). Ridge is irrelevant at this layer. The problem case is a | |
| name that shares the last token: median response **0.92**, indistinguishable from a paraphrase. That is also | |
| why MEMIT bleeds onto those names ~100% of the time (per-relation diag: nbr_b 0.5-1.0). | |
| - Plain filter, thresholds 0.5/0.7: eff 0.82, para 0.73, bleed 0.45. Efficacy is low because facts whose | |
| subjects end in the same word (company suffixes, device kinds) fire each other's neurons. | |
| - Five-layer version: worse (0.73 / 0.63). Dropped; one layer it is. | |
| - Zero constraints on made-up near-miss names (one subject token swapped): bleed 0.095, eff 0.98, para 0.78. | |
| - Joint solve (every filter also nulls every other stored fact, one linear system): a frontier between | |
| paraphrase and bleed. th 0.45/0.65: para 0.81 / bleed 0.23. th 0.35/0.55, eps 0.1: para 0.856 / bleed 0.39 / | |
| KL 0.0000 / QA 1.00 / real-nbr 0.994. th 0.25/0.45: para 0.87 / bleed 0.46. | |
| MEMIT on the same facts: para 0.826 / bleed 0.52 / KL 0.005 / QA 0.97. | |
| vs prediction: locality ~0 (right). "Indirect paraphrases under-fire" (WRONG: indirect 0.96, it is the direct | |
| rewordings that are weaker). "Same-last-token names fire the gate" (right, and fixable with nulls). | |
| Picked th 0.35/0.55, eps 0.1, 8 near-misses per fact for the full runs. | |
| ### E13 (tune) β FD values + gated neurons: the forward-only candidate | |
| File: results/E13_Composite_fd_gate.json. tune N=100: eff 0.95 / para 0.80 / hop 0.47 / KL 0.0000 / QA 1.00 / | |
| real-nbr 0.994 / bleed 0.365. Tune score 0.741 vs MEMIT's 0.725. No backprop anywhere, no pretrained weight touched. | |
| ### E11 β gated key neurons, oracle z* values: full protocol (results/E11_Composite.json) | |
| | | eff | para | hop | loc KL | QA | real nbr | name bleed | ppl | ARC | | |
| |---|---|---|---|---|---|---|---|---|---| | |
| | N=1 | 1.000 | 0.780 | 0.500 | 0.0000 | 1.000 | 0.994 | 0.450 | 26.17 | 0.475 | | |
| | N=10 | 1.000 | 0.820 | 0.567 | 0.0000 | 1.000 | 0.989 | 0.367 | 26.17 | 0.475 | | |
| | N=100 | 1.000 | 0.810 | 0.450 | 0.0002 | 1.000 | 0.994 | 0.330 | 26.17 | 0.475 | | |
| | N=800 | 0.999 | 0.835 | 0.420 | 0.0012 | 0.971 | 0.994 | 0.314 | 26.24 | 0.480 | | |
| | sequential 10x80 | 0.990 | 0.818 | 0.419 | 0.0006 | 0.971 | 0.983 | 0.320 | 26.21 | 0.480 | | |
| Scale score 0.751, sequential 0.742, batch-1 efficacy flat at 0.99 through all ten batches. Same values as | |
| MEMIT, different writer: paraphrase at 800 is 0.835 instead of 0.666, locality KL 40x lower. The writer is | |
| the reason MEMIT/AlphaEdit decay with scale, not the values. Peak VRAM 4566MB. | |
| ### E13 β FD values + gated key neurons ("Kibble"): full protocol (results/E13_Composite.json) | |
| Plan: the forward-only candidate, hyperparameters frozen from the tune split (L=6, read layers 9-13, | |
| graft start 0.75|h|, 6 FD rounds, cap 1.5|h|; gate th 0.35/0.55, 8 near-misses per fact, eps 0.1). | |
| | | eff | para | hop | loc KL | QA | real nbr | name bleed | ppl | ARC | | |
| |---|---|---|---|---|---|---|---|---|---| | |
| | N=1 | 1.000 | 0.760 | 0.600 | 0.0000 | 1.000 | 0.993 | 0.450 | 26.17 | 0.475 | | |
| | N=10 | 1.000 | 0.807 | 0.667 | 0.0000 | 1.000 | 0.987 | 0.367 | 26.17 | 0.475 | | |
| | N=100 | 0.970 | 0.796 | 0.580 | 0.0000 | 1.000 | 0.994 | 0.300 | 26.17 | 0.475 | | |
| | N=800 | 0.954 | 0.809 | 0.526 | 0.0149 | 1.000 | 0.983 | 0.299 | 26.35 | 0.475 | | |
| | sequential 10x80 | 0.941 | 0.790 | 0.499 | 0.0068 | 1.000 | 0.972 | 0.299 | 26.26 | 0.475 | | |
| - **Scale score 0.763 (AlphaEdit 0.655, MEMIT 0.650). Sequential score 0.743 (AlphaEdit 0.650, MEMIT 0.439).** | |
| Batch-1 efficacy over the ten batches: 0.97, 0.97, 0.97, 0.97, 0.99 ... 0.99 (it goes *up*: every re-solve | |
| nulls the new arrivals too). | |
| - Locality against the pre-registered bar: KL 0.015 / 0.007 (bar 0.049 / 0.039), QA 1.000 (bar 0.926), | |
| real-nbr 0.983 / 0.972 (bar 0.816 / 0.833), ppl 26.35 / 26.26 (bar 27.79 / 27.44), bleed 0.299 (bar 0.42). | |
| All passed. **Stop condition met at experiment 13.** | |
| - It loses to MEMIT on raw efficacy (0.954 vs 0.974 at 800) and on paraphrase at N=1 (0.76 vs 0.84). | |
| It wins paraphrase from N=100 up because it doesn't decay, and multi-hop everywhere (the graft). | |
| - Cost: values 1.53 s/fact forward-only (MEMIT's z*: 0.5 s/fact with backprop), writing 800 facts 61 s | |
| (MEMIT 135 s). Peak VRAM 4566MB. Search ends at mean log p(answer) = -0.13; 96% of facts above p=0.5. | |
| - Too-good check: the editor only sees values.req() fields; every knob was set on the tune split; phase 4 | |
| un-edit test checks the model goes back to bit-identical; held-out facts are still untouched. | |
| - Still below fact-in-context with a perfect retriever (0.873) on the headline score, because of multi-hop | |
| (0.53 vs 0.86). It does beat it on near-miss names (0.30 vs 0.72 bleed) and needs no retriever. | |
| M5 (hypernetwork) and M6 (spread vs concentrated, for the least-squares writer) were proposed but not run: | |
| the stop condition landed first. The one data point on M6: the gated writer spread over 5 layers was worse | |
| than on 1 (tune eff 0.73 vs 0.82, same thresholds). | |
| ## Phase 4 β attacking the winner (Kibble, E13 config) with AlphaEdit/MEMIT alongside | |
| ### E14 β adversarial paraphrases (results/E14_adv_Composite.json, E14_adv_AlphaEdit.json) | |
| Plan: edit all 800 eval facts, then hit 200 of them with prompts the judge doesn't have. Exact match: | |
| | attack | Kibble | AlphaEdit | | |
| |---|---|---| | |
| | plain direct prompt | 0.940 | 0.965 | | |
| | 60 tokens of unrelated wikitext first | 0.915 | 0.835 | | |
| | a different edited fact stated first | 0.845 | 0.685 | | |
| | "many people wrongly believe the answer is X" first | 0.930 | 0.915 | | |
| | two edited subjects named, then asked about one | 0.745 | 0.675 | | |
| | name lower-cased | 0.760 | 0.760 | | |
| | typo in the name (two letters swapped) | 0.510 | 0.570 | | |
| | judge's worst single paraphrase template | 0.640 | 0.585 | | |
| | answers with the *other* stated fact's answer (hijack, lower is better) | 0.085 | 0.175 | | |
| | fires on the last word of the name alone (bleed, lower is better) | 0.397 | 0.603 | | |
| Worst case for Kibble: **0.51 with a typo in the name** (0.64 if you only count well-formed prompts, the | |
| possessive "X's city of birth is" template). It's a key-addressed memory: break the key, lose the fact. | |
| AlphaEdit degrades the same way and is a bit more typo-tolerant (0.57), at the price of firing on the bare | |
| surname 60% of the time. | |
| ### E15 β contradicting facts the model already knows (results/E15_contra_*.json) | |
| Plan: take 56 real facts pythia gets right (8 per relation), overwrite each with a different answer. | |
| | | Kibble | AlphaEdit | | |
| |---|---|---| | |
| | new answer, direct | 0.964 | 1.000 | | |
| | old answer still given | 0.000 | 0.000 | | |
| | new answer, paraphrases | 0.829 | 0.779 | | |
| | multi-hop with the new answer | 0.321 | 0.482 | | |
| | 124 untouched real facts still right | 0.935 | 0.879 | | |
| | generic QA still right | 1.000 | 0.971 | | |
| Overwriting works. The multi-hop advantage disappears on real entities (0.32 vs 0.48): the graft has to fight | |
| what the model already believes about a famous subject, and loses more often than a gradient-found value. | |
| ### E16 β edit, then un-edit (results/E16_unedit_Composite.json, E16_unedit_MEMIT.json) | |
| - Kibble, 800 facts in, then block removed: sha256 over every parameter identical, max |log-prob diff| on 100 | |
| prompts = 0.0. **Bit-for-bit reversible**, because no pretrained weight is ever written (fingerprint is | |
| unchanged even while edited). | |
| - Forgetting ONE fact (drop its slot, re-solve addresses): that fact 0.00 on direct + paraphrases, the other | |
| 199 checked stay at 0.940. Per-fact deletion works. | |
| - MEMIT kept as a side delta is equally reversible. Merged into the fp16 weights and subtracted again (how it | |
| normally ships): 14.7M of 83.9M entries (17.5%) do not come back. Not reversible once merged. | |
| ### E17 β 4-bit quantisation after editing (results/E17_quant_Composite.json, E17_quant_AlphaEdit.json) | |
| Plan: edit 800 facts, then snap every attention/mlp Linear weight (and Kibble's memory block; for AlphaEdit | |
| the update is merged into the weight first) to simulated bitsandbytes NF4 (blocks of 64, 16 levels). | |
| Embeddings stay fp16. KL is measured against the fp16 unedited model, so the "no edit" row is the price of | |
| quantisation alone. | |
| | N=800 | eff | para | hop | loc KL | QA | real nbr | ppl | | |
| |---|---|---|---|---|---|---|---| | |
| | 4-bit, no edit | 0.004 | 0.002 | 0.006 | 0.077 | 0.912 | 0.906 | 27.92 | | |
| | Kibble fp16 | 0.954 | 0.809 | 0.526 | 0.015 | 1.000 | 0.983 | 26.35 | | |
| | Kibble 4-bit | 0.943 | 0.755 | 0.542 | 0.094 | 0.926 | 0.911 | (see json) | | |
| | AlphaEdit fp16 | 0.969 | 0.686 | 0.309 | 0.057 | 0.897 | 0.806 | 27.88 | | |
| | AlphaEdit 4-bit | 0.934 | 0.599 | 0.306 | 0.142 | 0.824 | 0.722 | 30.13 | | |
| Kibble survives: -0.011 efficacy, -0.054 paraphrase, and its locality lands on top of the unedited 4-bit | |
| model's (QA 0.926 vs 0.912, real-nbr 0.911 vs 0.906). AlphaEdit loses more (-0.035 / -0.087) and its locality | |
| damage stacks on top of quantisation's. Worst case for Kibble here: paraphrase 0.755. | |
| ### E18 β three fresh seeds (results/E18_Composite_seed1..3.json) | |
| Plan: a seed reshuffles the order of the 800 eval facts (so different batches, different N-subsets) and | |
| reseeds the near-miss sampling. Values are deterministic per fact, so they're reused from cache. | |
| | | eff | para | hop | loc KL | QA | real nbr | bleed | scale / seq score | batch-1 @10 | | |
| |---|---|---|---|---|---|---|---|---|---| | |
| | N=800 seed 0 (E13) | 0.954 | 0.809 | 0.526 | 0.015 | 1.000 | 0.983 | 0.299 | 0.763 | | | |
| | N=800 seed 1 | 0.953 | 0.805 | 0.525 | 0.004 | 1.000 | 0.983 | 0.302 | 0.761 | | | |
| | N=800 seed 2 | 0.956 | 0.809 | 0.516 | 0.004 | 0.985 | 0.983 | 0.296 | 0.760 | | | |
| | N=800 seed 3 | 0.951 | 0.803 | 0.522 | 0.004 | 1.000 | 0.983 | 0.298 | 0.759 | | | |
| | seq seed 0 (E13) | 0.941 | 0.790 | 0.499 | 0.007 | 1.000 | 0.972 | 0.299 | 0.743 | 0.988 | | |
| | seq seed 1 | 0.941 | 0.789 | 0.506 | 0.003 | 1.000 | 0.989 | 0.299 | 0.745 | 0.925 | | |
| | seq seed 2 | 0.936 | 0.786 | 0.505 | 0.004 | 1.000 | 0.989 | 0.294 | 0.742 | 0.975 | | |
| | seq seed 3 | 0.938 | 0.786 | 0.514 | 0.001 | 1.000 | 0.994 | 0.295 | 0.746 | 0.963 | | |
| Worst seed: scale 0.759, sequential 0.742, batch-1 retention 0.925. Spread is under 0.005 on the scores. | |
| The loc KL of 0.015 in E13 was the unlucky one (others 0.004): a few near-miss draws make a neuron twitchy. | |
| Caveat: the covariance sample and the model itself were not reseeded. | |
| ### E19 β ablations (results/E19_Composite_abl_*.json, tune split N=100; E19_Composite_fd_ls.json) | |
| | tune N=100 | eff | para | hop | bleed | note | | |
| |---|---|---|---|---|---| | |
| | full Kibble | 0.95 | 0.80 | 0.47 | 0.365 | | | |
| | no graft (head directions only, start from zero) | 0.87 | 0.61 | 0.51 | 0.320 | search ends at log p -0.44 instead of -0.11 | | |
| | graft only (no FD search) | 0.62 | 0.37 | 0.77 | 0.250 | | | |
| | no near-miss nulls | 0.95 | 0.81 | 0.48 | 0.500 | | | |
| - I had credited the graft with the multi-hop. Wrong, or at least incomplete: the head-direction search | |
| without any graft gets 0.51. What the graft start buys is a better optimum for the direct question | |
| (para 0.61 -> 0.80). Graft alone is still the multi-hop champion (0.77) and the worst editor. | |
| - The near-miss nulls cost nothing on paraphrase and cut name bleed from 0.50 to 0.37. | |
| - Writer ablation, full protocol (E19_Composite_fd_ls.json): the same FD values pushed through MEMIT's | |
| least-squares writer instead of the gate. N=800: eff 0.828 / para 0.554 / hop 0.316 / KL 0.124 / QA 0.824 / | |
| real-nbr 0.667 / ppl 29.3. Sequential: 0.505 / 0.288 / 0.194, batch-1 at 0.47. That is *worse* than MEMIT | |
| with its own z* (0.974 / 0.666 at 800). So the FD values are not as clean as gradient-found ones (they are | |
| twice as large, 1.47|h| vs a 0.75|h| clamp) and a linear writer smears that everywhere. The gate is what | |
| makes forward-only values usable. Put together with E11: the writer explains the scaling, not the values. | |
| ### E20 β where does it run out of room? (results/E20_stress_Composite.json) | |
| Plan: the judge's scale axis stops at 1000 facts and Kibble is flat there, so crowd the block instead: 800 | |
| real facts plus n decoy slots keyed on fresh made-up subjects with zero values, judge scores the 800. | |
| | extra slots | total slots | eff | para | hop | loc KL | QA | peak VRAM | | |
| |---|---|---|---|---|---|---|---| | |
| | 0 | 800 | 0.954 | 0.809 | 0.526 | 0.015 | 1.000 | 4548MB | | |
| | 1000 | 1800 | 0.954 | 0.810 | 0.530 | 0.012 | 1.000 | 5062MB | | |
| | 2000 | 2800 | 0.954 | 0.812 | 0.527 | 0.010 | 1.000 | 5587MB | | |
| | 3000 | 3800 | 0.954 | 0.813 | 0.530 | 0.010 | 1.000 | 6139MB | | |
| | 4000 | 4800 | OOM | | | | | 7GB cap | | |
| The facts don't interfere; the *solver* dies. One joint system over every stored key and near-miss is | |
| quadratic in memory, and at ~10k constraint rows (4800 slots + 6400 near-misses) it no longer fits beside the | |
| model in 7GB. So the collapse point on this box is a memory wall between 3800 and 4800 slots, not a quality | |
| slope. (Housekeeping: the run ended with a clean OOM while I was in the middle of killing it for looking | |
| stuck; I overwrote its json with a reconstruction from the log and then corrected the last row. Numbers for | |
| 0-3000 are identical to what the run printed.) | |
| ### E21 β held-out facts, once (results/E21_*.json) β FINAL | |
| Plan: everything frozen. For each method: edit the 200 held-out facts on a fresh model, then all 1000 | |
| (800 eval + 200 held-out) in one batch. One run per method, no reruns. | |
| | | 200 held-out: eff / para / hop / score | all 1000: eff / para / hop / score | all 1000: loc KL / QA / real nbr / bleed / ppl | | |
| |---|---|---|---| | |
| | no edit | 0.005 / 0.001 / 0.000 / 0.002 | 0.004 / 0.002 / 0.005 / 0.004 | 0 / 1.000 / 1.000 / 0.003 / 26.17 | | |
| | in context, perfect retriever | 0.995 / 0.779 / 0.825 / 0.866 | 0.997 / 0.766 / 0.851 / 0.871 | 0 / 1.000 / 1.000 / 0.718 / 26.17 | | |
| | LoRA | 1.000 / 0.936 / 0.360 / 0.765 | 1.000 / 0.924 / 0.280 / 0.735 | 0.600 / 0.706 / 0.306 / 0.654 / 44.40 | | |
| | MEMIT | 0.980 / 0.767 / 0.350 / 0.699 | 0.945 / 0.631 / 0.300 / 0.625 | 0.064 / 0.956 / 0.800 / 0.377 / 28.09 | | |
| | AlphaEdit | 0.980 / 0.777 / 0.365 / 0.707 | 0.949 / 0.641 / 0.296 / 0.629 | 0.077 / 0.912 / 0.744 / 0.378 / 28.41 | | |
| | z* + gated neurons | 0.995 / 0.839 / 0.420 / 0.751 | 0.998 / 0.836 / 0.423 / 0.752 | 0.0005 / 1.000 / 0.983 / 0.318 / 26.18 | | |
| | **Kibble** | 0.970 / 0.823 / 0.540 / 0.778 | 0.958 / 0.808 / 0.522 / 0.763 | 0.004 / 1.000 / 0.983 / 0.301 / 26.19 | | |
| The held-out numbers land where the eval numbers did (Kibble 0.763 at 800 eval facts, 0.763 at 1000). The | |
| ordering holds. The published editors keep sliding from 800 to 1000 (0.650 -> 0.625, 0.655 -> 0.629). | |
| Peak VRAM for the 1000-fact Kibble build: 5448MB. | |
| ## Final | |
| REPORT.md written, tables generated by methods/report_tables.py from the json files. 21 experiments used of | |
| 30 (E12 slot unused). STATE = DONE. | |
| ## After DONE: release packaging (not an experiment, no method change) | |
| Asked afterwards to make figures and put it on Hugging Face. Nothing in evals/ or the method was touched. | |
| - `methods/pack.py`: wrote all 1000 facts (800 eval + 200 held-out, E21 order, frozen E13 settings) and saved | |
| the slots as `kibble_pack.safetensors` (39MB). `kibble.py` is a standalone loader. Check: stock model + | |
| loader gives efficacy / paraphrase / multi-hop 0.958 / 0.808 / 0.522, identical to E21's all-1000 row. | |
| Peak VRAM 5448MB. results/pack_check.json. | |
| - Same script measured how hard each slot fires before the gate, for a figure. Gate opens on: own prompt | |
| 100%, paraphrases 97.2%, multi-hop prompt 99.1%, near-miss sharing the first word 6.1%, near-miss sharing | |
| the LAST word 71.1%, ordinary wikitext tokens (loudest of 1000 slots) 0.36%. The last-word number is the | |
| mechanism behind the 0.30 near-miss bleed and I had not looked at it this directly before: the slot keys on | |
| the final name token, so a shared surname gets most of the way to the key. This is a diagnostic on eval + | |
| held-out facts taken after everything was frozen; nothing was tuned on it. | |
| - `methods/figs.py` draws figures/*.png from results/*.json. README.md is the model card. | |
| - Assumption: I never saw the X post this was meant to match, so the figure set is my guess at the usual kit | |
| (headline card, method diagram, scale and forgetting curves, damage, attacks, limits). | |