ek.qe.decide#
Selective prediction: turn a calibrated probability into accept / flag / block.
Calibration makes a score mean something; a decision policy turns it into an
action with a guarantee (misc/docs/ek_03 §3-4). This is the formal home of
“accept / flag / block” – a selective predictor that abstains (routes to a human)
rather than emit a low-confidence answer. Every policy here reads **only calibrated
probabilities** (Hard Rule 1): a raw score must already have passed through a
Calibrator.
Three policies, by what you want to guarantee:
CostSensitiveGate– the default. The accept threshold falls out of the cost ratiorho = c_FN / c_FPon the calibrated probability; no labelled set needed once you know your costs.ConformalGate– split (inductive) conformal: a finite-sample, distribution-free marginal guarantee that at mostalphaof truly-correct items are flagged. Per-field-type validity needs the Mondrian (GroupConformalGate) variant – conditional coverage is otherwise impossible (Barber et al. 2019).RiskControlGate– conformal risk control: bound a monotone loss (the rate of accepted-yet-wrong items) at a target level.
risk_coverage_curve() exposes the trade-off so stakeholders pick the operating
point explicitly. The pure-Python gates need no extra dependencies; the
ek[calibration] extra (MAPIE/crepes) offers library-backed conformal for an
sklearn-centric stack. Exchangeability is the load-bearing assumption – track
realised coverage and re-fit on drift.
Example
>>> from ek.base import Decision
>>> gate = CostSensitiveGate(rho=9.0) # a false-negative costs 9x a review
>>> gate(0.95) is Decision.ACCEPT # tau = rho/(1+rho) = 9/10 = 0.9
True
>>> gate(0.5) is Decision.FLAG
True
Module Attributes
Default target miscoverage for conformal gates (flag at most ~alpha of correct items). |
|
Default risk bound for |
Functions
|
A Mondrian (class-conditional) conformal gate backed by |
|
The risk-coverage trade-off: |
|
The split-conformal threshold: the |
Classes
|
Split-conformal accept/flag with a finite-sample marginal guarantee. |
|
Accept/flag/block from the cost ratio |
|
Mondrian (class-conditional) conformal: one |
|
Conformal Risk Control: accept |
- class ek.qe.decide.ConformalGate(alpha=0.1, nonconformity=<function ConformalGate.<lambda>>, q=inf, _cal=<factory>)[source]#
Bases:
objectSplit-conformal accept/flag with a finite-sample marginal guarantee.
Fit on
(calibrated_prob, correct): the nonconformity scores of the correct calibration items set a thresholdqsuch that, among truly-correct test items exchangeable with them, at mostalphaare flagged. The guarantee is marginal, not per-field-type – useGroupConformalGatefor that.- Parameters:
- class ek.qe.decide.CostSensitiveGate(rho=1.0, block_threshold=0.0, accept_threshold=None)[source]#
Bases:
objectAccept/flag/block from the cost ratio
rho = c_FN / c_FPon calibratedp.Accept when the expected cost of accepting is below that of routing to a human. Accepting risks an undetected error: cost
(1 - p) * c_FN. Flagging a value that was actually correct wastes a review: costp * c_FP(the false-positive cost is incurred only with probabilityp, not unconditionally). So accept iff(1 - p) * c_FN <= p * c_FP, i.e. the Bayes-optimal thresholdp >= c_FN / (c_FN + c_FP) = rho / (1 + rho). A largerrho(misses much costlier than reviews) raises the bar to auto-accept (tau -> 1asrho -> inf);rho = 1gives the symmetrictau = 0.5.block_thresholdadds an optional hard-fail band at the bottom.- Parameters:
rho (
float) – Cost ratioc_FN / c_FP(>= 0). The single lever; no magic numbers.block_threshold (
float) – Calibrated probability at or below which toblockrather thanflag(default0-> never block on probability alone; a failed verifier is what forces a block, upstream).accept_threshold (
Optional[float]) – Override the derived accept threshold explicitly.
- ek.qe.decide.DEFAULT_ALPHA = 0.1#
Default target miscoverage for conformal gates (flag at most ~alpha of correct items).
- ek.qe.decide.DEFAULT_RISK_TARGET = 0.05#
Default risk bound for
RiskControlGate(accepted-error rate ceiling).
- class ek.qe.decide.GroupConformalGate(alpha=0.1, nonconformity=<function GroupConformalGate.<lambda>>, by_group=<factory>, pooled=None)[source]#
Bases:
objectMondrian (class-conditional) conformal: one
ConformalGateper group.Restores approximate per-field-type validity by calibrating separately per group key (e.g.
NodeType/FieldSpec), with a pooled fallback for unseen groups.
- class ek.qe.decide.RiskControlGate(target=0.05, loss=<function RiskControlGate.<lambda>>, loss_bound=1.0, lam=1.0)[source]#
Bases:
objectConformal Risk Control: accept
p >= lambdabounding the accepted-error rate.Picks the smallest threshold
lambda(the most coverage) whose finite-sample risk bound is at or belowtarget. The controlled quantity isE[ loss(item) * 1(accepted) ]– with the default 0/1 loss, the population rate of accepted-yet-wrong items.- Parameters:
target (
float) – Upper bound on the accepted-error rate (e.g.0.05).loss (
Callable[[bool],float]) –correct -> lossin[0, loss_bound]; default0if correct else1. Any monotone loss bounded byloss_boundworks.loss_bound (
float) – The loss’s upper boundB– the CRC finite-sample slack is+B/(n+1). Must matchloss’s range or the guarantee is invalid; keep the default1.0for the 0/1 loss.
- ek.qe.decide.crepes_mondrian_gate(*args, **kwargs)[source]#
A Mondrian (class-conditional) conformal gate backed by
crepes(ek[calibration]).The pure-Python
GroupConformalGateis the dependency-free default; use this for parity with a crepes/sklearn conformal stack (full CDFs, normalised variants). Raises an actionable install hint ifcrepesis absent.
- ek.qe.decide.risk_coverage_curve(probs, correct, *, thresholds=None)[source]#
The risk-coverage trade-off:
{threshold, coverage, selective_risk}per point.Coverage is the fraction auto-accepted at each threshold; selective risk is the error rate among the accepted. Lower-left is better; pick the operating point against a target risk or coverage.
- ek.qe.decide.split_conformal_quantile(scores, alpha)[source]#
The split-conformal threshold: the
ceil((n+1)(1-alpha)) / nempirical quantile.scoresare nonconformity scores on an exchangeable calibration set. Returns+infwhennis too small to guarantee1 - alpha(so nothing is flagged – the honest behaviour at that sample size), and-infat thealpha -> 1limit (flag everything).- Return type: