본문으로 이동
메뉴 여닫기
환경 설정 메뉴 여닫기
개인 메뉴 여닫기
로그인하지 않음
지금 편집한다면 당신의 IP 주소가 공개될 수 있습니다.
Gaussian mixture model; GMM; 가우시안 혼합 모델, 가우스 혼합 모델
데이터가 평균과 공분산이 서로 다른 여러 정규 분포를 일정 비율로 섞은 분포에서 나왔다고 보는 확률 모형

가우시안 혼합 모델(Gaussian mixture model, GMM)은 정규 분포 여러 개를 가중합해 복잡한 모양의 분포를 나타내는 확률 모형이다. 각 정규 분포(성분)를 하나의 군집으로 보면 각 점이 어느 군집에 속할 확률을 주는 연성 군집(soft clustering) 방법이 되고, 분포 전체를 보면 이상 탐지나 표본 생성에 쓸 수 있는 밀도 추정 방법이 된다. 매개변수는 보통 EM 알고리즘으로 추정한다.

혼합 분포로 데이터를 설명하려는 시도는 1894년 칼 피어슨(Karl Pearson)이 게의 몸 치수 분포를 두 정규 분포의 혼합으로 분해한 연구까지 거슬러 올라간다.[1] 딥 러닝 이전에는 음성 인식의 음향 모델(GMM-HMM)과 화자 인증의 표준 구성 요소였고, 지금도 비지도 학습의 기본 모형으로 쓰인다. 시험 대비 개요는 혼합 분포 군집 문서에도 있다.

d차원 데이터에 대해 성분이 K개인 GMM의 밀도는

p(x)=∑k=1Kπk𝒩(x∣μk,Σk),πk≥0, ∑kπk=1

이다. πk는 혼합 비율(혼합 가중치), μk는 평균 벡터, Σk는 공분산 행렬이다. 같은 모형을 잠재 변수 z∈{1,…,K}로 쓰면 생성 과정이 된다.

  • 1단계: 범주 분포 z∼Cat(π)에서 성분 번호를 뽑는다.
  • 2단계: 그 성분의 정규 분포 x∣z=k∼𝒩(μk,Σk)에서 점을 뽑는다.

z를 합해 없애면 위의 밀도가 된다. 학습된 모형에서 점 xi가 성분 k에 속할 사후 확률

γik=P(zi=k∣xi)=πk𝒩(xi∣μk,Σk)∑jπj𝒩(xi∣μj,Σj)

를 책임값(responsibility)이라 한다. 군집 결과로는 arg⁡maxkγik를 쓰거나 확률 그대로 쓴다. 성분 수만 충분하면 GMM은 매끄러운 밀도를 원하는 정밀도로 근사할 수 있다.

로그 우도 ∑ilog⁡∑kπk𝒩(xi∣μk,Σk)는 로그 안에 합이 있어 닫힌 해가 없으므로 EM으로 최대화한다.

  • E 단계: 현재 매개변수로 모든 점의 책임값 γik를 계산한다.
  • M 단계: Nk=∑iγik로 두고 가중 최대 우도 추정을 한다.

πk=NkN,μk=1Nk∑iγikxi,Σk=1Nk∑iγik(xi−μk)(xi−μk)⊤

  • 로그 우도가 더 이상 늘지 않을 때까지 반복한다. 유도와 수렴 성질은 EM 알고리즘 문서 참고.

실무에서 신경 쓸 점은 다음과 같다.

  • 초기화: EM은 국소 최적해에 빠지므로 k-평균 결과로 초기화하고(scikit-learn 기본값 init_params="kmeans"), 여러 번 돌려(n_init) 우도가 가장 큰 결과를 고른다.
  • 퇴화: 한 성분의 평균이 데이터 점 하나에 붙고 공분산이 0으로 줄면 우도가 무한대로 발산한다. 공분산 대각에 작은 값을 더해(scikit-learn reg_covar=1e-6) 막는다. 차원이 높고 성분의 점이 적으면 공분산 행렬이 특이 행렬이 되기 쉽다.
  • 수치 안정성: 고차원에서는 밀도 값이 아주 작아지므로 로그 밀도와 logsumexp로 계산한다.
  • 척도: 공분산을 대각이나 구형으로 제한할 때는 특성 척도를 맞춰 두는 것이 좋다.

공분산을 어떻게 제한하느냐로 군집 모양과 매개변수 수가 정해진다. 이름은 scikit-learn의 covariance_type 인자를 따른다.

형태 공분산 군집 모양 공분산 매개변수 수
full 성분마다 임의의 공분산 Σk 방향과 크기가 제각각인 타원 K⋅d(d+1)/2
tied 모든 성분이 공분산 하나를 공유 같은 모양·방향의 타원 d(d+1)/2
diag 성분마다 대각 공분산 축에 나란한 타원 K⋅d
spherical 성분마다 σk2I 크기가 다른 원(구) K

여기에 평균 Kd개와 혼합 비율 K−1개가 더해진다. full은 가장 유연하지만 d가 커지면 매개변수가 d2으로 늘어 데이터가 많이 필요하다.

성분 수 고르기

편집 원본 편집
  • 정보 기준: 성분 수를 늘리면 우도는 계속 오르므로 매개변수 수로 벌점을 준다. 베이즈 정보 기준 BIC=−2log⁡L^+plog⁡N(p는 매개변수 수)이 가장 작은 모형을 고른다.[2] AIC는 벌점이 2p로 작아 더 많은 성분을 고르는 경향이 있다.
  • 베이즈 혼합 모형: 혼합 비율에 디리클레(또는 디리클레 과정) 사전 분포를 두고 변분 추론으로 학습하면, 필요 없는 성분의 가중치가 0 근처로 줄어 성분 수가 사실상 자동으로 정해진다. scikit-learn의 BayesianGaussianMixture가 이 방식이다.
  • 검증 데이터 로그 우도: 밀도 추정이 목적이면 따로 떼어 둔 데이터의 평균 로그 우도로 비교한다.

k-평균과 비교

편집 원본 편집

GMM에서 모든 성분의 공분산을 σ2I로 고정하고 혼합 비율을 같게 둔 뒤 σ→0으로 보내면 책임값이 0 또는 1이 되고, EM의 두 단계는 k-평균의 "가장 가까운 중심에 배정"과 "중심 다시 계산"이 된다. 즉 k-평균은 GMM의 하드 할당 극한이다.

구분 k-평균 GMM
할당 하드(한 군집에만) 소프트(군집별 확률)
군집 모양 중심에서의 유클리드 거리로 나뉘는 볼록 영역. 비슷한 크기의 둥근 군집에 맞다 공분산에 따라 기울어진 타원, 크기가 다른 군집도 표현
목적 함수 군집 내 제곱 거리 합 로그 우도
출력 중심, 레이블 평균, 공분산, 혼합 비율, 확률, 밀도
성분 수 선택 엘보 방법, 실루엣 계수 등 BIC·AIC, 베이즈 혼합 모형
비용 반복당 O(NKd) full 공분산이면 반복당 O(NKd2), 역행렬·행렬식 계산 추가
이상 탐지 중심까지 거리로 대신 로그 밀도로 직접

군집이 둥글고 크기가 비슷하며 데이터가 크면 k-평균이 빠르고 충분하다. 군집이 길쭉하거나 겹치거나, 소속 확률이나 밀도가 필요하면 GMM을 쓴다. 군집 모양이 정규 분포와 거리가 먼 경우(초승달, 고리 모양)는 둘 다 맞지 않고 DBSCAN이나 스펙트럴 군집이 낫다.

  • 연성 군집: 고객 세분화처럼 한 대상이 여러 집단의 성질을 함께 가질 수 있는 문제.
  • 밀도 추정과 이상 탐지: 정상 데이터로 GMM을 학습하고 로그 밀도가 낮은 점을 이상치로 본다.
  • 화자 인증: 많은 화자로 학습한 범용 배경 모델(UBM) GMM을 각 화자 데이터로 MAP 적응시키는 GMM-UBM 방식이 2000년대 화자 인증의 대표 방식이었다.[3]
  • 음성 인식: 은닉 마르코프 모델의 각 상태가 음향 특징을 내보내는 분포를 GMM으로 두는 GMM-HMM이 오랫동안 쓰였고, 2010년 무렵부터 이 GMM 자리를 심층 신경망이 대신했다.[4]
  • 영상 배경 분리: 픽셀마다 시간에 따른 밝기를 GMM으로 모델링하고 온라인으로 갱신해 움직이는 물체를 찾는 방법이 1999년 발표되어 널리 퍼졌다.[5] OpenCV의 MOG 계열 배경 차분기가 이 계통이다.
  • 생성과 사전 분포: 학습된 GMM에서 표본을 뽑아 데이터를 합성할 수 있다. 심층 생성 모델에서 잠재 공간의 사전 분포나 출력 분포로 GMM을 쓰기도 한다(생성 모델).

NumPy로 EM 구현

편집 원본 편집
import numpy as np
from scipy.stats import multivariate_normal

def gmm_em(X, K, n_iter=100, tol=1e-6, seed=0):
    n, d = X.shape
    rng = np.random.default_rng(seed)
    pi = np.full(K, 1 / K)
    mu = X[rng.choice(n, K, replace=False)]          # 무작위 데이터 점으로 초기화
    Sigma = np.array([np.cov(X.T) for _ in range(K)])
    prev = -np.inf
    for _ in range(n_iter):
        # E 단계: 책임값 gamma[i, k] = P(z_i = k | x_i)
        dens = np.column_stack([pi[k] * multivariate_normal.pdf(X, mu[k], Sigma[k])
                                for k in range(K)])
        ll = np.log(dens.sum(axis=1)).sum()           # 관측 데이터 로그 우도
        gamma = dens / dens.sum(axis=1, keepdims=True)
        # M 단계: 책임값으로 가중한 최대 우도 추정
        Nk = gamma.sum(axis=0)
        pi = Nk / n
        mu = (gamma.T @ X) / Nk[:, None]
        for k in range(K):
            D = X - mu[k]
            Sigma[k] = (gamma[:, k, None] * D).T @ D / Nk[k] + 1e-6 * np.eye(d)
        if ll - prev < tol:
            break
        prev = ll
    return pi, mu, Sigma, ll

rng = np.random.default_rng(1)
X = np.vstack([
    rng.multivariate_normal([0, 0], ((1.0, 0.8), (0.8, 1.0)), 300),
    rng.multivariate_normal([4, 0], ((0.3, 0.0), (0.0, 0.3)), 200),
])
pi, mu, Sigma, ll = gmm_em(X, K=2)
print("혼합 비율:", pi.round(3))
for k in range(2):
    print(f"성분 {k} 평균:", mu[k].round(2))
혼합 비율: [0.599 0.401]
성분 0 평균: [0.08 0.08]
성분 1 평균: [ 3.94 -0.01]

참값(혼합 비율 0.6, 0.4, 평균 (0, 0), (4, 0))에 가깝게 복원한다. 고차원에서는 pdf 대신 logpdf와 logsumexp로 바꿔야 밑넘침이 나지 않는다.

sklearn.mixture.GaussianMixture로 k-평균과 비교하고, BIC로 성분 수와 공분산 형태를 고르고, 밀도로 이상치를 판정하는 예다(scikit-learn 1.7에서 실행).

import numpy as np
from sklearn.datasets import make_blobs
from sklearn.mixture import GaussianMixture, BayesianGaussianMixture
from sklearn.cluster import KMeans
from sklearn.metrics import adjusted_rand_score

# 세 군집을 선형 변환으로 길쭉하게 늘인 데이터
X, y = make_blobs(n_samples=600, centers=3, random_state=170)
X = X @ np.array(((0.6, -0.6), (-0.4, 0.8)))

km = KMeans(n_clusters=3, n_init=10, random_state=0).fit(X)
gmm = GaussianMixture(n_components=3, covariance_type="full", random_state=0).fit(X)
print("k-평균 ARI:", round(adjusted_rand_score(y, km.labels_), 3))
print("GMM   ARI:", round(adjusted_rand_score(y, gmm.predict(X)), 3))

# BIC로 성분 수와 공분산 형태 고르기
best = min(
    ((k, cov, GaussianMixture(k, covariance_type=cov, random_state=0).fit(X).bic(X))
     for k in range(1, 7) for cov in ["full", "diag", "spherical", "tied"]),
    key=lambda t: t[2],
)
print("BIC 최소:", best[0], best[1])

# 밀도 기반 이상 탐지: 학습 데이터 로그 밀도의 하위 1%를 임계값으로
thr = np.percentile(gmm.score_samples(X), 1)
print("(10, 10)은 이상치인가:", gmm.score_samples(np.array((10.0, 10.0)).reshape(1, -1))[0] < thr)

# 베이즈 혼합: 성분을 넉넉히 10개 주어도 쓸모없는 성분의 가중치가 0 근처로 줄어든다
bgmm = BayesianGaussianMixture(n_components=10, weight_concentration_prior=0.01,
                               max_iter=500, random_state=0).fit(X)
print("가중치 0.05 넘는 성분 수:", int((bgmm.weights_ > 0.05).sum()))
k-평균 ARI: 0.58
GMM   ARI: 1.0
BIC 최소: 3 tied
(10, 10)은 이상치인가: True
가중치 0.05 넘는 성분 수: 3

수정 랜드 지수(ARI)는 1이면 정답 군집과 완전히 일치한다. 같은 선형 변환으로 늘인 군집이라 k-평균은 경계를 잘못 긋고, GMM은 기울어진 타원을 그대로 잡는다. 세 군집이 같은 모양이므로 BIC도 공분산을 공유하는 tied 형태를 고른다. predict_proba는 책임값, sample은 새 표본을 준다.

  • 각 군집이 정규 분포라는 가정이 맞지 않으면 군집 하나를 성분 여러 개로 쪼개 표현하게 되어 해석이 어려워진다.
  • 고차원에서는 full 공분산의 매개변수가 너무 많다. 차원을 줄이거나 diag 공분산을 쓰거나, 요인 분석형 혼합 모형을 쓴다.
  • 국소 최적해, 초깃값 의존성, 성분 수 선택 문제가 있다.
  • 이상치 하나가 공분산 추정을 크게 흔든다. 꼬리가 두꺼운 t 분포 혼합이 대안이다.
  1. ↑ Karl Pearson, 〈Contributions to the Mathematical Theory of Evolution〉, Philosophical Transactions of the Royal Society of London A 185, 1894. doi:10.1098/rsta.1894.0003
  2. ↑ Gideon Schwarz, 〈Estimating the Dimension of a Model〉, Annals of Statistics 6(2), 1978. doi:10.1214/aos/1176344136
  3. ↑ Douglas A. Reynolds, Thomas F. Quatieri, Robert B. Dunn, 〈Speaker Verification Using Adapted Gaussian Mixture Models〉, Digital Signal Processing 10(1~3), 2000. doi:10.1006/dspr.1999.0361
  4. ↑ Geoffrey Hinton 외, 〈Deep Neural Networks for Acoustic Modeling in Speech Recognition: The Shared Views of Four Research Groups〉, IEEE Signal Processing Magazine 29(6), 2012. doi:10.1109/MSP.2012.2205597
  5. ↑ Chris Stauffer, W. E. L. Grimson, 〈Adaptive background mixture models for real-time tracking〉, CVPR 1999. doi:10.1109/CVPR.1999.784637