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

Reporting uncertainty to downstream systems

"A confidence score nobody downstream reads is just a number keeping the log file warm."

A Pragmatic AI Agent

The big picture

The previous six sections earned you a defensible uncertainty estimate: a calibrated probability, a conformal prediction set, an adaptive interval, an abstention flag. None of that matters until a downstream consumer can read it, trust its meaning, and change its behavior because of it. This section is about the last mile: how to package uncertainty into a contract that a Kalman filter, a fusion node, a motion planner, an alerting rule, or a human operator can act on without misreading what the number means. Get the schema and its semantics right and uncertainty becomes a load-bearing signal across your whole stack. Get it wrong and a well-calibrated model still causes a downstream failure, because the consumer treated a 90% interval as a hard bound, or dropped the covariance on the floor.

This section assumes you have a prediction with an attached uncertainty object from earlier in the chapter, and it builds on the probability and estimation vocabulary from Chapter 4. Where uncertainty feeds a recursive estimator we lean on the Kalman machinery of Chapter 9, and where it crosses a fusion boundary we anticipate Chapter 49.

What a downstream consumer actually needs

Different consumers need different shapes of uncertainty, and shipping the wrong shape is a silent bug. A recursive Bayesian filter needs a variance or covariance so it can weight your measurement against its prior; handing it a conformal prediction set is useless because the filter has no slot for a discrete set. A safety monitor needs a hard interval with a coverage guarantee so it can reason about worst case; handing it a Gaussian standard deviation forces it to assume normality your residuals may violate. A classification router needs per-class calibrated probabilities plus an abstain flag; a human dashboard needs a scalar it can threshold and a plain-language qualifier.

The practical rule: report uncertainty in the native algebra of the consumer, and report the raw sufficient statistic rather than a lossy summary whenever bandwidth allows. A predictive distribution can always be reduced to a variance or an interval downstream, but you cannot reconstruct a distribution from a single scalar. When you must compress, compress at the edge of the consumer, not at the source.

Key insight

Uncertainty is a typed contract, not a number. A field named confidence: 0.9 is ambiguous: is it a calibrated probability, a softmax max, a conformal miscoverage budget, or a heuristic? The consumer cannot tell, so it will guess, and it will guess wrong under drift. Name the type (variance, quantile pair, prediction set, coverage level) and the guarantee, or you have shipped a number that means whatever the reader assumes.

A serialization contract that survives the wire

A durable uncertainty report carries five things: the point estimate, an explicit uncertainty representation tagged by kind, the semantics of that representation (the nominal coverage or the distributional family), provenance (model version, calibration timestamp, whether adaptive conformal was active), and a validity window. The provenance fields are what let a downstream monitor in Chapter 69 later ask "was this interval produced by the recalibrated model or the stale one?" The code below defines such a contract and, critically, a converter that turns a conformal interval into the measurement-noise variance a Kalman update expects.

from dataclasses import dataclass, asdict
from enum import Enum
import json, math

class Kind(str, Enum):
    GAUSSIAN = "gaussian"          # carries mean + variance
    INTERVAL = "interval"          # carries [lo, hi] + coverage
    PREDICTION_SET = "pred_set"    # carries a set of labels + coverage

@dataclass
class UncertaintyReport:
    value: float                   # point estimate
    kind: Kind
    payload: dict                  # kind-specific fields
    coverage: float | None         # nominal guarantee, e.g. 0.9
    model_version: str
    calibrated_at: str             # ISO timestamp of last recalibration
    adaptive: bool                 # was ACI/PID active this step?

    def to_json(self) -> str:
        d = asdict(self); d["kind"] = self.kind.value
        return json.dumps(d)

def interval_to_measurement_variance(r: UncertaintyReport) -> float:
    """Feed a conformal interval into a Kalman R by matching coverage width
    to a Gaussian sigma. For 90% two-sided coverage, half-width = 1.645*sigma."""
    assert r.kind == Kind.INTERVAL and r.coverage is not None
    lo, hi = r.payload["lo"], r.payload["hi"]
    half_width = (hi - lo) / 2.0
    tail = (1.0 - r.coverage) / 2.0            # one-sided tail mass
    z = _inv_norm_cdf(1.0 - tail)              # z for that quantile
    sigma = half_width / z
    return sigma * sigma                       # variance -> Kalman R entry

def _inv_norm_cdf(p: float) -> float:          # Acklam approximation
    a=[-3.969683028665376e+01,2.209460984245205e+02,-2.759285104469687e+02,
       1.383577518672690e+02,-3.066479806614716e+01,2.506628277459239e+00]
    b=[-5.447609879822406e+01,1.615858368580409e+02,-1.556989798598866e+02,
       6.680131188771972e+01,-1.328068155288572e+01]
    c=[-7.784894002430293e-03,-3.223964580411365e-01,-2.400758277161838e+00,
       -2.549732539343734e+00,4.374664141464968e+00,2.938163982698783e+00]
    d=[7.784695709041462e-03,3.224671290700398e-01,2.445134137142996e+00,
       3.754408661907416e+00]
    pl=0.02425
    if p<pl:
        q=math.sqrt(-2*math.log(p))
        return (((((c[0]*q+c[1])*q+c[2])*q+c[3])*q+c[4])*q+c[5])/((((d[0]*q+d[1])*q+d[2])*q+d[3])*q+1)
    if p<=1-pl:
        q=p-0.5; rr=q*q
        return (((((a[0]*rr+a[1])*rr+a[2])*rr+a[3])*rr+a[4])*rr+a[5])*q/(((((b[0]*rr+b[1])*rr+b[2])*rr+b[3])*rr+b[4])*rr+1)
    q=math.sqrt(-2*math.log(1-p))
    return -(((((c[0]*q+c[1])*q+c[2])*q+c[3])*q+c[4])*q+c[5])/((((d[0]*q+d[1])*q+d[2])*q+d[3])*q+1)

rep = UncertaintyReport(
    value=21.4, kind=Kind.INTERVAL,
    payload={"lo": 19.8, "hi": 23.0}, coverage=0.90,
    model_version="tcn-v7", calibrated_at="2026-07-10T04:00:00Z", adaptive=True)
print(rep.to_json())
print("Kalman R entry:", round(interval_to_measurement_variance(rep), 4))
A tagged uncertainty report and the conformal-interval-to-Kalman-R bridge referenced above. The interval_to_measurement_variance converter matches the reported coverage half-width to a Gaussian sigma, so a distribution-free interval from Section 18.4 can drive the classical filter of Chapter 9. Note that this bridge reintroduces a normality assumption at the consumer, which is exactly the kind of lossy compression best done near the consumer, not at the source.

Right tool: schema validation

Hand-rolling the dataclass above plus type checks, enum coercion, range validation, and JSON round-tripping is roughly 60 to 80 lines once you cover the edge cases. A pydantic BaseModel with typed fields and a validator does the same in about 15 lines, and it rejects a malformed report (a coverage of 1.7, a missing lo) at the boundary instead of three hops downstream inside the filter. The library handles parsing, coercion, and error messages; you write only the field types and the one cross-field invariant.

Propagating uncertainty through fusion and control

A report that stops at the first consumer is half wasted. In a real stack the estimate flows onward: a per-sensor variance enters a fusion node, the fused covariance enters a planner, the planner widens its safety margin. Each hop must preserve the uncertainty's meaning under the transform applied to the value. When a downstream node applies a nonlinear function \(g\) to your estimate, a first-order propagation of variance is $$\sigma_y^2 \approx \left(\frac{\partial g}{\partial x}\right)^2 \sigma_x^2,$$ which is exactly the linearization the extended Kalman filter uses and which fails when \(g\) is strongly curved near the operating point. That failure is why some pipelines forward samples or full distributions rather than a scalar variance: a sample-based representation propagates through any \(g\) by construction, at the cost of bandwidth. The choice between forwarding a variance, an interval, or a set of samples is a bandwidth-versus-fidelity decision you should make deliberately per link, and it interacts with the streaming and latency budgets of temporal models running on the edge.

Practical example: an AMR that trusts its own doubt

An autonomous mobile robot in a warehouse fuses a wheel-odometry velocity estimate with a lidar-derived pose. The perception model emits, per frame, an UncertaintyReport whose interval widens whenever the aisle is featureless and adaptive conformal inflates the quantile. The fusion node converts that interval to a covariance with the bridge above and feeds it as the measurement noise \(R\). On a clear day the lidar variance is small, the filter leans on vision, and the robot drives at full speed. When a forklift kicks up dust and residuals grow, the reported interval doubles within a few frames, \(R\) grows, the filter down-weights vision and leans on odometry, and the planner reads the inflated fused covariance and drops speed until the pose tightens. Nothing crashed, no threshold was tripped manually: the robot slowed because the uncertainty contract carried its doubt intact from perception through fusion to control. The abstention and fallback logic of Section 18.6 sits underneath this as the floor, for the case where the interval blows past any usable width.

Monitoring the contract in production

A shipped uncertainty contract is a promise ("90% of my intervals cover the truth") that you must audit against reality, because coverage silently decays under the distribution shift covered in Chapter 66. The observability layer should log, per report, the point estimate, the interval width, the coverage claim, and later, when ground truth or a delayed label arrives, whether the interval actually covered. A rolling empirical coverage that drifts below the nominal level is the earliest, cheapest drift alarm you own, and it is far more actionable than raw accuracy because it points directly at the uncertainty head. Log the model_version and calibrated_at fields alongside so that a coverage regression can be attributed to a specific deploy. This closes the loop with the fleet operations discipline of Chapter 69: the same telemetry that proves your calibration in the lab must keep proving it in the field.

Exercise

Extend UncertaintyReport with a PREDICTION_SET path for a 5-class activity classifier. Write a converter that turns a prediction set into a scalar "route to human" flag: abstain when the set size exceeds 2 at 90% coverage. Then design the wire message a Kalman-style tracker would need if it consumed instead a Gaussian report, and explain in two sentences why the set-based classifier and the variance-based tracker cannot share one serialization field named confidence.

Self-check

  1. Why is reporting a single scalar confidence insufficient for a consumer that must reason about worst case, and what minimal extra field fixes it?
  2. The bridge from a conformal interval to a Kalman \(R\) reintroduces a normality assumption. Where in the pipeline is that assumption least harmful, and why?
  3. Your production coverage has drifted from 0.90 to 0.78 while accuracy is unchanged. Which reported provenance fields let you localize the cause, and what would you inspect first?

Research frontier

Time-series foundation models such as TimesFM, Chronos, and MOMENT increasingly emit probabilistic forecasts natively (quantile heads or sampled trajectories) rather than point predictions, which pushes the reporting problem upstream into the model interface itself. The open questions in 2025 and 2026 are how to serialize a foundation model's full predictive trajectory efficiently for edge consumers, how to attach a distribution-free coverage guarantee to a zero-shot forecast whose calibration set is a moving target, and how downstream agentic controllers should consume and reason over a probabilistic forecast rather than collapsing it to a mean. Conformalizing foundation-model outputs online, without a clean exchangeable calibration set, is the sharpest edge of this work.

Lab 18

compare split conformal vs ACI on a drifting sensor stream; verify coverage and interval width.

Bibliography

Conformal prediction and calibration foundations

Angelopoulos & Bates (2023). A Gentle Introduction to Conformal Prediction and Distribution-Free Uncertainty Quantification. Foundations and Trends in Machine Learning.

The reference tutorial for the coverage semantics you are serializing; its prediction-set and interval definitions are exactly the contracts a downstream consumer must be told the meaning of.

Guo, Pleiss, Sun & Weinberger (2017). On Calibration of Modern Neural Networks. ICML.

Shows why a raw softmax score is not a calibrated probability, the core reason a bare confidence field is an untyped and untrustworthy contract.

Predictive uncertainty in deep models

Lakshminarayanan, Pritzel & Blundell (2017). Simple and Scalable Predictive Uncertainty Estimation using Deep Ensembles. NeurIPS.

A canonical source of the predictive distribution you would forward to a fusion node; motivates why reporting a distribution beats a compressed scalar when bandwidth allows.

Fannjiang et al. (2022) and the Adaptive Conformal Inference line, Gibbs & Candes (2021). Adaptive Conformal Inference Under Distribution Shift. NeurIPS.

The adaptive-coverage method whose active/inactive state you record in the report provenance so downstream monitors can attribute coverage drift.

State estimation and fusion consumers

Welch & Bishop (2006). An Introduction to the Kalman Filter. UNC-Chapel Hill Technical Report.

Defines the measurement-noise matrix R that the interval-to-variance bridge in this section targets; the classic reference for the recursive consumer of your variance report.

Thrun, Burgard & Fox (2005). Probabilistic Robotics. MIT Press.

The reference for how robotic fusion and planning nodes consume covariance and propagate it through motion, the downstream stack the AMR example walks through.

Foundation-model probabilistic outputs

Das, Kong, Sen & Zhou (2024). A Decoder-Only Foundation Model for Time-Series Forecasting (TimesFM). ICML.

Represents the shift toward models that emit quantile or probabilistic forecasts natively, moving the uncertainty-reporting interface upstream into the model itself.

Goswami et al. (2024). MOMENT: A Family of Open Time-Series Foundation Models. ICML.

A general-purpose time-series foundation model whose outputs a downstream sensor stack must serialize and monitor exactly as this section prescribes.

What's Next

In Chapter 19, we leave bespoke per-task models behind and enter the pretrain-once, adapt-anywhere era of time-series foundation models. The reporting discipline you just built becomes even more load-bearing there: a zero-shot forecaster emits full predictive distributions with no clean calibration set, so the contract, the coverage audit, and the provenance fields are how you keep a general-purpose model honest on your specific sensors.