콘텐츠로 이동
Data Prep
상세

Gaussian Mixture Model (GMM)

논문 정보

항목 내용
제목 Maximum Likelihood from Incomplete Data via the EM Algorithm
저자 Arthur P. Dempster, Nan M. Laird, Donald B. Rubin
연도 1977
학회 Journal of the Royal Statistical Society, Series B
링크 JSTOR

핵심 아이디어

K-Means의 한계: - Hard Clustering: 각 점이 하나의 클러스터에만 속함 - 구형 가정: 모든 클러스터가 동일한 크기/형태 - 확률 없음: 클러스터 소속의 불확실성 표현 불가

GMM의 해결책: - Soft Clustering: 각 점이 각 클러스터에 속할 확률을 가짐 - 타원형 클러스터: 공분산 행렬로 다양한 형태 표현 - 확률적 모델: 생성 모델로 새 데이터 생성 가능

핵심 가정: 데이터가 K개의 가우시안 분포의 혼합으로 생성됨

알고리즘

모델 정의

\[p(x) = \sum_{k=1}^{K} \pi_k \mathcal{N}(x | \mu_k, \Sigma_k)\]
  • \(K\): 컴포넌트(클러스터) 수
  • \(\pi_k\): 혼합 계수 (mixing coefficient), \(\sum_k \pi_k = 1\)
  • \(\mu_k\): k번째 가우시안의 평균
  • \(\Sigma_k\): k번째 가우시안의 공분산 행렬

가우시안 분포

\[\mathcal{N}(x | \mu, \Sigma) = \frac{1}{(2\pi)^{d/2}|\Sigma|^{1/2}} \exp\left(-\frac{1}{2}(x-\mu)^T\Sigma^{-1}(x-\mu)\right)\]

EM 알고리즘

Maximum Likelihood를 직접 최적화하기 어려우므로 EM (Expectation-Maximization) 사용:

입력: 데이터 X = {x₁, ..., xₙ}, 컴포넌트 수 K
출력: 파라미터 θ = {π₁...πₖ, μ₁...μₖ, Σ₁...Σₖ}

1. 초기화: 파라미터 무작위 초기화 또는 K-Means 결과 사용

2. E-step (Expectation):
   각 점 xₙ이 클러스터 k에 속할 책임(responsibility) 계산:

   γₙₖ = πₖ N(xₙ|μₖ, Σₖ) / Σⱼ πⱼ N(xₙ|μⱼ, Σⱼ)

3. M-step (Maximization):
   책임을 가중치로 사용하여 파라미터 업데이트:

   Nₖ = Σₙ γₙₖ  (클러스터 k의 유효 개수)

   μₖ = (1/Nₖ) Σₙ γₙₖ xₙ

   Σₖ = (1/Nₖ) Σₙ γₙₖ (xₙ - μₖ)(xₙ - μₖ)ᵀ

   πₖ = Nₖ / N

4. 수렴 확인: 로그 가능도 증가량이 임계값 이하면 종료

   log L = Σₙ log(Σₖ πₖ N(xₙ|μₖ, Σₖ))

5. 2-4 반복

공분산 유형

유형 파라미터 수 클러스터 형태 설명
spherical K 구형 Σₖ = σₖ² I
diag K·d 축 정렬 타원 대각 공분산
tied 동일 타원 모든 클러스터 같은 Σ
full K·d² 임의 타원 완전 공분산

시간 복잡도

  • 한 반복: O(nKd² + Kd³)
  • d가 크면 공분산 역행렬 계산이 병목

장단점

장점

장점 설명
Soft Clustering 확률적 소속으로 불확실성 표현
유연한 형태 타원형 클러스터
확률 모델 새 데이터 생성, 이상치 탐지 가능
이론적 기반 Maximum Likelihood, EM 수렴 보장

단점

단점 설명
K 지정 필요 컴포넌트 수 사전 결정
초기화 민감 지역 최적해 수렴
특이점(Singularity) 공분산이 특이해질 수 있음
가우시안 가정 비가우시안 데이터에 부적합
고차원 공분산 추정에 많은 데이터 필요

K-Means vs GMM

항목 K-Means GMM
클러스터링 Hard Soft (확률)
클러스터 형태 구형 타원형
파라미터 중심점만 평균, 공분산, 혼합계수
목적 함수 WCSS Log-likelihood
수렴 빠름 느림
해석 거리 기반 확률 기반

언제 쓰는가

적합한 경우

  • 소프트 클러스터링이 필요할 때 (소속 확률)
  • 클러스터가 타원형일 때
  • 생성 모델이 필요할 때 (데이터 생성, 이상치 탐지)
  • 클러스터 불확실성을 표현하고 싶을 때
  • K-Means로 부족할 때

부적합한 경우

  • 클러스터가 비정형 (초승달 등) → DBSCAN
  • 고차원 데이터 → 차원 축소 후 사용
  • 데이터가 적을 때 → 공분산 추정 불안정
  • 클러스터 수를 모를 때 → BIC로 선택 또는 HDBSCAN

Python 코드

import numpy as np
from sklearn.mixture import GaussianMixture
from sklearn.datasets import make_blobs
import matplotlib.pyplot as plt
from matplotlib.patches import Ellipse

# 데이터 생성
np.random.seed(42)
X, y_true = make_blobs(n_samples=500, centers=3, cluster_std=[1.0, 1.5, 0.5])

# GMM 적용
gmm = GaussianMixture(
    n_components=3,
    covariance_type='full',   # 'spherical', 'diag', 'tied', 'full'
    init_params='k-means++',  # 초기화 방법
    n_init=10,                # 초기화 횟수
    max_iter=100,
    tol=1e-3,
    random_state=42
)
gmm.fit(X)

# 결과
labels = gmm.predict(X)              # Hard 라벨
probs = gmm.predict_proba(X)         # Soft 확률

print(f"수렴 여부: {gmm.converged_}")
print(f"반복 횟수: {gmm.n_iter_}")
print(f"Log-likelihood: {gmm.score(X) * len(X):.2f}")
print(f"BIC: {gmm.bic(X):.2f}")
print(f"AIC: {gmm.aic(X):.2f}")

타원 시각화

def draw_ellipse(position, covariance, ax=None, **kwargs):
    """공분산을 타원으로 시각화"""
    ax = ax or plt.gca()

    if covariance.shape == ():
        covariance = np.array([[covariance, 0], [0, covariance]])
    elif covariance.shape == (2,):
        covariance = np.diag(covariance)

    # 고유값 분해
    eigenvalues, eigenvectors = np.linalg.eigh(covariance)
    order = eigenvalues.argsort()[::-1]
    eigenvalues, eigenvectors = eigenvalues[order], eigenvectors[:, order]

    # 타원 각도
    angle = np.degrees(np.arctan2(*eigenvectors[:, 0][::-1]))

    # 2 표준편차 (95% 신뢰구간)
    width, height = 2 * 2 * np.sqrt(eigenvalues)

    ellipse = Ellipse(position, width, height, angle=angle, **kwargs)
    ax.add_patch(ellipse)

# 시각화
fig, axes = plt.subplots(1, 2, figsize=(14, 5))

# Hard clustering
axes[0].scatter(X[:, 0], X[:, 1], c=labels, cmap='viridis', s=20, alpha=0.6)
axes[0].scatter(gmm.means_[:, 0], gmm.means_[:, 1], c='red', marker='X', s=200)
for pos, covar in zip(gmm.means_, gmm.covariances_):
    draw_ellipse(pos, covar, ax=axes[0], alpha=0.2, color='red')
axes[0].set_title('GMM Hard Clustering')

# Soft clustering (불확실성 표현)
uncertainty = 1 - probs.max(axis=1)
scatter = axes[1].scatter(X[:, 0], X[:, 1], c=uncertainty, cmap='Reds', s=20)
plt.colorbar(scatter, ax=axes[1], label='Uncertainty')
axes[1].set_title('Clustering Uncertainty')

plt.tight_layout()
plt.show()

BIC/AIC로 최적 K 선택

K_range = range(1, 10)
bics = []
aics = []

for k in K_range:
    gmm = GaussianMixture(n_components=k, random_state=42)
    gmm.fit(X)
    bics.append(gmm.bic(X))
    aics.append(gmm.aic(X))

fig, ax = plt.subplots(figsize=(10, 5))
ax.plot(K_range, bics, 'bo-', label='BIC')
ax.plot(K_range, aics, 'ro-', label='AIC')
ax.axvline(x=np.argmin(bics)+1, color='blue', linestyle='--', alpha=0.5)
ax.axvline(x=np.argmin(aics)+1, color='red', linestyle='--', alpha=0.5)
ax.set_xlabel('Number of Components')
ax.set_ylabel('Information Criterion')
ax.set_title('Model Selection: BIC and AIC')
ax.legend()
plt.show()

print(f"Optimal K (BIC): {np.argmin(bics)+1}")
print(f"Optimal K (AIC): {np.argmin(aics)+1}")

공분산 유형 비교

covariance_types = ['spherical', 'diag', 'tied', 'full']
fig, axes = plt.subplots(2, 2, figsize=(12, 10))

for ax, cov_type in zip(axes.flatten(), covariance_types):
    gmm = GaussianMixture(n_components=3, covariance_type=cov_type, random_state=42)
    gmm.fit(X)
    labels = gmm.predict(X)

    ax.scatter(X[:, 0], X[:, 1], c=labels, cmap='viridis', s=10)
    ax.scatter(gmm.means_[:, 0], gmm.means_[:, 1], c='red', marker='X', s=100)

    # 공분산 시각화 (2D만)
    for i, (mean, covar) in enumerate(zip(gmm.means_, gmm.covariances_ if cov_type in ['full', 'tied'] else [np.diag(c) if cov_type == 'diag' else c * np.eye(2) for c in gmm.covariances_])):
        if cov_type == 'tied':
            covar = gmm.covariances_
        elif cov_type == 'diag':
            covar = np.diag(gmm.covariances_[i])
        elif cov_type == 'spherical':
            covar = gmm.covariances_[i] * np.eye(2)
        draw_ellipse(mean, covar, ax=ax, alpha=0.2, color='red')

    ax.set_title(f'{cov_type} (BIC: {gmm.bic(X):.0f})')

plt.tight_layout()
plt.show()

이상치 탐지

# GMM으로 이상치 탐지
gmm = GaussianMixture(n_components=3, random_state=42)
gmm.fit(X)

# Log-likelihood (낮을수록 이상치)
log_probs = gmm.score_samples(X)

# 임계값 설정 (5% 퍼센타일)
threshold = np.percentile(log_probs, 5)
outliers = log_probs < threshold

plt.figure(figsize=(10, 6))
plt.scatter(X[~outliers, 0], X[~outliers, 1], c='blue', s=20, label='Normal')
plt.scatter(X[outliers, 0], X[outliers, 1], c='red', s=50, marker='x', label='Outlier')
plt.title('Anomaly Detection with GMM')
plt.legend()
plt.show()

print(f"이상치 수: {outliers.sum()} ({outliers.mean()*100:.1f}%)")

관련 기법

참고 문헌

  1. Dempster, A. P., Laird, N. M., & Rubin, D. B. (1977). Maximum Likelihood from Incomplete Data via the EM Algorithm. Journal of the Royal Statistical Society, Series B, 39(1), 1-38.
  2. Bishop, C. M. (2006). Pattern Recognition and Machine Learning. Chapter 9: Mixture Models and EM.
  3. McLachlan, G., & Peel, D. (2000). Finite Mixture Models. Wiley.