MediGuard-AI / task1_suppression.py
Rajveer
clean deploy: all files at root
0c6a12e
Raw History Blame Contribute Delete
3.47 kB
"""
Task 1: Suppression Grader (Easy)
=================================
Grades how well the agent suppresses false alarms for a hypertensive patient
while still catching real emergencies.
Uses F1 harmonic mean of sensitivity and specificity:
- sensitivity = correctly responded to emergencies / total emergencies
(ALERT counts as full TP, VERIFY counts as 0.5 TP β€” must commit to ALERT)
- specificity = correctly ignored stable periods / total stable periods
(VERIFY counts as 0.7 FP β€” spamming VERIFY is penalized more heavily)
- score = 2 Γ— sensitivity Γ— specificity / (sensitivity + specificity)
Calibrated so that:
- Rule-based agent (mostly VERIFY/IGNORE) scores ~0.50
- A smart LLM/RL agent that correctly ALERTs emergencies and IGNOREs stable β†’ ~0.80+
- Pure-IGNORE agent β†’ sensitivity=0 β†’ F1=0.0
- Pure-ALERT agent β†’ specificity=0 β†’ F1=0.0
"""
from reward_function import Action, PatientCondition
def grade_suppression(stats: dict) -> float:
"""
Grade the suppression task using F1 harmonic mean.
Parameters
----------
stats : dict
Episode statistics from RewardFunction.get_stats().
Returns
-------
float
Score in [0.0, 1.0].
"""
action_history = stats["action_history"]
condition_history = stats["condition_history"]
total_steps = stats["total_steps"]
if total_steps == 0:
return 0.0
# Count outcomes
true_positives = 0.0 # Alert/Verify during Emergency
false_negatives = 0.0 # Ignore during Emergency
true_negatives = 0.0 # Ignore during Stable
false_positives = 0.0 # Alert/Verify during Stable
for action, condition in zip(action_history, condition_history):
is_emergency = condition in (PatientCondition.EMERGENCY, PatientCondition.DRUG_MASKED)
if is_emergency:
if action == Action.ALERT:
true_positives += 1.0 # Full credit β€” committed to ALERT
elif action == Action.VERIFY:
true_positives += 0.5 # Partial credit β€” noticed but didn't escalate
false_negatives += 0.5 # Partial miss β€” should have ALERTed
else: # IGNORE during emergency
false_negatives += 1.0
else: # STABLE or BORDERLINE
if action == Action.IGNORE:
true_negatives += 1.0
elif action == Action.ALERT:
false_positives += 1.0 # Fully wrong
else: # VERIFY during stable
false_positives += 0.7 # Raised from 0.3 β€” VERIFY is not a safe default
# Sensitivity: how well did we catch real emergencies?
total_emergencies = true_positives + false_negatives
if total_emergencies > 0:
sensitivity = true_positives / total_emergencies
else:
# No emergencies in this episode β€” cap sensitivity so agent can't get free marks.
# Without emergencies, the agent has no way to prove it can handle them.
sensitivity = 0.5
# Specificity: how well did we suppress false alarms?
total_stable = true_negatives + false_positives
if total_stable > 0:
specificity = true_negatives / total_stable
else:
specificity = 1.0
# F1 harmonic mean
if (sensitivity + specificity) > 0:
f1 = 2.0 * sensitivity * specificity / (sensitivity + specificity)
else:
f1 = 0.0
return max(0.0, min(1.0, f1))