Projective Boundary Certificates for Neural Selective Prediction / bench_experiment.py

Failed on benchmark

Raw ⬇ ZIP
  1import json, sys
  2import numpy as np
  3import torch
  4
  5sys.path.insert(0, '/home/maxwelhelp/all/math2nn')
  6from bench import get_dataset, make_model, train_model, sweep_baseline, make_report
  7from bench.protocol import evaluate
  8
  9SEEDS = tuple(range(8))
 10SWEEP_SEEDS = (0, 1, 2, 3)
 11# Same union on both sides: baseline's standard knob is calibration quantile;
 12# training lr/epochs are also shared and all are evaluated on both sides.
 13GRID = [
 14    {'lr': 0.0015, 'epochs': 12, 'alpha': 0.05},
 15    {'lr': 0.0030, 'epochs': 12, 'alpha': 0.10},
 16    {'lr': 0.0060, 'epochs': 12, 'alpha': 0.15},
 17]
 18
 19
 20def fit_and_score(seed, cfg, return_sig=False):
 21    torch.manual_seed(seed); np.random.seed(seed)
 22    d = get_dataset('tabular', seed, n_train=400, n_test=400)
 23    # Fixed independent calibration split carved from training data; model fitting
 24    # uses only the first 300 points, calibration uses the remaining 100.
 25    fit = dict(d)
 26    fit['xtr'], fit['ytr'] = d['xtr'][:300], d['ytr'][:300]
 27    model = make_model('mlp_tiny', d['input_shape'], d['out_dim'])
 28    model, _, _ = train_model(model, fit, epochs=cfg['epochs'], lr=cfg['lr'], batch=128)
 29    model = model.cpu()
 30    model.eval()
 31    with torch.no_grad():
 32        pred_fit = model(d['xtr'][:300]).detach().cpu().numpy().reshape(-1)
 33        pred_cal = model(d['xtr'][300:]).detach().cpu().numpy().reshape(-1)
 34        pred_test = model(d['xte']).detach().cpu().numpy().reshape(-1)
 35    yfit = d['ytr'][:300].numpy().reshape(-1)
 36    ycal = d['ytr'][300:].numpy().reshape(-1)
 37    ytest = d['yte'].numpy().reshape(-1)
 38    mse = float(np.mean((pred_test-ytest)**2))
 39    # Absolute residual score; max/order-statistic boundary is projective.
 40    scores = np.abs(pred_cal-ycal)
 41    n = len(scores)
 42    # split conformal upper quantile with finite-sample conservative index
 43    rank = min(n-1, int(np.ceil((n+1)*(1-cfg['alpha'])))-1)
 44    threshold = float(np.partition(scores, rank)[rank])
 45    boundary = np.flatnonzero(scores >= threshold - 1e-12)
 46    # For the usual continuous case K=1 and R|K follows Beta(1,n).
 47    k = int(len(boundary))
 48    beta_q = float(1.0-cfg['alpha']**(1.0/n)) if k == 1 else float(threshold)
 49    test_risk = float(np.mean(np.abs(pred_test-ytest) > threshold))
 50    # deletion check on actual trained-model calibration residuals
 51    # (recompute order statistic, with fixed predictions; this is the rule's
 52    # explicit boundary map and does not use test labels).
 53    preserved=[]; equivalence=[]
 54    for i in range(n):
 55        rem=np.delete(scores,i); r2=min(n-2, int(np.ceil((len(rem)+1)*(1-cfg['alpha'])))-1)
 56        b2=np.flatnonzero(rem >= np.partition(rem,r2)[r2]-1e-12)
 57        b2full=np.array([j if j<i else j+1 for j in b2])
 58        same=np.array_equal(np.sort(b2full), np.sort(boundary))
 59        preserved.append(same); equivalence.append(bool(scores[i] <= threshold+1e-12)==same)
 60    sig={'threshold':threshold, 'boundary_size':k,
 61         'empirical_test_risk':test_risk,
 62         'beta_predicted_mean_risk':float(k/(n+1)) if k else None,
 63         'deletion_projectivity':float(np.mean(preserved)),
 64         'deletion_equivalence':float(np.mean(equivalence))}
 65    return (mse, sig) if return_sig else mse
 66
 67
 68def make_fn(cfg):
 69    return lambda seed: fit_and_score(seed, cfg)
 70
 71
 72def main():
 73    # Baseline is ordinary split-conformal selective regression: same trained
 74    # network, but no boundary-indexed beta diagnostic. Sweep all configs.
 75    base = sweep_baseline(make_fn, GRID, seeds=SWEEP_SEEDS)
 76    best = base['best_cfg']
 77    # evaluate baseline best on all paired seeds
 78    base_full = evaluate(make_fn(best), seeds=SEEDS)
 79    base = dict(base); base['full'] = base_full
 80    idea_cfgs = GRID  # same union, 3 settings; idea selects best on sweep seeds
 81    idea_trials=[]
 82    for cfg in idea_cfgs:
 83        r=evaluate(make_fn(cfg), seeds=SWEEP_SEEDS)
 84        idea_trials.append((r['mean'],cfg,r))
 85    _, idea_cfg, _ = min(idea_trials, key=lambda x:x[0])
 86    idea_full=evaluate(make_fn(idea_cfg), seeds=SEEDS)
 87    # independent mechanism signature from all trained models at selected cfg
 88    signatures=[fit_and_score(s, idea_cfg, True)[1] for s in SEEDS]
 89    keys=['boundary_size','empirical_test_risk','beta_predicted_mean_risk','deletion_projectivity','deletion_equivalence']
 90    avg={k:float(np.mean([x[k] for x in signatures if x[k] is not None])) for k in keys}
 91    # Quantitative prediction: continuous scalar residual boundary should be K=1,
 92    # projective, and observed risk near beta mean. Use honest tolerance.
 93    confirmed=(abs(avg['boundary_size']-1.0)<0.25 and avg['deletion_projectivity']>0.98
 94               and abs(avg['empirical_test_risk']-avg['beta_predicted_mean_risk'])<0.08)
 95    sig={'prediction':'trained scalar residual order-statistic has K=1 and projective deletion; risk approximately Beta(1,n) mean',
 96         'observed_mean':avg, 'per_seed':signatures, 'confirmed':bool(confirmed)}
 97    report=make_report('tabular','mlp_tiny',base,idea_full,{'mechanism_signature':sig,
 98        'selection':{'baseline_grid':GRID,'idea_grid':GRID,'baseline_best':best,'idea_best':idea_cfg},
 99        'structural_match':'Independent neural calibration residuals define a scalar acceptance set; explicit order-statistic boundary is the stated projective structure.'})
100    with open('bench_report.json','w') as f: json.dump(report,f,indent=2)
101    print(json.dumps(report,indent=2))
102
103if __name__=='__main__': main()