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.
Entropy, cross-entropy and KL divergence · Multivariate Gaussian distributions · Gradient descent
On this page 6
The entropy note built the KL divergence from surprise and showed that minimizing over the model is the same as maximizing likelihood. This note puts it to work on the Gaussian model: a with trainable parameters is pulled towards a target by gradient descent. Three questions organize it:
- How are the parameters trained? What actually happens in one iteration of the fitting loop?
- What does KL assume? What must hold for the formula to make sense, and where does each assumption show up in code?
- 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. is the true (target) distribution with density , is the model with density and parameters , and is the learning rate. In one dimension : is stored through its logarithm, so that stays positive. In two dimensions the mean is and the covariance is , with 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 thatbackward()fills its.grad. In one dimension these are the mean and ; 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. Qis rebuilt from the current parameters at every iteration: with .qxholds the model density at the sample points, ;pxholds , the density of the true distribution at the same points.lossis the sample estimate of the KL divergence built frompxandqx.loss.backward()computes the gradients and by backpropagation and stores them in.grad.optimizer.step()moves the parameters against the gradient. For plain SGD this is
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.
One iteration of the fitting loop
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 , so it can be estimated by averaging over samples :
By the law of large numbers the average converges to the expectation as grows. The code above averages , so its loss is in bits; changing the base only rescales the loss and its gradients by the constant .
The sample estimate of KL(P‖Q) used in the notebook
Samples from another distribution. If the samples come from a distribution with density instead of , each term is reweighted by (importance sampling):
which requires wherever . With every weight is 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 (five parameters) to the target , with Adam on 1000 fixed samples, the loss after 4800 steps is bits. The reason is that 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 over the samples is at least the average of , and the estimate is . How far below zero follows from Wilks’ theorem: twice the gain in total log-likelihood of the fit over the true parameters is approximately -distributed with degrees of freedom for fitted parameters (Wilks, 1938), so on average the estimate sits nats below zero. Here that is nats, or bits. In a simulation of 2000 such fits the estimate averages bits with a standard deviation of bits, so 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, . The term does not depend on the parameters, so only contributes (natural logarithms):
Both vanish when is the sample mean and the (biased) sample variance. These are the maximum-likelihood estimates, as the entropy note predicted. The gradient never needs : the values only make the reported number equal to the KL. With real data, where is unknown, we drop that constant and minimize the negative log-likelihood instead.
The direction of the steps is easiest to see with the exact KL between two 1-D Gaussians:
It follows from with the expression for above and the same expression for (with ), using and . The constants cancel, and the term is from .
Fitting Q = 𝒩(μ, σ²) to P = 𝒩(3, 1) by gradient descent on KL(P‖Q)
With plain gradient descent on , as in the figure, the trajectory shows the priorities of the forward KL. While is narrow and far away, most of ‘s mass sits where , and is huge there. The cheapest fix is to widen : jumps up in the first steps while only creeps. Once covers , moves across and shrinks back to ‘s width. The order comes from the gradient sizes: from the closed form, grows like , while grows only like . 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):
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, . If but on a region to which gives positive probability, then there and the divergence is infinite.
Formally, is finite only if is absolutely continuous with respect to , written : every set to which gives probability zero also has -probability zero. Wherever has mass, 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 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 . 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).
Common support: Q must not have holes where P lives
The condition is one-sided. Points where but contribute to ; they matter only for the reverse direction.
(b) Integrability#
The integral must converge. Matching supports is not enough. For example, let be a standard Cauchy distribution and . Both are positive everywhere, but grows like and , so . A model with light tails cannot fit heavy-tailed data in KL.
(c) Normalized densities#
and must be probability densities (or probability mass functions): non-negative and integrating (or summing) to . 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, , so we must decide which distribution plays the truth and which the approximation:
- , the forward KL, averages over and penalizes missed modes: must cover everything where lives. This is the direction of maximum likelihood.
- , the reverse KL, averages over and penalizes extra mass: must not spread to where . This is the direction used in variational inference, including the VAE.
Which direction penalizes what: missed modes versus extra mass
(e) Continuous versus discrete#
For discrete distributions the integral becomes a sum, . Mixing the two gives an infinite divergence. If has an atom at , then for any distribution with a density, so is not absolutely continuous with respect to and by condition (a). This is why, for continuous data, maximum likelihood is read as the sample estimate of for the underlying density , 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 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. must be differentiable in , so that gradient descent applies. Reparameterizing also makes the parameters unconstrained, so a gradient step can never produce a negative .
- Computability. The forward KL needs samples from and the value of at them. The reverse KL needs samples from (reparameterized, for gradients) and the values of and 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. 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 once drops below about : for that is about standard deviations from , and the threshold shifts with . This makes the loss infinite when starts far from . Working with log_prob directly avoids this. The same care applies to the two-component targets below. Their log-density is computed from the component log-densities and as , which never exponentiates a large negative number on its own and so stays finite far from both components. The covariance with an exponentiated diagonal keeps 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 .
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 and . In 1-D it is
and the model starts from . 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 barely reaches the far mode at . A Gaussian is never exactly zero, but eight standard deviations from its mean it is zero for all practical purposes. There is huge, and the far mode dominates the whole divergence. If were exactly zero there, the divergence would be infinite, as condition (a) says. For a Gaussian the forward KL nevertheless stays finite, and its gradient in , , is large here. Overlap matters numerically, because underflows to 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).
When Q misses part of P's support
Forward KL covers both modes; reverse KL picks one#
Minimizing makes a single Gaussian cover both modes, with a wide spread. The loss averages over samples of , so any region where has mass and is small is punished heavily. For a Gaussian the optimum is exact moment matching. Writing the forward KL as
only the second term depends on , and for a Gaussian depends on only through and . Setting the derivatives with respect to and to zero gives
(Bishop, 2006, §10.7). For the 1-D target this gives and , so : each component contributes its own variance , and the spread of the two means around adds . For the 2-D target it gives the mean and the covariance : 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 nats ( bits). An Adam fit (learning rate , 2000 fixed samples, started from ) reaches exactly this picture, at an estimated bits after 4800 steps; the gap of bits is below the standard deviation of a 2000-sample estimate, about bits.
The reverse divergence averages over samples of . It punishes for putting mass where is small and does not care about parts of that ignores, so 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 : on that mode , so everywhere has mass.
Fitting one Gaussian to two modes: forward vs reverse KL
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 , 5000 steps), started from , ends with and a nearly unit covariance at a loss of bits. That is a Gaussian on the mode at ; the mode at is dropped. The loss in this run is a weighted variant, in bits. For it, exactly locking onto one mode gives bits, close to the value reached; for the symmetric JS it would give bits. The 1-D target ends the same way. Plain gradient descent (learning rate , 5000 fresh samples of each distribution per step) from is at , after 1800 steps, on the nearer mode at , with the same weighted loss of bits. ’s peak density there, , is twice the target’s : all of ‘s mass sits on a mode that holds half of ‘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,
so the estimate needs two samples: for the first term and for the second, each averaged as in the KL estimate above. The mixture density is evaluated in log space, , as for the two-component target. The second term depends on ‘s parameters also through the samples . For its gradient to be correct, the samples must be drawn with the reparameterization trick, with , so that backpropagation sees how they move when and change. The JS runs quoted above draw the without it, so their gradients miss this dependence.
Boundedness has a second cost. When and barely overlap, JS is already close to its maximum and almost flat, so its gradient is weak. For the 1-D target, at the initial it equals nats, already of . The same saturation is the reason the original minimax GAN objective, which equals 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:
Density values depend on the scale of the data. If every coordinate is stretched by a factor , all densities are divided by in dimensions. The squared differences, and with them the loss, then shrink by , 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 does not overlap , at the data points and the loss stays near the constant , which gives almost no gradient. KL has neither problem. The ratio , and hence the KL, does not change under rescaling. And grows like far from , so the KL gradient grows rather than vanishes as moves away from ( at the initial 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 (), but a Gaussian on one mode, , is a local minimum (loss against ). In 2-D, a Gaussian on one mode, , is a local minimum with loss ; the lowest value a numerical search finds is , for a Gaussian centred at and stretched along the diagonal (standard deviation along it, across). Which minimum gradient descent reaches depends on the start. An Adam fit (learning rate , 5000 steps, 1000 samples) started from , which overlaps only the mode at , ends with centred on that mode and wider than it; the mode at is ignored. Its samples contribute almost nothing to the gradient, because there. comes out wider than the component because a Gaussian holding all its mass on a mode of weight would be twice as dense as 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 is not in nats: it has units of squared density, so its value depends on the scale of the data.
| Single Gaussian | LS | |||
|---|---|---|---|---|
| initial | ||||
| moment-matched , | ||||
| one mode | ||||
| at the initial |
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 , found by grid search; the least-squares optimum is at , 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 towards the data: KL gives a strong signal, while JS and least squares are nearly flat.
A forward-KL fit with Adam (learning rate , 1000 fixed samples) lands on the moment-matched Gaussian: , , with an estimated bits. The exact value at the optimum is nats, or bits; the difference of bits is less than two standard deviations of a 1000-sample estimate (about 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 and of ):
- Forward KL on fixed samples: large, smooth, zero at the end. At the initial the gradients are for and for (from the gradient formulas above, and nats, divided by ). The gradient, by far the larger, collapses first, once is wide enough to cover both modes. The gradient shrinks more slowly and stays at a few tenths of a bit while moves across. Because the samples are fixed, the loss is a deterministic function of , 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 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 , 5000 new samples of and of at every step), no gradient exceeds about bits. The magnitudes peak twice, around step 40 while widens and around step 720 when it contracts onto the mode at . After that they do not decay but fluctuate between and about 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 ) are described in the Gaussian note.
- Cells 12–15: the sample estimate of (
kl_loss, in bits; itsweightsargument 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 on the 1000 samplesxof cell 8. The last plot, at step 4800, is titledKL = -0.0020. - Cells 16–19: the two-component 2-D mixture with means and , and the same KL fit applied to it, on 2000 fixed samples.
log_prob_mixtureusestorch.logaddexp.sample_from_mixturetakes 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, showsKL 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.logaddexpfor ), and a JS fit of the mixture started from with 5000 samples per step. The last printout, at step 4900, readsJS loss = 0.2734andmu = tensor([-4.9879, -5.0583]). Two implementation details matter when re-running it.js_divreturns the weighted combination rather than the symmetric ½–½ JS. It also draws the samples of withsample()rather thanrsample(), so the gradient ignores how those samples move when changes. - Cells 24–26:
LSE_loss, the mean squared error between and , and an Adam fit (learning rate 0.001, 5000 steps) started from . 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 samplesxbut reuses thepxleft over from the JS cell, the target densities at 1000 other points, sopxmust be recomputed for the loss to compare the two densities at the same points. The last plot, at step 4800, readsLSE = 0.0013, with on the mode at . Withpxrecomputed, the same fit still ends on that mode, at a loss of about in a re-implementation. - Cells 27–38: the helpers of the code above (
get_parameters_1dandcreate_distr_1din cell 29,target_1din cells 30–31) and the 1-D mixture , fitted with KL (Adam, learning rate 0.01, 2000 steps, 1000 fixed samples) and from with JS (SGD, learning rate 0.1, 2000 steps), with the gradient magnitudes of and plotted over training (cells 35 and 38). The JS fit uses the same weightedjs_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 , as cell 38 labels it. - The saved KL gradient curve does not start from . Cell 33 creates the parameters at ; cell 34 does not re-create them. The curve of cell 35 starts at and bits, where a start from gives and . These are the values a fresh run reaches after about 200 steps, at , , 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: falls to about within roughly 100 steps, stays near until about step 400 and reaches by about step 750, and the fit is at , by step 1500. A fresh run is slower: by step 1500 it is still at . 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#
- S. Kullback, R. A. Leibler. On Information and Sufficiency. Annals of Mathematical Statistics, 22(1), 1951.
- T. M. Cover, J. A. Thomas. Elements of Information Theory, 2nd ed. Wiley, 2006. §8.5: relative entropy for continuous distributions.
- C. M. Bishop. Pattern Recognition and Machine Learning. Springer, 2006. §10.1.2 and Figure 10.3: forward versus reverse KL on a bimodal target; §10.7: moment matching.
- K. P. Murphy. Probabilistic Machine Learning: Advanced Topics. MIT Press, 2023. §5.1: KL divergence, its properties and the forward/reverse distinction.
- T. Minka. Divergence measures and message passing. Microsoft Research Technical Report MSR-TR-2005-173, 2005.
- F. Huszár. How (not) to train your generative model: scheduled sampling, likelihood, adversary? arXiv:1511.05101, 2015.
- L. Theis, A. van den Oord, M. Bethge. A note on the evaluation of generative models. ICLR, 2016.
- M. Arjovsky, L. Bottou. Towards principled methods for training generative adversarial networks. ICLR, 2017. §2, Theorem 2.4: vanishing gradients of the minimax generator loss.
- D. P. Kingma, J. Ba. Adam: A method for stochastic optimization. ICLR, 2015.
- S. S. Wilks. The large-sample distribution of the likelihood ratio for testing composite hypotheses. Annals of Mathematical Statistics, 9(1), 60–62, 1938.
- X. Mao, Q. Li, H. Xie, R. Y. K. Lau, Z. Wang, S. P. Smolley. Least squares generative adversarial networks. ICCV, 2017.
- HSE-LAMBDA, Deep Generative Models course repository:
distances.ipynb.