Adaptive prediction sets (APS) and their regularised variant (RAPS).
Classes
class uncertainty.conformal.aps.APSConformalClassifier(SplitConformalClassifier)
Adaptive prediction sets that grow where the model is uncertain.
SplitConformalClassifier minimises average set size, but it does so by leaving hard samples under-covered and easy ones over-covered. APS instead accumulates probability mass down the sorted class ranking, producing small sets on easy inputs and large sets on ambiguous ones — much better conditional coverage at a modest cost in average size.__init__( self, estimator: Any, alpha: float = 0.1, randomized: bool = True, calibration_size: float = 0.25, random_state: Optional[int] = None) -> None, )
Overview
- Sort each sample's class probabilities in decreasing order.
- The nonconformity of the true label is the total probability mass down
- Calibrate the corrected quantile of those scores as usual.
- A test set includes classes in rank order until the accumulated mass
Theory
For sorted probabilities \hat{p}_{(1)} \geq \dots \geq \hat{p}_{(c)} and the true label at rank r, the score is
The randomised term u is what makes coverage exact rather than merely conservative: without it the discrete jumps between ranks force the set to over-cover. Set randomized=False for deterministic, reproducible sets at the cost of slight over-coverage.
Parameters
estimator
predict_proba.
alpha
randomized
calibration_size
random_state
Attributes
classes_
fit.
scores_
quantile_
fitted_
fit has been called.
Notes
Complexity. O(n c \log c) for the per-sample sort, on top of one estimator fit.
When to use. Prefer APS over LAC whenever coverage must hold across subgroups, not just on average — the LAC set is smaller overall but systematically fails the hard tail. Its known weakness is a long tail of very large sets when the probability estimates are noisy; RAPS fixes exactly that.
References
See Also
>>> import numpy as np
>>> from tuiml.uncertainty import APSConformalClassifier
>>> from tuiml.algorithms.trees import DecisionTreeClassifier
>>> rng = np.random.default_rng(0)
>>> X = rng.normal(size=(400, 4))
>>> y = (X[:, 0] + X[:, 1] > 0).astype(int)
>>> cp = APSConformalClassifier(DecisionTreeClassifier(max_depth=4),
... alpha=0.1, random_state=0)
>>> cp.fit(X, y)
APSConformalClassifier(estimator=DecisionTreeClassifier(), alpha=0.1)
>>> cp.predict_set(X[:5]).shape
(5, 2)
Methods
class uncertainty.conformal.aps.RAPSConformalClassifier(APSConformalClassifier)
Regularised adaptive prediction sets — APS without the long tail.
__init__( self, estimator: Any, alpha: float = 0.1, lambda_penalty: float = 0.01, k_reg: int = 1, randomized: bool = True, calibration_size: float = 0.25, random_state: Optional[int] = None) -> None, )
Overview
- Compute the APS cumulative-mass score.
-
Add
lambda_penaltyfor every class ranked beyondk_reg. - Calibrate and predict exactly as APS does, with the penalty applied on
Theory
With the true label at rank r, the RAPS score is
Because the penalty is a deterministic function of rank and is applied identically at calibration and prediction time, the exchangeability argument is untouched — the 1 - \alpha guarantee still holds. The penalty only reshapes which sets achieve it.
Parameters
estimator
predict_proba.
alpha
lambda_penalty
k_reg. Larger values shrink the tail harder; too large and every set collapses to k_reg classes.
k_reg
randomized
calibration_size
random_state
Attributes
classes_
fit.
scores_
quantile_
fitted_
fit has been called.
Notes
Complexity. Identical to APS: O(n c \log c).
When to use. Use RAPS on problems with many classes, where APS's tail of huge sets makes the output unusable. On binary or few-class problems the penalty has little to bite on and plain APS is simpler. Tune lambda_penalty and k_reg on a validation split against average_set_size at fixed coverage.
References
See Also
>>> import numpy as np
>>> from tuiml.uncertainty import RAPSConformalClassifier
>>> from tuiml.algorithms.trees import DecisionTreeClassifier
>>> rng = np.random.default_rng(0)
>>> X = rng.normal(size=(600, 4))
>>> y = rng.integers(0, 4, 600)
>>> cp = RAPSConformalClassifier(DecisionTreeClassifier(max_depth=3),
... alpha=0.2, lambda_penalty=0.05, random_state=0)
>>> cp.fit(X, y)
RAPSConformalClassifier(estimator=DecisionTreeClassifier(), alpha=0.2)
>>> cp.predict_set(X[:5]).shape
(5, 4)