Transient-risk certificate for Langevin training / official_bench.py
Mechanism confirmed, baseline not beaten
1import sys, json, math
2from pathlib import Path
3import numpy as np
4import torch
5
6sys.path.insert(0, '/home/maxwelhelp/all/math2nn')
7from bench import get_dataset, make_model, sweep_baseline, evaluate, make_report
8
9TRACK='tabular'; MODEL='mlp_tiny'; EPOCHS=18; BATCH=128
10# Union is shared: baseline is evaluated at every lr and temperature used by idea.
11GRID=[{'lr':lr,'temperature':t} for lr in (0.001,0.003,0.006) for t in (0.0005,0.002)]
12IDEA_GRID=GRID[:3]
13SEEDS=tuple(range(8))
14OBS={}
15
16def run(seed, cfg, controlled, record=False):
17 torch.manual_seed(seed); np.random.seed(seed)
18 ds=get_dataset(TRACK, seed, n_train=4000, n_test=1000)
19 net=make_model(MODEL, ds['input_shape'], ds['out_dim'])
20 # A fixed unsafe region in parameter space: leaving the initialization basin.
21 initial=[p.detach().clone() for p in net.parameters()]
22 threshold=1.5; m=0.15; chi2=4.0; delta=0.20
23 x,y=ds['xtr'],ds['ytr']; xt,yt=ds['xte'],ds['yte']
24 g=torch.Generator().manual_seed(seed+19001)
25 stopped=False; stop_epoch=None; unsafe=[]; bounds=[]; radii=[]
26 for ep in range(EPOCHS):
27 perm=torch.randperm(len(x), generator=g)
28 for j in range(0,len(x),BATCH):
29 idx=perm[j:j+BATCH]
30 loss=((net(x[idx])-y[idx])**2).mean()
31 net.zero_grad(set_to_none=True); loss.backward()
32 temp=0.0 if (controlled and stopped) else cfg['temperature']
33 with torch.no_grad():
34 for p in net.parameters():
35 if p.grad is not None:
36 p.add_(-cfg['lr']*p.grad)
37 if temp>0:
38 p.add_(math.sqrt(2*cfg['lr']*temp)*torch.randn(p.shape))
39 with torch.no_grad():
40 radius=float(torch.sqrt(sum(((p-a)**2).sum() for p,a in zip(net.parameters(),initial))))
41 # Empirical local Gaussian proxy for stationary event probability.
42 scale=max(float(torch.sqrt(sum((p**2).sum() for p in net.parameters())))/18.,1e-4)
43 z=(threshold-radius)/scale
44 pi=float(.5*math.erfc(max(min(z,8.),-8.)/math.sqrt(2)))
45 pi=min(max(pi,1e-3),1-1e-3)
46 bound=min(1.,pi+math.sqrt(pi*chi2)*math.exp(-m*(ep+1)))
47 radii.append(radius); bounds.append(bound); unsafe.append(float(radius>threshold))
48 if controlled and not stopped and bound<=delta:
49 stopped=True; stop_epoch=ep+1
50 net.eval()
51 with torch.no_grad(): metric=float(((net(xt)-yt)**2).mean())
52 result={'metric':metric,'mse':metric,'max_unsafe':max(unsafe),'mean_unsafe':float(np.mean(unsafe)),
53 'post_stop_unsafe':float(np.mean(unsafe[stop_epoch-1:])) if stop_epoch else float(np.mean(unsafe)),
54 'stop_epoch':stop_epoch,'final_radius':radii[-1],'final_bound':bounds[-1]}
55 if record: OBS.setdefault('records',[]).append(result)
56 return result if record else metric
57
58def baseline_fn(cfg):
59 return lambda seed: run(seed,cfg,False)
60def idea_fn(cfg):
61 def train(seed):
62 out=run(seed,cfg,True,True)
63 return out['metric']
64 return train
65
66def main():
67 base=sweep_baseline(baseline_fn, GRID)
68 # Three idea settings, all present in baseline union; choose by 4-seed pilot.
69 pilots=[]
70 for cfg in IDEA_GRID:
71 r=evaluate(lambda s: run(s,cfg,True), seeds=(0,1,2,3))
72 pilots.append((r['mean'],cfg))
73 best_cfg=min(pilots,key=lambda x:x[0])[1]
74 idea=evaluate(idea_fn(best_cfg))
75 # Signature is measured from trained idea systems and tests the transient claim.
76 rr=[r for r in OBS.get('records',[]) if 'final_bound' in r]
77 sig={'prediction':'observed unsafe frequency should not exceed certificate envelope',
78 'predicted_final_bound':float(np.mean([r['final_bound'] for r in rr])) if rr else None,
79 'observed_mean_unsafe_frequency':float(np.mean([r['mean_unsafe'] for r in rr])) if rr else None,
80 'observed_post_stop_unsafe_frequency':float(np.mean([r['post_stop_unsafe'] for r in rr])) if rr else None,
81 'n_trained_models':len(rr)}
82 sig['confirmed']=bool(sig['observed_mean_unsafe_frequency'] <= sig['predicted_final_bound']*1.25+0.02)
83 rep=make_report(TRACK,MODEL,base,idea,{'mechanism_signature':sig,
84 'idea_sweep':pilots,'protocol_note':'Official bench tabular track; custom Langevin loop is the intervention.'})
85 Path('bench_report.json').write_text(json.dumps(rep,indent=2))
86 print(json.dumps({'comparison':rep['comparison'],'baseline_best':base['best_cfg'],
87 'idea_best':best_cfg,'signature':sig},indent=2))
88
89if __name__=='__main__': main()