agu18dec commited on
Commit
bc681ad
·
verified ·
1 Parent(s): 82ccf34

Add the lens-A (whitened) AR, the frozen whitening basis (mu, W) and olens.py load_whitening / whiten / unwhiten

Browse files
ar_whitened/export.json ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "final_olens_lensB/ar_whitened",
3
+ "ckpt": "ar.whiten.lr7e-5.decay171155/milestone_step_205386",
4
+ "run": "ar.whiten.lr7e-5.decay171155",
5
+ "n_tensors": 994,
6
+ "n_params": 142947328,
7
+ "overrides": "kind=ar name=ar.whiten.lr7e-5 ar.space=whiten training.lr=7e-05 data.train_dir=/data/pairs/w0,/data/pairs/w1,/data/pairs/w2 data.val_dir=/data/rulers/ar grad_ckpt=False compile=True training.num_workers=16 training.val_batch_size=64 data.basis_path=/data/basis.safetensors training.train_batch_size=72 training.gradient_accumulation_steps=1 effective_batch=576 training.max_steps=213944 wandb_id=ar.whiten.lr7e-5.r1 name=ar.whiten.lr7e-5.decay171155 wandb_id=ar.whiten.lr7e-5.decay171155 branch_from=/runs/ar.whiten.lr7e-5/milestone_step_171155 decay_frac=0.2"
8
+ }
ar_whitened/model.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:b028bbf699192e66cc07efb2e574fa32402c255daa2c97084b94cbce956463ac
3
+ size 571936808
olens.py CHANGED
@@ -6,6 +6,10 @@ Self-contained (transformers + peft + safetensors + huggingface_hub). Three piec
6
  ``ar_direction``), used to score readouts and as the lens's RL reward;
7
  - ``residual``: capture the subject model's residual for a chat turn.
8
 
 
 
 
 
9
  from olens import from_hub, load_lens, load_ar, residual, read, ar_direction
10
  lens = load_lens(from_hub("agu18dec/final_olens", "lens_rl_step600"))
11
  h = residual(lens, "Who wrote the opera Carmen?", layer=40) # [5120]
@@ -13,6 +17,12 @@ Self-contained (transformers + peft + safetensors + huggingface_hub). Three piec
13
  ar = load_ar(from_hub("agu18dec/final_olens", "ar"))
14
  print(torch.cosine_similarity(ar_direction(ar, "Bizet", 40), h.float().cuda(), dim=0))
15
 
 
 
 
 
 
 
16
  Conventions (must match training):
17
  - A residual at layer L is the output of decoder block L (0..63), before the final norm.
18
  - Text is read with the chat template and enable_thinking=False.
@@ -38,12 +48,13 @@ PROMPT = (
38
  TAG = "[Layer {n:02d}]" # the AR's layer prefix (6 tokens)
39
 
40
 
41
- def from_hub(repo: str, folder: str, revision: str | None = None) -> str:
42
- """Download one checkpoint folder (``<folder>/model.safetensors``) from the Hub; returns the
43
- local path to pass to ``load_lens`` / ``load_ar``."""
 
44
  from huggingface_hub import hf_hub_download
45
 
46
- return hf_hub_download(repo, f"{folder}/model.safetensors", revision=revision)
47
 
48
 
49
  def _lora() -> LoraConfig:
@@ -172,3 +183,39 @@ def ar_direction(ar: AR, text: str, layer: int, max_tokens: int = 32) -> torch.T
172
  x = torch.tensor([tag + span], device=next(ar.parameters()).device)
173
  hidden = ar.backbone(input_ids=x).last_hidden_state
174
  return ar.head(hidden[0, -1].float())
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6
  ``ar_direction``), used to score readouts and as the lens's RL reward;
7
  - ``residual``: capture the subject model's residual for a chat turn.
8
 
9
+ Also published: the lens-A AR, trained in WHITENED space (``ar_whitened``, same architecture, so
10
+ ``load_ar`` loads it), and the frozen per-layer whitening basis it was trained with
11
+ (``whitening/basis.safetensors``: ``load_whitening``, ``whiten``, ``unwhiten``).
12
+
13
  from olens import from_hub, load_lens, load_ar, residual, read, ar_direction
14
  lens = load_lens(from_hub("agu18dec/final_olens", "lens_rl_step600"))
15
  h = residual(lens, "Who wrote the opera Carmen?", layer=40) # [5120]
 
17
  ar = load_ar(from_hub("agu18dec/final_olens", "ar"))
18
  print(torch.cosine_similarity(ar_direction(ar, "Bizet", 40), h.float().cuda(), dim=0))
19
 
20
+ wb = load_whitening(from_hub("agu18dec/final_olens", "whitening",
21
+ filename="basis.safetensors"), layers=[40]) # z = (x - mu_L) W_L
22
+ ar_w = load_ar(from_hub("agu18dec/final_olens", "ar_whitened"))
23
+ z_pred = whiten(ar_direction(ar_w, "Bizet", 40).cpu(), 40, wb)
24
+ print(torch.cosine_similarity(z_pred, whiten(h.cpu(), 40, wb), dim=0)) # its training metric
25
+
26
  Conventions (must match training):
27
  - A residual at layer L is the output of decoder block L (0..63), before the final norm.
28
  - Text is read with the chat template and enable_thinking=False.
 
48
  TAG = "[Layer {n:02d}]" # the AR's layer prefix (6 tokens)
49
 
50
 
51
+ def from_hub(repo: str, folder: str, revision: str | None = None,
52
+ filename: str = "model.safetensors") -> str:
53
+ """Download one file of a checkpoint folder (default ``<folder>/model.safetensors``) from the
54
+ Hub; returns the local path to pass to ``load_lens`` / ``load_ar`` / ``load_whitening``."""
55
  from huggingface_hub import hf_hub_download
56
 
57
+ return hf_hub_download(repo, f"{folder}/{filename}", revision=revision)
58
 
59
 
60
  def _lora() -> LoraConfig:
 
183
  x = torch.tensor([tag + span], device=next(ar.parameters()).device)
184
  hidden = ar.backbone(input_ids=x).last_hidden_state
185
  return ar.head(hidden[0, -1].float())
186
+
187
+
188
+ class Whitening:
189
+ """The frozen per-layer whitening basis: ``mu`` [64, 5120] and ``w[L]`` [5120, 5120] (fp32),
190
+ ``W_L = (Sigma_L + lam_L I)^(-1/2)`` with ``lam_L = 0.1 * mean(diag Sigma_L)``; ``W_L`` is
191
+ symmetric. Fit once on 1,310,720 residuals per layer (on-policy chat replies) and never refit.
192
+ """
193
+
194
+ def __init__(self, mu: torch.Tensor, w: dict[int, torch.Tensor]) -> None:
195
+ self.mu, self.w = mu, w
196
+ self._inv: dict[int, torch.Tensor] = {}
197
+
198
+
199
+ def load_whitening(path: str, layers: list[int] | None = None, device: str = "cpu") -> Whitening:
200
+ """Load the basis; ``layers`` limits which ``W_L`` are read (105 MB each; all 64: 6.7 GB)."""
201
+ from safetensors import safe_open
202
+
203
+ with safe_open(path, framework="pt") as f:
204
+ mu = f.get_tensor("mu").float().to(device)
205
+ sl = f.get_slice("w")
206
+ w = {lay: sl[lay].float().to(device) for lay in (layers or range(N_LAYERS))}
207
+ return Whitening(mu, w)
208
+
209
+
210
+ def whiten(x: torch.Tensor, layer: int, wb: Whitening) -> torch.Tensor:
211
+ """``z = (x - mu_L) W_L`` for residual(s) ``x`` [..., 5120] taken at ``layer``."""
212
+ w = wb.w[layer]
213
+ return (x.float().to(w.device) - wb.mu[layer]) @ w
214
+
215
+
216
+ def unwhiten(z: torch.Tensor, layer: int, wb: Whitening) -> torch.Tensor:
217
+ """The inverse map, ``x = z W_L^(-1) + mu_L`` (``W_L^(-1)`` computed once in fp64, cached)."""
218
+ if layer not in wb._inv:
219
+ wb._inv[layer] = torch.linalg.inv(wb.w[layer].double()).float()
220
+ inv = wb._inv[layer]
221
+ return z.float().to(inv.device) @ inv + wb.mu[layer]
whitening/basis.json ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "rows": 1310720,
3
+ "ridge": 0.1,
4
+ "n_shards": 16,
5
+ "sha256_16": "8d05c083386b6924",
6
+ "source": "/data/basis.safetensors",
7
+ "tensors": {
8
+ "mu": [
9
+ [
10
+ 64,
11
+ 5120
12
+ ],
13
+ "torch.float32"
14
+ ],
15
+ "w": [
16
+ [
17
+ 64,
18
+ 5120,
19
+ 5120
20
+ ],
21
+ "torch.float32"
22
+ ]
23
+ }
24
+ }
whitening/basis.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:61db172b0841eebf6124ea025f6f667c2c72d68d60b4504987127261f4866d35
3
+ size 6712197280