VAEs from scratch
The generative-models overview said a VAE encodes to a latent and decodes, trained with reconstruction plus KL. This is the math underneath: maximizing the ELBO, a lower bound on the data likelihood — and the reparameterization trick, the one idea that makes the whole thing trainable by backprop.
Written and reviewed by Shreyash Prashu Last reviewed
What you'll learn
- Why we maximize the ELBO instead of the intractable log-likelihood
- The ELBO's two terms — reconstruction and the KL regularizer
- The reparameterization trick and why sampling otherwise blocks gradients
- The closed-form Gaussian KL, and why VAE samples come out blurry
Before you start
The generative-models overview introduced the VAE as an encoder–decoder trained with a reconstruction loss plus a KL term. That’s the what. This lesson is the why it works: a VAE maximizes the ELBO (a tractable lower bound on the data likelihood), and it’s only trainable at all because of the reparameterization trick.
Why a lower bound
We’d love to maximize log p(x) — make the model assign high probability to real data. But
p(x) = ∫ p(x|z) p(z) dz integrates over every possible latent z, which is intractable. The
VAE’s move is to introduce an encoder q(z|x) that guesses which z likely produced x, and
to maximize a lower bound on log p(x) instead — the Evidence Lower BOund (ELBO):
ELBO = E[ log p(x|z) ] − KL( q(z|x) ‖ p(z) )
\_____________/ \_________________/
reconstruction regularizer
Maximizing the ELBO pushes log p(x) up. Its two terms are exactly the VAE loss: a
reconstruction term (the decoder should rebuild x from z) and a KL term that keeps the
encoder’s latent distribution close to a simple prior p(z) = N(0, 1), so the latent space stays
smooth and samplable.
The reparameterization trick
Here’s the snag that nearly sinks the whole approach. Training needs gradients to flow from the loss
back through z into the encoder — but z is sampled from q(z|x) = N(μ, σ²), and sampling
is not differentiable. You can’t backprop through a random draw.
The trick: move the randomness out of the gradient path. Instead of drawing z directly, draw a
fixed noise ε ~ N(0, 1) and compute z = μ + σ · ε. Now z is a deterministic, differentiable
function of μ and σ (the encoder’s outputs), with ε as an external constant — so gradients
flow straight through to the encoder:
import numpy as np
rng = np.random.default_rng(0)
mu, sigma = 2.0, 0.5
eps = rng.standard_normal(5) # external randomness, no gradient needed
z = mu + sigma * eps # differentiable w.r.t. mu and sigma
print("eps :", eps.round(2))
print("z :", z.round(2), " (= mu + sigma*eps)")
print("dz/dmu = 1 ; dz/dsigma = eps =", eps.round(2))
kl = 0.5 * (mu**2 + sigma**2 - np.log(sigma**2) - 1) # closed-form KL(N(mu,sigma^2) || N(0,1))
print(f"KL(N({mu},{sigma}^2) || N(0,1)) = {kl:.3f}")
eps : [ 0.13 -0.13 0.64 0.1 -0.54]
z : [2.06 1.93 2.32 2.05 1.73] (= mu + sigma*eps)
dz/dmu = 1 ; dz/dsigma = eps = [ 0.13 -0.13 0.64 0.1 -0.54]
KL(N(2.0,0.5^2) || N(0,1)) = 2.318
Because z = μ + σ·ε, the derivatives are trivial — ∂z/∂μ = 1 and ∂z/∂σ = ε — so the gradient
reaches μ and σ. And the KL term has a closed form for two Gaussians
(KL = ½ Σ(μ² + σ² − log σ² − 1)), so no sampling is needed for the regularizer at all. Those two
facts are what make the ELBO differentiable end-to-end.
Why VAE samples are blurry
With the ELBO trainable, generation is easy: sample z ~ N(0, 1), decode. But the reconstruction
term averages over many images consistent with a latent point — pixel-wise MSE rewards the mean
of plausible outputs, and the mean of several sharp images is a soft, blurry one. That’s the
structural reason VAEs trade sharpness for their smooth, well-behaved latent space (the opposite
trade from a GAN).
In one breath
- A VAE can’t maximize the intractable
log p(x) = ∫ p(x|z)p(z)dz, so it maximizes the ELBO, a tractable lower bound, using an encoderq(z|x). - The ELBO = reconstruction − KL: rebuild
xfromz, while keeping the latent close to a simple priorN(0,1)so the space stays smooth and samplable. - Training needs gradients through
z, but sampling isn’t differentiable — the reparameterization trick writesz = μ + σ·ε(ε ~ N(0,1)), makingzdifferentiable inμ, σ(∂z/∂μ=1, ∂z/∂σ=ε). - The Gaussian KL is closed-form (
½Σ(μ²+σ²−log σ²−1)), so the regularizer needs no sampling — together these make the ELBO differentiable end-to-end. - The reconstruction loss averages, so VAE samples are blurry — the trade for a smooth latent; the ELBO + reparam template also powers latent diffusion, β-VAE, and molecular design.
Quick check
Quick check
Next
The VAE’s adversarial counterpart — sharp but unstable — is GANs from scratch; both are framed in the generative-models overview. The Gaussian/KL machinery comes from distributions.
Practice this in an interview
All questionsGANs train a generator and discriminator adversarially to produce sharp samples but suffer from unstable training and mode collapse. VAEs optimise a tractable evidence lower bound for principled probability modelling but generate blurry samples. Diffusion models iteratively denoise from Gaussian noise, achieving state-of-the-art sample quality and diversity at the cost of slow sampling.
An autoencoder is a neural network trained to compress input into a low-dimensional bottleneck (encoder) and then reconstruct the original input from that bottleneck (decoder). It learns a compact representation without labels, making it useful for dimensionality reduction, anomaly detection, and as a component of generative models.
A KV cache stores the key and value tensors for previously processed tokens in each transformer layer, so autoregressive decoding computes only the new token's projections instead of rebuilding the whole prefix. This reduces per-step attention work from quadratic to linear in context length, while cache memory grows with layers, sequence length, batch size, and KV-head count.
BERT is an encoder-only transformer that reads all tokens bidirectionally and is trained with masked language modelling — ideal for tasks requiring a rich contextual representation of an entire sequence, like classification or NER. GPT is a decoder-only transformer that attends only to previous tokens via a causal mask and is trained with next-token prediction — ideal for text generation. Encoder-decoder models like T5 combine both for tasks that map one sequence to another.