본문 바로가기
내가 하는 데이터분석/내가 하는 머신러닝

[나이브 베이즈 분류, Naive Bayes Classification] - BernoulliNB with Python

by Pymoon 2023. 2. 19.

데이터가 각 클래스에 속할 특징 확률을 계산하는 조건부 확률 기반의 분류 방법인 나이브베이즈(NaiveBayes)에 대해서 정리해 보자.

 

그중에서 오늘은 BernoulliNB에 대해서 알아볼 것이다.

 

 

이 전의 머신러닝에서는 나이브베이즈(NaiveBayes) 기법 중 MultinomialNB에 대해서 알아보며 실습해 보았다.

 

[나이브 베이즈 분류Naive Bayes Classification] - MultinomialNB with Python

데이터가 각 클래스에 속할 특징 확률을 계산하는 조건부 확률 기반의 분류 방법인 나이브베이즈(NaiveBayes)에 대해서 정리해 보자. 그중에서 오늘은 MultinomialNB에 대해서 알아볼 것이다. 이 전의

py-moon.tistory.com

 

 

 

 

나이브(Naive)는 직역하면 단순하다, 순진하다는 의미를 가진다.

 

 

베이즈(Bayes)는 베이지안 통계를 기반으로 입력특징이 클래스 전체의 확률분포 대비 특정 클래스에 속할 확률을 정리하였다는 점에서 비롯되었다.

 

 

나이브베이즈 분류 기법이 예측한 특징을 상호 독립적이라는 가정하에 확률 계산을 단순화하고 모든 변수가 동등하다는 특징에서 나이브(Naive) 베이즈(Bayes)라는 이름이 결합하게 되었다.

 

 

이제, 나이브베이즈(NaiveBayes) 개념을 이해하기 위해서는 베이즈 정리에 대해 간단히 알아보자.

 


 

베이즈 정리(Bayes' Theorem)는 두 확률 변수의 사전확률과 사후확률 사이의 관계를 나타내는 정리이다.

 

사건 A, B가 있을 때, 사건 B가 일어난 것을 전제로 한 사건 A의 조건부 확률을 구하는 것이다.

 

scikit-learn에서 구현된 나이브베이즈 분류기는 BernoulliNB, MultinomialNB, GaussianNB가 있다.

 

 

 

이 중 오늘 실습해 볼 분류기는 BernoulliNB이다.

 

BernoulliNB는 이산형 데이터를 분류할 때 사용되며, 데이터 출현 여부에 따라 0(False)또는 1(True)로 구분될 때 사용한다.

 


 

UCI Machine Learning 저장소의 SMS 스팸 메시지 데이터를 BernoulliNB를 활용한 분류를 통해 스팸메일을 분류해 보자.

 

 

 

 

1
2
3
4
5
6
7
8
9
10
11
from sklearn.model_selection import train_test_split
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.naive_bayes import BernoulliNB
from sklearn.metrics import accuracy_score
from sklearn.metrics import classification_report
import pandas as pd
import numpy as np
import warnings
warnings.filterwarnings(action='ignore')
 
spam = pd.read_csv('data/spam.csv', encoding = 'utf-8')
cs

 

> 분류 분석을 진행하기에 필요한 라이브러리와 데이터를 불러온다.

 

 

 

spam.info()

> info()를 통해서 데이터셋에 대한 정보를 확인해 본다.

 

> 결측치 존재가 의심되어 확인해 보자.

 

 

 

 

1
2
3
4
5
6
7
8
print(spam.isnull().sum())
 
 
v1               0
v2               0
Unnamed: 2    5522
Unnamed: 3    5560
Unnamed: 4    5566
cs

 

> 세 변수에서 대부분의 데이터가 결측치임을 확인하였다.

 

> 따라서, v1변수와 v2변수만을 분석에 사용하기로 한다.

 

 

 

 

spam.head()

 

> 두 변수만을 추출하여 head()를 통해서 데이터를 확인해 본다.

 

 

 

 

1
2
3
4
print(spam['v1'].unique())
 
 
['ham' 'spam']
cs

 

> unique()를 통해서 종속변수로 쓰일 v1변수의 고윳값들을 확인해 본다.

 

 

 

 

1
2
import numpy as np
spam['label'= np.where(spam['v1'== 'spam'10)
cs

 

> spam메일은 1, 아닌 것은 0으로 변환해 준다.

 

> 기존의 변수를 제거하고, label이라는 이름으로 새로 저장해 준다.

 

 

 

 

spam.head()

> v1변수와 label변수를 비교해 보며 변환이 잘 진행되었는지 확인해 본다.

 

 

 

 

1
2
3
4
5
6
7
8
9
10
from sklearn.model_selection import train_test_split
 
= spam['v2']
= spam['label']
 
train_x, test_x, train_y, test_y = train_test_split(x, y, stratify = y, test_size = 0.3, random_state = 42)
print(train_x.shape, test_x.shape, train_y.shape, test_y.shape)
 
 
(3900,) (1672,) (3900,) (1672,)
cs

 

> 독립변수와 종속변수를 x와 y로 정의해 주고, 모델 학습을 위해 데이터셋을 7:3으로 분할해 준다.

 

 

 

 

1
2
3
4
5
from sklearn.feature_extraction.text import CountVectorizer
 
cv = CountVectorizer(binary = True)
x_traincv = cv.fit_transform(train_x)
x_traincv.shape
cs

 

> scikit-learn의 문서 전처리 기능을 가지는 CountVectorizer를 사용해서 v2변수를 전처리해 준다.

 

> CountVectorizer는 입력된 데이터에 출현된 모든 단어의 개수만큼의 크기를 가진 벡터를 만들어 이메일을 고정된 벡터로 표현한다.

 

> 하이퍼파라미터인 binary를 True로 설정함으로써 이메일마다 단어가 한 번 이상 등장하면 1, 그렇지 않으면 0을 반환하도록 한다.

 

> x_traincv의 shape을 살펴보니 (3900, 7234)로 확인할 수 있었다. 단어의 개수가 7234개란 뜻이다.

 

 

 

 

1
2
3
4
5
6
7
8
9
10
11
12
encoded_input = x_traincv.toarray()
 
print(encoded_input)
 
 
[[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]]
cs

 

> 위 코드결과를 통해서 x_traincv를 배열로 변환한 결과를 확인해 볼 수 있다.

 

 

 

 

1
2
3
4
5
6
print(cv.inverse_transform(encoded_input[[0]]))
 
 
[array(['78''after''arsenal''bergkamp''by''from''give''goal',
       'henry''liverpool''margin''mins''pass''scores''shot',
       'simple''to''with''yards'], dtype='<U34')]
cs

 

> 인코딩 된 이메일의 제목에 어떤 단어들이 포함되어 있는지 확인해 보기 위해 inverse_transform을 활용해서 첫 번째 데이터의 현황을 확인해 볼 수 있다.

 

 

 

 

1
2
3
4
5
print(cv.get_feature_names()[1000:1010], end='')
 
 
['ass''assessment''assistance''assume''asthere''asthma',
 'astne''astoundingly''astronomer''asusual']
cs

 

> 벡터의 인덱스로 뽑아낸 데이터가 어떤 단어를 뜻하는지 확인해 볼 수도 있다.

 

 

 

 

1
2
3
4
from sklearn.naive_bayes import BernoulliNB
 
bnb = BernoulliNB()
bnb.fit(x_traincv, train_y)
cs

 

> 이제, 분류기로 사용할 BernoulliNB를 불러와서 객체를 생성하고 학습을 진행한다.

 

 

 

 

1
2
3
4
5
6
7
8
9
10
from sklearn.metrics import accuracy_score
 
x_testcv = cv.transform(test_x)
pred = bnb.predict(x_testcv)
 
acc = accuracy_score(test_y, pred)
print('Accuracy Score : {:.2f}'.format(acc))
 
 
Accuracy Score : 0.97
cs

 

> test데이터도 전처리를 진행하고, pred변수에 예측치를 저장해 준다.

 

> 실제값인 test_y와 예측값인 pred를 입력하여 정확도를 확인해 볼 수 있다.

 

 

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
from sklearn.metrics import classification_report
 
print(classification_report(test_y, pred))
 
 
              precision    recall  f1-score   support
 
           0       0.97      1.00      0.99      1448
           1       0.99      0.82      0.90       224
 
    accuracy                           0.97      1672
   macro avg       0.98      0.91      0.94      1672
weighted avg       0.98      0.97      0.97      1672
cs

 

> 정확도로는 모델의 성능을 파악하기 어려우니, 분류분석에서 사용하는 다른 평가지표로 성능을 확인해볼 수 있다.

 

 


 

OUTTRO.

 

 

이번 실습을 진행하면서 처음 접해본 전처리 기법이 있다. CountVectorizer이다.

 

문장이나 둘 이상의 단어가 포함된 데이터들을 다룰 때, 변환이 필요할 때 사용하면 유용한 기법이다.

 

문장에 구성되어 있는 단어의 개수를 세어주고, 새로운 문장이 입력되었을 때 학습된 단어의 유무도 확인할 수 있다.

 

주로 자연어 처리와 같은 분석을 진행할 때 쓰이는 듯해 보인다.