Kullback-Leibler divergence estimation¶
Definition¶
Let \(p\) and \(q\) be two Gaussian mixture with \(K_p\) and \(K_q\) components respectively, defined as
and
where \(x \in \mathbb{R}^d\) and the component weights \(\pi_i\) and \(\omega_j\) are non-negative and sum to one:
Then the Kullback-Leibler (KL) divergence from \(p\) to \(q\) is defined as
For Gaussian mixtures, this can be expressed more explicitly as
This KL divergence does not have a closed-form expression, and must be estimated using numerical methods. There are various approaches to estimating the KL divergence between Gaussian mixtures, including Monte Carlo sampling, variational approximations, and unscented sigma point methods 1.
Example¶
To estimate the KL divergence between two Gaussian mixtures using the
kl_divergence function. For example:
import gmm_divergence as gd
p = gd.GaussianMixture.from_components( # (1)!
components=[
gd.Gaussian.univariate(mean=0.0, variance=1.0),
gd.Gaussian.univariate(mean=1.0, variance=1.0),
]
)
q = gd.GaussianMixture.from_components(
components=[
gd.Gaussian.univariate(mean=0.5, variance=1.0),
gd.Gaussian.univariate(mean=2.5, variance=0.5),
]
)
kl_estimate = gd.kl_divergence(
p, q, method=gd.divergence.MonteCarlo(sampling=gd.sampling.Draw(10_000, rng=9126))
)
assert abs(kl_estimate.value - 0.32286) < 1e-5
-
This defines \(p\) as a one dimensional (\(d=1\)) Gaussian mixture with two equally weighted components. The density of \(p\) is given by
\[ p(x) = 0.5 \mathcal{N}(x;0.0,1.0) + 0.5 \mathcal{N}(x;1.0,1.0). \]Simple constructors
For simple one-dimensional examples,
GaussianMixture.from_componentstogether withGaussian.univariateis usually the clearest way to define Gaussian mixtures.The
GaussianMixture.from_arraysconstructor remains useful when you already have weight, mean, and covariance arrays.
Other methods
The kl_divergence function also supports other estimation methods. See
the divergence API reference for details.
Sampling configuration¶
Monte Carlo sampling is configured with explicit sample specifications:
import gmm_divergence as gd
p = gd.GaussianMixture.from_components([
gd.Gaussian.univariate(mean=0.0, variance=1.0),
gd.Gaussian.univariate(mean=1.0, variance=1.0),
])
drawn = gd.divergence.MonteCarlo(sampling=gd.sampling.Draw(10_000, rng=9126))
reused = gd.divergence.MonteCarlo(sampling=gd.sampling.Samples(p.sample(10_000, rng=9126)))
stratified = gd.divergence.MonteCarlo(sampling=gd.sampling.Stratified(10_000, rng=9126))
gd.sampling.Draw is the default and works for any sampleable distribution.
gd.sampling.Samples is useful when comparing several methods on exactly the same
reference samples. gd.sampling.Stratified allocates fixed sample counts to
positive-weight components instead of relying on random component counts; a
single Gaussian is treated as a one-component mixture.
-
Hershey, John R., and Peder A. Olsen. "Approximating the Kullback Leibler divergence between Gaussian mixture models." 2007 IEEE International Conference on Acoustics, Speech and Signal Processing-ICASSP'07. Vol. 4. IEEE, 2007. ↩