calibrate a confidence threshold from one inference pass

Threshold calibration ran the model over every tile once per threshold — three
GPU passes to compare 0.50, 0.25 and 0.15 on a hundred-tile raster. The answer
is already in a single run at the lowest value: detections above a higher cut
are a subset of it, and duplicate suppression walks candidates in descending
confidence, so a lower-confidence box can never displace a higher-confidence
one. The kept set above any cut is identical whichever threshold the run used,
which is what makes one pass sufficient rather than merely cheaper.

QA now takes calibration_thresholds and reads each operating point off the same
precision/recall walk it already performs, marking the F1-optimal cut. The lab
runs inference once and fills its table from the sweep.

The contract test asserted the per-threshold loop by name, pinning the waste it
was meant to describe. It now states what calibration owes an operator: a row
per requested threshold, from one run.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Jens
2026-08-22 19:37:19 +02:00
co-authored by Claude Opus 5
parent 8a26007281
commit 2cd2c49389
11 changed files with 303 additions and 52 deletions
+20
View File
@@ -1431,6 +1431,25 @@ export interface DetectionQaRequest {
iou_threshold: number
class_name?: string | null
min_confidence?: number | null
/**
* Confidence cuts to report next to the run's own operating point. They are
* read off one matching pass, so a sweep costs no extra inference.
*/
calibration_thresholds?: number[]
}
/** One confidence cut, derived from a single run rather than a run of its own. */
export interface DetectionCalibrationPoint {
min_confidence: number
confidence_threshold: number | null
candidate_count: number
true_positives: number
false_positives: number
false_negatives: number
precision: number | null
recall: number | null
f1_score: number | null
best_f1_in_sweep: boolean
}
export interface DetectionQaResult {
@@ -1486,6 +1505,7 @@ export interface DetectionQaResult {
envelope_precision_recall_curve?: PrecisionRecallCurve
}
precision_recall_curve?: PrecisionRecallCurve
calibration_sweep?: DetectionCalibrationPoint[]
}
/**