"""Qualitative model checks and predeclared illumination-shift diagnostic.""" from dataclasses import asdict import json import cv2 import numpy as np import pandas as pd import torch from PIL import Image,ImageDraw from audit_pipeline import partition from dataset import LunarTerrainDataset from experiment_utils import ROOT,atomic_csv,atomic_json,metrics from training_engine import Settings,build_model,load_checkpoint,setup def bootstrap_groups(out,pred,threshold): if (out/'holdout_uncertainty.csv').exists(): return groups=[] for _,g in pred.groupby('group'): p=g.probability.to_numpy()>=threshold; y=g.label.to_numpy() groups.append([((y==0)&(~p)).sum(),(y==0).sum(),((y==1)&p).sum(),(y==1).sum()]) groups=np.asarray(groups); rng=np.random.default_rng(717); scores=[] for _ in range(1000): sums=groups[rng.integers(0,len(groups),len(groups))].sum(axis=0) if sums[1] and sums[3]: scores.append(.5*(sums[0]/sums[1]+sums[2]/sums[3])) atomic_csv(out/'holdout_uncertainty.csv',pd.DataFrame([dict(groups=len(groups),bootstrap_replicates=len(scores), lower_95=np.quantile(scores,.025),upper_95=np.quantile(scores,.975),unit='image_group')])) def visual_audit(out,run_id,pred): if (out/'gradcam_gallery.jpg').exists(): return checkpoint=load_checkpoint(out/'models'/run_id/'inference.pt') settings=Settings(**checkpoint['settings']);setup(settings.seed) model=build_model(settings,False).cuda();model.load_state_dict(checkpoint['model']);model.eval() pred=pred.copy();pred['correct']=pred.label==pred.prediction # Include opposite-label same-image pairs, then round-robin the class/error # strata rather than letting sorted angle bins fill the entire gallery. pair_groups=[g for _,g in pred.groupby('pixel_hash') if g.label.nunique()>1] paired=pd.concat(pair_groups[:2]).head(4) if pair_groups else pred.head(0) representatives=pred.groupby(['label','correct','azimuth_bin']).head(1).copy() representatives['angle_priority']=representatives.azimuth_bin.map({0:0,4:1,2:2,6:3,1:4,5:5,3:6,7:7}) candidates=representatives.sort_values(['angle_priority','image_id']).groupby(['label','correct'],sort=True) pools=[g.to_dict('records') for _,g in candidates] chosen=paired.to_dict('records'); seen=set(paired.image_id) for position in range(max(len(p) for p in pools)): for pool in pools: if position