Unlearning & Knowledge Edit (v0.61.0)
GDPR right-to-be-forgotten as a first-class task. Surgical fact patches without retraining.
task: unlearn — remove specific data, keep the rest
Three methods, all wired as live trainers:
| Method | Loss shape | Needs reference model | Notes |
|---|---|---|---|
| NPO (Negative Preference Optimization) | DPO-shaped, negative-only | yes | Pair with a retain set; NPO and SimNPO warn loudly without one |
| SimNPO | Length-normalized NPO | no | Faster on long sequences |
| RMU (Representation Misdirection Unlearning) | Residual-stream noise on forget inputs | yes | Best for concept-level removal |
NPO and RMU each load a second, frozen copy of the base model next to the LoRA-wrapped policy (RMU runs that copy over each retain input when a retain set is given), so they cost a second full-precision model in memory. Only SimNPO is reference-free.
# soup.yaml
base: meta-llama/Llama-3.1-8B
task: unlearn
training:
epochs: 3
unlearn_method: simnpo
unlearn_alpha: 0.7
data:
train: ./private_data_to_remove.jsonl
forget_set: ./private_data_to_remove.jsonl
retain_set: ./general_knowledge.jsonl
output: ./unlearned_modeldata.train is required by the schema for every task, and soup train still loads and format-checks that file before the unlearn trainer starts (a missing path stops the run with Data file not found), but the unlearn trainer itself never uses its rows; it reads forget_set and retain_set. unlearn_alpha (0.0–10.0) is the retain-set weight, and it needs unlearn_method to be set. It defaults to unset, and an unset value resolves to 1.0 in the trainer, not to 0.5. An explicit unlearn_alpha: 0.0 is inside the documented range and validates, but the trainer treats it as falsy and also uses 1.0, so it cannot switch the retain term off. forget_set is required; retain_set is optional but strongly recommended for NPO.
soup eval unlearning — score TOFU / MUSE / WMDP evidence
soup eval unlearning <run-id> --benchmark tofu --evidence ./unlearn_evidence.json --output ./unlearn_report.jsonSoup does not run the TOFU, MUSE or WMDP benchmarks. The command loads no model. <run-id> is only a label, and the scores come from an evidence JSON that you produce with your own harness:
{
"forget_quality": {"pre_loss": 0.5, "post_loss": 2.5},
"model_utility": {"pre_acc": 0.70, "post_acc": 0.68},
"priv_leak": {"mia_auc": 0.52}
}Three metrics are scored, and --benchmark (tofu by default, or muse or wmdp) labels the report:
- Forget Quality — the pre/post forget-set loss change.
- Model Utility — retain accuracy preserved.
- PrivLeak — membership-inference AUC, scored by its distance from 0.5.
Scores roll up into an OK / MINOR / MAJOR verdict mirroring v0.56 diagnose, and the command exits 2 on MAJOR. --output writes the rendered report, and --attach-to-registry <id> attaches that file to a registry entry as an eval_results artifact (it needs --output; without it the attach is skipped with a notice).
A metric you leave out scores as a neutral OK, and with no --evidence at all the whole report is OK: every row reads "no evidence supplied; score is neutral default", Overall: OK, exit 0. That holds for --benchmark muse and wmdp as well. An all-green report without evidence means nothing was measured.
soup edit set — ROME / MEMIT / AlphaEdit
Surgically patch facts without retraining:
soup edit set --base meta-llama/Llama-3.1-8B-Instruct \
--method rome \
--subject "The capital of France is" \
--target "Lyon" \
--plan-only- ROME — Rank-One Model Editing, targets MLP layers (Meng 2022).
- MEMIT — Mass-Edit Memory in a Transformer (Meng 2023).
- AlphaEdit — gradient-based fact patching.
v0.61.0 shipped the plan / validation surface; live application landed in v0.71.9 — covariance-free rank-1 ROME/MEMIT/AlphaEdit kernels apply the edit (on a tiny model a ROME edit moved P("Lyon" | "The capital of France is") from 0.0016 → 0.96), guarded by a persistent SQLite EditGovernor. GRACE is the fourth method value, --method grace (the --help text still lists only the other three); see the GRACE section on RAG & steering.
--registry-id <id> is meant to attach the finished edit to a Registry entry (an id, id prefix or name:tag) as an edited_model artifact, or as grace_codebook for --method grace, and it only runs when --output is set. In v0.75.2 it never records anything: the command hands the Registry the output directory, the Registry accepts only a single file, and the attach fails with Could not attach to Registry: artifact not found: <dir>. The edit is already saved and the command still exits 0, so read the output rather than the exit code.
soup edit diff — which probe answers changed
soup edit diff before-run after-run \
--before-model ./model_before --after-model ./model_after \
--probes ./probes.jsonl --top-k 10Runs each probe prompt (JSONL rows with a prompt field, up to 1,000) through both models with greedy decoding and lists the prompts whose completion changed, changed rows first, up to --top-k (1 to 100). before-run and after-run are labels recorded in the report; they are not Registry lookups, and the --help text calling them "Registry run ids" overstates it. Without both --before-model and --after-model the command prints a placeholder table and exits 0: every row reads <supply --before-model + --after-model to generate> with changed: no, so a placeholder looks like "nothing changed". There is no citation visualizer. -o writes the report as JSON.
Sequential Edit Governor
Consulted by soup edit set (on by default; --no-governor opts out) and by the GRACE codebook edit. Nothing in soup train calls it. The state is a SQLite store per base model (SOUP_EDIT_GOVERNOR_DB moves it), and it fails open only for a store path that is refused or cannot be created: then the edit goes ahead with the governor disabled and a dim "Governor disabled" note. A corrupt database is not caught: soup edit set stops with DatabaseError: file is not a database and exit 1.
- Edit count overflow — refuses further edits on a base model once 50 have been recorded (
max_sequential_edits, exit 2). A separateauto_switch_atof 10 exists in the library as a ROME to AlphaEdit recommendation, but no command acts on it. - Norm blowup — each edit's measured Frobenius delta is classed OK, WARN (at 1.0 or more) or BLOWUP (at 5.0 or more). After a BLOWUP the governor refuses further edits on that base. It cannot stop the edit that caused the blowup, because the delta is only known after the edit has run.
Numbers
+193 new tests in v0.61.0 (9446 → 9571). Security: 5 HIGH, 11 MEDIUM, 11 LOW fixes.
See also
- Diagnose — pair unlearning with the 7-probe report card to verify the forget actually happened.
- RAG & steering — v0.62 control-vector steering, a soft alternative to surgical edits.
- Post-train x-rays (v0.66) —
soup probe sae-difflooks at Sparse Autoencoder feature movement, andsoup probe sleeperapplies a linear defection probe (its bundled weights are synthetic placeholders, so bring your own with--weights), a mechanistic view beyond Forget Quality / Model Utility / PrivLeak benchmark scores.
Soup is free and Apache-2.0. If it saved you a training run, starring the repo costs nothing and helps most. You can also fund the GPU time behind the work a 4 GB laptop cannot reach.