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.

Natural Gradient Variational Inference from Scratch

This notebook derives and implements natural gradient variational inference for Gaussian approximate posteriors, showing how gaussx primitives make the natural parameterization clean and efficient.

What you’ll learn:

  1. Why Euclidean gradients are slow for distribution optimization
  2. How natural parameters and the Fisher metric fix this
  3. Using damped_natural_update for stable natural gradient steps
  4. Computing the ELBO via variational_elbo_gaussian and variational_elbo_mc
  5. The Bayesian Learning Rule with blr_full_update
  6. Building Gauss-Newton Hessian approximations with gauss_newton_precision

1. Background: Natural Gradient Descent

Standard gradient descent updates parameters along the steepest direction in Euclidean space. But when the parameters describe a probability distribution, Euclidean distance is the wrong metric. A small step in (μ,Σ)(\mu, \Sigma) can cause a huge change in KL-divergence, or vice versa.

The Fisher information metric measures distance between nearby distributions:

dF2(qθ,qθ+dθ)=dθ⊤F(θ) dθd_F^2(q_\theta, q_{\theta + d\theta}) = d\theta^\top F(\theta)\, d\theta

where F(θ)=Eqθ[∇log⁡q (∇log⁡q)⊤]F(\theta) = \mathbb{E}_{q_\theta} [\nabla \log q \,(\nabla \log q)^\top] is the Fisher information matrix.

The natural gradient is the steepest descent direction under this metric:

∇~θL=F(θ)−1∇θL\tilde{\nabla}_\theta \mathcal{L} = F(\theta)^{-1} \nabla_\theta \mathcal{L}

For the multivariate Gaussian q(f)=N(μ,Σ)q(f) = \mathcal{N}(\mu, \Sigma) in natural parameters η1=Σ−1μ\eta_1 = \Sigma^{-1}\mu and η2=−12Σ−1\eta_2 = -\tfrac{1}{2}\Sigma^{-1}, the Fisher metric becomes the identity --- the natural gradient is just the ordinary gradient in natural coordinates. This is why natural parameterization is so powerful.

/home/azureuser/localfiles/gaussx/.venv/lib/python3.13/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html
  from .autonotebook import tqdm as notebook_tqdm

2. A Non-Conjugate Model: GP Classification

We set up a 1D binary classification problem with a probit likelihood. The posterior p(f∣y)p(f \mid y) is non-Gaussian, so we must use approximate inference.

Model:

  • Prior: p(f)=N(0,K)p(f) = \mathcal{N}(0, K) with squared-exponential kernel
  • Likelihood: p(yi∣fi)=Φ(yifi)p(y_i \mid f_i) = \Phi(y_i f_i) (probit)

We approximate the posterior with q(f)=N(μ,Σ)q(f) = \mathcal{N}(\mu, \Sigma).

<Figure size 1000x400 with 1 Axes>

3. Euclidean Gradient Descent on the ELBO

First we try the naive approach: parameterize q(f)q(f) via (μ,L)(\mu, L) where Σ=LL⊤\Sigma = L L^\top, and optimize the ELBO with plain gradient descent.

The ELBO for non-conjugate likelihoods requires Monte Carlo:

L=Eq(f)[log⁡p(y∣f)]−KL(q∥p)\mathcal{L} = \mathbb{E}_{q(f)}[\log p(y \mid f)] - \mathrm{KL}(q \| p)
Euclidean (Adam) — final ELBO: -3738293339.769

4. Natural Gradient Descent

Now we parameterize q(f)q(f) via natural parameters:

η1=Σ−1μ,η2=−12Σ−1\eta_1 = \Sigma^{-1}\mu, \qquad \eta_2 = -\tfrac{1}{2}\Sigma^{-1}

The natural gradient update becomes a simple convex combination:

η(t+1)=(1−ρ) η(t)+ρ η∗\eta^{(t+1)} = (1 - \rho)\,\eta^{(t)} + \rho\,\eta^*

where η∗\eta^* are the target natural parameters from the Newton/Laplace step and ρ is the learning rate.

This is exactly what gaussx.damped_natural_update computes.

Natural gradient — final ELBO: nan

5. ELBO with Gaussian Likelihood

For a Gaussian observation model p(y∣f)=N(y;f,σ2I)p(y \mid f) = \mathcal{N}(y; f, \sigma^2 I), the expected log-likelihood has a closed form. gaussx.variational_elbo_gaussian computes this without Monte Carlo sampling:

L=−N2log⁡(2πσ2)−12σ2(∥y−μq∥2+∑iσq,i2)−KL(q∥p)\mathcal{L} = -\frac{N}{2}\log(2\pi\sigma^2) - \frac{1}{2\sigma^2} \left(\|y - \mu_q\|^2 + \sum_i \sigma^2_{q,i}\right) - \mathrm{KL}(q \| p)
Gaussian ELBO (closed form): -18.055

6. Monte Carlo ELBO for Non-Conjugate Likelihoods

For non-Gaussian likelihoods (Bernoulli, Poisson, etc.), we use gaussx.variational_elbo_mc. It takes a callable log_likelihood_fn and samples from the variational posterior:

LMC=1S∑s=1Slog⁡p(y∣f(s))−KL(q∥p)\mathcal{L}_{\mathrm{MC}} = \frac{1}{S}\sum_{s=1}^S \log p(y \mid f^{(s)}) - \mathrm{KL}(q \| p)
MC ELBO (S=64): nan
MC ELBO (S=512): nan

7. Bayesian Learning Rule Update

The Bayesian Learning Rule (Khan & Rue, 2023) unifies many approximate inference algorithms as natural gradient descent on the variational objective. For a Gaussian variational family, each step takes the form:

η1(t+1)=(1−ρ) η1(t)+ρ (∇μ−Hμ)\eta_1^{(t+1)} = (1-\rho)\,\eta_1^{(t)} + \rho\,(\nabla_\mu - H\mu)
η2(t+1)=(1−ρ) η2(t)+ρ 12H\eta_2^{(t+1)} = (1-\rho)\,\eta_2^{(t)} + \rho\,\tfrac{1}{2}H

where ∇μ\nabla_\mu is the gradient and HH is the Hessian of the log-likelihood at the current mean.

gaussx.blr_full_update implements this directly.

BLR — final ELBO: -8.036

8. Gauss-Newton Precision via gauss_newton_precision

When the Hessian is expensive or indefinite, the Gauss-Newton approximation provides a guaranteed PSD alternative:

ΛGN=J⊤J\Lambda_{\mathrm{GN}} = J^\top J

where JJ is the Jacobian of the residual mapping. This is always positive semi-definite, making it safe for precision updates.

gaussx.gauss_newton_precision returns a lineax operator; when Dobs<DlatentD_{\mathrm{obs}} < D_{\mathrm{latent}}, it returns a LowRankUpdate for efficient Woodbury solves.

Jacobian shape: (10, 30)
Operator type: LowRankUpdate
Operator shape: (30, 30)
Max error vs J^T J: 0.00e+00

Tall Jacobian (50, 10) -> MatrixLinearOperator
Min eigenvalue of GN precision: -2.65e-14 (>= 0)

9. Convergence Comparison and Posterior

Let’s compare the ELBO convergence of:

  • Euclidean (Adam on μ,L\mu, L) — 300 steps
  • Natural gradient (damped_natural_update) — 60 steps
  • BLR (blr_full_update) — 60 steps
<Figure size 1200x400 with 2 Axes>

10. Summary

FunctionPurpose
damped_natural_updateConvex combination in natural parameter space
newton_updateConvert Newton derivatives to natural site parameters
blr_full_updateBayesian Learning Rule step (full-rank)
blr_diag_updateBayesian Learning Rule step (diagonal)
gauss_newton_precisionPSD Hessian approximation J⊤JJ^\top J
riemannian_psd_correctionStabilize indefinite Hessians
variational_elbo_gaussianClosed-form ELBO for Gaussian likelihoods
variational_elbo_mcMonte Carlo ELBO for any likelihood

Key takeaways:

  • Natural gradient descent converges in ~60 steps where Euclidean gradient descent needs 300+ steps (and still hasn’t fully converged).
  • The natural parameterization (η1,η2)(\eta_1, \eta_2) makes the update a simple damped linear combination via damped_natural_update.
  • The Bayesian Learning Rule (blr_full_update) wraps the Newton target computation and damped update into a single call.
  • gauss_newton_precision gives a guaranteed PSD precision operator, using LowRankUpdate when the Jacobian is fat for efficient downstream Woodbury solves.
  • variational_elbo_gaussian avoids Monte Carlo noise for Gaussian likelihoods; variational_elbo_mc handles arbitrary likelihoods.