Upload colab_xfamily_crossformat.py with huggingface_hub
Browse files- colab_xfamily_crossformat.py +100 -0
colab_xfamily_crossformat.py
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Cross-FORMAT x cross-FAMILY deception transfer — the template-confound killer.
|
| 2 |
+
|
| 3 |
+
Format A = roleplay deception ("pretend X is W, stay in character").
|
| 4 |
+
Format B = bluff game ("make the user believe something false about X").
|
| 5 |
+
These are syntactically and pragmatically different ways to elicit a lie.
|
| 6 |
+
|
| 7 |
+
If a probe trained on format-A deception (in one model) detects format-B
|
| 8 |
+
deception (in another model), it cannot be keying on the prompt template -- it
|
| 9 |
+
is keying on the deception itself. We test all train/test combinations of
|
| 10 |
+
{format} x {family}.
|
| 11 |
+
"""
|
| 12 |
+
import json, numpy as np
|
| 13 |
+
from sklearn.linear_model import LogisticRegression
|
| 14 |
+
from sklearn.preprocessing import StandardScaler
|
| 15 |
+
from sklearn.metrics import roc_auc_score
|
| 16 |
+
|
| 17 |
+
A = json.load(open("/content/rift_xfamily_reps.json")) # roleplay
|
| 18 |
+
B = json.load(open("/content/rift_xfamily_reps_B.json")) # bluff
|
| 19 |
+
MODELS = [m for m in A["models"] if m in A["data"] and m in B["data"]]
|
| 20 |
+
short = {m: m.split("/")[-1] for m in MODELS}
|
| 21 |
+
|
| 22 |
+
def prep(blob):
|
| 23 |
+
Z = {}
|
| 24 |
+
for m in MODELS:
|
| 25 |
+
X = np.nan_to_num(np.array(blob["data"][m]["X"], dtype=float))
|
| 26 |
+
y = np.array(blob["data"][m]["y"])
|
| 27 |
+
Z[m] = (np.nan_to_num(StandardScaler().fit_transform(X)), y)
|
| 28 |
+
return Z
|
| 29 |
+
ZA, ZB = prep(A), prep(B)
|
| 30 |
+
banks = {"A": ZA, "B": ZB}
|
| 31 |
+
|
| 32 |
+
def probe():
|
| 33 |
+
return LogisticRegression(C=1.0, max_iter=5000)
|
| 34 |
+
|
| 35 |
+
rng = np.random.default_rng(0); NP = 5000
|
| 36 |
+
def perm_p(yte, sc, auc):
|
| 37 |
+
null = np.array([roc_auc_score(rng.permutation(yte), sc) for _ in range(NP)])
|
| 38 |
+
return float((null >= auc).mean())
|
| 39 |
+
|
| 40 |
+
print("=" * 72)
|
| 41 |
+
print("CROSS-FORMAT x CROSS-FAMILY DECEPTION TRANSFER")
|
| 42 |
+
print("A = roleplay lie, B = bluff-game lie (different templates)")
|
| 43 |
+
print("=" * 72)
|
| 44 |
+
|
| 45 |
+
# The key quantity: train on format A, test on format B (and vice versa),
|
| 46 |
+
# across every family pair. Pure cross-format (template changes every time).
|
| 47 |
+
xfmt_xfam = [] # different format AND different family (hardest)
|
| 48 |
+
xfmt_same = [] # different format, same family
|
| 49 |
+
results = {}
|
| 50 |
+
for tr_f in ["A", "B"]:
|
| 51 |
+
te_f = "B" if tr_f == "A" else "A"
|
| 52 |
+
for trm in MODELS:
|
| 53 |
+
Xtr, ytr = banks[tr_f][trm]
|
| 54 |
+
pr = probe().fit(Xtr, ytr)
|
| 55 |
+
for tem in MODELS:
|
| 56 |
+
Xte, yte = banks[te_f][tem]
|
| 57 |
+
sc = pr.predict_proba(Xte)[:, 1]
|
| 58 |
+
auc = roc_auc_score(yte, sc)
|
| 59 |
+
p = perm_p(yte, sc, auc)
|
| 60 |
+
key = f"{tr_f}:{short[trm]} -> {te_f}:{short[tem]}"
|
| 61 |
+
results[key] = {"auc": float(auc), "p": p}
|
| 62 |
+
if trm == tem:
|
| 63 |
+
xfmt_same.append(auc)
|
| 64 |
+
else:
|
| 65 |
+
xfmt_xfam.append(auc)
|
| 66 |
+
|
| 67 |
+
# print as readable blocks
|
| 68 |
+
for tr_f in ["A", "B"]:
|
| 69 |
+
te_f = "B" if tr_f == "A" else "A"
|
| 70 |
+
print(f"\n--- train format {tr_f} ({'roleplay' if tr_f=='A' else 'bluff'}), "
|
| 71 |
+
f"test format {te_f} ({'roleplay' if te_f=='A' else 'bluff'}) ---")
|
| 72 |
+
for trm in MODELS:
|
| 73 |
+
cells = []
|
| 74 |
+
for tem in MODELS:
|
| 75 |
+
r = results[f"{tr_f}:{short[trm]} -> {te_f}:{short[tem]}"]
|
| 76 |
+
star = "*" if tem != trm else " "
|
| 77 |
+
cells.append(f"{short[tem][:9]:>9s}:{r['auc']:.3f}(p{r['p']:.3f}){star}")
|
| 78 |
+
print(f" {short[trm]:24s} -> " + " | ".join(cells))
|
| 79 |
+
|
| 80 |
+
xs = np.array(xfmt_same); xf = np.array(xfmt_xfam)
|
| 81 |
+
print("\n" + "=" * 72)
|
| 82 |
+
print(f"CROSS-FORMAT, SAME family (n={len(xs)}): mean AUC {xs.mean():.3f} "
|
| 83 |
+
f"[{xs.min():.3f}, {xs.max():.3f}]")
|
| 84 |
+
print(f"CROSS-FORMAT + CROSS-FAMILY (n={len(xf)}): mean AUC {xf.mean():.3f} "
|
| 85 |
+
f"[{xf.min():.3f}, {xf.max():.3f}] <-- hardest: template AND architecture differ")
|
| 86 |
+
allp = [v["p"] for v in results.values()]
|
| 87 |
+
print(f"significant (p<0.05): {sum(p < 0.05 for p in allp)}/{len(allp)}")
|
| 88 |
+
|
| 89 |
+
verdict = ("DECEPTION, NOT TEMPLATE: probe transfers across BOTH format and family"
|
| 90 |
+
if xf.mean() > 0.7 and (np.array([results[k]['p'] for k in results
|
| 91 |
+
if k.split(':')[1].split(' ')[0] != k.split('> ')[1].split(':')[1]]) < 0.05).mean() > 0.8
|
| 92 |
+
else "PARTIAL: cross-format weaker than within-format (some template signal)"
|
| 93 |
+
if xf.mean() > 0.6 else "TEMPLATE-BOUND: signal does not survive format change")
|
| 94 |
+
print(f"\nVERDICT: {verdict}")
|
| 95 |
+
|
| 96 |
+
json.dump({"results": results, "xfmt_same_mean": float(xs.mean()),
|
| 97 |
+
"xfmt_xfam_mean": float(xf.mean()), "xfmt_xfam_aucs": xf.tolist(),
|
| 98 |
+
"verdict": verdict},
|
| 99 |
+
open("/content/rift_crossformat_results.json", "w"), indent=2)
|
| 100 |
+
print("\nsaved /content/rift_crossformat_results.json")
|