File size: 1,777 Bytes
f676146 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 | """
OmniDiag — Active Learning Sampler
=====================================
Implements entropy-based uncertainty sampling for the Human-in-the-Loop pipeline.
When a prediction lands in the uncertainty band (default: 35%–65% confidence),
it is flagged as a review candidate and queued in the review_queue table.
Doctors annotate queued items; the annotations feed the next retraining cycle.
Uncertainty metric: prediction entropy
H(p) = -p*log2(p) - (1-p)*log2(1-p)
Max entropy = 1.0 at p=0.5 (complete uncertainty)
High entropy (≥ 0.88) means the model is most uncertain.
"""
import math
import logging
from typing import Optional
log = logging.getLogger("omnidiag.active_learning")
# Predictions with entropy ≥ this threshold are queued for review
_DEFAULT_ENTROPY_THRESHOLD = 0.88 # corresponds to ~35–65% confidence range
def prediction_entropy(probability: float) -> float:
"""
Binary entropy H(p) = -p*log2(p) - (1-p)*log2(1-p).
Returns 0 for p=0 or p=1 (certain), 1.0 for p=0.5 (maximally uncertain).
"""
p = max(1e-9, min(1 - 1e-9, probability)) # avoid log(0)
return -(p * math.log2(p) + (1 - p) * math.log2(1 - p))
def should_queue_for_review(
probability: float,
entropy_threshold: float = _DEFAULT_ENTROPY_THRESHOLD,
) -> bool:
"""Return True if this prediction is uncertain enough to require expert review."""
return prediction_entropy(probability) >= entropy_threshold
def uncertainty_band(probability: float) -> str:
"""Human-readable band for the prediction certainty."""
p = probability
if p >= 0.85 or p <= 0.15:
return "CERTAIN"
if p >= 0.70 or p <= 0.30:
return "CONFIDENT"
if p >= 0.60 or p <= 0.40:
return "BORDERLINE"
return "UNCERTAIN"
|