Instructions to use itsalissonsilva/Restriction-Learning-Experiment with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- PEFT
How to use itsalissonsilva/Restriction-Learning-Experiment with PEFT:
from peft import PeftModel from transformers import AutoModelForCausalLM base_model = AutoModelForCausalLM.from_pretrained("google/gemma-3-1b-it") model = PeftModel.from_pretrained(base_model, "itsalissonsilva/Restriction-Learning-Experiment") - Notebooks
- Google Colab
- Kaggle
Correct donor capture to exact generation-prefill patch sites; verify all 26 self-restorations
Browse files
interpretability_patchfix/patching.json
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
interpretability_patchfix/train_interpretability_patchfix_pilot.py
ADDED
|
@@ -0,0 +1,144 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Match donor capture to generation prefill, including the final layer before normalization."""
|
| 2 |
+
|
| 3 |
+
import importlib.util
|
| 4 |
+
import json
|
| 5 |
+
import re
|
| 6 |
+
from pathlib import Path
|
| 7 |
+
|
| 8 |
+
import torch
|
| 9 |
+
from huggingface_hub import HfApi, hf_hub_download
|
| 10 |
+
from peft import PeftModel
|
| 11 |
+
from transformers import AutoModelForCausalLM, AutoTokenizer
|
| 12 |
+
|
| 13 |
+
REPO = "itsalissonsilva/Restriction-Learning-Experiment"
|
| 14 |
+
REVISION = "3a3cb6bef28c71ffc822f0a68645e2adc4b6aed2"
|
| 15 |
+
ADAPTER_REVISION = "efc3f02e5775d3d8428b4961f1fd94aedc6ca34f"
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
def main():
|
| 19 |
+
torch.manual_seed(1729)
|
| 20 |
+
source = hf_hub_download(REPO, "train_restriction_pilot.py", revision=ADAPTER_REVISION)
|
| 21 |
+
spec = importlib.util.spec_from_file_location("helper", source)
|
| 22 |
+
helper = importlib.util.module_from_spec(spec)
|
| 23 |
+
spec.loader.exec_module(helper)
|
| 24 |
+
report = json.loads(
|
| 25 |
+
Path(hf_hub_download(REPO, "interpretability/results.json", revision=REVISION)).read_text()
|
| 26 |
+
)
|
| 27 |
+
tokenizer = AutoTokenizer.from_pretrained(helper.BASE, revision=helper.REVISION)
|
| 28 |
+
tokenizer.padding_side = "left"
|
| 29 |
+
base = AutoModelForCausalLM.from_pretrained(
|
| 30 |
+
helper.BASE,
|
| 31 |
+
revision=helper.REVISION,
|
| 32 |
+
torch_dtype=torch.bfloat16,
|
| 33 |
+
attn_implementation="eager",
|
| 34 |
+
).to("cuda")
|
| 35 |
+
model = PeftModel.from_pretrained(
|
| 36 |
+
base, REPO, subfolder="selective", revision=ADAPTER_REVISION
|
| 37 |
+
).eval()
|
| 38 |
+
layers = [(n, m) for n, m in model.named_modules() if re.search(r"\.layers\.\d+$", n)]
|
| 39 |
+
private = report["patch_pairs"]["private"]
|
| 40 |
+
public = report["patch_pairs"]["public"]
|
| 41 |
+
|
| 42 |
+
@torch.no_grad()
|
| 43 |
+
def generate(rows, max_tokens=80):
|
| 44 |
+
inputs = tokenizer(
|
| 45 |
+
[helper.render_prompt(tokenizer, r) for r in rows], padding=True, return_tensors="pt"
|
| 46 |
+
).to("cuda")
|
| 47 |
+
generated = model.generate(
|
| 48 |
+
**inputs,
|
| 49 |
+
max_new_tokens=max_tokens,
|
| 50 |
+
do_sample=False,
|
| 51 |
+
pad_token_id=tokenizer.pad_token_id,
|
| 52 |
+
)
|
| 53 |
+
texts = tokenizer.batch_decode(
|
| 54 |
+
generated[:, inputs["input_ids"].shape[1] :], skip_special_tokens=True
|
| 55 |
+
)
|
| 56 |
+
return [helper.grading(r, t) for r, t in zip(rows, texts, strict=True)]
|
| 57 |
+
|
| 58 |
+
def capture(rows):
|
| 59 |
+
stored = {}
|
| 60 |
+
handles = []
|
| 61 |
+
for i, (_, layer) in enumerate(layers):
|
| 62 |
+
|
| 63 |
+
def hook(module, args, output, index=i):
|
| 64 |
+
h = output[0] if isinstance(output, tuple) else output
|
| 65 |
+
if h.shape[1] > 1:
|
| 66 |
+
stored[index] = h[:, -1].detach().clone()
|
| 67 |
+
|
| 68 |
+
handles.append(layer.register_forward_hook(hook))
|
| 69 |
+
try:
|
| 70 |
+
generate(rows, 1)
|
| 71 |
+
finally:
|
| 72 |
+
for handle in handles:
|
| 73 |
+
handle.remove()
|
| 74 |
+
assert len(stored) == 26
|
| 75 |
+
return stored
|
| 76 |
+
|
| 77 |
+
hp, hc = capture(private), capture(public)
|
| 78 |
+
baseline = generate(private)
|
| 79 |
+
result = dict(
|
| 80 |
+
source_revision=REVISION,
|
| 81 |
+
source_patch_exclusion_reason="Earlier capture used ordinary forward position IDs and post-normalized final hidden state; superseded by identical generation-prefill hooks at every patch site",
|
| 82 |
+
patching=[],
|
| 83 |
+
patch_pairs=dict(
|
| 84 |
+
private=private,
|
| 85 |
+
public=public,
|
| 86 |
+
baseline_private=baseline,
|
| 87 |
+
baseline_public=generate(public),
|
| 88 |
+
),
|
| 89 |
+
seed=1729,
|
| 90 |
+
)
|
| 91 |
+
for layer in range(26):
|
| 92 |
+
delta = hc[layer].float() - hp[layer].float()
|
| 93 |
+
noise = torch.randn_like(delta)
|
| 94 |
+
noise *= delta.norm(dim=1, keepdim=True) / noise.norm(dim=1, keepdim=True).clamp_min(1e-20)
|
| 95 |
+
for donor_kind, donor in [
|
| 96 |
+
("public", hc[layer]),
|
| 97 |
+
("self_restoration", hp[layer]),
|
| 98 |
+
("random_norm_matched", hp[layer] + noise.to(hp[layer].dtype)),
|
| 99 |
+
]:
|
| 100 |
+
|
| 101 |
+
def patch(module, args, output):
|
| 102 |
+
h = output[0] if isinstance(output, tuple) else output
|
| 103 |
+
if h.shape[1] <= 1:
|
| 104 |
+
return output
|
| 105 |
+
replacement = h.clone()
|
| 106 |
+
replacement[:, -1] = donor
|
| 107 |
+
return (replacement, *output[1:]) if isinstance(output, tuple) else replacement
|
| 108 |
+
|
| 109 |
+
handle = layers[layer][1].register_forward_hook(patch)
|
| 110 |
+
try:
|
| 111 |
+
outputs = generate(private)
|
| 112 |
+
finally:
|
| 113 |
+
handle.remove()
|
| 114 |
+
matches = sum(o["raw"] == b["raw"] for o, b in zip(outputs, baseline, strict=True))
|
| 115 |
+
if donor_kind == "self_restoration":
|
| 116 |
+
assert matches == 8, (layer, matches)
|
| 117 |
+
result["patching"].append(
|
| 118 |
+
dict(
|
| 119 |
+
layer=layer,
|
| 120 |
+
donor=donor_kind,
|
| 121 |
+
outputs=outputs,
|
| 122 |
+
summary=helper.summarize(private, outputs),
|
| 123 |
+
raw_matches_private_baseline=matches,
|
| 124 |
+
)
|
| 125 |
+
)
|
| 126 |
+
print(
|
| 127 |
+
json.dumps(dict(stage="patching", layer=layer, self_restoration_matches=8)), flush=True
|
| 128 |
+
)
|
| 129 |
+
assert generate(private) == baseline
|
| 130 |
+
folder = Path("patchfix")
|
| 131 |
+
folder.mkdir(exist_ok=True)
|
| 132 |
+
(folder / "patching.json").write_text(json.dumps(result, indent=2))
|
| 133 |
+
(folder / "train_interpretability_patchfix_pilot.py").write_bytes(Path(__file__).read_bytes())
|
| 134 |
+
HfApi().upload_folder(
|
| 135 |
+
repo_id=REPO,
|
| 136 |
+
folder_path=str(folder),
|
| 137 |
+
path_in_repo="interpretability_patchfix",
|
| 138 |
+
commit_message="Correct donor capture to exact generation-prefill patch sites; verify all 26 self-restorations",
|
| 139 |
+
)
|
| 140 |
+
print(json.dumps(dict(stage="complete", layers=26, self_restoration_matches=208)), flush=True)
|
| 141 |
+
|
| 142 |
+
|
| 143 |
+
if __name__ == "__main__":
|
| 144 |
+
main()
|