Upload colab_xfamily_analyze.py with huggingface_hub
Browse files- colab_xfamily_analyze.py +90 -0
colab_xfamily_analyze.py
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Cross-family deception transfer — STAGE 2: probe transfer analysis.
|
| 2 |
+
|
| 3 |
+
Train a linear deception probe on the relative-representation space of ONE
|
| 4 |
+
model family, test zero-shot on the OTHERS. If deception lives in a
|
| 5 |
+
basis-invariant relative geometry shared across families, the probe transfers.
|
| 6 |
+
"""
|
| 7 |
+
import json, numpy as np
|
| 8 |
+
from sklearn.linear_model import LogisticRegression
|
| 9 |
+
from sklearn.preprocessing import StandardScaler
|
| 10 |
+
from sklearn.metrics import roc_auc_score
|
| 11 |
+
from sklearn.model_selection import cross_val_score, StratifiedKFold
|
| 12 |
+
|
| 13 |
+
d = json.load(open("/content/rift_xfamily_reps.json"))
|
| 14 |
+
models = [m for m in d["models"] if m in d["data"]]
|
| 15 |
+
short = {m: m.split("/")[-1] for m in models}
|
| 16 |
+
print("models:", [short[m] for m in models])
|
| 17 |
+
print(f"anchors: {len(d['anchors'])}, facts: {d['n_facts']}, layer_frac: {d['layer_frac']}\n")
|
| 18 |
+
|
| 19 |
+
# Per-model standardization aligns the cosine-similarity distributions across
|
| 20 |
+
# families (different anisotropy) so the probe compares relative structure.
|
| 21 |
+
Z = {}
|
| 22 |
+
for m in models:
|
| 23 |
+
X = np.array(d["data"][m]["X"], dtype=float); y = np.array(d["data"][m]["y"])
|
| 24 |
+
n_nan = int(np.isnan(X).sum())
|
| 25 |
+
X = np.nan_to_num(X, nan=0.0, posinf=0.0, neginf=0.0)
|
| 26 |
+
sc = StandardScaler().fit(X)
|
| 27 |
+
Xz = np.nan_to_num(sc.transform(X), nan=0.0, posinf=0.0, neginf=0.0)
|
| 28 |
+
Z[m] = (Xz, y)
|
| 29 |
+
print(f" {short[m]}: X{X.shape}, raw NaN={n_nan}, "
|
| 30 |
+
f"constant-anchor features={(X.std(axis=0)==0).sum()}")
|
| 31 |
+
print()
|
| 32 |
+
|
| 33 |
+
def make_probe():
|
| 34 |
+
return LogisticRegression(C=1.0, max_iter=5000, solver="lbfgs")
|
| 35 |
+
|
| 36 |
+
rng = np.random.default_rng(0)
|
| 37 |
+
N_PERM = 5000
|
| 38 |
+
|
| 39 |
+
print("=" * 70)
|
| 40 |
+
print("CROSS-FAMILY DECEPTION PROBE TRANSFER (AUC)")
|
| 41 |
+
print("=" * 70)
|
| 42 |
+
matrix = {}
|
| 43 |
+
cross_aucs = []
|
| 44 |
+
for tr in models:
|
| 45 |
+
Xtr, ytr = Z[tr]
|
| 46 |
+
row = []
|
| 47 |
+
for te in models:
|
| 48 |
+
Xte, yte = Z[te]
|
| 49 |
+
if tr == te:
|
| 50 |
+
auc = cross_val_score(make_probe(), Xtr, ytr,
|
| 51 |
+
cv=StratifiedKFold(5, shuffle=True, random_state=0),
|
| 52 |
+
scoring="roc_auc").mean()
|
| 53 |
+
tag = "CV"
|
| 54 |
+
p = None
|
| 55 |
+
else:
|
| 56 |
+
p_ = make_probe().fit(Xtr, ytr)
|
| 57 |
+
scores = p_.predict_proba(Xte)[:, 1]
|
| 58 |
+
auc = roc_auc_score(yte, scores)
|
| 59 |
+
# permutation test: how often do shuffled test labels beat observed?
|
| 60 |
+
null = np.array([roc_auc_score(rng.permutation(yte), scores)
|
| 61 |
+
for _ in range(N_PERM)])
|
| 62 |
+
p = float((null >= auc).mean())
|
| 63 |
+
tag = f"p={p:.4f}"
|
| 64 |
+
cross_aucs.append(auc)
|
| 65 |
+
matrix[f"{short[tr]}->{short[te]}"] = {"auc": float(auc), "p": p}
|
| 66 |
+
row.append(f"{auc:.3f}({tag})")
|
| 67 |
+
print(f"{short[tr]:28s} -> " + " | ".join(f"{short[te][:10]:>10s}:{r}"
|
| 68 |
+
for te, r in zip(models, row)))
|
| 69 |
+
|
| 70 |
+
print("\n" + "=" * 70)
|
| 71 |
+
ca = np.array(cross_aucs)
|
| 72 |
+
print(f"MEAN cross-family AUC: {ca.mean():.3f} (min {ca.min():.3f}, max {ca.max():.3f})")
|
| 73 |
+
print(f"cross-family pairs above 0.7: {(ca > 0.7).sum()}/{len(ca)}")
|
| 74 |
+
print(f"cross-family pairs above 0.8: {(ca > 0.8).sum()}/{len(ca)}")
|
| 75 |
+
allp = [v["p"] for v in matrix.values() if v["p"] is not None]
|
| 76 |
+
print(f"cross-family pairs significant (p<0.05): {sum(p < 0.05 for p in allp)}/{len(allp)}")
|
| 77 |
+
|
| 78 |
+
verdict = ("UNIVERSAL DECEPTION GEOMETRY: probe transfers across families"
|
| 79 |
+
if ca.mean() > 0.75 and all(p < 0.05 for p in allp)
|
| 80 |
+
else "PARTIAL transfer — some shared structure"
|
| 81 |
+
if ca.mean() > 0.6
|
| 82 |
+
else "NO cross-family transfer — deception geometry is family-specific")
|
| 83 |
+
print(f"\nVERDICT: {verdict}")
|
| 84 |
+
|
| 85 |
+
json.dump({"matrix": matrix, "mean_cross_auc": float(ca.mean()),
|
| 86 |
+
"cross_aucs": ca.tolist(), "verdict": verdict,
|
| 87 |
+
"models": models, "n_anchors": len(d["anchors"]),
|
| 88 |
+
"n_facts": d["n_facts"]},
|
| 89 |
+
open("/content/rift_xfamily_results.json", "w"), indent=2)
|
| 90 |
+
print("\nsaved /content/rift_xfamily_results.json")
|