Edit forensics: reading back facts planted in a model

Someone takes an open model, rewrites one fact with ROME (PoisonGPT demonstrated this in 2023), and republishes it. This page measures how often a defender can name the planted fact from the weights, and whether that survives an attacker who tries to hide the edit. GPT-2 family, CounterFact facts, run locally on one laptop.

Who this is for. The threat is the middlemen between a lab and its users, not the lab's own files. On Hugging Face, Meta's original Llama-3.1-8B-Instruct got 4.88M downloads in the last 30 days, against about 1.26M for the eight biggest third-party copies combined (bartowski's quantized versions alone: 501k). Those few accounts are high-leverage targets, like npm or PyPI maintainers. No in-the-wild case of fact-editing tampering is known to us, so this is pre-emptive work.

99/99
tampered GPT-2 medium models where the planted subject ranked #1 of 20,391 names
82/99
where the planted object was read back exactly (first token)
0/99
where the subject was in the top 10 with no clean base (base-free signal)

1 · Clean attacker: one planted fact, defender holds the base

Each row is a batch of independently tampered checkpoints (one ROME edit each, random CounterFact facts). Rates after "edit took" are over the edits that actually worked.

modelmodelsedit tooklayer foundsubject #1object exactobject top-3relationbase-free top-10base-free median rank
gpt2-medium10099/100 (99%, 95% CI 95–100)99/99 (100%, 95% CI 96–100)99/99 (100%, 95% CI 96–100)82/99 (83%, 95% CI 74–89)92/99 (93%, 95% CI 86–97)29/99 (29%, 95% CI 21–39)0/99 (0%, 95% CI 0–4)163

2 · Attacker hides the edit under weight noise

After the edit, Gaussian noise goes on every weight matrix, scaled to that matrix's own spread. The defender truncates the diff to its top singular direction before scoring ("denoised").

Noise masking (GPT-2 medium)
0%25%50%75%100%0.0030.010.030.1noise scale (× each matrix's std)edit still works at 0.003: 97%edit still works at 0.01: 93%edit still works at 0.03: 93%edit still works at 0.1: 80%edit still workssubject #1 (denoised) at 0.003: 100%subject #1 (denoised) at 0.01: 100%subject #1 (denoised) at 0.03: 100%subject #1 (denoised) at 0.1: 100%subject #1 (denoised)object exact vs base at 0.003: 86%object exact vs base at 0.01: 86%object exact vs base at 0.03: 71%object exact vs base at 0.1: 4%object exact vs base
noise ×stdedit surviveslayer foundsubject #1 rawsubject #1 denoisedobject vs baseobject vs base, denoisedobject vs self-ablatedperplexity (base)
0.00329/30 (97%, 95% CI 83–99)29/29 (100%, 95% CI 88–100)29/29 (100%, 95% CI 88–100)29/29 (100%, 95% CI 88–100)25/29 (86%, 95% CI 69–95)25/29 (86%, 95% CI 69–95)—27.33 (27.26)
0.0128/30 (93%, 95% CI 79–98)28/28 (100%, 95% CI 88–100)28/28 (100%, 95% CI 88–100)28/28 (100%, 95% CI 88–100)24/28 (86%, 95% CI 69–94)24/28 (86%, 95% CI 69–94)—28.20 (27.26)
0.0328/30 (93%, 95% CI 79–98)28/28 (100%, 95% CI 88–100)28/28 (100%, 95% CI 88–100)28/28 (100%, 95% CI 88–100)20/28 (71%, 95% CI 53–85)20/28 (71%, 95% CI 53–85)—37.52 (27.26)
0.124/30 (80%, 95% CI 63–90)24/24 (100%, 95% CI 86–100)24/24 (100%, 95% CI 86–100)24/24 (100%, 95% CI 86–100)1/24 (4%, 95% CI 1–20)1/24 (4%, 95% CI 1–20)—949.68 (27.26)

3 · Attacker fine-tunes after editing

Whole-model fine-tune on WikiText (lr 2e-5, batch 4×128 tokens) after the edit, so every weight moves.

fine-tune stepsedit surviveslayer foundsubject #1 rawsubject #1 denoisedobject vs baseobject vs base, denoisedobject vs self-ablatedperplexity (base)
2515/15 (100%, 95% CI 80–100)15/15 (100%, 95% CI 80–100)15/15 (100%, 95% CI 80–100)15/15 (100%, 95% CI 80–100)12/15 (80%, 95% CI 55–93)12/15 (80%, 95% CI 55–93)13/15 (87%, 95% CI 62–96)21.98 (27.26)
10015/15 (100%, 95% CI 80–100)15/15 (100%, 95% CI 80–100)15/15 (100%, 95% CI 80–100)15/15 (100%, 95% CI 80–100)9/15 (60%, 95% CI 36–80)9/15 (60%, 95% CI 36–80)12/15 (80%, 95% CI 55–93)19.94 (27.26)
30012/13 (92%, 95% CI 67–99)12/12 (100%, 95% CI 76–100)12/12 (100%, 95% CI 76–100)12/12 (100%, 95% CI 76–100)8/12 (67%, 95% CI 39–86)8/12 (67%, 95% CI 39–86)10/12 (83%, 95% CI 55–95)18.76 (27.26)

4 · Attacker spreads the edit over five layers

MEMIT-style: the same output change split across layers 4–8, so no single layer carries it. The spread-aware defender sums normalised fire over every layer whose diff is rank-one.

edit took23/30 (77%, 95% CI 59–88)
all five layers flagged23/23 (100%, 95% CI 86–100)
subject #1, single-layer defender23/23 (100%, 95% CI 86–100)
subject #1, spread-aware defender23/23 (100%, 95% CI 86–100)
object exact, spread-aware19/23 (83%, 95% CI 63–93)

5 · Attacker plants many facts at once

N facts planted in one checkpoint at one layer. Precision@N: of the N names the update fires on hardest, how many were really planted.

Batch edits (GPT-2 medium)
0%25%50%75%100%1030100facts planted in one checkpointprecision@N at 10: 50%precision@N at 30: 41%precision@N at 100: 44%precision@Nobject exact at 10: 47%object exact at 30: 33%object exact at 100: 34%object exactedits that work at 10: 100%edits that work at 30: 96%edits that work at 100: 98%edits that work
Ntrialsedits that workprecision@Nobject exactperplexity (base)
103100%50%47%27.27 (27.26)
30396%41%33%27.29 (27.26)
100398%44%34%27.56 (27.26)

6 · Attacker adds decoys

One real fact plus 50 rank-one decoys of the same size on random other subjects, so the diff fires on 51 names. Stage 2 reads back every fired name and keeps the one whose read-back is strongest.

edit took20/20 (100%, 95% CI 84–100)
real subject #1 by fire alone1/20 (5%, 95% CI 1–24) · median rank 34.5
real subject #1 after stage-2 read-back12/20 (60%, 95% CI 39–78)
top read-back is the planted object10/20 (50%, 95% CI 30–70)

Limits