Part IV: Deep Learning for Sensor Time Series
Chapter 18: Uncertainty, Calibration, and Conformal Prediction

Calibration and temperature scaling for time series

"When I say I am 90 percent sure, I want to be wrong exactly one time in ten. A confident liar is worse than an honest doubter."

A Well-Calibrated AI Agent

Prerequisites

This section builds on the aleatoric-versus-epistemic split from Section 18.1: calibration is about making the aleatoric story, the probability your model reports, honest. You need softmax, cross-entropy, and negative log-likelihood from Appendix B, the proper scoring rule notion from Chapter 4, and the leakage-safe splitting discipline of Chapter 5, the load-bearing idea once we move from images to streams. Any classifier or forecaster from Chapter 14 or Chapter 15 will do as the model whose confidences we repair.

The Big Picture

A modern neural network is usually a good ranker and a bad probabilist: it puts the right class on top far more often than it deserves the 99.8 percent it prints next to it. That gap between stated confidence and empirical accuracy is miscalibration, and it is not cosmetic: a fall detector that cries "97 percent certain" on every twitch, or an arrhythmia model whose "borderline" and "definite" look identical to the cardiologist, will be switched off by the people who depend on it. Calibration is the cheap, post-hoc repair. The flagship trick, temperature scaling, is a single learned number that rescales the logits until the probabilities tell the truth, changing not one prediction's rank and costing a few dozen lines of code. The catch: every derivation assumes the calibration data are exchangeable with the test data. Sensor streams are autocorrelated and drift, so a carelessly carved calibration set will lie to you about how well you calibrated. This section teaches both together.

What calibration means, and how to measure it

Let a classifier output a probability vector \(\hat{p}(x)\) with predicted class \(\hat{y}=\arg\max_k \hat{p}_k(x)\) and confidence \(\hat{c}=\max_k \hat{p}_k(x)\). Perfect calibration is the statement

$$\mathbb{P}\big(\hat{y}=y \,\big|\, \hat{c}=c\big) = c \quad\text{for all } c\in[0,1].$$

Read literally: among all windows the model called "80 percent," it should be right 80 percent of the time. We cannot condition on a continuous \(c\), so we bin. Sort predictions into \(M\) confidence bins \(B_1,\dots,B_M\), and compare each bin's average confidence to its empirical accuracy. The Expected Calibration Error is the weighted average gap,

$$\mathrm{ECE} = \sum_{m=1}^{M} \frac{|B_m|}{n}\,\big|\operatorname{acc}(B_m) - \operatorname{conf}(B_m)\big|,$$

and plotting \(\operatorname{acc}(B_m)\) against \(\operatorname{conf}(B_m)\) gives the reliability diagram: the diagonal is perfect, points below it are overconfident (the usual failure), points above underconfident. ECE is a diagnostic, not a training loss, and can be gamed; report it alongside negative log-likelihood or Brier score, which are proper scoring rules (Chapter 4), so improving calibration cannot secretly wreck sharpness.

Key Insight

Accuracy and calibration are orthogonal. A model can be 92 percent accurate and grossly overconfident, or 70 percent accurate and perfectly calibrated. Temperature scaling exploits exactly this independence: because it applies one monotone rescaling to every logit vector, it cannot change which class is largest, so accuracy is provably unchanged. You are free to fix the probabilities without touching the decisions, which is why it is safe to bolt onto a frozen, already-validated model, precisely what a deployed sensor pipeline wants.

Temperature scaling: one number, learned after training

Train the network normally. Freeze it. Now introduce a single scalar \(T>0\), the temperature, and soften every logit vector \(z(x)\) before the softmax:

$$\hat{p}_k(x;T) = \frac{\exp\big(z_k(x)/T\big)}{\sum_j \exp\big(z_j(x)/T\big)}.$$

With \(T=1\) nothing changes. \(T>1\) flattens the distribution toward uniform (less confident); \(T<1\) sharpens it. Because \(T\) scales all logits equally, the argmax and every predicted label is invariant: temperature scaling can change how loudly the model speaks, never what it says. Fit \(T\) by minimizing negative log-likelihood on a held-out calibration set the network never trained on, a one-dimensional, convex-in-practice optimization solved in a handful of iterations. This is Guo et al.'s 2017 result: a single temperature recovers near-perfect calibration across modern architectures, outperforming heavier fixes like Platt scaling, isotonic regression, or Bayesian binning, while adding exactly one parameter. The code below fits \(T\) on cached logits.

import numpy as np
from scipy.optimize import minimize_scalar

def temperature_scale(logits, labels):
    # logits: (n, K) raw pre-softmax outputs on the CALIBRATION split
    # labels: (n,) integer class ids
    def nll(T):
        z = logits / T
        z = z - z.max(axis=1, keepdims=True)          # stable softmax
        logp = z - np.log(np.exp(z).sum(axis=1, keepdims=True))
        return -logp[np.arange(len(labels)), labels].mean()
    res = minimize_scalar(nll, bounds=(0.05, 10.0), method="bounded")
    return res.x

def ece(probs, labels, n_bins=15):
    conf = probs.max(axis=1); pred = probs.argmax(axis=1)
    correct = (pred == labels).astype(float)
    bins = np.linspace(0, 1, n_bins + 1)
    e = 0.0
    for lo, hi in zip(bins[:-1], bins[1:]):
        m = (conf > lo) & (conf <= hi)
        if m.any():
            e += m.mean() * abs(correct[m].mean() - conf[m].mean())
    return e

T = temperature_scale(cal_logits, cal_labels)         # e.g. T = 1.9
scaled = np.exp(cal_logits / T)
scaled /= scaled.sum(axis=1, keepdims=True)
print(f"T={T:.2f}  ECE {ece(softmax(test_logits), test_labels):.3f} "
      f"-> {ece(softmax(test_logits / T), test_labels):.3f}")
Fitting a single temperature by NLL on a held-out calibration split, then reporting ECE before and after. A typical overconfident sensor classifier lands at \(T\approx 1.5\) to \(2.5\); the second ECE print is usually several times smaller than the first. Note that test_labels never enter the fit.

Right Tool: let the library own the optimizer loop

The snippet above is deliberately from-scratch to show the mechanics; in practice, do not hand-roll the LBFGS loop, the stable softmax, and the binning. torchcal, netcal (its TemperatureScaling, BetaCalibration, and HistogramBinning classes), or torch-uncertainty reduce the whole fit-and-evaluate cycle to about 3 lines: construct the scaler, call fit(cal_logits, cal_labels), call transform(test_logits), and read ECE from the same package. That is a drop from roughly 40 lines to 3, and the library handles multi-class edge cases, class-wise ECE, and reliability-diagram plotting that you would otherwise reimplement and quietly get wrong.

Common Misconception: "It can't hurt accuracy, so any split will do"

Because temperature scaling provably leaves accuracy untouched, it is tempting to assume it cannot be misused either, so fitting \(T\) on whatever data are lying around, or worse on the test set itself, seems harmless. It is not: the calibration split plays the same role a validation set plays for early stopping, and reusing test data (or leaked windows, such as adjacent frames from the same session) to both fit \(T\) and report the final ECE makes the model look better calibrated than it is. The failure is silent because accuracy on that same set really is unaffected, so the number practitioners habitually check gives no warning. Fit \(T\) on a split held to the same leakage discipline as your test split (Chapter 5), and report ECE on a third, untouched split when the data budget allows.

Why time series breaks the textbook recipe

Every guarantee above rests on one assumption: the calibration split is a representative, exchangeable sample of what test time looks like. For a shuffled image benchmark that is nearly free. For a sensor stream it is where projects fail silently. Three specific traps:

Practical Example: the ICU monitor that everyone muted

A bedside model flags atrial fibrillation from a wearable ECG patch (Chapter 29). Validation accuracy is excellent, yet nurses report it "always says 99 percent," stop reading the confidence, and eventually silence it, the classic alarm-fatigue failure. A reliability diagram confirms severe overconfidence. The team fits one temperature on a calibration set drawn from different patients than both training and test, careful that no patient's beats straddle two splits. The fit returns \(T = 2.3\); post-scaling ECE falls from 0.19 to 0.03, accuracy is byte-for-byte identical, and a "72 percent" now genuinely means roughly seven in ten. The nurses' triage rule, "escalate above 90 percent, watch between 60 and 90," becomes meaningful, and the monitor is turned back on: nothing about the network changed, one honest number did.

Calibrating regression and forecast intervals

Most sensor deep learning is regression or forecasting, not classification, and "calibration" generalizes cleanly. A probabilistic forecaster emits quantiles or a predictive distribution; it is calibrated if its stated coverage matches reality. Concretely, for every level \(\alpha\), the fraction of test targets falling below the predicted \(\alpha\)-quantile should equal \(\alpha\), and a 90 percent prediction interval should contain the truth 90 percent of the time. The regression analogue of the reliability diagram plots nominal coverage against empirical coverage; the analogue of temperature scaling is a learned rescaling of the predicted standard deviation \(\hat{\sigma}\to s\,\hat{\sigma}\), or a monotone recalibration map fit on held-out residuals (Kuleshov et al., 2018). The same three traps apply, sharpened: interval calibration under autocorrelation and drift is exactly the pressure that motivates conformal prediction.

Research Frontier

Post-hoc calibration and conformal prediction are converging. Current practice for sensor streams pairs a lightweight recalibration (temperature or \(\sigma\)-scaling for sharpness) with a distribution-free coverage wrapper on top: temperature scaling gives well-shaped probabilities, split conformal (Section 18.4) gives the finite-sample guarantee a scalar cannot. On non-stationary streams, Adaptive Conformal Inference (Gibbs and Candes, 2021) and its successor conformal PID control (Angelopoulos, Candes, and Tibshirani, 2023) track miscalibration online, updating the effective quantile every step so coverage holds as the stream drifts, exactly where a frozen temperature fails. The same idea is reaching pretrained time-series models (Chapter 19): 2024-2025 conformal wrappers around zero-shot forecasters such as Chronos and TimesFM recalibrate per-deployment coverage without touching pretrained weights. Expect sharp probabilities and honest coverage to arrive as one library call rather than two.

Exercise

Take a multi-session human-activity dataset. (1) Train any classifier from Chapter 15, then plot its reliability diagram and compute ECE. (2) Fit a temperature two ways: on a random per-window calibration split, and on a subject-disjoint split. Report both temperatures and the test ECE each produces on held-out subjects, and explain the gap using the autocorrelation argument. (3) Simulate drift by scaling the test-session gains by 1.3, refit ECE, and describe what a static \(T\) can and cannot repair. Keep accuracy printed at every step to confirm it never moves.

Self-Check

  1. Why can temperature scaling never change a model's accuracy, and why is that property so convenient for a deployed pipeline?
  2. You fit \(T\) on 50,000 consecutive windows from one recording and see ECE 0.01. Give two reasons this number is likely too optimistic, and how you would re-measure it.
  3. ECE is not a proper scoring rule. What can go wrong if you select a model or a temperature by minimizing ECE alone, and what should you report alongside it?

What's Next

In Section 18.3, we stop repairing a single point estimate after the fact and instead ask the model to represent its own uncertainty from the start: deep ensembles, MC-dropout, Bayesian layers, and evidential deep learning. Temperature scaling fixed the confidence you already had; those methods try to produce a trustworthy distribution in the first place, and they capture the epistemic gap that a lone temperature, however honest, cannot see.