[CS300 #013] 확률 기초 — 조건부 확률과 베이즈 정리
컴퓨터공학 300 주제 시리즈의 013번째 글이다. 전체 지도는 여기.
한 줄 요약
조건부 확률은 “B 가 일어났다는 정보를 알 때 A 의 확률” 이고, 베이즈 정리는 그 방향을 뒤집어 “증거를 봤을 때 원인의 확률” 을 계산하게 해 준다. 기저율을 무시하면 정확도 99% 검사의 양성 결과도 대부분 오경보일 수 있다.
왜 필요한가
알림 시스템이 “이상 징후” 를 보냈을 때 실제 장애일 확률은 얼마인가? 스팸 필터가 어떤 단어를 봤을 때 그 메일이 스팸일 확률은? 테스트가 실패했을 때 진짜 버그일 확률은? 모두 조건부 확률 문제이고, 답은 직관과 크게 다를 때가 많다.
확률은 무작위 알고리즘(퀵정렬의 피벗, 해시 함수, 블룸 필터), 분산 시스템의 장애 모델, 머신러닝 전반의 언어이기도 하다. 이 글은 그 출발점인 확률 공간, 조건부 확률, 독립, 베이즈 정리를 다룬다.
(이 글에서 조건부 확률은 P(A ∣ B) 로 쓴다. 가운데 기호는 “주어졌을 때” 로 읽는다.)
핵심 개념
확률 공간
- 표본 공간 S: 일어날 수 있는 모든 결과의 집합. 주사위 하나면 {1, 2, 3, 4, 5, 6}.
- 사건: 표본 공간의 부분집합. “짝수가 나온다” = {2, 4, 6}.
- 확률 P: 사건에 0 이상 1 이하의 수를 주는 함수로, 다음 공리를 만족한다.
- 모든 사건 A 에 대해 P(A) ≥ 0
- P(S) = 1
- 서로 겹치지 않는 사건들에 대해 P(A ∪ B ∪ …) = P(A) + P(B) + …
공리에서 바로 나오는 성질은 다음과 같다.
- P(Aᶜ) = 1 − P(A) (여사건)
- P(A ∪ B) = P(A) + P(B) − P(A ∩ B) (007번 글의 포함-배제와 같은 모양)
- 결과가 모두 같은 확률이면 P(A) = n(A) / n(S). 이때 확률 계산은 경우의 수 세기다.
조건부 확률
B 가 일어났다는 것을 알면 표본 공간이 B 로 줄어든다. 그 안에서 A 가 차지하는 비율이 조건부 확률이다.
P(A ∣ B) = P(A ∩ B) / P(B) (P(B) > 0)
주사위에서 “짝수가 나왔다” 는 것을 알 때 “4 이상” 일 확률은 P({4, 6}) / P({2, 4, 6}) = (2/6) / (3/6) = 2/3 이다. 정보가 없을 때의 1/2 보다 크다.
식을 바꾸면 곱셈 법칙 P(A ∩ B) = P(B)·P(A ∣ B) 가 된다. 여러 단계로 일어나는 사건의 확률을 단계별 조건부 확률의 곱으로 계산할 때 쓴다.
주의: P(A ∣ B) 와 P(B ∣ A) 는 다르다. “장애가 나면 알림이 울린다” 의 확률이 99% 라도, “알림이 울리면 장애다” 의 확률은 훨씬 낮을 수 있다. 이 혼동을 “조건 뒤집기 오류” 라 부르며, 아래 베이즈 정리가 정확한 관계를 준다.
전체 확률의 법칙
B₁, …, Bₖ 가 표본 공간을 겹치지 않게 나누면(분할이면)
P(A) = P(A ∣ B₁)P(B₁) + P(A ∣ B₂)P(B₂) + ... + P(A ∣ Bₖ)P(Bₖ)
“경우를 나눠 계산하고 가중 평균한다” 는 뜻이다.
독립
P(A ∩ B) = P(A)·P(B) 이면 A 와 B 는 독립이다. P(B) > 0 이면 이는 P(A ∣ B) = P(A), 즉 B 를 알아도 A 에 대한 정보가 없다는 뜻이다.
독립은 서로소(겹치지 않음)와 다르다. 확률이 양수인 두 사건이 서로소면 하나가 일어나면 다른 하나는 반드시 안 일어나므로 강하게 종속이다.
독립을 함부로 가정하면 위험하다. “디스크 하나가 1 년 안에 고장 날 확률이 p 면, 두 디스크가 모두 고장 날 확률은 p²” 이라는 계산은 두 고장이 독립일 때만 맞다. 같은 배치에서 생산되어 같은 랙에서 같은 열과 진동을 받는 디스크는 함께 고장 나는 경향이 있다.
베이즈 정리
곱셈 법칙을 양쪽으로 쓰면 P(A ∣ B)P(B) = P(B ∣ A)P(A) 이므로
P(B ∣ A) · P(A)
P(A ∣ B) = ------------------
P(B)
분모 P(B) 는 전체 확률의 법칙으로 P(B ∣ A)P(A) + P(B ∣ Aᶜ)P(Aᶜ) 다.
| 항 | 이름 | 뜻 |
|---|---|---|
| P(A) | 사전 확률(prior) | 증거를 보기 전의 믿음, 기저율 |
| P(B ∣ A) | 가능도(likelihood) | 가설이 참일 때 이 증거가 나올 확률 |
| P(A ∣ B) | 사후 확률(posterior) | 증거를 본 뒤의 믿음 |
기저율 오류: 정확도 99% 의 함정
어떤 장애 탐지기가 실제 장애의 99% 를 잡고(민감도), 정상 상황의 1% 에서 잘못 울린다(위양성률). 장애는 전체 시간 구간의 0.1% 에서만 일어난다. 경보가 울렸을 때 실제 장애일 확률은?
P(장애) = 0.001, P(경보 ∣ 장애) = 0.99, P(경보 ∣ 정상) = 0.01
P(경보) = 0.99 × 0.001 + 0.01 × 0.999 = 0.01098
P(장애 ∣ 경보) = 0.99 × 0.001 / 0.01098 ≈ 0.090
약 9% 다. 경보 열한 번 중 열 번은 오경보다. 탐지기가 나빠서가 아니라 장애가 드물어서다. 10 만 구간으로 바꿔 세어 보면 직관적이다. 장애 100 구간 중 99 개에서 울리고, 정상 99,900 구간 중 999 개에서 잘못 울린다. 울린 1,098 번 중 진짜는 99 번이다.
직접 해 보기
위 계산을 정확한 분수로 하고, 무작위 시뮬레이션으로 같은 값이 나오는지 확인한다. fractions.Fraction 을 쓰면 부동소수점 오차 없이 계산할 수 있다.
from fractions import Fraction as F
import random
def posterior(prior, sens, fpr):
evidence = sens * prior + fpr * (1 - prior)
return sens * prior / evidence
p = posterior(F(1, 1000), F(99, 100), F(1, 100))
print(p, float(p))
# 기저율에 따른 사후 확률 변화
for base in [F(1, 10000), F(1, 1000), F(1, 100), F(1, 10)]:
print(f"기저율 {float(base):.4f} -> 사후 확률 {float(posterior(base, F(99,100), F(1,100))):.3f}")
# 시뮬레이션
random.seed(42)
N = 1_000_000
alarms = true_alarms = 0
for _ in range(N):
incident = random.random() < 0.001
alarm = random.random() < (0.99 if incident else 0.01)
if alarm:
alarms += 1
true_alarms += incident
print(alarms, true_alarms, round(true_alarms / alarms, 3))
# 독립 vs 서로소: 주사위에서 A={짝수}, B={1,2}
S = range(1, 7)
P = lambda E: F(sum(1 for s in S if E(s)), 6)
A = lambda s: s % 2 == 0
B = lambda s: s in (1, 2)
print(P(lambda s: A(s) and B(s)) == P(A) * P(B))
실행 결과다(시뮬레이션 줄은 시드에 따라 조금씩 다르다).
11/122 0.09016393442622951
기저율 0.0001 -> 사후 확률 0.010
기저율 0.0010 -> 사후 확률 0.090
기저율 0.0100 -> 사후 확률 0.500
기저율 0.1000 -> 사후 확률 0.917
10969 935 0.085
True
정확한 답은 11/122 ≈ 0.090 이다. 시뮬레이션의 0.085 는 진짜 장애가 천 번 안팎뿐이라 생기는 표본 오차다. 같은 탐지기라도 기저율이 1% 면 사후 확률이 0.5, 10% 면 0.917 로 올라간다. 마지막 줄은 “짝수” 와 “1 또는 2” 가 독립이라는 뜻이다. P(짝수 ∩ {1,2}) = 1/6 = (1/2)(1/3).
현업에서는
- 알림 설계. 드물게 일어나는 사건을 탐지하는 알림은 위양성률이 조금만 높아도 오경보가 대부분이 된다. 알림 피로를 줄이려면 탐지 정확도를 올리는 것만큼, 알림 조건을 여러 신호의 AND 로 묶어 위양성률을 곱으로 낮추는 것이 효과적이다. 단, 신호들이 서로 독립일 때 얘기다.
- 나이브 베이즈 분류기. 스팸 필터의 고전적 방법은 단어들이 (클래스가 주어졌을 때) 서로 독립이라고 가정하고 베이즈 정리를 적용한다. 가정은 틀렸지만 실무에서 꽤 잘 동작한다. scikit-learn 같은 라이브러리에 기본 모델로 들어 있다.
- 상관된 장애. 쿠버네티스에서 레플리카를 여러 개 띄워도 모두 같은 노드에 있으면 노드 하나의 장애가 전부를 죽인다. 파드 안티 어피니티나 토폴로지 분산 제약은 장애 사건들을 독립에 가깝게 만들려는 장치다(Kubernetes, Assigning Pods to Nodes).
- 플래키 테스트 판단. 어떤 테스트가 평소 2% 확률로 무작위 실패한다면, 이번 실패가 진짜 회귀인지는 “최근 변경이 버그를 넣었을 사전 확률” 에 크게 좌우된다. 재실행해서 또 실패하면 가능도가 곱해지므로 사후 확률이 빠르게 오른다(재실행 결과가 독립이라는 가정 아래).
확인 문제
- 동전 두 개를 던져 적어도 하나가 앞면일 때, 둘 다 앞면일 확률은?
- 확률이 양수인 두 사건 A, B 가 서로소면 독립일 수 있는가?
- 질병 유병률 2%, 민감도 90%, 위양성률 5% 인 검사에서 양성일 때 실제 환자일 확률은?
- 서버 3 대가 각각 독립적으로 하루에 1% 확률로 다운된다. 하루에 적어도 한 대가 다운될 확률은?
풀이
- 표본 공간 {HH, HT, TH, TT} 에서 조건 사건은 {HH, HT, TH}. 답은 1/3.
- 없다. P(A ∩ B) = 0 이지만 P(A)P(B) > 0 이다.
- 0.9 × 0.02 / (0.9 × 0.02 + 0.05 × 0.98) = 0.018 / 0.067 ≈ 0.269.
- 1 − 0.99³ ≈ 0.0297.
더 읽을거리 (References)
- Eric Lehman, F. Thomson Leighton, Albert R. Meyer, Mathematics for Computer Science, 17장 Events and Probability Spaces, 18장 Conditional Probability — MIT 공개 PDF
- MIT OpenCourseWare, 6.041 Probabilistic Systems Analysis and Applied Probability
- Python Documentation, fractions — Rational numbers
- Dimitri P. Bertsekas, John N. Tsitsiklis, Introduction to Probability, 2판, Athena Scientific, 1장