Level-Adaptive Replay Memory / level_adaptive_bench.py
Failed on benchmark
1import sys, json, math, random
2from pathlib import Path
3import numpy as np
4import torch
5import torch.nn as nn
6sys.path.insert(0, '/home/maxwelhelp/all/math2nn')
7from bench import get_dataset, make_model, evaluate, sweep_baseline, make_report
8
9TRACK = 'tabular'
10MODEL = 'mlp_tiny'
11EPOCHS = 18
12BATCH = 64
13PROPOSALS = 4
14DEPTHS = (1, 2, 4, 8)
15LR_GRID = (1e-3, 3e-3, 1e-2)
16
17
18def seed_all(seed):
19 random.seed(seed)
20 np.random.seed(seed)
21 torch.manual_seed(seed)
22 if torch.cuda.is_available():
23 try:
24 torch.cuda.manual_seed_all(seed)
25 except Exception:
26 pass
27
28
29def fold_escape(rho, M=PROPOSALS):
30 rho = float(np.clip(rho, 0.0, 1.0))
31 return 1.0 - (1.0 - rho) ** M
32
33
34def math_check():
35 rng = np.random.default_rng(1268)
36 rows = []
37 rho = 0.173
38 for M in (1, 2, 4, 8, 16, 32):
39 p = fold_escape(rho, M)
40 observed = rng.binomial(120000, p) / 120000.0
41 rows.append({'M': M, 'predicted': p, 'observed': float(observed),
42 'abs_error': float(abs(p - observed))})
43 optima = []
44 for stale in (0.0, 0.005, 0.015, 0.03, 0.05):
45 vals = []
46 for m in range(1, 17):
47 rho_m = 0.04 + 0.28 * (1.0 - math.exp(-m / 2.0)) - stale * max(0, m - 3)
48 vals.append(fold_escape(rho_m, 8))
49 optima.append({'stale': stale, 'optimal_depth': int(np.argmax(vals) + 1)})
50 return {'formula_check': rows,
51 'max_abs_error': max(r['abs_error'] for r in rows),
52 'stale_curve_optima': optima}
53
54
55def make_pools(ds, seed):
56 rng = np.random.default_rng(seed)
57 n = len(ds['xtr'])
58 order = rng.permutation(n)
59 # Ordered pools emulate successive elite batches; each pool has a level
60 # based on its target quantile, while preserving the same data for both arms.
61 pools = []
62 for start in range(0, n, BATCH):
63 ix = order[start:start + BATCH]
64 pools.append((ds['xtr'][ix], ds['ytr'][ix]))
65 return pools
66
67
68def run_system(seed, lr, fixed_depth=None, adaptive=False, return_signature=False):
69 seed_all(seed)
70 ds = get_dataset(TRACK, seed=seed, n_train=400, n_test=200)
71 pools = make_pools(ds, seed + 991)
72 device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
73 try:
74 net = make_model(MODEL, ds['input_shape'], ds['out_dim']).to(device)
75 opt = torch.optim.Adam(net.parameters(), lr=lr)
76 lossf = nn.MSELoss()
77 history, chosen = [], []
78 current = 1 if adaptive else int(fixed_depth)
79 tau = 0.015
80 for ep in range(EPOCHS):
81 # Recent pools are the replay window. A fixed arm uses the same
82 # construction but never changes depth.
83 if adaptive and ep % 3 == 0:
84 candidates = [current]
85 if current > 1: candidates.append(max(1, current // 2))
86 if current < max(DEPTHS): candidates.append(min(max(DEPTHS), current * 2))
87 net.eval()
88 estimates = {}
89 with torch.no_grad():
90 for dep in candidates:
91 recent = pools[-dep:]
92 xx = torch.cat([p[0] for p in recent]).to(device)
93 yy = torch.cat([p[1] for p in recent]).to(device)
94 # A level-k escape is prediction above the current
95 # pool's upper target quartile; this is independent of
96 # the training loss used for final evaluation.
97 threshold = float(torch.quantile(yy.flatten(), 0.75))
98 rho = float((net(xx).flatten() > threshold).float().mean())
99 estimates[dep] = fold_escape(rho)
100 if current * 2 in estimates and estimates[current * 2] - estimates[current] > tau:
101 current = current * 2
102 elif current // 2 in estimates and estimates[current] - estimates[current // 2] < -tau:
103 current = max(1, current // 2)
104 chosen.append(current)
105 recent = pools[-current:]
106 xx = torch.cat([p[0] for p in recent]).to(device)
107 yy = torch.cat([p[1] for p in recent]).to(device)
108 perm = torch.randperm(len(xx), device=device)
109 net.train(); total = 0.0
110 for i in range(0, len(xx), BATCH):
111 ix = perm[i:i + BATCH]
112 loss = lossf(net(xx[ix]), yy[ix])
113 opt.zero_grad(); loss.backward(); opt.step()
114 total += float(loss.detach()) * len(ix)
115 history.append(total / len(xx))
116 net.eval()
117 with torch.no_grad():
118 pred = net(ds['xte'].to(device))
119 metric = float(((pred - ds['yte'].to(device)) ** 2).mean().cpu())
120 # Trained-model signature: measured rho and observed M-fold
121 # escapes on independent test proposals, not an analytic toy.
122 q = float(torch.quantile(ds['yte'].flatten(), 0.75))
123 p = pred.flatten()
124 rho_obs = float((p > q).float().mean().cpu())
125 groups = p[:(len(p) // PROPOSALS) * PROPOSALS].reshape(-1, PROPOSALS)
126 observed_escape = float((groups > q).any(dim=1).float().mean().cpu())
127 predicted_escape = fold_escape(rho_obs, PROPOSALS)
128 sig = {'rho_hat': rho_obs, 'predicted_escape': predicted_escape,
129 'observed_group_escape': observed_escape,
130 'absolute_error': abs(predicted_escape - observed_escape),
131 'confirmed': abs(predicted_escape - observed_escape) < 0.10,
132 'mean_depth': float(np.mean(chosen)), 'depth_trajectory': chosen}
133 return (metric, sig) if return_signature else metric
134 except Exception:
135 # Explicit CPU fallback for shared/limited CUDA environments.
136 if device.type == 'cuda':
137 torch.cuda.empty_cache()
138 torch.set_default_device('cpu')
139 return run_system(seed, lr, fixed_depth, adaptive, return_signature)
140 raise
141
142
143def baseline_factory(cfg):
144 return lambda seed: run_system(seed, cfg['lr'], fixed_depth=cfg['depth'])
145
146
147def idea_factory(cfg):
148 return lambda seed: run_system(seed, cfg['lr'], adaptive=True)
149
150
151def main():
152 math_result = math_check()
153 grid = [{'lr': lr, 'depth': depth} for lr in LR_GRID for depth in DEPTHS]
154 base = sweep_baseline(baseline_factory, grid)
155 best_lr = float(base['best_cfg']['lr'])
156 # The idea grid is exactly the selected baseline lr plus two nearby lr
157 # values, all of which are present in the baseline union grid.
158 nearby = [lr for lr in LR_GRID if lr != best_lr]
159 idea_cfgs = [{'lr': x} for x in [best_lr] + nearby[:2]]
160 idea_trials = []
161 for cfg in idea_cfgs:
162 r = evaluate(idea_factory(cfg))
163 idea_trials.append({'cfg': cfg, 'result': r})
164 best_trial = min(idea_trials, key=lambda z: z['result']['mean'])
165 idea = best_trial['result']
166 sig_metric, signature = run_system(0, best_trial['cfg']['lr'], adaptive=True,
167 return_signature=True)
168 report = make_report(TRACK, MODEL, base, idea,
169 {'math_sanity': math_result,
170 'trained_model_signature': signature,
171 'idea_lr_trials': idea_trials,
172 'track_choice': 'tabular: replay-memory training intervention; same MLP and regression task in both arms'})
173 report['idea']['selected_cfg'] = best_trial['cfg']
174 Path('bench_report.json').write_text(json.dumps(report, indent=2))
175 print(json.dumps(report, indent=2))
176
177
178if __name__ == '__main__':
179 main()