Absctract

나이브 베이즈 분류는 베이지안 방법론을 이용해 classification 문제를 해결하고자 한다. 특히 자연어처리분야의 텍스트 분류문제에 많이 활용된다. 기본적인 원리는 다음과 같다.

혈액형 안경 합격
A yes pass
A no fail
A yes pass
A yes fail
B no pass
B no fail
B no pass

위의 table에서 혈액형이 A이면서 안경을 쓴 사람이 합격할 확률은 다음과 같이 나타낼 수 있다.

$$P(pass|혈액형=A, 안경=yes)$$

실제 관측결과 혈액형이 A이고 안경을 쓴 사람은 3명이고, 그중 2명이 합격을 했다. 따라서 별다른 가정이 없다면 이 값을 \(\frac{2}{3}\) 로 추정하는게 바람직할 것이다.
하지만 혈액형이 B이고, 안경을 쓴 사람의 경우는 관측되었지 않았기 때문에 예측을 할 수가 없다. 이러한 경우 각각의 조건을 따로 분리하여 다음과 같이 예측에 이용할 수 있을 것이다.
“혈액형이 B인 사람은 2/3가 합격했고, 안경을 쓴사람도 2/3가 합격했으므로 혈액형이 B이고, 안경을 썼으면 합격할 확률이 50%보다는 높을 것이다”
이와같은 아이디어가 나이브 베이즈의 Base가 된다. 나이브 베이즈는 문장을 학습해 단어의 등장확률을 계산하는 등 자연어 처리분야에서 많이 활용된다. 위처럼 특정 단어들이 연속적으로 나오는 경우를 관측하기 힘들기 때문이다.

수식

나이브 베이즈를 이해하기전에 알아야할 몇가지 조건부 확률 수식에 대해 알아보자.
우리가 궁금한 것은 어떠한 조건이 주어졌을때 해당 결과(pass or fail)가 나올 확률이다. 즉 베이지안의 관점에서 \(P(pass|x_1, x_2,...,x_n)\)가 \(P(fail|x_1, x_2,...,x_n)\) 보다 크다면 주어진 조건(\(x_1, x_2\))에서 결과를 pass로 예측하는것이 바람직하다. 또한 두 값은 조건부 확률의 정의에 다음과 같이 나타낼 수 있다.

$$P(pass,x_1, x_2,...,x_n)/P(x_1, x_2,...,x_n),\ P(fail,x_1,x_2,...,x_n)/P(x_1,x_2,...,x_n)$$

이 식에서 알 수 있듯이 두 확률을 비교할때는 분모가 관여하지 않는다. 즉 분자의 값들만을 이용해 바람직한 예측값을 계산할 수 있다는 뜻이다. 그리고 위의 식은 다음과같이 나타낼 수 있다.

$$P(pass,x_1,...,x_n)=P(x_1,...,x_n|pass)\times P(pass)$$

$$=\frac{P(pass,x_1)}{P(pass)}\times \frac{P(pass,x_1,x_2)}{P(pass,x_1)}\times \cdots \times \frac{P(pass,x_1,...,x_n)}{P(pass,x_1,...,x_{n-1})}\times P(pass)$$

그리고 여기에 Naive한 가정(각 \(x_i\)는 나머지 \(x_j\)들에 대해 y가 주어진 조건하에 서로 독립이다 - 조건부 독립) 이 추가되면 위의 수식을 간단하게 변환할 수 있다. 참고로 조건부 독립가정은 다음과 같다.

$$\frac{P(pass,x_1,...,x_k)}{P(pass,x_1,...,x_{k-1})}=P(x_k|pass,x_1,...,x_{k-1})=P(x_k|pass)$$

결과적으로 pass, fail의 조건부 확률은 다음과 같이 나타낼 수 있다.

$$P(pass|x_1,...,x_n) \propto P(x_1,...,x_n|pass)\times P(pass)$$ $$=P(pass)\times \frac{P(pass,x_1)}{P(pass)}\times \cdots \times \frac{P(pass,x_n)}{P(pass)}=P(pass)\prod^n_{i=1}P(x_i|pass),$$ $$P(fail|x_1,...,x_n) \propto P(fail)\prod^n_{i=1}P(x_i|pass)$$

그리고 베이지안의 관점에서 \(pass\)할 확률은 아래와 같이 계산할 수 있다.

$$\frac{P(pass|x_1,x_2,...,x_n)}{P(pass|x_1,x_2,...,x_n)+P(fail|x_1,x_2,...,x_n)}$$

다시 위의 예시로 돌아가면 \(P(혈액형=B| pass)= 1/2,\ P(혈액형=B|fail)=1/3,\ P(안경=no|pass)=1/2,\) \(P(안경=no|fail)=2/3\) 와같이 나타낼 수 있다.
결과적으로 혈액형이 B이고, 안경을 쓴 사람의 합격확률은 아래와 같이 계산할 수 있을 것이다.

$$P(pass,혈액형=B,안경=yes) = P(pass)P(혈액형=B|pass)P(안경=yes|pass) = \frac{4}{7}\times \frac{1}{2} \times \frac{1}{2}=\frac{1}{7}$$

$$P(fail,혈액형=B,안경=yes) = P(fail)P(혈액형=B|fail)P(안경=yes|fail) = \frac{3}{7}\times \frac{1}{3} \times \frac{2}{3}=\frac{2}{21}$$

\(P(pass|혈액형=B,안경=yes)=\frac{1}{7}/(\frac{1}{7}+\frac{2}{21})=\frac{3}{5}\)

코드

Make dataset

import pandas as pd
import numpy as np
blood_types = ['A', 'B', 'AB', 'O']
glasses = [0, 1]
grade = ['A','B','C','D','F']
blood_types = ['A', 'A', 'B', 'B', 
               'B','AB', 'AB', 'O']
glasses = [0, 1, 0, 0, 1, 1, 0, 1]
passes = [0, 1, 1, 1, 0, 0, 1, 1]
df = pd.DataFrame({'blood_type': blood_types, 
                   'glasses': glasses,
                   'pass': passes
                  })
X = df.iloc[:,:-1]
y = df.iloc[:,-1]
df
blood_type glasses pass
A 0 0
A 1 1
B 0 1
B 0 1
B 1 0
AB 1 0
AB 0 1
O 1 1

Train

def get_ratio(df, cx, cy):
    df = df.copy()
    df['count'] = 1  # count
    pivot = pd.pivot_table(df, index=cx, columns=cy, values='count', aggfunc=np.sum)
    pivot['sum'] = pivot.sum(axis=1)
    pivot = pivot.apply(lambda x:x[:-1] / x['sum'],axis=1)
    pivot = pivot.fillna(0)
    return pivot.to_dict()
		
def train(X, y):
    df = pd.concat([X,y], axis=1)
    col_x = df.columns[:-1]  # 열 이름들
    col_y = df.columns[-1]

    param = {}  # 학습된 파라미터들
    for cx in col_x:
        param[cx] = get_ratio(df, cx, col_y)

    y_ratio = {}  # 조건부 확률이 아닌 그냥 p(C_k)
    for y_val in y.unique():
        y_ratio[y_val] = np.mean(df[col_y] == y_val)
    param['y'] = y_ratio
    return param
		
train(X,y)
{'blood_type': {0: {'A': 0.5, 'AB': 0.5, 'B': 0.3333333333333333, 'O': 0.0},
  1: {'A': 0.5, 'AB': 0.5, 'B': 0.6666666666666666, 'O': 1.0}},
 'glasses': {0: {0: 0.25, 1: 0.5}, 1: {0: 0.75, 1: 0.5}},
 'y': {0: 0.375, 1: 0.625}}

blood_type을 예시로 설명하면 \(P(y=0|blood\_type=A)=0.5,\ P(y=1|blood\_type=A)=0.5\) 라는 뜻이다. 또 \(P(y=0)=0.375,\ P(y=1)=0.625\) 이다.
blood_type이 B이고 pass=1인 경우는 2가지, blood_type이 B이고 pass=0인 경우는 한가지이므로
\(P(y=0|blood\_type=B)=0.666666\) 이 된다.

Test

X_test = pd.DataFrame({'blood_type': ['A','B','O'], 'glasses': [0, 1, 0]})
def test(X_test):
    col_x = X_test.columns
    y_pred = []
    for i in range(len(X_test)):
        row = X_test.iloc[i]
        probs = []

        for y_val in y.unique():
            prob = param['y'][y_val]
            for i in range(len(col_x)):
                prob *= param[col_x[0]][y_val][row[0]]

            probs.append(prob)
        y_pred.append(probs)

    y_pred = pd.DataFrame(y_pred)
    y_pred['sum'] = y_pred.sum(axis=1)
    y_pred = y_pred.apply(lambda x:x[:-1]/x['sum'], axis=1)
    y_pred.columns = y.unique()
    return y_pred
test(X_test)

X_test

blood_type glasses pred_pass pred_fail
A 0 0.375 0.625
B 1 0.130435 0.869565
O 0 0 1

결과 해석

frequentist의 관점에서 살펴보면 \(P(y=0|blood\_type=A,glasses=0)=P(y=0|blood\_type=A,glasses=1)=0.5\) 이지만,
Naive한 가정하에 bayesian 관점에서 확률은 각각 0.375, 0.625로 차이를 보임을 알 수 있다.
Frequentist vs Bayesian : 만약 blood_type과 glasses가 조건부(given pass)독립이 아니라면 빈도주의관점에서 접근하는게 더 정확하다.
하지만 주어진 데이터가 얼마없거나, X의 차원이 클 경우 각각의 빈도당 표본이 적어 베이지안 관점이 좀더 정확한 결과를 낼 수 있을 것이다.