Course contents 04 / 33

04 / 33 · Foundations

Fitting distributions with KL

Training a Gaussian's mean and covariance by minimizing a sample estimate of KL(P‖Q), the assumptions the KL divergence quietly relies on, and how KL, reverse KL, JS and a least-squares loss behave when one Gaussian must fit two modes.

On this page 6

The entropy note built the KL divergence from surprise and showed that minimizing DKL(P ∥ Q)D_{\mathrm{KL}}(P\,\|\,Q) over the model is the same as maximizing likelihood. This note puts it to work on the Gaussian model: a QQ with trainable parameters is pulled towards a target PP by gradient descent. Three questions organize it:

  1. How are the parameters trained? What actually happens in one iteration of the fitting loop?
  2. What does KL assume? What must hold for the formula to make sense, and where does each assumption show up in code?
  3. What if the model cannot represent the target? One Gaussian fitted to two modes shows how KL, reverse KL, JS and a least-squares loss differ.

Notation. PP is the true (target) distribution with density pp, Q=QθQ = Q_\theta is the model with density qq and parameters θ\theta, and η\eta is the learning rate. In one dimension θ=(μ,log⁡σ)\theta = (\mu, \log\sigma): σ\sigma is stored through its logarithm, so that σ=elog⁡σ\sigma = e^{\log\sigma} stays positive. In two dimensions the mean is μ\boldsymbol\mu and the covariance is Σ=LL⊤\boldsymbol\Sigma = \mathbf L\mathbf L^\top, with L\mathbf L the lower-triangular Cholesky factor from the Gaussian note.

How the parameters are trained#

One iteration of the loop, step by step:

  • The optimizer is given the list of tensors to train. Each was created with requires_grad=True, so that backward() fills its .grad. In one dimension these are the mean and log⁡σ\log\sigma; in two dimensions the mean and the three raw entries of the Cholesky factor from the Gaussian note. The target’s parameters have no gradient and stay fixed.
  • Q is rebuilt from the current parameters at every iteration: Q=N(μ,σ2)Q = \mathcal N(\mu, \sigma^2) with σ=elog⁡σ\sigma = e^{\log\sigma}.
  • qx holds the model density at the sample points, q(xi)q(x_i); px holds p(xi)p(x_i), the density of the true distribution at the same points.
  • loss is the sample estimate of the KL divergence built from px and qx.
  • loss.backward() computes the gradients ∂ loss/∂μ\partial\,\text{loss}/\partial\mu and ∂ loss/∂log⁡σ=σ ∂ loss/∂σ\partial\,\text{loss}/\partial\log\sigma = \sigma\,\partial\,\text{loss}/\partial\sigma by backpropagation and stores them in .grad.
  • optimizer.step() moves the parameters against the gradient. For plain SGD this is
μ←μ−η ∂ loss∂μ,log⁡σ←log⁡σ−η ∂ loss∂log⁡σ.\begin{gathered} \mu \leftarrow \mu - \eta\,\frac{\partial\,\text{loss}}{\partial \mu}, \\ \log\sigma \leftarrow \log\sigma - \eta\,\frac{\partial\,\text{loss}}{\partial \log\sigma}. \end{gathered}

Adam (Kingma and Ba, 2015) moves in a similar direction but rescales each coordinate’s step by running estimates of the gradient’s mean and magnitude. The KL fits below use Adam.

Figure

One iteration of the fitting loop

The KL training loop as a cycleSix boxes connected clockwise. Trainable parameters mu and s equal to log sigma, with requires_grad true, build the distribution Q equal to a normal with mean mu and variance sigma squared, where sigma is exp of s. Q is evaluated at samples x_i drawn from P, which also come with the true densities p of x_i. The loss is the mean of log p minus log q, an estimate of KL of P from Q. loss.backward gives the gradients of the loss with respect to mu and s. optimizer.step moves mu and s against the gradient, and the updated parameters feed the next iteration.Trainable parametersμ, s = log σrequires_grad=TrueDistributionQ = 𝒩(μ, σ²)σ = exp(s)Densities at xᵢq(xᵢ) and p(xᵢ)Lossmean(log p(xᵢ) − log q(xᵢ))≈ KL(P ‖ Q)Gradients∂loss/∂μ, ∂loss/∂soptimizer.step()μ ← μ − η·∂loss/∂μs ← s − η·∂loss/∂sfixed samples xᵢ ~ Ppx = p(xᵢ), known true densitybuild Qlog_probkl_loss(px, qx)loss.backward().gradupdated μ, s:next iteration↻ repeat; zero_grad() firstoptimizer = Adam([train_mu, train_log_std])“optimize these tensors by gradient”The KL training loop as a cycleSix boxes stacked vertically, top to bottom: trainable parameters mu and s equal to log sigma, the distribution Q equal to a normal with mean mu and variance sigma squared with sigma equal to exp of s, the densities q and p at the samples x_i drawn from P, the loss equal to the mean of log p minus log q, the gradients of the loss with respect to mu and s, and optimizer.step which moves mu and s against the gradient. An arrow on the left returns from the last box to the first: the updated parameters feed the next iteration.Adam([train_mu, train_log_std])optimize these tensors by gradientTrainable parametersμ, s = log σrequires_grad=Truebuild QDistributionQ = 𝒩(μ, σ²)σ = exp(s)Q.log_prob(xᵢ)Densities at xᵢq(xᵢ) and p(xᵢ)kl_loss(px, qx)Lossmean(log p(xᵢ) − log q(xᵢ))≈ KL(P ‖ Q)loss.backward()Gradients∂loss/∂μ, ∂loss/∂sgradientsoptimizer.step()μ ← μ − η·∂loss/∂μs ← s − η·∂loss/∂sxᵢ ~ P, p(xᵢ)updated μ, s → next iteration
The optimizer is given the list of tensors to train, here μ and s = log σ, so that σ = exp(s) stays positive; each was created with requires_grad=True, so that backward() fills its .grad. Each iteration rebuilds Q from the current parameters, scores the fixed samples xᵢ ~ P, computes the sample estimate of KL(P‖Q), back-propagates it and takes one step. Adam, used in the notebook, steps along a running average of the anti-gradient with per-coordinate step sizes; the update shown is plain SGD.

The 1-D fit in code:

# helpers: kl_loss (the estimate below), get_parameters_1d (mu = 0, log_std = 0),
# create_distr_1d, and target_1d, the 1-D two-mode mixture defined below
train_mu, train_log_std = get_parameters_1d()   # requires_grad=True
optimizer = torch.optim.Adam([train_mu, train_log_std], lr=0.01)

x  = target_1d.sample((1000,))      # fixed samples x_i ~ P
px = target_1d.log_prob(x).exp()    # p(x_i), known for a toy target

for i in range(2000):
    optimizer.zero_grad()           # clear .grad from the previous step
    Q  = create_distr_1d(train_mu, train_log_std)   # Normal(mu, exp(log_std))
    qx = Q.log_prob(x).exp()        # q(x_i); can underflow, see 'In code'
    loss = kl_loss(px, qx)          # mean(log2 p(x_i) - log2 q(x_i))
    loss.backward()                 # d loss / d mu, d loss / d log_std
    optimizer.step()                # move against the gradient

The loss is a Monte Carlo estimate of KL#

KL is an expectation under PP, so it can be estimated by averaging over samples x1,…,xN∼Px_1, \dots, x_N \sim P:

DKL(P ∥ Q)=∫p(x)log⁡p(x)q(x) dx=Ex∼P[log⁡p(x)−log⁡q(x)]≈1N∑i=1N[log⁡p(xi)−log⁡q(xi)].\begin{aligned} &D_{\mathrm{KL}}(P\,\|\,Q) = \int p(x)\log\frac{p(x)}{q(x)}\,dx \\ &\quad= \mathbb E_{x\sim P}\big[\log p(x) - \log q(x)\big] \\ &\quad\approx \frac1N\sum_{i=1}^{N}\big[\log p(x_i) - \log q(x_i)\big]. \end{aligned}

By the law of large numbers the average converges to the expectation as NN grows. The code above averages log⁡2p(xi)−log⁡2q(xi)\log_2 p(x_i) - \log_2 q(x_i), so its loss is in bits; changing the base only rescales the loss and its gradients by the constant 1/ln⁡2≈1.4431/\ln 2 \approx 1.443.

Figure

The sample estimate of KL(P‖Q) used in the notebook

Monte Carlo estimate of the KL divergenceUpper plot: a red Gaussian P centred at 3 and a wider blue Gaussian Q centred at 1.5, with forty short red ticks along the axis for samples drawn from P. Lower plot: the curve log p minus log q, which is positive between about 2 and 6.4 and negative on both sides. Dots on the curve at the sample positions are mostly in the positive region; their average, 0.72 nats, estimates the exact KL divergence of 0.63 nats.p(x)q(x)xᵢ ~ P−20246−2−1012mean = 0.72 (exact 0.63)Q underestimates p:log p − log q > 0Q overestimates: < 0,but few samples herelog p − log qx−20246Monte Carlo estimate of the KL divergenceUpper plot: a red Gaussian P centred at 3 and a wider blue Gaussian Q centred at 1.5, with forty short red ticks along the axis for samples drawn from P. Lower plot: the curve log p minus log q, which is positive between about 2 and 6.4 and negative on both sides. Dots on the curve at the sample positions are mostly in the positive region; their average, 0.72 nats, estimates the exact KL divergence of 0.63 nats.p(x)q(x)xᵢ ~ P−20246−2−1012mean = 0.72exact KL = 0.63Q too low:log p − log q > 0Q too high: < 0,few sampleslog p − log qx−20246
Top: P = 𝒩(3, 1) in red, Q = 𝒩(1.5, 1.5²) in blue, and 40 samples xᵢ ~ P as ticks. Bottom: log p(x) − log q(x) with the samples marked. It is positive between about 2.0 and 6.4, where Q underestimates P's density, and the samples land mostly there. Their mean, 0.723 nats, is a noisy estimate (N = 40) of the exact KL(P‖Q) = 0.628 nats; the notebook uses log₂ and so reports bits (divide by ln 2).

Samples from another distribution. If the samples come from a distribution RR with density rr instead of PP, each term is reweighted by p/rp/r (importance sampling):

DKL(P ∥ Q)≈1N∑i=1Nwi[log⁡p(xi)−log⁡q(xi)],wi=p(xi)r(xi),xi∼R,\begin{aligned} D_{\mathrm{KL}}(P\,\|\,Q) &\approx \frac1N\sum_{i=1}^{N} w_i\big[\log p(x_i) - \log q(x_i)\big], \\ w_i &= \frac{p(x_i)}{r(x_i)}, \qquad x_i \sim R , \end{aligned}

which requires r>0r > 0 wherever p>0p > 0. With R=PR = P every weight is 11 and the plain estimate returns.

An estimate can be negative. KL is never negative, but its estimate on a finite sample can be. In a fit of a full-covariance 2-D Gaussian QQ (five parameters) to the target N((−5,−5),I)\mathcal N\big((-5,-5), \mathbf I\big), with Adam on 1000 fixed samples, the loss after 4800 steps is −0.0020-0.0020 bits. The reason is that QQ is fitted to the same samples on which the loss is evaluated. The target is one of the candidate Gaussians, so at the maximum-likelihood fit the average of log⁡q\log q over the samples is at least the average of log⁡p\log p, and the estimate is ≤0\le 0. How far below zero follows from Wilks’ theorem: twice the gain in total log-likelihood of the fit over the true parameters is approximately χ2\chi^2-distributed with kk degrees of freedom for kk fitted parameters (Wilks, 1938), so on average the estimate sits k/(2N)k/(2N) nats below zero. Here that is 5/2000=0.00255/2000 = 0.0025 nats, or 0.00360.0036 bits. In a simulation of 2000 such fits the estimate averages −0.0037-0.0037 bits with a standard deviation of 0.00240.0024 bits, so −0.0020-0.0020 is a typical value. An honest estimate of the fitted model’s divergence needs fresh samples, as held-out data does for any model.

What the gradients are#

For a 1-D Gaussian, log⁡q(x)=−log⁡σ−12log⁡2π−(x−μ)22σ2\log q(x) = -\log\sigma - \tfrac12\log 2\pi - \frac{(x-\mu)^2}{2\sigma^2}. The term log⁡p(xi)\log p(x_i) does not depend on the parameters, so only −log⁡q-\log q contributes (natural logarithms):

∂ loss∂μ=−1N∑i=1Nxi−μσ2,∂ loss∂σ=1N∑i=1N[1σ−(xi−μ)2σ3].\begin{gathered} \frac{\partial\,\text{loss}}{\partial \mu} = -\frac1N\sum_{i=1}^{N}\frac{x_i-\mu}{\sigma^2}, \\ \frac{\partial\,\text{loss}}{\partial \sigma} = \frac1N\sum_{i=1}^{N}\left[\frac1\sigma - \frac{(x_i-\mu)^2}{\sigma^3}\right]. \end{gathered}

Both vanish when μ\mu is the sample mean and σ2=1N∑i(xi−μ)2\sigma^2 = \frac1N\sum_i (x_i-\mu)^2 the (biased) sample variance. These are the maximum-likelihood estimates, as the entropy note predicted. The gradient never needs pp: the values p(xi)p(x_i) only make the reported number equal to the KL. With real data, where pp is unknown, we drop that constant and minimize the negative log-likelihood −1N∑ilog⁡q(xi)-\frac1N\sum_i \log q(x_i) instead.

The direction of the steps is easiest to see with the exact KL between two 1-D Gaussians:

DKL(N(μP,σP2) ∥ N(μ,σ2))=log⁡σσP+σP2+(μP−μ)22σ2−12.\begin{aligned} &D_{\mathrm{KL}}\big(\mathcal N(\mu_P, \sigma_P^2)\,\|\,\mathcal N(\mu, \sigma^2)\big) \\ &\quad= \log\frac{\sigma}{\sigma_P} + \frac{\sigma_P^2 + (\mu_P - \mu)^2}{2\sigma^2} - \frac12 . \end{aligned}

It follows from DKL=EP[log⁡p(x)−log⁡q(x)]D_{\mathrm{KL}} = \mathbb E_P[\log p(x) - \log q(x)] with the expression for log⁡q\log q above and the same expression for log⁡p\log p (with μP,σP\mu_P, \sigma_P), using EP[(x−μP)2]=σP2\mathbb E_P[(x-\mu_P)^2] = \sigma_P^2 and EP[(x−μ)2]=σP2+(μP−μ)2\mathbb E_P[(x-\mu)^2] = \sigma_P^2 + (\mu_P-\mu)^2. The constants 12log⁡2π\tfrac12\log 2\pi cancel, and the term −12-\tfrac12 is −σP2/(2σP2)-\sigma_P^2/(2\sigma_P^2) from log⁡p\log p.

Figure

Fitting Q = 𝒩(μ, σ²) to P = 𝒩(3, 1) by gradient descent on KL(P‖Q)

Trajectory of Q while minimizing KL to a Gaussian targetTop: the red target density P, a normal with mean 3 and standard deviation 1, and seven blue curves for Q at increasing iterations, drawn from faint and thin to solid and bold. The first Q is a tall narrow bump at minus 2. The next ones become very wide and flat, then move right and narrow down until the last, boldest curve almost coincides with P. Bottom: the KL divergence on a log scale drops from about 51 nats to almost zero over 1600 iterations, with a long plateau around 1.5 nats while Q is wide.00.20.40.60.8−6−4−20246810xdensityP = 𝒩(3, 1)start: 𝒩(−2, 0.5²)iteration 50:very wide,σ = 3.83iteration 1600:μ = 2.92, σ = 1.0110⁻³10⁻²10⁻¹110¹10²040080012001600iterationKL(P‖Q), natsplateau ≈ 1.5: Q wide and still off-centreTrajectory of Q while minimizing KL to a Gaussian targetTop: the red target density P, a normal with mean 3 and standard deviation 1, and seven blue curves for Q at increasing iterations, drawn from faint and thin to solid and bold. The first Q is a tall narrow bump at minus 2. The next ones become very wide and flat, then move right and narrow down until the last, boldest curve almost coincides with P. Bottom: the KL divergence on a log scale drops from about 51 nats to almost zero over 1600 iterations, with a long plateau around 1.5 nats while Q is wide.00.20.40.60.8−4048xdensityPstart: 𝒩(−2, 0.5²)iter. 50:σ = 3.83iter. 1600:μ = 2.92σ = 1.0110⁻³10⁻²10⁻¹110¹10²08001600iterationKL(P‖Q), natsplateau ≈ 1.5:Q wide, off-centre
Q starts at 𝒩(−2, 0.5²) and is drawn at iterations 0, 5, 50, 400, 800, 1100, 1600 (from faint and thin to solid and bold). Because P has mass where the narrow Q is almost zero, the first steps mostly inflate σ (to 3.83 by iteration 50, while μ only creeps from −2 to −1.49); then μ slides towards 3 and σ shrinks to 1. After 1600 steps μ = 2.92, σ = 1.01. Lower panel: KL(P‖Q) in nats on a log scale. Computed with the closed-form Gaussian KL and plain gradient descent, learning rate 0.01, on the parameters (μ, log σ).

With plain gradient descent on (μ,log⁡σ)(\mu, \log\sigma), as in the figure, the trajectory shows the priorities of the forward KL. While QQ is narrow and far away, most of PP‘s mass sits where q≈0q \approx 0, and log⁡p−log⁡q\log p - \log q is huge there. The cheapest fix is to widen QQ: σ\sigma jumps up in the first steps while μ\mu only creeps. Once QQ covers PP, μ\mu moves across and σ\sigma shrinks back to PP‘s width. The order comes from the gradient sizes: from the closed form, ∂/∂log⁡σ=1−σP2+(μP−μ)2σ2\partial/\partial\log\sigma = 1 - \frac{\sigma_P^2 + (\mu_P-\mu)^2}{\sigma^2} grows like (μP−μ)2/σ2(\mu_P-\mu)^2/\sigma^2, while ∂/∂μ=μ−μPσ2\partial/\partial\mu = \frac{\mu-\mu_P}{\sigma^2} grows only like ∣μP−μ∣/σ2\lvert\mu_P-\mu\rvert/\sigma^2. Adam, which normalizes each coordinate’s step, moves both at a similar rate and follows a different path to the same optimum.

What KL assumes#

Start from the formula for continuous distributions (Kullback and Leibler, 1951):

DKL(P ∥ Q)=∫p(x) log⁡p(x)q(x) dx.D_{\mathrm{KL}}(P\,\|\,Q) = \int p(x)\,\log\frac{p(x)}{q(x)}\,dx .

For it to be a meaningful, finite number, conditions (a)–(c) must hold; (d) and (e) are choices that must be made consistently (Cover and Thomas, 2006, §8.5; Murphy, 2023, §5.1).

(a) Common support#

The support of a distribution is the set of its possible values, supp⁡p={x:p(x)>0}\operatorname{supp} p = \lbrace x : p(x) > 0\rbrace. If p(x)>0p(x) > 0 but q(x)=0q(x) = 0 on a region to which PP gives positive probability, then log⁡p(x)q(x)=∞\log\frac{p(x)}{q(x)} = \infty there and the divergence is infinite.

Formally, DKL(P ∥ Q)D_{\mathrm{KL}}(P\,\|\,Q) is finite only if PP is absolutely continuous with respect to QQ, written P≪QP \ll Q: every set to which QQ gives probability zero also has PP-probability zero. Wherever PP has mass, QQ must have mass too. This is one reason models trained by maximum likelihood use densities that are positive everywhere, such as Gaussians: a density with no holes avoids the infinities caused by support mismatch, since the per-sample loss −log⁡q(xi)-\log q(x_i) stays finite at every observed point. Positivity does not guarantee a finite expected loss or KL: condition (b) below gives two densities that are positive everywhere and still have DKL=∞D_{\mathrm{KL}} = \infty. Positivity alone does not make a model blurry. But combined with the forward KL, which punishes any missed mass, a model that cannot represent the data spreads probability between and around the modes (see One Gaussian, two modes). This is one explanation offered for blurry samples of likelihood-trained image models (Theis, van den Oord and Bethge, 2016).

Figure

Common support: Q must not have holes where P lives

A model with holes versus a blurry modelTwo plots of the same red standard normal density P. In the first, Q is a flat box between minus 1.5 and 1.5 and zero outside; the tails of P outside the box are shaded red and labelled p greater than 0, q equal to 0, so log p over q is infinite and KL is infinite. In the second, Q is a wider blue Gaussian that is positive everywhere and KL is finite, about 0.128 nats.Q with holesKL(P‖Q) = ∞−1.51.5QPp > 0, q = 0⇒ log p/q = ∞q = 0“Blurry” Gaussian QKL(P‖Q) = 0.128 natsPq > 0everywhere−202A model with holes versus a blurry modelTwo plots of the same red standard normal density P. In the first, Q is a flat box between minus 1.5 and 1.5 and zero outside; the tails of P outside the box are shaded red and labelled p greater than 0, q equal to 0, so log p over q is infinite and KL is infinite. In the second, Q is a wider blue Gaussian that is positive everywhere and KL is finite, about 0.128 nats.Q with holesKL(P‖Q) = ∞−1.51.5QPp > 0, q = 0⇒ log p/q = ∞q = 0“Blurry” Gaussian QKL(P‖Q) = 0.128 natsPq > 0everywhere−202
P = 𝒩(0, 1) in red. Left: Q uniform on [−1.5, 1.5]. The shaded tails hold 13.4% of P's mass, and there p(x) > 0 while q(x) = 0, so the integrand p log(p/q) is infinite and KL(P‖Q) = ∞. Right: a “blurry” Gaussian Q = 𝒩(0, 1.5²) is positive everywhere, and KL(P‖Q) = 0.128 nats.

The condition is one-sided. Points where q(x)>0q(x) > 0 but p(x)=0p(x) = 0 contribute 0⋅log⁡0q(x)=00\cdot\log\frac{0}{q(x)} = 0 to DKL(P ∥ Q)D_{\mathrm{KL}}(P\,\|\,Q); they matter only for the reverse direction.

(b) Integrability#

The integral must converge. Matching supports is not enough. For example, let PP be a standard Cauchy distribution and Q=N(0,σ2)Q = \mathcal N(0, \sigma^2). Both are positive everywhere, but log⁡pq\log\frac{p}{q} grows like x22σ2\frac{x^2}{2\sigma^2} and EP[x2]=∞\mathbb E_P[x^2] = \infty, so DKL(P ∥ Q)=∞D_{\mathrm{KL}}(P\,\|\,Q) = \infty. A model with light tails cannot fit heavy-tailed data in KL.

(c) Normalized densities#

pp and qq must be probability densities (or probability mass functions): non-negative and integrating (or summing) to 11. KL is not defined for raw scores such as a classifier’s logits; they must go through a softmax first. A torch.distributions object returns normalized log-densities from log_prob.

(d) Direction#

KL is not symmetric, DKL(P ∥ Q)≠DKL(Q ∥ P)D_{\mathrm{KL}}(P\,\|\,Q) \ne D_{\mathrm{KL}}(Q\,\|\,P), so we must decide which distribution plays the truth and which the approximation:

  • DKL(P ∥ Q)D_{\mathrm{KL}}(P\,\|\,Q), the forward KL, averages over PP and penalizes missed modes: QQ must cover everything where PP lives. This is the direction of maximum likelihood.
  • DKL(Q ∥ P)D_{\mathrm{KL}}(Q\,\|\,P), the reverse KL, averages over QQ and penalizes extra mass: QQ must not spread to where P≈0P \approx 0. This is the direction used in variational inference, including the VAE.
Figure

Which direction penalizes what: missed modes versus extra mass

Missed modes versus extra massTwo plots of the same red bimodal density P with peaks at minus 2.5 and 2.5. In the first, a narrow blue Q sits only on the right peak; the left peak is shaded and labelled missed mode, and the forward divergence KL of P from Q is about 12 nats while the reverse is about 0.69. In the second, a wide blue Gaussian covers both peaks; the area where Q is above P, in the middle and in the outer tails, is shaded and labelled extra mass, and now the reverse divergence, about 1.54 nats, is larger than the forward one, about 0.62.Q misses a modeKL(P‖Q) = 12.06KL(Q‖P) = 0.69−2.502.5QPmissed mode: p > 0, q ≈ 0KL(P‖Q) punishes thisQ spreads mass where P has noneKL(P‖Q) = 0.62KL(Q‖P) = 1.54−2.502.5QPextra mass: q > 0, p ≈ 0KL(Q‖P) punishes thisMissed modes versus extra massTwo plots of the same red bimodal density P with peaks at minus 2.5 and 2.5. In the first, a narrow blue Q sits only on the right peak; the left peak is shaded and labelled missed mode, and the forward divergence KL of P from Q is about 12 nats while the reverse is about 0.69. In the second, a wide blue Gaussian covers both peaks; the area where Q is above P, in the middle and in the outer tails, is shaded and labelled extra mass, and now the reverse divergence, about 1.54 nats, is larger than the forward one, about 0.62.Q misses a modeKL(P‖Q) = 12.06KL(Q‖P) = 0.69−2.502.5QPmissed mode: p > 0, q ≈ 0KL(P‖Q) punishes thisQ spreads mass where P has noneKL(P‖Q) = 0.62KL(Q‖P) = 1.54−2.502.5QPextra mass: q > 0, p ≈ 0KL(Q‖P) punishes this
P (red) is an equal mixture of 𝒩(±2.5, 0.7²). Left: Q sits on one mode. The missed mode is where P lives and Q is almost zero, so the forward divergence KL(P‖Q) = 12.06 nats is large, while the reverse KL(Q‖P) = 0.69 ≈ ln 2. Right: Q = 𝒩(0, 2.60²) covers both modes (it is the best single Gaussian for KL(P‖Q)), but puts mass between and beyond them where P ≈ 0 (shaded), so KL(Q‖P) = 1.54 is now the larger one and KL(P‖Q) = 0.62. All values computed by numerical integration.

(e) Continuous versus discrete#

For discrete distributions the integral becomes a sum, DKL(P ∥ Q)=∑xp(x)log⁡p(x)q(x)D_{\mathrm{KL}}(P\,\|\,Q) = \sum_{x} p(x)\log\frac{p(x)}{q(x)}. Mixing the two gives an infinite divergence. If PP has an atom at xx, then Q({x})=0Q(\lbrace x\rbrace) = 0 for any distribution QQ with a density, so PP is not absolutely continuous with respect to QQ and DKL(P ∥ Q)=∞D_{\mathrm{KL}}(P\,\|\,Q) = \infty by condition (a). This is why, for continuous data, maximum likelihood is read as the sample estimate of DKL(P ∥ Q)D_{\mathrm{KL}}(P\,\|\,Q) for the underlying density pp, not as the KL of the empirical distribution of the samples (compare the entropy note, where that reading is exact for discrete outcomes).

Modelling assumptions#

When KL is used as a training loss, a few more things are taken on faith:

  • Representability. The family QθQ_\theta can express the shape we need. A single Gaussian can match a single-Gaussian target exactly, but not a two-component mixture; the last section shows what happens then.
  • Differentiability. log⁡qθ(x)\log q_\theta(x) must be differentiable in θ\theta, so that gradient descent applies. Reparameterizing σ=elog⁡σ\sigma = e^{\log\sigma} also makes the parameters unconstrained, so a gradient step can never produce a negative σ\sigma.
  • Computability. The forward KL needs samples from PP and the value of log⁡q\log q at them. The reverse KL needs samples from QQ (reparameterized, for gradients) and the values of log⁡q\log q and log⁡p\log p at them. For real data only the first is available, which is why maximum likelihood uses the forward direction.

In code. Each assumption has a concrete place in the code. QQ is a Normal or MultivariateNormal, positive everywhere, so (a) holds mathematically whatever the target. Numerically it can still fail. The code computes qx = Q.log_prob(x).exp() and then qx.log2(), and in 32-bit floating point the exponential underflows to 00 once log⁡q(x)\log q(x) drops below about −103-103: for σ≈1\sigma \approx 1 that is about 1414 standard deviations from μ\mu, and the threshold shifts with log⁡σ\log\sigma. This makes the loss infinite when QQ starts far from PP. Working with log_prob directly avoids this. The same care applies to the two-component targets below. Their log-density log⁡(12p1(x)+12p2(x))\log\big(\tfrac12 p_1(x) + \tfrac12 p_2(x)\big) is computed from the component log-densities a=log⁡p1(x)+log⁡12a = \log p_1(x) + \log\tfrac12 and b=log⁡p2(x)+log⁡12b = \log p_2(x) + \log\tfrac12 as logaddexp⁡(a,b)=max⁡(a,b)+log⁡(1+e−∣a−b∣)\operatorname{logaddexp}(a, b) = \max(a, b) + \log\big(1 + e^{-|a - b|}\big), which never exponentiates a large negative number on its own and so stays finite far from both components. The covariance LL⊤\mathbf L\mathbf L^\top with an exponentiated diagonal keeps QQ a valid normalized density, as (c) requires. The target is a known Gaussian or mixture, so px can be evaluated. Finally, the loss averages over x ~ P, which fixes the direction to DKL(P ∥ Q)D_{\mathrm{KL}}(P\,\|\,Q).

One Gaussian, two modes#

With a single Gaussian target, KL fitting simply recovers the target. The interesting case is a target that the model family cannot represent. In 2-D the target is an equal mixture of N((−5,−5),I)\mathcal N\big((-5,-5), \mathbf I\big) and N((4,4),I)\mathcal N\big((4,4), \mathbf I\big). In 1-D it is

P=12 N(−3, 0.52)+12 N(−8, 0.52),P = \tfrac12\,\mathcal N(-3,\, 0.5^2) + \tfrac12\,\mathcal N(-8,\, 0.5^2),

and the model starts from Q=N(0,1)Q = \mathcal N(0, 1). Four losses are compared: forward KL, reverse KL, JS and a least-squares loss on density values. None of them is satisfactory, each for a different reason. The same trade-offs reappear across generative models, which is why this toy problem is worth understanding in detail (Huszár, 2015; Theis, van den Oord and Bethge, 2016).

Far from the target, the missed mode dominates#

The initial Q=N(0,1)Q = \mathcal N(0, 1) barely reaches the far mode at −8-8. A Gaussian is never exactly zero, but eight standard deviations from its mean it is zero for all practical purposes. There log⁡p(x)q(x)\log\frac{p(x)}{q(x)} is huge, and the far mode dominates the whole divergence. If QQ were exactly zero there, the divergence would be infinite, as condition (a) says. For a Gaussian QQ the forward KL nevertheless stays finite, and its gradient in μ\mu, (μ−EP[x])/σ2(\mu - \mathbb E_P[x])/\sigma^2, is large here. Overlap matters numerically, because qq underflows to 00 in 32-bit floating point (see In code above), and for the bounded losses JS and least squares, whose gradients vanish without it (see the table below).

Figure

When Q misses part of P's support

Forward KL is huge where Q is nearly zero but P is notTop panel: red target density with modes at -8 and -3, blue model density 𝒩(0, 1) centred at 0. A grey band left of x = -5.1 marks where q is below one in a million while p is still positive. At x = -8, q is about 5 times 10 to the -15, and log p over q is about 32 nats. Bottom panel: the integrand p(x) log p(x)/q(x) has a tall peak of about 12.8 at the far mode and a small one at -3; its area, the KL divergence, is 17.9 nats.q(x) < 10⁻⁶, but p(x) > 0−10−8−6−4−202PQ = 𝒩(0, 1)q(−8) ≈ 5·10⁻¹⁵Contribution to KL(P‖Q): p(x) log p(x)/q(x)04812−10−8−6−4−202here log p/q ≈ 32 natsarea = KL(P‖Q) ≈ 17.9 natsif q(x) = 0 where p(x) > 0:KL(P‖Q) = ∞Forward KL is huge where Q is nearly zero but P is notTop panel: red target density with modes at -8 and -3, blue model density 𝒩(0, 1) centred at 0. A grey band left of x = -5.1 marks where q is below one in a million while p is still positive. At x = -8, q is about 5 times 10 to the -15, and log p over q is about 32 nats. Bottom panel: the integrand p(x) log p(x)/q(x) has a tall peak of about 12.8 at the far mode and a small one at -3; its area, the KL divergence, is 17.9 nats.q(x) < 10⁻⁶,but p(x) > 0q(−8) ≈ 5·10⁻¹⁵−10−8−6−4−202PQ = 𝒩(0, 1)Contribution to KL(P‖Q):p(x) log p(x)/q(x)04812−10−8−6−4−202here log p/q ≈ 32 natsarea = KL(P‖Q) ≈ 17.9 natsif q(x) = 0 where p(x) > 0:KL(P‖Q) = ∞
Top: the notebook's bimodal target P and the initial model Q = 𝒩(0, 1). Left of x ≈ −5.1 the model density is below 10⁻⁶, yet P still has a whole mode there. Bottom: each point's contribution p(x) log p(x)/q(x) to KL(P‖Q); the far mode dominates the total of 17.9 nats. If q were exactly zero where p > 0, the log ratio and the divergence would be infinite.

Forward KL covers both modes; reverse KL picks one#

Minimizing DKL(P ∥ Q)D_{\mathrm{KL}}(P\,\|\,Q) makes a single Gaussian cover both modes, with a wide spread. The loss averages log⁡pq\log\frac{p}{q} over samples of PP, so any region where PP has mass and QQ is small is punished heavily. For a Gaussian QQ the optimum is exact moment matching. Writing the forward KL as

DKL(P ∥ Q)=−H(P)−EP[log⁡q(x)],D_{\mathrm{KL}}(P\,\|\,Q) = -H(P) - \mathbb E_P[\log q(x)],

only the second term depends on QQ, and for a Gaussian log⁡q\log q depends on xx only through xx and xx⊤xx^{\top}. Setting the derivatives with respect to μ\boldsymbol\mu and Σ\boldsymbol\Sigma to zero gives

μ∗=EP[x],Σ∗=Cov⁡P[x]\boldsymbol\mu^\ast = \mathbb E_P[x],\qquad \boldsymbol\Sigma^\ast = \operatorname{Cov}_P[x]

(Bishop, 2006, §10.7). For the 1-D target this gives μ∗=−5.5\mu^\ast = -5.5 and σ∗2=0.25+2.52=6.5\sigma^{\ast2} = 0.25 + 2.5^2 = 6.5, so σ∗≈2.55\sigma^\ast \approx 2.55: each component contributes its own variance 0.250.25, and the spread of the two means around −5.5-5.5 adds 2.522.5^2. For the 2-D target it gives the mean (−0.5,−0.5)(-0.5, -0.5) and the covariance (21.2520.2520.2521.25)\begin{pmatrix} 21.25 & 20.25 \\ 20.25 & 21.25\end{pmatrix}: one elongated Gaussian along the diagonal through both modes, with its densest part in the empty gap between them. At this optimum the divergence is 1.1701.170 nats (1.691.69 bits). An Adam fit (learning rate 0.10.1, 2000 fixed samples, started from N(0,I)\mathcal N(\mathbf 0, \mathbf I)) reaches exactly this picture, at an estimated 1.6691.669 bits after 4800 steps; the gap of 0.0190.019 bits is below the standard deviation of a 2000-sample estimate, about 0.0230.023 bits.

The reverse divergence DKL(Q ∥ P)D_{\mathrm{KL}}(Q\,\|\,P) averages over samples of QQ. It punishes QQ for putting mass where PP is small and does not care about parts of PP that QQ ignores, so QQ locks onto one mode (Bishop, 2006, §10.1.2, Figure 10.3; Minka, 2005). For two equal, well-separated modes, sitting exactly on one of them costs only DKL(Q ∥ P)≈ln⁡2D_{\mathrm{KL}}(Q\,\|\,P) \approx \ln 2: on that mode p≈q/2p \approx q/2, so log⁡qp≈ln⁡2\log\frac{q}{p} \approx \ln 2 everywhere QQ has mass.

Figure

Fitting one Gaussian to two modes: forward vs reverse KL

Forward and reverse KL fits of a Gaussian to a bimodal targetTwo panels with the same red bimodal target, peaks at x = -8 and x = -3. Top panel: the Gaussian minimizing KL(P‖Q), mean −5.50 and standard deviation 2.55, is a wide blue curve spanning both modes, with its peak in the empty gap between them; KL(P‖Q) = 0.94 nats. Bottom panel: the Gaussian minimizing KL(Q‖P), mean −3.00 and standard deviation 0.50, is a narrow blue curve on the right mode only, so the solid fit ignores the mode at -8; KL(Q‖P) = 0.69 nats, about ln 2. A dashed curve on the left mode marks the mirror solution, which is equally good.Forward KL: min over Q of KL(P‖Q)−10−8−6−4−2PReverse KL: min over Q of KL(Q‖P)−10−8−6−4−2covers both modes, wideP ≈ 0 herebest Gaussian:μ = −5.50σ = 2.55KL(P‖Q) = 0.94locks ontoone modeμ = −3.00σ = 0.50KL(Q‖P) = 0.69≈ ln 2ignored by thesolid fit;dashed: mirrorfit, as goodForward and reverse KL fits of a Gaussian to a bimodal targetTwo panels with the same red bimodal target, peaks at x = -8 and x = -3. Top panel: the Gaussian minimizing KL(P‖Q), mean −5.50 and standard deviation 2.55, is a wide blue curve spanning both modes, with its peak in the empty gap between them; KL(P‖Q) = 0.94 nats. Bottom panel: the Gaussian minimizing KL(Q‖P), mean −3.00 and standard deviation 0.50, is a narrow blue curve on the right mode only, so the solid fit ignores the mode at -8; KL(Q‖P) = 0.69 nats, about ln 2. A dashed curve on the left mode marks the mirror solution, which is equally good.Forward KL: min over Q of KL(P‖Q)−10−8−6−4−2PReverse KL: min over Q of KL(Q‖P)−10−8−6−4−2covers both modes, wideP ≈ 0 herebest Gaussian: μ = −5.50, σ = 2.55KL(P‖Q) = 0.94 natslocks ontoone modebest Gaussian: μ = −3.00, σ = 0.50KL(Q‖P) = 0.69 nats ≈ ln 2the solid fit ignores the mode at −8;dashed: the mirror fit, equally good
The target is the notebook's 1-D mixture P = ½𝒩(−3, 0.5²) + ½𝒩(−8, 0.5²). The best single Gaussian under KL(P‖Q) (found by grid search) has μ = −5.50, σ = 2.55: it matches the mean and variance of P. Its densest part, and about a third of its mass, lie in the gap where P is almost zero. Under KL(Q‖P) the best Gaussian sits on one mode (μ = −3.00, σ = 0.50); the other mode is an equally good solution.

JS does not cover the modes either#

JS never blows up, but it does not fix mode coverage. A JS fit of the 2-D mixture with Adam (learning rate 0.10.1, 5000 steps), started from μ=(−2,−2)\boldsymbol\mu = (-2,-2), ends with μ≈(−4.99,−5.06)\boldsymbol\mu \approx (-4.99, -5.06) and a nearly unit covariance at a loss of 0.2730.273 bits. That is a Gaussian on the mode at (−5,−5)(-5,-5); the mode at (4,4)(4,4) is dropped. The loss in this run is a weighted variant, 0.7 DKL(P ∥ M)+0.3 DKL(Q ∥ M)0.7\,D_{\mathrm{KL}}(P\,\|\,M) + 0.3\,D_{\mathrm{KL}}(Q\,\|\,M) in bits. For it, exactly locking onto one mode gives 0.2700.270 bits, close to the value reached; for the symmetric JS it would give 34log⁡243≈0.311\tfrac34\log_2\tfrac43 \approx 0.311 bits. The 1-D target ends the same way. Plain gradient descent (learning rate 0.10.1, 5000 fresh samples of each distribution per step) from N(0,1)\mathcal N(0, 1) is at μ=−2.995\mu = -2.995, σ=0.495\sigma = 0.495 after 1800 steps, on the nearer mode at −3-3, with the same weighted loss of 0.2700.270 bits. QQ’s peak density there, 0.810.81, is twice the target’s 0.400.40: all of QQ‘s mass sits on a mode that holds half of PP‘s. The symmetric JS behaves the same way: for the 1-D target its best single Gaussian also sits on one mode (see the table below).

Estimating JS from samples. Each half of JS is an expectation under its own distribution,

DJS(P ∥ Q)=12 Ex∼P[log⁡p(x)m(x)]+12 Ey∼Q[log⁡q(y)m(y)],m=12(p+q),\begin{aligned} D_{\mathrm{JS}}(P\,\|\,Q) &= \tfrac12\,\mathbb E_{x\sim P}\Big[\log\frac{p(x)}{m(x)}\Big] + \tfrac12\,\mathbb E_{y\sim Q}\Big[\log\frac{q(y)}{m(y)}\Big], \\ m &= \tfrac12(p + q), \end{aligned}

so the estimate needs two samples: x1,…,xN∼Px_1, \dots, x_N \sim P for the first term and y1,…,yN∼Qy_1, \dots, y_N \sim Q for the second, each averaged as in the KL estimate above. The mixture density is evaluated in log space, log⁡m=logaddexp⁡(log⁡p+log⁡12, log⁡q+log⁡12)\log m = \operatorname{logaddexp}\big(\log p + \log\tfrac12,\ \log q + \log\tfrac12\big), as for the two-component target. The second term depends on QQ‘s parameters also through the samples yjy_j. For its gradient to be correct, the samples must be drawn with the reparameterization trick, y=μ+σεy = \mu + \sigma\varepsilon with ε∼N(0,1)\varepsilon \sim \mathcal N(0, 1), so that backpropagation sees how they move when μ\mu and σ\sigma change. The JS runs quoted above draw the yjy_j without it, so their gradients miss this dependence.

Boundedness has a second cost. When QQ and PP barely overlap, JS is already close to its maximum ln⁡2\ln 2 and almost flat, so its gradient is weak. For the 1-D target, at the initial N(0,1)\mathcal N(0,1) it equals 0.6450.645 nats, already 93%93\% of ln⁡2\ln 2. The same saturation is the reason the original minimax GAN objective, which equals 2 DJS−ln⁡42\,D_{\mathrm{JS}} - \ln 4 when the discriminator is optimal, gives vanishing generator gradients (Arjovsky and Bottou, 2017, Theorem 2.4; see the GAN note and Wasserstein GANs).

A least-squares loss on densities is sensitive to scale#

A naive alternative compares density values directly:

LLS=1N∑i=1N(q(xi)−p(xi))2,xi∼P.\mathcal L_{\mathrm{LS}} = \frac1N \sum_{i=1}^{N} \bigl(q(x_i) - p(x_i)\bigr)^2 , \qquad x_i \sim P .

Density values depend on the scale of the data. If every coordinate is stretched by a factor cc, all densities are divided by cdc^{d} in dd dimensions. The squared differences, and with them the loss, then shrink by c2dc^{2d}, and the gradients shrink by a similar power. With plain SGD a learning rate that works for one scale fails for another. Adaptive optimizers such as Adam absorb a constant factor, but not the second problem: when QQ does not overlap PP, q(xi)≈0q(x_i) \approx 0 at the data points and the loss stays near the constant EP[p(x)2]\mathbb E_P[p(x)^2], which gives almost no gradient. KL has neither problem. The ratio p/qp/q, and hence the KL, does not change under rescaling. And −log⁡q(xi)-\log q(x_i) grows like (xi−μ)2/2σ2(x_i-\mu)^2/2\sigma^2 far from μ\mu, so the KL gradient grows rather than vanishes as QQ moves away from PP (∣∂/∂μ∣=5.5\lvert\partial/\partial\mu\rvert = 5.5 at the initial QQ in the table below).

The least-squares loss also has several minima. For the 1-D target its global minimum is the wide Gaussian of the table below (σ≈2.53\sigma \approx 2.53), but a Gaussian on one mode, N(−3,1.212)\mathcal N(-3, 1.21^2), is a local minimum (loss 0.04950.0495 against 0.04700.0470). In 2-D, a Gaussian on one mode, N((4,4),1.582 I)\mathcal N\big((4,4), 1.58^2\,\mathbf I\big), is a local minimum with loss 0.001130.00113; the lowest value a numerical search finds is 0.001020.00102, for a Gaussian centred at (−0.5,−0.5)(-0.5,-0.5) and stretched along the diagonal (standard deviation 6.56.5 along it, 0.430.43 across). Which minimum gradient descent reaches depends on the start. An Adam fit (learning rate 0.0010.001, 5000 steps, 1000 samples) started from N((1,1),I)\mathcal N\big((1,1), \mathbf I\big), which overlaps only the mode at (4,4)(4,4), ends with QQ centred on that mode and wider than it; the mode at (−5,−5)(-5,-5) is ignored. Its samples contribute almost nothing to the gradient, because q≈0q \approx 0 there. QQ comes out wider than the component because a Gaussian holding all its mass on a mode of weight 12\tfrac12 would be twice as dense as PP there, and widening lowers its peak. A larger learning rate or four times as many steps end on the same mode.

The four losses side by side#

1-D target above, natural logarithms. The least-squares loss EP[(q−p)2]\mathbb E_P\big[(q-p)^2\big] is not in nats: it has units of squared density, so its value depends on the scale of the data.

Single Gaussian QQDKL(P ∥ Q)D_{\mathrm{KL}}(P\,\Vert\,Q)DKL(Q ∥ P)D_{\mathrm{KL}}(Q\,\Vert\,P)DJSD_{\mathrm{JS}}LS
initial N(0,1)\mathcal N(0, 1)17.8817.8819.5019.500.6450.6450.09010.0901
moment-matched N(−5.5, 6.5)\mathcal N(-5.5,\, 6.5), σ≈2.55\sigma \approx 2.550.936\mathbf{0.936}3.7093.7090.2550.2550.0470\mathbf{0.0470}
one mode N(−3, 0.52)\mathcal N(-3,\, 0.5^2)24.3124.310.693\mathbf{0.693}0.216\mathbf{0.216}0.09190.0919
∣∂/∂μ∣\lvert\partial/\partial\mu\rvert at the initial QQ5.55.512.012.00.0700.0700.00450.0045

Bold marks the best of the three candidates for each loss. For KL, reverse KL and JS the bold entry is also the global optimum of the loss over all (μ,σ)(\mu, \sigma), found by grid search; the least-squares optimum is at σ≈2.53\sigma \approx 2.53, essentially the moment-matched Gaussian. The values were computed by numerical integration. Forward KL and least squares prefer the wide Gaussian; reverse KL and JS prefer one mode. The last row shows how strongly each loss pulls the initial QQ towards the data: KL gives a strong signal, while JS and least squares are nearly flat.

A forward-KL fit with Adam (learning rate 0.010.01, 1000 fixed samples) lands on the moment-matched Gaussian: μ=−5.494\mu = -5.494, σ=2.566\sigma = 2.566, with an estimated 1.3001.300 bits. The exact value at the optimum is 0.9360.936 nats, or 1.3501.350 bits; the difference of 0.050.05 bits is less than two standard deviations of a 1000-sample estimate (about 0.030.03 bits each).

How the gradients behave during training#

Optional depth: how the gradient magnitudes evolve over two training runs, one of them with an incomplete JS estimator. The rest of the note does not depend on it.

The last row of the table is a snapshot at the start. Over a whole fit, KL and JS gradients behave differently (1-D target, magnitudes in bits per unit of μ\mu and of log⁡σ\log\sigma):

  • Forward KL on fixed samples: large, smooth, zero at the end. At the initial N(0,1)\mathcal N(0, 1) the gradients are 7.97.9 for μ\mu and 51.651.6 for log⁡σ\log\sigma (from the gradient formulas above, 5.55.5 and 35.7535.75 nats, divided by ln⁡2\ln 2). The log⁡σ\log\sigma gradient, by far the larger, collapses first, once QQ is wide enough to cover both modes. The μ\mu gradient shrinks more slowly and stays at a few tenths of a bit while QQ moves across. Because the samples are fixed, the loss is a deterministic function of (μ,log⁡σ)(\mu, \log\sigma), and both gradients fall to zero as the fit reaches its minimum.
  • JS with fresh samples: small and noisy. This run is a diagnostic, not a measurement of the true JS gradient: it draws the yjy_j without the reparameterization trick, so its gradients miss the dependence of the second term on the samples. In the plain gradient-descent run above (learning rate 0.10.1, 5000 new samples of PP and of QQ at every step), no gradient exceeds about 0.110.11 bits. The magnitudes peak twice, around step 40 while QQ widens and around step 720 when it contracts onto the mode at −3-3. After that they do not decay but fluctuate between 00 and about 0.040.04 bits until the last of the 2000 steps. Each step estimates the loss from new samples, so its gradient carries Monte Carlo noise that does not vanish at the optimum, and with a constant learning rate the parameters keep responding to it.

Three effects are mixed here and should be kept apart. The geometry of the objective: JS is nearly flat far from the target, which the last row of the table shows from exact integrals, while KL gives a strong signal there and a clean stop at the optimum. The estimator: the JS run’s gradients are incomplete, so the exact shape of its trajectory says little about the true JS gradient. The sampling: with fresh samples at every step, any estimated gradient keeps Monte Carlo noise at the optimum, so a fit with a constant learning rate never settles exactly.

Notebook#

The experiments follow the Distances between distributions notebook (HSE-LAMBDA Deep Generative Models, pinned commit; cell numbers count every cell, markdown included, from 1). The run results quoted above come from its saved outputs; the exact divergences and gradients, the minima of the least-squares loss and the simulation of negative estimates were computed separately, as were the least-squares runs with px recomputed and the resumed KL gradient curve. Cells 1–11 (helpers, the single-Gaussian target and the initial QQ) are described in the Gaussian note.

  • Cells 12–15: the sample estimate of DKL(P ∥ Q)D_{\mathrm{KL}}(P\,\|\,Q) (kl_loss, in bits; its weights argument takes importance weights and defaults to ones) and an Adam fit (learning rate 0.1, 5000 steps) of the 2-D model to the single target N((−5,−5),I)\mathcal N((-5,-5), \mathbf I) on the 1000 samples x of cell 8. The last plot, at step 4800, is titled KL = -0.0020.
  • Cells 16–19: the two-component 2-D mixture with means (−5,−5)(-5,-5) and (4,4)(4,4), and the same KL fit applied to it, on 2000 fixed samples. log_prob_mixture uses torch.logaddexp. sample_from_mixture takes exactly half of the samples from each component, a stratified sample; i.i.d. draws from the mixture would split them binomially. The last plot, at step 4800, shows KL mixture = 1.6688.
  • Cells 20–23: the JS divergence and its Monte Carlo estimate (js_div: x = P.sample(...) for the first term, y = sample(Q, ...) for the second, torch.logaddexp for log⁡m\log m), and a JS fit of the mixture started from μ=(−2,−2)\boldsymbol\mu = (-2,-2) with 5000 samples per step. The last printout, at step 4900, reads JS loss = 0.2734 and mu = tensor([-4.9879, -5.0583]). Two implementation details matter when re-running it. js_div returns the weighted 0.7/0.30.7/0.3 combination rather than the symmetric ½–½ JS. It also draws the samples of QQ with sample() rather than rsample(), so the gradient ignores how those samples move when QQ changes.
  • Cells 24–26: LSE_loss, the mean squared error between q(xi)q(x_i) and p(xi)p(x_i), and an Adam fit (learning rate 0.001, 5000 steps) started from N((1,1),I)\mathcal N((1,1), \mathbf I). The notebook cites the least-squares GAN paper (Mao et al., 2017) as background, although that paper applies least squares to discriminator outputs, not to densities. The training cell draws fresh samples x but reuses the px left over from the JS cell, the target densities at 1000 other points, so px must be recomputed for the loss to compare the two densities at the same points. The last plot, at step 4800, reads LSE = 0.0013, with QQ on the mode at (4,4)(4,4). With px recomputed, the same fit still ends on that mode, at a loss of about 0.00110.0011 in a re-implementation.
  • Cells 27–38: the helpers of the code above (get_parameters_1d and create_distr_1d in cell 29, target_1d in cells 30–31) and the 1-D mixture 12N(−3,0.52)+12N(−8,0.52)\tfrac12\mathcal N(-3, 0.5^2) + \tfrac12\mathcal N(-8, 0.5^2), fitted with KL (Adam, learning rate 0.01, 2000 steps, 1000 fixed samples) and from N(0,1)\mathcal N(0, 1) with JS (SGD, learning rate 0.1, 2000 steps), with the gradient magnitudes of μ\mu and log⁡σ\log\sigma plotted over training (cells 35 and 38). The JS fit uses the same weighted js_div, with 5000 fresh samples per step. The saved plots show the KL fit at step 1500 (Loss (KL) = 1.3001, mu = -5.494, std = 2.566) and the JS fit at step 1800 (JS = 0.2698, mu = -2.995, std = 0.495). Cell 35’s legend says |grad sigma|, but the curve is the gradient with respect to log⁡σ\log\sigma, as cell 38 labels it.
  • The saved KL gradient curve does not start from N(0,1)\mathcal N(0, 1). Cell 33 creates the parameters at N(0,1)\mathcal N(0, 1); cell 34 does not re-create them. The curve of cell 35 starts at ∣∂/∂μ∣≈0.85\lvert\partial/\partial\mu\rvert \approx 0.85 and ∣∂/∂log⁡σ∣≈3.6\lvert\partial/\partial\log\sigma\rvert \approx 3.6 bits, where a start from N(0,1)\mathcal N(0, 1) gives 7.97.9 and 51.651.6. These are the values a fresh run reaches after about 200 steps, at μ≈−1.1\mu \approx -1.1, σ≈2.7\sigma \approx 2.7, and the execution counts skip a number between cells 33 and 34: the saved run continued from where an earlier, interrupted run of cell 34 had left the parameters. A re-implementation that resumes from that point with a new optimizer reproduces the saved curve: ∣∂/∂log⁡σ∣\lvert\partial/\partial\log\sigma\rvert falls to about 00 within roughly 100 steps, ∣∂/∂μ∣\lvert\partial/\partial\mu\rvert stays near 0.250.25 until about step 400 and reaches 00 by about step 750, and the fit is at μ≈−5.50\mu \approx -5.50, σ≈2.56\sigma \approx 2.56 by step 1500. A fresh run is slower: by step 1500 it is still at μ≈−4.1\mu \approx -4.1. Re-run cell 33 before cell 34 to get the intended start.

Next#

The model so far had five parameters and a closed-form density. Real generative models replace the five parameters with a neural network, and many give up the closed-form density. Training such networks brings its own machinery. The next note collects the pieces used throughout the course: batch normalization, how gradients flow through deep stacks of layers, and the shapes of convolutions.

References#