나이브 베이즈(Naive Bayes) 실습(2) - 스팸 메일 분류

2022. 1. 30. 18:30AI/ML

이 포스트는 허민석님의 유튜브 머신러닝 내용을 정리한 글입니다. 실습 코드는 도서 나의 첫 머신러닝/딥러닝에서 발췌해왔습니다. 실습 코드와 자료는 링크의 Github에서 볼 수 있습니다

이전 포스트에서는 나이브 베이즈 분류기 중 하나인 가우시안 나이브 베이즈 모델을 사용해 꽃받침과 꽃잎 특징에 따른 붓꽃 종류를 분류해봤습니다. 이번 포스트에서는 다른 분류기인 베르누이 나이브 모델을 사용해, 수신된 이메일 중 스팸 메일을 분류해보겠습니다. 실제로 나이브 베이즈 모델은 스팸 메일 분류 등의 문서 분류에 적극 활용되고 있으며 높은 정확도를 보입니다. 이번에는 간단한 스팸 메일 분류를 위해, 학습과 테스트에 사용할 데이터들을 이메일 제목과 스팸 메일인지를 담은 레이블을 리스트로 직접 작성하여 사용하겠습니다.

spam or ham?

먼저 필요한 라이브러리를 임포트하고 학습 데이터를 작성한 다음, 그 데이터를 확인해보겠습니다. 참고로 베르누이 나이브 모델은 사이킷런 naive_bayes에 BernoulliNB로 저장되어 있으며 햄(ham) 메일은 스팸(spam) 메일이 아닌 메일입니다. 데이터를 살펴보면 이메일은 총 6개로, 스팸 메일 3개와 햄 메일 3개로 구성되어있습니다. 여느 때처럼 판다스 데이터 프레임 형식으로 변환한 다음 데이터를 확인해보겠습니다.

다음은 데이터 전처리입니다. 베르누이 나이브 베이즈 분류 모델은 숫자 데이터만 다루기 때문에, 먼저 레이블을 스팸인지 아닌지에 따라 1과 0으로 치환해줍니다. 다음에는 입력 데이터로, 베르누이 나이브 베이즈의 입력 데이터는 고정된 크기의 벡터여야 합니다. 따라서 이메일을 고정된 크기의 벡터로 치환해줍니다. 이를 위해 사이킷런의 CountVectorizer를 이용해, 전체 데이터의 모든 단어를 담은 고정된 크기의 벡터를 만들고 파라미터로 binary=True로 설정해 각각의 이메일에서 해당 이메일이 등장하는지, 등장하지 않는지를 1과 0으로 표시합니다. 베르누이 나이브 베이즈 모델에서는 등장 빈도가 중요한 것이 아니라 등장 여부에 중점을 맞추고 있기 때문에, 한 번 이상 등장했더라도 1로 표시합니다.

아래의 코드를 보면 데이터를 학습 데이터와 레이블로 분리한 다음 학습 데이터를 CountVectorizer로 변환(fit_transform)하여 베르누이 나이브 베이즈 모델의 입력값으로 사용할 인코딩 된 입력값(encoded_input)을 생성합니다. 모든 벡터의 길이는 17로, 전체 이메일의 중복을 제외한 단어 수를 세보면 17개임을 알 수 있습니다. get_features_name_out()으로 해당 인덱스에는 어떤 단어가 표시되어 있는지, 그리고 inverse_transform()으로 해당 벡터에는 어떤 단어가 있는지를 역으로 추적해봤습니다. 예를 들어 첫 번째 벡터는 6, 7, 11, 15번째 인덱스만 1의 값을 갖는데, cv.get_feature_names_out()으로 확인해보면 인덱스에 해당하는 단어들이 free, game, only, today에 매칭 됨을 알 수 있습니다.

 

이제 모델을 학습하겠습니다. 모델 학습은 사이킷런의 BernoulliNB()로 할 수 있습니다. 학습된 모델을 테스트하기 위해 테스트 데이터를 설정하는데, 잘 보면 테스트 데이터에는 hey, traveler 등 학습 데이터에 등장하지 않는 단어들이 보입니다. 이전 이론 포스트에서 잠시 알아봤듯이 이산적인 데이터 상에서 학습 데이터에 없는 데이터가 출현하면 확률이 0이 되는 문제점이 있습니다. 이를 방지하고자 스무딩 기법을 활용하는데, 사이킷런에 구현된 베르누이 나이브 베이즈 모델에서는 이미 디폴트로 스무딩을 지원하고 있어 문제없이 테스트를 할 수 있습니다. 다만 우리는 스무딩, 언더플로우에 대해 알아봤으므로 설사 학습 및 테스트 시 원치 않는 결과가 나왔다 할지라도 문제의 시발점이 어디였는지를 눈치챌 수 있을 것입니다.

마지막으로 작성한 테스트 데이터를 활용해 모델을 테스트하고, 정확도를 확인해보겠습니다. 단 6개의 학습 데이터로 학습한 모델임에도, 0.83의 높은 수치를 보였습니다. 조금 더 많은 학습 데이터와 데이터 전처리 과정을 거쳤다면 정확도는 한층 더 높았을 것으로 예측할 수 있습니다. 이번 포스트에서 중요한 점은 베르누이 나이브 베이즈의 모델의 입력 데이터를 위한 인코딩 작업입니다. 베르누이 나이브 베이즈 모델의 특성을 이해해, CountVectorizer의 파라미터로 binary=True를 설정하여 1과 0으로 인코딩 된 입력값을 만들었습니다. 이전 가우시안 나이브 베이즈 모델에서는 데이터를 시각화하여 데이터 분포를 본 다음, 데이터 분포가 가우시안 분포를 따르고 있음과 동시에 데이터가 이산적이지 않고 연속적이라는 특징을 활용하여 가우시안 나이브 베이즈 모델을 떠올리는 과정이 주였지만, 이번에는 그 역으로 모델의 특성에 맞게끔 데이터를 전처리하는 과정에 중점을 두고 보는 것이 좋습니다. 바로 다음 포스트로 넘어가 영화 리뷰 긍정 / 부정 분류하는 다항 분포 나이브 베이즈 모델을 실습해보겠습니다.