Problem
Classification metrics like accuracy and AUC judge a model's decisions and its ranking. Many uses need more: the probability itself. A doctor weighs a 3% risk differently from a 30% one; an ad system multiplies click probability by bid; a fraud team sets its review threshold from the expected cost of each case; a language model decides when to say "I'm not sure". All of these read the number as a frequency.
Most models are not trained to make that reading true. A support vector machine outputs a margin, not a probability. Boosted trees and random forests output averages that are systematically pulled away from, or toward, the extremes. A deep network trained with cross-entropy can be highly accurate and still say 99% on cases it gets wrong a quarter of the time. Calibration is the property that the probabilities mean what they say, and the post-processing that restores it without retraining the model.
Intuition
Take every case where a weather forecaster said "70% chance of rain" and count how often it rained. If it rained on about 70% of them, and the same holds for every other number the forecaster uses, the forecaster is calibrated. Calibration says nothing about how useful the forecasts are: someone who says "it rains on 30% of days" every single day is perfectly calibrated and tells you nothing. A good probabilistic model is both calibrated and sharp, or discriminating: its numbers spread out and separate the cases that happen from the ones that don't.
This split suggests the fix. A model that ranks cases well but uses the wrong numbers can be repaired by a monotone function that relabels its outputs: "when you say 95%, that really means 75%". Learning that function needs only held-out predictions and labels, and since it is monotone it leaves the ranking, and so the AUC, as it was.
Mechanics
What calibrated means
A binary classifier with predicted probability p̂ = f(x) is calibrated if, for every value p it outputs,
For K classes the strict version requires the whole predicted vector to match the conditional class frequencies; the version usually measured is confidence calibration, where the probability of the predicted class should equal the accuracy on cases with that confidence. The condition is about a population, never about one prediction: a single 70% forecast is neither right nor wrong.
Reliability diagrams and ECE
Since p̂ is continuous, the condition is checked in bins. Split [0, 1] into B intervals, put each prediction in its
bin, and compare each bin's mean predicted probability (its confidence) with the fraction of
positives in it (its accuracy, or observed frequency). Plotting accuracy against confidence gives a
reliability diagram; a calibrated model lies on the diagonal, and points below it are
overconfident on the positive side. scikit-learn's calibration_curve returns exactly these two arrays,
with uniform bins by default (n_bins=5) or quantile bins that hold equal numbers of predictions.
The expected calibration error summarizes the diagram as the bin-size-weighted gap, and the maximum calibration error as the worst bin:
ECE is easy to read but depends on the binning: few bins hide errors, many bins leave few cases per bin and add noise, so an ECE should always be reported with its bin scheme. It is a diagnostic, not a training objective; scikit-learn provides the curve but no ECE function.
Proper scoring rules: Brier score and log loss
A proper scoring rule is one whose expected value is optimized by reporting your true belief, so no model can gain by shading its probabilities. Two are standard:
Both reward calibration and sharpness together (the Brier score splits exactly into a reliability term minus a resolution term plus the irreducible uncertainty of the labels, when predictions are grouped by value). The Brier score is bounded and forgiving; log loss punishes confident mistakes without limit, which makes it the one models are trained on and the one temperature scaling minimizes. Neither tells you whether the problem is calibration, which is why they are reported next to a reliability diagram.
Recalibration: Platt, isotonic and temperature
All three methods fit a monotone map g from the model's output to a new probability, on data the model was not trained on. Fitting it on the training set learns nothing useful, since the model's training predictions are more confident than its predictions on new data.
- Platt scaling (Platt, 2000, for SVMs) is a one-feature logistic regression on the model's score s: g(s) = σ(a·s + b). Two parameters, so it needs little data, but it can only produce sigmoid-shaped corrections. To avoid overfitting small sets, Platt replaced the 0/1 labels with targets (N+ + 1)/(N+ + 2) and 1/(N− + 2), and scikit-learn does the same.
- Isotonic regression (Zadrozny and Elkan, 2002) fits the best non-decreasing step function in squared error. The pool-adjacent-violators algorithm sorts by score, walks left to right, and whenever a block's mean is not below the next block's, merges them into their weighted mean, repeating backwards until the sequence is monotone. It can fix any monotone distortion but has as many parameters as steps, so it overfits small calibration sets; scikit-learn's documentation advises against it well below about a thousand samples.
- Temperature scaling (Guo et al., 2017) divides the logits by one number T > 0 before the softmax: p̂ = softmax(z / T). T is fitted by minimizing log loss on a validation set. T > 1 softens overconfident outputs. One parameter, no change to the argmax, and it works for any number of classes directly; for binary models it is Platt scaling with the intercept fixed at zero.
| Component | Shape/Params | Meaning | Source |
|---|---|---|---|
CalibratedClassifierCV | method="sigmoid", cv=None, ensemble="auto" | Wraps a classifier and fits a calibrator on held-out folds | calibration.py |
method | 'sigmoid', 'isotonic' or 'temperature' | Platt scaling, isotonic regression, or temperature scaling (added in 1.8) | calibration.py |
_sigmoid_calibration | returns slope a, intercept b | Platt's fit with smoothed targets, by L-BFGS; predicts σ(−(a·s + b)), so a is negative for a good model | calibration.py |
_TemperatureScaling | beta_ = 1/T | Minimizes multinomial log loss over log β in [−10, 10]; probabilities are first turned back into logits with log | calibration.py |
IsotonicRegression(out_of_bounds="clip") | step thresholds X_thresholds_, y_thresholds_ | The isotonic calibrator; interpolates linearly between steps | calibration.py |
_inplace_contiguous_isotonic_regression | y, w of length n | Pool-adjacent-violators in one pass with backtracking | _isotonic.pyx |
calibration_curve | n_bins=5, strategy="uniform" | Returns per-bin accuracy and confidence, dropping empty bins | calibration.py |
brier_score_loss | scalar | Mean squared error of the probabilities | _classification.py |
log_loss | scalar | Mean negative log-likelihood | _classification.py |
Why modern networks are overconfident
Guo et al. (2017) found that a 1998 LeNet was well calibrated on CIFAR-100, while a 2016 ResNet, far more accurate, was overconfident: their average confidence exceeds their accuracy. They linked the change to greater depth and width, batch normalization, and training with less weight decay, and observed that test log loss starts to rise while test error keeps falling: the network overfits the loss, not the labels.
The loss explains the incentive. Once a training example is classified correctly, cross-entropy can still be lowered by making its logit larger, without changing any decision. Scaling every logit up shrinks the loss on correct examples toward zero and grows it on wrong ones only linearly, so on a training set the network mostly gets right, growing its logits pays. The result carries over to test data, where more examples are wrong. Temperature scaling undoes exactly this uniform inflation, which is why one parameter fixes so much of it.
Worked example
Twenty held-out predictions
A classifier makes 20 predictions on held-out data; 11 of the cases are positive (base rate 0.55). Grouped into calibration_curve's five uniform bins:
| Bin | Predictions | Positives | Confidence | Accuracy | Gap | Weight |
|---|---|---|---|---|---|---|
| (0.0, 0.2] | 0.05, 0.05, 0.1, 0.1, 0.15 | 1 of 5 | 0.090 | 0.200 | 0.110 | 0.25 |
| (0.2, 0.4] | 0.3, 0.35 | 1 of 2 | 0.325 | 0.500 | 0.175 | 0.10 |
| (0.4, 0.6] | 0.5, 0.55 | 1 of 2 | 0.525 | 0.500 | 0.025 | 0.10 |
| (0.6, 0.8] | 0.65, 0.7, 0.75 | 2 of 3 | 0.700 | 0.667 | 0.033 | 0.15 |
| (0.8, 1.0] | 0.85, 0.9, 0.9, 0.95, 0.95, 0.95, 0.99, 0.99 | 6 of 8 | 0.935 | 0.750 | 0.185 | 0.40 |
The top bin is the problem: eight predictions averaging 0.935, of which only 6 were positive (0.75). The bottom bin is overconfident the other way, predicting 0.09 where 0.2 happened. ECE is 0.25·0.110 + 0.10·0.175 + 0.10·0.025 + 0.15·0.033 + 0.40·0.185 = 0.1265, and the worst bin's gap (MCE) is 0.185.
Compare a model that ignores its input and always says 0.55. Every prediction falls in one bin whose accuracy is exactly 0.55, so its ECE is 0.00: perfectly calibrated. Its AUC is 0.50, and the proper scoring rules see through it: Brier 0.2475 and log loss 0.6881, both worse than the real model's 0.2219 and 0.6705.
Three recalibrators on the same predictions
Each method is fitted on the twenty predictions above and scored on them, to keep the arithmetic visible. In practice the calibrator is fitted on one held-out set and scored on another, and the in-sample numbers below flatter it, isotonic most of all.
Temperature. Turn each probability back into a logit, z = log(p / (1 − p)), so 0.99 becomes 4.595 and 0.05 becomes -2.944. Newton's method on the log loss of σ(β·z) converges to β = 0.4555, so T = 1/β = 2.195. Every logit is divided by 2.20: 0.99 becomes 0.890, 0.95 becomes 0.793, 0.05 becomes 0.207. Nothing crosses 0.5, so the decisions and the AUC are unchanged.
Platt. With 11 positives and 9 negatives, the targets are 12/13 = 0.923 and 1/11 = 0.091. Fitting σ(a·z + b) to them gives a = 0.3639 and b = -0.0173 (scikit-learn would store −a and −b). The intercept is almost zero, so this is nearly temperature scaling with T = 1/a = 2.75; the smoothed targets pull it a little further toward 0.5, and 0.99 becomes 0.840.
Isotonic. The 14 distinct scores, sorted, have positive rates 0.00, 0.50, 0.00, 1.00, 0.00, 1.00, 0.00, 1.00, 0.00, 1.00, 1.00, 0.50, 0.67, 1.00. That sequence rises and falls, so pool-adjacent-violators merges neighbours 9 times into 5 monotone steps:
| Scores | Cases | Calibrated probability |
|---|---|---|
| 0.05 | 2 | 0.000 |
| 0.1–0.15 | 3 | 0.333 |
| 0.3–0.7 | 6 | 0.500 |
| 0.75–0.95 | 7 | 0.714 |
| 0.99 | 2 | 1.000 |
The result scores perfectly in-sample, ECE 0.00, because each step is the observed rate of its own cases. But it predicts exactly 0 and 1 at the ends, so a single new mistake there costs infinite log loss, and its ties change the ranking: AUC moves from 0.7475 to 0.7828. With twenty points isotonic is memorizing; it earns its flexibility only with thousands.
| Predictions | ECE | MCE | Brier | Log loss | AUC | Accuracy at 0.5 |
|---|---|---|---|---|---|---|
| Original | 0.1265 | 0.185 | 0.2219 | 0.6705 | 0.7475 | 65% |
| Constant 0.55 | 0.0000 | 0.000 | 0.2475 | 0.6881 | 0.5000 | 55% |
| Platt | 0.0720 | 0.160 | 0.1995 | 0.5827 | 0.7475 | 65% |
| Temperature | 0.0329 | 0.110 | 0.1989 | 0.5789 | 0.7475 | 65% |
| Isotonic (in-sample) | 0.0000 | 0.000 | 0.1798 | 0.5128 | 0.7828 | 70% |
What a larger logit buys
Take an example with logit 2 and multiply the logit by k. Its loss if the label agrees, −log σ(kz), and if it doesn't, −log σ(−kz):
| k | Logit | Probability | Loss if right | Loss if wrong |
|---|---|---|---|---|
| 1 | 2 | 0.880797 | 1.27e-01 | 2.127 |
| 2 | 4 | 0.982014 | 1.81e-02 | 4.018 |
| 4 | 8 | 0.999665 | 3.35e-04 | 8.000 |
| 8 | 16 | 1.000000 | 1.13e-07 | 16.000 |
The two losses always differ by exactly the logit. Doubling from k = 1 to 2 saves 0.109 on each correct example and costs 1.891 on each wrong one, so on a training set where more than about 95% of such examples are right, the larger logits lower the training loss. A network that fits its training set well is pushed toward confidence it cannot back up on new data.
Implementation
Calibrating a fitted classifier on a held-out set with scikit-learn, and checking it on a third split. Wrapping the model
in FrozenEstimator tells CalibratedClassifierCV not to refit it, so ensemble="auto"
resolves to False and the calibrator is fitted on all of the calibration data.
from sklearn.calibration import CalibratedClassifierCV, calibration_curve
from sklearn.frozen import FrozenEstimator
from sklearn.metrics import brier_score_loss, log_loss
model.fit(X_train, y_train) # any classifier
calibrated = CalibratedClassifierCV(FrozenEstimator(model), method="temperature")
calibrated.fit(X_cal, y_cal) # data the model never saw
for name, clf in [("raw", model), ("calibrated", calibrated)]:
p = clf.predict_proba(X_test)[:, 1]
frac_pos, mean_pred = calibration_curve(y_test, p, n_bins=10, strategy="quantile")
ece = abs(frac_pos - mean_pred).mean() # equal-count bins, so a plain mean
print(name, brier_score_loss(y_test, p), log_loss(y_test, p), ece)
With quantile bins every bin holds about the same number of predictions, so the unweighted mean of the gaps is the ECE;
with uniform bins it would need the bin counts. In PyTorch, temperature scaling is a single learnable scalar: collect
validation logits once, then minimize F.cross_entropy(logits / T, labels) over T and divide by T at inference.
Swap "temperature" for "sigmoid" on small calibration sets with a binary model that is biased toward
one class, and for "isotonic" only with thousands of calibration examples.
Tradeoffs
- Platt vs isotonic vs temperature. Platt and temperature scaling have one or two parameters and are hard to overfit, but can only apply a sigmoid-shaped correction. Isotonic fits any monotone distortion but needs much more data, produces ties, and can output exact 0 and 1. Temperature scaling is the default for neural networks and multiclass models; Platt's extra intercept helps when the model is biased toward one class.
- Calibration data. Every example spent on calibration is one not spent on training. Cross-validated
calibration (
CalibratedClassifierCVwithcv) reuses the data at the cost of fitting several models. - ECE vs proper scores. ECE isolates calibration but depends on the bins and can be zero for a useless model; Brier and log loss can't be gamed but mix calibration with discrimination. Report both.
- Average vs subgroup calibration. A model can be calibrated overall and miscalibrated for a subgroup or a slice of the input space. Check reliability diagrams per important segment.
- Shift. Calibration holds for the distribution it was fitted on. When inputs or the base rate change, it degrades first, often before accuracy does, so it needs monitoring and refitting.
- Resampling and class weights. Training on rebalanced data (see class imbalance) deliberately shifts the probabilities toward the minority class; recalibrate on data with the real base rate.
A model predicts the base rate for everyone. What are its ECE and AUC?
ECE 0, since its one bin's accuracy equals its confidence, and AUC 0.5, since it can't rank. Calibration alone does not make a model useful; that is why Brier and log loss are reported alongside it.
Why does temperature scaling never change accuracy?
Dividing all logits by the same positive T keeps their order, so the argmax, and for binary models the side of 0.5, is the same. It only changes how confident the model is.
You fit isotonic calibration on 200 examples and its validation ECE is near zero. Should you trust it?
Not if that is the same data it was fitted on: isotonic matches its own data almost by construction. Score it on a separate split, and with 200 examples prefer Platt or temperature scaling.
A model was trained on data rebalanced to 50% positives; the real rate is 5%. What happens to its probabilities?
They are far too high for the real population, even though ranking is fine. Recalibrate on data with the real base rate, or correct the odds by the ratio of real to training prior odds.
Connections
Read first
- Classification metrics intro
Read next
- Uncertainty advanced
- Monitoring and drift core
References
- Guo, Pleiss, Sun and Weinberger. On Calibration of Modern Neural Networks. ICML 2017. arXiv:1706.04599
- Platt. Probabilistic Outputs for Support Vector Machines and Comparisons to Regularized Likelihood Methods. In Advances in Large Margin Classifiers, MIT Press, 2000.
- Zadrozny and Elkan. Transforming Classifier Scores into Accurate Multiclass Probability Estimates. KDD 2002.
- Niculescu-Mizil and Caruana. Predicting Good Probabilities with Supervised Learning. ICML 2005.
- Brier. Verification of Forecasts Expressed in Terms of Probability. Monthly Weather Review, 1950.
- scikit-learn 1.9.1:
CalibratedClassifierCV,calibration_curve,IsotonicRegression,brier_score_loss.