Files
hypertower/v3/classes/results.py
T
rpotter6298 512ebd13b2 Add distributed server implementation and protocol definitions
- Introduced `protocol.py` for shared data models used in server/client communication, including request and response schemas for registration, job submission, and status updates.
- Implemented `server.py` to manage a SQLite job queue and client registry, handling job polling, status updates, and job completion.
- Created a cheat sheet for server usage, detailing commands for starting the server, submitting jobs, and monitoring clients.
- Added several experiment configuration files for various training setups, including geometry vector injections and baseline ensembles.
2026-04-28 08:24:25 +02:00

145 lines
5.3 KiB
Python

"""Result dataclasses and serialisation helpers for V3 fold outputs."""
from __future__ import annotations
from dataclasses import dataclass
from typing import Optional
import numpy as np
def _nan() -> float:
return float("nan")
def _f(v) -> Optional[float]:
"""Round a scalar to 6 dp, return None for nan/None."""
if v is None or (isinstance(v, float) and np.isnan(v)):
return None
return round(float(v), 6)
def _sv(vec) -> Optional[str]:
"""Serialise a vector to a pipe-separated string, or None."""
if vec is None:
return None
return "|".join(f"{float(v):.4f}" for v in vec)
@dataclass
class FoldResult:
mode: str
fold: int
# Epoch where each model hit its peak val AUC
best_epoch_single: int
best_epoch_bilat: int
# Classic (eye-level eval of SingleEyeHT)
classic_val_auc: float
classic_val_acc: float
classic_val_kappa: float
classic_val_mcc: float
classic_val_f1: float
classic_val_recall: Optional[str]
classic_val_ece: float
classic_val_threshold: float
classic_val_bias: Optional[str]
classic_val_n: int
# Ensemble (patient-level eval of SingleEyeHT)
ensemble_val_auc: float
ensemble_val_acc: float
ensemble_val_kappa: float
ensemble_val_mcc: float
ensemble_val_f1: float
ensemble_val_recall: Optional[str]
ensemble_val_ece: float
ensemble_val_threshold: float
ensemble_val_bias: Optional[str]
ensemble_val_n: int
# Bilateral (BilateralHT patient-level)
bilat_val_auc: float
bilat_val_acc: float
bilat_val_kappa: float
bilat_val_mcc: float
bilat_val_f1: float
bilat_val_recall: Optional[str]
bilat_val_ece: float
bilat_val_threshold: float
bilat_val_bias: Optional[str]
bilat_val_n: int
# Test metrics (evaluated once after training on final-epoch model)
ensemble_test_auc: float = float("nan")
ensemble_test_acc: float = float("nan")
ensemble_test_kappa: float = float("nan")
ensemble_test_f1: float = float("nan")
ensemble_test_ece: float = float("nan")
classic_test_auc: float = float("nan")
classic_test_acc: float = float("nan")
classic_test_kappa: float = float("nan")
classic_test_f1: float = float("nan")
classic_test_ece: float = float("nan")
bilat_test_auc: float = float("nan")
bilat_test_acc: float = float("nan")
bilat_test_kappa: float = float("nan")
bilat_test_f1: float = float("nan")
bilat_test_ece: float = float("nan")
test_n: int = 0
# Training sample counts
single_train_n: int = 0
bilat_train_n: int = 0
# Fused head (optional)
fused_val_auc: float = float("nan")
fused_val_acc: float = float("nan")
fused_val_kappa: float = float("nan")
fused_val_mcc: float = float("nan")
fused_val_f1: float = float("nan")
fused_val_recall: Optional[str] = None
fused_val_ece: float = float("nan")
fused_val_threshold: float = float("nan")
fused_val_bias: Optional[str] = None
fused_val_n: int = 0
fused_test_auc: float = float("nan")
fused_test_acc: float = float("nan")
fused_test_kappa: float = float("nan")
fused_test_f1: float = float("nan")
fused_test_ece: float = float("nan")
fused_test_n: int = 0
@dataclass
class FoldArtifacts:
# Val split
y_true_classic: Optional[np.ndarray]
probs_classic: Optional[np.ndarray]
y_true_ensemble: Optional[np.ndarray]
probs_ensemble: Optional[np.ndarray]
y_true_bilat: Optional[np.ndarray]
probs_bilat: Optional[np.ndarray]
y_true_fused: Optional[np.ndarray] = None
probs_fused: Optional[np.ndarray] = None
probs_ensemble_img: Optional[np.ndarray] = None
probs_ensemble_md: Optional[np.ndarray] = None
probs_classic_img: Optional[np.ndarray] = None
probs_classic_md: Optional[np.ndarray] = None
# per-eye (pre-averaged) for ensemble mode
y_true_ensemble_pereye: Optional[np.ndarray] = None
probs_ensemble_pereye: Optional[np.ndarray] = None
probs_ensemble_img_pereye: Optional[np.ndarray] = None
probs_ensemble_md_pereye: Optional[np.ndarray] = None
# raw logits — patient-level
logits_ensemble: Optional[np.ndarray] = None
logits_ensemble_img: Optional[np.ndarray] = None
logits_ensemble_md: Optional[np.ndarray] = None
logits_classic: Optional[np.ndarray] = None
logits_classic_img: Optional[np.ndarray] = None
logits_classic_md: Optional[np.ndarray] = None
# raw logits — per-eye
logits_ensemble_pereye: Optional[np.ndarray] = None
logits_ensemble_img_pereye: Optional[np.ndarray] = None
logits_ensemble_md_pereye: Optional[np.ndarray] = None
# Test split equivalents
y_true_test: Optional[np.ndarray] = None
probs_test: Optional[np.ndarray] = None
probs_test_img: Optional[np.ndarray] = None
probs_test_md: Optional[np.ndarray] = None
y_true_fused_test: Optional[np.ndarray] = None
probs_fused_test: Optional[np.ndarray] = None