import sys, json, random import numpy as np import torch import torch.nn as nn sys.path.insert(0, '/home/maxwelhelp/all/math2nn') from bench import get_dataset, make_model, train_model, sweep_baseline, evaluate, make_report SEEDS = tuple(range(8)) EPOCHS, BATCH, WD = 15, 128, 0.0 # The union of all learning rates is evaluated by both methods. LRS = [0.0015, 0.003, 0.006] RHO_GRID = [0.03, 0.05, 0.10] BASE_GRID = [{'lr': lr, 'rho': rho} for lr in LRS for rho in RHO_GRID] # Three idea settings: baseline-best lr plus two nearby union-grid lrs. IDEA_GRID = [{'lr': lr, 'rho': 0.05, 'tau': tau, 'alpha': alpha} for lr, tau, alpha in [(0.0015, 0.5, 1.0), (0.003, 0.5, 1.0), (0.006, 0.5, 1.0)]] def seed_all(seed): random.seed(seed); np.random.seed(seed); torch.manual_seed(seed) if torch.cuda.is_available(): torch.cuda.manual_seed_all(seed) def dataset(seed): return get_dataset('tabular', seed, n_train=4000, n_test=1000) def baseline_factory(cfg): def train(seed): seed_all(190800 + seed) ds = dataset(seed) model = make_model('mlp_tiny', ds['input_shape'], ds['out_dim']) _, metric, history = train_model(model, ds, epochs=EPOCHS, lr=cfg['lr'], batch=BATCH, weight_decay=WD, log=lambda *_: None) return float(metric) return train def sam_factory(cfg): def train(seed, collect=False): seed_all(190800 + seed) ds = dataset(seed) # Use the same robust device ladder policy as bench.train_model. ladder = [('cuda', False), ('cuda', True), ('cpu', False)] if torch.cuda.is_available() else [('cpu', False)] for device, no_cudnn in ladder: try: if no_cudnn: torch.backends.cudnn.enabled = False net = make_model('mlp_tiny', ds['input_shape'], ds['out_dim']).to(device) x, y = ds['xtr'].to(device), ds['ytr'].to(device) lossf = nn.MSELoss() opt = torch.optim.Adam(net.parameters(), lr=cfg['lr'], weight_decay=WD) observed, predicted, norms = [], [], [] for _ in range(EPOCHS): net.train(); order = torch.randperm(len(x), device=device) for start in range(0, len(x), BATCH): xb, yb = x[order[start:start+BATCH]], y[order[start:start+BATCH]] opt.zero_grad(set_to_none=True) lossf(net(xb), yb).backward() first = [p.grad.detach().clone() if p.grad is not None else None for p in net.parameters()] norm = torch.sqrt(sum((g*g).sum() for g in first if g is not None)) ne = norm + 1e-12 radius = min(cfg['rho'], cfg['tau'] * float(ne ** cfg['alpha'])) scale = radius / float(ne ** cfg['alpha']) deltas = [] with torch.no_grad(): for p, g in zip(net.parameters(), first): d = scale * g if g is not None else None deltas.append(d) if d is not None: p.add_(d) # Second forward/backward is the SAM update gradient. opt.zero_grad(set_to_none=True) lossf(net(xb), yb).backward() with torch.no_grad(): for p, d in zip(net.parameters(), deltas): if d is not None: p.sub_(d) opt.step() disp = torch.sqrt(sum((d*d).sum() for d in deltas if d is not None)) observed.append(float(disp / ne)) predicted.append(float(cfg['tau'])) norms.append(float(norm)) net.eval() with torch.no_grad(): metric = float(((net(ds['xte'].to(device)) - ds['yte'].to(device)) ** 2).mean()) if no_cudnn: torch.backends.cudnn.enabled = True result = {'metric': metric} if collect: result.update({'observed_disp_over_grad': float(np.mean(observed)), 'predicted_bound': float(cfg['tau']), 'fraction_clipped': float(np.mean(np.asarray(norms) < (cfg['rho']/cfg['tau']) ** (1/cfg['alpha'])))}) return result except RuntimeError: if no_cudnn: torch.backends.cudnn.enabled = True raise RuntimeError('SAM training failed on CUDA and CPU') return train def main(): # Baseline sweep has the complete lr union and sweeps its method radius knob. base = sweep_baseline(baseline_factory, BASE_GRID, seeds=SEEDS) best_cfg = base['best_cfg'] # Explicitly ensure the idea settings include baseline-best lr and nearby values. idea_runs = [] for cfg in IDEA_GRID: vals = [sam_factory(cfg)(s)['metric'] for s in SEEDS] idea_runs.append({'cfg': cfg, 'mean': float(np.mean(vals)), 'per_seed': vals}) chosen = min(idea_runs, key=lambda z: z['mean']) idea_cfg = chosen['cfg'] idea_full = evaluate(lambda s: sam_factory(idea_cfg)(s)['metric'], SEEDS) # Re-test signature on trained models, not an analytical toy. sig = [sam_factory(idea_cfg)(s, collect=True) for s in SEEDS] observed = float(np.mean([r['observed_disp_over_grad'] for r in sig])) predicted = float(idea_cfg['tau']) signature = { 'predicted_max_displacement_over_gradient': predicted, 'observed_mean_displacement_over_gradient': observed, 'relative_error_to_bound': abs(observed-predicted) / predicted, 'observed_fraction_clipped': float(np.mean([r['fraction_clipped'] for r in sig])), 'confirmed': bool(observed <= predicted * 1.05) } report = make_report('tabular', 'mlp_tiny', base, {'config': idea_cfg, **idea_full}, {'mechanism_signature': signature, 'track_justification': 'Optimizer intervention; tabular Friedman#1 is the mandated optimizer track.', 'idea_sweep': idea_runs, 'budget': {'epochs': EPOCHS, 'batch': BATCH, 'seeds': list(SEEDS)}}) with open('bench_report.json', 'w') as f: json.dump(report, f, indent=2) print(json.dumps(report, indent=2)) if __name__ == '__main__': main()