whassan's picture
Deploy Sensorflow Studio
73d9b6f verified
Raw History Blame Contribute Delete
4.74 kB
"""Quantitative validation: for candidates with model outputs, compute the
OBSERVED difficulty from actual baseline behavior and compare it with the
miner's PREDICTED difficulty (agreement matrix).
Observed metrics per track candidate: mean baseline confidence, per-example
correctness, localization quality (IoU), miss rate, class confusion, track
stability. Predicted difficulty comes from the miner and is never edited here
— this stage only measures.
"""
from __future__ import annotations
from typing import Any, Dict, List, Optional
from sensorflow.raremine.models import RareMineStore, TrackCandidate
DIFF_ORDER = ["EASY", "MODERATE", "HARD", "EXTREME"]
def _track_object_ids(store: RareMineStore, tc: TrackCandidate) -> List[tuple]:
"""(scene, object) pairs across the track's frames."""
out = []
for scene in store.where("scenes", sequence_id=tc.sequence_id):
for obj in scene.objects:
if obj.track_id == tc.track_id:
out.append((scene, obj))
return out
def observed_metrics(store: RareMineStore, tc: TrackCandidate) -> Optional[Dict[str, Any]]:
"""None when no baseline predictions exist anywhere on the track."""
pairs = [(s, o) for s, o in _track_object_ids(store, tc)
if s.modalities.get("baseline_predictions")]
if not pairs:
return None
confidences: List[float] = []
ious: List[float] = []
misses = 0
confusion: Dict[str, int] = {}
unstable = 0
correct = 0
for scene, obj in pairs:
pred = next((p for p in scene.baseline_predictions if p.object_id == obj.object_id), None)
if pred is None:
misses += 1
continue
confidences.append(pred.confidence)
if pred.iou_with_gt is not None:
ious.append(pred.iou_with_gt)
if pred.predicted_class != "pedestrian":
confusion[pred.predicted_class] = confusion.get(pred.predicted_class, 0) + 1
else:
correct += 1
if not pred.track_stable:
unstable += 1
n = len(pairs)
mean_conf = round(sum(confidences) / len(confidences), 3) if confidences else 0.0
mean_iou = round(sum(ious) / len(ious), 3) if ious else None
miss_rate = round(misses / n, 3)
confused_rate = round(sum(confusion.values()) / n, 3)
stability = round(1.0 - unstable / max(n - misses, 1), 3)
# observed difficulty grade from measured behavior
if miss_rate > 0.5 or confused_rate > 0.5:
grade = "EXTREME"
elif miss_rate > 0.0 or confusion or unstable:
grade = "HARD"
elif mean_conf < 0.4 or (mean_iou is not None and mean_iou < 0.5):
grade = "MODERATE" if mean_conf >= 0.25 else "HARD"
else:
grade = "EASY"
return {
"frames_with_predictions": n,
"mean_baseline_confidence": mean_conf,
"mean_iou": mean_iou,
"miss_rate": miss_rate,
"class_confusion": confusion,
"confused_rate": confused_rate,
"track_stability": stability,
"per_example_correct": correct,
"observed_difficulty": grade,
"exposes_model_failure": miss_rate > 0 or bool(confusion) or unstable > 0
or mean_conf < 0.35 or (mean_iou is not None and mean_iou < 0.5),
}
def quantitative_report(store: RareMineStore, bank_id: str) -> Dict[str, Any]:
tcs = [t for t in store.where("track_candidates", bank_id=bank_id)
if t.duplicate_of is None and t.representative.edge_case_detected]
rows = []
matrix: Dict[str, Dict[str, int]] = {d: {d2: 0 for d2 in DIFF_ORDER} for d in DIFF_ORDER}
agree = 0
with_obs = 0
for tc in sorted(tcs, key=lambda t: t.track_candidate_id):
obs = observed_metrics(store, tc)
predicted = tc.representative.perception_difficulty
row = {
"track_candidate_id": tc.track_candidate_id,
"costume_type": tc.representative.costume_type,
"predicted_difficulty": predicted,
"observed": obs,
}
if obs is not None:
with_obs += 1
matrix[predicted][obs["observed_difficulty"]] += 1
if abs(DIFF_ORDER.index(predicted) - DIFF_ORDER.index(obs["observed_difficulty"])) <= 1:
agree += 1
rows.append(row)
return {
"candidates_evaluated": len(rows),
"with_model_outputs": with_obs,
"agreement_matrix": matrix,
"within_one_level_agreement": round(agree / with_obs, 3) if with_obs else None,
"rows": rows,
"note": "predicted difficulty is the miner's forecast; observed difficulty is "
"measured from actual baseline model behavior — they are compared, "
"never merged",
}