
정확도 99%짜리 검사에서 양성이 나왔습니다. 실제로 병에 걸렸을 확률은 얼마일까요? 대부분 "99%"라고 답하지만, 이 글의 계산으로는 겨우 1.94% 입니다. 😱
이 함정을 풀어 주는 도구가 베이즈 정리예요. 새로운 증거가 들어올 때마다 믿음을 조금씩 조정하는 저울이죠. 1부에서는 확률 → 조건부 확률 → 베이즈 정리를 차근차근 쌓아 올립니다.
📘 이 글은 위키독스 도서 Zero to 머신러닝 딥러닝 Master 의 00-05 확률·베이즈 정리 — 병원 검사의 함정 (1부) 을 블로그용으로 다듬은 글입니다.
💻 실습 코드: GitHub · 🏠 mldict.net · ✍️ 기술 블로그
💻 이 글의 코드:
00-05-probability-bayes· Open in Colab — 핵심 코드와 실제 실행 결과는 글 아래 💻 GitHub 실습 섹션에 있습니다.
⬅️ 이전 글: #06 경사하강법 완전 정복 — 학습률·발산 조건·선형회귀·자동미분 (딥러닝 수학, 2부) \
➡️ 다음 글: #08 정규분포·소프트맥스·교차 엔트로피 — AI는 확률로 대답한다 (2부)
✏️ 꼭 알아야 할 TIP
🎯 핵심 개념: 확률은 "원하는 경우의 수 ÷ 전체 경우의 수"이고, 조건부 확률은 "새 정보를 알고 나서 전체(분모)를 다시 좁혀 계산한 확률"입니다.
⚖️ 한 줄 비유: 베이즈 정리는 "새로운 증거가 들어올 때마다 내 믿음을 조금씩 조정하는 저울"입니다.
🔑 이 장을 관통하는 한 줄 공식:
코드에서는
posterior = like*prior / (like*prior + fpr*(1-prior))딱 한 줄이 됩니다. 수식 한 줄 = 코드 한 줄의 대응을 계속 확인하며 읽어보세요.😱 가장 중요한 포인트: 검사 정확도가 99%여도, 병이 희귀하면(발병률 0.1%) 양성 판정을 받은 사람이 실제 환자일 확률은 겨우 1.9%입니다. 이 장에서 그 이유를 인원수·공식·코드 세 가지 방법으로 확인합니다.
🏗️ 실무에서 쓰는 이유: 스팸 필터, 추천 시스템, 의료 진단 보조, 자율주행 센서 융합 — "불확실한 증거로 확률을 업데이트하는" 모든 AI의 근간이 베이즈 정리입니다. 그리고 5단계의 소프트맥스·교차 엔트로피는 여러분이 쓰게 될 모든 분류 모델의 출력과 손실입니다.
📋 선행지식 (Prerequisites)
- ➗ 초등 수준의 분수와 나눗셈, 백분율(%) 개념 — 이것만 있으면 3단계까지 갑니다
- 🔢 지수 표기 와 로그 의 대략적인 느낌 (5단계에서 다시 설명합니다)
- 🧮 00-01 벡터의 성분 개념 — 소프트맥스 출력이 벡터라서 필요
- 📈 00-04 미분·경사하강법 — 5단계에서 교차 엔트로피의 기울기를 유도할 때 연결됩니다
- 🐍 파이썬 함수 정의(
def)와 리스트 — 코드마다 한 줄씩 해설하니 몰라도 괜찮습니다
🎯 학습 목표
☐ 🎲 확률을 로 계산하고, 시뮬레이션으로 검증할 수 있다 🟢 초급
☐ 📊 대수의 법칙을 실험으로 확인하고, 시행 횟수와 오차의 관계를 설명할 수 있다 🟢 초급
☐ 🔀 와 가 왜 다른 값인지 분할표로 설명할 수 있다 🟢 초급 ⭐
☐ 🧮 곱셈정리 로부터 베이즈 정리를 직접 유도할 수 있다 🔵 중급 ⭐
☐ 🏥 발병률·정확도·오탐률이 주어졌을 때 사후 확률을 손과 코드로 계산할 수 있다 🔵 중급 ⭐
☐ ⚖️ 오즈(odds) 형태 "사후 오즈 = 사전 오즈 × 우도비"를 유도하고 암산에 활용할 수 있다 🔴 심화
☐ 🔁 검사를 2번, 3번 반복했을 때 확률이 어떻게 갱신되는지 코드로 시뮬레이션할 수 있다 🔴 심화 🚀
☐ 🔔 정규분포의 확률밀도함수를 직접 구현하고 68-95-99.7 규칙을 검증할 수 있다 🔵 중급
☐ 🗳️ 소프트맥스를 구현하고, 최댓값을 빼도 결과가 같은 이유를 증명할 수 있다 🔵 중급
☐ 🎁 소프트맥스+교차엔트로피의 기울기가 임을 유도하고 수치미분으로 검증할 수 있다 🔴 심화 ⭐🚀
📊 핵심 학습 내용 — 수식과 코드의 대응
| 단계 | 학습 내용 | 핵심 수학공식 | 대응하는 코드 |
|---|---|---|---|
| 1️⃣ | 확률의 정의·대수의 법칙 | len(favorable)/len(space) | |
| 2️⃣ | 조건부 확률·곱셈정리 | data[mask].mean() | |
| 3️⃣ | 베이즈 정리·오즈 형태 | like*prior/evidence | |
| 4️⃣ | 정규분포·표준화 | np.exp(-(x-mu)**2/(2*s**2)) | |
| 5️⃣ | 소프트맥스·교차 엔트로피 | , | np.exp(z)/np.exp(z).sum() |
| 🎁 | 00-04와 연결: 손실의 기울기 | gz = y_hat - y |
🗺️ 시각화 ① — 이번 장의 전체 흐름

🔎 ① 핵심 정의 — 무엇인가?\
확률(Probability)은 "어떤 일이 일어날 가능성"을 0과 1 사이의 숫자로 표현한 것입니다. 0은 "절대 일어나지 않는다", 1은 "반드시 일어난다", 0.5는 "반반"입니다.
❓ 왜 필요한가?\
세상의 거의 모든 일은 100% 확실하지 않기 때문입니다. 내일 비가 올지, 이 메일이 스팸인지, 이 사진이 강아지인지는 미리 확정할 수 없습니다. 대신 "가능성이 얼마인지"를 숫자로 다뤄야 합리적 판단이 가능해집니다. AI가 "강아지 86%"라고 말하는 것도 이 때문입니다.
⏰ 언제 사용하는가?\
분류 모델의 출력, 생성 모델의 샘플링, 강화학습의 행동 선택 — AI가 "확신의 정도"를 표현해야 하는 모든 순간입니다.
💜 더 쉽게 이해하기 — 🎱 복권 추첨함
추첨함에 공이 100개 들어 있고 그중 30개가 빨간 공입니다. 눈을 감고 하나를 뽑을 때 빨간 공이 나올 가능성은? "전체 100개 중 빨강 30개"이므로 30%입니다. 🎱
확률 계산은 언제나 이 추첨함을 상상하는 것에서 시작합니다.\
① "전체 공은 몇 개인가?" → 분모\
② "내가 원하는 공은 몇 개인가?" → 분자\
이 두 질문만 정확히 답하면 확률 문제의 90%는 풀립니다. 🔑
🔢 진행 과정 — 확률 계산 4단계
🔬 수학공식 ① — 확률의 정의와 세 가지 성질
변수 정의\
• (오메가) : 표본공간 — 일어날 수 있는 모든 경우의 집합\
• : 관심 있는 사건 (표본공간의 부분집합)\
• : 원소의 개수\
• : 사건 가 일어날 확률\
\
반드시 성립하는 세 가지 성질(확률의 공리)\
① 범위: — 확률은 절대 음수나 1 초과가 될 수 없습니다\
② 전체: — "무언가는 반드시 일어난다"\
③ 여사건: — "일어나지 않을 확률 = 1 − 일어날 확률"\
\
📌 ③번은 실전에서 매우 유용합니다. "적어도 하나는 나올 확률"처럼 계산이 복잡한 문제는, 반대(하나도 안 나올 확률)를 구해서 1에서 빼는 것이 훨씬 쉽습니다. 🔄
📖 수학적 의미 — 예제로 직접 확인
예제 ① 🟢 주사위 짝수\
• → \
• → \
•
예제 ② 🔵 주사위 두 개의 합이 7\
• 표본공간은 부터 까지 → \
• 합이 7인 경우: → \
• \
• 📌 합이 7이 가장 흔합니다. 합 2나 12는 방법이 1가지뿐이라 입니다.
예제 ③ 🔵 여사건 활용 — "주사위 3번 던져 6이 적어도 한 번"\
• 직접 세면 복잡합니다 (1번만, 2번만, 3번 모두… 경우가 많음) 😵\
• 반대를 생각합니다: "6이 한 번도 안 나올 확률" \
• 따라서 \
• ⚠️ 흔한 오답: — 중복을 세어버려 틀립니다!
💡 직관적 의미\
확률 공식은 결국 "전체를 똑같은 크기의 조각으로 나눴을 때, 내가 원하는 조각이 몇 개인가"를 재는 것입니다. 🍕 여기서 "똑같은 크기"가 중요합니다 — 주사위 두 개의 합에서 "합이 7"이 "합이 2"보다 6배 흔한 이유는, 7을 만드는 조각(경우)이 6개나 되기 때문입니다.
📐 시각화 ② — 표본공간과 사건

⚙️ 구현 원리 — 이론값과 실험값 두 갈래로 확인하기\
확률은 두 가지 방식으로 구할 수 있고, 코드에서 둘 다 해보는 것이 최고의 학습법입니다.\
① 이론값(해석적) — 공식 를 그대로 계산\
② 실험값(시뮬레이션) — 주사위를 실제로 수만 번 던져 비율을 셈\
두 값이 가까워지면 이론이 옳다는 것을 실험으로 검증한 셈입니다. 이는 00-04에서 "공식 미분 vs 수치 미분"을 비교했던 것과 완전히 같은 습관입니다. 🔗
🔗 프레임워크 관점
| 개념 | 파이썬 표준 | NumPy | SciPy | PyTorch |
|---|---|---|---|---|
| 무작위 시행 | random.randint(1,6) | rng.integers(1,7,n) | — | torch.randint |
| 확률분포 객체 | — | 직접 계산 | stats.randint(1,7) | torch.distributions |
| 재현성(시드) | random.seed(0) | default_rng(0) | random_state=0 | torch.manual_seed |
💻 코드 구현 — 공식을 그대로 파이썬으로
🐍 Python — 이론값 vs 시뮬레이션, 그리고 대수의 법칙
import numpy as np
def theoretical_prob(sample_space, condition):
"""
공식 P(A) = n(A) / n(Ω) 를 그대로 계산합니다.
비유:
추첨함 안의 공 전체(n(Ω)) 중에서
원하는 색 공(n(A))이 몇 개인지 세는 것과 같습니다.
매개변수:
sample_space: 표본공간 (모든 가능한 결과의 리스트)
condition : 원하는 결과인지 판정하는 함수
"""
favorable = [x for x in sample_space if condition(x)] # n(A)
return len(favorable) / len(sample_space) # n(A)/n(Ω)
def simulate_prob(n_trials, seed=0):
"""주사위를 n_trials 번 던져 '짝수가 나온 비율'을 셉니다."""
rng = np.random.default_rng(seed) # 재현 가능한 난수 생성기
rolls = rng.integers(1, 7, n_trials) # 1~6 (7은 미포함!)
return np.mean(rolls % 2 == 0) # True 비율 = 확률 추정
dice = [1, 2, 3, 4, 5, 6]
# ── 예제 ①: 짝수 ──────────────────────────────────────
print("[짝수] 이론값:", theoretical_prob(dice, lambda x: x % 2 == 0))
# ── 예제 ②: 두 주사위 합이 7 (표본공간 36개) ───────────
two_dice = [(a, b) for a in dice for b in dice] # 6×6 = 36가지
print("[합=7] 이론값:", round(theoretical_prob(two_dice, lambda p: sum(p) == 7), 4))
# ── 예제 ③: 여사건 — 3번 던져 6이 적어도 한 번 ─────────
p_no_six = (5 / 6) ** 3 # 6이 한 번도 안 나올 확률
print("[6 등장] 이론값:", round(1 - p_no_six, 4), "← 1 - P(여사건)")
# ── 대수의 법칙: 시행 횟수를 늘리면 이론값에 수렴 ───────
print("\n--- 대수의 법칙 (짝수 확률, 이론값 0.5) ---")
for n in [10, 100, 1000, 10000, 100000]:
est = simulate_prob(n)
print(f"{n:>7}회 → 추정 {est:.4f} (오차 {abs(est-0.5):.4f})")
🔗 GitHub에서 코드 보기 — 00_05_probability_bayes.py L12–L52
📘 코드 이해 — 수식과 코드 한 줄씩 대응
| 수학공식 | 파이썬 코드 | 설명 |
|---|---|---|
sample_space | 표본공간을 리스트로 표현 | |
len([x for x in ... if cond(x)]) | 조건을 만족하는 원소만 세기 | |
len(favorable)/len(sample_space) | 공식을 1:1로 옮김 | |
[(a,b) for a in dice for b in dice] | 이중 컴프리헨션 = 곱의 법칙 | |
1 - (5/6)**3 | 여사건 공식 — 계산이 훨씬 간단 | |
| 실험 비율 | np.mean(rolls % 2 == 0) | True/False 배열의 평균 = 비율 ⭐ |
🧩 코드 설명 — Python/NumPy 기초
• 리스트 컴프리헨션
[x for x in A if cond(x)]— "A의 각 원소 중 조건을 만족하는 것만 모아 새 리스트를 만들라"는 뜻입니다.for문 3줄을 1줄로 줄여줍니다.\
\
• 함수를 인자로 넘기기lambda x: x % 2 == 0— 이름 없는 한 줄 함수입니다. 이렇게 하면theoretical_prob하나로 어떤 조건이든 계산할 수 있어 재사용성이 높아집니다.\
\
•np.mean(불리언 배열)⭐ — 파이썬은True를 1,False를 0으로 취급합니다. 그래서 불리언 배열의 평균 = True의 비율 = 확률 추정치가 됩니다. 확률 코딩에서 가장 자주 쓰는 관용구이니 꼭 기억하세요!\
\
•rng.integers(1, 7, n)⚠️ — 끝값 7은 포함되지 않습니다. 주사위(1~6)를 만들려면 반드시7을 써야 합니다.6을 쓰면 1~5만 나오는 흔한 버그입니다.\
\
•np.random.default_rng(seed)— 요즘 NumPy 권장 방식입니다.seed를 고정하면 매번 같은 난수가 나와 결과를 재현할 수 있습니다.
✅🟩 실행 결과
[짝수] 이론값: 0.5
[합=7] 이론값: 0.1667
[6 등장] 이론값: 0.4213 ← 1 - P(여사건)
--- 대수의 법칙 (짝수 확률, 이론값 0.5) ---
10회 → 추정 0.6000 (오차 0.1000)
100회 → 추정 0.5000 (오차 0.0000)
1000회 → 추정 0.5200 (오차 0.0200)
10000회 → 추정 0.5019 (오차 0.0019)
100000회 → 추정 0.4976 (오차 0.0024)
🔍 결과 해석: 10회에서는 오차가 0.1이나 되지만, 10,000회에서는 0.0019까지 줄어듭니다. 이것이 대수의 법칙(Law of Large Numbers)입니다. 📌 흥미롭게도 100회에서 오차가 0.0000이 나왔는데, 이는 실력이 아니라 우연입니다 — 적은 횟수에서는 운 좋게 정확할 수도, 크게 빗나갈 수도 있습니다. 오차는 대략 에 비례해 줄어들어, 정확도를 10배 높이려면 시행을 100배 늘려야 합니다. 🐢
🛠️ 실습 1 🟢
①
theoretical_prob로 "3 이상이 나올 확률"을 구하세요. (정답: 4/6 ≈ 0.667)\
② 두 주사위의 합이 2부터 12까지 각각 나올 확률을 모두 구해 표로 출력하세요. (힌트:for s in range(2,13)) 어떤 합이 가장 흔한가요?\
③ "주사위 5번 던져 6이 한 번도 안 나올 확률"을 여사건으로 구하세요. (정답: )
🐞 실수하기 쉬운 부분 & 디버깅 포인트
❌ 실수 ① 시뮬레이션이 이론값과 다르다고 당황\
왜 틀리나? 시행이 10회뿐이면 우연히 0.6, 0.3도 나옵니다. 원인: 버그가 아니라 통계적으로 자연스러운 흔들림입니다. 수정: 시행을 10,000회 이상으로 늘려보세요. 오차가 로 줄어듭니다.❌ 실수 ②
randint의 끝값\
NumPyrng.integers(1, 6)는 1~5만 만듭니다. 주사위는(1, 7)이 맞습니다. 반면 파이썬 표준random.randint(1, 6)은 6을 포함합니다 — 두 라이브러리의 규칙이 달라 가장 헷갈리는 부분입니다! ⚠️ 확인법:print(rolls.min(), rolls.max())❌ 실수 ③ "적어도 한 번"을 곱셈으로\
로 계산하면 틀립니다(정답 0.4213). 원인: 6이 두 번 이상 나오는 경우를 중복해서 세기 때문입니다. 수정: 반드시 여사건 을 쓰세요.✅ 검산 습관: 확률을 구했으면
assert 0 <= p <= 1을 넣으세요. 1을 넘으면 분모를 잘못 잡은 것입니다.
🔎 ① 핵심 정의 — 무엇인가?\
조건부 확률(Conditional Probability)은 "사건 가 이미 일어났다는 사실을 알고 있을 때, 사건 가 일어날 확률"입니다. 로 쓰고 "B라는 조건 하에서 A"라고 읽습니다.
❓ 왜 필요한가?\
아무 정보가 없을 때의 확률과 어떤 정보를 알고 난 뒤의 확률이 서로 다르기 때문입니다. 새 정보를 얻으면 우리는 확률을 "다시 계산"해야 합니다. 이 아이디어가 다음 단계 베이즈 정리의 뼈대가 됩니다.
⏰ 언제 사용하는가?\
"이 단어가 들어 있을 때 스팸일 확률", "이 픽셀 패턴일 때 고양이일 확률" — AI의 거의 모든 예측이 사실은 조건부 확률입니다.
💜 더 쉽게 이해하기 — 🧭 내비게이션의 경로 재탐색
출발 전 내비게이션은 "이 길이면 30분"이라 예상합니다. 그런데 달리던 중 "앞에 사고 발생"이라는 새 정보(조건)가 들어오면, 내비게이션은 즉시 소요 시간을 다시 계산합니다. 🚗
핵심은 "고려 대상이 좁아진다"는 것입니다. 사고가 났다는 정보를 알기 전에는 모든 교통 상황이 후보였지만, 알고 난 뒤에는 "사고가 난 상황들"만이 새로운 전체(분모)가 됩니다. 조건부 확률은 이렇게 분모를 갈아끼우는 연산입니다. 🔄
🔢 진행 과정 — 조건부 확률 3단계
📌 기억법: "세로줄(|) 뒤에 있는 것이 분모"입니다. 면 분모는 반드시 입니다. 🔑
🔬 수학공식 ② — 조건부 확률과 곱셈정리
조건부 확률의 정의
변수 정의\
• : 가 일어난 조건에서 가 일어날 확률\
• : 와 가 동시에 일어날 확률 (교집합, "그리고")\
• : 조건이 되는 사건의 확률 — 새로운 분모\
• 조건: — 일어나지 않는 일을 조건으로 삼을 수는 없습니다\
\
양변에 를 곱하면 → 곱셈정리(chain rule) ⭐📌 이 식이 3단계 베이즈 정리의 씨앗입니다. 같은 교집합을 두 가지 방향으로 쪼갤 수 있다는 사실 하나에서 베이즈 정리가 곧바로 유도됩니다! 🌱\
\
독립(independence) — 정보가 확률을 바꾸지 않는 특수한 경우예: 주사위를 두 번 던질 때, 첫 번째 결과는 두 번째에 아무 영향이 없습니다. 🎲🎲
📖 수학적 의미 — 의대 지원자 1000명 예제\
지원자 1000명 중 수능 고득점자 400명, 합격자 200명, 그중 고득점이면서 합격 160명입니다. 이 숫자들을 표(분할표)로 정리하면 관계가 한눈에 보입니다.
| 합격 ✅ | 불합격 ❌ | 행 합계 | |
|---|---|---|---|
| 고득점 📈 | 160 | 240 | 400 |
| 저득점 📉 | 40 | 560 | 600 |
| 열 합계 | 200 | 800 | 1000 |
모든 수를 1000으로 나누면 확률이 됩니다: , , .
질문 A — "고득점자 중 합격 비율은?" (분모 = 고득점 400명, 표의 가로줄)
질문 B — "합격자 중 고득점 비율은?" (분모 = 합격 200명, 표의 세로줄)
⚠️ 40%와 80%. 분자는 똑같이 160명인데 답이 두 배 차이납니다.\
분모가 400 vs 200으로 다르기 때문입니다. "고득점자 중 합격 비율"과 "합격자 중 고득점 비율"은 완전히 다른 질문입니다. 이것을 혼동하는 것이 3단계에서 다룰 "병원 검사의 함정"의 정체입니다! 🏥
💡 직관적 의미 — 표에서 방향 읽기\
분할표에서 가로로 나누면 "행 조건", 세로로 나누면 "열 조건"입니다. 조건부 확률을 계산할 때는 언제나 "지금 나는 표의 어느 방향으로 나누고 있는가?"를 스스로에게 물어보세요. 🧭
📌 독립인지 확인해보기: 만약 두 사건이 독립이라면 여야 합니다. 그런데 실제로는 0.4이므로, 고득점은 합격 확률을 2배로 높이는 유의미한 정보입니다. ✅
📐 시각화 ③ — 벤 다이어그램: 같은 교집합, 다른 분모

⚙️ 구현 원리 — 마스크로 "분모 갈아끼우기"\
코드에서 조건부 확률은 두 가지로 구현할 수 있습니다.\
① 공식 그대로 — 확률값을 넣어 나누기\
② 데이터에서 직접 — 불리언 마스크로 조건을 만족하는 부분집합만 걸러낸 뒤, 그 안에서 다시 비율을 셈\
②번이 훨씬 중요합니다. 실무에서는 확률이 아니라 원본 데이터가 주어지기 때문입니다. 그리고 이 방식은 "분모를 갈아끼운다"는 개념을 코드로 그대로 보여줍니다. 🔍
🔗 프레임워크 관점
| 개념 | NumPy | Pandas | SciPy |
|---|---|---|---|
| 조건부 필터링 | arr[mask] | df[df["고득점"]] | — |
| 분할표 | 직접 계산 | pd.crosstab(a, b) | stats.contingency |
| 독립성 검정 | — | — | stats.chi2_contingency |
🐍 Python — 공식 계산 + 실제 1000명 데이터로 검증
import numpy as np
def conditional_prob(p_joint, p_condition):
"""
P(A|B) = P(A∩B) / P(B) 를 계산합니다.
비유:
조건(B)에 해당하는 사람들만 방에 남기고,
그 방 안에서만 다시 비율을 재는 것과 같습니다.
매개변수:
p_joint : P(A∩B) — 둘 다 만족할 확률 (분자)
p_condition: P(B) — 조건 사건의 확률 (분모)
"""
if p_condition <= 0:
raise ValueError("조건 사건의 확률이 0이면 정의되지 않습니다")
return p_joint / p_condition
# ══════ 방법 1) 공식에 확률을 직접 대입 ══════
p_high = 400 / 1000 # P(고득점) = 0.4
p_pass = 200 / 1000 # P(합격) = 0.2
p_both = 160 / 1000 # P(고득점 ∩ 합격) = 0.16 (분자는 공통!)
print(f"P(합격|고득점) = {conditional_prob(p_both, p_high):.2f}") # 분모=0.4
print(f"P(고득점|합격) = {conditional_prob(p_both, p_pass):.2f}") # 분모=0.2
# ══════ 방법 2) 실제 1000명 데이터에서 마스크로 계산 ══════
# 분할표대로 1000명을 만듭니다: [고득점여부, 합격여부]
high = np.array([True]*400 + [False]*600) # 고득점 400명
# 고득점 400명 중 160명 합격 / 저득점 600명 중 40명 합격
passed = np.array([True]*160 + [False]*240
+ [True]*40 + [False]*560)
# ★ 핵심: high 마스크로 '고득점자만' 골라낸 뒤 그 안에서 평균 = 조건부 확률
print(f"\n[데이터] P(합격|고득점) = {passed[high].mean():.2f}",
f"(분모 {high.sum()}명)")
print(f"[데이터] P(고득점|합격) = {high[passed].mean():.2f}",
f"(분모 {passed.sum()}명)")
# ══════ 독립인지 확인 ══════
# 독립이라면 P(A∩B) == P(A)×P(B) 여야 합니다
print(f"\n실제 P(A∩B) = {p_both:.3f}")
print(f"독립이라면 = {p_high * p_pass:.3f}")
print("→ 다르므로 두 사건은 독립이 아니다 (고득점은 유의미한 정보!)")
🔗 GitHub에서 코드 보기 — 00_05_probability_bayes.py L56–L99
📘 코드 이해 — 수식과 코드 대응
| 수학공식 | 코드 | 설명 |
|---|---|---|
p_both | 분자 — 두 계산에서 공통 | |
p_high 또는 p_pass | 분모 — 여기만 바뀐다 ⭐ | |
| "조건으로 좁히기" | passed[high] | 고득점자 400명만 남긴 배열 |
passed[high].mean() | 좁힌 집단 안에서의 비율 | |
p_high * p_pass | 독립 가정 시의 교집합 (비교용) |
🧩 코드 설명 — NumPy 불리언 인덱싱
•
passed[high]⭐ —high가True인 위치의 원소만 뽑아 새 배열을 만듭니다. 이것이 바로 "분모를 갈아끼우는" 연산입니다. 400명짜리 배열이 되고, 그 평균이 곧 입니다.\
\
•high[passed]— 순서만 바꿨을 뿐인데 완전히 다른 질문이 됩니다. 합격자 200명만 남기고 그중 고득점 비율을 재는 것이죠. 인덱싱의 방향이 조건의 방향입니다. 🔄\
\
•[True]*400 + [False]*600— 리스트에*는 반복,+는 이어붙이기입니다. 분할표를 그대로 데이터로 만드는 간편한 방법입니다.\
\
•.sum()on 불리언 —True가 1이므로 합계가 곧 개수입니다.high.sum()은 400을 돌려줍니다.\
\
•raise ValueError(...)— 분모가 0이면 명확한 메시지와 함께 프로그램을 멈춥니다. 조용히inf가 나오는 것보다 훨씬 안전한 습관입니다. 🛡️
✅🟩 실행 결과
P(합격|고득점) = 0.40
P(고득점|합격) = 0.80
[데이터] P(합격|고득점) = 0.40 (분모 400명)
[데이터] P(고득점|합격) = 0.80 (분모 200명)
실제 P(A∩B) = 0.160
독립이라면 = 0.080
→ 다르므로 두 사건은 독립이 아니다 (고득점은 유의미한 정보!)
✅ 공식 계산과 데이터 계산이 정확히 일치합니다. 그리고 실제 교집합(0.160)이 독립 가정값(0.080)의 정확히 2배라는 사실은, "고득점이라는 정보가 합격 확률을 2배로 끌어올린다"는 뜻입니다. 이 "2배"라는 비율이 3단계에서 우도비(likelihood ratio)라는 이름으로 다시 등장합니다! 🔗
🛠️ 실습 2 🟢🔵
① 어느 반 40명 중 숙제를 한 학생 30명, 그중 시험 합격 24명일 때 을 구하세요. (정답: 24/30 = 0.8)\
② 위 코드에서passed[high]를passed[~high]로 바꿔 저득점자의 합격률을 구하세요. (~는 부정 연산자, 정답: 40/600 ≈ 0.067)\
③ 주사위 두 개를 던져 "합이 7일 때 첫 주사위가 1일 확률"을 코드로 구하세요. (정답: 1/6)
🐞 실수하기 쉬운 부분 & 디버깅 포인트
❌ 실수 ① 와 혼동 (가장 흔함!)\
왜 틀리나? 두 표현이 같은 글자 A, B를 쓰기 때문에 분모가 바뀐다는 사실을 놓칩니다. 수정: "세로줄 뒤가 분모"를 주문처럼 외우세요. 확신이 안 서면 분할표를 그려서 가로로 나눌지 세로로 나눌지 눈으로 확인하세요.❌ 실수 ② 교집합과 조건부를 혼동\
과 은 전혀 다른 값입니다. "둘 다 일어날 확률"과 "B가 일어난 상황에서 A일 확률"을 구분하세요. 확인법: 조건부 확률은 항상 교집합보다 크거나 같습니다(분모가 1 이하이므로).❌ 실수 ③ 분모가 0\
ZeroDivisionError또는nan이 나옵니다. 수정: 위 코드처럼if p_condition <= 0: raise ...로 미리 막으세요.❌ 실수 ④ 마스크 길이 불일치\
passed[high]에서 두 배열의 길이가 다르면IndexError가 납니다. 확인법:print(len(high), len(passed))로 둘 다 1000인지 확인하세요.
🔎 ① 핵심 정의 — 무엇인가?\
베이즈 정리(Bayes' Theorem)는 "결과(증거)를 관찰한 뒤, 원인이 무엇이었을지를 역으로 계산하는 공식"입니다. 즉 를 알고 싶은데 우리가 아는 것은 뿐일 때 씁니다.
❓ 왜 필요한가?\
현실에서 측정 가능한 것과 알고 싶은 것의 방향이 정반대이기 때문입니다.\
• 우리가 아는 것: "병에 걸린 사람이 양성이 나올 확률 99%" ← 검사 스펙, 실험으로 측정 가능\
• 우리가 알고 싶은 것: "양성이 나온 내가 실제로 병에 걸렸을 확률" ← 진짜 중요한 것\
2단계에서 배웠듯 이 둘은 분모가 다른 완전히 다른 값입니다.
⏰ 언제 사용하는가?\
스팸 필터(단어를 보고 스팸 판정), 의료 진단, 스팸/사기 탐지, 로봇의 위치 추정, A/B 테스트 — 증거를 보고 원인을 추정하는 모든 곳입니다.
💜 더 쉽게 이해하기 — 🕵️ 탐정의 추리
명탐정은 두 가지를 함께 고려합니다.
① 증거의 결정력(우도) — "범인이라면 이런 흔적을 남길 확률이 높다"\
② 배경 지식(사전 확률) — "애초에 이 사람이 범인일 가능성이 얼마나 되나?"🔍 결정적 증거가 나와도, 애초에 용의자가 10만 명이라면 결론은 신중해야 합니다. 반대로 용의자가 2명뿐이었다면 같은 증거가 훨씬 강력합니다.
이것이 베이즈 정리의 전부입니다: "원래 믿음(사전) × 새 증거의 힘(우도) = 갱신된 믿음(사후)" ⚖️
🔢 진행 과정 — 인원수로 먼저 이해하기 (공식 없이!)\
공식을 보기 전에 10만 명을 실제로 검사한다고 상상해봅시다. 조건은 발병률 0.1%, 검사 정확도(민감도) 99%, 오탐률 5%입니다.
😱 충격적인 결론: 정확도 99%짜리 검사에서 양성이 나와도, 실제 환자일 확률은 겨우 1.94%입니다.\
\
왜? 진짜 환자는 99명인데 가짜 양성이 4,995명입니다. 진짜가 가짜에 50배 넘게 파묻혀 버린 것이죠. 병이 희귀할수록 비환자 집단이 압도적으로 크기 때문에, 5%라는 "작아 보이는" 오탐률도 절대 인원수로는 거대해집니다. 🌊
🗺️ 시각화 ④ — 10만 명이 갈라지는 과정

📐 시각화 ⑤ — 양성 판정자 5,094명의 구성 (면적 비교)

🔬 수학공식 ③ — 베이즈 정리 직접 유도하기 ⭐ (이 장의 하이라이트)
놀랍게도 베이즈 정리는 2단계의 곱셈정리 한 줄에서 곧바로 나옵니다.\
\
① 같은 교집합을 두 방향으로 쪼갠다 (2단계 곱셈정리)② 왼쪽이 같으므로 오른쪽끼리 같다
③ 양변을 로 나눈다 → 베이즈 정리 완성!
변수 정의\
• (Hypothesis) : 가설 = 원인 (예: "병에 걸렸다")\
• (Evidence) : 증거 = 관찰된 결과 (예: "검사 양성")\
• : 사전 확률(prior) — 증거를 보기 전의 믿음 = 발병률 0.001\
• : 우도(likelihood) — 원인이 참일 때 증거가 나올 확률 = 민감도 0.99\
• : 증거 확률(evidence) — 증거가 나올 전체 확률 (정규화 상수)\
• : 사후 확률(posterior) — 증거를 본 후 갱신된 믿음 ← 우리가 원하는 것\
\
④ 분모 는 어떻게 구하나? — 전확률 정리\
증거(양성)가 나오는 모든 경로를 더합니다. 경로는 두 개뿐입니다.
📖 수학적 의미 — 공식에 숫자를 넣어보기
✅ 앞서 인원수로 센 1.94%와 완벽히 일치합니다! 사실 분자 는 10만 명 중 99명, 분모 는 5,094명을 각각 10만으로 나눈 값입니다. 공식은 인원수 계산을 확률로 표현한 것일 뿐입니다. 🔗
💡 직관적 의미 — 분자와 분모가 말하는 것\
• 분자는 "내가 원하는 경로 하나"(환자→양성)입니다.\
• 분모는 "증거가 나올 수 있는 모든 경로의 합"입니다.\
즉 베이즈 정리는 결국 "전체 양성 중 내가 원하는 양성의 비율"이라는, 1단계의 그 자체입니다. 표본공간이 "전체 인구"에서 "양성 판정자"로 좁혀졌을 뿐입니다. 🎯
🔬 수학공식 ④ — 오즈(odds) 형태: 암산으로 베이즈 하기 🔴 심화
베이즈 정리에는 훨씬 외우기 쉬운 형태가 있습니다. 분모를 없애는 방법입니다.\
\
① 가설이 참일 때와 거짓일 때를 각각 쓴다② 두 식을 나눈다 — 공통 분모 가 깔끔하게 약분됩니다! ✨
③ 말로 옮기면
변수 정의\
• 오즈(odds) : "일어날 확률 ÷ 안 일어날 확률". 확률 0.5 → 오즈 1(1:1), 확률 0.9 → 오즈 9(9:1)\
• 우도비(LR) : 증거의 힘 — 이 증거가 가설을 몇 배 그럴듯하게 만드는가\
\
④ 우리 예제로 암산해보기\
• 사전 오즈 (환자 1명당 비환자 999명)\
• 우도비 (검사는 믿음을 약 20배 강화한다)\
• 사후 오즈 \
• 확률로 환산 ✅\
\
📌 이 형태의 위력: "1:999에서 출발해 증거가 20배 밀어줬으니 약 20:999 ≈ 2%" — 계산기 없이 암산이 됩니다! 🧠 또한 증거가 여러 개면 우도비를 계속 곱하기만 하면 됩니다. 이것이 스팸 필터가 단어를 하나씩 보며 점수를 누적하는 원리입니다. 📧
⚙️ 구현 원리 — 함수 하나로 시나리오 전체 탐색하기\
베이즈 정리를 사전확률·우도·오탐률 세 인자를 받는 함수로 만들면, 값을 바꿔가며 "무엇이 결과를 좌우하는가"를 실험할 수 있습니다. 이것이 공식을 그냥 계산하는 것과 코드로 만드는 것의 결정적 차이입니다. 🔬
🔗 프레임워크 관점
| 개념 | NumPy | scikit-learn | 확률 프로그래밍 |
|---|---|---|---|
| 베이즈 정리 계산 | 수식 직접 구현 | — | — |
| 나이브 베이즈 분류 | 직접 구현 | MultinomialNB() | — |
| 베이즈 추론(분포 갱신) | 직접 구현 | — | PyMC, Stan, NumPyro |
| 평가 지표 | 직접 구현 | precision_score ⭐ | — |
📌 중요: 우리가 구한 사후 확률 1.94%는 머신러닝에서 정밀도(precision)와 정확히 같은 개념입니다. sklearn.metrics.precision_score가 재는 것이 바로 이것입니다! 🔗
🐍 Python — 베이즈 정리 + 인원수 검증 + 시나리오 탐색
def bayes_posterior(prior, likelihood, false_positive_rate):
"""
P(H|E) = P(E|H)·P(H) / P(E) 를 계산합니다.
비유:
탐정이 '원래 의심 정도(prior)'에 '증거의 힘(likelihood)'을 곱한 뒤,
'증거가 나올 모든 경우(evidence)'로 나눠 갱신된 의심 정도를 구합니다.
매개변수:
prior : P(H) — 사전 확률(발병률)
likelihood : P(E|H) — 우도(민감도, 검사 정확도)
false_positive_rate: P(E|H^c) — 오탐률(위양성률)
반환:
P(H|E) — 사후 확률
"""
# 분자: '원인이 참이면서 증거가 나온' 경로 하나
numerator = likelihood * prior
# 분모: 전확률 정리 — 증거가 나올 모든 경로의 합
evidence = numerator + false_positive_rate * (1 - prior)
return numerator / evidence
def bayes_by_odds(prior, likelihood, false_positive_rate):
"""오즈 형태로 같은 값을 계산합니다: 사후오즈 = 사전오즈 × 우도비"""
prior_odds = prior / (1 - prior) # P(H)/P(H^c)
likelihood_ratio = likelihood / false_positive_rate # LR
posterior_odds = prior_odds * likelihood_ratio # 곱하기만!
return posterior_odds / (1 + posterior_odds) # 오즈 → 확률
# ══════ ① 기본 시나리오 ══════
p = bayes_posterior(prior=0.001, likelihood=0.99, false_positive_rate=0.05)
print(f"양성 판정 후 실제 발병 확률: {p:.4f} ({p:.2%})")
print(f"오즈 형태로 계산해도 동일 : {bayes_by_odds(0.001, 0.99, 0.05):.4f}")
# ══════ ② 인원수로 검증 (공식이 맞는지 확인) ══════
total = 100_000
sick = total * 0.001 # 환자 100명
healthy = total - sick # 비환자 99,900명
true_pos = sick * 0.99 # 진짜 양성 99명
false_pos = healthy * 0.05 # 가짜 양성 4,995명
print(f"\n[인원수] 진짜양성 {true_pos:.0f}명 / 전체양성 {true_pos+false_pos:.0f}명"
f" = {true_pos/(true_pos+false_pos):.4f}")
# ══════ ③ 발병률을 바꿔가며 탐색 ══════
print("\n--- 발병률의 영향 (정확도 99%, 오탐 5% 고정) ---")
for rate in [0.001, 0.01, 0.1, 0.5]:
print(f" 발병률 {rate:>6.1%} → 사후 확률 {bayes_posterior(rate,0.99,0.05):>6.1%}")
# ══════ ④ 오탐률을 바꿔가며 탐색 (더 중요!) ══════
print("\n--- 오탐률의 영향 (발병률 0.1%, 정확도 99% 고정) ---")
for fpr in [0.05, 0.01, 0.001, 0.0001]:
print(f" 오탐률 {fpr:>7.2%} → 사후 확률 {bayes_posterior(0.001,0.99,fpr):>6.2%}")
# ══════ ⑤ 재검사 — 사후 확률을 다음 사전 확률로! ══════
print("\n--- 같은 검사를 반복하면? (사후 → 다음 사전) ---")
current = 0.001
for n in range(1, 4):
current = bayes_posterior(current, 0.99, 0.05) # ★ 갱신된 값을 다시 입력
print(f" {n}차 양성 후 → {current:.4f} ({current:.2%})")
🔗 GitHub에서 코드 보기 — 00_05_probability_bayes.py L103–L162
📘 코드 이해 — 수식이 코드의 어느 줄인가
| 수학공식 | 코드 | 설명 |
|---|---|---|
numerator = likelihood * prior | 분자 — 원하는 경로 하나 | |
fpr * (1 - prior) | 오탐 경로 — 1-prior가 | |
| (전확률) | evidence = numerator + ... | 분모 — 모든 경로의 합 |
prior/(1-prior) | 사전 오즈 | |
likelihood/fpr | 우도비 — 증거의 힘 | |
| 오즈 → 확률 | o/(1+o) | |
| 사후 → 새 사전 | current = bayes(current, ...) | ⭐ 반복 갱신의 핵심 |
🧩 코드 설명 — Python 기초
• 키워드 인자
bayes_posterior(prior=0.001, likelihood=0.99, ...)— 값 세 개가 모두 0~1 사이 소수라서 순서를 헷갈리기 매우 쉽습니다. 이름을 붙여 넘기면 실수를 원천 차단할 수 있습니다. 🛡️\
\
• 서식{p:.2%}— 소수를 백분율로 자동 변환해 출력합니다.0.0194→1.94%.*100을 직접 곱할 필요가 없습니다.\
\
• 정렬{rate:>6.1%}—>는 오른쪽 정렬,6은 너비입니다. 반복문 출력이 표처럼 줄 맞춰져 읽기 좋아집니다.\
\
• 숫자 구분자100_000— 파이썬은 큰 숫자에 밑줄을 넣어 읽기 쉽게 쓸 수 있습니다.100000과 완전히 같은 값입니다.\
\
•current = bayes_posterior(current, ...)⭐ — 이 장에서 가장 중요한 한 줄입니다. 이전 결과를 다시 입력으로 넣는 반복 갱신이며, 로봇의 위치 추정이나 온라인 학습이 이 구조로 동작합니다.
✅🟩 실행 결과
양성 판정 후 실제 발병 확률: 0.0194 (1.94%)
오즈 형태로 계산해도 동일 : 0.0194
[인원수] 진짜양성 99명 / 전체양성 5094명 = 0.0194
--- 발병률의 영향 (정확도 99%, 오탐 5% 고정) ---
발병률 0.1% → 사후 확률 1.9%
발병률 1.0% → 사후 확률 16.7%
발병률 10.0% → 사후 확률 68.8%
발병률 50.0% → 사후 확률 95.2%
--- 오탐률의 영향 (발병률 0.1%, 정확도 99% 고정) ---
오탐률 5.00% → 사후 확률 1.94%
오탐률 1.00% → 사후 확률 9.02%
오탐률 0.10% → 사후 확률 49.77%
오탐률 0.01% → 사후 확률 90.83%
--- 같은 검사를 반복하면? (사후 → 다음 사전) ---
1차 양성 후 → 0.0194 (1.94%)
2차 양성 후 → 0.2818 (28.18%)
3차 양성 후 → 0.8860 (88.60%)
🔍 결과 해석 — 세 가지 큰 교훈
① 세 가지 계산법이 모두 일치 — 공식(0.0194), 오즈 형태(0.0194), 인원수(99/5094=0.0194). 수학이 맞다는 삼중 검증입니다. ✅
② 사전 확률(발병률)이 결과를 지배한다 — 검사 성능은 전혀 바꾸지 않았는데 발병률만 0.1%→50%로 올리자 사후 확률이 1.9%→95.2%로 50배 뛰었습니다. 그래서 의사는 증상이 있는 사람에게만 검사를 권합니다 — 증상 자체가 사전 확률을 끌어올리기 때문입니다! 🏥
③ 희귀병에서는 "정확도"보다 "오탐률"이 훨씬 중요하다 ⭐ — 오탐률만 5%→0.01%로 낮추자 사후 확률이 1.94%→90.83%로 폭등했습니다. 민감도 99%는 그대로인데도 말이죠. 희귀한 것을 찾을 때는 "놓치지 않는 능력"보다 "헛짚지 않는 능력"이 결정적입니다. 스팸 필터가 "정상 메일을 스팸으로 분류하지 않는 것"에 목숨 거는 이유입니다. 📧
④ 재검사의 위력 🔁 — 1.94% → 28.18% → 88.60%. 1차 양성이 사전 확률을 0.1%에서 1.94%로 약 20배 끌어올렸고(우도비가 19.8이므로!), 그 상태에서 2차 양성이 또 20배를 밀어줍니다. 이것이 실제 의료 현장에서 정밀 재검사를 하는 수학적 근거입니다. 🏥
🛠️ 실습 3 🔵🔴
① 오탐률을 5%→1%로 낮췄을 때 사후 확률이 몇 배 올라가는지 계산하세요. (정답: 1.94%→9.02%, 약 4.6배)\
② 음성이 나왔을 때 실제로 건강할 확률(음성 예측도)을 구하는 함수를 작성하세요. (힌트: 우도가 )\
③ 재검사 코드를 5차까지 늘려보고, 확률이 1에 수렴하는 모습을 관찰하세요.\
④ 🚀 스팸 필터 만들기: 단어 3개의 우도비가 각각 10, 5, 0.2일 때, 사전 오즈 1:9에서 시작해 사후 확률을 구하세요. (힌트: 오즈에 우도비를 모두 곱하기)
🐞 실수하기 쉬운 부분 & 디버깅 포인트
❌ 실수 ① "정확도 99%니까 양성이면 99% 확률로 환자" ⭐ 가장 위험한 오해\
왜 틀리나? (우도)과 (사후)을 혼동한 것입니다. 2단계에서 배운 "방향이 다르면 값도 다르다"를 놓친 경우죠. 수정: 반드시 사전 확률을 곱하세요. 우도만 보고 판단하면 안 됩니다.❌ 실수 ② 분모에 오탐 경로를 빠뜨림\
evidence = likelihood * prior만 쓰면 분자와 분모가 같아져 사후 확률이 항상 1이 나옵니다. 확인법: 결과가 1.0이면 100% 이 버그입니다.print(evidence)로 분모를 찍어보세요.❌ 실수 ③ 를 1로 착각\
비환자 비율은1 - prior(0.999)이지 1이 아닙니다. 발병률이 낮으면 차이가 작아 버그를 발견하기 어렵습니다. 발병률 50%로 테스트해보면 즉시 드러납니다.❌ 실수 ④ 인자 순서 뒤바꿈\
bayes_posterior(0.99, 0.001, 0.05)처럼 사전확률과 우도를 바꿔 넣어도 에러 없이 그럴듯한 숫자가 나옵니다. 수정: 항상 키워드 인자를 쓰세요.✅ 검산 습관: ①
assert 0 <= p <= 1② 인원수로 다시 세보기 ③ 오즈 형태로 교차검증 — 세 방법이 일치하면 안심해도 됩니다. 🛡️
📂 이 장의 코드 폴더: book/00-math-overview-for-beginners/00-05-probability-bayes
책에 실린 코드를 장(절)별로 모아 둔 폴더입니다. 아래 파일을 열어 보거나, Colab에서 바로 실행해 보세요.
| 파일 | 용도 | 링크 |
|---|---|---|
00_05_probability_bayes.py | 책의 코드 블록 6개를 순서대로 모은 실행 스크립트 (# %% 셀 구분) | 코드 보기 |
00_05_probability_bayes.ipynb | 같은 코드를 블록당 셀 1개로 담은 주피터 노트북 | 보기 · Colab |
README.md | 코드 블록 목록 · 실행 방법 · 검증 결과 | 보기 |
🎬 medium-series/07-1-probability-bayes/ | Medium 시리즈 예제 — 같은 주제를 블로그용으로 다듬은 실습 | 폴더 · Colab |
🧪 저장소 검증 상태: ✅ 실행 OK
📄 00_05_probability_bayes.py L103–L136 · Block 3 (📍 00-05-C · 3단계 — 베이즈 정리: 병원 검사의 함정)
def bayes_posterior(prior, likelihood, false_positive_rate):
"""
P(H|E) = P(E|H)·P(H) / P(E) 를 계산합니다.
비유:
탐정이 '원래 의심 정도(prior)'에 '증거의 힘(likelihood)'을 곱한 뒤,
'증거가 나올 모든 경우(evidence)'로 나눠 갱신된 의심 정도를 구합니다.
매개변수:
prior : P(H) — 사전 확률(발병률)
likelihood : P(E|H) — 우도(민감도, 검사 정확도)
false_positive_rate: P(E|H^c) — 오탐률(위양성률)
반환:
P(H|E) — 사후 확률
"""
# 분자: '원인이 참이면서 증거가 나온' 경로 하나
numerator = likelihood * prior
# 분모: 전확률 정리 — 증거가 나올 모든 경로의 합
evidence = numerator + false_positive_rate * (1 - prior)
return numerator / evidence
def bayes_by_odds(prior, likelihood, false_positive_rate):
"""오즈 형태로 같은 값을 계산합니다: 사후오즈 = 사전오즈 × 우도비"""
prior_odds = prior / (1 - prior) # P(H)/P(H^c)
likelihood_ratio = likelihood / false_positive_rate # LR
posterior_odds = prior_odds * likelihood_ratio # 곱하기만!
return posterior_odds / (1 + posterior_odds) # 오즈 → 확률
# ══════ ① 기본 시나리오 ══════
p = bayes_posterior(prior=0.001, likelihood=0.99, false_positive_rate=0.05)
print(f"양성 판정 후 실제 발병 확률: {p:.4f} ({p:.2%})")
print(f"오즈 형태로 계산해도 동일 : {bayes_by_odds(0.001, 0.99, 0.05):.4f}")
▶ 실행 결과 — 위 코드를 실행한 출력 (직접 실행: Windows · Python 3.11.2 · numpy 2.4.6 · scipy 1.17.1, 앞 블록부터 순서대로 실행)
양성 판정 후 실제 발병 확률: 0.0194 (1.94%)
오즈 형태로 계산해도 동일 : 0.0194
📄 00_05_probability_bayes.py L157–L162 · Block 3 (📍 00-05-C · 3단계 — 베이즈 정리: 병원 검사의 함정)
# ══════ ⑤ 재검사 — 사후 확률을 다음 사전 확률로! ══════
print("\n--- 같은 검사를 반복하면? (사후 → 다음 사전) ---")
current = 0.001
for n in range(1, 4):
current = bayes_posterior(current, 0.99, 0.05) # ★ 갱신된 값을 다시 입력
print(f" {n}차 양성 후 → {current:.4f} ({current:.2%})")
▶ 실행 결과 — 위 코드를 실행한 출력 (직접 실행: Windows · Python 3.11.2 · numpy 2.4.6 · scipy 1.17.1, 앞 블록부터 순서대로 실행)
--- 같은 검사를 반복하면? (사후 → 다음 사전) ---
1차 양성 후 → 0.0194 (1.94%)
2차 양성 후 → 0.2818 (28.18%)
3차 양성 후 → 0.8860 (88.60%)
👉 Open in Colab — 00_05_probability_bayes.ipynb · 설치 없이 브라우저에서 셀을 위에서부터 순서대로 실행하세요.
git clone https://github.com/otoloism/Zero_to_ML_Master.git
cd Zero_to_ML_Master
python -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -r book/requirements.txt # 책 전체용 (PyTorch 등 포함, 용량 큼)
# 이 장만 실행한다면 이것만으로 충분합니다: pip install numpy scipy
cd book/00-math-overview-for-beginners/00-05-probability-bayes
python 00_05_probability_bayes.py
⭐ 코드가 도움이 되셨다면 GitHub 저장소에 Star를 눌러 주세요. 새 장의 코드가 올라올 때 가장 먼저 알 수 있습니다.
🍴 직접 고쳐 보며 실험하고 싶다면 Fork 해서 내 저장소에서 마음껏 바꿔 보세요. 오류 제보·개선 제안은 Issues로 환영합니다!
다음 글은 00-05 확률·베이즈 정리 (2부) — 정규분포·소프트맥스·교차 엔트로피 🔔 입니다. 자연계의 종 모양 곡선인 정규분포와 68-95-99.7 규칙, 점수를 확률로 바꾸는 소프트맥스, 그리고 분류기를 채점하는 교차 엔트로피와 그 놀랍도록 단순한 기울기 까지, "AI는 확률로 대답한다"는 말의 의미를 확인합니다.
⬅️ 이전 글: #06 경사하강법 완전 정복 — 학습률·발산 조건·선형회귀·자동미분 (딥러닝 수학, 2부) \
➡️ 다음 글: #08 정규분포·소프트맥스·교차 엔트로피 — AI는 확률로 대답한다 (2부)
이 시리즈는 수학을 놓았던 비전공자가 AI를 직접 구현하기까지의 공부 기록을 그대로 옮긴 연재입니다. 원문 책, 코드, 개념 사전을 함께 보면 훨씬 빨리 늡니다. 🚀
| 바로가기 | 이런 분께 | |
|---|---|---|
| 📘 책 (위키독스, 무료) | Zero to 머신러닝 딥러닝 Master | 5권 · 20개 장 · 163개 절 전체를 순서대로 읽고 싶은 분 |
| 📄 이 글의 원문 | 00-05 확률·베이즈 정리 — 병원 검사의 함정 (1부) | 연습문제·해설까지 전부 보고 싶은 분 |
| 💻 실습 코드 (GitHub) | otoloism/Zero_to_ML_Master | .py·.ipynb를 Colab에서 바로 돌려보고 싶은 분 |
| 🏠 머신러닝 백과사전 | mldict.net | 본문 속 핵심 개념을 카드로 다시 확인하고 싶은 분 |
| ✍️ 기술 블로그 | wikidocs.net/blog/@mldict | 새 글 알림을 가장 먼저 받고 싶은 분 |
⭐ 도움이 되셨다면 velog 팔로우와 GitHub Star를 눌러 주세요. 다음 글을 쓰는 가장 큰 힘이 됩니다. 궁금한 점은 댓글로 편하게 남겨 주세요! 🙌