ernlavr/fine_grained_unlearning
Fine-Grained Knowledge Unlearning — Namesake Benchmark A benchmark for fine-grained knowledge unlearning: can a method remove a fact about entity X without damaging the same fact on entity Y, when X and Y have (near-)identical names and share exactly that one attribute? Each sample is a pair of real people who share an identical or near-identical name, share one career element (e.g. both are basketball players) — the fact to unlearn on X and retain on Y, differ on everything… See the full description on the dataset page: https://huggingface.co/datasets/ernlavr/fine_grained_unlearning.
Fine-Grained Knowledge Unlearning — Namesake Benchmark
A benchmark for fine-grained knowledge unlearning: can a method remove a fact about entity X without damaging the same fact on entity Y, when X and Y have (near-)identical names and share exactly that one attribute?
Each sample is a pair of real people who
- share an identical or near-identical name,
- share one career element (e.g. both are basketball players) — the fact to unlearn on X and retain on Y,
- differ on everything else (birth year, birthplace, teams, works, …) — the disambiguators used to point a model at the right namesake,
- are approximately equally famous (Wikipedia pageview ratio ≤ 20, both ≥ 1,500 annual views, plus a probe-model log-prob balance term).
The point is that a coarse unlearning method will delete "the Michael Jordan who plays basketball" rather than "this Michael Jordan", and the retain side measures exactly that collateral damage.
Example. Unlearn "James Allen (b. 1864) → writer", then check that "James Lane Allen, born in 1849, is a ___" still yields writer.
Files
from datasets import load_dataset
ds = load_dataset("ernlavr/fine_grained_unlearning") # the 247 pairs
scored = load_dataset("ernlavr/fine_grained_unlearning", "scored") # all 1,701 scoredunlearning_dataset.json holds nested records, so fetch it as a file:
import json
from huggingface_hub import hf_hub_download
p = hf_hub_download("ernlavr/fine_grained_unlearning",
"unlearning_dataset.json", repo_type="dataset")
records = json.load(open(p)){
"case_id": 0,
"pair_id": "Q1371577_Q3297881",
"unlearn": {"subject", "prompt", "disambiguated_prompt", "paraphrases", "target"},
"retain": {"subject", "prompt", "paraphrases", "target"},
"disambiguation_check": {"x": {"prompt", "target"}, "y": {"prompt", "target"}},
"metadata": {"qid_x", "qid_y", "name_match_type", "career_category",
"family_relation", "fame_ratio", "scores": {...}}
}Schema (dataset.csv)
Identity — pair_id, qid_x, qid_y (Wikidata QIDs), name_x, name_y, description_x, description_y, birth_year_x, birth_year_y.
Pair properties — name_match_type (see below), shared_occupation (the fact to unlearn/retain), career_category, family_relation.
Fame — pageviews_x, pageviews_y (12-month enwiki, 2025-07 → 2026-07), sitelinks_x, sitelinks_y, fame_ratio (max/min pageviews).
Prompts — unlearn_prompt (bare name), unlearn_prompt_disambiguated, unlearn_target, retain_prompt, retain_target, hint_attr, hint_x, hint_y, probe_attr, probe_{x,y}_prompt, probe_{x,y}_target, probe_options.
Probe scores — know_{x,y}, margin_{x,y}, shared_{x,y}, shared_ambig, shared_margin_{x,y}, balance, quality.
Two gotchas:
probe_optionsis a JSON-encoded string —json.loadsit. It holds every candidate[attr, value_x, value_y]triple; the pipeline picked the one with the best worst-case margin and exposed it asprobe_attr.family_relationis an empty string when absent (pandas reads it as NaN). Filter on specific values. `different_from` is not a family relation — it is Wikidata's explicit distinctness marker (P1889), i.e. positive evidence the two items are different people. Of the 247 pairs: 41 are blood/marital relatives (child22,father14,sibling4,spouse1), 23 aredifferent_from, 183 unmarked. Drop the 41 if a shared family context would confound your experiment.
Score definitions
All scores are mean per-token log-probs of the gold continuation under the probe model (meta-llama/Llama-3.1-8B, 39,943 continuations scored).
The published 247 were cut with margin_{x,y} > 0 and shared_margin_{x,y} > 0, ranked by quality, capped at 2 pairs per unlearn entity. See Limitations — that threshold is too loose, and you probably want to re-cut.
Name closeness (name_match_type)
Composition
Career category — literature 45, football 38, basketball 36, acting 35, music 32, politics 27, filmmaking 9, visual_art 5, baseball 4, law 3, then a tail of 11 categories with ≤ 2 pairs each.
Probe attribute — birthplace 186, citizenship 35, team 14, birth_year 10, work 2.
Fame balance — fame_ratio mean 4.86, median 2.85, max 19.56; the least viewed entity has 1,505 annual pageviews.
⚠️ Limitations — read before using
An audit of the published 247 pairs generated greedily from the probe model and scored the output with a synonym map (so "jurist" counts for lawyer). The `usable` figures below are that heuristic, not human-verified ground truth — treat them as directional.
~45% of the 247 pairs fail the benchmark's core premise, i.e. the model does not demonstrably hold the fact you are asking a method to unlearn:
- both occupations produced correctly: 150/247 (61%)
- cross-contamination (X's generation leaks Y's attribute or vice versa): 9.3% — low; the disambiguation hints do work
- usable (occupation right for both and no leak): 136/247 (55%)
- for 37 pairs (15%) the model gets the occupation wrong for both people
The published `> 0` threshold is the cause, and it is cheap to fix. A stricter cut on the same log-prob columns raises quality sharply, and because dataset_scored.csv ships all 1,701 scored pairs, re-cutting needs no GPU:
import pandas as pd
s = pd.read_csv("dataset_scored.csv")
smin = s[["shared_margin_x", "shared_margin_y"]].min(axis=1)
mmin = s[["margin_x", "margin_y"]].min(axis=1)
strict = s[(smin > 2) & (mmin > 0.5)] # 69 pairsOf the three log-prob signals, shared_margin is the best predictor of a usable pair (correlation 0.352), ahead of margin (0.160). A "regret" metric (logp(gold) − logp(model's own greedy continuation)) was also tested and did worse (0.244), so it is not included.
A residual ~20% is a data problem no threshold can fix. Wikidata's P106 (occupation) includes peripheral tags, so the "shared career" is sometimes one neither person is actually known for — John Smith the explorer is tagged `writer`; William Clark of Lewis & Clark is tagged `politician`. This is strongly category-dependent:
Literature is both the largest category (45 pairs) and near the bottom — writer is Wikidata's worst catch-all. A cheap extra filter is to require shared_occupation to appear in both Wikidata one-line descriptions (description_x/description_y), which splits usability 62% vs 44%.
Other caveats. The fame filter is purely relative (ratio ≤ 20) with no absolute ceiling, so 8 pairs have pageviews_x > 1M — including Donald Trump / Donald Trump Jr. under businessperson, a career neither is primarily known for. Scores are specific to Llama-3.1-8B; re-probe for another model. English Wikipedia only, so fame and coverage are anglophone-biased, and the underlying Wikidata gender/occupation distribution is inherited unchanged.
Provenance
Built from Wikidata (CC0) via WDQS SPARQL, plus the Wikimedia Pageviews API for fame estimation (12 months, 2025-07-01 → 2026-07-01). Pipeline: mine humans per occupation (≥ 5 sitelinks) → pair by name closeness within an occupation → fetch attributes and family links → filter for fame balance, single shared career and sufficient disambiguators → score with Llama-3.1-8B and cut.
11,024 candidate pairs → 5,168 after a sitelink prefilter → 1,701 after attribute/fame filtering → 247 published.
Licence
CC0-1.0, following Wikidata. Pageview counts are aggregate Wikimedia statistics. All entities are public figures with English Wikipedia articles; every attribute is drawn from public Wikidata statements.
