귀퉁이 서재

머신러닝 - 1. 나이브 베이즈 분류 (Naive Bayes Classification) 본문

머신러닝

머신러닝 - 1. 나이브 베이즈 분류 (Naive Bayes Classification)

Baek Kyun Shin 2019. 7. 14. 13:54

나이브 베이즈는 스팸 메일 필터, 텍스트 분류, 감정 분석, 추천 시스템 등에 광범위하게 활용되는 분류 기법입니다. 나이브 베이즈 분류에 대해서 배우기 위해서는 베이즈 정리를 먼저 알아야 합니다. 베이즈 정리를 모르신다면 DATA - 10. 베이즈 추정(Bayesian Estimation)을 먼저 보고 오시기 바랍니다.

머신러닝을 통해 어떤 동물의 사진이 있을 때 그 동물이 호랑이인지 고양이인지 얼룩말인지 등을 구분할 수 있습니다. 사전에 수많은 호랑이, 고양이, 얼룩말 사진에 대해 학습을 시킵니다. 다양한 자세, 표정, 생김새, 털의 색깔 등을 가진 호랑이, 고양이, 얼룩말에 대해 '이 사진은 호랑이고, 이 사진은 고양이야'라고 학습시키는 것입니다. 학습된 머신러닝 모델은 이제 호랑이, 고양이, 얼룩말을 정확히 분류할 수 있습니다. 이제는 학습시 사용되었던 사진이 아닌 새로운 사진을 갖다 줬을 때도 정확히 분류할 수 있습니다. 이렇게 사전 데이터를 기반으로 충분히 학습시키는 방법을 지도학습(Supervised learning)이라고 합니다. 

지도학습을 하기 위한 첫 스텝은 Feature와 Label을 파악하는 것입니다. Label은 우리가 원하는 분류 결과입니다. 위 예시에서는 호랑이, 고양이, 얼룩말이 Label입니다. 이 Label 결과에 영항을 주는 요소를 Feature라고 합니다. 동물의 자세, 표정, 생김새, 털의 색깔 등이 바로 Feature입니다. 즉 수많은 동물의 자세, 표정, 생김새, 털의 색깔(Feature)을 기반으로 그 동물이 호랑이인지 고양이인지 얼룩말인지(Label) 분류를 하는 것입니다.

나이브 베이즈 분류는 지도학습의 일종입니다. 따라서 Feature와 Label이 필요합니다. Feature에 따라 Label을 분류하는데 베이즈 정리를 사용하는 것이 특징입니다. 또한 모든 Feature가 서로 독립(independent)이어야 한다는 가정이 필요합니다.

아래의 컨텐츠는 Udacity와 DataCamp의 내용을 Mix해서 정리해놓은 것입니다. (Reference1)

Classification Workflow

분류를 하기 위한 첫 스텝은 Feature와 Label을 파악하는 것이라고 했습니다. Label은 우리가 원하는 분류 결과입니다. 스팸 메일을 예로 들면 스팸 메일인지 아닌지의 여부가 Label입니다. 이 Label 결과에 영항을 주는 요소가 Feature입니다. 역시 스팸 메일을 예로 들면, 스팸 메일의 제목 및 내용에 기재된 광고성 단어, 비속어, 성적 용어 등입니다. 

Feature -> 광고성 단어 개수, 비속어 개수, 성적 용어 개수 등... (각각이 하나의 Feature이며, 하나의 분류 모델에는 여러 개의 Feature가 있음)

Label -> 스팸 메일인 경우 Label = 1, 스팸 메일이 아닌 경우 Label = 0

분류는 두 단계로 나누어집니다. 훈련 단계와 테스트 단계입니다. 훈련 단계에서는 주어진 Training data set을 통해 classifier 모델을 훈련시키고, 테스트 단계에서는 classfier 모델의 성능(performance)을 평가합니다. 성능(performance)은 정밀도(accuracy),  정확성(Precision), 재현율(Recall) 등으로 측정할 수 있습니다. (정밀도, 정확성, 재현율이란 무엇인가? Reference 2) 

출처: DataCamp

나이브 베이즈 분류기(Naive Bayes Classifier)란 무엇인가?

위에서 설명했듯이 나이브 베이즈 분류는 베이즈 정리에 기반한 통계적 분류 기법입니다. 가장 단순한 지도 학습 (supervised learning) 중 하나입니다. 나이브 베이즈 분류기는 빠르고, 정확하며, 믿을만한 알고리즘입니다. 정확성도 높고 대용량 데이터에 대해 속도도 빠릅니다.

나이브 베이즈는 feature끼리 서로 독립이라는 조건이 필요합니다. 즉, 스펨 메일 분류에서 광고성 단어의 개수와 비속어의 개수가 서로 연관이 있어서는 안 됩니다. 

나이브 베이즈는 어떻게 동작하는가?

예를 들어 설명하겠습니다. 날씨 정보와 축구 경기 여부에 대한 데이터가 있습니다. 날씨에 대한 정보를 기반으로 축구를 할것인지 안 할 것인지 확률을 구하는 예제입니다.

출처: DataCamp

먼저, 맨 왼쪽 테이블을 보겠습니다. 날씨에 따라 축구를 했는지 안했는지에 대한 과거 데이터입니다. 이 과거 데이터를 먼저 Training 시켜 모델을 만든 뒤 그 모델을 기반으로 어떤 날씨가 주어졌을 때 축구를 할지 안 할지 판단하는 것이 목적입니다.

Frequency Table은 주어진 과거 데이터를 횟수로 표현한 것입니다. Likelihood Table 1은 각 Feature (여기서는 날씨)에 대한 확률, 각 Label (여기서는 축구를 할지 말지 여부)에 대한 확률을 구한 것입니다. Likelihood Table 2는 각 Feature에 대한 사후 확률을 구한 것입니다.

Feature가 하나일 때 나이브 베이즈 분류

문제 1. 날씨가 overcast일 때 경기를 할 확률은?

P(Yes|Overcast) = P(Overcast|Yes) P(Yes) / P(Overcast)   <- 베이즈 정리에 의해

1. 사전 확률

P(Overcast) = 4/14 = 0.29

P(Yes) = 9/14 = 0.64

2. 사후 확률

P(Overcast|Yes) = 4/9 = 0.44

3. 베이즈 정리 공식에 대입

P(Yes|Overcast) = P(Overcast|Yes) P(Yes) / P(Overcast) = 0.44 * 0.64 / 0.29 = 0.98

즉, 날씨가 Overcast일 때 축구를 할 확률이 0.98이라는 뜻입니다.

문제 2. 날씨가 Overcast일 때 경기를 하지 않을 확률은?

P(No|Overcast) = P(Overcast|No) P(No) / P(Overcast)

1. 사전 확률

P(Overcast) = 4/14 = 0.29

P(No) = 5/14 = 0.36

2. 사후 확률

P(Overcast|No) = 0/5 = 0

3. 베이즈 정리 공식에 대입

P(No|Overcast) = P(Overcast|No) P(No) / P(Overcast) = 0 * 0.36 / 0.29 = 0

즉, 날씨가 Overcast일 때 축구를 할 확률이 0이라는 뜻입니다.

P(Yes|Overcast) = 0.98, P(No|Overcast) = 0입니다. 즉, 날씨가 Overcast일 때 축구를 하는 확률은 0.98, 축구를 하지 않을 확률은 0입니다. 두 확률을 비교한 뒤 더 높은 확률의 Label로 분류를 하면 됩니다. 두 확률을 비교했을 때 'Yes' Label의 확률이 0.98로 더 높습니다. 따라서 나이브 베이즈 분류기는 날씨가 Overcast일 때 축구를 할 것이라고 판단합니다.

Feature가 Multiple일 때 나이브 베이즈 분류

 

출처: DataCamp

문제 1. 날씨가 overcast, 기온이 Mild일 때 경기를 할 확률은?

P(Paly=Yes | Weather=Overcast, Temp=Mild) = P(Weather=Overcast, Temp=Mild | Play=Yes) P(Play=Yes) / P(Weather=Overcast, Temp=Mild)

P(Weather=Overcast, Temp=Mild | Play=Yes) = P(Overcast|Yes) P(Mild|Yes)

P(Weather=Overcast, Temp=Mild) = P(Weather=Overcast) P(Temp=Mild) = (4/14) * (6/14) = 0.1224

1. 사전 확률

P(Yes) = 9/14 = 0.64

2. 사후 확률

P(Overcast|Yes) = 4/9 = 0.44

P(Mild|Yes) = 4/9 = 0.44

3. 베이즈 공식에 대입

P(Weather=Overcast, Temp=Mild | Play=Yes) = P(Overcast|Yes) P(Mild|Yes) = 0.44 * 0.44 = 0.1936

P(Paly=Yes | Weather=Overcast, Temp=Mild) = P(Weather=Overcast, Temp=Mild | Play=Yes) P(Play=Yes) / P(Weather=Overcast, Temp=Mild)

= 0.1936 * 0.64 / 0.1224 = 1

문제 2. 날씨가 overcast, 기온이 Mild일 때 경기를 하지 않을 확률은?

P(Paly=No | Weather=Overcast, Temp=Mild) = P(Weather=Overcast, Temp=Mild | Play=No) P(Play=No) / P(Weather=Overcast, Temp=Mild)

P(Weather=Overcast, Temp=Mild | Play=No) = P(Overcast|Yes) P(Mild|No)

1. 사전 확률

P(No) = 5/14 = 0.36

2. 사후 확률

P(Overcast|No) = 0/5 = 0

P(Mild|No) = 2/5 = 0.4

3. 베이즈 공식에 대입

P(Weather=Overcast, Temp=Mild | Play=No) = P(Overcast|No) P(Mild|No) = 0 * 0.4 = 0

P(Paly=No | Weather=Overcast, Temp=Mild) = P(Weather=Overcast, Temp=Mild | Play=No) P(Play=No) P(Weather=Overcast, Temp=Mild)

= 0 * 0.36 / 0.1224 = 0

축구를 할 확률은 1이고, 축구를 하지 않을 확률은 0입니다. 축구를 할 확률이 더 크기 때문에 날씨가 Overcast이고 기온이 Mild일 때는 축구를 할 것이라고 분류합니다. 이렇듯 나이브 베이즈는 베이즈 정리를 활용하여 확률이 더 큰 Label로 분류를 합니다. 

Scikit-learn을 활용한 나이브 베이즈 분류기 구축

날씨, 기온에 따른 축구 여부 분류

2개의 Feature (Weather, Temp)와 1개의 Label (Play)로 구성된 dataset을 아래와 같이 만들어 주었습니다.

# Assigning features and label variables
weather=['Sunny','Sunny','Overcast','Rainy','Rainy','Rainy','Overcast','Sunny','Sunny',
'Rainy','Sunny','Overcast','Overcast','Rainy']
temp=['Hot','Hot','Hot','Mild','Cool','Cool','Cool','Mild','Cool','Mild','Mild','Mild','Hot','Mild']

play=['No','No','Yes','Yes','Yes','No','Yes','No','Yes','Yes','Yes','Yes','Yes','No']

string을 int로 바꾸어 주는 Feature Encoding을 해줍니다.

# Import LabelEncoder
from sklearn import preprocessing

#creating labelEncoder
le = preprocessing.LabelEncoder()
# Converting string labels into numbers.
weather_encoded=le.fit_transform(weather)
print(weather_encoded)

>>> [2 2 0 1 1 1 0 2 2 1 2 0 0 1]

비슷한 방식으로 temp와 play도 인코딩합니다.

# Converting string labels into numbers
temp_encoded=le.fit_transform(temp)
label=le.fit_transform(play)
print("Temp:",temp_encoded)
print("Play:",label)

>>> Temp: [1 1 1 2 0 0 0 2 0 2 2 2 1 2]
>>> Play: [0 0 1 1 1 0 1 0 1 1 1 1 1 0]

인코딩 된 두 feature를 결합합니다.

#Combinig weather and temp into single listof tuples
features = zip(weather_encoded,temp_encoded)
features = list(features)
print(features)

>>> [(2, 1), (2, 1), (0, 1), (1, 2), (1, 0), (1, 0), (0, 0), (2, 2), (2, 0), (1, 2), (2, 2), (0, 2), (0, 1), (1, 2)]

sklearn을 활용하여 나이브 베이즈 분류기 모델을 만들어보겠습니다. 이를 위해 모델 생성 -> 훈련 데이터 Fitting -> 성능 평가 순으로 실습해보겠습니다.

#Import Gaussian Naive Bayes model
from sklearn.naive_bayes import GaussianNB

#Create a Gaussian Classifier
model = GaussianNB()

# Train the model using the training sets
model.fit(features,label)

#Predict Output
predicted= model.predict([[0,2]]) # 0:Overcast, 2:Mild
print("Predicted Value:", predicted) # 1: Yes

>>> Predicted Value: [1]

아까 베이즈 정리를 활용하여 직접 계산했을 때, 날씨가 Overcast, 기온이 Mild일 때 play로 예측을 했습니다. sklearn의 naive_bayes에서도 동일한 결과가 나옵니다. 1이 Play를 한다입니다.

Label이 여러개인 나이브 베이즈

sklearn이 신기한게 라이브러리에서 datasets을 제공해준다는 것입니다. 공부하는 사람의 편의를 위해 라이브러리로 datasets를 제공해줄 생각을 하다니... 이 라이브러리를 만드는데 기여한 분들께 감사를 표합니다. 갑자기 sklearn에 애정이 가네요.. ㅎㅎ

#Import scikit-learn dataset library
from sklearn import datasets

#Load dataset
wine = datasets.load_wine()

# print the names of the 13 features
print("Features: ", wine.feature_names)

# print the label type of wine(class_0, class_1, class_2)
print("Labels: ", wine.target_names)

>>> Features:  ['alcohol', 'malic_acid', 'ash', 'alcalinity_of_ash', 'magnesium', 'total_phenols', 'flavanoids', 'nonflavanoid_phenols', 'proanthocyanins', 'color_intensity', 'hue', 'od280/od315_of_diluted_wines', 'proline']
>>> Labels:  ['class_0' 'class_1' 'class_2']
wine.data.shape

>>> (178, 13)
wine.data[0:5]

>>> array([[1.423e+01, 1.710e+00, 2.430e+00, 1.560e+01, 1.270e+02, 2.800e+00,
        3.060e+00, 2.800e-01, 2.290e+00, 5.640e+00, 1.040e+00, 3.920e+00,
        1.065e+03],
       [1.320e+01, 1.780e+00, 2.140e+00, 1.120e+01, 1.000e+02, 2.650e+00,
        2.760e+00, 2.600e-01, 1.280e+00, 4.380e+00, 1.050e+00, 3.400e+00,
        1.050e+03],
       [1.316e+01, 2.360e+00, 2.670e+00, 1.860e+01, 1.010e+02, 2.800e+00,
        3.240e+00, 3.000e-01, 2.810e+00, 5.680e+00, 1.030e+00, 3.170e+00,
        1.185e+03],
       [1.437e+01, 1.950e+00, 2.500e+00, 1.680e+01, 1.130e+02, 3.850e+00,
        3.490e+00, 2.400e-01, 2.180e+00, 7.800e+00, 8.600e-01, 3.450e+00,
        1.480e+03],
       [1.324e+01, 2.590e+00, 2.870e+00, 2.100e+01, 1.180e+02, 2.800e+00,
        2.690e+00, 3.900e-01, 1.820e+00, 4.320e+00, 1.040e+00, 2.930e+00,
        7.350e+02]])
wine.target

>>> array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1,
       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2,
       2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
       2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
       2, 2])

여기서 target값 0은 class_0, 1은 class_1, 2은 class_3을 뜻합니다.

지도 학습을 위해서는 train 데이터와 test 데이터가 필요하다고 했습니다. 따라서 sklearn에서 제공해준 전체 data를 train 데이터와 test 데이터로 나누어야 합니다. train 데이터는 모델을 학습시키기 위해서, test는 학습된 모델의 성능을 평가하기 위해서 필요합니다. 아래와 같이 전체 data set을 구분하는 작업이 필요합니다.

출처: DataCamp

# Import train_test_split function
from sklearn.model_selection import train_test_split

# Split dataset into training set and test set
# 70% training and 30% test
X_train, X_test, y_train, y_test = train_test_split(wine.data, wine.target, test_size=0.3, random_state=109)

#Import Gaussian Naive Bayes model
from sklearn.naive_bayes import GaussianNB

#Create a Gaussian Classifier
gnb = GaussianNB()

#Train the model using the training sets
gnb.fit(X_train, y_train)

#Predict the response for test dataset
y_pred = gnb.predict(X_test)

#Import scikit-learn metrics module for accuracy calculation
from sklearn import metrics

# Model Accuracy, how often is the classifier correct?
print("Accuracy:", metrics.accuracy_score(y_test, y_pred))

>>> Accuracy: 0.9074074074074074

이 모델의 정밀도는 90.74%입니다. 꽤 높은 수치입니다.

나이브 베이즈의 장단점

장점

1. 간단하고, 빠르며, 정확한 모델입니다.

2. computation cost가 작습니다. (따라서 빠릅니다.)

3. 큰 데이터셋에 적합합니다.

4. 연속형보다 이산형 데이터에서 성능이 좋습니다.

5. Multiple class 예측을 위해서도 사용할 수 있습니다.

단점

feature 간의 독립성이 있어야 합니다. 하지만 실제 데이터에서 모든 feature가 독립인 경우는 드뭅니다. 장점이 많지만 feature가 서로 독립이어야 한다는 크리티컬한 단점이 있습니다.

feature간 독립성이 있다는 말은 feature간에 서로 상관관계가 없다는 뜻입니다. X1과 X2라는 feature가 있을 때 X1이 증가하면 X2도 같이 증가한다고 합시다. 그럼 X1과 X2는 서로 상관관계가 있다고 말할 수 있고, 이는 X1과 X2가 독립성이 없다는 뜻입니다. X1과 X2가 독립성이 있으려면 X1이 증가하든 말든, X2에는 아무런 영향을 미치지 않아야 합니다. 하지만 우리가 얻을 수 있는 데이터에서는 feature간의 독립성이 항상 보장되지는 않습니다. 나이브 베이즈 모델은 feature간 독립성이 있다는 가정하에 성립되는 모델이기 때문에 실생활에서 바로 적용하기는 어려움있습니다.

이상으로 나이브 베이즈 분류에 대해서 알아봤습니다.

Reference

Reference1: Naive Bayes Classification using sikit-learn

Reference2: 분류성능평가지표 - Precision(정밀도), Recall(재현율) and Accuracy(정확도)

Comments