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 | d² | 동일 타원 | 모든 클러스터 같은 Σ |
| 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}%)")
관련 기법¶
- K-Means - Hard clustering 버전
- Bayesian GMM - 자동 컴포넌트 수 결정
- Hidden Markov Model - 시퀀스 데이터용 확장
- DPGMM - Dirichlet Process GMM
참고 문헌¶
- 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.
- Bishop, C. M. (2006). Pattern Recognition and Machine Learning. Chapter 9: Mixture Models and EM.
- McLachlan, G., & Peel, D. (2000). Finite Mixture Models. Wiley.