| """ |
| Model run / experiment tracking. |
| |
| A lightweight MLOps layer: every time a scoring model is run against a worklist |
| we record a ``ModelRun`` (model name + version, timestamp, score statistics, and |
| per-sequence scores). ``RunHistory`` stores them and can compare two runs — e.g. |
| two versions of the same model — to show how scores shifted. |
| |
| Pure-Python (uses only the stdlib ``statistics`` module). |
| """ |
| from __future__ import annotations |
|
|
| import math |
| import statistics |
| import uuid |
| from dataclasses import dataclass, field |
| from typing import Dict, List, Optional |
|
|
|
|
| @dataclass |
| class ModelRun: |
| """A single scoring run over a set of sequences.""" |
| model_name: str |
| model_version: str |
| model_source: str |
| worklist_name: str |
| n_sequences: int |
| score_min: float |
| score_max: float |
| score_mean: float |
| score_std: float |
| timestamp: str |
| run_id: str = field(default_factory=lambda: str(uuid.uuid4())[:8]) |
| notes: str = "" |
| scores: Dict[str, float] = field(default_factory=dict) |
|
|
| @property |
| def label(self) -> str: |
| return f"{self.model_name} v{self.model_version} · {self.timestamp}" |
|
|
|
|
| def summarize_run( |
| model_name: str, |
| model_version: str, |
| model_source: str, |
| worklist_name: str, |
| scores: Dict[str, float], |
| timestamp: str, |
| notes: str = "", |
| ) -> ModelRun: |
| """Build a ModelRun from a {seq_id: score} mapping (ignoring NaNs in stats).""" |
| valid = [v for v in scores.values() if v is not None and not math.isnan(v)] |
| n = len(valid) |
| smin = min(valid) if valid else float("nan") |
| smax = max(valid) if valid else float("nan") |
| smean = statistics.fmean(valid) if valid else float("nan") |
| sstd = statistics.pstdev(valid) if len(valid) > 1 else 0.0 |
| return ModelRun( |
| model_name=model_name, model_version=model_version, |
| model_source=model_source, worklist_name=worklist_name, |
| n_sequences=n, score_min=smin, score_max=smax, |
| score_mean=smean, score_std=sstd, timestamp=timestamp, |
| notes=notes, scores=dict(scores), |
| ) |
|
|
|
|
| @dataclass |
| class RunComparison: |
| """Per-sequence delta between two runs over their shared sequences.""" |
| run_a: ModelRun |
| run_b: ModelRun |
| shared_ids: List[str] |
| deltas: Dict[str, float] |
| mean_delta: float |
| n_improved: int |
| n_worsened: int |
| n_unchanged: int |
|
|
|
|
| class RunHistory: |
| """Append-only store of ModelRun records.""" |
|
|
| def __init__(self) -> None: |
| self.runs: List[ModelRun] = [] |
|
|
| def add(self, run: ModelRun) -> None: |
| self.runs.append(run) |
|
|
| def for_model(self, model_name: str) -> List[ModelRun]: |
| return [r for r in self.runs if r.model_name == model_name] |
|
|
| def model_names(self) -> List[str]: |
| |
| seen: List[str] = [] |
| for r in self.runs: |
| if r.model_name not in seen: |
| seen.append(r.model_name) |
| return seen |
|
|
| @staticmethod |
| def compare(run_a: ModelRun, run_b: ModelRun) -> RunComparison: |
| shared = [sid for sid in run_a.scores if sid in run_b.scores] |
| deltas: Dict[str, float] = {} |
| improved = worsened = unchanged = 0 |
| for sid in shared: |
| a, b = run_a.scores[sid], run_b.scores[sid] |
| if a is None or b is None or math.isnan(a) or math.isnan(b): |
| continue |
| d = b - a |
| deltas[sid] = d |
| if d > 1e-9: |
| improved += 1 |
| elif d < -1e-9: |
| worsened += 1 |
| else: |
| unchanged += 1 |
| mean_delta = statistics.fmean(deltas.values()) if deltas else 0.0 |
| return RunComparison( |
| run_a=run_a, run_b=run_b, shared_ids=list(deltas.keys()), |
| deltas=deltas, mean_delta=mean_delta, |
| n_improved=improved, n_worsened=worsened, n_unchanged=unchanged, |
| ) |
|
|