When EM applies¶
EM applies when two easy problems depend on each other.
If the hidden variable were known, estimating would be easy.
If were known, calculating would be easy.
EM solves them in turn.
E step. Use the current to calculate probabilities for .
M step. Use those probabilities to update .
Then repeat. Each round raises or preserves the observed-data likelihood. The iteration converges to a stationary point, although the point can depend on the starting values.
This requires a joint model and E and M steps that we can compute.
A two-group model¶
We observe , but not its group . If the group labels were known, the group proportions and means would be simple counts and averages. EM calculates a probability for each label and uses those probabilities as weights.
Let and
The unknown parameters are .
One fixed sample¶
We simulate one observed value from one of two hidden groups. The hidden label is shown because this is a teaching example. In real data, it is unknown. The observed value could, for example, be an apple weight and the hidden label its variety.
set.seed(8149)
n_sample <- 80L
hidden_group <- sample(rep(c(0, 1), each = n_sample / 2))
true_mean <- c(-1.5, 1.5)
observed <- rnorm(
n_sample, mean = true_mean[hidden_group + 1], sd = 1
)
data.frame(
observed = round(observed[1:8], 2),
hidden_group = hidden_group[1:8]
)E step: infer the hidden group¶
Given the current parameters, calculate the probability that observation came from group 1.
is also . It is the expected group-1 count contributed by observation .
responsibility <- function(observed, pi, mu) {
density_0 <- (1 - pi) * dnorm(observed, mean = mu[1], sd = 1)
density_1 <- pi * dnorm(observed, mean = mu[2], sd = 1)
density_1 / (density_0 + density_1)
}
start <- list(pi = 0.5, mu = c(-0.5, 0.5))
r <- responsibility(observed, start$pi, start$mu)
ordered <- order(observed)
options(repr.plot.width = 7, repr.plot.height = 4)
par(mar = c(4, 4, 1, 1))
plot(
observed[ordered], r[ordered], type = "b", pch = 19,
xlab = "Observed value",
ylab = "P(hidden group = 1 | observed value)",
ylim = c(0, 1), col = "#24567a"
)
abline(h = 0.5, lty = 2, col = "#7a8793")
A responsibility is not a hard group label. It measures uncertainty about the hidden group. Values near mean that the two groups remain difficult to distinguish.
M step: update the parameters¶
Use the responsibilities as fractional group labels.
In general, the M step maximizes the expected complete-data log likelihood.
observed_loglik <- function(observed, pi, mu) {
density <-
(1 - pi) * dnorm(observed, mean = mu[1], sd = 1) +
pi * dnorm(observed, mean = mu[2], sd = 1)
sum(log(density))
}
em_update <- function(observed, pi, mu) {
r <- responsibility(observed, pi, mu)
list(
pi = mean(r),
mu = c(
sum((1 - r) * observed) / sum(1 - r),
sum(r * observed) / sum(r)
)
)
}
fit_em <- function(observed, start, tolerance = 1e-8) {
parameter <- start
history <- data.frame(
iteration = 0L, pi = parameter$pi,
mu_0 = parameter$mu[1], mu_1 = parameter$mu[2],
loglik = observed_loglik(observed, parameter$pi, parameter$mu)
)
for (iteration in seq_len(500L)) {
updated <- em_update(observed, parameter$pi, parameter$mu)
change <- max(abs(
c(updated$pi, updated$mu) - c(parameter$pi, parameter$mu)
))
parameter <- updated
history <- rbind(
history,
data.frame(
iteration = iteration, pi = parameter$pi,
mu_0 = parameter$mu[1], mu_1 = parameter$mu[2],
loglik = observed_loglik(
observed, parameter$pi, parameter$mu
)
)
)
if (change < tolerance) break
}
parameter$history <- history
parameter
}
fit <- fit_em(observed, start)Why the iteration works¶
The observed-data log likelihood contains a sum over the hidden variable.
After the E step, let . This distribution gives every possible hidden assignment a weight. With these weights fixed, EM uses the bound
measures the remaining uncertainty about the hidden assignment. It is constant during the M step. The bound touches the observed-data log likelihood at the current parameters. The M step raises the bound, and the new likelihood lies above the new bound.
Any valid initial value can start the iteration. Under the usual regularity conditions, the likelihood converges and the parameter estimates approach a stationary point. Different initial values can reach different stationary points, so a useful starting value can still matter.
data.frame(
parameter = c("pi", "mu_0", "mu_1"),
true_value = c(0.5, true_mean),
estimate = round(c(fit$pi, fit$mu), 3)
)
stopifnot(all(diff(fit$history$loglik) >= -1e-10))
shown <- fit$history[fit$history$iteration <= 10, ]
options(repr.plot.width = 7, repr.plot.height = 4)
par(mar = c(4, 4, 1, 1))
plot(
shown$iteration, shown$loglik,
type = "b", pch = 19, col = "#24567a",
xlab = "EM iteration", ylab = "Observed-data log likelihood"
)
What to remember¶
EM applies when hidden data would make estimation simple and both the E and M steps can be computed.
The E step turns each hidden value into a probability. The M step uses those probabilities as fractional observations.
Each iteration raises or preserves the observed-data likelihood, but different starting values can lead to different stationary points.
The genotyping-error model uses the same structure. The latent variable is the true genotype, and the observed data are uncertain genotype measurements.