File size: 5,470 Bytes
e418394
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
"""Gap analysis: transport quality vs distance on Ouro, using mid-band targets
(v120, v132) to place proven-content sources (loop-2 band v72-88) at distances
32-60, plus the loop-end lenses (t143, t191) for the rest of the curve.

Metrics (matched to qwen_overlay.py): ||J||, state-space cosine J h_v vs h_t,
KL and top-1 agreement against the target layer's own readout. Plus the
semantic test: does the transported loop-2 band state carry the Italy
direction at the mid-band target?
"""

import json

import torch
import torch.nn.functional as F

from jlens.hooks import ActivationRecorder
from jlens.lens import JacobianLens
from ouro_lens import from_ouro, load_ouro

torch.set_grad_enabled(False)

hf, tok = load_ouro()
hf = hf.cuda()
model = from_ouro(hf, tok)
NP, V = model.n_physical_layers, model.n_layers
d = model.d_model

LENSES = {
    120: "jacobian_lens_ouro_mid_t120.pt",
    132: "jacobian_lens_ouro_mid_t132.pt",
    143: "jacobian_lens_ouro-2.6b_t143.pt",
    191: "jacobian_lens_ouro-2.6b.pt",
}
lenses = {t: JacobianLens.load(p) for t, p in LENSES.items()}
J = {t: {v: lenses[t].jacobians[v].cuda() for v in lenses[t].source_layers}
     for t in LENSES}
for t in LENSES:
    print(t, lenses[t], flush=True)

with open("prompts_1000.json") as f:
    eval_prompts = json.load(f)[980:1000]

results = {"norms": {}, "curves": {}}
for t in LENSES:
    results["norms"][t] = {v: (lenses[t].jacobians[v].norm().item() / d**0.5)
                           for v in lenses[t].source_layers}

acc = {t: {"cos": {}, "kl": {}, "agree": {}} for t in LENSES}
n_pos = 0
for pi, p in enumerate(eval_prompts):
    ids = model.encode(p, max_length=128)
    with ActivationRecorder(model.layers, at=list(range(V))) as rec:
        model.forward(ids)
    acts = {v: rec.activations[v].detach()[0] for v in range(V)}
    seq = ids.shape[1]
    positions = list(range(16, seq - 1, 8))
    n_pos += len(positions)
    for t in LENSES:
        h_t = acts[t][positions].float()
        t_logits = model.unembed(h_t).float()
        t_lsm = t_logits.log_softmax(-1)
        t_top = t_logits.argmax(-1)
        for v in J[t]:
            h = acts[v][positions].float()
            th = h @ J[t][v].T
            c = F.cosine_similarity(th, h_t, dim=-1).sum().item()
            lg = model.unembed(th).float()
            k = F.kl_div(lg.log_softmax(-1), t_lsm, log_target=True,
                         reduction="sum").item()
            a = (lg.argmax(-1) == t_top).float().sum().item()
            acc[t]["cos"][v] = acc[t]["cos"].get(v, 0) + c
            acc[t]["kl"][v] = acc[t]["kl"].get(v, 0) + k
            acc[t]["agree"][v] = acc[t]["agree"].get(v, 0) + a
    print(f"prompt {pi + 1}/20", flush=True)

for t in LENSES:
    results["curves"][t] = {
        m: {v: acc[t][m][v] / n_pos for v in acc[t][m]} for m in acc[t]
    }

print("\n=== state-space cosine (J h_v vs h_t) by distance ===", flush=True)
print("dist |  t120  t132  t143  t191", flush=True)
for dist in (1, 4, 8, 16, 24, 32, 40, 44, 48, 56, 60, 72):
    row = []
    for t in LENSES:
        v = t - dist
        row.append(f"{results['curves'][t]['cos'].get(v, float('nan')):5.2f}"
                   if v in results["curves"][t]["cos"] else "  -  ")
    print(f" {dist:3d} | " + "  ".join(row), flush=True)

print("\n=== loop-2 band (v72-88, proven content) through each target ===", flush=True)
for t in (120, 132, 143, 191):
    cs = [results["curves"][t]["cos"][v] for v in range(72, 89)
          if v in results["curves"][t]["cos"]]
    dists = f"{t-88}-{t-72}"
    print(f"  ->t{t}: dist {dists:7s} mean cos {sum(cs)/len(cs):.3f}", flush=True)

# ---- semantic test: Italy through the mid-band transport ----
prompt = "Fact: The currency used in the country shaped like a boot is"
ids = model.encode(prompt)
with ActivationRecorder(model.layers, at=list(range(V))) as rec:
    model.forward(ids)
acts = {v: rec.activations[v].detach()[0] for v in range(V)}
pos = -2
U = hf.lm_head.weight.detach().float().cuda()
tok_italy = tok(" Italy", add_special_tokens=False).input_ids[0]
tok_ctrl = tok(" France", add_special_tokens=False).input_ids[0]

print("\n=== Italy-direction projection at mid-band targets (boot position) ===", flush=True)
for t in (120, 132):
    d_it = J[143][t].T @ U[tok_italy]
    d_it = d_it / d_it.norm()
    d_fr = J[143][t].T @ U[tok_ctrl]
    d_fr = d_fr / d_fr.norm()
    h_t = acts[t][pos].float()
    ref_it, ref_fr = float(h_t @ d_it), float(h_t @ d_fr)
    print(f"  target v{t}: actual state  proj_Italy={ref_it:7.1f}  proj_France={ref_fr:7.1f}", flush=True)
    for v in (72, 76, 80, 84, 88):
        th = J[t][v] @ acts[v][pos].float()
        print(f"    from v{v} (dist {t-v:2d}): proj_Italy={float(th @ d_it):7.1f}  "
              f"proj_France={float(th @ d_fr):7.1f}  "
              f"|Jh|/|h_t|={float(th.norm()/h_t.norm()):.2f}", flush=True)
    top = model.unembed(J[143][t] @ (J[t][80] @ acts[80][pos].float())).topk(5).indices
    print(f"    composed readout v80->v{t}->t143: "
          f"{[tok.decode([x]).strip() for x in top.tolist()]}", flush=True)

json.dump({str(t): {m: {str(v): results["curves"][t][m][v] for v in results["curves"][t][m]}
                    for m in results["curves"][t]} for t in results["curves"]}
          | {"norms": {str(t): {str(v): n for v, n in results["norms"][t].items()}
                       for t in results["norms"]}},
          open("gap_analysis.json", "w"))
print("\nDONE_GAP_ANALYSIS", flush=True)