AI-320 · Lesson 9 of 12 · 31 min

Variational inference

Monte Carlo methods approximate a posterior by samples, and given enough time they are exact. But "enough time" can be prohibitive: MCMC on a model with millions of parameters or millions of data points may need days, every iteration touches the whole data set, and convergence is hard to verify. Variational inference (VI) takes a different route. Instead of sampling from the posterior, it chooses a family of simple distributions and finds the member closest to the posterior by optimization. Inference becomes an optimization problem, which can use stochastic gradients, mini-batches and GPUs, the same tools that train neural networks. The price is bias: the answer is only as good as the chosen family. This lesson covers the KL divergence and the evidence lower bound, mean-field coordinate ascent, the properties of the approximation, stochastic and black-box VI with the reparameterization trick, variational autoencoders, and how to choose between VI and MCMC.

Inference as optimization

Given a posterior \(p(\mathbf z \mid \mathbf x)\) over latent variables or parameters \(\mathbf z\), VI chooses a family \(\mathcal Q\) of tractable distributions \(q(\mathbf z)\) and seeks

\[q^*(\mathbf z) = \arg\min_{q \in \mathcal Q} \mathrm{KL}\big(q(\mathbf z)\,\|\,p(\mathbf z \mid \mathbf x)\big), \qquad \mathrm{KL}(q\,\|\,p) = \mathbb{E}_q\!\left[\log\frac{q(\mathbf z)}{p(\mathbf z \mid \mathbf x)}\right]\]

The Kullback-Leibler divergence is non-negative and zero only when the two distributions are equal. It is not symmetric, and the direction matters a great deal, as shown below. The obvious obstacle is that the KL divergence involves the posterior, which is what we cannot compute. The way around it is the identity from Lesson 5:

\[\log p(\mathbf x) = \underbrace{\mathbb{E}_q[\log p(\mathbf x, \mathbf z)] - \mathbb{E}_q[\log q(\mathbf z)]}_{\mathrm{ELBO}(q)} + \mathrm{KL}\big(q(\mathbf z)\,\|\,p(\mathbf z \mid \mathbf x)\big)\]

The left side does not depend on \(q\), so minimizing the KL divergence is the same as maximizing the evidence lower bound (ELBO), which involves only the joint distribution \(p(\mathbf x, \mathbf z)\), the model we wrote down. The ELBO has an intuitive split: the first term rewards placing \(q\) where the joint probability is high (fitting the data and prior), and the second, the entropy of \(q\), rewards spreading out. As its name says, the ELBO is also a lower bound on the log evidence, so it can serve as an approximate criterion for model comparison.

Bar diagram showing the log evidence log p(x) as a fixed total height split into two stacked parts: the ELBO at the bottom and the KL divergence between q and the posterior on top. Three versions show q improving from left to right: the ELBO grows and the KL gap shrinks while the total stays fixed.
The log evidence is fixed; raising the ELBO shrinks the KL gap to the true posterior.

The direction of the KL divergence

VI minimizes the reverse divergence \(\mathrm{KL}(q\,\|\,p)\), an expectation under \(q\). Where \(q\) puts mass but \(p\) is nearly zero, \(\log(q/p)\) is enormous, so \(q\) is strongly penalized for covering regions the posterior rules out, but barely penalized for missing regions where the posterior has mass. Reverse KL is therefore mode-seeking (or zero-forcing): when the family cannot represent the whole posterior, the optimal \(q\) locks onto one mode and underestimates the spread. The forward divergence \(\mathrm{KL}(p\,\|\,q)\), used by expectation propagation and by maximum likelihood fitting, is mass-covering: it matches moments and spreads \(q\) over all modes, even if that puts mass in the valley between them.

Fitting a single Gaussian to a bimodal target by reverse and forward KL
import numpy as np
from scipy.stats import norm
from scipy.optimize import minimize

x = np.linspace(-8, 8, 4001); dx = x[1] - x[0]
p = 0.5 * norm.pdf(x, -2, 0.6) + 0.5 * norm.pdf(x, 2, 0.6)

def kl(a, b):                                  # KL(a || b) on the grid
    mask = a > 1e-300
    return np.sum(a[mask] * (np.log(a[mask]) - np.log(b[mask] + 1e-300))) * dx

def reverse(params):                           # KL(q || p): mode-seeking
    q = norm.pdf(x, params[0], np.exp(params[1]))
    return kl(q, p)

def forward(params):                           # KL(p || q): mass-covering
    q = norm.pdf(x, params[0], np.exp(params[1]))
    return kl(p, q)

for name, obj in [("reverse KL(q||p)", reverse), ("forward KL(p||q)", forward)]:
    for start in [0.5, -1.0]:
        r = minimize(obj, [start, 0.0], method="Nelder-Mead")
        print(f"{name}, start mu={start:+.1f}:  mu={r.x[0]:+.3f}  sd={np.exp(r.x[1]):.3f}  KL={r.fun:.3f}")
Output
reverse KL(q||p), start mu=+0.5:  mu=+1.997  sd=0.605  KL=0.692
reverse KL(q||p), start mu=-1.0:  mu=-1.997  sd=0.605  KL=0.692
forward KL(p||q), start mu=+0.5:  mu=-0.000  sd=2.088  KL=0.555
forward KL(p||q), start mu=-1.0:  mu=-0.000  sd=2.088  KL=0.555

Reverse KL fits a narrow Gaussian to whichever mode is nearer the starting point, ignoring the other entirely; forward KL fits a broad Gaussian centered on the valley, matching the target's mean and variance. Neither is right: the family is simply too poor. In practice, the mode-seeking behavior means that VI answers are sharper than the true posterior, and that VI on a multimodal posterior reports one mode and is confident about it.

Plot of a bimodal target density with peaks at minus 2 and plus 2. Overlaid are two Gaussian approximations: a narrow one centered on the right peak, labeled reverse KL, mode-seeking; and a wide one centered at zero covering both peaks and the valley between, labeled forward KL, mass-covering.
Reverse KL (used by VI) seeks a mode; forward KL covers all the mass.

Mean-field variational inference

The most common family is the mean-field family, which assumes the latent variables are independent under \(q\): \(q(\mathbf z) = \prod_j q_j(z_j)\). The posterior itself is not assumed to factorize; the approximation simply cannot represent dependence between groups. For this family, the optimal factor \(q_j\) with all the others held fixed has a remarkably general form:

\[\log q_j^*(z_j) = \mathbb{E}_{q_{-j}}\big[\log p(\mathbf x, \mathbf z)\big] + \text{const}\]

That is, take the log joint, average over all the other factors, and exponentiate. In conjugate-exponential models, which include most of the models of Lessons 1, 3 and 5, this expectation has the same form as the prior, so each factor stays in a standard family and the update reduces to recomputing a few parameters. Cycling through the factors is coordinate ascent variational inference (CAVI); each update increases the ELBO, so CAVI converges, like EM, to a local optimum. EM is in fact the special case in which \(q\) over the latent variables is unrestricted (the exact posterior) and the parameters get point estimates; variational Bayesian EM extends it by also giving the parameters factorized distributions.

A clean example is estimating the mean \(\mu\) and precision \(\tau\) of normal data with the conjugate Normal-Gamma prior. The exact posterior is available, which allows the mean-field answer \(q(\mu)q(\tau)\) to be checked.

Mean-field CAVI for the mean and precision of normal data, compared with the exact posterior
import numpy as np
from scipy import stats

rng = np.random.default_rng(4)
x = rng.normal(5.0, 2.0, size=15); N = len(x); xbar = x.mean()
mu0, lam0, a0, b0 = 0.0, 0.1, 1.0, 1.0           # Normal-Gamma prior

# exact posterior (conjugate): tau ~ Gamma(aN, bN), mu | tau ~ N(muN, 1/((lam0+N) tau))
muN = (lam0 * mu0 + N * xbar) / (lam0 + N)
aN = a0 + N / 2
bN = b0 + 0.5 * ((x - xbar) ** 2).sum() + lam0 * N * (xbar - mu0) ** 2 / (2 * (lam0 + N))
mu_marg = stats.t(df=2 * aN, loc=muN, scale=np.sqrt(bN / (aN * (lam0 + N))))

# mean-field CAVI: q(mu) q(tau)
E_tau = 1.0
for it in range(20):
    lamN = (lam0 + N) * E_tau                    # q(mu) = N(muN, 1/lamN)
    E_sq = ((x - muN) ** 2).sum() + N / lamN + lam0 * ((muN - mu0) ** 2 + 1 / lamN)
    a_q = a0 + (N + 1) / 2                       # q(tau) = Gamma(a_q, b_q)
    b_q = b0 + 0.5 * E_sq
    E_tau = a_q / b_q

print(f"posterior mean of mu:   exact {mu_marg.mean():.3f}   CAVI {muN:.3f}")
print(f"posterior sd of mu:     exact {mu_marg.std():.3f}   CAVI {1 / np.sqrt(lamN):.3f}")
print(f"posterior mean of tau:  exact {aN / bN:.4f}  CAVI {E_tau:.4f}")
print(f"implied noise sd:       sample {x.std(ddof=1):.3f}   1/sqrt(E[tau]) {1 / np.sqrt(E_tau):.3f}")
Output
posterior mean of mu:   exact 4.855   CAVI 4.855
posterior sd of mu:     exact 0.525   CAVI 0.493
posterior mean of tau:  exact 0.2728  CAVI 0.2728
implied noise sd:       sample 2.035   1/sqrt(E[tau]) 1.915

CAVI converges in a few iterations. It recovers the posterior means exactly in this model, but its standard deviation for \(\mu\) is about 6% too small, because the true posterior couples \(\mu\) and \(\tau\) (when the noise precision is lower, the mean is less certain), and the factorized \(q\) cannot represent that coupling. This underestimation of uncertainty is the characteristic error of mean-field VI, and it is larger when posterior correlations are stronger.

Worked problem

Mean-field approximation of a correlated Gaussian

The posterior over two parameters is Gaussian with variances 1 and correlation \(\rho\). The mean-field optimum for a Gaussian posterior has each factor's variance equal to the inverse of the corresponding diagonal element of the posterior precision matrix. Find the mean-field standard deviations for \(\rho = 0.5\) and \(\rho = 0.9\).

Show solution
  1. Covariance \(\Sigma = \begin{pmatrix} 1 & \rho \\ \rho & 1 \end{pmatrix}\); precision \(\Sigma^{-1} = \frac{1}{1 - \rho^2}\begin{pmatrix} 1 & -\rho \\ -\rho & 1 \end{pmatrix}\).
  2. Mean-field variance: \(1/(\Sigma^{-1})_{11} = 1 - \rho^2\).
  3. \(\rho = 0.5\): variance 0.75, standard deviation 0.87. \(\rho = 0.9\): variance 0.19, standard deviation 0.44.

Answer: The true marginal standard deviations are 1 in both cases, but mean-field VI reports 0.87 and 0.44. With strong correlation, the approximation is overconfident by more than a factor of two: it matches the width of the posterior across the narrow direction, not along the ridge.

Stochastic and black-box VI

CAVI requires deriving update equations by hand and passes over the whole data set each iteration. Two developments made VI practical for large and non-conjugate models.

Stochastic variational inference

When the log joint is a sum over \(N\) data points, the ELBO's gradient is also a sum, and it can be estimated without bias from a mini-batch, scaled by \(N\) divided by the batch size. Stochastic optimization with decreasing step sizes then converges, exactly as in training neural networks (AI-310). Stochastic VI made topic models and other Bayesian models feasible on millions of documents.

The reparameterization trick

For non-conjugate models, the expectations in the ELBO have no closed form, so they are estimated by Monte Carlo. The difficulty is differentiating an expectation with respect to the parameters of the distribution being sampled. The reparameterization trick writes the random variable as a deterministic, differentiable function of the variational parameters and a fixed noise source. For a Gaussian, \(\mathbf z = \mathbf m + \mathbf s \odot \boldsymbol\epsilon\) with \(\boldsymbol\epsilon \sim \mathcal N(0, I)\), so

\[\nabla_{\mathbf m, \mathbf s}\,\mathbb{E}_{q}[f(\mathbf z)] = \mathbb{E}_{\boldsymbol\epsilon}\big[\nabla_{\mathbf m, \mathbf s}\, f(\mathbf m + \mathbf s \odot \boldsymbol\epsilon)\big]\]

which is estimated by averaging gradients over a few noise samples. Combined with automatic differentiation, this gives black-box VI: for any model with a differentiable log joint, the ELBO and its gradient can be computed automatically, and the user only writes the model. The gradient estimates have low variance compared with the alternative score-function (REINFORCE) estimator, which works for discrete variables but is much noisier. Probabilistic programming systems such as Stan (ADVI), PyMC and Pyro offer this automatically.

Black-box VI with reparameterization gradients for Bayesian logistic regression, checked against MCMC
import numpy as np
rng = np.random.default_rng(0)
n = 60
X = np.column_stack([np.ones(n), rng.normal(size=n), rng.normal(size=n)])
w_true = np.array([-0.5, 1.5, -1.0])
y = (rng.uniform(size=n) < 1 / (1 + np.exp(-X @ w_true))).astype(float)

def grad_logp(W):                         # W: S x 3 samples; prior N(0, 5^2 I)
    p = 1 / (1 + np.exp(-W @ X.T))        # S x n
    return (y - p) @ X - W / 25.0

def logp(w):
    z = X @ w
    return np.sum(y * z - np.logaddexp(0, z)) - w @ w / 50.0

# variational inference: q(w) = N(m, diag(s^2)), reparameterization gradients, Adam
m = np.zeros(3); rho = np.full(3, -1.0)        # s = exp(rho)
state = [np.zeros(3) for _ in range(4)]
lr, b1, b2 = 0.05, 0.9, 0.999
m_avg = np.zeros(3); rho_avg = np.zeros(3)
for t in range(1, 6001):
    lr = 0.05 if t <= 3000 else 0.01
    eps = rng.normal(size=(16, 3))
    s = np.exp(rho)
    W = m + s * eps
    g = grad_logp(W)
    gm = g.mean(axis=0)
    grho = (g * eps * s).mean(axis=0) + 1.0        # +1 from the entropy term
    for i, gr in enumerate([gm, grho]):
        state[2*i] = b1 * state[2*i] + (1 - b1) * gr
        state[2*i+1] = b2 * state[2*i+1] + (1 - b2) * gr ** 2
        step = lr * (state[2*i] / (1 - b1 ** t)) / (np.sqrt(state[2*i+1] / (1 - b2 ** t)) + 1e-8)
        if i == 0: m = m + step
        else: rho = rho + step
    if t > 4000:
        m_avg += m / 2000; rho_avg += rho / 2000
m, s = m_avg, np.exp(rho_avg)

# reference: long random-walk Metropolis run
w = m.copy(); lp = logp(w); samples = []
for it in range(60_000):
    prop = w + 0.35 * rng.normal(size=3)
    lpp = logp(prop)
    if np.log(rng.uniform()) < lpp - lp:
        w, lp = prop, lpp
    if it >= 5000: samples.append(w)
samples = np.array(samples)
C = np.corrcoef(samples.T)
print("            VI mean   MCMC mean    VI sd   MCMC sd")
for j, name in enumerate(["intercept", "w1", "w2"]):
    print(f"{name:10s} {m[j]:8.3f}  {samples[:, j].mean():9.3f}  {s[j]:7.3f}  {samples[:, j].std():8.3f}")
print(f"posterior correlation of w1 and w2 (MCMC): {C[1, 2]:.2f}; mean-field VI assumes 0")
Output
            VI mean   MCMC mean    VI sd   MCMC sd
intercept    -0.802     -0.815    0.338     0.370
w1            1.725      1.782    0.459     0.562
w2           -1.142     -1.179    0.364     0.410
posterior correlation of w1 and w2 (MCMC): -0.42; mean-field VI assumes 0

The variational means agree closely with the MCMC reference, but every variational standard deviation is smaller, by up to about 20% for \(w_1\), because the posterior correlations between coefficients are ignored by the mean-field family. A full-covariance Gaussian \(q\) (parameterized by a Cholesky factor) would capture the correlations at the cost of \(O(d^2)\) parameters; normalizing flows go further, transforming a simple distribution through invertible neural networks to represent skewed and multimodal posteriors.

Amortized inference and variational autoencoders

In latent variable models with one latent \(\mathbf z_n\) per data point, classical VI optimizes separate variational parameters for every data point. Amortized inference instead trains an inference network (an encoder) that maps any \(\mathbf x_n\) to the parameters of \(q(\mathbf z_n \mid \mathbf x_n)\), sharing the cost of inference across data points and making inference on new points a single forward pass.

The variational autoencoder (VAE) combines an amortized encoder \(q_\phi(\mathbf z \mid \mathbf x)\) with a neural decoder \(p_\theta(\mathbf x \mid \mathbf z)\) and a simple prior \(p(\mathbf z) = \mathcal N(0, I)\). Both networks are trained together by maximizing the ELBO with reparameterization gradients:

\[\mathrm{ELBO}(\theta, \phi) = \mathbb{E}_{q_\phi(\mathbf z \mid \mathbf x)}\big[\log p_\theta(\mathbf x \mid \mathbf z)\big] - \mathrm{KL}\big(q_\phi(\mathbf z \mid \mathbf x)\,\|\,p(\mathbf z)\big)\]

The first term is a reconstruction term, rewarding latent codes from which the decoder can rebuild the input; the second is a regularizer pulling each code distribution toward the prior, which keeps the latent space smooth enough to sample from. New data are generated by drawing \(\mathbf z \sim \mathcal N(0, I)\) and decoding. VAEs are used for representation learning, anomaly detection (inputs with low ELBO are unusual) and, as the compression stage of latent diffusion image generators, inside many modern generative systems.

Diagram of a variational autoencoder. An input x enters an encoder network that outputs a mean vector and a standard deviation vector. A sampling node combines them with noise epsilon drawn from a standard normal, z equals mu plus sigma times epsilon, labeled reparameterization. The latent z enters a decoder network that outputs a reconstruction of x. Two loss terms are shown: reconstruction log-likelihood between x and the output, and the KL divergence between the encoder distribution and the standard normal prior.
A variational autoencoder: amortized inference with an encoder, reparameterized sampling and a decoder.

VI or MCMC?

Neither method dominates; the choice depends on the problem and the purpose:

Comparing MCMC and variational inference.
ConsiderationMCMCVariational inference
AccuracyAsymptotically exactBiased by the family; typically underestimates variance
SpeedSlow; full data each stepFast; mini-batches, GPUs
ScaleThousands of parameters, moderate dataMillions of parameters and data points
DiagnosticsR-hat, ESS, divergencesELBO convergence; accuracy hard to assess
MultimodalityCan mix poorly between modesLocks onto one mode
Typical useScientific inference, small to medium models, final answersLarge models, amortized inference, VAEs, quick iteration

A common workflow uses VI to explore models and initialize, then MCMC for final inference on the chosen model when accurate uncertainty matters, such as in scientific, medical or engineering decisions. For deep learning scale models, approximate methods are the only option: besides VI, the Laplace approximation (a Gaussian at the MAP estimate with covariance from the Hessian), Monte Carlo dropout and deep ensembles are widely used to estimate the uncertainty of neural network predictions. Whatever the method, posterior predictive checks, which simulate data from the fitted model and compare them with the real data, remain the best test of whether the model and its inference make sense.

Lesson 10 shows how to check whether an approximate posterior can be trusted, using simulation-based calibration, and Lesson 12 compares variational methods with ensembles and other ways of capturing uncertainty in deep networks.

Common mistakes

  • MistakeTrusting mean-field VI uncertainties as calibrated.

    InsteadMean-field VI typically underestimates variance, especially with correlated parameters; check against MCMC or use richer families.

  • MistakeInterpreting a VI fit to a multimodal posterior as complete.

    InsteadReverse KL locks onto one mode; run from several initializations or use methods that cover multiple modes.

  • MistakeComparing models by ELBO as if it were the exact evidence.

    InsteadThe ELBO is a lower bound whose gap differs between models; use it cautiously.

  • MistakeUsing score-function gradients when reparameterization is possible.

    InsteadReparameterization gradients have far lower variance for continuous latent variables.

  • MistakeReporting the last iterate of noisy stochastic VI.

    InsteadDecrease the step size and average iterates, and monitor a smoothed ELBO.

Summary and key terms

  • VI approximates the posterior by the member of a tractable family closest in KL divergence.
  • \(\log p(\mathbf x) = \mathrm{ELBO}(q) + \mathrm{KL}(q\,\|\,p(\mathbf z \mid \mathbf x))\): maximizing the ELBO minimizes the KL without knowing the posterior.
  • Reverse KL is mode-seeking and underestimates spread; forward KL is mass-covering.
  • Mean-field VI factorizes q; the optimal factor is \(\exp \mathbb E_{-j}[\log p(\mathbf x, \mathbf z)]\), giving CAVI updates in conjugate models.
  • Mean-field approximations ignore posterior correlations and are overconfident.
  • Stochastic VI uses mini-batch gradients; the reparameterization trick gives low-variance gradients for black-box VI.
  • Amortized inference trains an encoder; VAEs maximize a reconstruction term minus a KL regularizer.
  • MCMC is asymptotically exact but slow; VI is fast and scalable but biased. Choose by purpose and scale.
Variational inference
Approximating a posterior by optimizing over a family of tractable distributions.
Kullback-Leibler divergence
An asymmetric measure of how one distribution differs from another.
ELBO
Evidence lower bound: the objective maximized by variational inference.
Mean-field family
Variational distributions that factorize over variables or groups.
CAVI
Coordinate ascent variational inference, updating one factor at a time.
Mode-seeking
The tendency of reverse KL to fit one mode of a multimodal target.
Mass-covering
The tendency of forward KL to spread over all of the target's mass.
Stochastic variational inference
VI using mini-batch estimates of the ELBO gradient.
Reparameterization trick
Writing a sample as a differentiable function of parameters and fixed noise.
Score-function estimator
A general but high-variance gradient estimator for expectations.
Amortized inference
Training a network to output variational parameters for any input.
Variational autoencoder
A generative model trained with an amortized encoder and the ELBO.
Normalizing flow
An invertible neural transformation used to build flexible distributions.
Laplace approximation
A Gaussian approximation at the posterior mode using the Hessian.

Practice problems

Work each problem on paper first, then reveal the answer to check.

  1. Why can VI optimize the ELBO even though the KL divergence to the posterior is unknown?

    Show answer

    log p(x) = ELBO + KL and log p(x) is constant in q, so maximizing the ELBO minimizes the KL; the ELBO needs only the joint.

  2. Write the ELBO as an expected log joint plus an entropy.

    Show answer

    ELBO = E_q[log p(x, z)] + H[q], where H[q] = −E_q[log q(z)].

  3. A target has two well-separated modes. Describe the Gaussian fitted by reverse KL and by forward KL.

    Show answer

    Reverse KL: narrow, on one mode. Forward KL: broad, centered between the modes, matching overall mean and variance.

  4. What is the optimal mean-field factor in general?

    Show answer

    log q_j*(z_j) = E over the other factors of log p(x, z), plus a constant.

  5. For a Gaussian posterior with unit variances and correlation 0.8, what standard deviation does mean-field VI give?

    Show answer

    √(1 − 0.64) = 0.6.

  6. A data set has N = 1,000,000 points and mini-batches of 100. How is the mini-batch log-likelihood term scaled in the ELBO gradient?

    Show answer

    Multiplied by N/B = 10,000.

  7. Write the reparameterization of a Gaussian with mean m and standard deviation s.

    Show answer

    z = m + s ε with ε ~ N(0, 1).

  8. Why are reparameterization gradients preferred to score-function gradients for continuous latents?

    Show answer

    They have much lower variance because they use the gradient of the model through the sample.

  9. What are the two terms of a VAE objective and what does each do?

    Show answer

    Reconstruction log-likelihood (rebuild the input from z) and KL from q(z|x) to the prior (regularize the latent space).

  10. Give one situation favoring MCMC and one favoring VI.

    Show answer

    MCMC: a medium-sized scientific model needing accurate uncertainty. VI: a large model or data set, or amortized inference in a VAE.

Lesson quiz · 5 questions

Score 60% or more to complete this lesson.

Take the quiz