Writing

Machine Learning (16) — EM Algorithm

Maximum likelihood with latent variables, K-Means as EM intuition, Jensen's inequality, and a GMM training example.

Algorithm idea: MLE with hidden variables

We often estimate model parameters from observed samples by maximizing log-likelihood. When data include unobserved latent variables, both latents and parameters are unknown—we cannot maximize log-likelihood directly. EM helps. First, review MLE.

Maximum likelihood estimation (MLE)

Example: hunting with a friend—a rabbit is shot once and falls. Who fired? One shot, one hit—the hunter usually has higher hit rate, so we guess the hunter. That is MLE thinking.

MLE: given outcomes, find parameters that maximize the probability of those outcomes. “Model fixed, parameters unknown.” Steps:

(1)Write likelihood;

(2)Log-likelihood;

(3)Differentiate, set to zero;

(4)Solve.

EM when latents exist: E-step guess latents; M-step maximize log-likelihood over parameters given that guess. Repeat until convergence.

Each iteration: E then M until parameters stabilize.

K-Means algorithm

K-Means recap:

  • Choose k centers a₁…a_k

  • Assign each x to nearest center

  • Update centers to cluster means:

image

  • Repeat until stop (iterations, MSE, center change)

K-Means is intuitive EM: cluster centroids are latents; initialize k centroids (E), assign and update means (M). See K-Means clustering.

Real problems need precise math.

EM algorithm

Example: 50 boys and 50 girls, heights N(μ₁,σ₁) and N(μ₂,σ₂)—MLE per group if labels known. If 100 heights are mixed and unlabeled, we need EM.

Zhihu walkthrough: https://www.zhihu.com/question/27976634/answer/39132183

EM iterates: prior beliefs → fractional counts (E) → update proportions (M) until convergence—like splitting candy between two bags by weight until balanced.

Initialize parameters; repeat until convergence:

  • E-step: estimate latent distribution / responsibilities;

  • M-step: maximize expected log-likelihood for new parameters.

Setup: samples {x⁽¹⁾,…,x⁽ᵐ⁾}; latent z⁽ⁱ⁾ unknown (like cluster id). Estimate θ for p(x,z). Likelihood has latent z:

image

“log of a sum” makes direct differentiation hard. Transform to (2)(3)—Jensen’s inequality:

Convex f: E[f(X)] ≥ f(E[X]); equality for constant X.

image

(2) and (3) are inequalities unless we maximize lower bound J until L(θ) is tight.

Fix θ, adjust Q(z) to raise J to L at θ; fix Q, optimize θ (θ_t → θ_{t+1}); repeat to θ*.

Equality in Jensen when

image

Since

image

(Q is density over z), numerator sums to c, hence

image

E-step: Q(z) = posterior of latents. M-step: maximize J w.r.t. θ.

EM (Expectation-Maximization): MLE with incomplete data / missing latents.

Algorithm

Initialize θ;

Repeat until convergence:

E-step: posterior / responsibility for latents:

image

M-step: maximize expected complete log-likelihood:

image

Iterate until L(θ) is maximized.

Example

Hand-implemented EM for 2-component GMM:

#-*- conding:utf-8 -*-
# --encoding:utf-8 --
"""
Implement GMM clustering via EM
http://scipy.github.io/devdocs/generated/scipy.stats.multivariate_normal.html#scipy.stats.multivariate_normal
"""
import numpy as np
from scipy.stats import multivariate_normal

def train(x, max_iter=100):
    """
    Train 2-component GMM; return (pi, mu1, mu2, sigma1, sigma2)
    :param x: feature matrix
    :param max_iter: max iterations
    :return: (pi, mu1, mu2, sigma1, sigma2)
    """
    m, n = np.shape(x)

    mu1 = x.min(axis=0)
    mu2 = x.max(axis=0)
    sigma1 = np.identity(n)
    sigma2 = np.identity(n)
    pi = 0.5

    for i in range(max_iter):
        norm1 = multivariate_normal(mu1, sigma1)
        norm2 = multivariate_normal(mu2, sigma2)

        # E step
        tau1 = pi * norm1.pdf(x)
        tau2 = (1 - pi) * norm2.pdf(x)
        w = tau1 / (tau1 + tau2)

        # M step
        mu1 = np.dot(w, x) / np.sum(w)
        mu2 = np.dot(1 - w, x) / np.sum(1 - w)
        sigma1 = np.dot(w * (x - mu1).T, (x - mu1)) / np.sum(w)
        sigma2 = np.dot((1 - w) * (x - mu2).T, (x - mu2)) / np.sum(1 - w)
        pi = np.sum(w) / m

    return (pi, mu1, mu2, sigma1, sigma2)

if __name__ == '__main__':
    np.random.seed(28)

    mean1 = (0, 0, 0)
    cov1 = np.diag((1, 1, 1))
    data1 = np.random.multivariate_normal(mean=mean1, cov=cov1, size=500)

    mean2 = (2, 2, 3)
    cov2 = np.array([[1, 1, 3], [1, 2, 1], [0, 0, 1]])
    data2 = np.random.multivariate_normal(mean=mean2, cov=cov2, size=200)

    data = np.vstack((data1, data2))

    pi, mu1, mu2, sigma1, sigma2 = train(data, 100)
    print("第一个类别的相关参数:")
    print(mu1)
    print(sigma1)
    print("第二个类别的相关参数:")
    print(mu2)
    print(sigma2)

    print("预测样本属于那个类别(概率越大就是那个类别):")
    norm1 = multivariate_normal(mu1, sigma1)
    norm2 = multivariate_normal(mu2, sigma2)
    x = np.array([0, 1, 0])
    print(pi * norm1.pdf(x)) # class 1: ~0.989
    print((1 - pi) * norm2.pdf(x))# class 2: ~0.011

Output:

第一个类别的相关参数:
[-0.03813579 -0.0265211   0.06435217]
[[ 0.89477712 -0.01408823 -0.0630486 ]
 [-0.01408823  1.04926061  0.05625028]
 [-0.0630486   0.05625028  1.01136971]]
第二个类别的相关参数:
[1.89859865 1.9345104  2.77821259]
[[0.84837261 1.11138647 1.12554675]
 [1.11138647 2.19189624 1.37848616]
 [1.12554675 1.37848616 3.43711088]]
预测样本属于那个类别(概率越大就是那个类别):
0.02758258224417439
0.00039921371592994276

Process finished with exit code 0