Skip to content

Commit 3059822

Browse files
committed
Fix linting errors with ruff
1 parent edae1bc commit 3059822

1 file changed

Lines changed: 127 additions & 73 deletions

File tree

evaluate_baselines.py

Lines changed: 127 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1+
import os
12
import sys
23
import traceback
3-
import os
4+
45
import numpy as np
56
import scipy.stats as stats
67

@@ -12,14 +13,18 @@
1213

1314
# Inject pip-installed NVIDIA DLLs into Windows PATH so TF 2.10 can use the GPU without a system CUDA install
1415
try:
15-
site_packages = site.getsitepackages()[1] if len(site.getsitepackages()) > 1 else site.getsitepackages()[0]
16-
nvidia_base = os.path.join(site_packages, "nvidia")
16+
site_packages = (
17+
site.getsitepackages()[1]
18+
if len(site.getsitepackages()) > 1
19+
else site.getsitepackages()[0]
20+
)
21+
nvidia_base = os.path.join(site_packages, 'nvidia')
1722
dll_paths = [
18-
os.path.join(nvidia_base, "cudnn", "bin"),
19-
os.path.join(nvidia_base, "cublas", "bin"),
20-
os.path.join(nvidia_base, "cuda_runtime", "bin"),
23+
os.path.join(nvidia_base, 'cudnn', 'bin'),
24+
os.path.join(nvidia_base, 'cublas', 'bin'),
25+
os.path.join(nvidia_base, 'cuda_runtime', 'bin'),
2126
]
22-
os.environ["PATH"] = os.pathsep.join(dll_paths) + os.pathsep + os.environ.get("PATH", "")
27+
os.environ['PATH'] = os.pathsep.join(dll_paths) + os.pathsep + os.environ.get('PATH', '')
2328
except Exception:
2429
pass
2530

@@ -32,7 +37,9 @@
3237
from validate_pu import load_pu_domain_split
3338

3439
print('Loading Authentic PU dataset (Cross-Domain RPM Split)...')
35-
(X_train_full, y_train_full, cond_train_full), (X_target, y_target, cond_target) = load_pu_domain_split()
40+
(X_train_full, y_train_full, cond_train_full), (X_target, y_target, cond_target) = (
41+
load_pu_domain_split()
42+
)
3643

3744
unique_rpm = set(cond_train_full).union(set(cond_target))
3845
rpm_map = {float(r): float(r) for r in unique_rpm}
@@ -56,11 +63,11 @@ def get_matched_coverage_gap(score, correct, target_n):
5663
sorted_indices = np.argsort(score)[::-1]
5764
hi_indices = sorted_indices[:target_n]
5865
lo_indices = sorted_indices[target_n:]
59-
66+
6067
hi_mask = np.zeros(len(score), dtype=bool)
6168
hi_mask[hi_indices] = True
6269
lo_mask = ~hi_mask
63-
70+
6471
ah = correct[hi_mask].mean() if hi_mask.any() else float('nan')
6572
al = correct[lo_mask].mean() if lo_mask.any() else float('nan')
6673
return ah - al
@@ -75,105 +82,135 @@ def clone_for_mc(layer):
7582
return layer.__class__.from_config(layer.get_config())
7683

7784
seeds = [42, 43, 44, 45, 46]
78-
85+
7986
results = {
8087
'phys_gap': [],
8188
'soft_gap': [],
8289
'mc_gap': [],
8390
'ens_gap': [],
84-
'noise_catch': {db: [] for db in [np.inf, 20, 10, 5, 0]}
91+
'noise_catch': {db: [] for db in [np.inf, 20, 10, 5, 0]},
8592
}
86-
93+
8794
# Test data processing
8895
test_ds = Dataset.from_arrays(
89-
X_target, y_target, cond_target, fs=64000, physics=pu_physics, taxonomy=pu_taxonomy, name='PU_Test'
96+
X_target,
97+
y_target,
98+
cond_target,
99+
fs=64000,
100+
physics=pu_physics,
101+
taxonomy=pu_taxonomy,
102+
name='PU_Test',
90103
)
91104
sig_te = np.stack([test_ds.X[i].reshape(-1) for i in range(len(test_ds.X))]).astype(np.float32)
92105
yte = test_ds.y
93106
cond_te = test_ds.cond
94107
Xin_te = sig_te[..., None]
95-
108+
96109
for seed in seeds:
97-
print(f"\n{'='*80}\n=== RUNNING SEED {seed} ===\n{'='*80}")
110+
print(f'\n{"=" * 80}\n=== RUNNING SEED {seed} ===\n{"=" * 80}')
98111
tf.keras.backend.clear_session()
99112
np.random.seed(seed)
100113
tf.random.set_seed(seed)
101-
114+
102115
# 1. 80/20 Split for Calibration
103116
indices = np.arange(len(y_train_full))
104117
np.random.shuffle(indices)
105-
118+
106119
split_idx = int(0.8 * len(indices))
107120
train_idx = indices[:split_idx]
108121
calib_idx = indices[split_idx:]
109-
110-
X_tr, y_tr, cond_tr = X_train_full[train_idx], y_train_full[train_idx], cond_train_full[train_idx]
111-
X_ca, y_ca, cond_ca = X_train_full[calib_idx], y_train_full[calib_idx], cond_train_full[calib_idx]
112-
113-
train_ds = Dataset.from_arrays(X_tr, y_tr, cond_tr, fs=64000, physics=pu_physics, taxonomy=pu_taxonomy, name='PU_Train')
114-
calib_ds = Dataset.from_arrays(X_ca, y_ca, cond_ca, fs=64000, physics=pu_physics, taxonomy=pu_taxonomy, name='PU_Calib')
115-
122+
123+
X_tr, y_tr, cond_tr = (
124+
X_train_full[train_idx],
125+
y_train_full[train_idx],
126+
cond_train_full[train_idx],
127+
)
128+
X_ca, y_ca, cond_ca = (
129+
X_train_full[calib_idx],
130+
y_train_full[calib_idx],
131+
cond_train_full[calib_idx],
132+
)
133+
134+
train_ds = Dataset.from_arrays(
135+
X_tr, y_tr, cond_tr, fs=64000, physics=pu_physics, taxonomy=pu_taxonomy, name='PU_Train'
136+
)
137+
calib_ds = Dataset.from_arrays(
138+
X_ca, y_ca, cond_ca, fs=64000, physics=pu_physics, taxonomy=pu_taxonomy, name='PU_Calib'
139+
)
140+
116141
# 2. Train Primary Model (Bypass SCM to prevent multiprocess deadlocks)
117142
model = CNSD()
118143
nc = int(train_ds.y.max()) + 1
119144
model.cnn = _train_cnn(train_ds.X, train_ds.y, num_classes=nc, epochs=20, seed=seed)
120145
model.symbolic = model._build_symbolic(train_ds)
121146
model._fitted = True
122-
147+
123148
# 3. Train Ensemble Models
124149
ens = []
125150
nc = int(train_ds.y.max()) + 1
126-
for s in [seed*10, seed*10+1, seed*10+2]:
151+
for s in [seed * 10, seed * 10 + 1, seed * 10 + 2]:
127152
np.random.seed(s)
128153
tf.random.set_seed(s)
129154
m_cnn = _train_cnn(train_ds.X, train_ds.y, num_classes=nc, epochs=20, seed=s)
130155
ens.append(m_cnn)
131-
156+
132157
# 4. Calibrate Tau
133-
sig_ca = np.stack([calib_ds.X[i].reshape(-1) for i in range(len(calib_ds.X))]).astype(np.float32)
158+
sig_ca = np.stack([calib_ds.X[i].reshape(-1) for i in range(len(calib_ds.X))]).astype(
159+
np.float32
160+
)
134161
Xin_ca = sig_ca[..., None]
135162
probs_ca = model.cnn.predict(Xin_ca, batch_size=128, verbose=0)
136163
pred_ca = probs_ca.argmax(1)
137-
correct_ca = (pred_ca == calib_ds.y)
138-
164+
correct_ca = pred_ca == calib_ds.y
165+
139166
best_tau = 1.0
140167
best_gap = -100.0
141168
for tau in [1.0, 1.5, 2.0, 2.5, 3.0]:
142169
model.symbolic.tau = float(tau)
143-
verds = np.array([model.symbolic.diagnose(sig_ca[i], pred_ca[i], calib_ds.cond[i])['verdict'] for i in range(len(sig_ca))])
144-
conf = (verds == 'CONFIRMED')
145-
cnfl = (verds == 'CONFLICT')
170+
verds = np.array(
171+
[
172+
model.symbolic.diagnose(sig_ca[i], pred_ca[i], calib_ds.cond[i])['verdict']
173+
for i in range(len(sig_ca))
174+
]
175+
)
176+
conf = verds == 'CONFIRMED'
177+
cnfl = verds == 'CONFLICT'
146178
ca = correct_ca[conf].mean() if conf.any() else 0.0
147179
fa = correct_ca[cnfl].mean() if cnfl.any() else 1.0
148180
gap = ca - fa
149181
if gap > best_gap:
150182
best_gap = gap
151183
best_tau = tau
152-
153-
print(f"Calibrated best tau = {best_tau} (Calib GAP = {best_gap:+.3f})")
184+
185+
print(f'Calibrated best tau = {best_tau} (Calib GAP = {best_gap:+.3f})')
154186
model.symbolic.tau = float(best_tau)
155-
187+
156188
# 5. Evaluate on Test Set
157189
probs_te = model.cnn.predict(Xin_te, batch_size=128, verbose=0)
158190
pred_te = probs_te.argmax(1)
159-
correct_te = (pred_te == yte)
160-
191+
correct_te = pred_te == yte
192+
161193
# Physics evaluation
162-
verds = np.array([model.symbolic.diagnose(sig_te[i], pred_te[i], cond_te[i])['verdict'] for i in range(len(sig_te))])
163-
conf = (verds == 'CONFIRMED')
164-
cnfl = (verds == 'CONFLICT')
194+
verds = np.array(
195+
[
196+
model.symbolic.diagnose(sig_te[i], pred_te[i], cond_te[i])['verdict']
197+
for i in range(len(sig_te))
198+
]
199+
)
200+
conf = verds == 'CONFIRMED'
201+
cnfl = verds == 'CONFLICT'
165202
ca = correct_te[conf].mean() if conf.any() else float('nan')
166203
fa = correct_te[cnfl].mean() if cnfl.any() else float('nan')
167204
phys_gap = ca - fa
168205
target_n = int(conf.sum())
169206
results['phys_gap'].append(phys_gap)
170-
print(f"Physics GAP={phys_gap:+.3f} (Coverage N={target_n})")
171-
207+
print(f'Physics GAP={phys_gap:+.3f} (Coverage N={target_n})')
208+
172209
# Softmax evaluation at matched coverage
173210
softmax_score = probs_te.max(1)
174211
soft_gap = get_matched_coverage_gap(softmax_score, correct_te, target_n)
175212
results['soft_gap'].append(soft_gap)
176-
213+
177214
# MC-Dropout at matched coverage
178215
mc_model = tf.keras.models.clone_model(model.cnn, clone_function=clone_for_mc)
179216
mc_model.set_weights(model.cnn.get_weights())
@@ -184,65 +221,82 @@ def clone_for_mc(layer):
184221
mc_preds = np.stack(mc_preds)
185222
mc_mean = mc_preds.mean(0)
186223
mc_pred_class = mc_mean.argmax(1)
187-
mc_correct = (mc_pred_class == yte)
224+
mc_correct = mc_pred_class == yte
188225
eps = 1e-12
189-
mc_score = (mc_mean * np.log(mc_mean + eps)).sum(1) # Certainty (negative entropy)
226+
mc_score = (mc_mean * np.log(mc_mean + eps)).sum(1) # Certainty (negative entropy)
190227
mc_gap = get_matched_coverage_gap(mc_score, mc_correct, target_n)
191228
results['mc_gap'].append(mc_gap)
192-
229+
193230
# Ensemble at matched coverage
194-
ens_preds_probs = np.stack([m.predict(Xin_te, batch_size=128, verbose=0) for m in ens]) # (3, n, c)
195-
ens_mean = ens_preds_probs.mean(0) # (n, c)
231+
ens_preds_probs = np.stack(
232+
[m.predict(Xin_te, batch_size=128, verbose=0) for m in ens]
233+
) # (3, n, c)
234+
ens_mean = ens_preds_probs.mean(0) # (n, c)
196235
ens_pred_class = ens_mean.argmax(1)
197-
ens_correct = (ens_pred_class == yte)
236+
ens_correct = ens_pred_class == yte
198237
# Score = negative entropy of ensemble mean
199238
ens_score = (ens_mean * np.log(ens_mean + eps)).sum(1)
200239
ens_gap = get_matched_coverage_gap(ens_score, ens_correct, target_n)
201240
results['ens_gap'].append(ens_gap)
202-
203-
print(f"Matched Coverage GAPs -> Softmax:{soft_gap:+.3f} | MC-Drop:{mc_gap:+.3f} | Ens:{ens_gap:+.3f}")
204-
241+
242+
print(
243+
f'Matched Coverage GAPs -> Softmax:{soft_gap:+.3f} | MC-Drop:{mc_gap:+.3f} | Ens:{ens_gap:+.3f}'
244+
)
245+
205246
# 6. Noise Test
206247
rng = np.random.RandomState(seed)
207-
sig_power = (sig_te ** 2).mean()
248+
sig_power = (sig_te**2).mean()
208249
for snr_db in [np.inf, 20, 10, 5, 0]:
209250
if np.isinf(snr_db):
210251
sig_n = sig_te
211252
else:
212253
npow = sig_power / (10 ** (snr_db / 10))
213254
sig_n = sig_te + rng.randn(*sig_te.shape).astype(np.float32) * np.sqrt(npow)
214-
255+
215256
Xin_n = sig_n[..., None]
216257
# Use ensemble mode vote to define 'unanimous' exactly like Abhi's template
217-
ep_class = np.stack([m.predict(Xin_n, batch_size=128, verbose=0).argmax(1) for m in ens])
258+
ep_class = np.stack(
259+
[m.predict(Xin_n, batch_size=128, verbose=0).argmax(1) for m in ens]
260+
)
218261
v = stats.mode(ep_class, axis=0, keepdims=False).mode
219262
unan = (ep_class == v).all(0)
220-
ok = (v == yte)
221-
222-
pv = np.array([model.symbolic.diagnose(sig_n[i], v[i], cond_te[i])['verdict'] for i in range(len(sig_n))])
223-
pc = (pv == 'CONFLICT')
263+
ok = v == yte
264+
265+
pv = np.array(
266+
[
267+
model.symbolic.diagnose(sig_n[i], v[i], cond_te[i])['verdict']
268+
for i in range(len(sig_n))
269+
]
270+
)
271+
pc = pv == 'CONFLICT'
224272
uw = unan & (~ok)
225273
catch = pc[uw].mean() if uw.sum() > 0 else float('nan')
226274
results['noise_catch'][snr_db].append(catch)
227275
s = 'clean' if np.isinf(snr_db) else f'{snr_db}dB'
228-
print(f" Noise={s:>5} | catch_rate={catch:.3f}")
229-
230-
print("\n" + "="*60 + "\nFINAL AGGREGATED RESULTS (5 Seeds)\n" + "="*60)
231-
print(f"Physics GAP: {np.nanmean(results['phys_gap']):+.3f} ± {np.nanstd(results['phys_gap']):.3f}")
232-
print(f"Softmax GAP: {np.nanmean(results['soft_gap']):+.3f} ± {np.nanstd(results['soft_gap']):.3f}")
233-
print(f"MC-Drop GAP: {np.nanmean(results['mc_gap']):+.3f} ± {np.nanstd(results['mc_gap']):.3f}")
234-
print(f"Ensemble GAP: {np.nanmean(results['ens_gap']):+.3f} ± {np.nanstd(results['ens_gap']):.3f}")
235-
276+
print(f' Noise={s:>5} | catch_rate={catch:.3f}')
277+
278+
print('\n' + '=' * 60 + '\nFINAL AGGREGATED RESULTS (5 Seeds)\n' + '=' * 60)
279+
print(
280+
f'Physics GAP: {np.nanmean(results["phys_gap"]):+.3f} ± {np.nanstd(results["phys_gap"]):.3f}'
281+
)
282+
print(
283+
f'Softmax GAP: {np.nanmean(results["soft_gap"]):+.3f} ± {np.nanstd(results["soft_gap"]):.3f}'
284+
)
285+
print(f'MC-Drop GAP: {np.nanmean(results["mc_gap"]):+.3f} ± {np.nanstd(results["mc_gap"]):.3f}')
286+
print(
287+
f'Ensemble GAP: {np.nanmean(results["ens_gap"]):+.3f} ± {np.nanstd(results["ens_gap"]):.3f}'
288+
)
289+
236290
print("\nNoise Test Catch Rate (Physics catches Ensemble's confident errors):")
237291
for snr_db in [np.inf, 20, 10, 5, 0]:
238292
s = 'clean' if np.isinf(snr_db) else f'{snr_db}dB'
239293
vals = [v for v in results['noise_catch'][snr_db] if not np.isnan(v)]
240294
m = np.nanmean(vals) if len(vals) > 0 else float('nan')
241295
std = np.nanstd(vals) if len(vals) > 0 else float('nan')
242-
print(f" {s:>6}: {m:.3f} ± {std:.3f}")
243-
244-
print("================ DONE ================")
245-
296+
print(f' {s:>6}: {m:.3f} ± {std:.3f}')
297+
298+
print('================ DONE ================')
299+
246300
except Exception:
247301
with open('crash_traceback.txt', 'w') as f:
248302
traceback.print_exc(file=f)

0 commit comments

Comments
 (0)