7 · 혼합 모델과 EM
관측 변수와 잠재 변수의 결합 분포를 정의하면, 복잡한 관측 변수의 주변 분포를 상대적으로 더 다루기 쉬운 확장 공간(관측+잠재)의 결합 분포로 표현할 수 있다.

7.1 평균 집단화
차원 데이터 을 개 집단으로 나누는 문제에서, 각 집단의 원형prototype(중심) 와 이진 표시 변수 (원 핫)을 도입한다. 목표는 각 점에서 가장 가까운 중심까지의 거리 제곱합인 뒤틀림 척도distortion measure를 최소화하는 것이다.
를 두 단계로 번갈아 최소화한다. E 단계에서 를 고정하고 각 점을 가장 가까운 중심에 할당하고( if ), M 단계에서 를 고정하고 중심을 갱신한다.
즉 집단









그림 7.1 · (a) 데이터와 두 초기 중심(적·청
실습 ·
E 단계(가까운 중심에 할당)와 M 단계(중심을 집단 평균으로 갱신)를 번갈아 반복하며, 뒤틀림 척도
import numpy as np
rng = np.random.default_rng(0)
X = np.vstack([rng.normal([0,0], 0.6, (80,2)),
rng.normal([3,3], 0.6, (80,2)),
rng.normal([0,3], 0.6, (80,2))])
K = 3; mu = X[rng.choice(len(X), K, replace=False)].copy()
prev = None
for it in range(12):
d = ((X[:,None,:]-mu[None,:,:])**2).sum(2); r = d.argmin(1) # E: 할당
J = d[np.arange(len(X)), r].sum()
for k in range(K):
if (r==k).any(): mu[k] = X[r==k].mean(0) # M: 중심 갱신
print(f"반복 {it+1}: J={J:.2f}")
if prev is not None and abs(J-prev) < 1e-9: break
prev = J7.2 혼합 가우시안
- 가우시안 혼합은 가우시안들의 선형 중첩이다.
- 원 핫 잠재 변수
( , )를 도입해 , 즉 로 두고, 조건부 분포를 로 두면, 를 주변화해 (9.7)을 얻는다. 가 주어졌을 때 성분 의 사후 확률(책임responsibility)은



그림 7.2 · (a) 결합 분포
7.2.1 최대 가능도 방법
- 로그 가능도는 다음과 같다.
- 최대 가능도는 특이점singularity 문제를 갖는다. 어떤 성분이 한 데이터 포인트로 붕괴하면
이 되어 로그 가능도가 무한대로 발산한다.


그림 7.3·7.4 · (왼쪽)
7.2.2 가우시안 혼합 분포에 대한 EM
- (9.14)를
, , 에 대해 미분해 으로 두면(마지막은 제약에 라그랑주 승수) 다음을 얻는다( 는 성분 의 유효 데이터 수).
- 이 식들은 책임값
가 매개변수에 복잡하게 의존하므로 닫힌 형태의 해가 아니다. 대신 E 단계(현재 매개변수로 책임값 계산)와 M 단계(책임값으로 매개변수 재추정)를 번갈아 반복한다. 보통 -평균으로 초기화한 뒤 EM을 적용한다.
가우시안 혼합 분포에 대한 EM 알고리즘
평균
, 공분산 , 혼합 계수 를 초기화하고 로그 가능도를 계산한다.E 단계 — 책임값을 계산한다.
- M 단계 — 매개변수를 재추정한다(
).
- 로그 가능도를 계산하고, 수렴하지 않았으면 2단계로 돌아간다.






그림 7.5 · (a) 초기 두 성분. (b) 최초 E 단계 후 책임값으로 색칠. (c) 최초 M 단계 후 평균·공분산 갱신. (d)~(f) 2·5·20단계 후, (f)에서 거의 수렴.
실습 · 가우시안 혼합 EM
E 단계(책임값
import numpy as np
def gauss(X, mu, S):
D = X.shape[1]; d = X-mu; Si = np.linalg.inv(S)
return np.exp(-0.5*np.einsum('ni,ij,nj->n', d, Si, d))/np.sqrt((2*np.pi)**D*np.linalg.det(S))
rng = np.random.default_rng(1)
X = np.vstack([rng.multivariate_normal([0,0], [[0.5,0],[0,0.5]], 120),
rng.multivariate_normal([3,3], [[0.6,0.3],[0.3,0.6]], 120)])
K = 2; N = len(X)
mu = X[rng.choice(N, K, replace=False)].copy(); S = [np.eye(2) for _ in range(K)]; pi = np.ones(K)/K
prev = -np.inf
for it in range(40):
R = np.stack([pi[k]*gauss(X, mu[k], S[k]) for k in range(K)], 1) # E 단계
ll = np.log(R.sum(1)).sum(); R = R/R.sum(1, keepdims=True)
Nk = R.sum(0)
for k in range(K): # M 단계
mu[k] = (R[:,k,None]*X).sum(0)/Nk[k]
d = X-mu[k]; S[k] = (R[:,k,None,None]*np.einsum('ni,nj->nij', d, d)).sum(0)/Nk[k]
pi = Nk/N
if it < 4 or it % 6 == 0: print(f"반복 {it+1:2d}: 로그가능도={ll:.2f}")
if ll-prev < 1e-6: print(f"수렴 (반복 {it+1})"); break
prev = ll
print("추정 π:", np.round(pi,3).tolist(), " 평균:", np.round(mu,2).tolist())7.3 EM에 대한 다른 관점
- 관측 데이터
, 잠재 변수 , 매개변수 에 대해 로그 가능도는 이다. 를 완전한complete 데이터, 만을 불완전한incomplete 데이터라 한다. 잠재 변수에 대한 지식은 사후 분포 로만 주어진다.
일반적인 EM 알고리즘
결합 분포
를 초기화한다.E 단계 — 사후 분포
를 계산한다.M 단계 — 완전 데이터 로그 가능도의 기댓값을 최대화한다.
- 수렴하지 않으면
로 두고 2단계로 돌아간다.
7.3.1 베르누이 분포들의 혼합
- 이진 변수들에 대한 베르누이 혼합(잠재 클래스 분석latent class analysis)을 생각하자. 각 성분은
이고 혼합은 이다. 책임값과 M 단계는
가우시안 혼합과 달리
실습 · 베르누이 혼합 EM (이진 데이터 군집화)
두 이진 원형 패턴에서 생성한 잡음 섞인 데이터를 베르누이 혼합 EM으로 군집화하면, 각 성분의
import numpy as np
rng = np.random.default_rng(2)
proto = np.array([[1,1,1,0,0,0], [0,0,0,1,1,1]], float) # 두 원형 패턴
X = np.vstack([(rng.uniform(size=(60,6)) < 0.85*proto[0]+0.05).astype(float),
(rng.uniform(size=(60,6)) < 0.85*proto[1]+0.05).astype(float)])
K = 2; N, D = X.shape
mu = rng.uniform(0.25, 0.75, (K, D)); pi = np.ones(K)/K; prev = -np.inf
def bern(X, mu): # p(x|μ_k), (N,K)
return np.prod(mu**X[:,None,:]*(1-mu)**(1-X[:,None,:]), 2)
for it in range(60):
P = pi*bern(X, mu); ll = np.log(P.sum(1)).sum(); R = P/P.sum(1, keepdims=True) # E
Nk = R.sum(0); mu = (R.T@X)/Nk[:,None]; pi = Nk/N # M
if ll-prev < 1e-7: break
prev = ll
print(f"수렴 (반복 {it+1}), 로그가능도={ll:.2f}")
print("성분1 μ:", np.round(mu[0], 2).tolist())
print("성분2 μ:", np.round(mu[1], 2).tolist())7.3.2 베이지안 선형 회귀에 대한 EM
- 베이지안 선형 회귀에서 가중치
를 잠재 변수로 보면 초매개변수 , 를 EM으로 추정할 수 있다. 의 사후 분포에 대한 완전 데이터 로그 가능도의 기댓값을 에 대해 최대화하면
7.4 일반적 EM 알고리즘
- 잠재 변수 분포
를 도입하면 로그 가능도가 다음처럼 분해된다.
이므로 는 로그 가능도의 하한이다. EM은 이 하한을 두 단계로 올린다. E 단계에서 를 고정하고 로 두면 이 되어 하한이 로그 가능도와 같아진다. M 단계에서 를 고정하고 를 에 대해 최대화하면, 하한이 오르고 로그 가능도도 최소한 그만큼 오른다(이때 가 새 사후 분포와 달라져 ).



그림 7.6~7.8 · (왼쪽)
실습 · ELBO 분해
가우시안 혼합의 한 스냅샷에서, 임의의
import numpy as np
def gauss(X, mu, S):
D = X.shape[1]; d = X-mu; Si = np.linalg.inv(S)
return np.exp(-0.5*np.einsum('ni,ij,nj->n', d, Si, d))/np.sqrt((2*np.pi)**D*np.linalg.det(S))
rng = np.random.default_rng(3)
X = np.vstack([rng.multivariate_normal([0,0], np.eye(2)*0.4, 50),
rng.multivariate_normal([2.5,2.5], np.eye(2)*0.4, 50)])
K = 2; mu = np.array([[0.5,0.0],[2.0,2.0]]); S = [np.eye(2)*0.5]*2; pi = np.array([0.5,0.5])
px = np.stack([pi[k]*gauss(X, mu[k], S[k]) for k in range(K)], 1)
lnpX = np.log(px.sum(1)).sum()
post = px/px.sum(1, keepdims=True) # 사후 분포
def elbo(q):
L = KL = 0.0
for k in range(K):
m = q[:,k] > 1e-12
L += np.sum(q[m,k]*(np.log(px[m,k]) - np.log(q[m,k])))
KL += np.sum(q[m,k]*(np.log(q[m,k]) - np.log(post[m,k])))
return L, KL
Lb, KLb = elbo(np.full_like(post, 0.5)) # 임의의 q
Lg, KLg = elbo(post) # q = 사후 분포
print(f"ln p(X) = {lnpX:.3f}")
print(f"임의 q : L={Lb:.3f}, KL={KLb:.3f}, L+KL={Lb+KLb:.3f}")
print(f"q=사후 : L={Lg:.3f}, KL={KLg:.3f}, L+KL={Lg+KLg:.3f} (KL≈0 → L=ln p(X))")연습문제
문제 7.1
잠재 변수의 주변 분포가 (9.10)
풀이
(
이는 정확히 (9.7)의 가우시안 혼합이다.
문제 7.2
혼합 밀도
풀이
조건부 분포의 정의에서
성분 내에서
따라서
를 갖는 혼합 분포다.