Gaussian mixture models and EM
Fit a mixture of Gaussians with the EM algorithm and interpret it as soft k-means with covariances.
A Gaussian mixture model (GMM) describes data as drawn from a weighted sum of Gaussians, each with its own mean and covariance. Fitting it directly by MLE is hard, but expectation-maximization (EM)[1] alternates two simple steps: probabilistic cluster assignment (E-step) and Gaussian re-estimation (M-step). The result is a soft, covariance-aware generalization of k-means[2].
The model
For components with parameters , the GMM density is
with mixing weights , . Equivalently, draw a component from a categorical with probabilities , then draw .
The log-likelihood
has the log-of-sum that prevents a closed-form MLE. EM solves it by introducing latent assignment variables.
The EM algorithm
Initialize parameters. Iterate:
E-step. Compute the responsibility that component takes for example under the current parameters:
M-step. Update parameters with responsibility-weighted MLE:
where is the effective number of points assigned to component .
Each iteration is guaranteed to not decrease the log-likelihood (proof via Jensen's inequality applied to the latent-variable formulation)[1]. The algorithm converges to a local maximum.
GMM as soft k-means
When for all components and , responsibilities collapse to hard 0/1 assignments and the M-step reduces to mean update: GMM-EM becomes k-means. The general GMM relaxes k-means in two ways: covariance per cluster (elliptical, not spherical), and soft assignments (a point can belong fractionally to several clusters).
Initialization
EM is sensitive to local minima. The standard recipe:
- Run k-means with multiple restarts to find good initial means.
- Initialize each as the within-cluster covariance.
- Initialize as the empirical cluster frequencies.
Then run EM to refine.
Choosing
Use BIC or AIC:
with the number of free parameters ( for full covariances). Pick minimizing BIC. AIC uses instead of and tends to pick larger .
For very large datasets and many components, variational Bayesian GMMs add a Dirichlet prior on that automatically prunes unnecessary components.
Strengths and limits
Strengths:
- Probabilistic model: gives data likelihood, component memberships, and density estimates.
- Captures elliptical clusters that k-means misses.
- Natural extension of k-means; same intuition.
Limits:
- Assumes Gaussian components. Curved or skewed clusters violate the assumption.
- Many parameters (). For small samples per cluster, regularize covariances or restrict to diagonal.
- Component degeneracy: a component can collapse onto a single point, giving infinite likelihood. Regularize by adding to each .
Check it on arrays
import numpy as np
rng = np.random.default_rng(0)
# Three elliptical clusters with different orientations
mu_true = [np.array([2.0, 0.0]), np.array([-2.0, 0.0]), np.array([0.0, 2.0])]
cov_true = [np.array([[1.0, 0.7], [0.7, 1.0]]),
np.array([[1.0, -0.3], [-0.3, 0.3]]),
np.array([[0.5, 0.0], [0.0, 0.5]])]
pi_true = np.array([0.4, 0.4, 0.2])
n = 500
z_true = rng.choice(3, size=n, p=pi_true)
# Draw z ~ N(0, I), then x = z @ L.T + mu with L L.T = Sigma to get N(mu, Sigma).
X = np.vstack([rng.normal(size=(1, 2)) @ np.linalg.cholesky(cov_true[c]).T + mu_true[c]
for c in z_true])
def log_gaussian(X, mu, Sigma):
d = X.shape[1]
diff = X - mu
inv = np.linalg.inv(Sigma)
sign, logdet = np.linalg.slogdet(Sigma)
return -0.5 * d * np.log(2 * np.pi) - 0.5 * logdet - 0.5 * np.sum(diff @ inv * diff, axis=1)
def gmm_em(X, K, max_iter=100, tol=1e-5, rng=rng):
n, d = X.shape
# init: random points + identity covariance
mu = X[rng.choice(n, K, replace=False)]
Sigma = [np.eye(d) for _ in range(K)]
pi = np.full(K, 1 / K)
prev_ll = -np.inf
for it in range(max_iter):
# E-step
log_resp = np.column_stack([log_gaussian(X, mu[k], Sigma[k]) + np.log(pi[k]) for k in range(K)])
log_norm = np.logaddexp.reduce(log_resp, axis=1)
log_resp -= log_norm[:, None]
gamma = np.exp(log_resp)
ll = log_norm.sum()
if abs(ll - prev_ll) < tol: break
prev_ll = ll
# M-step
Nk = gamma.sum(axis=0)
pi = Nk / n
for k in range(K):
mu[k] = (gamma[:, k][:, None] * X).sum(axis=0) / Nk[k]
diff = X - mu[k]
Sigma[k] = (gamma[:, k][:, None] * diff).T @ diff / Nk[k] + 1e-4 * np.eye(d)
return pi, mu, Sigma, gamma, ll
pi, mu, Sigma, gamma, ll = gmm_em(X, K=3)
print(f"log-likelihood: {ll:.2f}")
print(f"learned mixing weights: {pi.round(2)}, true: {pi_true.round(2)}")
print(f"learned means (sorted): \n{mu[np.argsort(mu[:, 0])].round(2)}")
print(f"true means (sorted): \n{np.array(mu_true)[np.argsort([m[0] for m in mu_true])].round(2)}")
# BIC for K selection
def bic(X, K, pi, mu, Sigma, ll):
n, d = X.shape
p = K * d + K * d * (d + 1) // 2 + (K - 1)
return -2 * ll + p * np.log(n)
for K in [1, 2, 3, 4, 5]:
pi_k, mu_k, S_k, _, ll_k = gmm_em(X, K=K, rng=np.random.default_rng(K))
print(f"K={K}: BIC = {bic(X, K, pi_k, mu_k, S_k, ll_k):.1f}")GMM-EM recovers the mixing weights and means, the log-likelihood is monotonically increasing, and BIC correctly favors .
Exercises
Exercise 1. A two-component GMM has mixing weights . At a point , the component densities are and . Compute the responsibilities and .
Solution
Step 1. Form the weighted component values . Component 1: . Component 2: .
Step 2. Normalize by their sum :
The point belongs twice as strongly to component 2, even though component 1 has the larger prior, because component 2's much higher density at outweighs the prior.
Exercise 2. Show that when for all components with a common , the responsibilities reduce to a softmax over negative squared distances, and argue that as this collapses to the hard k-means assignment.
Solution
Step 1. With the Gaussian is , the same normalizing constant for every .
Step 2. Substituting into the responsibility and cancelling the shared constant and (taking equal weights),
a softmax over the negative squared distances scaled by .
Step 3. As , the scale , so the softmax sharpens to an argmax: the component with the smallest gets responsibility and the rest . That hard nearest-center assignment is the k-means assignment step, and the M-step mean update then matches k-means exactly.
Exercise 3 (code). Fit a two-component GMM with EM on data drawn from two Gaussians and verify that the per-iteration log-likelihood is non-decreasing, the monotonicity guaranteed by EM.
Solution
import numpy as np
rng = np.random.default_rng(0)
X = np.vstack([rng.normal(loc=[-3.0, 0.0], scale=1.0, size=(150, 2)),
rng.normal(loc=[3.0, 0.0], scale=1.0, size=(150, 2))])
def log_gaussian(X, mu, Sigma):
d = X.shape[1]
diff = X - mu
inv = np.linalg.inv(Sigma)
_, logdet = np.linalg.slogdet(Sigma)
return -0.5 * d * np.log(2 * np.pi) - 0.5 * logdet - 0.5 * np.sum(diff @ inv * diff, axis=1)
def gmm_em(X, K, max_iter=50, rng=rng):
n, d = X.shape
mu = X[rng.choice(n, K, replace=False)]
Sigma = [np.eye(d) for _ in range(K)]
pi = np.full(K, 1 / K)
lls = []
for _ in range(max_iter):
log_resp = np.column_stack([log_gaussian(X, mu[k], Sigma[k]) + np.log(pi[k])
for k in range(K)])
log_norm = np.logaddexp.reduce(log_resp, axis=1)
gamma = np.exp(log_resp - log_norm[:, None])
lls.append(log_norm.sum())
Nk = gamma.sum(axis=0)
pi = Nk / n
for k in range(K):
mu[k] = (gamma[:, k][:, None] * X).sum(axis=0) / Nk[k]
diff = X - mu[k]
Sigma[k] = (gamma[:, k][:, None] * diff).T @ diff / Nk[k] + 1e-4 * np.eye(d)
return np.array(lls)
lls = gmm_em(X, K=2)
# EM never decreases the log-likelihood from one iteration to the next
assert np.all(np.diff(lls) >= -1e-6)
print("first ll:", round(lls[0], 1), " last ll:", round(lls[-1], 1))The log-likelihood rises monotonically across iterations and levels off at the local maximum, the convergence behavior EM's Jensen-bound proof guarantees.
The final C5 lesson covers the visualization-focused dimensionality-reduction methods t-SNE and UMAP.
References
- [1]Dempster, A. P., Laird, N. M., & Rubin, D. B. (1977). "Maximum likelihood from incomplete data via the EM algorithm". JRSS-B, 39(1), 1-38. doi:10.1111/j.2517-6161.1977.tb01600.x. The general EM algorithm and its monotone-likelihood guarantee.↩
- [2]Hastie, T., Tibshirani, R., & Friedman, J. (2009). The Elements of Statistical Learning (2nd ed.). Springer, §8.5, §14.3. doi:10.1007/978-0-387-84858-7. GMMs, EM as soft k-means, and BIC/AIC model selection.↩