Gaussian process regression
A Gaussian process predicts a value and an honest error bar together, by treating "smooth curves through my data" as the model itself.
- 8 min read
- 3 reading levels
- Published
Read these first
On this page 5
One lesson, three depths. Pick the one that fits you today — you can switch any time.
Beginner — No maths. Plain English.
Gaussian process regression predicts a value and, in the same breath, tells you how sure it is.
Imagine asking a sensible friend to guess the afternoon temperature on your roof. She has readings from the morning and the evening, but nothing from midday. Near her readings, she answers firmly: "about 20 degrees, give or take one."
For 2 pm she still answers. But she widens her hands: "somewhere between 20 and 27, honestly."
Most regression models give you the guess without the hand-widening. A Gaussian process (GP) gives both, and the widening is computed, not vibes.
Why it exists
An ordinary regression line commits to one formula and reports one number per question — equally confidently everywhere, including regions it has never seen. For a doctor's dosage model or an engineer's safety margin, confident guesses in blind spots are dangerous.
A Gaussian process starts differently. Instead of one formula, imagine every smooth curve that passes near your data points. Where the curves bunch together, the answer is settled — narrow band. Where they fan out, in the gaps between readings, the model is honestly unsure — wide band.
The only ingredient you owe it is a definition of "smooth": how strongly nearby inputs should agree. That definition is a kernel, the same similarity-scoring idea from the kernel trick.
How it works
temp │ ╭─────╮ ← the band of curves that fit the readings
│ ●──╮│ │╭───●
│ ╰──╮ ││ band is NARROW near readings ●
│ ╰──╯│ band is WIDE in the midday gap
└────────────────────── hour
readings ● gap readings ●A real example you have seen
Weather apps that say "27°, likely between 25 and 30" are doing this kind of reasoning. GPs also sit inside hyperparameter tuning tools — the software that picks a model's dials for you. They model "score as a function of settings". The uncertainty band then decides which setting to try next. That is the trick behind Bayesian optimisation in tools like Optuna.
Remember this
- A GP predicts a band, not a point: value plus honest uncertainty.
- The band is narrow near data, wide in gaps — exactly where it should be.
- The kernel encodes your one assumption: how smooth the world is.
What to learn next
- The kernel trick — the similarity machinery GPs reuse as covariance.
- Probability — the language of priors and posteriors used here.
- Linear regression — the one-formula model the GP generalises.
Developer — Code and libraries.
Setup
pip install scikit-learnOutputs verified with scikit-learn 1.7.2 on CPU.
Seven readings and a midday blind spot
import numpy as np
from sklearn.gaussian_process import GaussianProcessRegressor
from sklearn.gaussian_process.kernels import RBF, WhiteKernel
# hour of day -> roof temperature in deg C; nothing between 8:00 and 20:00
X = np.array([[0.0], [2.0], [4.0], [6.0], [8.0], [20.0], [22.0]])
y = np.array([21.0, 20.0, 19.0, 19.5, 22.0, 26.0, 23.5])
# RBF: "hours close together have similar temperatures, fading over ~3 hours"
# WhiteKernel: "each reading carries sensor noise of about half a degree"
kernel = RBF(length_scale=3.0) + WhiteKernel(noise_level=0.5)
gp = GaussianProcessRegressor(kernel=kernel, optimizer=None, normalize_y=True).fit(X, y)
for hour in (5.0, 9.0, 14.0, 21.0):
mean, std = gp.predict([[hour]], return_std=True)
print(f"{int(hour):2d}:00 -> {mean[0]:5.1f} deg C, 95% range +/- {1.96 * std[0]:.1f}")
print("log marginal likelihood:", round(gp.log_marginal_likelihood_value_, 2))5:00 -> 19.8 deg C, 95% range +/- 3.8 9:00 -> 21.7 deg C, 95% range +/- 4.3 14:00 -> 22.1 deg C, 95% range +/- 5.5 21:00 -> 24.2 deg C, 95% range +/- 3.8 log marginal likelihood: -8.92
The walkthrough
Watch the error bars, not the means. At 5:00 and 21:00, snug between readings, the band is ±3.8. At 14:00 — six hours from the nearest reading — it swells to ±5.5. Nobody programmed "be less sure at midday". It fell out of the maths, which is the entire selling point.
The kernel is the model. RBF(length_scale=3.0) declares that temperatures 3 hours apart are strongly related and 10 hours apart barely at all. WhiteKernel(noise_level=0.5) declares the sensor itself wobbles. Change these declarations and predictions change — a GP is opinionated about smoothness because you told it what to believe.
Why optimizer=None? By default, scikit-learn tunes kernel settings by maximising the log marginal likelihood — a built-in score of how well the kernel explains the data. On seven points that automation can chase extremes (for instance, deciding the sensor is noiseless) and emit a ConvergenceWarning. Fixing the kernel keeps this demo honest and repeatable. With ~30+ points, drop optimizer=None and let it tune; compare fits via gp.log_marginal_likelihood_value_ — higher is better.
normalize_y=True centres and scales the targets internally. Without it, a GP quietly assumes your values hover around zero, and temperatures near 22 confuse the prior.
Common mistakes
Reading the band as a guarantee. ±5.5 is the model's uncertainty given its assumptions. A midday sun-spike more violent than the kernel's idea of smoothness will fall outside the band. Garbage assumptions, confident garbage bands.
No noise term on noisy data. Omit the WhiteKernel (or alpha) and the GP treats every reading as gospel, threading the curve through each wobble — overfitting with error bars attached. Real sensors always deserve a noise term.
Using exact GPs on big data. Training cost grows with the cube of the number of points. A thousand points is fine; fifty thousand freezes your machine. For large data, use sparse approximations (GPyTorch, GPflow) or a different model family.
Forgetting to scale inputs in multi-feature GPs. One length_scale shared across features with wildly different units makes "nearby" meaningless. Scale features, or give each its own length scale via RBF(length_scale=[1.0, 1.0]) — which also enables automatic relevance detection.
Try it yourself
Add a midday reading — 12.0 hours, 31.0 degrees — and rerun. Predict beforehand: what happens to the 14:00 band? Then change length_scale to 0.5 and to 10.0, and describe in one sentence what each setting believes about roof temperature.
What to learn next
- The kernel trick — the similarity machinery GPs reuse as covariance.
- Probability — the language of priors and posteriors used here.
- Linear regression — the one-formula model the GP generalises.
Researcher — Mathematics and papers.
Definition
A Gaussian process is a collection of random variables, any finite subset of which is jointly Gaussian. It is specified by a mean function $m(x)$ (usually 0 after centring) and covariance kernel $k(x, x')$:
$$ f \sim \mathcal{GP}(m, k) $$
For training inputs $X$ (with targets $y$, noise variance $\sigma_n^2$) and test point $x_*$, the posterior predictive is Gaussian with:
$$ \mu_* = k_^\top (K + \sigma_n^2 I)^{-1} y, \qquad \sigma_^2 = k(x_, x_) - k_^\top (K + \sigma_n^2 I)^{-1} k_ $$
Where:
- $K$ — the $n \times n$ Gram matrix, $K_{ij} = k(x_i, x_j)$.
- $k_$ — the vector of covariances between $x_$ and each training point.
- $\sigma_n^2$ — observation noise (the
WhiteKernelterm). - $\mu_, \sigma_^2$ — predictive mean and variance.
Note $\sigma_*^2$ does not depend on $y$ at all — uncertainty is a function of where you have data, not of the values measured there.
Hyperparameters and the marginal likelihood
Kernel parameters $\theta$ (length scale $\ell$, signal variance, noise) are set by maximising the log marginal likelihood:
$$ \log p(y \mid X, \theta) = -\tfrac{1}{2} y^\top (K_\theta + \sigma_n^2 I)^{-1} y - \tfrac{1}{2} \log |K_\theta + \sigma_n^2 I| - \tfrac{n}{2} \log 2\pi $$
The first term rewards fitting the data; the second penalises complex (large-determinant) covariances — an automatic Occam's razor, no validation set required. The surface is non-convex; restarts matter (n_restarts_optimizer).
Cost
Exact inference: $O(n^3)$ time for the Cholesky factorisation, $O(n^2)$ memory, then $O(n)$ per predictive mean and $O(n^2)$ per variance. Scaling routes: sparse inducing-point methods — FITC (Snelson and Ghahramani, 2006), variational SVGP (Titsias, 2009; Hensman et al., 2013) at $O(nm^2)$ for $m \ll n$ inducing points — and structure-exploiting solvers in GPyTorch (Gardner et al., 2018).
Connections and standing
- Textbook: Rasmussen and Williams (2006), Gaussian Processes for Machine Learning — free online, still the reference.
- Kernel ridge regression computes the same mean without the variance; the GP is the Bayesian reading of the same algebra.
- A single-hidden-layer network with infinite width converges to a GP (Neal, 1996); deep versions birthed the NNGP and neural-tangent-kernel literature (Lee et al., 2018; Jacot et al., 2018).
- Workhorse of Bayesian optimisation (Snoek et al., 2012): the GP posterior drives acquisition functions like expected improvement — the machinery behind modern hyperparameter tuners.
What to learn next
- The kernel trick — the similarity machinery GPs reuse as covariance.
- Probability — the language of priors and posteriors used here.
- Linear regression — the one-formula model the GP generalises.