Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Expectation Propagation from Scratch

This notebook implements expectation propagation (EP) for Gaussian process classification using gaussx primitives. EP approximates non-Gaussian likelihoods with local Gaussian “sites” in natural parameter form, iterating a cavity-update-project loop until convergence.

What you’ll learn:

  1. The EP algorithm for GP classification with a probit likelihood
  2. How GaussianSites stores per-observation natural parameters
  3. Computing cavity distributions and tilted moments
  4. Damped site updates via cvi_update_sites
  5. How sites produce block-diagonal precision via sites_to_precision

1. Background: EP for GP Classification

In GP classification we place a GP prior over a latent function f(x)f(\mathbf{x}) and connect it to binary labels via a probit likelihood:

p(yi=1∣fi)=Φ(fi)p(y_i = 1 \mid f_i) = \Phi(f_i)

where Φ is the standard normal CDF. The posterior is:

p(f∣y)∝p(f)∏i=1Np(yi∣fi)p(\mathbf{f} \mid \mathbf{y}) \propto p(\mathbf{f}) \prod_{i=1}^N p(y_i \mid f_i)

This posterior is intractable because the probit factors break conjugacy. EP approximates each likelihood factor with a Gaussian site in natural parameter form:

t~i(fi)∝exp⁡ ⁣(η1,ifi+η2,ifi2)\tilde{t}_i(f_i) \propto \exp\!\bigl(\eta_{1,i} f_i + \eta_{2,i} f_i^2\bigr)

where η1,i\eta_{1,i} is the natural location and η2,i=−12λi\eta_{2,i} = -\tfrac{1}{2}\lambda_i encodes the site precision λi\lambda_i.

The EP loop repeats three steps for each site ii:

  1. Cavity: Remove site ii from the current posterior to get the cavity distribution q−i(fi)q_{-i}(f_i).
  2. Tilt: Form the tilted distribution p^i(fi)∝p(yi∣fi) q−i(fi)\hat{p}_i(f_i) \propto p(y_i \mid f_i)\, q_{-i}(f_i) and compute its mean and variance.
  3. Update: Project the tilted moments back to obtain new site natural parameters, then apply a damped update.

2. Setup and Data Generation

Generate 1D binary classification data. The true decision boundary comes from a latent function f∗(x)=sin⁡(2x)f^*(x) = \sin(2x); labels are drawn from yi∼Bernoulli(Φ(f∗(xi)))y_i \sim \text{Bernoulli}\bigl(\Phi(f^*(x_i))\bigr).

<Figure size 1000x400 with 1 Axes>

3. GP Prior

We use an RBF (squared exponential) kernel:

k(x,x′)=σf2exp⁡ ⁣(−(x−x′)22ℓ2)k(x, x') = \sigma_f^2 \exp\!\Bigl(-\frac{(x - x')^2}{2\ell^2}\Bigr)

4. Initialize Gaussian Sites

GaussianSites(nat1, nat2) stores per-observation natural parameters. For scalar EP (d=1d = 1), nat1 has shape (N, 1) and nat2 has shape (N, 1, 1). We initialize both to zero, corresponding to an uninformative (improper flat) site.

Sites nat1 shape: (30, 1)
Sites nat2 shape: (30, 1, 1)

5. EP Helper Functions

Tilted moments via Gauss-Hermite quadrature

The tilted distribution for site ii is:

p^i(fi)∝p(yi∣fi) q−i(fi)\hat{p}_i(f_i) \propto p(y_i \mid f_i)\, q_{-i}(f_i)

where q−i(fi)=N(fi∣μ−i,σ−i2)q_{-i}(f_i) = \mathcal{N}(f_i \mid \mu_{-i}, \sigma_{-i}^2) is the cavity marginal. We need the mean and variance of p^i\hat{p}_i.

Using the change of variable fi=μ−i+σ−izf_i = \mu_{-i} + \sigma_{-i} z with z∼N(0,1)z \sim \mathcal{N}(0, 1), we can compute the moments via Gauss-Hermite quadrature using gaussx.gauss_hermite_points.

Gauss-Hermite: 30 quadrature points

6. The EP Loop

Each EP iteration:

  1. Posterior from prior + sites. The site precision is Λsites,i=−2 η2,i\Lambda_{\text{sites},i} = -2\,\eta_{2,i} (diagonal), and the posterior precision is Λ=K−1+Λsites\Lambda = K^{-1} + \Lambda_{\text{sites}}.

  2. Cavity. For each ii, remove site ii: λ−i=λpost,i−λi\lambda_{-i} = \lambda_{\text{post},i} - \lambda_i, η1,−i=η1,post,i−η1,i\eta_{1,-i} = \eta_{1,\text{post},i} - \eta_{1,i}.

  3. Tilted moments. Compute mean and variance of p(yi∣fi)q−i(fi)p(y_i|f_i) q_{-i}(f_i) by quadrature.

  4. New site params. Convert tilted moments to natural parameters using newton_update: the site precision is λinew=1/vtilt−1/v−i\lambda_i^{\text{new}} = 1/v_{\text{tilt}} - 1/v_{-i} and η1,inew=mtilt/vtilt−m−i/v−i\eta_{1,i}^{\text{new}} = m_{\text{tilt}}/v_{\text{tilt}} - m_{-i}/v_{-i}.

  5. Damped update via cvi_update_sites.

Iteration  1: log Z sum = 6.7737
Iteration  2: log Z sum = 8.3711
Iteration  3: log Z sum = 8.5472
Iteration  4: log Z sum = 8.5733
Iteration  5: log Z sum = 8.5774
Iteration  6: log Z sum = 8.5782
Iteration  7: log Z sum = 8.5787
Iteration  8: log Z sum = 8.5792
Iteration  9: log Z sum = 8.5796
Iteration 10: log Z sum = 8.5800
Iteration 11: log Z sum = 8.5802
Iteration 12: log Z sum = 8.5804
Iteration 13: log Z sum = 8.5805
Iteration 14: log Z sum = 8.5805
Iteration 15: log Z sum = 8.5806

7. Convergence

The sum of log-normalizer terms ∑ilog⁡Zi\sum_i \log Z_i should stabilize as EP converges.

<Figure size 1000x400 with 1 Axes>

8. Posterior Predictions

Compute the final EP posterior and plot classification probabilities Φ(μi/1+σi2)\Phi(\mu_i / \sqrt{1 + \sigma_i^2}) with uncertainty bands.

<Figure size 800x600 with 2 Axes>

9. Precision Structure via sites_to_precision

The site natural parameters form a diagonal precision contribution. sites_to_precision wraps these into a BlockTriDiag operator with zero sub-diagonals --- a block-diagonal structure. In a state-space GP model, combining this with the prior’s block-tridiagonal precision yields a full block-tridiagonal posterior precision, enabling O(Nd3)O(Nd^3) inference.

Type: BlockTriDiag
Diagonal blocks shape: (30, 1, 1)
Sub-diagonal blocks shape: (29, 1, 1)
Sub-diagonal norm: 0.00e+00
<Figure size 1000x400 with 1 Axes>

Sites near the decision boundary have higher precision --- EP concentrates its approximation effort where the likelihood is most informative.

10. Using cavity_distribution and newton_update

For completeness, let us verify the gaussx API functions on the final posterior. cavity_distribution removes a site from the full posterior, and newton_update converts derivatives into natural parameters.

Cavity mean at i=15: 0.3418
Cavity var  at i=15: 0.2653
Newton nat1: -0.7967
Newton nat2 (= -hessian): 0.6590

Summary

This notebook demonstrated EP for GP classification from scratch using gaussx building blocks:

gaussx APIRole in EP
GaussianSites(nat1, nat2)Store per-site natural parameters
cvi_update_sites(sites, ...)Damped natural parameter update
sites_to_precision(sites)Convert sites to BlockTriDiag precision
cavity_distribution(...)Remove a site from the posterior
newton_update(mean, jac, hess)Derivatives to natural parameters
gauss_hermite_points(order, dim)Quadrature for tilted moments
BlockTriDiagStructured block-tridiagonal operator

Key takeaways:

  • EP iterates cavity-tilt-project to approximate non-Gaussian likelihoods with local Gaussian sites.
  • The natural parameterization (η1=Λμ\eta_1 = \Lambda \mu, η2=−12Λ\eta_2 = -\tfrac{1}{2}\Lambda) makes site addition and removal simple additions in parameter space.
  • sites_to_precision produces a BlockTriDiag that can be combined with state-space GP priors for scalable temporal inference.
  • Damping (rho < 1) in cvi_update_sites is essential for EP stability.