Gaussian Mixture Models and Expectation-Maximization › Example

Michael BrenndoerferOctober 29, 20265 min read
Machine Learning from ScratchAll chapters
Part 9 of 15Unsupervised Learning
Chapter 66Gaussian Mixture Models and Expectation-Maximization › Example
Reading section 5 of 10 · Example · 5 minSection 5 of 10 · 5 min

Section 5 of 10 · 5 min read

Gaussian Mixture Models and Expectation-Maximization › ExampleSection view · Sections in this chapter5 / 10 ↓
Reading tools Definitions, links & notes

Choose your expertise level to adjust how many terms are explained. Beginners see more tooltips, experts see fewer to maintain reading flow. Hover over underlined terms for instant definitions.

Article links

Make inline references clickable

Notes & comments

We can carry a full EM iteration by hand on four one-dimensional observations, x={1.0, 2.5, 4.0, 6.0}x = \{1.0,\ 2.5,\ 4.0,\ 6.0\}, with two components. To keep the arithmetic transparent we fix the variances at σ12=σ22=1.52=2.25\sigma_1^2 = \sigma_2^2 = 1.5^2 = 2.25 and the weights at π1=π2=0.5\pi_1 = \pi_2 = 0.5 for the first E-step; the M-step then updates everything.

The normal density at xx is 1σ2πexp⁡ ⁣(−(x−μ)22σ2)\frac{1}{\sigma\sqrt{2\pi}}\exp\!\left(-\frac{(x-\mu)^2}{2\sigma^2}\right), where μ\mu is the mean and σ2\sigma^2 is the variance, here μ1=2.0\mu_1 = 2.0 and μ2=5.0\mu_2 = 5.0 with σ=1.5\sigma = 1.5. With σ=1.5\sigma = 1.5 the leading constant is 11.52π≈0.26596\frac{1}{1.5\sqrt{2\pi}} \approx 0.26596. Starting means are μ1=2.0\mu_1 = 2.0 and μ2=5.0\mu_2 = 5.0. For x=2.5x = 2.5 the squared distances are (2.5−2)2=0.25(2.5-2)^2 = 0.25 and (2.5−5)2=6.25(2.5-5)^2 = 6.25; dividing by 2σ2=4.52\sigma^2 = 4.5 gives approximately 0.05560.0556 and 1.38891.3889. Evaluating the densities with the unrounded constant and exponents gives approximately 0.251590.25159 and 0.066320.06632. The responsibilities follow from the ratio:

γ2,1=0.5×0.251590.5×0.25159+0.5×0.06632=0.251590.31791=0.7914,γ2,2=1−0.7914=0.2086.\gamma_{2,1} = \frac{0.5 \times 0.25159}{0.5 \times 0.25159 + 0.5 \times 0.06632} = \frac{0.25159}{0.31791} = 0.7914, \qquad \gamma_{2,2} = 1 - 0.7914 = 0.2086.

Repeating for all four points gives the E-step below. The two outer points are almost certain, while x=4.0x = 4.0 has a mixed posterior assignment, sitting at γ3,1=0.3392\gamma_{3,1} = 0.3392 (that is, γi1\gamma_{i1} is close to but below one half, so component 2 is slightly favoured). That contested point is useful because a nearest-centroid method still assigns it deterministically, while the mixture retains both possibilities under its fitted probability model.

Responsibilities for the four-point example after the first E-step.
xix_iN(xi∣μ1,2.25)\mathcal{N}(x_i \mid \mu_1, 2.25)N(xi∣μ2,2.25)\mathcal{N}(x_i \mid \mu_2, 2.25)γi1\gamma_{i1}γi2\gamma_{i2}
1.00.212970.007600.96560.0344
2.50.251590.066320.79140.2086
4.00.109340.212970.33920.6608
6.00.007600.212970.03440.9656

The log-likelihood at this parameter value is the sum of the logs of the mixture densities. For x=1.0x = 1.0 the mixture density is π1N(1.0∣μ1,2.25)+π2N(1.0∣μ2,2.25)≈0.11028133\pi_1 \mathcal{N}(1.0 \mid \mu_1, 2.25) + \pi_2 \mathcal{N}(1.0 \mid \mu_2, 2.25) \approx 0.11028133 using unrounded densities, whose natural logarithm is approximately −2.20472063-2.20472063. Summing the four contributions gives ℓ(θ0)=−8.074\ell(\boldsymbol{\theta}^{0}) = -8.074. That is our baseline. The exact M-step cannot lower it; equality is possible at a stationary configuration. In this example we can check the strict increase numerically.

Now the M-step. Effective counts are the column sums: N1=0.9656+0.7914+0.3392+0.0344=2.1306N_1 = 0.9656 + 0.7914 + 0.3392 + 0.0344 = 2.1306 and N2=4−2.1306=1.8694N_2 = 4 - 2.1306 = 1.8694. These are not integer counts. They are the total fractional membership each component has earned and provide the denominators for the M-step's weighted averages. The new mean of component 1 is the responsibility-weighted average of the observations,

μ1=0.9656(1.0)+0.7914(2.5)+0.3392(4.0)+0.0344(6.0)2.1306=4.50732.1306≈2.116,\mu_1 = \frac{0.9656(1.0) + 0.7914(2.5) + 0.3392(4.0) + 0.0344(6.0)}{2.1306} = \frac{4.5073}{2.1306} \approx 2.116,

and similarly μ2=(13.5−4.5073)/1.8694=4.810\mu_2 = (13.5 - 4.5073)/1.8694 = 4.810, where we used the fact that the two weighted sums must add to the total ∑ixi=13.5\sum_i x_i = 13.5. The variances use the same weights applied to squared deviations:

σ12=0.9656(1.0−2.116)2+0.7914(2.5−2.116)2+0.3392(4.0−2.116)2+0.0344(6.0−2.116)22.1306=1.428,\sigma_1^2 = \frac{0.9656(1.0 - 2.116)^2 + 0.7914(2.5-2.116)^2 + 0.3392(4.0-2.116)^2 + 0.0344(6.0-2.116)^2}{2.1306} = 1.428,

giving σ1=1.195\sigma_1 = 1.195, and the same calculation for component 2 gives σ22=1.826\sigma_2^2 = 1.826, so σ2=1.351\sigma_2 = 1.351. The weights become π1=2.1306/4=0.533\pi_1 = 2.1306/4 = 0.533 and π2=0.467\pi_2 = 0.467.

The test of the whole procedure is whether the likelihood improved. Recomputing the mixture density at the new parameters gives ℓ(θ1)=−7.897\ell(\boldsymbol{\theta}^{1}) = -7.897, which is larger than −8.074-8.074. The log-likelihood increases by about 0.1770.177 in this step, consistent with the nondecrease guarantee for this exact update. The contested point at x=4.0x = 4.0 has also moved: its responsibility for component 2 increases from about 0.6610.661 to 0.6920.692. Both means move closer to this point. The posterior change depends on the updated weights and variances as well as the means; distances to the means alone do not determine it.

The cell below reproduces the hand calculation with the same formulas, so we can confirm the arithmetic and inspect the responsibilities directly.

PythonIn [3]
CollapseExpand
x_ex = np.array([1.0, 2.5, 4.0, 6.0])
mu_ex = np.array([2.0, 5.0])
var_ex = np.array([2.25, 2.25])
pi_ex = np.array([0.5, 0.5])

# log component density for every (point, component) pair
log_dens = -0.5 * np.log(2 * np.pi * var_ex)[None, :] - (
    x_ex[:, None] - mu_ex[None, :]
) ** 2 / (2 * var_ex[None, :])
log_joint = log_dens + np.log(pi_ex)[None, :]
ll_ex = logsumexp(log_joint, axis=1)
resp_ex = np.exp(log_joint - ll_ex[:, None])
ConsoleOut [4]
CollapseExpand
responsibilities (rows = observations):
[[0.9656 0.0344]
 [0.7914 0.2086]
 [0.3392 0.6608]
 [0.0344 0.9656]]
total log-likelihood before the M-step: -8.074
PythonIn [5]
CollapseExpand
# one M-step
Nk = resp_ex.sum(axis=0)
mu_new = (resp_ex * x_ex[:, None]).sum(axis=0) / Nk
var_new = (resp_ex * (x_ex[:, None] - mu_new) ** 2).sum(axis=0) / Nk
pi_new = Nk / x_ex.size

log_dens_new = -0.5 * np.log(2 * np.pi * var_new)[None, :] - (
    x_ex[:, None] - mu_new[None, :]
) ** 2 / (2 * var_new[None, :])
ll_new = logsumexp(log_dens_new + np.log(pi_new)[None, :], axis=1).sum()
ConsoleOut [6]
CollapseExpand
effective counts:        [2.1306 1.8694]
updated means:           [2.116 4.81 ]
updated variances:       [1.428 1.826]
updated weights:         [0.533 0.467]
log-likelihood after:    -7.897
improvement:             +0.177

The printed numbers agree with the rounded hand calculation to three decimal places, and the log-likelihood strictly increased. Further exact EM steps cannot lower it, but that alone does not guarantee a finite limiting fit. Our later implementation uses a small-change stopping rule and explicitly checks for a material likelihood decrease. The example therefore demonstrates the full rhythm of EM: infer soft labels, turn them into weighted estimates, and check that the objective moved in the promised direction.

Comments

Add to the conversation

A question, a correction, or an example from your own work.

No comments yet. Be the first to share your thoughts!

Newsletter

Stay up to date

Get new articles, book updates, and notes on AI and technology.

Unsubscribe at any time. Privacy policy