Download app/ml/evaluate.py from sitcod3/modul-x: direct link, hf CLI and curl.
- Browser
- Download file 2.95 kB
-
https://huggingface.co/spaces/sitcod3/modul-x/resolve/main/app/ml/evaluate.py
- Command line
-
hf download hf://spaces/sitcod3/modul-x/app/ml/evaluate.py
-
curl -L -o evaluate.py https://huggingface.co/spaces/sitcod3/modul-x/resolve/main/app/ml/evaluate.py
2.95 kB
| """Evaluation harness and promotion gate for the email-risk model. | |
| The single most dangerous failure mode is a **false DISPOSABLE**: flagging a | |
| legitimate business domain as throwaway destroys a real lead and a credit. So | |
| the promotion gate is deliberately stricter than "good accuracy": | |
| promotion_ready = DISPOSABLE precision >= 0.98 AND zero dangerous misses | |
| `dangerous_misses` lists every NORMAL case the model predicted DISPOSABLE - | |
| those are reviewed individually before the model is ever allowed to block. | |
| """ | |
| from __future__ import annotations | |
| from typing import Optional | |
| PROMOTION_PRECISION = 0.98 | |
| LABELS = ("NORMAL", "DISPOSABLE", "ROLE", "SPAM_TRAP") | |
| def evaluate_model(model, cases: list[dict]) -> dict: | |
| """Run `cases` ({"text", "expected"}) through the model and score it. | |
| `model.predict(texts)` must honor the runtime contract: | |
| list[tuple[label, confidence]] or None on failure. | |
| """ | |
| empty = { | |
| "checked": 0, "correct": 0, "wrong": 0, | |
| "per_class": {}, "dangerous_misses": [], | |
| "promotion_ready": False, | |
| "gate": f"DISPOSABLE precision >= {PROMOTION_PRECISION} and no dangerous misses", | |
| } | |
| if not cases: | |
| return empty | |
| texts = [c["text"] for c in cases] | |
| try: | |
| preds = model.predict(texts) | |
| except Exception: | |
| return empty | |
| if preds is None or len(preds) != len(cases): | |
| return empty | |
| per_class: dict[str, dict[str, int]] = { | |
| label: {"tp": 0, "fp": 0, "fn": 0, "support": 0} for label in LABELS | |
| } | |
| dangerous: list[str] = [] | |
| correct = 0 | |
| checked = 0 | |
| for case, (pred_label, _score) in zip(cases, preds): | |
| expected = case.get("expected") | |
| checked += 1 | |
| per_class.setdefault(expected, {"tp": 0, "fp": 0, "fn": 0, "support": 0}) | |
| per_class[expected]["support"] += 1 | |
| if pred_label == expected: | |
| correct += 1 | |
| per_class[expected]["tp"] += 1 | |
| continue | |
| per_class[pred_label]["fp"] += 1 | |
| per_class[expected]["fn"] += 1 | |
| if pred_label == "DISPOSABLE" and expected == "NORMAL": | |
| dangerous.append(case["text"]) | |
| precision = {} | |
| for label, m in per_class.items(): | |
| predicted = m["tp"] + m["fp"] | |
| precision[label] = (m["tp"] / predicted) if predicted else 0.0 | |
| disposable_precision = precision.get("DISPOSABLE", 0.0) | |
| promotion = disposable_precision >= PROMOTION_PRECISION and not dangerous | |
| return { | |
| "checked": checked, | |
| "correct": correct, | |
| "wrong": checked - correct, | |
| "per_class": { | |
| label: { | |
| "precision": round(precision.get(label, 0.0), 4), | |
| "tp": m["tp"], "fp": m["fp"], "fn": m["fn"], "support": m["support"], | |
| } | |
| for label, m in per_class.items() | |
| }, | |
| "dangerous_misses": dangerous, | |
| "promotion_ready": promotion, | |
| "gate": empty["gate"], | |
| } |