Download confidence_member.py from opticalfibre/PIN: direct link, hf CLI and curl.
- Browser
- Download file 20.8 kB
-
https://huggingface.co/opticalfibre/PIN/resolve/main/confidence_member.py
- Command line
-
hf download hf://opticalfibre/PIN/confidence_member.py
-
curl -L -o confidence_member.py https://huggingface.co/opticalfibre/PIN/resolve/main/confidence_member.py
20.8 kB
| """ | |
| A MEMBER THAT CHANGES ONLY THE CONFIDENCE. | |
| Two results boxed the correction loop in. A member that changes the ANSWER | |
| has a damage channel, and the arithmetic is brutal: right answers outnumber | |
| wrong ones 6.6 to 1, so a point of damage costs 6.6 times what a point of | |
| correction earns, and no gate we built came out ahead. And the base cannot | |
| say where it needs help — its confidence separates what it can do from what | |
| it has never seen at AUC 0.4245, BELOW chance. | |
| Lee's move: manipulate the confidence only. The base is confident that its | |
| ignorance is correct, so change that rather than the answer. | |
| A member applied as a positive SCALE on the logits cannot alter the argmax | |
| — accuracy is invariant by construction, not by measurement — so it has no | |
| damage channel at all. What it can alter is how peaked the distribution is, | |
| which is exactly what abstention reads. | |
| logits -> logits / exp(s(a)) | |
| with s a linear read of the frozen base's features, fitted by the same | |
| closed-form solve as any other member. Large s flattens the distribution | |
| and the model reports doubt; small s sharpens it. | |
| THE QUESTION UNDERNEATH is whether the information exists at all. The | |
| output layer demonstrably cannot express unfamiliarity — it was trained to | |
| name one of seven classes and it always does. But the FEATURES might still | |
| carry it, and a probe would find it if so. If a ridge read of the features | |
| separates seen from unseen where the softmax cannot, then ignorance is | |
| visible one layer down and the failsafe has something to run on. If it | |
| cannot, the representation genuinely does not encode unfamiliarity and no | |
| member can supply it. | |
| Measured three ways: | |
| DOES IT SEPARATE? AUC of the new confidence against the base's 0.4245 | |
| IS ACCURACY SAFE? it must be EXACTLY unchanged; anything else is a bug | |
| IS IT USEFUL? risk-coverage — accuracy on the most confident X%, | |
| before and after, which is what abstention buys | |
| """ | |
| import numpy as np | |
| import time | |
| import json | |
| try: | |
| import cupy as _cp | |
| _GPU = _cp.cuda.runtime.getDeviceCount() > 0 | |
| except Exception: | |
| _GPU = False | |
| xp = _cp if _GPU else np | |
| DT = np.float32 | |
| def to_dev(a, dtype=DT): | |
| a = np.asarray(a, dtype=dtype) | |
| return xp.asarray(a) if _GPU else a | |
| def to_host(a): | |
| return _cp.asnumpy(a) if _GPU and isinstance(a, _cp.ndarray) else np.asarray(a) | |
| def windowed(g, c_in, k, c_out): | |
| ni, no = c_in*g*g, c_out*g*g | |
| ii, jj = np.meshgrid(np.arange(ni), np.arange(no), indexing='ij') | |
| ci, pi = ii // (g*g), ii % (g*g) | |
| co, po = jj // (g*g), jj % (g*g) | |
| dr = pi // g - (po // g - k//2) | |
| dc = pi % g - (po % g - k//2) | |
| inside = (dr >= 0) & (dr < k) & (dc >= 0) & (dc < k) | |
| K = c_in*c_out*k*k + 1 | |
| idx = np.where(inside, (ci*c_out + co)*k*k + dr*k + dc, K-1) | |
| return idx.ravel().astype(np.int32), K, no | |
| _FIXED = {} | |
| class FixedScatter: | |
| def __init__(self, idx, K, cap=8192): | |
| h = to_host(idx).astype(np.int64).reshape(-1) | |
| order = np.argsort(h, kind="stable") | |
| counts = np.bincount(h, minlength=K) | |
| starts = np.cumsum(counts) - counts | |
| big = np.where(counts > cap)[0] | |
| small = np.where(counts <= cap)[0] | |
| self.K = K | |
| self.order = to_dev(order, np.int64) if _GPU else order | |
| self.big = [(int(b), int(starts[b]), int(starts[b]+counts[b])) | |
| for b in big] | |
| self.small = to_dev(small, np.int64) if _GPU else small | |
| self.width = int(counts[small].max()) if len(small) else 0 | |
| if self.width: | |
| pos = np.concatenate([np.arange(counts[s]) for s in small]) | |
| src = np.concatenate([np.arange(starts[s], starts[s]+counts[s]) | |
| for s in small]) | |
| row = np.repeat(np.arange(len(small)), counts[small]) | |
| self.src = to_dev(src, np.int64) if _GPU else src | |
| sl = row*self.width + pos | |
| self.slot = to_dev(sl, np.int64) if _GPU else sl | |
| self.buf = xp.zeros(len(small)*self.width, DT) | |
| self._keep = idx | |
| def __call__(self, g): | |
| gs = g.reshape(-1)[self.order] | |
| out = xp.zeros(self.K, DT) | |
| if self.width: | |
| self.buf[:] = 0 | |
| self.buf[self.slot] = gs[self.src] | |
| out[self.small] = self.buf.reshape(-1, self.width).sum(1) | |
| for b, a, z in self.big: | |
| out[b] = gs[a:z].sum() | |
| return out | |
| def scatter(dW, idx, K): | |
| key = (id(idx), K) | |
| if key not in _FIXED: | |
| _FIXED[key] = FixedScatter(idx, K) | |
| return _FIXED[key](dW) | |
| def train_base(Xtr, Ytr, cfg, seed): | |
| D, g, ch = Xtr.shape[1], cfg["grid"], cfg["chan"] | |
| rg = np.random.default_rng(seed) | |
| layers, cin = [], cfg["c_in"] | |
| for l in range(cfg["depth"]): | |
| idx, K, no = windowed(g, cin, 3, ch) | |
| layers.append(dict(idx=to_dev(idx, np.int32) if _GPU else idx, | |
| K=K, out=no, taps=cin*9, | |
| ins=D if l == 0 else layers[-1]["out"])) | |
| cin = ch | |
| L = cfg["depth"] | |
| P = [] | |
| for l in layers: | |
| v = rg.normal(0, np.sqrt(2.0/l["taps"]), l["K"]).astype(np.float32) | |
| v[-1] = 0.0 | |
| P.append(to_dev(v)) | |
| P += [xp.ones(l["out"], DT) for l in layers] | |
| P += [xp.zeros(l["out"], DT) for l in layers] | |
| P += [to_dev(rg.normal(0, np.sqrt(2.0/layers[-1]["out"]), | |
| (layers[-1]["out"], 10))), xp.zeros(10, DT)] | |
| HEAD, OB = 3*L, 3*L+1 | |
| M = [xp.zeros_like(p) for p in P]; V = [xp.zeros_like(p) for p in P] | |
| n = Xtr.shape[0]; t = 0 | |
| ag = np.random.default_rng(seed + 991) | |
| def fwd(x): | |
| cache = []; h = x | |
| for li, l in enumerate(layers): | |
| W = P[li][l["idx"]].reshape(l["ins"], l["out"]) | |
| z = h @ W | |
| var = z.var(1, keepdims=True) + 1e-5 | |
| zn = (z - z.mean(1, keepdims=True))/xp.sqrt(var) | |
| zs = zn*P[L+li] + P[2*L+li] | |
| a = xp.maximum(zs, 0) | |
| cache.append((h, W, var, zn, zs)) | |
| h = a | |
| return h, cache | |
| for ep in range(cfg["epochs"]): | |
| perm = ag.permutation(n) | |
| for st in range(0, n, cfg["batch"]): | |
| b = perm[st:st+cfg["batch"]] | |
| x = Xtr[b]; y = Ytr[b] | |
| h, cache = fwd(x) | |
| lg = h @ P[HEAD] + P[OB] | |
| e = xp.exp(lg - lg.max(1, keepdims=True)) | |
| d = (e/e.sum(1, keepdims=True) - y)/len(b) | |
| G = [xp.zeros_like(p) for p in P] | |
| G[HEAD] = h.T @ d; G[OB] = d.sum(0) | |
| dh = d @ P[HEAD].T | |
| for li in range(L-1, -1, -1): | |
| hin, W, var, zn, zs = cache[li] | |
| dzs = dh*(zs > 0) | |
| G[L+li] = (dzs*zn).sum(0); G[2*L+li] = dzs.sum(0) | |
| dzn = dzs*P[L+li] | |
| dz = (dzn - dzn.mean(1, keepdims=True) | |
| - zn*(dzn*zn).mean(1, keepdims=True))/xp.sqrt(var) | |
| G[li] = scatter(hin.T @ dz, layers[li]["idx"], layers[li]["K"]) | |
| if li > 0: | |
| dh = dz @ W.T | |
| t += 1 | |
| for i, (p_, gr) in enumerate(zip(P, G)): | |
| M[i] = 0.9*M[i] + 0.1*gr | |
| V[i] = 0.999*V[i] + 0.001*gr*gr | |
| P[i] = p_ - cfg["lr"]*(M[i]/(1-0.9**t)) \ | |
| / (xp.sqrt(V[i]/(1-0.999**t))+1e-8) | |
| return P, fwd, HEAD, OB, layers[-1]["out"] | |
| def ridge(A, R, lam): | |
| """The closed-form member: one solve, no steps. | |
| Fitted on the RESIDUAL between the target and what the base already | |
| says, so a member that explains nothing contributes nothing — and as | |
| lambda dominates the solution shrinks toward zero and the base answers. | |
| That is the failsafe as arithmetic rather than as a rule.""" | |
| n, d = A.shape | |
| A1 = xp.concatenate([A, xp.ones((n, 1), DT)], 1) | |
| if d + 1 <= n: | |
| G = A1.T @ A1 + lam*xp.eye(d+1, dtype=DT) | |
| W = xp.linalg.solve(G, A1.T @ R) | |
| else: | |
| # the wide case: solve in the sample space instead, which is the | |
| # only tractable form when there are five examples and 3,136 | |
| # features | |
| G = A1 @ A1.T + lam*xp.eye(n, dtype=DT) | |
| W = A1.T @ xp.linalg.solve(G, R) | |
| return W[:-1], W[-1] | |
| def descent(A, R, lam, steps, lr): | |
| """The same fit by gradient descent, for comparison. The closed form is | |
| only worth having if it matches.""" | |
| n, d = A.shape | |
| W = xp.zeros((d, R.shape[1]), DT); b = xp.zeros(R.shape[1], DT) | |
| M = [xp.zeros_like(W), xp.zeros_like(b)] | |
| V = [xp.zeros_like(W), xp.zeros_like(b)] | |
| for t in range(1, steps+1): | |
| E = A @ W + b - R | |
| G = [A.T @ E/n + lam*W/n, E.mean(0)] | |
| for i, (p_, gr) in enumerate(zip([W, b], G)): | |
| M[i] = 0.9*M[i] + 0.1*gr | |
| V[i] = 0.999*V[i] + 0.001*gr*gr | |
| upd = p_ - lr*(M[i]/(1-0.9**t))/(xp.sqrt(V[i]/(1-0.999**t))+1e-8) | |
| if i == 0: | |
| W = upd | |
| else: | |
| b = upd | |
| return W, b | |
| def load(cfg): | |
| from tensorflow import keras | |
| (a, b), (c, d) = keras.datasets.fashion_mnist.load_data() | |
| X = np.concatenate([a, c]).astype(np.float32)/255.0 | |
| y = np.concatenate([b, d]).ravel().astype(np.int64) | |
| if cfg["grid"] != 28: | |
| s = 28//cfg["grid"] | |
| X = X.reshape(-1, cfg["grid"], s, cfg["grid"], s).mean(axis=(2, 4)) | |
| rg = np.random.default_rng(0); p = rg.permutation(len(X)) | |
| tr, te = p[:cfg["n_train"]], p[cfg["n_train"]:cfg["n_train"]+10000] | |
| mu, sd = X[tr].mean(), X[tr].std()+1e-8 | |
| f = lambda Z: ((Z-mu)/sd).reshape(len(Z), -1) | |
| Y = np.zeros((len(tr), 10), np.float32); Y[np.arange(len(tr)), y[tr]] = 1 | |
| return f(X[tr]), Y, y[tr], f(X[te]), y[te] | |
| # fit_on: "classes" fits a specialist on a class subset (measured: an | |
| # oracle gate is worth only +0.0070, because the base was already good | |
| # there). "errors" fits on the base's own mistakes, which is the correction | |
| # loop and has 13% of the test set to work with rather than a 1.8-point | |
| # margin on 40% of it. | |
| CFG = dict(grid=14, c_in=1, chan=16, depth=3, n_train=20000, batch=128, | |
| lr=1e-3, epochs=30, seed=0, | |
| base_classes=(0, 1, 2, 3, 4, 5, 6), | |
| new_classes=(7, 8, 9), | |
| n_holdout=4000, N=200, lam=1.0, draws=5, | |
| # THE BASE SEED IS SWEPT TOO: the AUCs below are properties of a | |
| # trained representation, and a first version measured them on | |
| # one training run of it. | |
| base_seeds=(0, 1, 2), | |
| coverage=(0.2, 0.4, 0.6, 0.8, 1.0)) | |
| def auc_of(score, pos): | |
| """Probability a positive ranks above a negative.""" | |
| r = np.argsort(np.argsort(score)) | |
| n1, n0 = pos.sum(), (~pos).sum() | |
| if n1 == 0 or n0 == 0: | |
| return float("nan") | |
| return float((r[pos].sum() - n1*(n1-1)/2)/(n1*n0)) | |
| def conf_of(lg): | |
| e = np.exp(lg - lg.max(1, keepdims=True)) | |
| return (e/e.sum(1, keepdims=True)).max(1) | |
| def risk_coverage(conf, correct, cover): | |
| """Accuracy on the most confident fraction — what abstention buys.""" | |
| order = np.argsort(-conf) | |
| out = [] | |
| for c in cover: | |
| k = max(1, int(c*len(conf))) | |
| out.append(float(correct[order[:k]].mean())) | |
| return out | |
| def main(**over): | |
| CFG.update(over) | |
| t0 = time.time() | |
| print("=" * 78) | |
| print("A MEMBER THAT CHANGES ONLY THE CONFIDENCE") | |
| print("=" * 78) | |
| print(f" backend: {'cupy (GPU)' if _GPU else 'numpy (CPU)'}") | |
| for k, v in CFG.items(): | |
| print(f" {k:14s} = {v}") | |
| print(f"\n the base is trained only on {CFG['base_classes']}; a positive") | |
| print(f" scale on its logits cannot change the argmax, so accuracy is") | |
| print(f" invariant BY CONSTRUCTION and only confidence can move") | |
| print("=" * 78, flush=True) | |
| X, Y, y, Xte, yte = load(CFG) | |
| old_m = np.isin(y, CFG["base_classes"]) | |
| tr_i = np.where(old_m)[0] | |
| # the held-out split must contain BOTH kinds, or a probe has nothing to | |
| # contrast: familiar examples the base was not trained on, and unseen | |
| # ones it has never met | |
| ho_new = np.where(~old_m)[0][:CFG["n_holdout"]//2] | |
| ho_old = tr_i[-(CFG["n_holdout"]//2):] | |
| tr_i = tr_i[:-(CFG["n_holdout"]//2)] | |
| ho_i = np.concatenate([ho_old, ho_new]) | |
| Xtr, Ytr = to_dev(X[tr_i]), to_dev(Y[tr_i]) | |
| Xho = to_dev(X[ho_i]); yho = y[ho_i] | |
| unseen_ho = np.isin(yho, CFG["new_classes"]) | |
| Xte_d = to_dev(Xte) | |
| A_auc, A_err, A_acc, A_rc = [], [], [], [] | |
| B_auc_fam, B_rc_fam, B_auc_all = [], [], [] | |
| base_seen_unseen, base_fam_rw, rc0_all, rc0_fam_all = [], [], [], [] | |
| for bs in CFG["base_seeds"]: | |
| P, fwd, HEAD, OB, width = train_base(Xtr, Ytr, CFG, bs) | |
| fho, _ = fwd(Xho); fte, _ = fwd(Xte_d) | |
| base_te = to_host(fte @ P[HEAD] + P[OB]) | |
| base_ho = to_host(fho @ P[HEAD] + P[OB]) | |
| unseen = np.isin(yte, CFG["new_classes"]) | |
| pred = base_te.argmax(1); correct = pred == yte | |
| c0 = conf_of(base_te) | |
| fam_ho = ~unseen_ho | |
| wrong_ho = base_ho.argmax(1) != yho | |
| fam_te = ~unseen | |
| base_seen_unseen.append(auc_of(-c0, unseen)) | |
| base_fam_rw.append(auc_of(-c0[fam_te], ~correct[fam_te])) | |
| rc0_all.append(risk_coverage(c0, correct, CFG["coverage"])) | |
| rc0_fam_all.append(risk_coverage(c0[fam_te], correct[fam_te], | |
| CFG["coverage"])) | |
| print(f" base seed {bs}: {float(correct[~unseen].mean()):.4f} on its " | |
| f"seven, confidence separates seen from unseen at AUC " | |
| f"{base_seen_unseen[-1]:.4f} [{time.time()-t0:.0f}s]", | |
| flush=True) | |
| for dr in range(CFG["draws"]): | |
| rg = np.random.default_rng(4000 + 83*bs + dr) | |
| sub = rg.choice(len(ho_i), min(CFG["N"], len(ho_i)), replace=False) | |
| sd_ = to_dev(sub, np.int64) if _GPU else sub | |
| tgt = to_dev(np.where(unseen_ho[sub], 1.0, -1.0) | |
| .astype(np.float32)[:, None]) | |
| W, b = ridge(fho[sd_], tgt, CFG["lam"]) | |
| sc = to_host(fte @ W + b).ravel() | |
| scaled = base_te/np.exp(np.clip(sc, -4, 4))[:, None] | |
| c1 = conf_of(scaled) | |
| A_auc.append(auc_of(-c1, unseen)) | |
| A_err.append(auc_of(-c1, ~correct)) | |
| A_acc.append(float((scaled.argmax(1) == yte).mean()) | |
| - float(correct.mean())) | |
| A_rc.append(risk_coverage(c1, correct, CFG["coverage"])) | |
| fi = np.where(fam_ho)[0] | |
| rgb = np.random.default_rng(6000 + 83*bs + dr) | |
| subb = rgb.choice(fi, min(CFG["N"], len(fi)), replace=False) | |
| sb = to_dev(subb, np.int64) if _GPU else subb | |
| tb = to_dev(np.where(wrong_ho[subb], 1.0, -1.0) | |
| .astype(np.float32)[:, None]) | |
| Wb, bb = ridge(fho[sb], tb, CFG["lam"]) | |
| s2 = to_host(fte @ Wb + bb).ravel() | |
| c2 = conf_of(base_te/np.exp(np.clip(s2, -4, 4))[:, None]) | |
| B_auc_fam.append(auc_of(-c2[fam_te], ~correct[fam_te])) | |
| B_auc_all.append(auc_of(-c2, unseen)) | |
| B_rc_fam.append(risk_coverage(c2[fam_te], correct[fam_te], | |
| CFG["coverage"])) | |
| a0 = float(np.mean(base_seen_unseen)) | |
| fb = float(np.mean(base_fam_rw)) | |
| aucs, aucs_err, accs, rc_new = A_auc, A_err, A_acc, A_rc | |
| print(f"\n {len(CFG['base_seeds'])} bases; their confidence separates") | |
| print(f" seen from unseen at {min(base_seen_unseen):.4f} to " | |
| f"{max(base_seen_unseen):.4f}, and right from wrong on FAMILIAR") | |
| print(f" input at {min(base_fam_rw):.4f} to {max(base_fam_rw):.4f}") | |
| rc0 = np.mean(rc0_all, 0) | |
| rc1 = np.mean(A_rc, 0) | |
| print("\n" + "=" * 78) | |
| print(" DOES A MEMBER SUPPLY WHAT THE OUTPUT LAYER CANNOT?") | |
| print("=" * 78) | |
| print(f" {'':>28s} {'AUC':>8s} {'sd':>7s}") | |
| print(f" {'base confidence, seen/unseen':>28s} {a0:8.4f} " | |
| f"{np.std(base_seen_unseen):7.4f}") | |
| print(f" {'scaled confidence, seen/unseen':>28s} {np.mean(aucs):8.4f} " | |
| f"{np.std(aucs):7.4f}") | |
| print(f" {'scaled confidence, right/wrong':>28s} " | |
| f"{np.mean(aucs_err):8.4f} {np.std(aucs_err):7.4f}") | |
| print(f"\n BUT THAT right/wrong FIGURE IS INFLATED: 40% of the test set") | |
| print(f" is unseen and therefore wrong by construction. The honest") | |
| print(f" question is errors on FAMILIAR input, where there is no") | |
| print(f" category boundary to give them away:\n") | |
| fb = auc_of(-c0[fam_te], ~correct[fam_te]) | |
| print(f" {'':>34s} {'AUC':>8s} {'sd':>7s}") | |
| print(f" {'base confidence, familiar right/wrong':>34s} {fb:8.4f}") | |
| print(f" {'probe B on familiar right/wrong':>34s} " | |
| f"{np.mean(B_auc_fam):8.4f} {np.std(B_auc_fam):7.4f}") | |
| print(f" {'probe B also spotting the unseen':>34s} " | |
| f"{np.mean(B_auc_all):8.4f} {np.std(B_auc_all):7.4f}") | |
| print(f"\n accuracy change from the scale, across every base and " | |
| f"draw: {np.abs(accs).max():.3e}") | |
| print(f" (a positive scale cannot move an argmax; this is a bug check)") | |
| print("\n" + "=" * 78) | |
| print(" WHAT ABSTENTION BUYS") | |
| print("=" * 78) | |
| print(f" {'coverage':>9s} {'base':>9s} {'with member':>12s} " | |
| f"{'gain':>8s}") | |
| for i, c in enumerate(CFG["coverage"]): | |
| print(f" {c:8.0%} {rc0[i]:9.4f} {rc1[i]:12.4f} " | |
| f"{rc1[i]-rc0[i]:+8.4f}") | |
| rcb = np.mean(B_rc_fam, 0) | |
| rc0f = np.mean(rc0_fam_all, 0) | |
| print(f"\n and abstention on FAMILIAR input alone, which is what a") | |
| print(f" correction loop would actually run on:") | |
| print(f" {'coverage':>9s} {'base':>9s} {'probe B':>12s} {'gain':>8s}") | |
| for i, c in enumerate(CFG["coverage"]): | |
| print(f" {c:8.0%} {rc0f[i]:9.4f} {rcb[i]:12.4f} " | |
| f"{rcb[i]-rc0f[i]:+8.4f}") | |
| print("\n" + "=" * 78) | |
| print(" READOUT") | |
| print("=" * 78) | |
| fam_gain = np.mean(B_auc_fam) - fb | |
| print(f" THERE ARE TWO BLIND SPOTS AND ONLY ONE IS BLIND.\n") | |
| print(f" On FAMILIAR input the base already knows when it is unsure —") | |
| print(f" AUC {fb:.4f} — and a {CFG['N']}-example probe on the same") | |
| print(f" features reaches {np.mean(B_auc_fam):.4f} ({fam_gain:+.4f}). A") | |
| print(f" member adds nothing there.") | |
| print(f"\n On UNFAMILIAR input it has no idea: {a0:.4f}, below chance,") | |
| print(f" and a member supplies {np.mean(aucs):.4f}.") | |
| print(f"\n AND THE TWO DETECTORS DO NOT TRANSFER: probe B, fitted on") | |
| print(f" familiar errors, spots the unseen at {np.mean(B_auc_all):.4f}.") | |
| print(f" Knowing you are unsure and knowing you are out of your depth") | |
| print(f" are different quantities living in different places.") | |
| print(f"\n WHICH MEANS THE GATE NEVER FAILED FOR WANT OF A SIGNAL. The") | |
| print(f" 0.5481 that made the base's confidence look useless was an") | |
| print(f" aggregate over a test set 40% of which it had never seen. What") | |
| print(f" killed the correction loop was the 6.6-to-1 arithmetic and a") | |
| print(f" member that could only reach 17% of irreducible errors.") | |
| print() | |
| gain = np.mean(aucs) - a0 | |
| sd = max(np.std(aucs), 1e-9) | |
| if np.mean(aucs) > 0.7: | |
| print(f" IGNORANCE IS VISIBLE ONE LAYER DOWN. The output layer") | |
| print(f" separates seen from unseen at {a0:.4f} — below chance —") | |
| print(f" and a {CFG['N']}-example linear read of the SAME frozen") | |
| print(f" features reaches {np.mean(aucs):.4f}. The representation") | |
| print(f" encodes unfamiliarity; the head was trained to name one of") | |
| print(f" seven classes and never to admit it could not.") | |
| print(f"\n And it costs nothing to try: a positive scale cannot") | |
| print(f" change the argmax, so the failure mode of a bad confidence") | |
| print(f" member is a worse confidence, never a worse answer.") | |
| elif np.mean(aucs) > 0.6: | |
| print(f" PARTLY VISIBLE: {np.mean(aucs):.4f} against the output") | |
| print(f" layer's {a0:.4f}. Real signal, not enough to abstain on") | |
| print(f" confidently.") | |
| else: | |
| print(f" NOT VISIBLE EITHER. A probe on the features reaches") | |
| print(f" {np.mean(aucs):.4f}, so the representation does not encode") | |
| print(f" unfamiliarity and no member can supply it. Ignorance is") | |
| print(f" invisible from the inside at every level, which is a") | |
| print(f" stronger and more useful claim than the output-layer") | |
| print(f" result alone.") | |
| print(f"\n total {time.time()-t0:.0f}s; wrote confidence_member.json") | |
| json.dump(dict(base_auc=a0, member_auc=float(np.mean(aucs)), | |
| member_auc_sd=float(np.std(aucs)), | |
| err_auc=float(np.mean(aucs_err)), | |
| rc_base=rc0, rc_member=list(map(float, rc1))), | |
| open("confidence_member.json", "w"), indent=2) | |
| if __name__ == "__main__": | |
| main() | |