API: Calibrators
Base class
Calibrator
Calibrator(*, input_type: str = 'logits', ignore_index: int = -100, device: DeviceLike | None = None)
Bases: ABC
Abstract base class for post-hoc segmentation calibrators.
All calibrators share the same interface and tensor convention:
- predictions are channel-first, class axis at dimension 1
(
(B, C, *spatial);(N, C)is also accepted); - targets are integer labels
(B, *spatial)or floating class distributions(B, C, *spatial), including mean human responses; - mask, when given, is
(B, *spatial)withTruefor valid voxels.
Inputs may be raw logits or normalised probs (see input_type).
transform and predict_proba always return calibrated probabilities
of the same shape as the input.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
input_type
|
str
|
|
'logits'
|
ignore_index
|
int
|
Label value excluded from fitting (default |
-100
|
device
|
DeviceLike | None
|
Computation device. |
None
|
Notes
Calibrators fitted by gradient descent also accept, as keyword-only constructor arguments, an optional validation-based early stopping rule (Adam only):
patience-- stop after this many iterations without the validation NLL improving by more thanmin_delta(default0.0);lr_patience/lr_factor-- multiply the learning rate bylr_factor(default0.1) afterlr_patienceiterations without improvement (ReduceLROnPlateau).
Setting either requires val_predictions / val_targets in :meth:fit;
the iterate with the best validation NLL is kept. With max_iter acting as
an upper bound, this reproduces only the Adam + validation-NLL early-stopping
recipe; the paper's case batching, class weighting, schedules, clipping and
hyperparameter search are not part of the library.
Source code in src/fiducio/base.py
num_classes
property
Number of classes seen at fit time, or None before fitting.
fit
fit(predictions: Any, targets: Any, mask: Any | None = None, *, val_predictions: Any | None = None, val_targets: Any | None = None, val_mask: Any | None = None) -> Calibrator
Fit the calibrator on a labelled calibration set.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
predictions
|
Any
|
|
required |
targets
|
Any
|
|
required |
mask
|
Any | None
|
Optional |
None
|
val_predictions
|
Any | None
|
Optional held-out validation set, in the same format as
|
None
|
val_targets
|
Any | None
|
Optional held-out validation set, in the same format as
|
None
|
val_mask
|
Any | None
|
Optional held-out validation set, in the same format as
|
None
|
Notes
Calling fit again on an already-fitted instance re-fits from
scratch: all learned parameters are reinitialised and overwritten, and
a new num_classes (which may differ from the previous fit) is
recorded. No state from the previous fit is reused. If fitting fails,
the previous state is preserved. Model outputs are detached from autograd.
Source code in src/fiducio/base.py
transform
Apply calibration and return probabilities of the input shape.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
predictions
|
Any
|
|
required |
mask
|
Any | None
|
Optional |
None
|
Returns:
| Type | Description |
|---|---|
Tensor
|
Calibrated probabilities, always in |
Source code in src/fiducio/base.py
predict_proba
Alias for :meth:transform; returns calibrated probabilities.
decision_function
Return calibrated logits (pre-softmax) of the input shape.
The mask argument of :meth:transform is deliberately not accepted
here: a masked logit of 0 is a meaningful value, and argmax over its
output at masked voxels is meaningless. Use :meth:transform when masks
matter. softmax of the result along dimension 1 equals
:meth:transform without a mask.
Source code in src/fiducio/base.py
fit_transform
fit_transform(predictions: Any, targets: Any, mask: Any | None = None, *, val_predictions: Any | None = None, val_targets: Any | None = None, val_mask: Any | None = None) -> torch.Tensor
Fit on the calibration set, then transform the same predictions.
Source code in src/fiducio/base.py
save
Save this calibrator to path (see :func:fiducio.save_calibrator).
get_config
Return constructor keyword arguments (excluding device).
Source code in src/fiducio/base.py
Temperature scaling
TemperatureScaling
TemperatureScaling(*, init_temperature: float = 1.0, optimizer: str = 'adam', lr: float | None = None, max_iter: int | None = None, patience: int | None = None, min_delta: float = 0.0, lr_patience: int | None = None, lr_factor: float = 0.1, input_type: str = 'logits', ignore_index: int = -100, device: DeviceLike | None = None)
Bases: Calibrator
Temperature scaling: softmax(z / T) with a single scalar T > 0.
The simplest post-hoc calibrator. It preserves class ordering in exact arithmetic; floating-point ties and rounding can change an argmax.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
init_temperature
|
float
|
Initial temperature (must be positive). |
1.0
|
optimizer
|
str
|
|
'adam'
|
lr
|
float | None
|
Learning rate. Defaults to |
None
|
max_iter
|
int | None
|
Maximum optimizer iterations. Defaults to |
None
|
patience
|
int | None
|
Optional validation-based early stopping (Adam only), see
:class: |
None
|
min_delta
|
int | None
|
Optional validation-based early stopping (Adam only), see
:class: |
None
|
lr_patience
|
int | None
|
Optional validation-based early stopping (Adam only), see
:class: |
None
|
lr_factor
|
int | None
|
Optional validation-based early stopping (Adam only), see
:class: |
None
|
input_type
|
str
|
See :class: |
'logits'
|
ignore_index
|
str
|
See :class: |
'logits'
|
device
|
str
|
See :class: |
'logits'
|
References
Guo et al. (2017), On Calibration of Modern Neural Networks, ICML.
Source code in src/fiducio/calibrators/temperature.py
Ensemble temperature scaling
EnsembleTemperatureScaling
EnsembleTemperatureScaling(*, init_temperature: float = 1.0, optimizer: str = 'adam', lr: float | None = None, max_iter: int | None = None, patience: int | None = None, min_delta: float = 0.0, lr_patience: int | None = None, lr_factor: float = 0.1, input_type: str = 'logits', ignore_index: int = -100, device: DeviceLike | None = None)
Bases: Calibrator
Ensemble Temperature Scaling.
Calibrated probabilities are a convex combination of a temperature-scaled distribution, the original distribution and the uniform distribution:
.. math:: p = w_0\,\mathrm{softmax}(z/T) + w_1\,\mathrm{softmax}(z) + w_2\,u
where :math:w lies on the 3-simplex and :math:u is uniform. Fitting is
done in two stages: first the temperature T (NLL), then the mixture
weights w with T fixed.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
init_temperature
|
float
|
Initial temperature for stage 1. |
1.0
|
optimizer
|
str
|
|
'adam'
|
lr
|
float | None
|
Learning rate. Defaults to |
None
|
max_iter
|
int | None
|
Maximum optimizer iterations per stage. Defaults to |
None
|
patience
|
int | None
|
Optional validation-based early stopping (Adam only), see
:class: |
None
|
min_delta
|
int | None
|
Optional validation-based early stopping (Adam only), see
:class: |
None
|
lr_patience
|
int | None
|
Optional validation-based early stopping (Adam only), see
:class: |
None
|
lr_factor
|
int | None
|
Optional validation-based early stopping (Adam only), see
:class: |
None
|
input_type
|
str
|
See :class: |
'logits'
|
ignore_index
|
str
|
See :class: |
'logits'
|
device
|
str
|
See :class: |
'logits'
|
Notes
Class ordering is preserved in exact arithmetic: each mixture component is
monotone in the input scores and the weights are non-negative, so the
calibrated probabilities never strictly invert the input order. In float32
the mixture can round to exact ties, most visibly once w2 (the uniform
component) dominates; argmax then resolves ties by class index and may
differ from the uncalibrated argmax. Treat the guarantee as order
preservation up to floating-point ties.
References
Adapted from Zhang et al. (2020), Mix-n-Match: Ensemble and Compositional
Methods for Uncertainty Calibration in Deep Learning, ICML. Like the
authors' reference code, fitting is sequential (T, then w with T
fixed). Unlike it, both stages minimize the NLL rather than squared error,
w is parameterized by a softmax instead of a constrained SLSQP solve, and
stage 2 starts at w = [0.998, 0.001, 0.001] because a softmax cannot start
at a one-hot.
Source code in src/fiducio/calibrators/ensemble_temperature.py
Vector scaling
VectorScaling
VectorScaling(*, optimizer: str = 'adam', lr: float | None = None, max_iter: int | None = None, patience: int | None = None, min_delta: float = 0.0, lr_patience: int | None = None, lr_factor: float = 0.1, lambda_reg: float = 0.0, mu_reg: float = 0.0, input_type: str = 'logits', ignore_index: int = -100, device: DeviceLike | None = None)
Bases: _AffineCalibrator
Vector scaling: softmax(diag(w) z + b).
A per-class generalisation of temperature scaling with one scale w_c and
one bias b_c per class. Operates on logits (or log of probabilities
when input_type='probs').
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
optimizer
|
str
|
|
'adam'
|
lr
|
float | None
|
Learning rate. Defaults to |
None
|
max_iter
|
int | None
|
Maximum optimizer iterations. Defaults to |
None
|
lambda_reg
|
float
|
L2 penalty pulling the scale vector towards 1. |
0.0
|
mu_reg
|
float
|
L2 penalty on the bias vector. |
0.0
|
patience
|
int | None
|
Optional validation-based early stopping (Adam only), see
:class: |
None
|
min_delta
|
int | None
|
Optional validation-based early stopping (Adam only), see
:class: |
None
|
lr_patience
|
int | None
|
Optional validation-based early stopping (Adam only), see
:class: |
None
|
lr_factor
|
int | None
|
Optional validation-based early stopping (Adam only), see
:class: |
None
|
input_type
|
str
|
See :class: |
'logits'
|
ignore_index
|
str
|
See :class: |
'logits'
|
device
|
str
|
See :class: |
'logits'
|
References
Guo et al. (2017), On Calibration of Modern Neural Networks, ICML.
Source code in src/fiducio/calibrators/_affine.py
Matrix scaling
MatrixScaling
MatrixScaling(*, optimizer: str = 'adam', lr: float | None = None, max_iter: int | None = None, patience: int | None = None, min_delta: float = 0.0, lr_patience: int | None = None, lr_factor: float = 0.1, lambda_reg: float = 0.0, mu_reg: float = 0.0, input_type: str = 'logits', ignore_index: int = -100, device: DeviceLike | None = None)
Bases: _AffineCalibrator
Matrix scaling: softmax(W z + b) with a full C x C matrix.
Optional off-diagonal / bias L2 regularisation (ODIR) penalizes class mixing and bias. Diagonal entries are not directly penalized.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
optimizer
|
str
|
|
'adam'
|
lr
|
float | None
|
Learning rate. Defaults to |
None
|
max_iter
|
int | None
|
Maximum optimizer iterations. Defaults to |
None
|
lambda_reg
|
float
|
L2 penalty on the off-diagonal entries of |
0.0
|
mu_reg
|
float
|
L2 penalty on the bias vector (ODIR). |
0.0
|
patience
|
int | None
|
Optional validation-based early stopping (Adam only), see
:class: |
None
|
min_delta
|
int | None
|
Optional validation-based early stopping (Adam only), see
:class: |
None
|
lr_patience
|
int | None
|
Optional validation-based early stopping (Adam only), see
:class: |
None
|
lr_factor
|
int | None
|
Optional validation-based early stopping (Adam only), see
:class: |
None
|
input_type
|
str
|
See :class: |
'logits'
|
ignore_index
|
str
|
See :class: |
'logits'
|
device
|
str
|
See :class: |
'logits'
|
References
Guo et al. (2017), On Calibration of Modern Neural Networks, ICML; Kull et al. (2019) for off-diagonal/intercept regularisation.
Source code in src/fiducio/calibrators/_affine.py
Translation-invariant matrix scaling
TranslationInvariantMatrixScaling
TranslationInvariantMatrixScaling(*, optimizer: str = 'adam', lr: float | None = None, max_iter: int | None = None, patience: int | None = None, min_delta: float = 0.0, lr_patience: int | None = None, lr_factor: float = 0.1, lambda_reg: float = 0.0, mu_reg: float = 0.0, input_type: str = 'logits', ignore_index: int = -100, device: DeviceLike | None = None)
Bases: _AffineCalibrator
Constrained matrix scaling that is invariant to logit translations.
The matrix W is constrained so that every row has the same learned sum. Because
softmax ignores constant shifts of its input, this makes the calibrated
output invariant to adding the same constant to every input logit
(g(z + c·1) = g(z)). The first C-1 columns and the common row sum
are optimized, with the final column reconstructed. Initialization is the
identity and regularization uses the reconstructed matrix, matching the
research MSc parameterization.
Parameters are identical to :class:MatrixScaling.
Source code in src/fiducio/calibrators/_affine.py
Dirichlet calibration
DirichletCalibration
DirichletCalibration(*, optimizer: str = 'adam', lr: float | None = None, max_iter: int | None = None, patience: int | None = None, min_delta: float = 0.0, lr_patience: int | None = None, lr_factor: float = 0.1, lambda_reg: float = 0.0, mu_reg: float = 0.0, input_type: str = 'logits', ignore_index: int = -100, device: DeviceLike | None = None)
Bases: _AffineCalibrator
Dirichlet calibration: softmax(W log p + b).
A log-linear transform in probability space, equivalent to matrix scaling
applied to log-probabilities. When input_type='logits' the inputs are
converted to log-probabilities with log_softmax before calibration.
Off-diagonal / bias L2 regularisation (ODIR) is recommended.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
optimizer
|
str
|
|
'adam'
|
lr
|
float | None
|
Learning rate. Defaults to |
None
|
max_iter
|
int | None
|
Maximum optimizer iterations. Defaults to |
None
|
lambda_reg
|
float
|
L2 penalty on the off-diagonal entries of |
0.0
|
mu_reg
|
float
|
L2 penalty on the bias vector (ODIR). |
0.0
|
patience
|
int | None
|
Optional validation-based early stopping (Adam only), see
:class: |
None
|
min_delta
|
int | None
|
Optional validation-based early stopping (Adam only), see
:class: |
None
|
lr_patience
|
int | None
|
Optional validation-based early stopping (Adam only), see
:class: |
None
|
lr_factor
|
int | None
|
Optional validation-based early stopping (Adam only), see
:class: |
None
|
input_type
|
str
|
See :class: |
'logits'
|
ignore_index
|
str
|
See :class: |
'logits'
|
device
|
str
|
See :class: |
'logits'
|
References
Kull et al. (2019), Beyond temperature scaling: Obtaining well-calibrated multiclass probabilities with Dirichlet calibration, NeurIPS.
Source code in src/fiducio/calibrators/_affine.py
Class-conditional matrix scaling
ClassConditionalMatrixScaling
ClassConditionalMatrixScaling(*, optimizer: str = 'adam', lr: float | None = None, max_iter: int | None = None, patience: int | None = None, min_delta: float = 0.0, lr_patience: int | None = None, lr_factor: float = 0.1, lambda_reg: float = 0.0, mu_reg: float = 0.0, independent_experts: bool = False, init_alpha: float = 1.0, init_floor: float = 1e-06, input_type: str = 'logits', ignore_index: int = -100, device: DeviceLike | None = None)
Bases: _ClassConditionalBase
Class-conditional matrix scaling (CMS).
One unconstrained affine map A_c log p + b_c per uncalibrated top class
c. Unlike the preserving variants it may change the argmax. Off-diagonal
and bias L2 regularization (lambda_reg / mu_reg) penalize class mixing
and bias, leaving the diagonal unpenalized. Experts are optimized jointly by default; set
independent_experts=True to fit each one in its own optimization loop.
Source code in src/fiducio/calibrators/class_conditional.py
Argmax-preserving matrix scaling
ArgmaxPreservingMatrixScaling
ArgmaxPreservingMatrixScaling(*, optimizer: str = 'adam', lr: float | None = None, max_iter: int | None = None, patience: int | None = None, min_delta: float = 0.0, lr_patience: int | None = None, lr_factor: float = 0.1, lambda_reg: float = 0.0, mu_reg: float = 0.0, independent_experts: bool = False, init_alpha: float = 1.0, init_floor: float = 1e-06, input_type: str = 'logits', ignore_index: int = -100, device: DeviceLike | None = None)
Bases: _ClassConditionalBase
Argmax-preserving class-conditional matrix scaling (CMSAP).
Parameterised through non-negative margins between the top class and its
competitors, which guarantees the calibrated argmax equals the uncalibrated
argmax in exact arithmetic; floating-point ties and rounding can change it.
Experts are optimized jointly by default; set
independent_experts=True to fit each one in its own optimization loop.
Source code in src/fiducio/calibrators/class_conditional.py
Order-preserving matrix scaling
OrderPreservingMatrixScaling
OrderPreservingMatrixScaling(*, optimizer: str = 'adam', lr: float | None = None, max_iter: int | None = None, patience: int | None = None, min_delta: float = 0.0, lr_patience: int | None = None, lr_factor: float = 0.1, lambda_reg: float = 0.0, mu_reg: float = 0.0, independent_experts: bool = False, init_alpha: float = 1.0, init_floor: float = 1e-06, input_type: str = 'logits', ignore_index: int = -100, device: DeviceLike | None = None)
Bases: _ClassConditionalBase
Order-preserving class-conditional matrix scaling (CMSOP).
Parameterised through non-negative gaps between consecutively ranked
classes, which guarantees the full class ranking is preserved for every
voxel in exact arithmetic, subject to floating-point ties and rounding.
Experts are optimized jointly by default; set
independent_experts=True to fit each one in its own optimization loop.
Source code in src/fiducio/calibrators/class_conditional.py
Binary isotonic calibration
BinaryIsotonicCalibration
BinaryIsotonicCalibration(*, n_bins: int = 250, input_type: str = 'logits', ignore_index: int = -100, device: DeviceLike | None = None)
Bases: Calibrator
Monotone binary calibration by equal-mass bins and weighted PAVA.
Fits class 1 against hard labels or soft distributions, including the mean human response (MHR). Inspired by Kirscher et al., TwinTrack: Post-hoc Multi-Rater Calibration for Medical Image Segmentation, MIDL 2026 (short paper), https://arxiv.org/abs/2604.15950v2.
Equal scores are never split across bins. Approximate equal-mass binning, linear interpolation between fitted points and constant extrapolation are explicit Fiducio conventions, not a claim of exact TwinTrack reproduction. Weighted PAVA minimizes squared error on the bins. The map is nondecreasing across voxels and may have plateaus or change a segmentation at threshold 0.5.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
n_bins
|
int
|
Maximum number of fitting bins (default 250). Ties can reduce this count. |
250
|
input_type
|
str
|
See :class: |
'logits'
|
ignore_index
|
str
|
See :class: |
'logits'
|
device
|
str
|
See :class: |
'logits'
|
Notes
Fit sorts all valid voxels: O(N log N) time and O(N) memory, not streaming.
No optimizer or validation-based early stopping is used. Learned coordinates
are stored in float64; public probabilities are float32. Fitted map values
of exactly 0/1 are returned without clipping. Input scores of 0/1 are still
calibrated and need not map to 0/1.
decision_function returns log-probabilities, including -inf at zeros.
Source code in src/fiducio/calibrators/isotonic.py
transform
Return calibrated probabilities; fitted map outputs of exactly 0/1 are not clipped.
Input scores of 0 or 1 are calibrated like any other score and need not map to 0 or 1 (see the class notes).
Source code in src/fiducio/calibrators/isotonic.py
decision_function
Return log-probabilities; zero probabilities map to mathematical -inf.