Definition
A model is calibrated when its confidence tells the truth: of all the times it says something is 80% likely, close to 80% of them actually happen. The number it prints next to an answer is a promise about how often that kind of answer is right, and calibration is whether the model keeps that promise. This is a completely different question from whether any single answer is correct — it is about the honesty of the probability, not the verdict.
The idea comes from weather forecasting, which is where reliability diagrams and calibration scores were first used. When a forecaster says "70% chance of rain," you cannot check that against a single day — it either rained or it did not. You check it against every day they said 70%: if it rained on about 70% of them, the forecast was calibrated, and if it rained on 40% of them, the forecaster was overconfident. A calibrated probability is one you can act on, because the number means what it says.
Why does this matter enough to name? Because a model can be highly accurate and still lie about its confidence, and it is the confidence you build decisions on. A medical triage model that is 90% accurate but reports 99% on every case will send borderline patients home, because nothing ever looks uncertain to it. A model that abstains whenever it is less than 95% sure only works if "95% sure" is a real 95%. The moment you set a threshold on a model's confidence — to flag, to escalate, to auto-approve, to ask a human — you are trusting its calibration, and accuracy alone will not tell you whether that trust is warranted.
How It Works
Start with the reliability diagram, the picture calibration is defined against. Take a model's predictions on a test set, and for each one record two things: the confidence it assigned to its top choice, and whether that choice was correct. Sort the predictions into confidence bins — say ten bins of width 0.1 — and for each bin compute two numbers: the average confidence of the predictions in it, and the fraction that were actually correct (the bin's accuracy). Plot accuracy against confidence. A perfectly calibrated model lands on the diagonal, where accuracy equals confidence in every bin. A bar that sits below the diagonal is a bin where the model was more confident than it deserved — overconfidence — and a bar above it is underconfidence.
The diagram is the intuition; Expected Calibration Error (ECE) is the single number that summarizes it. ECE is the average gap between confidence and accuracy across the bins, weighted by how many predictions fall in each bin, so a bin holding half your predictions counts for more than a bin holding three of them. Written out, with n total predictions split into bins B_1 through B_M:
ECE = sum over m of (|B_m| / n) * | acc(B_m) - conf(B_m) |
Here acc(B_m) is the accuracy inside bin m, conf(B_m) is its average confidence, and |B_m|/n is its share of all predictions. Guo et al. used M = 15 bins. Zero means every bin sits exactly on the diagonal.
A worked example
Suppose a model makes ten predictions, and we group them into three confidence bins. For each bin we record how many predictions landed in it, their average confidence, and the fraction that were correct:
| Bin | Count | Avg confidence | Accuracy | Gap | Weight |
|---|---|---|---|---|---|
| [0.5, 0.7) | 4 | 0.62 | 0.50 (2 of 4) | 0.12 | 0.40 |
| [0.7, 0.9) | 4 | 0.80 | 0.50 (2 of 4) | 0.30 | 0.40 |
| [0.9, 1.0] | 2 | 0.95 | 0.50 (1 of 2) | 0.45 | 0.20 |
Every bin is overconfident — its accuracy sits below its average confidence — and the gap widens as the model gets more sure of itself, which is the classic shape of a modern network. Now weight each gap by the bin's share and add them up:
ECE = 0.40 * 0.12 + 0.40 * 0.30 + 0.20 * 0.45
= 0.048 + 0.120 + 0.090
= 0.258 -> 25.8%
The model is right half the time (overall accuracy 50%), but its average confidence across all ten predictions is 75.8% — it walks around believing it is right three times in four while being right one time in two, and ECE puts a number on that self-deception: 25.8 points of confidence it has not earned.
The example also shows why accuracy and calibration are genuinely two properties, not one. Suppose this same model had instead reported 0.50 on all ten predictions. Its accuracy would be unchanged — still 50%, because we did not touch which answers it gave — but its ECE would drop to 0, because now every bin's confidence matches its accuracy. Same correctness, opposite calibration. You cannot read one off the other, which is exactly why calibration needs its own metric.
Real-World Applications
The oldest and most disciplined use of calibration is probabilistic weather forecasting, which is where the whole apparatus was built. A published "40% chance of rain" is only useful if, over hundreds of such days, it rains about 40% of the time, and national weather services verify their forecasts against exactly this standard using reliability diagrams. The forecast that is well calibrated is the one you can plan a wedding or a harvest around; the miscalibrated one is noise wearing a percentage.
In machine learning, calibration became a standard concern once temperature scaling turned it into a cheap post-training step. Because the fix is a single learned parameter that leaves the model's ranked predictions untouched, it can be bolted onto an already-trained classifier without retraining or any accuracy cost, and it has become a routine final stage in classification pipelines where the probability itself is consumed downstream.
The applications that most depend on calibration are the ones that act on a confidence threshold. Selective prediction, where a system answers only when it is sure enough and otherwise abstains or escalates to a human, is only safe if the confidence used for the cutoff is honest — a related but distinct framework, conformal prediction, exists precisely because raw softmax confidence often is not trustworthy enough on its own. The same logic governs a medical model that routes low-confidence scans to a radiologist, a fraud system that auto-approves transactions above a probability, and a perception stack in an autonomous vehicle deciding whether it has seen an obstacle clearly enough to brake. In every case the decision is a comparison against the model's confidence, so a miscalibrated confidence is a miscalibrated decision.
Key Concepts
Overconfidence in modern networks. The headline finding of Guo et al. (2017), "On Calibration of Modern Neural Networks," is that the networks that got more accurate over the years got less honest about it. Their comparison is stark: a 5-layer LeNet from 1998 on CIFAR-100 had average confidence that closely tracked its accuracy, while a 110-layer ResNet from 2016 on the same task was far more accurate but substantially overconfident, with an Expected Calibration Error of 16.53%. They traced this to the very things that improved accuracy — increasing depth and width both raised ECE, batch normalization made models more miscalibrated, and training with less weight decay hurt calibration even past the point where it stopped helping accuracy. Better accuracy and worse calibration arrived together.
The post-hoc fixes. Rather than retrain, you learn a small correction on a held-out validation set and apply it to the model's outputs. There are several, and they trade flexibility against data hunger:
- Platt scaling fits a logistic regression to the model's scores, turning them into calibrated probabilities with just two parameters — cheap, but it can only apply a smooth sigmoid-shaped correction.
- Isotonic regression fits any non-decreasing function, so it can correct more complex distortions, at the cost of needing more validation data and risking overfitting on small sets.
- Histogram binning sorts predictions into bins and replaces each bin's confidence with its observed accuracy — simple and direct, but its output is coarse and step-shaped.
- Temperature scaling divides all the logits by a single learned scalar
Tbefore the softmax. ATabove 1 softens an overconfident distribution toward uniform; aTbelow 1 sharpens it. It is the simplest of the four — one number for the whole model — yet on Guo et al.'s vision benchmarks it was the most effective, cutting the ResNet-110's CIFAR-100 ECE from 16.53% to 1.26%, versus 2.66% for histogram binning and 4.99% for isotonic regression.
Temperature scaling is not sampling temperature. This is the trap to name explicitly. The temperature that an LLM API exposes and the temperature in temperature scaling share one line of arithmetic — both divide logits by a scalar before the softmax — but they are different tools for different jobs. Sampling temperature is a knob you turn at generation time to make output more random or more deterministic, and it changes what the model produces. Calibration temperature is a single value fit once, after training, to make the model's probabilities honest; crucially, dividing every logit by the same T never changes which class has the largest logit, so temperature scaling leaves the model's predictions and its accuracy exactly as they were — it only rescales the confidence attached to them.
Calibration is not correctness. A calibrated model is not a more accurate model. It has learned to be honest about how often it is wrong, which is a different and more modest virtue. This is worth stating plainly because it is a common source of disappointment: recalibrating a model will make its 70% mean 70%, but it will not make its 70%s become 90%s.
Challenges
Calibration decays when the data shifts. A model calibrated on one distribution is not calibrated on another. The correction is fit on validation data drawn from the training distribution, so the moment the live inputs drift — a new population, a new season, an adversarial mix — the learned temperature or scaling curve no longer matches reality, and calibration silently rots. This ties calibration directly to concept drift: monitoring accuracy alone can miss it, because a model can stay roughly as accurate while its confidence quietly goes wrong.
ECE has real blind spots. The metric is convenient but crude. It usually only looks at the top predicted class, so it can call a multi-class model "calibrated" while the probabilities it assigns to the other classes are nonsense — a problem whenever you use the full distribution rather than just the winner. ECE is also sensitive to how you bin: too few bins hide miscalibration by averaging it away, too many leave each bin with so few samples that its accuracy estimate is noise. Two labs reporting "ECE" with different bin counts are not reporting the same number, which is why the bin count has to be stated alongside it.
A perfect ECE can still hide danger. Because ECE is an average over bins, a model can post a low overall score while being severely overconfident in one narrow, rare, high-stakes region — exactly the region a safety-critical system cares about. The aggregate metric was designed to summarize the reliability diagram, and summarizing throws away the tails. When the cost of a confident error is not uniform, the single number is not enough and you have to look at the diagram.
Recalibration costs held-out data. Every post-hoc method needs a validation set that was not used for training, and it needs enough of it — isotonic regression especially will overfit a small one and produce a correction that is itself miscalibrated on new data. That is data you cannot also spend on training, which is a real budget in low-data settings.
Code Example
This computes ECE for the ten-prediction example above from raw per-prediction data — the confidence the model assigned and whether it was correct — in pure Python. Running it reproduces the table and the 25.8% figure exactly:
confidences = [0.55, 0.60, 0.65, 0.68, # predictions that land in bin [0.5, 0.7)
0.72, 0.78, 0.83, 0.87, # predictions that land in bin [0.7, 0.9)
0.93, 0.97] # predictions that land in bin [0.9, 1.0]
correct = [1, 1, 0, 0, 1, 0, 1, 0, 1, 0] # 1 = the model's top guess was right
edges = [0.5, 0.7, 0.9, 1.001] # three confidence bins
n = len(confidences)
ece = 0.0
print(f"{'bin':<12}{'count':>6}{'avg conf':>10}{'accuracy':>10}{'gap':>8}{'weight':>8}")
for lo, hi in zip(edges[:-1], edges[1:]):
idx = [i for i, c in enumerate(confidences) if lo <= c < hi]
if not idx:
continue
count = len(idx)
avg_conf = sum(confidences[i] for i in idx) / count
accuracy = sum(correct[i] for i in idx) / count
gap = abs(accuracy - avg_conf)
weight = count / n
ece += weight * gap
label = f"[{lo:.1f},{min(hi,1.0):.1f})"
print(f"{label:<12}{count:>6}{avg_conf:>10.3f}{accuracy:>10.3f}{gap:>8.3f}{weight:>8.2f}")
overall_acc = sum(correct) / n
overall_conf = sum(confidences) / n
print(f"\noverall accuracy = {overall_acc:.3f}")
print(f"overall confidence = {overall_conf:.3f}")
print(f"ECE = {ece:.3f} ({ece*100:.1f}%)")
Output:
bin count avg conf accuracy gap weight
[0.5,0.7) 4 0.620 0.500 0.120 0.40
[0.7,0.9) 4 0.800 0.500 0.300 0.40
[0.9,1.0) 2 0.950 0.500 0.450 0.20
overall accuracy = 0.500
overall confidence = 0.758
ECE = 0.258 (25.8%)
To calibrate this model you would fit a correction — the simplest being temperature scaling, one scalar T chosen to minimize this same ECE (or the negative log-likelihood) on a validation set — and then recompute the table. The gaps shrink toward zero while the accuracy column stays put, because rescaling confidence never changes which answer the model picked.