"""Final OOD audit: v8-mvmm (multi-view multi-modal) classifiers. 4-way comparison: - OLD: real_eval_current/ (Kaggle 4-class only) - v8-RAW: real_eval_v8_retrained/ (dataset_v8 axial-T1c, 78% positive) - v8-BAL: real_eval_v8_balanced/ (+ OpenNeuro healthy, pos_weight) - v8-MVMM: real_eval_v8_mvmm/ (+ BraTS sag/cor/T1/T2/FLAIR) This is the round where the training data actually contains the acquisition geometries and modalities the OOD set tests on. Target: ≥70% OOD tumor recall while keeping FPR <30%. """ from __future__ import annotations import sys import time from pathlib import Path import numpy as np from PIL import Image ROOT = Path(__file__).resolve().parent.parent sys.path.insert(0, str(ROOT)) from scripts.eval_ood_cascade import SEG_ONNX, MIN_TUMOR_AREA, _sess, _preprocess_seg, _preprocess_clf, seg_tta, GT CLF_SETS = { 'OLD': {m: ROOT/'real_eval_current'/m/'best_weights.onnx' for m in ('cnn','transfer','vit')}, 'v8-RAW': {m: ROOT/'real_eval_v8_retrained'/m/'best_weights.onnx' for m in ('cnn','transfer','vit')}, 'v8-BAL': {m: ROOT/'real_eval_v8_balanced'/m/'best_weights.onnx' for m in ('cnn','transfer','vit')}, 'v8-MVMM': {m: ROOT/'real_eval_v8_mvmm'/m/'best_weights.onnx' for m in ('cnn','transfer','vit')}, } NORMALIZE_IMAGENET = {'cnn': False, 'transfer': True, 'vit': True} SAMPLES_DIR = ROOT / 'samples' / 'ood' def classify(sess, img, normalise): chw = _preprocess_clf(img, normalise) logit = float(sess.run(None, {sess.get_inputs()[0].name: chw[None]})[0].reshape(-1)[0]) return 1.0 / (1.0 + np.exp(-logit)) def stats(rows, pkey, thr=0.5): TP = sum(1 for r in rows if r['gt']=='tumor' and r[pkey]>=thr) FN = sum(1 for r in rows if r['gt']=='tumor' and r[pkey]=thr) TN = sum(1 for r in rows if r['gt']=='no_tumor' and r[pkey]5.0%} {fp:>4.0%} {acc:>4.0%} {f1:.2f}{marker}') # ============= per-source on v8-MVMM (the candidate) ============ print('\n' + '='*84) print('PER-SOURCE on v8-MVMM (the new candidate)') print('='*84) by_src = {} for r in rows: by_src.setdefault(r['source'], []).append(r) print(f'\n{"source":48s} GT n cnn trans vit') for src in sorted(by_src): rs = by_src[src]; gt = rs[0]['gt']; n = len(rs) kind = 'recall' if gt=='tumor' else 'FPR' cells = [] for c in ('cnn', 'transfer', 'vit'): hits = sum(1 for r in rs if r[f'v8-MVMM__{c}'] >= 0.5) cells.append(f'{hits/n:.0%}'.rjust(5)) print(f' {src:46s} {gt[:6]:6s} {n:3d} {cells[0]} {cells[1]} {cells[2]} <- {kind}') # ============= tumor consensus across all 4 sets ================ print('\n' + '='*84) print('CONSENSUS on 36 OOD TUMOR SAMPLES — improvement progression') print('='*84) tum = [r for r in rows if r['gt'] == 'tumor'] for tag in ('OLD', 'v8-RAW', 'v8-BAL', 'v8-MVMM'): if tag not in sets: continue all_yes = sum(1 for r in tum if all(r[f'{tag}__{c}']>=0.5 for c in ('cnn','transfer','vit'))) all_no = sum(1 for r in tum if all(r[f'{tag}__{c}']<0.5 for c in ('cnn','transfer','vit'))) print(f' {tag:10s} all_3_say_tumor={all_yes:3d}/{len(tum)} ({all_yes/len(tum):.0%}) ' f'all_3_say_no_tumor(catastrophic_miss)={all_no:3d}/{len(tum)} ({all_no/len(tum):.0%})') neg = [r for r in rows if r['gt'] == 'no_tumor'] print(f'\nCONSENSUS on {len(neg)} OOD HEALTHY (OpenNeuro coronal T1)') print('-'*84) for tag in ('OLD', 'v8-RAW', 'v8-BAL', 'v8-MVMM'): if tag not in sets: continue all_no = sum(1 for r in neg if all(r[f'{tag}__{c}']<0.5 for c in ('cnn','transfer','vit'))) all_yes = sum(1 for r in neg if all(r[f'{tag}__{c}']>=0.5 for c in ('cnn','transfer','vit'))) print(f' {tag:10s} all_3_correctly_no_tumor={all_no:3d}/{len(neg)} ({all_no/len(neg):.0%}) ' f'all_3_wrongly_say_tumor(catastrophic_FP)={all_yes:3d}/{len(neg)} ({all_yes/len(neg):.0%})') if __name__ == '__main__': main()