for textmining

베이즈 규칙과 다양한 문제들

|

이번 글에서는 베이즈 규칙을 활용해 풀 수 있는 여러 문제를 살펴보도록 하겠습니다. 이 글은 고려대 김성범 교수님 강의와 ‘Think Bayes(앨런 B. 다우니 지음, 권정민 옮김, 한빛미디어 펴냄)’를 정리했음을 먼저 밝힙니다. 베이지안 추론과 관련해서는 이곳이 글, 문서 분류를 위한 나이브 베이지안 분류기와 관련해서는 이곳을 참고하시면 좋을 것 같습니다. 그럼 시작하겠습니다.

베이즈 규칙

일반적으로 사건 $A_1$, $A_2$, $A_3$가 서로 배반(mutually exclusive)이고 $A_1$, $A_2$, $A_3$의 합집합이 표본공간(sample space)과 같으면 사건 $A_1$, $A_2$, $A_3$는 표본공간 $S$의 분할이라고 정의합니다. 우리가 관심있는 사건 $B$가 나타날 확률을 그림과 식으로 나타내면 다음과 같습니다.

$P(B)$를 조건부확률의 정의를 이용해 다시 쓰면 아래와 같습니다. 이를 전확률 공식(Law of Total Probability) 또는 베이즈 법칙이라고 합니다.

보통 $P(A_1)$, $P(A_2)$, $P(A_3)$는 미리 알고 있다는 의미의 사전확률(prior probability)로 불립니다. $P(B$|$A1)$, $P(B$|$A2)$, $P(B$|$A3)$는 우도(likelihood probability)라 부릅니다.

그럼 우리가 관심있는 사건인 $B$가 $A_1$에 기인했을 조건부확률은 어떻게 구할까요? 바로 아래와 같이 구할 수 있습니다.

$P(A1$|$B)$는 사건 B를 관측한 후에 그 원인이 되는 사건 $A$의 확률을 따졌다는 의미의 사후확률(posterior probability)로 정의됩니다. 사후확률은 사건 $B$의 정보가 더해진, 사전확률의 업데이트 버전 정도라고 생각하면 좋을 것 같습니다. (Posterior probability is an updated version of prior probability) 같은 방식으로 $P(A2$|$B)$, $P(A3$|$B)$도 구할 수 있습니다.

한번 예를 들어보겠습니다. 어떤 이름모를 질병에 걸린 환자가 전체 인구의 약 1% 정도 되는 것으로 알려져 있다고 칩시다. 그렇다면 전체 인구라는 표본공간에서 질병에 걸릴 확률 P(D)는 0.01, 그렇지 않을 확률 P(~D)는 0.99입니다. 이것이 바로 우리가 이미 알고 있는 사전확률이 되겠네요.

질병 발생 여부를 측정해주는 테스트의 정확도는 이렇다고 합니다. 진짜 환자를 양성이라고 정확하게 진단할 확률 P(+|D)은 97%, 정상환자를 양성이라고 오진할 확률 P(+|~D)은 6%입니다. 이것이 바로 우도입니다.

그럼 진단테스트 결과 양성이라고 나왔는데 실제 환자일 확률 P(D|+)는 얼마일까요? 이건 우리가 이미 알고 있는 정보를 활용해 아래와 같이 구할 수 있습니다. 이것이 바로 사후확률입니다. 즉 ‘진단’이라는 사건의 정보를 더해 ‘질병에 걸릴 확률’이라는 사전확률을 업데이트한거죠.

그런데 왜 이렇게 복잡하게 사후확률을 구하는거냐고요? 실제로 사후확률은 구하기 어려운 경우가 많다고 합니다. 그에 반해 사전확률은 우리가 이미 알고 있는 값이고, 우도는 비교적 계산하기 수월합니다. 그래서 전확률법칙과 사전확률과 우도를 활용해 사후확률을 도출해내는 것이죠.

다시 말해 우리가 알고 싶은 확률을 단박에 계산하기가 까다로울 때 조건과 결과를 뒤집어서 우회적으로 계산하는 것, 이것이 베이즈 모델의 강점이라고 말할 수 있겠습니다.

쿠키 문제

쿠키가 들어 있는 그릇 두 개가 있다고 가정해보겠습니다. 첫번째 그릇에는 바닐라 쿠키 30개와 초콜렛 쿠키 10개가 들어있고, 두번째 그릇에는 두 가지 쿠키가 종류별로 20개씩 들어 있습니다.

어떤 그릇인지 보지 않고 한 그릇에서 임의로 쿠키를 집었는데 바닐라 쿠키였다고 칩시다. 그렇다면 이 때 ‘이 바닐라 쿠키가 그릇 1에서 나왔을 가능성’은 얼마일까요? 사실 이 확률을 계산하기는 쉽지 않습니다.

그런데 ‘그릇1에서 바닐라 쿠키가 나올 확률’은 30/40으로 구하기 쉽습니다. 베이즈 규칙을 활용해 조건절과 결과절을 바꾸어 원하는 확률을 구해보겠습니다. 저 아래 표의 첫번째 열을 기준으로 계산한 식과 용어 설명은 다음과 같습니다.

$P(H)$ : 어떤 쿠키를 골랐던지 상관없이 그릇1을 골랐을 확률. 문제에서는 그릇을 임의로 선택한 것이므로 0.5라고 가정할 수 있습니다. 이를 데이터를 보기 전의 가설의 확률, 즉 사전확률입니다.

$P(D$|$H)$ : 그릇1에서 바닐라 쿠키가 나올 확률. 3/4입니다. 이를 데이터가 가설에 포함될 확률, 즉 우도입니다.

$P(D)$ : 바닐라 쿠키를 고를 확률입니다. 각 그릇을 고를 확률이 동일하고 그릇에 동일한 쿠키가 들어있으므로 어떤 그릇을 택하든 바닐라 쿠키를 고를 확률도 같습니다. 두 그릇에 50개 바닐라 쿠키와 30개의 초콜렛 쿠키가 들어있으므로 $P(V)$는 5/8이 됩니다. 이를 어떤 가설에든 포함되는 데이터의 비율, 즉 한정상수입니다.

$P(H$|$D)$ : 바닐라 쿠키가 그릇1에서 나왔을 확률. 우리가 알고 싶은 확률입니다. 이를 데이터를 확인한 이후의 가설 확률, 즉 사후확률입니다. 이는 베이즈 이론을 통시적(diachronic)으로 해석한 것으로 가설에 대한 확률이 시간에 따라 새로운 데이터를 접하게 되면서 달라진다, 다시 말해 데이터 $D$의 관점에서 봤을 때 가설 $H$의 확률을 업데이트해 준다는 방식으로 이해하는 것입니다.

여기에서 한정상수 $P(D)$는 다음과 같은 두 가지 조건을 만족할 경우 어떤 가설이든지 같은 값을 지니게 돼 계산을 생략할 수 있습니다.

상호배제(mutually exclusive) : 집합 중 하나의 가설만 참이다

전체포괄(collectively exhaustive) : 고려 대상 가설 이외에 다른 가설이 전혀 없는 경우

쿠키 문제를 가설별로 표로 나타내면 다음과 같습니다.

항목 가설1(그릇1) 가설2(그릇2)
사전확률 $P(H)$ 1/2 1/2
우도 $P(D$|$H)$ 3/4 1/2
사전확률 × 우도 3/8 1/4
한정상수 $P(D)$ 5/8 5/8
사후확률 $P(H$|$D)$ 3/5 2/5

위 표에서 한정상수 $P(D)$ 계산을 생략할 수 있는 이유에 대해 살펴보겠습니다. 사후확률은 사전확률에 우도를 곱한 뒤 한정상수를 나누어 구합니다. 그런데 한정상수가 가설에 상관없이 같은 값을 가진다면 사전확률에 우도를 곱한 걸 모두 더한 값이 1이 되도록 정규화하면 한정상수 계산을 생략해도 동일한 결과가 나옵니다.

위 표에서 사전확률에 우도를 곱한 값은 그릇1이 3/8, 그릇2가 1/4입니다. 이를 모두 더하면 5/8이 되는데요. 이 값이 1이 되도록 정규화를 하려면 그릇1과 그릇2가 가진 값에 8/5를 각각 곱해주면 됩니다. 그런데 이 값은 정확히 사후확률과 일치합니다. 한정상수가 가설에 관계없이 동일하다면 계산을 생략해도 관계 없다는 이야기입니다.

쿠키문제를 파이썬 코드로 풀어보겠습니다.

import thinkbayes as tb

class Cookie(tb.Pmf):
    """A map from string bowl ID to probablity."""
	
    # 사전확률 정의
    def __init__(self, hypos):
        """Initialize self.

        hypos: sequence of string bowl IDs
        """
        tb.Pmf.__init__(self)
        for hypo in hypos:
            # 가설마다 동일한 사전확률(1) 부여
            self.Set(hypo, 1)
        # 총합이 1이 되도록(확률이 되도록) 정규화
        # 가설이 두 개이므로 정규화 결과 가설마다 0.5 사전확률 가짐
        self.Normalize()

    # 사후확률 구하기
    def Update(self, data):
        """Updates the PMF with new data.

        data: string cookie type
        """
        for hypo in self.Values():
            # 우도 구하기
            like = self.Likelihood(data, hypo)
            # 사전확률에 우도 업데이트(곱)
            self.Mult(hypo, like)
        # 사전확률 x 우도 모든 값의 합이 1이 되도록 정규화
        # 한정상수가 동일하므로 사후확률 계산에서 생략
        self.Normalize()

    mixes = {
        'Bowl 1':dict(vanilla=0.75, chocolate=0.25),
        'Bowl 2':dict(vanilla=0.5, chocolate=0.5),
        }

    # 우도 함수
    def Likelihood(self, data, hypo):
        """The likelihood of the data under the hypothesis.

        data: string cookie type
        hypo: string bowl ID
        """
        # mixes 데이터 자체가 우도를 나타내고 있으므로 
        # 그냥 불러오기만 하면 우도함수 정의 끝
        mix = self.mixes[hypo]
        like = mix[data]
        return like

# 가설정의 (가설1=그릇1, 가설2=그릇2)
hypos = ['Bowl 1', 'Bowl 2']

# 가설에 따른 객체 선언
pmf = Cookie(hypos)

# 사후확률 구하기
# 사전확률에 우도 업데이트 + 정규화
pmf.Update('vanilla')

# 결과 출력
for hypo, prob in pmf.Items():
    print hypo, prob

M&M 문제

여러 색의 설탕이 입혀진 작은 초콜렛 M&M은 만드는 회사는 시간에 따라 색 조합을 바꿔 왔습니다. 이 회사는 1995년에 파란색을 추가했는데요. 그 전에는 봉지 내 색 조합이 갈색 30%, 노랑 20%, 빨강 20%, 녹색 10%, 주황 10%, 황갈색 10%였습니다. 파란색을 추가한 뒤에는 파랑 24%, 녹색 20%, 주황 16%, 노랑 14%, 빨강 13%, 갈색 13%가 됐습니다.

한 친구가 M&M을 두 봉지 샀는데 각각 생산년도가 1994년, 1996년이라고 칩시다. 생산년도를 알려주지 않고 각 봉지에서 M&M을 하나씩 꺼냈을 때 한 알은 노란색이고 한 알은 녹색이었습니다. 문제입니다. 이 때 노랑 초콜렛이 1994년에 생산한 봉지에서 나왔을 확률은 얼마일까요?

먼저 가설을 만들어보겠습니다. 노란 초콜렛은 봉지1에서 녹색 초콜렛은 봉지2에서 꺼냈다고 칩시다. 이 때 가설은 다음과 같습니다.

가설1 : 봉지1은 1994년에 생산했고 봉지2는 1996년에 생산했다.

가설2 : 봉지1은 1996년에 생산했고 봉지2는 1994년에 생산했다.

이를 쿠키문제와 마찬가지로 표로 만들면 다음과 같습니다. 쿠키문제와 마찬가지로 한정상수가 같은 값이기 때문에 계산을 생략할 수 있습니다.

항목 가설1 가설2
사전확률 $P(H)$ 1/2 1/2
우도 $P(D$|$H)$ 0.2×0.2 0.14×0.1
사전확률 × 우도 0.02 0.007
사후확률 $P(H$|$D)$ 20/27 7/27

M&M 문제의 파이썬 풀이는 다음과 같습니다. Think Bayes 저자는 베이즈 규칙을 언제든 사용할 수 있도록 Suite 객체로 캡슐화하였습니다. 쿠키문제의 풀이와 본질적으로 다르지 않다는 얘기입니다. 어쨌든 Suite 객체로 베이즈 문제를 풀 때는 데이터의 우도 값과 우도 함수만 정의해 주면 됩니다.

from thinkbayes import Suite


class M_and_M(Suite):
    """Map from hypothesis (A or B) 
    to probability."""
	
    # 1994년 데이터
    mix94 = dict(brown=30,
                 yellow=20,
                 red=20,
                 green=10,
                 orange=10,
                 tan=10)
	
    # 1996년 데이터
    mix96 = dict(blue=24,
                 green=20,
                 orange=16,
                 yellow=14,
                 red=13,
                 brown=13)
	
    # 가설 정의
    hypoA = dict(bag1=mix94, bag2=mix96)
    hypoB = dict(bag1=mix96, bag2=mix94)
    hypotheses = dict(A=hypoA, B=hypoB)
	
    # 우도함수 정의
    def Likelihood(self, data, hypo):
        """Computes the likelihood of the data 
        under the hypothesis.
        hypo: string hypothesis (A or B)
        data: tuple of string bag, string color
        """
        bag, color = data
        # 데이터가 우도이므로 참조하기만 하면 됨
        mix = self.hypotheses[hypo][bag]
        like = mix[color]
        return like

# 가설과 객체 선언
suite = M_and_M('AB')

# 사전확률에 데이터를 반영해 사후확률 계산
suite.Update(('bag1', 'yellow'))
suite.Update(('bag2', 'green'))

# 결과 출력
suite.Print()

주사위 문제

4면체, 6면체, 8면체, 12면체, 20면체 주사위가 든 상자가 있다고 가정해 봅시다. 상자에서 임의로 주사위 하나를 집어서 던졌더니 12가 나왔습니다. 그러면 각 주사위를 선택했을 확률은 어떻게 될까요?

여기에서 사전확률은 주사위를 선택할 확률, 우도는 해당 주사위를 던졌을 때 12가 나올 확률입니다. 이를 표로 나타내면 다음과 같습니다.

항목 4면체 6면체 8면체 12면체 20면체
사전확률 $P(H)$ 1/5 1/5 1/5 1/5 1/5
우도 $P(D$|$H)$ 0 0 0 1/12 1/20
사전확률 × 우도 0 0 0 1/60 1/100
사후확률 $P(H$|$D)$ 0 0 0 5/8 3/8

파이썬 코드는 다음과 같습니다.

import thinkbayes as tb

class Dice(tb.Suite):
    # 우도함수 정의
    def Likelihood(self, data, hypo):
        if hypo < data:
            return 0
        else:
            return 1.0/hypo

# 가설정의 및 객체 선언
suite=Dice([4,6,8,12,20])

# 사전확률에 우도 곱해줌
# 우도는 관찰결과(데이터)
# 이를 통해 사후확률 계산
suite.Update(12)

# 결과 출력
suite.Print()

위 코드의 실행 결과는 다음과 같습니다.

4 0.0 6 0.0 8 0.0 12 0.625 20 0.375

기본코드

객체 생성에 쓰인 코드는 thinkbayes.py인데 정리 겸 하단에 첨부하였습니다. 출처는 이곳입니다.

Comments