Files
rpotter6298 280060db82 Add new regression and ensemble experiment configurations for V2-M and OrthoBridge
- Introduced multiple regression experiment configurations targeting vf_md, including:
  - cd_solo_reg_set.json: CD tower only regression setup.
  - img_solo_reg_set.json: Image tower only regression setup.
  - reg_head_epoch_sweep.json: Baseline regression sweeps at different epochs (50, 75, 100).
  - reg_head_set.json: Various regression setups including baseline and OrthoBridge configurations.
  - single_eye_reg.json: Single-eye regression setup for worst-eye aggregation analysis.

- Added ensemble configurations for OrthoBridge with different inner bridges:
  - ortho_alts_ensemble.json: Ensemble tests with ConcatBridge, PairwiseAdditiveBridge, and GatedAdditiveBridge.
  - ortho_alts_tritower.json: Tritower tests with the same inner bridges.

- Created V2-M specific configurations:
  - baseline_reg_nt50.json: Regression baseline with V2-M backbone.
  - geom_vec_gt.json and geom_vec_unet.json: Geometry vector injection experiments with V2-M.
  - single_l1_bridges.json: Single-eye ensemble experiments with various bridge types.
  - tritower_geom_gt.json: Tritower setup with GT contour-rasterized masks.

- Promoted existing experiments to higher repetitions for robustness.
2026-06-11 15:08:20 +02:00

56 lines
1.9 KiB
Python

"""Shared loaders/aggregators for v4 figure scripts."""
from __future__ import annotations
import json
from pathlib import Path
from typing import Optional
import numpy as np
REPO_ROOT = Path(__file__).resolve().parents[3]
RESULTS_ROOT = REPO_ROOT / "v4" / "results" / "experiments"
def summarise_run(run_path: Path, primary_hint: Optional[str] = None) -> Optional[dict]:
"""Aggregate val/test primary-metric mean & std across reps for one run folder.
Returns dict with n, val_mean, val_std, test_mean, test_std, metric_name, or None if no reps."""
val, test = [], []
metric_name = primary_hint
for rep in sorted(run_path.glob("rep*")):
s = next(iter(rep.rglob("summary.json")), None)
if not s:
continue
d = json.loads(s.read_text())
pm = d.get("primary_metric") or metric_name or "auc"
metric_name = metric_name or pm
v = d.get(f"mean_val_{pm}")
t = d.get(f"mean_test_{pm}")
if v is None or t is None or not np.isfinite(v) or not np.isfinite(t):
continue
val.append(float(v)); test.append(float(t))
if not val:
return None
return {
"n": len(val),
"metric": metric_name or "auc",
"val_mean": float(np.mean(val)),
"val_std": float(np.std(val)),
"test_mean": float(np.mean(test)),
"test_std": float(np.std(test)),
"val_arr": np.array(val),
"test_arr": np.array(test),
}
def summarise_many(name_to_path: dict[str, Path], primary_hint: Optional[str] = None) -> dict[str, Optional[dict]]:
"""Apply summarise_run to a dict of labelled run folders."""
return {label: summarise_run(p, primary_hint) for label, p in name_to_path.items()}
def fmt_status(s: Optional[dict]) -> str:
if s is None:
return "pending"
return f"n={s['n']:>2d} test={s['test_mean']:.4f}±{s['test_std']:.4f}"