itsalissonsilva commited on
Commit
8cb2b5d
·
verified ·
1 Parent(s): 3a3cb6b

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()