JustAGeek commited on
Commit
f31d6bc
·
verified ·
1 Parent(s): 6eecae3

Upload run_multiple_seeds.py with huggingface_hub

Browse files
Files changed (1) hide show
  1. run_multiple_seeds.py +271 -0
run_multiple_seeds.py ADDED
@@ -0,0 +1,271 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ run_multiple_seeds.py
3
+ Multi-seed training for statistical significance (Phase 2, Step 4).
4
+
5
+ Trains a student model N times with different seeds, reports mean +/- std
6
+ for median, P90, P99 localization error in meters.
7
+
8
+ Supports: mobilenet, tinycnn, unet
9
+ Modes: standalone, distill
10
+
11
+ Usage:
12
+ python run_multiple_seeds.py
13
+ Edit CONFIG below to select model/mode/seeds.
14
+ """
15
+ import os
16
+ import sys
17
+ import time
18
+ import random
19
+ import torch
20
+ import torch.nn as nn
21
+ import numpy as np
22
+ import warnings
23
+ with warnings.catch_warnings():
24
+ warnings.filterwarnings("ignore", category=FutureWarning)
25
+
26
+ from utils import localization_error, write_log
27
+ from data_loader import load_data
28
+
29
+ # ============================================================
30
+ # CONFIG
31
+ # ============================================================
32
+ STUDENT_TYPE = 'mobilenet' # 'mobilenet', 'tinycnn', 'unet'
33
+ MODE = 'standalone' # 'standalone' or 'distill'
34
+ SCHEDULER_TYPE = 'none' # 'none', 'cosine', 'step' (only for unet)
35
+
36
+ SEEDS = [42, 123, 777]
37
+ EPOCHS = 50
38
+ LR = 1e-4
39
+ BATCH_SIZE = 32
40
+ NUM_WORKERS = 4
41
+
42
+ ALPHA = 0.7
43
+ BETA = 0.3
44
+
45
+ TEACHER_TRAIN_PT = './data/teacher_train_outputs.pt'
46
+ TEACHER_TEST_PT = './data/teacher_test_outputs.pt'
47
+
48
+ # Data paths (Fig 10b -- Jacobs Hall)
49
+ trainpath = [
50
+ './data/dataset_jacobs_July28.mat',
51
+ './data/dataset_non_fov_train_jacobs_July28_2.mat',
52
+ './data/dataset_fov_train_jacobs_July28_2.mat',
53
+ ]
54
+ testpath = [
55
+ './data/dataset_fov_test_jacobs_July28_2.mat',
56
+ './data/dataset_non_fov_test_jacobs_July28_2.mat',
57
+ ]
58
+
59
+
60
+ def set_seed(seed):
61
+ random.seed(seed)
62
+ np.random.seed(seed)
63
+ torch.manual_seed(seed)
64
+ if torch.cuda.is_available():
65
+ torch.cuda.manual_seed_all(seed)
66
+
67
+
68
+ def create_student(student_type, device):
69
+ if student_type == 'mobilenet':
70
+ from student_mobilenet import MobileNetStudent
71
+ return MobileNetStudent(input_nc=4).to(device)
72
+ elif student_type == 'tinycnn':
73
+ from student_tinycnn import TinyCNNStudent
74
+ return TinyCNNStudent(input_nc=4).to(device)
75
+ elif student_type == 'unet':
76
+ from student_unet import MobileNetUNetStudent
77
+ return MobileNetUNetStudent(input_nc=4).to(device)
78
+ else:
79
+ raise ValueError(f'Unknown student type: {student_type}')
80
+
81
+
82
+ def train_one_seed(seed, train_loader, test_loader, device, save_dir):
83
+ """Train a fresh model with the given seed. Returns best test metrics."""
84
+ set_seed(seed)
85
+ print(f'\n{"="*60}')
86
+ print(f' SEED {seed} — {STUDENT_TYPE} / {MODE}')
87
+ print(f'{"="*60}')
88
+
89
+ student = create_student(STUDENT_TYPE, device)
90
+ optimizer = torch.optim.Adam(student.parameters(), lr=LR)
91
+ criterion = nn.MSELoss()
92
+
93
+ # Optional LR scheduler
94
+ scheduler = None
95
+ if SCHEDULER_TYPE == 'cosine':
96
+ scheduler = torch.optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max=EPOCHS)
97
+ elif SCHEDULER_TYPE == 'step':
98
+ scheduler = torch.optim.lr_scheduler.StepLR(optimizer, step_size=20, gamma=0.5)
99
+
100
+ best_median = float('inf')
101
+ best_p90 = float('inf')
102
+ best_p99 = float('inf')
103
+ seed_dir = os.path.join(save_dir, f'seed_{seed}')
104
+ os.makedirs(seed_dir, exist_ok=True)
105
+
106
+ for epoch in range(1, EPOCHS + 1):
107
+ epoch_start = time.time()
108
+
109
+ # Train
110
+ student.train()
111
+ train_loss_sum = 0
112
+ for data in train_loader:
113
+ inputs = data[0].to(device)
114
+ labels = data[1].to(device)
115
+
116
+ output = student(inputs)
117
+ L_GT = criterion(output, labels)
118
+
119
+ if MODE == 'distill':
120
+ teacher_out = data[2].to(device)
121
+ L_KD = criterion(output, teacher_out)
122
+ loss = ALPHA * L_GT + BETA * L_KD
123
+ else:
124
+ loss = L_GT
125
+
126
+ optimizer.zero_grad()
127
+ loss.backward()
128
+ optimizer.step()
129
+ train_loss_sum += loss.item()
130
+
131
+ # Test
132
+ student.eval()
133
+ test_errors = []
134
+ with torch.no_grad():
135
+ for data in test_loader:
136
+ inputs = data[0].to(device)
137
+ labels = data[1].to(device)
138
+ output = student(inputs)
139
+ test_errors.extend(localization_error(
140
+ output.cpu().numpy(),
141
+ labels.cpu().numpy(),
142
+ scale=0.1,
143
+ ))
144
+
145
+ test_median = np.median(test_errors)
146
+ test_p90 = np.percentile(test_errors, 90)
147
+ test_p99 = np.percentile(test_errors, 99)
148
+
149
+ if scheduler is not None:
150
+ scheduler.step()
151
+
152
+ # Log
153
+ run_label = f'seed{seed}_{STUDENT_TYPE}'
154
+ write_log([str(test_median)], run_label, log_dir=seed_dir, log_type='test_median_error')
155
+ write_log([str(test_p90)], run_label, log_dir=seed_dir, log_type='test_90_error')
156
+ write_log([str(test_p99)], run_label, log_dir=seed_dir, log_type='test_99_error')
157
+
158
+ elapsed = time.time() - epoch_start
159
+ marker = ''
160
+ if test_median < best_median:
161
+ best_median = test_median
162
+ best_p90 = test_p90
163
+ best_p99 = test_p99
164
+ torch.save(student.state_dict(), os.path.join(seed_dir, f'best_{STUDENT_TYPE}.pth'))
165
+ marker = ' *'
166
+
167
+ if epoch % 10 == 0 or epoch == 1 or marker:
168
+ print(f' Epoch {epoch}/{EPOCHS} | {elapsed:.0f}s | '
169
+ f'med={test_median:.3f}m P90={test_p90:.3f}m P99={test_p99:.3f}m{marker}')
170
+
171
+ print(f' Seed {seed} done. Best: med={best_median:.4f}m P90={best_p90:.3f}m P99={best_p99:.3f}m')
172
+ return {'median': best_median, 'p90': best_p90, 'p99': best_p99}
173
+
174
+
175
+ def main():
176
+ device = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')
177
+ print(f'Device: {device}')
178
+ print(f'Student: {STUDENT_TYPE} | Mode: {MODE} | Seeds: {SEEDS}')
179
+ print(f'Epochs: {EPOCHS} | Scheduler: {SCHEDULER_TYPE}')
180
+
181
+ # Load data once (shared across seeds)
182
+ print('\nLoading training data...')
183
+ train_w_list, train_lbl_list = [], []
184
+ for fp in trainpath:
185
+ _, w, lbl = load_data(fp)
186
+ if lbl.dim() == 3:
187
+ lbl = lbl.unsqueeze(1)
188
+ train_w_list.append(w)
189
+ train_lbl_list.append(lbl)
190
+ train_w = torch.cat(train_w_list, dim=0)
191
+ train_lbl = torch.cat(train_lbl_list, dim=0)
192
+ print(f'Training samples: {train_w.shape[0]}')
193
+
194
+ print('Loading testing data...')
195
+ test_w_list, test_lbl_list = [], []
196
+ for fp in testpath:
197
+ _, w, lbl = load_data(fp)
198
+ if lbl.dim() == 3:
199
+ lbl = lbl.unsqueeze(1)
200
+ test_w_list.append(w)
201
+ test_lbl_list.append(lbl)
202
+ test_w = torch.cat(test_w_list, dim=0)
203
+ test_lbl = torch.cat(test_lbl_list, dim=0)
204
+ print(f'Testing samples: {test_w.shape[0]}')
205
+
206
+ # Load teacher if distilling
207
+ if MODE == 'distill':
208
+ print('Loading teacher outputs...')
209
+ train_teacher = torch.load(TEACHER_TRAIN_PT, weights_only=True)
210
+ test_teacher = torch.load(TEACHER_TEST_PT, weights_only=True)
211
+ assert train_teacher.shape[0] == train_w.shape[0]
212
+ train_data = torch.utils.data.TensorDataset(train_w, train_lbl, train_teacher)
213
+ test_data = torch.utils.data.TensorDataset(test_w, test_lbl, test_teacher)
214
+ else:
215
+ train_data = torch.utils.data.TensorDataset(train_w, train_lbl)
216
+ test_data = torch.utils.data.TensorDataset(test_w, test_lbl)
217
+
218
+ train_loader = torch.utils.data.DataLoader(
219
+ train_data, batch_size=BATCH_SIZE, shuffle=True,
220
+ num_workers=NUM_WORKERS, pin_memory=True, persistent_workers=True,
221
+ )
222
+ test_loader = torch.utils.data.DataLoader(
223
+ test_data, batch_size=BATCH_SIZE, shuffle=False,
224
+ num_workers=NUM_WORKERS, pin_memory=True, persistent_workers=True,
225
+ )
226
+
227
+ # Run all seeds
228
+ save_dir = os.path.join('./runs', f'multiseed_{STUDENT_TYPE}_{MODE}_{time.strftime("%Y-%m-%d-%H-%M-%S")}')
229
+ os.makedirs(save_dir, exist_ok=True)
230
+
231
+ all_results = []
232
+ for seed in SEEDS:
233
+ result = train_one_seed(seed, train_loader, test_loader, device, save_dir)
234
+ all_results.append(result)
235
+
236
+ # Statistical report
237
+ medians = [r['median'] for r in all_results]
238
+ p90s = [r['p90'] for r in all_results]
239
+ p99s = [r['p99'] for r in all_results]
240
+
241
+ print(f'\n\n{"="*65}')
242
+ print(f' MULTI-SEED RESULTS: {STUDENT_TYPE.upper()} / {MODE.upper()}')
243
+ print(f'{"="*65}')
244
+ for i, seed in enumerate(SEEDS):
245
+ r = all_results[i]
246
+ print(f' Seed {seed:<5}: median={r["median"]:.4f}m P90={r["p90"]:.3f}m P99={r["p99"]:.3f}m')
247
+ print(f'{"-"*65}')
248
+ print(f' Median error: {np.mean(medians):.4f} +/- {np.std(medians):.4f} m')
249
+ print(f' P90 error: {np.mean(p90s):.3f} +/- {np.std(p90s):.3f} m')
250
+ print(f' P99 error: {np.mean(p99s):.3f} +/- {np.std(p99s):.3f} m')
251
+ print(f'{"="*65}')
252
+
253
+ # Save report
254
+ report_path = os.path.join(save_dir, 'report.txt')
255
+ with open(report_path, 'w') as f:
256
+ f.write(f'Student: {STUDENT_TYPE}\n')
257
+ f.write(f'Mode: {MODE}\n')
258
+ f.write(f'Scheduler: {SCHEDULER_TYPE}\n')
259
+ f.write(f'Seeds: {SEEDS}\n')
260
+ f.write(f'Epochs: {EPOCHS}\n\n')
261
+ for i, seed in enumerate(SEEDS):
262
+ r = all_results[i]
263
+ f.write(f'Seed {seed}: median={r["median"]:.4f}m P90={r["p90"]:.3f}m P99={r["p99"]:.3f}m\n')
264
+ f.write(f'\nMedian: {np.mean(medians):.4f} +/- {np.std(medians):.4f} m\n')
265
+ f.write(f'P90: {np.mean(p90s):.3f} +/- {np.std(p90s):.3f} m\n')
266
+ f.write(f'P99: {np.mean(p99s):.3f} +/- {np.std(p99s):.3f} m\n')
267
+ print(f'\nReport saved to: {report_path}')
268
+
269
+
270
+ if __name__ == '__main__':
271
+ main()