for textmining

나이브 베이즈 분류기

|

이번 글에서는 문서 분류를 하기 위한 나이브 베이지안 분류기(Naive Bayesian Classifier)에 대해 살펴보도록 하겠습니다. 이번 글 역시 고려대 강필성 교수님과 역시 같은 대학의 정순영 교수님 강의, 그리고 Speech and Language Processing(2nd ver)을 정리했음을 먼저 밝힙니다. 베이즈 규칙과 관련해서는 이곳을 참고하시면 좋을 것 같습니다. 그럼 시작하겠습니다.

나이브 베이즈 모델

문서 이진분류 문제를 예로 들어보겠습니다. 우리가 풀려는 문제는 문서 $d$가 주어졌을 때 범주 $c_1$ 혹은 $c_2$로 분류하는 것입니다. 지금까지 설명한 베이즈 법칙을 다시 쓰면 아래와 같습니다.

\[\begin{align*} P({ c }_{ 1 }|d)&=\frac { P({ c }_{ 1 },d) }{ P(d) } =\frac { \frac { P({ c }_{ 1 },d) }{ P({ c }_{ 1 }) } \cdot P({ c }_{ 1 }) }{ P(d) } =\frac { P(d|{ c }_{ 1 }) { P({ c }_{ 1 }) }}{ P(d) } \\ P({ c }_{ 2 }|d)&=\frac { P(d|{ c }_{ 2 }){ P({ c }_{ 2 }) } }{ P(d) } \end{align*}\]

위 식에서 $P(c_i)$는 사전확률(prior)입니다. 범주 $c_i$인 문서 개수를 전체 문서 개수로 나눈 비율을 뜻합니다. $P(d$|$c_i)$는 우도(likelihood)입니다. $P(c_i$|$d)$는 사후확률(posterior)입니다. 문서 $d$가 주어졌을 때 해당 문서가 범주 $c_i$일 확률, 즉 우리가 알고 싶은 값입니다.

베이즈 모델은 $P(c_1$|$d)/P(d)$와 $P(c_2$|$d)/P(d)$를 비교해 큰 쪽으로 범주를 할당합니다. 그런데 여기에서 $P(d)$는 겹치므로 계산을 아예 생략할 수 있습니다. 그러면 위 베이즈 공식을 아래와 같이 다시 쓸 수 있습니다.

\[P({ c }_{ i }|d)\propto P(d|{ c }_{ i }){ P({ c }_{ i }) }\]

만약 문서 범주 비율, 즉 사전확률 $P(c_1)$과 $P(c_2)$가 0.5로 서로 같다면 사전확률 계산도 생략 가능합니다.

\[P({ c }_{ i }|d)\propto P(d|{ c }_{ i })\]

이번엔 문서 $d$가 단어 $w_1$, $w_2$로 구성돼 있다고 칩시다. 식을 또 다시 써보겠습니다.

\[\begin{align*} P({ c }_{ i }|d)&=P({ c }_{ i }|{ w }_{ 1 },{ w }_{ 2 })\\ &\propto P({ w }_{ 1 },{ w }_{ 2 }|{ c }_{ i }){ P({ c }_{ i }) } \\ &\propto P({ w }_{ 1 },{ w }_{ 2 }|{ c }_{ i }) \end{align*}\]

나이브 베이즈 분류기는 각 단어가 독립(independent)임을 가정합니다. 모델 이름에 나이브라는 말이 붙은 이유이기도 합니다. 이에 따라 식을 다시 쓸 수 있습니다.

\[P({ w }_{ 1 },{ w }_{ 2 })=P({ w }_{ 1 })\cdot P({ w }_{ 2 })\\ P({ w }_{ 1 },{ w }_{ 2 }|{ c }_{ i })=P({ w }_{ 1 }|{ c }_{ i })\cdot P({ w }_{ 2 }|{ c }_{ i })\]

bag-of-words representation

각 단어가 독립임을 가정하는 나이브 베이즈 분류기는, 문서 표현(representation) 방식 중 가장 간단한 기법인 Bag-of-Words와 본질적으로 같은 접근을 취합니다. 나이브 베이즈 분류기는 범주가 주어졌을 때 각 단어가 나타날 확률(우도)의 연쇄적인 곱으로 계산을 하는데, $a$ 다음에 $b$를 곱하든 그 반대로 곱하든 결과가 같습니다.

bag-of-words도 나이브 베이즈 모델처럼 단어의 등장 순서를 무시합니다. 그저 문서 내 빈도만을 따져서 문서를 표현합니다. 문서를 어떤 가방(bag) 안에 넣고 이를 뒤섞는다는 느낌 정도로 이해하면 직관적일 것 같습니다. 아래 그림은 영화 리뷰를 bag-of-words 방식의 representation으로 바꾸는 걸 도식화한 것입니다.

unigram model

나이브 베이즈 분류기는 언어모델(Language Model)유니그램(unigram) 모델과도 접점이 많습니다. 유니그램 모델은 도메인(domain)별로 각 단어의 등장확률을 구해놓은 다음 표와 같은 형태입니다.

임의의 문자열이 주어졌을 때 유니그램 모델은 단어별 등장 확률을 반복적으로 곱해 해당 문자열이 나타날 확률을 계산합니다. 나이브 베이즈 모델과 비교해 생각해볼 때 사전확률을 제외하면 그 과정이 동일합니다.

계산예시1

다음과 같은 말뭉치가 주어졌다고 치겠습니다.

우선 사전확률부터 구해보겠습니다. 학습데이터 전체 5건의 문서 가운데 3개가 부정($-$), 2개가 긍정($+$)이므로 다음과 같습니다.

\[P\left( - \right) =\frac { 3 }{ 5 } ,\quad P\left( + \right) =\frac { 2 }{ 5 }\]

이번엔 우도를 구해보겠습니다. 우선 학습데이터 문서 가운데 부정 범주의 전체 단어수는 14개입니다. 이 가운데 각 단어가 몇 개 있는지 세어서 각각의 우도를 구합니다. 다음과 같습니다.

\[P\left( predictable|- \right) =\frac { 1 }{ 14 } \\ P\left(no|- \right) =\frac { 1 }{ 14 } \\ P\left(fun|- \right) =\frac { 0 }{ 14 }\]

이번엔 긍정 범주의 우도를 구해볼까요? 다음과 같습니다.

\[P\left(predictable|+ \right) =\frac { 0 }{ 9 } \\ P\left(no|+ \right) =\frac { 0 }{ 9 } \\ P\left(fun|+ \right) =\frac { 1 }{ 9 }\]

이번엔 검증데이터 문서를 예측할 차례입니다. 각 범주에 해당하는 사전확률과 우도의 곱(아래 식)을 비교해 큰 쪽으로 분류하는 방식입니다.

\[P\left( - \right) \times P\left( predictable|- \right) \times P\left( no|- \right) \times P\left(fun|- \right) \\ P\left( + \right) \times P\left(predictable|+ \right) \times P\left(no|+ \right) \times P\left(fun|+ \right)\]

그런데 실제 계산을 해보면 둘 모두 0이 될 겁니다. 우도 값 가운데 0이 되는 값이 있기 때문입니다. 우도값이 0이 되지 않도록 보정하는 평활화(smoothing) 기법이 제안되었습니다. 실제 구현에서 어떻게 동작하는지 이따가 살펴보겠습니다.

나이브 베이즈 모델의 학습은 학습말뭉치의 빈도 수를 세어서 위의 사전확률과 우도를 모두 구해놓는 과정을 가리킵니다. 추론(inference)은 사전확률과 우도의 곱을 계산하고, 큰 쪽의 범주를 할당합니다.

검증데이터 문서에 학습데이터에 없는 단어가 있을 경우엔 우도가 존재하지 않으므로 이를 빼고 계산합니다. 또한 관사, 전치사 등 범주 분류에 불필요하다고 판단되는 단어들에 대해서는 불용어(stopwords) 처리를 해서 빼기도 합니다.

계산예시2

예를 하나 더 들어보겠습니다. ‘love’, ‘fantastic’ 두 개 단어로 구성된 영화 리뷰1을 긍정, 부정 두 개 범주 가운데 하나로 할당해야 한다고 가정합시다. 리뷰1이 긍정일 확률은 아래와 같이 우도의 연쇄적인 곱으로 구합니다. (사전확률, 즉 긍정/부정 리뷰 비율은 동일하다고 가정)

\[\begin{align*} P(positive|{ review }_{ 1 })&\propto P(love|positive)\times P(fantastic|positive)\\ \\ &=\frac { count(love,positive) }{ \sum _{ w\in V }^{ }{ count(w,positive) } } \times \frac { count(fantastic,positive) }{ \sum _{ w\in V }^{ }{ count(w,positive) } } \end{align*}\]

이와 같은 방식으로 리뷰1이 부정일 확률도 구할 수 있습니다. 둘 중 큰 쪽으로 해당 리뷰의 범주를 할당합니다. 그러면 아래 리뷰 두 개를 분류해 봅시다.

review1 : This movie was awesome! I really enjoyed it.

review2 : This movie was boring and waste of time.

전체 말뭉치로부터 구한 우도는 아래와 같습니다.

Words P(Word|positive) P(Word|negative)
This 0.1 0.1
Movie 0.1 0.1
Was 0.1 0.1
Awesome 0.4 0.01
I 0.2 0.2
Really 0.3 0.05
enjoyed 0.5 0.05
It 0.1 0.1
Boring 0.02 0.3
And 0.1 0.1
Waste 0.02 0.35
Of 0.02 0.02
Time 0.15 0.15

Review1은 위 우도 표에 의해 긍정, Review2는 부정 범주로 분류됩니다.

\[{ review }_{ 1 }\quad :\quad \prod _{ i }^{ }{ P({ word }_{ i }|Pos)=120\times { 10 }^{ -8 } } >\prod _{ i }^{ }{ P({ word }_{ i }|Neg)=0.5\times { 10 }^{ -8 } } \\ { review }_{ 2 }\quad :\quad \prod _{ i }^{ }{ P({ word }_{ i }|Pos)=0.012\times { 10 }^{ -8 } } <\prod _{ i }^{ }{ P({ word }_{ i }|Neg)=3.15\times { 10 }^{ -8 } }\]

장단점 및 최적화

나이브 베이즈 분류기는 앞선 예시의 우도 테이블 하나만 있으면 분류가 가능합니다. 사전확률이 다르다면 전체 문서 범주 비율만 더 반영해주면 됩니다. 그만큼 계산복잡성이 낮다는 얘기입니다. 이미 말씀드렸던 것처럼 단어 등장확률을 독립으로 가정하는 Bag-of-Words 기법과 잘 어울리는 모델이라고 합니다. 딥러닝 이전 자연언어처리 기법으로 각광받았던 모델입니다.

다만 나이브 베이즈 분류기는 문서에 등장하는 단어 수만큼의 우도 확률 곱으로 분류를 수행하기 때문에 단어 수가 늘어날 수록 그 값이 0으로 수렴하는 경향이 있습니다. 1보다 작은 값은 곱할 수록 작아지는 게 당연한 이치입니다. 특히 특정 범주, 가령 긍정 문서에 단 한번도 등장하지 않은 단어가 있다면 해당 단어의 우도는 0이 되기 때문에 분류 과정에 큰 문제가 됩니다. 이를 위해 smoothing을 수행합니다.

문서 내 중복단어를 없애 분석 정확성을 높이는 Binary multinomial naive Bayes(binary NB) 기법도 있습니다. 한 단어가 문서 내에 등장했는지 여부만 따집니다. 개별 문서에 한두차례 등장하지만 정보성이 높은 단어를 잘 포착하는 경향이 있어 성능 향상에 도움이 된다고 합니다.

나이브 베이즈 모델은 그 특성상 부정어(negation) 처리에 취약합니다. 이 때문에 부정어가 등장한 위치에서 뒤에 나오는 모든 단어에 다음과 같은 처리를 하여 이 문제를 보완하는 방법도 제안됐습니다. 이렇게 되면 not_like, not_movie와 관련된 우도가 like, movie와 따로 계산되고, 추론 과정에서도 따로 계산된 결과가 반영됩니다.

didn’t like this movie => didn’t not_like not_this not_movie

파이썬 구현

실제 구현 단계에서는 다음과 같은 평활화 처리가 필요합니다. $i$번째 단어가 긍정 문서에 단 한번도 쓰이지 않더라도 그 우도확률이 0이 되지 않도록 분자와 분모에 적당히 작은 상수를 더해주는 것입니다.

\[P({ w }_{ i }|positive)=\frac { k+count({ w }_{ i },positive) }{ 2k+\sum _{ w\in V }^{ }{ count(w,positive) } }\]

또한 컴퓨터는 0에 가까운 부동소수점(floating point number)을 제대로 처리하지 못하기 때문에 우도의 곱은 로그 우도의 합으로 처리합니다. 예컨대 긍정 범주에 대해서는 다음과 같습니다.

\[\prod _{ i }^{ }{ P({ word }_{ i }|Pos) } =exp\left[ \sum _{ i }^{ }{ \left\{ \log { P({ word }_{ i }|Pos) } \right\} } \right]\]

나이브 베이지안 분류기를 ‘밑바탁부터 시작하는 데이터 과학(조엘 그루스 지음, 인사이트 펴냄)’을 기본으로 해서 살짝 손질한 파이썬 코드는 다음과 같습니다. 별도 토크나이징은 하지 않고 어절 단위(띄어쓰기)로 문자열을 나눠서 학습을 진행합니다.

학습은 다음과 같이 하면 됩니다.

model = NaiveBayesClassifier()
model.train(trainfile_path='kor_review.csv')

학습용 말뭉치(kor_review.csv)는 아래처럼 생겼습니다.

"이게 왜 명작 이라는 건지",0.5
"감동이 오지 않음에 나 역시 놀란다 내가 문제인걸까",2

테스트는 다음과 같이 하면 됩니다.

model.classify('모든 것이 완벽하다 평생 함께 갈 영화')

파일럿 실험

왓챠 영화 리뷰 70만여개를 학습한 뒤 테스트 문장을 다음과 같이 넣어봤습니다. 오른쪽의 숫자는 해당 리뷰가 긍정 범주일 가능성을 나타냅니다. 클 수록 긍정, 작을 수록 부정 리뷰로 분류된 겁니다.

(1) 단언컨대 이 영화의 결말은 영화사를 통틀어 최고 중 하나입니다 물론 거기까지 이끌어낸 플롯도 실로 대단하고 말이죠 : 0.99998

(2) 이 영화가 지루하고 뻔했다는건 내가 너무 늦게 본 탓이겠지 ㅠㅠ : 0.01623

(3) 졸작이 아니라 대작이다 : 0.13773

간단한 모델임에도 비교적 좋은 성능을 보이는 가운데 (3)과 같이 전혀 엉뚱한 결과를 보이는 점이 눈에 띕니다. 부정어에 대한 별도 처리를 하지 않았기 때문에 생기는 문제 아닌가 하는 생각이 듭니다.

다음은 $P(w_i$|$positive)$가 큰 상위 200개 단어 목록입니다.

영화, 수, 이, 그, 너무, 정말, 더, 영화를, 내, 것, 있는, 최고의, 내가, 그리고, 가장, 본, 한, 진짜, 영화가, 잘, 보고, 다시, 없는, 이런, 다, 모든, 대한, 영화는, 없다, 보는, 또, 있다, 마지막, 하는, 최고, 이렇게, 나는, 영화의, 때, 좋다, 그냥, 볼, 난, 왜, 할, 같다, 같은, 좋은, 아름다운, 내내, 중, 다른, 함께, 것이, 꼭, 작품, 것을, 하지만, 봐도, 역시, 이야기, 나도, 나의, 모두, 많은, 않는, 사랑, 완벽한, 아닌, 말이, 어떤, 않은, 보면, 하나, 연기, 싶다, 있을까, 많이, 두, 것은, 아, 아니라, 참, 영화다, 것이다, 인생, 어떻게, 되는, 좋았다, 좋아하는, 한다, 하고, 스토리, 만드는, 만든, 나를, 제일, 지금, 아직도, 위한, 그런, 된, 싶은, 결국, 명작, 위해, 된다, 않는다, 될, 얼마나, 계속, 처음, 밖에, 그의, 사랑을, 없이, 영화에, 끝까지, 이건, 별, 좀, 사람이, 감독의, 봤다, 음악, 우리, 대해, 주는, 인간의, U, 한번, 줄, 않고, 눈물이, 액션, 건, gt, 듯, ㅠㅠ, 봤는데, 우리는, 그렇게, 새로운, 멋진, 안, 아니다, 느낌, 너무나, 사랑하는, 재밌게, 큰, 순간, 걸, 감동, 생각이, 그저, 같이, 나, 연출, 와, 재밌다, 장면, lt, 있었다, 게, 보여주는, 그래서, 이게, 나오는, 없었다, 그래도, 마음이, 진정한, 나에게, 아주, 애니메이션, 삶을, 보면서, 여운이, 배우들의, 날, 생각을, 아닐까, 때문에, 우리가, 특히, 더욱, 느낄, 미친, ㅋㅋ, 연기가, 좋고, 항상, 최고다, 영화관에서, 자신의, 장면이, the, you, 것도

다음은 $P(w_i$|$negative)$가 큰 상위 200개 단어 목록입니다.

영화, 이, 너무, 수, 왜, 더, 그냥, 영화를, 없다, 그, 없는, 이런, 다, 영화는, 것, 영화가, 내가, 정말, 좀, 진짜, 잘, 내, 본, 이렇게, 보는, 한, 보고, 안, 좋은, 하는, 대한, 이건, 있는, 별, 난, 없고, 스토리, 느낌, 영화의, 하지만, 그리고, 아, 이게, 같은, 많이, 같다, 끝까지, 만든, 할, 봤는데, 내내, 볼, 뻔한, 뭐, 무슨, 참, 건, 하고, 모르겠다, 그래도, 있다, 이걸, 듯, 나는, 아닌, 별로, 보면, 하나, 모든, 전혀, 다른, 뭔가, 게, 않는, 차라리, 않은, 때문에, 아니다, 마지막, 연기, 가장, 이제, 영화에, 감독이, 것도, 다시, 근데, 위한, 때, 이야기, gt, 못한, 못, 감독의, 또, 이거, 것이, 아깝다, 뭘, 아니라, 줄, 않는다, 그저, 어떻게, 그나마, 봤다, 없었다, 그래서, 역시, 최악의, 재미가, 않고, 싶다, 두, 나오는, 위해, 것은, 결국, 했다, 한다, lt, 감독, 제대로, 연출, 재미도, 것을, 많은, 1, 스토리가, 도대체, 걸, 그런, 뿐, 없이, 딱, 쓰레기, 0, 보다가, 보다, 그렇게, 않다, 제일, 아무리, 작품, 밖에, ㅠㅠ, 싶은, 전개, 아니고, 중, 영화관에서, 계속, 액션, 영화로, 기억에, 굳이, 만드는, 제발, 되는, 연기는, 대체, 배우들의, 큰, 그만, 보기, 없음, 거, 안되는, 어떤, 솔직히, 된, 모두, 끝, 하나도, 아쉽다, 돈, 한국, 될, 캐릭터, 연기가, 원작을, 함께, 스토리는, 노잼, 않았다, 기억이, 코미디, 배우, 전형적인, 최악, 내용이, 이해가, 생각이, 극장에서, 시간, 지루한, 그게, 수가, 말이, 영화도



Comments