9장 나이브 베이즈 분류#

나이브 베이즈(naive Bayes)는 분류(classification)에 사용되는 아주 간단한 기법이다. 베이즈 정리를 기반으로 하되, 예측변수 간에 강력한(순진한) 독립성(independence) 가정을 적용하는 간단한 확률론적 분류기(probabilistic classifier)이다.(wikipedia, “Naive Bayes classifier”.)

9.1 스팸 필터링 예제: 확률론적 접근#

스팸 필터링 예제 1#

정크메일(스팸) 필터링과 관련된 아주 간단한 예제를 생각해보자. 100개의 이메일이 있다. 아래 표에서 보듯이 1번 이메일은 정상적인 이메일, 즉 햄(ham)이고, 2번 이메일은 스팸(spam)이고, 이런 식으로 100개 이메일의 클래스(class)를 구분해 놓았다. 이 상태에서 우리의 과제는 아래 표의 맨 아래에 0번으로 표시된 이메일이 스팸인지, 햄인지를 분류하는 것이다.

이 책에서 그동안 사용해 온 용어로 표현하면, 아래 데이터세트에는 100개의 훈련 관측이 있고, 예측변수 또는 특성(feature)은 없으며, 반응(response) \(Y\)는 범주(클래스)로 된 이진(binary) 변수이다. 그리고 맨 아래 0번 관측은 테스트 관측에 해당한다.

스팸 필터링 예제 1

확률론적 접근

이 문제에 대한 확률론적(probabilistic) 접근은 테스트 관측이 스팸 및 햄일 확률을 추정해 비교해보는 것이다.

100개의 훈련 관측을 분류한 결과가 아래 다이어그램과 같다고 가정해보자. 즉, 100개의 이메일 중 스팸은 20%이고, 햄은 80%이다. 이 정보만으로 테스트 관측의 클래스를 예측한다면, 스팸 확률은 0.2이고, 햄 확률은 0.8이라고 예측하는 것이 최선일 것이다. 이를 기호로 표시하면 \(p\rm(spam)=0.2\), \(p\rm(ham)=0.8\)이다.

만약 스팸과 햄 중 어느 쪽 확률이 크냐에 따라 클래스를 분류하기로 결정 규칙(decision rule)을 세운다면, 이런 경우 테스트 관측(0번 관측)은 햄으로 분류된다.

벤 다이어그램

  • 그림 출처: Brett Lantz (2015), Machine Learing With R, 4장.

스팸 필터링 예제 2#

또 다른 정크메일(스팸) 필터링 예제를 생각해보자. 이번에도 100개의 이메일이 있다. 아래 표에서 보듯이 이번에는 추가적인 정보가 있는데, 이메일 메시지에 “Viagra”라는 단어가 들어가 있는지 여부가 Yes/No로 표시돼 있다. 이 \(X\) 변수는 반응(\(Y\))의 클래스를 예측하는 데 사용되는 예측변수에 해당한다.

스팸 필터링 예제 2

이 상태에서 우리의 과제는 표의 맨 아래에 0번으로 표시된 이메일이 스팸인지, 햄인지를 분류하는 것이다. 0번 관측과 관련하여 앞의 예제와 다른 점은 우리에게 추가적인 정보가 있다는 것인데, 0번 이메일 메시지에 Viagra 단어가 들어있다는 점이다.

통계학에서는 이런 확률을 조건부확률(conditional probability)이라고 부른다. 이메일 메시지에 Viagra 단어가 들어있다는 조건(\(\rm Viagra=Yes\)) 하에서 spamham의 확률을 따지는 것으로 이를 각각 \(p\rm(spam \mid Yes)\)\(p\rm(ham \mid Yes)\)로 표기한다.

사실 우리 경우에는 \(p\rm(spam \mid Yes)\)\(p\rm(ham \mid Yes)\) 중 어느 하나만 추정하면 된다. 왜냐하면 클래스가 spamham 두 개밖에 없기 때문에 어느 한 쪽의 확률을 구하면, 다른 쪽 확률은 1에서 해당 확률을 빼면 되기 때문이다. 여기에서는 \(p\rm(spam \mid Yes)\)를 추정해보기로 하자.

spam/hamViagra의 관계

훈련 세트에 들어있는 100개의 이메일을 분석한 결과, 클래스(spam/ham)와 Viagra의 관계가 아래 표 9.1과 같다고 해보자. 이것은 Viagra 단어가 들어있는 경우와 그렇지 않은 각 경우에 대해 spam/ham이 몇 개인지 빈도수(frequency)를 보여준다. 소위 분할표(contingency table)이다. 100개의 이메일 중 스팸은 20%이고, 햄은 80%라는 점은 앞의 예제와 동일하다.

이 표의 정보를 이용하면 \(p\rm(spam \mid Yes)\)를 쉽게 구할 수 있다. 조건부확률은 말 그대로 해당 조건 내에서 확률을 따지는 것이기 때문에 여기서는 Viagra 단어가 들어있는 이메일 중에서 스팸이 발생할 확률을 계산하면 된다. 아래 분할표에 따르면 전체 이메일 중 Viagra 단어가 들어있는 것은 5개이고, 이 중에서 4개(80%)가 스팸이다. 따라서 \(p\rm(spam \mid Yes)=0.8\)이고, \(p{\rm(ham \mid Yes)}=1-p{\rm(spam \mid Yes)}=0.2\)가 된다. 스팸일 확률이 더 크므로 0번 관측은 정크메일로 분류된다. 앞의 예제 1과는 반대의 결과이다.

표 9.1. 스팸 메일 필터링 예제 2.

스팸 필터링 예제 2 표

  • 표 출처: Brett Lantz (2015), Machine Learing With R, 4장.

조건부확률과 베이즈 정리#

나이브 베이즈를 이해하기 위해서는 조건부확률에 대해 좀 더 알아야 한다. 조건부확률은 다음과 같이 정의된다.

\[p(y\mid x)={\frac {p(y, x)}{p(x)}} \tag{9.1}\]

여기에서 \(p(y, x)\)\(y\)\(x\)가 동시에 발생할 확률로서 결합확률(joint probability)이라고 부른다.

앞에서 예로 다루었던 \(p\rm(spam\mid Yes)\)를 위의 조건부확률 정의에 따라 계산해보자. 위 표 9.1을 보면 \(p\rm (Yes)=5/100\)이고, \(p\rm(spam, Yes)=4/100\)이다. 따라서 조건부확률의 정의(식 9.1)에 따라 계산하면 0.8로서 앞에서와 동일한 결과를 얻는다.

\[p{\rm(spam\mid Yes)}={\frac {p{\rm(spam, Yes)}}{p{\rm(Yes)}}}=\frac{4/100}{5/100}=\frac{4}{5}=0.8\]

이제 베이즈 정리를 도출해보자. 베이즈 정리는 조건부확률의 정의에서 출발하는데, 위 식 9.1에 있는 결합확률 \(p(y, x)\)의 경우, 괄호안 두 개 변수의 순서를 바꿔도 내용상 전혀 변화가 없다는 점에서 \(p(y, x)=p(x, y)\)가 성립한다. 또한 \(p(x, y)\)는 위 조건부확률의 정의에 의해 다음과 같이 쓸 수 있다.

\[p(x\mid y)={\frac {p(x, y)}{p(y)}} \Rightarrow \ p(x, y)=p(y)\ p(x\mid y) \tag{9.2}\]

따라서 식 9.1과 9.2에 의해 다음의 베이즈 정리(Bayes’ theorem)가 도출된다.

\[p(y\mid x)= \frac{p(y)\ p(x\mid y)}{ p(x)} \tag{9.3}\]

베이즈 정리의 의미를 생각해보기 위해 앞의 스팸 필터링 예에 베이즈 정리를 적용하면 다음과 같다.

\[p{\rm(spam\mid Yes)}=\frac {p{\rm(spam)}\ p{\rm(Yes\mid spam)}}{p{\rm(Yes)}} \tag{9.4}\]

베이즈 정리의 의미는 다음과 같다.

\[{\text{posterior}}={\frac {{\text{prior}}\times {\text{likelihood}}}{\text{evidence}}}\]

식 9.4에서 \(p{\rm(spam)}\)사전적 확률(prior probability)이라고 한다. 다른 정보가 없는 상태에서 어떤 이메일이 스팸일 확률이다. 그런데 이제 이메일 메시지에 들어있는 단어들을 살펴 볼 수 있게 됨으로써 Viagra 단어의 존재 유무에 대한 정보를 얻었다고 해보자. 이처럼 Viagra 단어의 존재 유무에 대한 추가 정보가 있는 상황에서 이메일이 스팸일 확률, 즉 조건부확률 \(p{\rm(spam\mid Yes)}\)사후적 확률(posterior probability)이라고 부른다. 그것은 사전적 확률을 업데이트하는 형태인데, Viagra에 대한 증거(evidence), 즉 \(p\rm(Yes)\)에 비해 스팸 메일에 Viagra 단어가 들어있는 가능도(likelihood), 즉 \(p{\rm(Yes\mid spam)}\)이 클수록 사후적 확률이 사전적 확률보다 더 커진다.

앞에서 예로 다루었던 \(p\rm(spam\mid Yes)\)를 베이즈 정리에 의해 계산해보자. 위 표 9.1을 보면 \(p\rm (spam)=20/100\)이고, \(p\rm (Yes)=5/100\)이다. 또한 \(p{\rm(Yes\mid spam)}=4/20\)이다. 따라서 식 9.4에 의해 계산하면 다음과 같이 0.8로서 앞에서 계산한 것과 동일하다.

\[p{\rm(spam\mid Yes)}=\frac {p{\rm(spam)}\ p{\rm(Yes\mid spam)}}{p{\rm(Yes)}}=\frac{(20/100)\times(4/20)}{5/100}=\frac{4/100}{5/100}=0.8\]

9.2 나이브 베이즈 분류 예제#

앞의 스팸 메일 필터링 예제를 좀 더 발전시켜 보자. 이제 이메일 메시지에서 Viagra 외에 Money와 Groceries라는 단어를 추가적으로 검색할 수 있다. 이번에도 100개의 이메일이 있다. 아래 표에서 보듯이 각 이메일 별로 Viagra, Money, Groceries 단어가 들어가 있는지 여부가 Yes/No로 표시돼 있다. 이제 예측변수가 하나가 아니라 3개(\(X_1,X_2,X_3\))이고, 이를 이용해 반응(\(Y\))의 범주를 예측한다. 우리에게 주어진 테스트 관측은 아래 표의 맨 아래 0번 관측으로서 \(\rm Viagra=Yes\), \(\rm Money=Yes\), \(\rm Groceries=No\)이다.

나이브 베이즈 분류 예제

위 예제에서 테스트 이메일이 스팸일 사후적 확률\(p({\rm spam}\mid x_{1},x_{2},x_{3})\)으로 표현할 수 있으며, 이는 조건부확률의 정의에 의해 다음과 같다.(식 9.1 참조)

\[p({\rm spam}\mid x_{1},x_{2},x_{3})={\frac {p({\rm spam}, x_{1},x_{2},x_{3})}{p(x_{1},x_{2},x_{3})}} \tag{9.5}\]

여기에서 우변의 분자는 다음과 같이 쓸 수 있는데, 첫 번째 등호의 경우, 결합확률은 괄호안 변수의 순서를 바꿔도 내용상 변화가 없다는 점을 이용한 것이고, 두 번째 등호부터는 조건부확률의 정의를 연쇄적으로 적용한 것이다.

\[\begin{split} {\begin{aligned}p({\rm spam},x_{1},x_{2},x_{3}) &=p(x_{1},x_{2},x_{3},{\rm spam})\\ &=p(x_{1}\mid x_{2},x_{3},{\rm spam})\ p(x_{2},x_{3},{\rm spam})\\ &=p(x_{1}\mid x_{2},x_{3},{\rm spam})\ p(x_{2}\mid x_{3},{\rm spam})\ p(x_{3},{\rm spam})\\ &=p(x_{1}\mid x_{2},x_{3},{\rm spam})\ p(x_{2}\mid x_{3},{\rm spam})\ p(x_{3}\mid {\rm spam})\ p({\rm spam}) \end{aligned}} \end{split}\]

우리는 이 단계에서 “순진한(naive)” 조건부독립(conditionally independent) 가정을 적용한다. \(A\), \(B\), \(C\) 세 개의 사건(event)이 있을 때, 다음이 성립하면 \(C\)의 조건 하에서 \(A\)\(B\)는 조건부독립이다.(이는 \(B\)의 발생이 \(A\)의 확률에 영향을 미치지 않는 것을 의미한다.)

\[p(A\mid B,C)=p(A\mid C)\]

이러한 조건부독립의 정의를 우리 예에 적용해보자. 만약 \(X_{1},X_{2},X_{3}\)spam의 조건 하에서 서로 조건부독립이면 다음이 성립한다.

\[p(x_{1}\mid x_{2},x_{3},{\rm spam})=p(x_{1}\mid {\rm spam}),~~~~~p(x_{2}\mid x_{3},{\rm spam})=p(x_{2}\mid {\rm spam})\]

따라서 이상의 논의를 종합하면, 식 9.5의 조건부확률 \(p({\rm spam}\mid x_{1},x_{2},x_{3})\)은 조건부독립의 가정 하에서 다음과 같이 된다.

\[ p({\rm spam}\mid x_{1},x_{2},x_{3})= p(x_{1}\mid {\rm spam})\ p(x_{2}\mid {\rm spam})\ p(x_{3}\mid {\rm spam})\ p({\rm spam})~/~Z \tag{9.6} \]

여기서 \(Z\)\(p(x_{1},x_{2},x_{3})\)이다. 마찬가지로 ham에 대한 조건부확률은 다음과 같다.

\[ p({\rm ham}\mid x_{1},x_{2},x_{3})= p(x_{1}\mid {\rm ham})\ p(x_{2}\mid {\rm ham})\ p(x_{3}\mid {\rm ham})\ p({\rm ham})~/~Z \tag{9.7} \]

식 9.6과 9.7을 비교해보면 \(Y\) 클래스가 spam이든 ham이든 \(Z=p(x_{1},x_{2},x_{3})\)는 바뀌지 않는다. 따라서 양자를 비교할 때, \(Z\)는 계산하지 않고 나머지 부분만 비교하면 된다.

스팸 메일 분류 예제#

앞의 예에서 테스트 관측에 대해 스팸/햄의 확률을 구해보자. 0번 관측의 경우, \(\rm Viagra=Yes\), \(\rm Money=Yes\), \(\rm Groceries=No\)이다. 조건부확률 계산을 위해서는 가능도(likelihood)에 대한 정보가 필요한데, 아래 표와 같다고 하자. 표를 읽는 법은 가령 \(p(X_{1}={\rm Yes}\mid Y={\rm spam})=4/20\)이다.

표 9.2. 스팸 메일 필터링 예제 3.

스팸 필터링 예제 3 표

  • 표 출처: Brett Lantz (2015), Machine Learing With R, 4장.

위 표를 이용해 테스트 관측이 스팸일 확률을 구하면 다음과 같다.(식 9.6에서 \(Z\)를 제외한 부분만 계산함.)

\[(4/20)\times(10/20)\times(20/20)\times(20/100)=0.02\]

다음으로 테스트 관측이 햄일 확률을 구하면 다음과 같다.(식 9.7에서 \(Z\)를 제외한 부분만 계산함.)

\[(1/80)\times(16/80)\times(72/80)\times(80/100)=0.0018\]

두 값을 비교해보면, 0번 관측이 스팸일 확률이 햄일 확률에 비해 11배 이상 더 크다. 따라서 0번 관측은 스팸으로 분류된다.

사실 우리 예에서 Money 단어는 Viagra나 Groceries 단어와 상관성이 있을 가능성이 높다. 하지만 조건부독립 가정은 (이들 단어 간에 상관성이 어찌됐든) 이들 단어가 스팸일 확률에 독립적으로 기여한다고 가정하는 것이다.

순진한(naive) 가정#

조건부독립 가정이 실제 현실에 위반될 가능성이 많다는 점에서 우리는 이를 순진한(naive) 가정이라고 이름을 붙였다. 이렇게 순진한 가정을 하는 이유는 이것이 조건부확률 추정을 획기적으로 단순하게 만들어주기 때문이다. 왜냐하면 조건부확률을 추정함에 있어서 예측변수(즉 \(x_1,x_2,x_3\)) 상호간의 관계를 파악하지 않아도 되며, 오직 각 개별 예측변수와 주어진 클래스(스팸/햄) 간의 관계만 따지면 되기 때문이다.

사실 우리 예에서의 예측변수는 Yes/No만으로 이루어진 단순한 형태이지만 일반적으로는 연속형 변수까지 포함될 것이며, 거기에다 변수의 개수가 수십개 또는 수백개로 늘어나면 이들 간 확률적 관계를 파악하는 것이 사실상 불가능해질 수도 있다. 이런 상황에서 조건부독립 가정은 추정을 아주 간단하게 만들어준다.

나이브 베이즈에 의한 분류는 실행 속도가 상대적으로 빠르다. 조건부독립 가정 덕분에 예측변수에 대한 분석이 개별적 차원(즉 1차원)에서 독립적으로 이루어질 수 있기 때문이다. 소위 차원의 저주(curse of dimensionality) 문제를 해결해 준다.

또한 조건부독립 가정 덕분에 훈련 관측의 개수가 많지 않은 경우에도 조건부확률의 추정이 가능하다. 우리 예의 경우, 만약 조건부독립의 가정이 없다면, 100개의 이메일 중에서 Viagra와 Money 단어는 들어있고, Groceries 단어는 들어있지 않은 이메일을 일차적으로 파악해야 하는데, 그 조건에 부합하는 관측이 극히 소수(가령 1개)일 경우 그것만으로는 신뢰할 만한 조건부확률을 추정하기 어렵다.

지나치게 단순화된 가정에도 불구하고 나이브 베이즈 분류기는 실제 상황에서 상당히 잘 작동하는 것으로 평가받고 있다. 특히 문서 분류(document classification)나 스팸 필터링에서 좋은 성과를 나타냈다. Zhang(2004)은 겉보기에 단순한 베이즈 분류기가 놀라운 분류 성과를 보이는 데는 나름대로의 이론적 이유가 있음을 보여주었다. Caruana와 Niculescu-Mizil(2006)은 여러 분류 알고리즘을 종합적으로 비교한 결과, 베이즈 분류가 랜덤 포레스트(random forest)나 부스팅(boosting)과 같은 다른 접근 방식보다 성과가 우수함을 보여주었다.

9.3 나이브 베이즈 알고리즘#

앞에서 예제를 통해 살펴 본 나이브 베이즈 분류를 일반적 환경에서 이해해보자.

나이브 베이즈는 한 마디로 조건부확률 모델이다. \(m\)개의 예측변수 벡터 \((X_{1},\ldots ,X_{m})\)이 있고, 반응변수를 \(Y\)로 표기하자. 이 상황에서 나이브 베이즈는 주어진 예측변수 정보 \((x_{1},\ldots ,x_{m})\)을 이용해 다음의 조건부확률을 구하고자 한다.

\[p(y\mid x_{1},\ldots ,x_{m})\]

조건부확률의 정의는 다음과 같다.

\[p(y\mid x_{1},\ldots ,x_{m})={\frac {p(y,x_{1},\ldots ,x_{m})}{p(x_{1},\ldots ,x_{m})}} \tag{9.8}\]

위 식 우변의 분자는 \(p(x_{1},\ldots ,x_{m},y)\)와 동일하며, 여기에 조건부확률 정의를 반복 적용하면 다음과 같이 다시 쓸 수 있다.

\[\begin{split} {\begin{aligned} p(y,x_{1},\ldots ,x_{m}) &=p(x_{1},\ldots ,x_{m},y)\\ &=p(x_{1}\mid x_{2},\ldots ,x_{m},y)\ p(x_{2},\ldots ,x_{m},y)\\ &=p(x_{1}\mid x_{2},\ldots ,x_{m},y)\ p(x_{2}\mid x_{3},\ldots ,x_{m},y)\ p(x_{3},\ldots ,x_{m},y)\\ &=\cdots \\ &=p(x_{1}\mid x_{2},\ldots ,x_{m},y)\ p(x_{2}\mid x_{3},\ldots ,x_{m},y)\cdots p(x_{n-1}\mid x_{m},y)\ p(x_{m}\mid y)\ p(y)\end{aligned}} \end{split}\]

여기에 순진한(naive) 조건부독립 가정을 적용하여, 모든 특성 \((X_{1},\ldots ,x_{m})\)이 클래스 \(y\) 내에서 조건부로 상호독립적(mutually independent)이라고 가정한다. 이 가정 하에서 다음이 성립한다.

\[p(x_{i}\mid x_{i+1},\ldots ,x_{m},y)=p(x_{i}\mid y)\]

이를 적용하면 식 9.8의 조건부확률은 다음과 같이 된다.

\[ p(y \mid x_{1}, \ldots, x_{m}) = \frac{p(x_{1} \mid y) \, p(x_{2} \mid y) \cdots p(x_{m} \mid y) \, p(y)}{p(x_{1}, \ldots, x_{m})} = \frac{1}{Z} p(y) \prod_{i=1}^{m} p(x_{i} \mid y) \tag{9.9} \]

여기서 \(Z=p(x_{1},\ldots ,x_{m})\)은 모든 클래스 \(y\)에 동일하게 적용되는 배율 인수(scaling factor)이다.

확률 모델을 이용한 분류기#

이로써 식 9.9의 나이브 베이즈 확률 모델을 도출했다. 이를 분류 작업에 적용하려면 결정규칙과 결합시켜야 한다. 가장 일반적이고 직관적인 규칙은 앞에서도 언급했듯이 가장 확률이 높은 클래스를 선택하는 것이다. 이것을 MAP(maximum a posteriori) 결정규칙이라고 부른다. 나이브 베이즈 확률 모델인 식 9.9에 MAP 결정규칙을 적용할 경우, 테스트 관측 \((x_{1},\ldots ,x_{m})\)의 클래스로서 다음을 만족하는 \(\hat y\)을 선택한다는 것을 의미한다.(\(Z\)는 상수이기 때문에 식에서 빼도 된다.)

\[{\hat {y}}={\underset {y}{\operatorname {argmax} }}\ p(y)\displaystyle \prod _{i=1}^{m}p(x_{i}\mid y) \tag{9.10}\]

사전적 확률 추정#

\(p(y)\)는 훈련 데이터세트에서 클래스 \(y\)의 빈도를 카운트함으로써 쉽게 추정이 가능하다. 앞의 표 9.1 및 9.2의 스팸 메일 필터링 예에서 100개 중 20개가 spam이고 나머지 80개가 ham이기 때문에 \(p(\rm spam)=0.2\)이고, \(p(\rm ham)=0.8\)이다.

가능도 추정#

\(p(x_{i}\mid y)\)의 추정은 예측변수(즉 \(X_i\))의 종류에 따라 다른 접근법이 사용된다.

  • \(X_i\)정량적(quantitative) 변수이면 일반적으로 \(X_i|y ∼ N(\mu_{yi},\sigma^2_{yi})\)라고 가정한다. 즉, 클래스 \(y\) 내에서 예측변수 \(X_i\)정규 분포(normal/Gaussian distribution)를 따른다고 가정한다. 이 가정 하에서 \(p(x_{i}\mid y)\)을 추정하기 위해서는 먼저 해당 클래스 \(y\) 내에서 \(x_i\)의 평균 \(\mu_{yi}\)와 분산 \(\sigma^2_{yi}\)를 추정한다. 이것들은 단순히 표본평균과 표본분산을 계산하면 된다. 그런 다음, 그것을 다음과 같이 정규 분포 확률밀도 함수에 대입하여 \(p(x_{i}\mid y)\)를 계산한다. 이런 방식으로 \(p(x_{i}\mid y)\)를 추정하는 것을 가우시안 나이브 베이즈(Gaussian Naive Bayes)라고 부른다.

\[p(x_{i}\mid y)={\frac {1}{\sqrt {2\pi \sigma _{yi}^{2}}}}\,\exp \left({-{\frac {(x_i-\mu_{yi})^{2}}{2\sigma^2_{yi}}}}\right)\]

  • \(X_i\)정성적(qualitative)인 경우는 이미 앞에서 다루었다. 가령 앞의 표 9.2 예에서 Viagra 단어를 의미하는 \(X_1\)\(\{\rm Yes, No\}\)이진(binary) 범주로 돼있다. 이 경우, \(p(X_1={\rm Yes}\mid Y=\rm spam)\)을 구하기 위해서는 전체 스팸 메일 20개 중 Viagra 단어를 포함한 이메일의 비율을 구하면 되고, 가령 \(p(X_1={\rm No}\mid Y=\rm ham)\)이면 전체 햄 메일 80개 중 Viagra 단어가 들어있지 않은 이메일의 비율을 구하면 된다. 이런 방식으로 \(p(x_{i}\mid y)\)를 추정하는 것을 베르누이 나이브 베이즈(Bernoulli Naive Bayes)라고 부른다.

  • \(X_i\)가 정성적 변수로서 범주가 2개보다 많은 다항(multinomial)인 경우에도 동일한 방식으로 추정할 수 있다. 예를 들어, \(X_i\)의 범주가 \(\{1, 2, 3\}\)으로 구성돼 있다고 하자. 그리고 주어진 \(y\) 클래스에 100개의 관측이 있다고 하자. 이 상황에서 예측변수 \(X_i\)의 100개 관측 중 가령 32, 55, 13개의 관측이 각각 1, 2, 3의 값을 취한다고 하자. 그러면 \(p(x_{i}\mid y)\)를 다음과 같이 추정할 수 있으며, 이를 다항 나이브 베이즈(Multinomial Naive Bayes)라고 부른다.

\[\begin{split} p(x_{i}\mid y)= \begin{cases} 0.32, & \text{if }x_i=1 \\ 0.55, & \text{if }x_i=2 \\ 0.13, & \text{if }x_i=3 \\ \end{cases} \end{split}\]

9.4 나이브 베이즈 실습#

주식시장 데이터#

ISLP에서 제공하는 Smarket 데이터세트는 2001년 초부터 2005년 말까지 1,250일 동안 S&P 500 주가지수의 수익률(%)을 기록한 것이다.

Today는 각 날짜의 수익률이고, 각 날짜에 대해 이전 5개 거래일의 수익률을 Lag1부터 Lag5까지의 이름으로 기록했다. 또한 Volume은 전날 거래된 주식 수(단위: 십억)이고, Direction은 해당 날짜에 시장이 상승(Up) 또는 하락(Down)했는지를 나타낸다. Year는 각 날짜가 속한 연도이다.

앞의 로지스틱 회귀 및 KNN에 의한 분류에서 이 데이터세트를 분석했기 때문에 아래 나이브 베이즈 분류의 성과를 그것들과 비교해보자.

import pandas as pd
import numpy as np 

Smarket = pd.read_csv('../Data/Smarket.csv')
Smarket.head()
Year Lag1 Lag2 Lag3 Lag4 Lag5 Volume Today Direction
0 2001 0.381 -0.192 -2.624 -1.055 5.010 1.1913 0.959 Up
1 2001 0.959 0.381 -0.192 -2.624 -1.055 1.2965 1.032 Up
2 2001 1.032 0.959 0.381 -0.192 -2.624 1.4112 -0.623 Down
3 2001 -0.623 1.032 0.959 0.381 -0.192 1.2760 0.614 Up
4 2001 0.614 -0.623 1.032 0.959 0.381 1.2057 0.213 Up
Smarket.shape
(1250, 9)

나이브 베이즈 분류#

나이브 베이즈 분류의 예측 정확도를 테스트 데이터세트를 이용하여 평가하기 위해 2001년부터 2004년까지의 관측을 훈련 세트로 하고, 2005년의 관측을 테스트 세트로 한다. 또한 예측변수로는 앞의 KNN 분류에서와 마찬가지로 Lag1, Lag2 변수만 사용한다.

train_df = Smarket[Smarket.Year < 2005]
test_df = Smarket[Smarket.Year == 2005]

X_train = train_df[['Lag1','Lag2']]
y_train = train_df['Direction']

X_test = test_df[['Lag1','Lag2']]
y_test = test_df['Direction']

GaussianNB 함수

예측변수 Lag1Lag2가 모두 수익률로서 정량적 변수이기 때문에 가우시안 나이브 베이즈가 적절하다. 이를 수행하기 위해 사이킷런(sklearn)의 naive_bayes 모듈에서 GaussianNB 함수를 불러들인다.

모델을 가우시안 나이브 베이즈(GaussianNB)로 선택하고, 모델의 이름을 gnb로 지정했다. 그런 다음, fit 메서드를 사용하여 모델을 피팅하는데, 이를 위해 예측변수(X_train)와 반응변수(y_train)를 입력한다. 그 피팅 결과를 바탕으로 predict 메서드를 이용해 테스트 세트 관측(X_test)에 대해 클래스를 예측하도록 하여 pred라는 이름으로 지정했다.

from sklearn.naive_bayes import GaussianNB

gnb = GaussianNB()
pred = gnb.fit(X_train, y_train).predict(X_test)

confusion_matrix() 함수를 사용해서 pred로 지정해 놓은 예측 클래스를 테스트 세트 실제 클래스(y_test)와 비교하여 혼동행렬(confusion matrix)을 만들었다.(혼동행렬에 대해서는 7장 부록 참조.) 또한 classification_report() 함수를 사용해서 정밀도(precision), 재현율(recall) 등을 출력했다.

from sklearn.metrics import classification_report, confusion_matrix

cm = confusion_matrix(y_test, pred) 
cm_df = pd.DataFrame(cm, index=['Down','Up'], columns=['Down','Up'])
cm_df.index.name = 'True'
cm_df.columns.name = 'Predicted'
print(cm_df)
print()
print(classification_report(y_test, pred, digits=3))
Predicted  Down   Up
True                
Down         29   82
Up           20  121

              precision    recall  f1-score   support

        Down      0.592     0.261     0.362       111
          Up      0.596     0.858     0.703       141

    accuracy                          0.595       252
   macro avg      0.594     0.560     0.533       252
weighted avg      0.594     0.595     0.553       252

위 결과를 보면, 정확도(accuracy)가 59.5%이다. 테스트 세트의 252 거래일 중 150 거래일의 상승과 하락을 제대로 맞췄다. 아래에서 보듯이 테스트 세트에서 56.0%가 상승이고, 나머지 44.0%가 하락이다. 따라서 테스트 세트의 모든 관측을 Up으로 예측할 경우, 정확도는 56.0%가 된다. 이것보다는 정확도가 높다는 점에서 어느 정도 예측력을 인정해 줄 수 있지만, 그 차이가 그다지 크지는 않다.

test_df.Direction.value_counts()/len(test_df.Direction)
Direction
Up      0.559524
Down    0.440476
Name: count, dtype: float64

모델별 정확도 비교#

우리는 앞 장에서도 동일한 주식시장 데이터세트를 사용해서 주가의 상승/하락을 예측해보았다. 동일한 예측변수(즉 Lag1, Lag2)와 동일한 훈련/테스트 세트를 사용하여 지금까지 세 가지 모델로 상승/하락을 예측했는데, 이들의 정확도(accuracy)와 정밀도(Precision: Up)를 비교한 표가 아래 나와 있다. KNN(K-nearest neighbors)의 경우 \(K=3\)인 경우가 예측 성과가 가장 좋았기 때문에 그것을 사용하였다.

\[\begin{split} \begin{array}{l|ccc} \text{Model} & \text{Accuracy} & \text{Precision (Up)} \\ \hline \text{Logistic} & 0.560 & 0.582 \\ \text{KNN }(K=3) & 0.532 & 0.577 \\ \text{Naive Bayes} & 0.595 & 0.596 \\ \end{array} \end{split}\]

위 결과를 보면, 나이브 베이즈가 정확도와 정밀도 모두에서 예측 성과가 가장 좋다. 여기에서 정밀도는 Up에 대한 것으로서 시장이 상승할 것으로 예측했는데, 실제로 상승한 비율을 의미한다. Up에 대한 정밀도가 높다면, 모델이 상승을 예측할 때는 매수하고 하락을 예측할 때는 거래를 피하는 전략을 생각해볼 수 있다.

타이타닉 데이터#

타이타닉호는 1912년 미국 뉴욕으로 향하던 첫 항해 중에 빙산과 충돌하여 침몰하여 2,224명의 승객 및 승무원 중 1,502명이 사망했다. Titanic 데이터세트는 Kaggle에서 주최한 머신러닝 대회 “Titanic - Machine Learning from Disaster”에서 제공한 것을 사용한다.

Titanic = pd.read_csv('http://bit.ly/kaggletrain', index_col='PassengerId')
Titanic.tail()
Survived Pclass Name Sex Age SibSp Parch Ticket Fare Cabin Embarked
PassengerId
887 0 2 Montvila, Rev. Juozas male 27.0 0 0 211536 13.00 NaN S
888 1 1 Graham, Miss. Margaret Edith female 19.0 0 0 112053 30.00 B42 S
889 0 3 Johnston, Miss. Catherine Helen "Carrie" female NaN 1 2 W./C. 6607 23.45 NaN S
890 1 1 Behr, Mr. Karl Howell male 26.0 0 0 111369 30.00 C148 C
891 0 3 Dooley, Mr. Patrick male 32.0 0 0 370376 7.75 NaN Q

변수

  • Survived: 생존 더미변수(0 = No, 1 = Yes)

  • Pclass: 티켓 등급(1 = 1st, 2 = 2nd, 3 = 3rd)

  • Sex: 성별

  • Age: 나이(년)

  • SibSp: 타이타닉에 함께 승선한 형제/배우자 숫자

  • Parch: 타이타닉에 함께 승선한 부모/자녀 숫자

  • Ticket: 티켓 번호

  • Fare: 여객 운임

  • Cabin: 캐빈 번호

  • Embarked: 승선 항구(C = Cherbourg, Q = Queenstown, S = Southampton)

아래 데이터세트 정보를 보면 Age, Cabin, Embarked 변수에 결측값이 있는 것을 알 수 있다.

Titanic.info()
<class 'pandas.core.frame.DataFrame'>
Index: 891 entries, 1 to 891
Data columns (total 11 columns):
 #   Column    Non-Null Count  Dtype  
---  ------    --------------  -----  
 0   Survived  891 non-null    int64  
 1   Pclass    891 non-null    int64  
 2   Name      891 non-null    object 
 3   Sex       891 non-null    object 
 4   Age       714 non-null    float64
 5   SibSp     891 non-null    int64  
 6   Parch     891 non-null    int64  
 7   Ticket    891 non-null    object 
 8   Fare      891 non-null    float64
 9   Cabin     204 non-null    object 
 10  Embarked  889 non-null    object 
dtypes: float64(2), int64(4), object(5)
memory usage: 83.5+ KB

성별에 의한 생존 여부 예측#

우선 아무런 사전 정보가 없는 상태에서 승객의 생존 여부에 대한 사전적 확률을 예측해보자. 이를 위해서는 Survived 변수의 클래스별 상대적 빈도를 구하면 된다. 아래 결과에서 보듯이 \(p(\rm Survived=0)=0.616\)이고, \(p(\rm Survived=1)=0.384\)이다. 확률론적 접근에 따르면, 사망 확률이 생존 확률보다 더 크기 때문에 어떤 승객이 주어졌을 때 사망으로 예측하는 것이 오분류를 줄이는 방법이다.

이에 따라 훈련 데이터의 승객 891명에 대해 모두 \(\rm Survived=1\)로 분류할 경우, 정확도는 61.6%(오분류율 38.4%)가 될 것이다.

Titanic.Survived.value_counts()/len(Titanic.Survived)
Survived
0    0.616162
1    0.383838
Name: count, dtype: float64

성별 정보를 이용한 생존 여부 예측

이제 예시의 목적으로 성별 변수(Sex)만을 사용하여 생존 여부를 예측해보자. 이를 위해 먼저 female/male의 문자형 변수인 Sex 변수를 Female 더미변수(female이면 1, male이면 0)로 전환시킨다.

Titanic['Female'] = [1 if d == 'female' else 0 for d in Titanic.Sex]

분할표 만들기

pandas에서 제공하는 crosstab() 함수를 사용해서 두 범주형 변수인 SurvivedFemale 간의 교차빈도표, 즉 분할표를 만들어보자.

crosstable = pd.crosstab(index = Titanic['Survived'],
                   columns = Titanic['Female'],
                   margins = True)   # 행과 열의 합을 표시하기
crosstable
Female 0 1 All
Survived
0 468 81 549
1 109 233 342
All 577 314 891

위 분할표에 의해 여성과 남성 각각에 대해 생존 확률, 즉 조건부확률을 구할 수 있다. 다시 말하면, 생존에 대한 사후적 확률을 구할 수 있다.

\[p(\rm Survived=1 \mid Female=1)=233/314=0.742\]
\[p(\rm Survived=1 \mid Female=0)=109/577=0.189\]

여성(\(\rm Female=1\))의 경우에는 생존 확률(74.2%)이 사망 확률(25.8%)보다 높고, 반대로 남성(\(\rm Female=0\))은 생존 확률(18.9%)이 사망 확률(81.1%)보다 낮다. 따라서 어떤 승객이 주어졌을 때 여성이면 생존으로 예측하고, 남성이면 사망으로 예측하는 것이 최선이다.

이와 같은 식으로 훈련 데이터 승객 891명의 생존 여부를 분류할 경우 정확도를 계산해보자. 위 분할표에서 보듯이 전체 승객 891명 중 여성으로서 생존자는 233명이고, 남성으로서 사망자는 468명이기 때문에 여성을 생존, 남성을 사망으로 예측할 경우 정확도는 78.7%(오분류율 21.3%)가 된다.

\[ \rm Accuracy = \frac{233+468}{891}=0.787\]

BernoulliNB 함수를 이용한 분석

위 예제는 예측변수가 Female 하나뿐이어서 분할표 작성만으로 조건부확률을 구할 수 있지만, 여기에서는 예시의 목적으로 동일한 작업을 나이브 베이즈 알고리즘으로 실행시켜보자. 예측변수인 Female이 이진(bianry) 정성적 변수이기 때문에 베르누이 나이브 베이즈가 적절하다. 이를 수행하기 위해 사이킷런(sklearn)의 naive_bayes 모듈에서 BernoulliNB 함수를 불러 들인다.

이제 모델을 베르누이 나이브 베이즈(BernoulliNB)로 선택하고, 모델의 이름을 clf로 지정했다. 그런 다음, fit 메서드를 사용하여 모델을 피팅하는데, 예측변수는 Titanic[['Female']]이고 반응변수는 Titanic.Survived이다. 그 피팅 결과를 바탕으로 predict 메서드를 이용해 훈련 세트(Titanic[['Female']])에 대해 Survived 범주를 예측하도록 하여 pred라는 이름으로 지정했다.

아래 결과를 보면, 혼동행렬 자체가 앞의 분할표와 정확히 일치하는 것을 알 수 있다.

from sklearn.naive_bayes import BernoulliNB

clf = BernoulliNB()
clf_fit = clf.fit(Titanic[['Female']], Titanic.Survived)
pred = clf_fit.predict(Titanic[['Female']])
cm = confusion_matrix(Titanic.Survived, pred) 
cm_df = pd.DataFrame(cm, index=['0','1'], columns=['0','1'])
cm_df.index.name = 'True'
cm_df.columns.name = 'Predicted'
print(cm_df)
print()
print(classification_report(Titanic.Survived, pred, digits=3))
Predicted    0    1
True               
0          468   81
1          109  233

              precision    recall  f1-score   support

           0      0.811     0.852     0.831       549
           1      0.742     0.681     0.710       342

    accuracy                          0.787       891
   macro avg      0.777     0.767     0.771       891
weighted avg      0.785     0.787     0.785       891

로지스틱 회귀 모델 추정

예시의 목적으로 이번에는 앞에서 배운 로지스틱 회귀 모델로 생존 확률을 예측하고, 그것에 기반하여 생존 여부를 분류해보자. statsmodels.formula.api 모듈의 logit() 함수를 이용하면 된다. 반응변수는 생존 더미변수인 Survived이고, 예측변수는 Female이다.

import statsmodels.formula.api as smf

model = smf.logit('Survived ~ Female', data=Titanic)
logitfit = model.fit()
print(logitfit.summary())
Optimization terminated successfully.
         Current function value: 0.515041
         Iterations 5
                           Logit Regression Results                           
==============================================================================
Dep. Variable:               Survived   No. Observations:                  891
Model:                          Logit   Df Residuals:                      889
Method:                           MLE   Df Model:                            1
Date:                Sat, 29 Nov 2025   Pseudo R-squ.:                  0.2266
Time:                        23:20:56   Log-Likelihood:                -458.90
converged:                       True   LL-Null:                       -593.33
Covariance Type:            nonrobust   LLR p-value:                 2.020e-60
==============================================================================
                 coef    std err          z      P>|z|      [0.025      0.975]
------------------------------------------------------------------------------
Intercept     -1.4571      0.106    -13.701      0.000      -1.666      -1.249
Female         2.5137      0.167     15.036      0.000       2.186       2.841
==============================================================================

아래는 생존 확률 분계점으로 0.5를 사용하여 훈련 세트에 대해 생존 여부를 분류한 것이다. 혼동행렬 자체가 앞의 베르누이 나이브 베이즈와 정확히 일치하는 것을 확인할 수 있다.

logit_pred = pd.Series(logitfit.predict(Titanic) > 0.5).map({False: 0, True: 1})
cm = confusion_matrix(Titanic.Survived, logit_pred)  
cm_df = pd.DataFrame(cm, index=[0,1], columns=[0,1])
cm_df.index.name = 'True'
cm_df.columns.name = 'Predicted'
print(cm_df)
print()
print(classification_report(Titanic.Survived, logit_pred, digits=3))
Predicted    0    1
True               
0          468   81
1          109  233

              precision    recall  f1-score   support

           0      0.811     0.852     0.831       549
           1      0.742     0.681     0.710       342

    accuracy                          0.787       891
   macro avg      0.777     0.767     0.771       891
weighted avg      0.785     0.787     0.785       891

나이 및 운임에 의한 생존 여부 예측#

이번에도 예시의 목적으로 나이(Age)와 여객 운임(Fare) 변수를 사용하여 생존 여부를 예측해보자. 두 예측변수 모두 정량적 변수들이다.

먼저 Age 변수에 결측값이 있는데, 이를 제외시키면 관측이 714개로 줄어든다. 그런 다음, Titanic 데이터세트에서 AgeFare 변수만을 골라 X라는 이름으로 지정한다.

Titanic = Titanic.dropna(subset=['Age'])
Titanic.shape
(714, 12)
X = Titanic[['Age', 'Fare']]

가우시안 나이브 베이즈

예측변수가 모두 정량적 변수이기 때문에 가우시안 나이브 베이즈가 적절하다. 모델을 가우시안 나이브 베이즈(GaussianNB)로 선택하고, 모델의 이름을 gnb로 지정했다. 그런 다음, fit 메서드를 사용하여 모델을 피팅하는데, 이를 위해 예측변수(X)와 반응변수(Titanic.Survived)를 입력한다. 그 피팅 결과를 바탕으로 predict 메서드를 이용해 테스트 세트 관측(X)에 대해 클래스를 예측하도록 하여 pred라는 이름으로 지정했다.

gnb = GaussianNB()
gnb_fit = gnb.fit(X, Titanic.Survived)
pred = gnb_fit.predict(X)

confusion_matrix() 함수를 사용해서 pred로 지정해 놓은 예측 클래스를 실제 클래스(Titanic.Survived)와 비교하여 혼동행렬을 만들었다. 아래 결과를 보면, 정확도(accuracy)가 65.3%이다.

cm = confusion_matrix(Titanic.Survived, pred) 
cm_df = pd.DataFrame(cm, index=['0','1'], columns=['0','1'])
cm_df.index.name = 'True'
cm_df.columns.name = 'Predicted'
print(cm_df)
print()
print(classification_report(Titanic.Survived, pred, digits=3))
Predicted    0   1
True              
0          399  25
1          223  67

              precision    recall  f1-score   support

           0      0.641     0.941     0.763       424
           1      0.728     0.231     0.351       290

    accuracy                          0.653       714
   macro avg      0.685     0.586     0.557       714
weighted avg      0.677     0.653     0.596       714

로지스틱 회귀 모델 추정

이번에는 로지스틱 회귀 모델로 동일한 예측변수를 사용하여 생존 확률을 예측하고, 그것에 기반하여 생존 여부를 분류해보자. 반응변수는 생존 더미변수인 Survived이고, 예측변수는 AgeFare이다.

model = smf.logit('Survived ~ Age + Fare', data = Titanic)
logitfit = model.fit()
print(logitfit.summary())
Optimization terminated successfully.
         Current function value: 0.624185
         Iterations 6
                           Logit Regression Results                           
==============================================================================
Dep. Variable:               Survived   No. Observations:                  714
Model:                          Logit   Df Residuals:                      711
Method:                           MLE   Df Model:                            2
Date:                Sat, 29 Nov 2025   Pseudo R-squ.:                 0.07587
Time:                        23:20:56   Log-Likelihood:                -445.67
converged:                       True   LL-Null:                       -482.26
Covariance Type:            nonrobust   LLR p-value:                 1.286e-16
==============================================================================
                 coef    std err          z      P>|z|      [0.025      0.975]
------------------------------------------------------------------------------
Intercept     -0.4171      0.186     -2.243      0.025      -0.782      -0.053
Age           -0.0176      0.006     -3.103      0.002      -0.029      -0.006
Fare           0.0173      0.003      6.596      0.000       0.012       0.022
==============================================================================

아래는 생존 확률 분계점으로 0.5를 사용하여 훈련 세트에 대해 생존 여부를 분류한 것이다. 정확도(accuracy)가 65.7%로 앞의 나이브 베이즈(65.3%)와 큰 차이가 없다.

logit_pred = pd.Series(logitfit.predict(Titanic) > 0.5).map({False: 0, True: 1})
cm = confusion_matrix(Titanic.Survived, logit_pred)  
cm_df = pd.DataFrame(cm, index=[0,1], columns=[0,1])
cm_df.index.name = 'True'
cm_df.columns.name = 'Predicted'
print(cm_df)
print()
print(classification_report(Titanic.Survived, logit_pred, digits=3))
Predicted    0   1
True              
0          380  44
1          201  89

              precision    recall  f1-score   support

           0      0.654     0.896     0.756       424
           1      0.669     0.307     0.421       290

    accuracy                          0.657       714
   macro avg      0.662     0.602     0.589       714
weighted avg      0.660     0.657     0.620       714

ChatGPT Q&A#

Titanic 데이터세트에서 Survived 여부를 Female, Age, Fare 변수를 사용하여 나이브 베이즈 기법으로 예측하는 코드를 만들어줘.


Titanic 데이터셋에서 ‘Survived’ 여부를 예측하기 위해 나이브 베이즈(Naive Bayes) 모델을 사용하는 Python 코드를 작성해보겠습니다. 여기서는 scikit-learn 라이브러리를 활용할 것입니다. ‘Female’, ‘Age’, ‘Fare’ 변수를 특성으로 사용할 예정이며, 먼저 필요한 라이브러리와 데이터를 불러온 후, 데이터를 전처리하고, 마지막으로 나이브 베이즈 모델을 학습시키고 예측을 진행합니다.

import pandas as pd  
from sklearn.model_selection import train_test_split  
from sklearn.naive_bayes import GaussianNB  
from sklearn.metrics import accuracy_score  
from sklearn.impute import SimpleImputer

# 데이터 로드 (Titanic 데이터셋의 경로는 상황에 따라 달라질 수 있습니다)  
titanic = pd.read_csv('titanic.csv')

# 'Female' 컬럼 생성: 'Sex' 컬럼을 사용하여 female이면 1, male이면 0으로 설정  
titanic['Female'] = titanic['Sex'].map({'female': 1, 'male': 0})

# 필요한 특성 선택  
features = ['Female', 'Age', 'Fare']  
X = titanic[features]  
y = titanic['Survived']

# 결측치 처리: 'Age'와 'Fare'에 대한 결측치를 평균값으로 대체  
imputer = SimpleImputer(strategy='mean')  
X_imputed = imputer.fit_transform(X)

# 데이터를 학습 세트와 테스트 세트로 분할  
X_train, X_test, y_train, y_test = train_test_split(X_imputed, y, test_size=0.3, random_state=42)

# 나이브 베이즈 모델 생성 및 학습  
model = GaussianNB()  
model.fit(X_train, y_train)

# 테스트 데이터에 대해 예측  
predictions = model.predict(X_test)

# 예측 정확도 출력  
print("Accuracy:", accuracy_score(y_test, predictions))

이 코드는 다음과 같은 단계를 포함합니다:

  1. 필요한 라이브러리와 데이터를 불러옵니다.

  2. ‘Sex’ 컬럼을 기반으로 새로운 이진 ‘Female’ 컬럼을 생성합니다.

  3. ‘Age’와 ‘Fare’ 컬럼의 결측치를 평균값으로 대체합니다.

  4. 데이터를 학습 세트와 테스트 세트로 분할합니다.

  5. Gaussian Naive Bayes 모델을 생성하고, 학습 데이터에 대해 학습시킵니다.

  6. 테스트 데이터에 대한 예측을 수행하고, 정확도를 계산하여 출력합니다.

Titanic 데이터셋의 경로는 사용하는 환경이나 데이터셋이 위치한 경로에 따라 달라질 수 있으니, 실제 경로로 수정해 사용해야 합니다.

ChatGPT가 제시한 코드 실행

import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.naive_bayes import GaussianNB
from sklearn.metrics import accuracy_score
from sklearn.impute import SimpleImputer

# 'Female' 컬럼 생성
Titanic['Female'] = Titanic['Sex'].map({'female': 1, 'male': 0})

# 필요한 특성 선택
features = ['Female', 'Age', 'Fare']
X = Titanic[features]
y = Titanic['Survived']

# 결측치 처리: 'Age'와 'Fare'에 대한 결측치를 평균값으로 대체
imputer = SimpleImputer(strategy='mean')
X_imputed = imputer.fit_transform(X)

# 데이터를 학습 세트와 테스트 세트로 분할
X_train, X_test, y_train, y_test = train_test_split(
    X_imputed, y, test_size=0.3, random_state=42)

# 나이브 베이즈 모델 생성 및 학습
model = GaussianNB()
model.fit(X_train, y_train)

# 테스트 데이터에 대해 예측
predictions = model.predict(X_test)

# 예측 정확도 출력
print("Accuracy:", accuracy_score(y_test, predictions))
Accuracy: 0.7674418604651163

ChatGPT Q&A#

똑같은 데이터세트와 X, y 변수에 대해 로짓 모델로 추정했을 때의 Accuracy를 구해줘.


로지스틱 회귀(Logistic Regression) 모델을 사용하여 Titanic 데이터셋에서 ‘Survived’ 여부를 예측하는 Python 코드를 작성하겠습니다. 앞서 나이브 베이즈 모델에서 사용했던 동일한 데이터셋과 특성(‘Female’, ‘Age’, ‘Fare’)을 사용할 것입니다. 이번에는 scikit-learnLogisticRegression 클래스를 사용합니다. 데이터 전처리 단계는 이전과 동일하며, 모델 학습 후에는 테스트 데이터셋에 대한 정확도를 계산합니다.

import pandas as pd  
from sklearn.model_selection import train_test_split  
from sklearn.linear_model import LogisticRegression  
from sklearn.metrics import accuracy_score  
from sklearn.impute import SimpleImputer

# 데이터 로드  
titanic = pd.read_csv('titanic.csv')

# 'Female' 컬럼 생성  
titanic['Female'] = titanic['Sex'].map({'female': 1, 'male': 0})

# 필요한 특성 선택  
features = ['Female', 'Age', 'Fare']  
X = titanic[features]  
y = titanic['Survived']

# 결측치 처리  
imputer = SimpleImputer(strategy='mean')  
X_imputed = imputer.fit_transform(X)

# 데이터 분할  
X_train, X_test, y_train, y_test = train_test_split(X_imputed, y, test_size=0.3, random_state=42)

# 로지스틱 회귀 모델 생성 및 학습  
model = LogisticRegression()  
model.fit(X_train, y_train)

# 예측 및 정확도 계산  
predictions = model.predict(X_test)  
accuracy = accuracy_score(y_test, predictions)

print("Accuracy:", accuracy)

이 코드는 앞서 나이브 베이즈 모델과 비슷한 절차를 따르며, 주요 차이점은 모델 생성 부분에서 LogisticRegression을 사용한다는 것입니다. 로지스틱 회귀는 이진 분류 문제에 적합한 모델이며, 이 예제에서는 ‘Survived’ 여부를 예측하는 데 사용됩니다. 코드 실행 후에는 테스트 데이터셋에 대한 정확도가 출력됩니다.

Titanic 데이터셋의 위치는 사용 환경에 따라 다를 수 있으니, 실제 데이터 파일이 위치한 경로로 수정하여 사용해야 합니다.

ChatGPT가 제시한 코드 실행

import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score
from sklearn.impute import SimpleImputer

# 'Female' 컬럼 생성
Titanic['Female'] = Titanic['Sex'].map({'female': 1, 'male': 0})

# 필요한 특성 선택
features = ['Female', 'Age', 'Fare']
X = Titanic[features]
y = Titanic['Survived']

# 결측치 처리
imputer = SimpleImputer(strategy='mean')
X_imputed = imputer.fit_transform(X)

# 데이터 분할
X_train, X_test, y_train, y_test = train_test_split(
    X_imputed, y, test_size=0.3, random_state=42)

# 로지스틱 회귀 모델 생성 및 학습
model = LogisticRegression()
model.fit(X_train, y_train)

# 예측 및 정확도 계산
predictions = model.predict(X_test)
accuracy = accuracy_score(y_test, predictions)

print("Accuracy:", accuracy)
Accuracy: 0.7627906976744186