결합·주변·조건부분포와 독립성
먼저 읽을 장 11.1 확률공간과 확률변수
과학의 질문은 거의 언제나 둘 이상의 변수 사이의 관계에 대한 것이다. 이 유전자 변이가 있으면 질병 위험이 얼마나 달라지는가. 경보가 울렸을 때 실제로 고장일 확률은 얼마인가. 구름이 짙으면 비가 올 확률은 얼마인가. 모두 "다른 것을 알았을 때의 확률", 즉 조건부확률에 대한 질문이다. 머신러닝 모델도 대부분 조건부분포 를 학습한다(12.3장).
이 장은 여러 확률변수를 함께 다루는 세 가지 분포, 즉 결합·주변·조건부분포를 오가는 계산 규칙(합의 법칙과 곱의 법칙)을 세운다. 그 두 규칙만으로 베이즈 정리가 나온다. 이어서 독립과 조건부 독립을 구분하고, 변수들 사이의 조건부 독립 구조를 그래프로 그리는 확률 그래프 모델의 기초를 다룬다. 직관이 가장 자주 틀리는 영역이므로 모든 계산을 시뮬레이션으로 한 번 더 확인한다.
학습 목표
- 결합·주변·조건부분포를 오가며 계산한다
- 베이즈 정리를 사용한다
- 조건부 독립을 그래프로 표현한다
결합과 주변화
두 확률변수 를 함께 보는 분포를 결합분포(joint distribution) 라 한다. 이산이면 이고, 값들의 표로 적을 수 있다. 연속이면 결합밀도이고, 영역 의 확률은 다. 11.1장에서 본 것처럼 와 는 같은 표본공간 위의 두 함수이므로, 결합분포는 "같은 실험 결과에서 두 값이 함께 어떻게 나오는가"를 적은 것이다.
결합분포에서 한 변수만의 분포, 즉 주변분포(marginal distribution)를 얻으려면 다른 변수에 대해 모두 더한다.
이것이 합의 법칙(sum rule)이고, 이 연산을 주변화(marginalization)라 한다. 표로 보면 행 또는 열의 합이다. "주변"이라는 이름은 표의 가장자리(margin)에 합을 적던 관습에서 왔다.
주변화는 확률 모델에서 가장 자주 하는 계산 중 하나다. 관측하지 않은 변수(잠재변수, 결측값, 귀찮은 파라미터)가 있으면 그 변수에 대해 주변화해 관측한 변수의 분포를 얻는다. 예를 들어 잠재변수 를 가진 생성모델의 데이터 분포는 이고, 이 적분이 어렵다는 것이 VAE(19.3장)와 변분추론(13.6장)의 출발점이다.
조건부분포
라는 것을 알았을 때 의 분포가 조건부분포(conditional distribution)다.
표로 보면 인 행만 남기고, 그 행의 합이 1이 되도록 다시 나누는 것이다. 조건을 안다는 것은 표본공간을 그 조건이 성립하는 부분으로 좁히는 것이고, 좁힌 공간에서 확률을 다시 정규화한 것이 조건부확률이다. 정의를 뒤집으면 곱의 법칙(product rule)이 된다.
변수가 여럿이면 곱의 법칙을 반복해 연쇄 법칙(chain rule) 을 얻는다. 언어모델이 문장의 확률을 다음 토큰의 조건부확률의 곱으로 계산하는 것이 이 식이다(19.2장의 자기회귀 모델).
합의 법칙과 곱의 법칙을 합치면 전확률 법칙(law of total probability) 가 된다. 조건부분포들을 조건의 확률로 가중평균하면 주변분포가 된다는 뜻이다.
조건부 기댓값. 조건부분포의 평균 를 조건부 기댓값이라 한다. 마다 값이 정해지므로 의 함수이고, 를 넣은 는 그 자체로 확률변수다. 전확률 법칙의 기댓값 버전인 반복 기댓값의 법칙(law of iterated expectations)
이 성립한다. 제곱손실로 학습한 회귀 모델은 이 를 추정한다(12.3장).
구름 양 (맑음·구름 조금·흐림)와 비 (안 옴·옴)의 가상 결합분포로 계산해 본다.
import numpy as np
# 행: 구름 X (0 맑음, 1 구름 조금, 2 흐림), 열: 비 Y (0 안 옴, 1 옴)
P = np.array([[0.40, 0.01],
[0.25, 0.04],
[0.15, 0.15]]) # (3, 2) 결합분포 p(x, y)
p_x = P.sum(axis=1) # (3,) 주변화: y에 대해 합
p_y = P.sum(axis=0) # (2,)
p_y_given_x = P / p_x[:, None] # (3, 2) 각 행을 p(x)로 나눈다
print("p(x) =", p_x, " p(y) =", p_y)
print("p(비 | x) =", np.round(p_y_given_x[:, 1], 3))
print("곱의 법칙 p(y|x) p(x) == p(x, y):", np.allclose(p_y_given_x * p_x[:, None], P))
print("E[Y] =", p_y[1], " sum_x E[Y|x] p(x) =", round(float(p_y_given_x[:, 1] @ p_x), 3))p(x) = [0.41 0.29 0.3 ] p(y) = [0.8 0.2] p(비 | x) = [0.024 0.138 0.5 ] 곱의 법칙 p(y|x) p(x) == p(x, y): True E[Y] = 0.2 sum_x E[Y|x] p(x) = 0.2
비가 올 주변확률은 0.2지만, 하늘이 흐리면 0.5, 맑으면 0.024다. 흐린 날의 결합확률 와 조건부확률 는 다른 양이다. 앞의 것은 "흐리고 비가 오는 날이 전체의 15%"이고, 뒤의 것은 "흐린 날 중 절반에 비가 온다"이다. 가 0/1이므로 이고, 이를 로 가중평균하면 주변확률 0.2가 그대로 나온다.
베이즈 정리
곱의 법칙의 두 표현 를 정리하면 조건의 방향을 뒤집는 식이 나온다.
이것이 베이즈 정리(Bayes' theorem)다. 분모는 전확률 법칙으로 계산한 정규화 상수다. 원인 에서 관측 가 나오는 과정 는 알기 쉽지만(물리 모델, 검사의 성능), 연구에서 알고 싶은 것은 관측을 보고 원인을 추론하는 다. 베이즈 정리가 이 둘을 잇는다.
파라미터 와 데이터 에 적용하면 각 항에 이름이 붙는다.
사전분포(prior)는 데이터를 보기 전의 믿음, 우도(likelihood, 12.2장)는 각 가 데이터를 얼마나 잘 설명하는지, 사후분포(posterior)는 데이터를 본 뒤의 믿음이다. 이 틀 전체를 13장에서 다룬다. 여기서는 가 두 값(병/정상, 고장/정상)뿐인 가장 단순한 경우로 감을 잡는다.
기저율 무시. 유병률이 1/1000인 질환을, 환자를 99% 잡아내고(민감도) 정상인의 5%를 양성으로 잘못 판정하는(위양성률) 검사로 진단한다. 양성이 나온 사람이 실제로 환자일 확률은 얼마일까. 베이즈 정리로 계산하고, 200만 명을 시뮬레이션해 센 비율과 비교한다.
prev, sens, fpr = 0.001, 0.99, 0.05 # 유병률, 민감도 P(+|병), 위양성률 P(+|정상)
post = sens * prev / (sens * prev + fpr * (1 - prev))
print(f"베이즈 정리 P(병 | +) = {post:.4f}")
rng = np.random.default_rng(0)
n = 2_000_000
sick = rng.random(n) < prev # (n,) 실제 상태
positive = np.where(sick, rng.random(n) < sens, rng.random(n) < fpr) # (n,) 검사 결과
print(f"시뮬레이션 양성 {positive.sum():,}명 중 환자 {(sick & positive).sum():,}명"
f" → {np.mean(sick[positive]):.4f}")
print(f"P(+ | 병) = {np.mean(positive[sick]):.3f} vs P(병 | +) = {np.mean(sick[positive]):.3f}")베이즈 정리 P(병 | +) = 0.0194 시뮬레이션 양성 102,459명 중 환자 1,991명 → 0.0194 P(+ | 병) = 0.993 vs P(병 | +) = 0.019
양성인 사람 중 실제 환자는 2%가 채 안 된다. 200만 명 중 환자는 약 2000명이고 거의 모두 양성이 나오지만, 정상인 약 200만 명의 5%인 10만 명이 위양성이기 때문이다. 와 는 조건의 방향만 다른 두 확률인데 50배 가까이 차이가 난다. 사전확률(기저율)을 무시하고 검사 성능만 보고 판단하는 오류를 기저율 무시(base rate neglect)라 한다.
이 문제는 Casscells 등(1978)이 하버드 의대 부속 병원의 의사와 의대생에게 던진 질문으로 유명하다(유병률 1/1000, 위양성률 5%, 정답은 약 2%). 가장 흔한 답은 95%였다. 확률 대신 자연 빈도("1000명 중 1명이 환자이고, 나머지 999명 중 약 50명이 위양성")로 다시 쓰면 같은 계산이 훨씬 덜 헷갈린다. 위 시뮬레이션이 바로 그 계산이다.
독립과 조건부 독립
독립. 두 확률변수가 독립(independent)이라는 것은 한쪽을 알아도 다른 쪽의 분포가 바뀌지 않는다는 것이다.
결합분포 표가 주변분포 두 개의 바깥곱이 된다는 뜻이다. 앞 절의 구름–비 표는 가 행마다 다르므로 독립이 아니다.
상관과 독립. 상관계수(correlation coefficient)는 두 변수의 선형 관계의 세기만 잰다. 독립이면 상관이 0이지만, 상관이 0이라고 독립은 아니다. , 이면 는 로 완전히 정해지는데도 상관이 0이다. 독립성을 판단하려면 조건부분포가 조건에 따라 달라지는지를 봐야 한다. 실제 데이터에서 독립성을 검정할 때도 같은 직관을 쓴다. 의 구간마다 의 분포(히스토그램, 분위수)를 비교하고, 차이가 표본 잡음보다 큰지를 본다.
조건부 독립. 제3의 변수 를 알 때 와 가 독립이면 조건부 독립(conditional independence)이라 한다.
독립과 조건부 독립은 서로를 함의하지 않는다. 대표적인 경우가 공통 원인이다. 장비 고장이 센서 A와 센서 B의 경보를 함께 일으키면, 두 경보는 서로 상관이 있다(한쪽이 울리면 고장일 가능성이 높아지고, 그러면 다른 쪽도 울리기 쉽다). 그러나 고장 여부를 알면 두 센서는 각자의 잡음으로만 울리므로 독립이 된다.
x = rng.uniform(-1, 1, size=200_000) # (N,)
y = x**2 # y는 x로 완전히 정해진다
print(f"corr(X, Y) = {np.corrcoef(x, y)[0, 1]:+.3f}")
print(f"P(Y > 0.5) = {np.mean(y > 0.5):.3f} P(Y > 0.5 | |X| > 0.8) = {np.mean(y[np.abs(x) > 0.8] > 0.5):.3f}")
fault = rng.random(200_000) < 0.1 # 공통 원인: 장비 고장
a = rng.random(200_000) < np.where(fault, 0.9, 0.05) # 센서 A 경보
b = rng.random(200_000) < np.where(fault, 0.8, 0.05) # 센서 B 경보
print(f"전체: corr(A, B) = {np.corrcoef(a, b)[0, 1]:+.3f}")
for f in (False, True):
m = fault == f
print(f"고장 = {f!s:5}: corr(A, B) = {np.corrcoef(a[m], b[m])[0, 1]:+.3f}")corr(X, Y) = -0.000 P(Y > 0.5) = 0.292 P(Y > 0.5 | |X| > 0.8) = 1.000 전체: corr(A, B) = +0.511 고장 = False: corr(A, B) = +0.001 고장 = True : corr(A, B) = +0.017
는 상관이 0이지만, 을 알면 가 확실해진다(0.29 → 1). 두 센서의 경보는 전체로 보면 상관계수가 0.51이지만, 고장 여부로 나누어 보면 두 그룹 모두에서 상관이 0 근처다. 고장인 그룹의 0.017은 표본이 약 2만 개로 적어서 생긴 잡음 수준이다. 경보 사이의 상관은 전부 공통 원인에서 왔다.
연구에서의 의미. 조건부 독립은 모델을 단순하게 만드는 핵심 가정이다. 센서 개가 고장 여부에 대해 조건부 독립이면 이므로, 개 조합의 확률표 대신 센서마다 숫자 두 개만 있으면 된다. 나이브 베이즈 분류기가 바로 이 가정이다. 반대로 이 가정이 틀리면 같은 증거를 여러 번 센 셈이 되어 사후확률이 지나치게 확신에 찬다. 같은 원인으로 함께 고장 나는 센서, 같은 시료에서 나온 반복 측정이 대표적이다(실습에서 확인한다).
확률 그래프 모델 입문
변수가 많아지면 결합분포를 표로 적을 수 없다. 이진 변수 30개의 결합분포는 개, 약 10억 개의 숫자가 필요하다. 그러나 실제 문제에서는 대부분의 변수가 몇몇 다른 변수에만 직접 의존한다. 이 구조를 그래프로 적은 것이 확률 그래프 모델(probabilistic graphical model)이다.
베이지안 네트워크. 방향 비순환 그래프(DAG)에서 각 노드는 확률변수이고, 화살표 는 "의 분포가 에 직접 의존한다"는 뜻이다. 결합분포는 각 노드의 부모가 주어졌을 때의 조건부분포의 곱으로 인수분해된다.
는 의 부모 노드들이다. 연쇄 법칙은 어떤 분포에도 성립하는 완전한 인수분해이고, 그래프에서 빠진 화살표가 곧 조건부 독립 가정이다. 이진 변수 30개가 각각 부모를 최대 3개 가지면 필요한 숫자는 개 이하로 준다.
세 가지 기본 구조. 변수 세 개의 연결 방식은 세 가지뿐이고, 각각 조건부 독립의 성질이 다르다.
| 구조 | 그래프 | 인수분해 | 를 모를 때 | 를 알 때 |
|---|---|---|---|---|
| 사슬(chain) | , 의존 | |||
| 공통 원인(fork) | , 의존 | |||
| 공통 결과(collider) | , 의존 |
사슬과 공통 원인에서는 를 알면 경로가 막힌다. 앞 절의 두 센서가 공통 원인의 예다. collider는 반대다. 서로 독립인 두 원인이 같은 결과를 낼 때, 결과를 조건으로 하면 두 원인이 의존하게 된다. 압력 경보가 누설 또는 센서 drift 때문에 울린다고 하자.
n = 500_000
leak = rng.random(n) < 0.05 # 원인 1: 누설
drift = rng.random(n) < 0.10 # 원인 2: 센서 drift (누설과 독립)
alarm = leak | drift # 결과: 압력 경보
print(f"P(누설) = {leak.mean():.3f}")
print(f"P(누설 | drift) = {leak[drift].mean():.3f} (조건 없음: 독립)")
print(f"P(누설 | 경보) = {leak[alarm].mean():.3f}")
print(f"P(누설 | 경보, drift) = {leak[alarm & drift].mean():.3f} (경보를 조건으로: drift가 누설을 '설명해 버린다')")P(누설) = 0.050 P(누설 | drift) = 0.050 (조건 없음: 독립) P(누설 | 경보) = 0.344 P(누설 | 경보, drift) = 0.050 (경보를 조건으로: drift가 누설을 '설명해 버린다')
조건이 없을 때는 drift를 알아도 누설 확률이 0.05 그대로다. 경보가 울렸다는 것을 알면 누설 확률은 0.344로 오른다. 그런데 drift까지 확인되면 0.050으로 다시 떨어진다. 경보가 drift로 이미 설명되었으므로 누설에 대한 증거가 사라진 것이다. 이를 explaining away라 한다. 경보를 조건으로 하자 두 원인이 의존하게 되었다.
d-separation의 직관. 큰 그래프에서도 판단 방법은 같다. 에서 로 가는 모든 경로(화살표 방향 무시)를 따라가며, 조건으로 둔 변수 집합 에 대해 각 경로가 막혔는지 본다. 경로 위의 사슬·공통 원인 노드가 에 있으면 막히고, collider는 그 노드나 그 후손이 에 없으면 막힌다. 모든 경로가 막혀 있으면 와 는 가 주어졌을 때 조건부 독립이고, 이를 d-separation이라 한다. 형식적인 정의와 증명은 Bishop(2006) 8장을 참고한다.
연구에서의 의미. collider를 조건으로 하는 일은 데이터 수집 단계에서 자주 일어난다. "경보가 울린 사례만 분석한다", "입원한 환자만 연구한다", "출판된 논문만 모은다"는 모두 결과를 조건으로 하는 선택이고, 원래 독립인 원인들 사이에 가짜 상관을 만든다. 이것이 선택 편향(selection bias)의 한 형태다. 반대로 공통 원인을 조건으로 하지 않으면 원인 사이에 가짜 상관이 남는데, 이것이 교란(confounding)이다. 인과추론은 16.8장에서 다룬다.
실습
진단 검사 예제에서 기저율에 따라 양성 예측도 가 어떻게 바뀌는지, 그리고 재검사를 할 때 "두 검사는 병의 유무에 대해 조건부 독립"이라는 가정이 깨지면 무엇이 달라지는지를 계산과 시뮬레이션으로 비교한다. 코드는 labs/probability/conditional-probability/base_rate_table.py다. 위양성 중 절반은 그 사람의 특성(예: 검사를 교란하는 체질) 때문이라 재검사에서도 양성이 나온다고 가정한 경우(sim persist)를 함께 계산한다. 집필 시점의 실행 결과는 다음과 같다(시드 0, 각 400만 명).
sens 0.99, FPR 0.05, persist 0.5, N = 4,000,000, seed 0
P(sick | positive): formula vs simulation
prev | 1 test sim | 2 tests indep sim indep sim persist
0.001 | 0.0194 0.0195 | 0.2818 0.2853 0.0360
0.010 | 0.1667 0.1680 | 0.7984 0.8023 0.2762
0.100 | 0.6875 0.6872 | 0.9776 0.9775 0.8056
0.500 | 0.9519 0.9519 | 0.9975 0.9975 0.9739
- 같은 검사라도 기저율에 따라 양성의 의미가 다르다. 유병률 50%에서는 양성의 95%가 환자지만, 0.1%에서는 2%다. 검사 성능은 그대로인데 결과의 해석이 바뀐다.
- 조건부 독립이면 재검사가 강력하다. 두 번 모두 양성이면 우도비가 제곱되어, 유병률 1%에서 사후확률이 17%에서 80%로 오른다.
- 조건부 독립이 깨지면 재검사의 효과가 크게 줄어든다. 위양성의 절반이 반복되면 같은 상황의 사후확률은 28%에 그친다. 독립을 가정한 공식(80%)을 그대로 쓰면 크게 과신한다. 모델의 조건부 독립 가정은 계산의 편의가 아니라 데이터 생성 과정에 대한 주장이다.
이어서 센서 고장 예제를 연습문제로 푼다. 여러 센서의 경보와 침묵을 조건부 독립 가정으로 결합해 고장의 사후확률을 계산한다.
실패 사례
- 와 를 혼동하는 것. "환자의 99%가 양성"과 "양성의 99%가 환자"는 다르다. 분류기의 재현율과 정밀도, 법정의 "무죄인 사람에게서 이 증거가 나올 확률"과 "이 증거가 있을 때 무죄일 확률"도 같은 혼동이다. 조건부확률을 쓸 때는 무엇이 조건인지 문장으로 다시 읽는다.
- 상관이 없으면 독립이라고 단정하는 것. 상관계수는 선형 관계만 본다. 비선형 의존성은 조건부분포를 비교하거나, 상호정보량(14.2장) 같은 비선형 의존성 척도로 확인한다.
- 조건부 독립을 확인하지 않고 증거를 곱하는 것. 같은 원인을 공유하는 증거(같은 시료의 반복 측정, 같은 데이터로 학습한 여러 모델)를 독립인 것처럼 곱하면 사후확률이 과신된다.
- 결과를 조건으로 데이터를 고르는 것. 결과(경보, 입원, 출판)로 걸러낸 데이터에서는 독립인 원인들 사이에도 상관이 생긴다(collider bias).
통과 기준
- 결합분포 표나 식에서 주변분포·조건부분포·조건부 기댓값을 계산하고, 시뮬레이션으로 센 비율과 비교해 확인한다.
- 진단 검사 예제에서 기저율을 바꿔 가며 사후확률을 베이즈 정리로 계산하고, 자연 빈도로 설명한다.
- 주어진 베이지안 네트워크의 결합분포를 인수분해하고, 사슬·공통 원인·collider 구조에서 조건부 독립 관계를 읽어낸다.
- 연습문제
sensor-fault-posterior의 테스트를 통과한다.
참고 문헌
- Casscells, Schoenberger, Graboys (1978). Interpretation by physicians of clinical laboratory results. New England Journal of Medicine, 299(18), 999–1001. — 기저율 무시를 보여 준 진단 검사 문항(유병률 1/1000, 위양성률 5%)
- Bishop (2006). Pattern Recognition and Machine Learning. Springer. — 8장(그래프 모델): 베이지안 네트워크와 d-separation