나이브 베이즈(Naive Bayes)를 활용한 문서 분류 – 파이썬 코드 예제

이전 포스팅에서 나이브 베이즈(Naive Bayes)를 사용해서 텍스트를 어떻게 분류할 수 있는지 정말 간단한 예제를 통해 살펴보았다.

이번에는 파이썬 머신러닝 라이브러리 scikit-learn에서 실제로 어떻게 구현하고 동작하는지 코드를 알아볼 차례.

scikit-learn 사용법

1. CounterVectorizer

scikit-learn에서 Naive Bayes 분류기를 사용하기 전에 일단 자연어(텍스트)로 이루어진 문서들을 1과 0 밖에 모르는 컴퓨터가 이해할 수 있는 형식으로 변환해야 할 거다. feature extraction, 어휘(특성) 추출 과정이라 볼 수 있다.

.fit()

일단 CountVectorizer라는 객체를 만든 후, fit() 메소드를 호출해서 학습 데이터 세트에 등장하는 어휘를 가르쳐놓아야 한다.

예를 들어 아래와 같이 코드를 작성하면

from sklearn.feature_extraction.text import CountVectorizer

vectorizer = CountVectorizer()
vectorizer.fit(["첫번째 문서 테스트", "두번째 문서 테스트"])

“첫번째”, “문서”, “테스트”, “두번째” 이렇게 총 4개의 어휘를 학습한 CountVectorizer를 만들게 된다.

못 믿겠다면 vectorizer.vocabulary_를 출력해보면 된다. 이렇게 뜰 거다.

{'첫번째': 2, '문서': 1, '테스트': 3, '두번째': 0}

고유한 어휘가 딕셔너리처럼 각각의 인덱스를 가지고 있다.

.transform()

어휘 사전을 만들었으니 이제 .transform() 메서드를 호출할 수 있다. .transform()는 문자열 목록을 가져와 미리 학습해놓은 사전을 기반으로 어휘의 빈도를 세주는 거다.

아래와 같이 공백으로 구분되는 문자열로 가져와서 넣어주면

counts = vectorizer.transform (["직접 첫번째 테스트 두번째 테스트"])

counts에는 각 어휘가 등장한 빈도수가 저장되는 거다. counts.toarray()를 출력해보면 [[1 0 1 2]]와 같은 array를 볼 수 있다. 위에서 학습된 인덱스에 따라 개수를 세주었다. 그리고 “직접”이라는 단어는 애초에 사전에 등록된 단어가 아니기 때문에 고려하지 않는다.

이렇게 텍스트를 컴퓨터가 이해할 수 있는 숫자들로 변환시켜놓은 셈이니 이제 나이브 베이즈를 본격적으로 사용해볼 수 있다.

2. MultinomialNB

나이브 베이즈 분류기를 학습시킬 때는 당연히 2개의 파라미터가 필요하다. 위에서 여러 문서들을 .transform() 해놓은 문서-단어 행렬과 그 문서들이 어떤 분류에 속하는지 레이블을 준비해서 넣어주면 된다.

from sklearn.naive_bayes import MultinomialNB

classifier = MultinomialNB()
classifier.fit(counts, labels)

이제 새로운 문서가 등장했을 때 어떤 분류로 속할지 예측을 할 수 있다.

단…! 텍스트를 바로 넣는다고 되는 게 아니다. 위에서 모델을 학습시키기 위해 학습 데이터를 문서-단어 행렬로 tranform 한 것처럼 새로운 텍스트도 tranform해서 넣어줘야 한다.

new_text= "이건 새로운 문서"
new_text_counts = counter.transform([new_text])

print(classifier.predict(new_text_counts))
print(classifier.predict_proba(new_text_counts))

.predict_proba()는 각 레이블로 분류될 확률을 돌려준다. 베이즈 정리로 해당 문서가 어떤 분류에 속할 확률을 각각 다 계산할 수 있기 때문이다.


나이브 베이즈 분류기를 구현하는 방법은 일단 여기까지다.

주요 내용을 다시 간단히 요약해보자.

요약

  • 베이즈 정리에서 사용되는 확률을 계산하려면 레이블이 지정된 데이터 세트가 필요하다.
  • 데이터 세트에 포함된 문서에서 등장하는 어휘들이 곧 특성(feature)이다. 그리고 베이즈 정리를 적용하기 위해 이 특성(어휘)들은 모두 독립적인 것으로 가정한다.
  • 베이즈 정리를 사용하면 어떤 문서가 있을 때 그것이 특정 레이블에 속할 확률, P(레이블 | 문서)를 계산할 수 있으며, 이 중 확률이 가장 높은 레이블로 예측을 하게 된다.

뭐 이렇게 간단해보이긴 하지만 사실 텍스트(문서)를 나이브 베이즈로 제대로 분류하려면 그 전에 꽤 복잡한 과정을 거쳐 데이터를 잘 가공하는 게 정말 중요하다. garbage in, garbage out이라는 거…

뭐 일단 간단하고 일반적인 작업으로는 이런 게 있겠다.

  • 문장 부호, 불용어 등을 제거한다.
  • 모든 단어를 소문자로 통일한다. (한국어는 해당사항 없지만)
  • ngram모델을 사용한다. 예를 들어 “이 음식은 너무 맛있다”는 문서가 있다면 여기서 feature는 “이”, “음식은”, “너무”, “맛있다”가 될 거다. 그러나 만약 bigram 모델을 사용하면 “이 음식은”, “음식은 너무”, “너무 맛있다”가 된다. 나이브 베이즈에서는 애초에 각 어휘들이 독립적이라고 가정하는데 이렇게 bigram 모델을 사용하면 연달아 등장하는 어휘를 고려하는 셈이니 조금 나아지는 편이다.
  • 형태소 단위로 쪼개서 집어 넣는다. 예를 들면 “공부하다”, “공부했다”, “공부하고”, “공부하는” 등의 다양한 어휘가 등장할 때 이것들 각각을 어휘로 보지 않고 “공부하-“라는 대표형으로 묶어서 보는 게 효과적이기 때문이다.
  • 특정 품사들만 선택해서 사용한다. 형태소 분석을 하면 각 어휘가 어떤 품사를 지니는지(태깅) 알 수 있기 때문에 이걸 바탕으로 일반명사, 고유명사, 형용사, 동사, 일반부사 위주로 사용하면 결과가 더 좋을 수도 있다. 물론 케바케다. 게다가 형태소를 나누는 기준이나 체계도 딱 정해져 있는 건 아니라…

사실 텍스트 분석은 생각보다 어렵다. 늪이다.

추천 글

댓글 남기기