Conformalized quantile regression.
Classes
class uncertainty.conformal.cqr.ConformalizedQuantileRegressor(ConformalPredictor)
Conformalized quantile regression — valid and heteroscedastic intervals.
SplitConformalRegressor adds a constant radius everywhere, which over-covers where the noise is small and under-covers where it is large. CQR starts from a quantile regressor, which already models how the spread varies with x, and conformalises it — keeping the shape while restoring the exact coverage guarantee the quantile fit lacks on its own.__init__( self, lower_estimator: Any, upper_estimator: Any, alpha: float = 0.1, calibration_size: float = 0.25, random_state: Optional[int] = None) -> None, )
Overview
- Fit a lower-quantile model at \alpha/2 and an upper-quantile
- On the calibration set, score each sample by how far outside the
- Take the corrected quantile of those scores.
- Widen (or, when the quantile fit was conservative, narrow) the band
Theory
With \hat{q}_{lo} and \hat{q}_{hi} the fitted quantile functions, the conformity score is the signed distance outside the band:
and with \hat{q} its corrected quantile the interval is
A negative \hat{q} is not an error: it means the quantile models over-covered, and CQR correctly shrinks the band. Coverage remains at least 1 - \alpha either way.
Parameters
lower_estimator
tuiml.sklearn.linear.QuantileRegressor(quantile=0.05) or tuiml.sklearn.ensemble.GradientBoostingRegressor(loss='quantile', alpha=0.05).
upper_estimator
alpha
calibration_size
random_state
Attributes
scores_
quantile_
fitted_
fit has been called.
Notes
Complexity. Two estimator fits plus O(n \log n).
When to use. Use CQR whenever the noise level plainly depends on the input — sensor readings that degrade with range, demand that fluctuates more at high volume, any funnel-shaped residual plot. On homoscedastic data it costs a second model fit for intervals that are no tighter than split conformal's.
Because TuiML has no native quantile regressor yet, this class takes two externally-fitted quantile models rather than cloning one estimator. Pass the tuiml.sklearn wrappers named above, or any object with fit and predict.
References
See Also
Requires pip install tuiml[sklearn] for a quantile regressor. Passing
two mean regressors instead silently degenerates this class into
SplitConformalRegressor: both models predict
the same thing, the band has zero width, and only the constant correction
survives.
>>> import numpy as np
>>> from tuiml.uncertainty import ConformalizedQuantileRegressor
>>> from tuiml.sklearn.ensemble import GradientBoostingRegressor
>>> rng = np.random.default_rng(0)
>>> X = rng.uniform(0, 4, size=(400, 1))
>>> y = X[:, 0] + rng.normal(0, 0.2 + 0.5 * X[:, 0], 400)
>>> cqr = ConformalizedQuantileRegressor(
... GradientBoostingRegressor(loss='quantile', alpha=0.05, n_estimators=50),
... GradientBoostingRegressor(loss='quantile', alpha=0.95, n_estimators=50),
... alpha=0.1, random_state=0)
>>> cqr.fit(X, y)
ConformalizedQuantileRegressor(alpha=0.1)
>>> widths = np.diff(cqr.predict_interval(X), axis=1).ravel()
>>> bool(widths.std() > 0.0) # the band adapts to the local noise
True
Methods
fit
(self, X: np.ndarray, y: np.ndarray) -> 'ConformalizedQuantileRegressor'
fit
(self, X: np.ndarray, y: np.ndarray) -> 'ConformalizedQuantileRegressor'
Fit both quantile models and calibrate the band correction.
Parameters
X
y
Returns
self
predict_interval
(self, X: np.ndarray) -> np.ndarray
predict_interval
(self, X: np.ndarray) -> np.ndarray
Predict calibrated, input-dependent lower and upper bounds.
Parameters
X
Returns
intervals
X wherever the quantile models say they should.