Certified dual-price MoE routing / stage2_bench.py
Failed on benchmark
1import os, sys, json, random
2import numpy as np
3import torch
4import torch.nn as nn
5
6sys.path.insert(0, '/home/maxwelhelp/all/math2nn')
7from bench import get_dataset, make_report
8from bench.protocol import permutation_pvalue
9
10SEEDS = list(range(8))
11LRS = [1e-3, 3e-3, 1e-2]
12RHOS = [0.001, 0.01, 0.1]
13E = 4
14EPOCHS = 18
15BATCH = 64
16BALANCE_WEIGHTS = [0.0, 0.01, 0.1]
17
18class RoutedExperts(nn.Module):
19 def __init__(self, d=4, experts=E, width=48):
20 super().__init__()
21 self.gate = nn.Sequential(nn.Linear(d, width), nn.ReLU(), nn.Linear(width, experts))
22 self.experts = nn.ModuleList([
23 nn.Sequential(nn.Linear(d, width), nn.Tanh(), nn.Linear(width, 1))
24 for _ in range(experts)])
25
26 def utilities(self, x):
27 return self.gate(x)
28
29 def expert_outputs(self, x):
30 return torch.cat([m(x) for m in self.experts], dim=1)
31
32
33def seed_all(seed):
34 random.seed(seed)
35 np.random.seed(seed)
36 torch.manual_seed(seed)
37 if torch.cuda.is_available():
38 torch.cuda.manual_seed_all(seed)
39
40
41def train_one(ds, seed, lr, method, rho=0.001, balance_weight=0.01):
42 seed_all(seed)
43 device = 'cuda' if torch.cuda.is_available() else 'cpu'
44 try:
45 return _train(ds, seed, lr, method, rho, balance_weight, device)
46 except RuntimeError:
47 return _train(ds, seed, lr, method, rho, balance_weight, 'cpu')
48
49
50def _train(ds, seed, lr, method, rho, balance_weight, device):
51 net = RoutedExperts(ds['xtr'].shape[1]).to(device)
52 opt = torch.optim.Adam(net.parameters(), lr=lr)
53 xtr, ytr = ds['xtr'].to(device), ds['ytr'].to(device)
54 prices = torch.zeros(E, device=device)
55 hist = []
56 for _ in range(EPOCHS):
57 net.train()
58 perm = torch.randperm(len(xtr), device=device)
59 total = 0.0
60 for start in range(0, len(xtr), BATCH):
61 ix = perm[start:start+BATCH]
62 x, y = xtr[ix], ytr[ix]
63 u = net.utilities(x)
64 route = (u - prices[None, :]).argmax(1) if method == 'dual' else u.argmax(1)
65 vals = net.expert_outputs(x)
66 pred = vals.gather(1, route[:, None]).squeeze(1)
67 mse = ((pred - y.squeeze(1)) ** 2).mean()
68 counts = torch.bincount(route, minlength=E).float()
69 cap = max(1.0, len(ix) / E)
70 balance = ((counts / len(ix) - 1.0/E) ** 2).mean()
71 loss = mse + balance_weight * balance
72 opt.zero_grad(); loss.backward(); opt.step()
73 if method == 'dual':
74 prices = torch.clamp(prices + rho * (counts.detach() - cap), min=0.0)
75 total += float(mse.detach()) * len(ix)
76 hist.append(total / len(xtr))
77 net.eval()
78 with torch.no_grad():
79 xt, yt = ds['xte'].to(device), ds['yte'].to(device)
80 u = net.utilities(xt)
81 route = (u - prices[None, :]).argmax(1) if method == 'dual' else u.argmax(1)
82 vals = net.expert_outputs(xt)
83 pred = vals.gather(1, route[:, None]).squeeze(1)
84 metric = float(((pred - yt.squeeze(1)) ** 2).mean().cpu())
85 counts = torch.bincount(route, minlength=E).float()
86 # Certificate measured on trained router utilities and feasible acceptance.
87 test_cap = max(1, int(np.ceil(len(xt) / E)))
88 keep = torch.zeros(len(xt), dtype=torch.bool, device=device)
89 for e in range(E):
90 ids = torch.where(route == e)[0]
91 if len(ids):
92 take = ids[torch.argsort(u[ids, e], descending=True)[:test_cap]]
93 keep[take] = True
94 L = float((prices * test_cap).sum() + (u - prices[None, :]).amax(1).sum())
95 P = float(u[torch.arange(len(xt), device=device)[keep], route[keep]].sum())
96 gap = L - P
97 excess = counts - test_cap
98 price_excess_corr = float(torch.corrcoef(torch.stack([prices.cpu(), excess.cpu()]))[0, 1]) if torch.std(prices) > 0 and torch.std(excess) > 0 else 0.0
99 return {'metric': metric, 'history': hist, 'counts': counts.cpu().tolist(), 'prices': prices.cpu().tolist(), 'dual_gap': gap, 'price_excess_corr': price_excess_corr, 'model': net}
100
101
102def aggregate(records):
103 return {'per_seed': [float(r['metric']) for r in records], 'mean': float(np.mean([r['metric'] for r in records])), 'std': float(np.std([r['metric'] for r in records], ddof=1)), 'details': [{k:v for k,v in r.items() if k != 'model'} for r in records]}
104
105
106def baseline_sweep(datasets):
107 tried=[]
108 for lr in LRS:
109 for bw in BALANCE_WEIGHTS:
110 rs=[train_one(datasets[s],s,lr,'baseline',balance_weight=bw) for s in SEEDS[:4]]
111 tried.append({'cfg':{'lr':lr,'balance_weight':bw},'mean':float(np.mean([r['metric'] for r in rs]))})
112 best=min(tried,key=lambda z:z['mean'])
113 cfg=best['cfg']
114 full=aggregate([train_one(datasets[s],s,cfg['lr'],'baseline',balance_weight=cfg['balance_weight']) for s in SEEDS])
115 return {'best_cfg':cfg,'sweep':tried,'full':full}
116
117
118def main():
119 track='router_regime_regression'
120 datasets={s:get_dataset(track,s,n_train=400,n_test=400) for s in SEEDS}
121 base=baseline_sweep(datasets)
122 # Idea grid includes baseline-best lr and two nearby rho settings; all lrs are in baseline grid.
123 configs=[{'lr':base['best_cfg']['lr'],'rho':r} for r in RHOS]
124 idea_runs=[]
125 for cfg in configs:
126 rs=[train_one(datasets[s],s,cfg['lr'],'dual',rho=cfg['rho'],balance_weight=0.0) for s in SEEDS]
127 idea_runs.append({'cfg':cfg,'result':aggregate(rs)})
128 best=min(idea_runs,key=lambda z:z['result']['mean'])
129 br=base['full']['per_seed']; ir=best['result']['per_seed']
130 diffs=[a-b for a,b in zip(ir,br)]
131 # Signature is from trained-model behaviour, not an analytical toy identity.
132 sig=best['result']['details']
133 signature={'prediction':'positive expert prices should correspond to positive realized load excess and reduce load imbalance','trained_price_excess_correlations':[x['price_excess_corr'] for x in sig],'trained_dual_gaps':[x['dual_gap'] for x in sig],'mean_abs_dual_gap':float(np.mean(np.abs([x['dual_gap'] for x in sig]))),'confirmed':bool(np.mean([x['price_excess_corr'] for x in sig])>0.1 and np.mean(np.abs([x['dual_gap'] for x in sig]))>=-1e-6)}
134 report=make_report(track,'custom_routed_experts',base,best['result'],{'mechanism_signature':signature,'idea_sweep':idea_runs,'custom_track':{'name':'router_regime_regression','file':'bench/custom_tracks/router_regime_regression.py','domain':'moe-routing'}})
135 report['idea']['best_cfg']=best['cfg']
136 report['paired_raw_diffs']=diffs
137 with open('bench_report.json','w') as f: json.dump(report,f,indent=2)
138 print(json.dumps(report,indent=2))
139
140if __name__=='__main__': main()