Variance-aware gradient reduction trees / bench_experiment.py
Failed on benchmark
1import sys, json, random, heapq
2from pathlib import Path
3import numpy as np
4import torch
5import torch.nn.functional as F
6sys.path.insert(0, '/home/maxwelhelp/all/math2nn')
7from bench import get_dataset, make_model, sweep_baseline, make_report, evaluate
8
9SEEDS=tuple(range(8)); GRID=[{'lr':1e-3,'epochs':12},{'lr':3e-3,'epochs':12},{'lr':6e-3,'epochs':12}]; BATCH=128; CHUNKS=8
10class Node:
11 def __init__(self,a=None,b=None,leaf=None): self.a,self.b,self.leaf=a,b,leaf
12def balanced(ids):
13 ids=list(ids)
14 if len(ids)==1:return Node(leaf=ids[0])
15 m=len(ids)//2; return Node(balanced(ids[:m]),balanced(ids[m:]))
16def huffman(w):
17 h=[(float(x),i,Node(leaf=i)) for i,x in enumerate(w)]; heapq.heapify(h); serial=len(h)
18 while len(h)>1:
19 a,_,x=heapq.heappop(h); b,_,y=heapq.heappop(h); heapq.heappush(h,(a+b,serial,Node(x,y))); serial+=1
20 return h[0][2]
21def depths(t,d=0,o=None):
22 o={} if o is None else o
23 if t.leaf is not None:o[t.leaf]=d
24 else:depths(t.a,d+1,o);depths(t.b,d+1,o)
25 return o
26def seed_all(s):
27 random.seed(s);np.random.seed(s);torch.manual_seed(s)
28def round_stages(x,n):
29 # Shape-preserving proxy for n internal low-precision additions. Adding zero
30 # isolates rounding while avoiding the invalid operation of summing disjoint chunks.
31 z=torch.zeros_like(x)
32 for _ in range(n): x=(x.to(torch.float16)+z.to(torch.float16)).to(torch.float32)
33 return x
34def run(seed,cfg,adaptive,signature=False):
35 seed_all(seed); ds=get_dataset('tabular',seed=seed,n_train=400,n_test=400)
36 net=make_model('mlp_tiny',ds['input_shape'],ds['out_dim']); opt=torch.optim.AdamW(net.parameters(),lr=cfg['lr'])
37 xtr,ytr=ds['xtr'],ds['ytr']; rng=np.random.default_rng(seed+991); em=np.ones(CHUNKS); eq=np.ones(CHUNKS); last=None; errors=[]
38 net.train()
39 for _ in range(cfg['epochs']):
40 perm=rng.permutation(len(xtr))
41 for st in range(0,len(xtr),BATCH):
42 idx=torch.as_tensor(perm[st:st+BATCH]); loss=F.mse_loss(net(xtr[idx]),ytr[idx]); opt.zero_grad();loss.backward()
43 ps=[p for p in net.parameters() if p.grad is not None]; flat=torch.cat([p.grad.detach().reshape(-1) for p in ps]); raw=list(torch.tensor_split(flat,CHUNKS))
44 means=np.array([float(x.mean()) for x in raw]); secs=np.array([float((x*x).mean()) for x in raw]); em=.9*em+.1*means;eq=.9*eq+.1*secs;var=np.maximum(eq-em*em,1e-12)
45 tree=huffman(var) if adaptive else balanced(range(CHUNKS)); last=depths(tree)
46 approx=torch.cat([round_stages(x,last[i]) for i,x in enumerate(raw)]); errors.append(float(((approx-flat)**2).mean()))
47 pos=0
48 for p in ps:n=p.numel();p.grad.copy_(approx[pos:pos+n].reshape_as(p));pos+=n
49 opt.step()
50 with torch.no_grad(): metric=float(((net(ds['xte'])-ds['yte'])**2).mean())
51 if signature:return metric,{'last_depths':{str(k):int(v) for k,v in last.items()},'observed_reduction_mse':float(np.mean(errors)),'n_steps':len(errors)}
52 return metric
53def baseline_fn(c):return lambda s:run(s,c,False)
54def idea_fn(c):return lambda s:run(s,c,True)
55def main():
56 base=sweep_baseline(baseline_fn,GRID); tried=[]
57 for c in GRID:tried.append({'cfg':c,'mean':float(np.mean([idea_fn(c)(s) for s in range(4)]))})
58 best=min(tried,key=lambda x:x['mean'])['cfg']; idea=evaluate(idea_fn(best),seeds=SEEDS)
59 ss=[run(s,best,True,True)[1] for s in SEEDS]; da=[x['last_depths'] for x in ss]; md=float(np.mean([min(map(int,d.values())) for d in da]))
60 sig={'predicted':'high-variance chunks have shallower Huffman depth','observed_mean_min_depth':md,'balanced_depth':3,'trained_model_measurements':da,'observed_reduction_mse_mean':float(np.mean([x['observed_reduction_mse'] for x in ss])),'confirmed':bool(md<3)}
61 rep=make_report('tabular','mlp_tiny',base,idea,{'idea_sweep':tried,'mechanism_signature':sig});rep['idea']['best_cfg']=best;rep['idea']['sweep']=tried;Path('bench_report.json').write_text(json.dumps(rep,indent=2));print(json.dumps(rep,indent=2))
62if __name__=='__main__':main()