활동/프로젝트

나이브 베이즈 모델을 이용한 확률 기반의 스팸 메일 분류기

짐니♡ 2024. 9. 18. 20:14

 

 

머신러닝 기말고사 대체과제

 

많이 부족했지만 성적 잘 받은 보고서 !!

 

* pc버전으로 보는걸 권장합니다 


주제: 나이브 베이즈 모델을 이용한 확률 기반의 스팸 메일 분류기

참고 자료

* 나이브 베이즈 개념 및 스팸메일 필터 데이터 구축

* 나이브 베이즈 기반 스팸메일 필터 모델 학습

 

 

1. 나이브 베이즈 분류기란?

 

- 확률 기반의 분류기
- 데이터가 각 클래스에 속할 특징 확률을 계산
- 나이브:예측한 특징이 상호 독립적이라는 가정 하에 확률 계산을 단순화
- 베이즈:입력 특징이 클래스 전체의 확률 분포 대비 특정 클래스에 속할 확률을 베이즈 정리를 기반으로 계산

 

 

2. 이용 데이터

 

- Enron 이메일 데이터 세트: preprocessed format

- 3672개의 정상 메일,1500개의 스팸 메일로 구성

- ham:정상 메일이 담긴 폴더

- spam:스팸 메일이 담긴 폴더

 

 

 

 

3. 처리 과정(코랩 이용)

 

from sklearn.feature_extraction.text import CountVectorizer
vectorizer = CountVectorizer(stop_words="english", max_features=500)
term_docs = vectorizer.fit_transform(cleaned_emails)
print(term_docs [0])

 

 

3-1. 파일 경로로 메일/스팸 기본 분류하기

 

from pickle import FALSE
import os
import re
import random
import glob
import numpy as np
from collections import defaultdict
from math import log


emails, labels = [], []

paths = ['/content/drive/MyDrive/enron1/spam', '/content/drive/MyDrive/enron1/ham']

  

# txt 파일 경로, 구글 드라이브에 enron1이라는 폴더 생성, spam,ham 데이터 나눔

 

for path in paths:
    for filename in glob.glob(os.path.join(path, '*.txt')):
        with open(filename, 'r', encoding="ISO-8859-1") as file:
            emails.append(file.read())        # txt 파일을 읽어서 email 리스트에 저장
            if path.endswith("spam"):
                labels.append(1)
            else:                             # spam 문구가 여부에 따라 1또는 0레이블을 붙임
                labels.append(0)

print(np.unique(labels, return_counts=True))

 

(array([0, 1]), array([3672, 1500]))

 

 

 

3-2. 숫자, 구두점, 사람 이름 제거 

 

import nltk
nltk.download('names')
nltk.download('wordnet')

from nltk.corpus import names
from nltk.stem import WordNetLemmatizer

all_names=set(names.words())
lemmatizer = WordNetLemmatizer()
# WordNetLemmatizer()로 불용어 처리

cleaned_emails=[]
for email in emails:
  cleaned_emails.append(' '.join([lemmatizer.lemmatize(word.lower())
                                    for word in email.split()
                                    if word.isalpha()
                                    and word not in all_names]))

cleaned_emails[0]

 

1. 모든 단어를 소문자로 변환
2. 단어별로 분리
3. All.names에 속하지 않은 단어를 cleaned emails에 추가

 

 

3-3. 불용어 제거와 단어의 출현 빈도 특징을 추출

 

from sklearn.feature_extraction.text import CountVectorizer
vectorizer = CountVectorizer(stop_words="english", max_features=500)
term_docs = vectorizer.fit_transform(cleaned_emails)
print(term_docs [0])

 

 # CountVectorizer : 단어 출현 빈도와 관련 돼 있는 직업을 수행하는 변환기

 

 

 

 

3-4. 훈련 데이터와 테스트 데이터의 분리 및 변환

 

from sklearn.model_selection import train_test_split
X_train, X_test,y_train,y_test = train_test_split(cleaned_emails,labels,
                                                  test_size=0.3, random_state=35)

 

  # text size를 30%로

 

term_docs_train = vectorizer.fit_transform(X_train)
term_docs_test = vectorizer.transform(X_test)

 

 

# fit transform,transform 으로 벡터 변환
# train 데이터는 모델 적합화를 하고 > fit.transform(X_train)
# test 데이터는 적합화를 하지 않음 > transform(X_test)

 

 

 

 

3-5. 모델의 성능 측정:정확도,AUC

 

from sklearn.naive_bayes import MultinomialNB
naive_bayes = MultinomialNB(alpha=1,fit_prior=True)
naive_bayes.fit(term_docs_train, y_train)

 

# vectorizer에 의해서 변환된 term docs train라고 하는 벡터 변환된 훈련 데이터를 사용

 

y_pred =naive_bayes.predict(term_docs_test)
y_pred[:5]
naive_bayes.score(term_docs_test,y_test)
0.9162371134020618

 

# 나이브 베이즈가 갖고 있는 score라는 메서드 > 정확도를 알려줌
# 정확도가 약91%!!

 

y_pred_proba = naive_bayes.predict_proba(term_docs_test)
y_pred_proba[:5]

 

# 결과에서
# '0'햄(스팸 메일이 아닌 것)일 확률 , '1'스팸일 확률
#          왼쪽 결과                      오른쪽 결과

 

array([[1.00000000e+00, 6.32034713e-15],
       [1.00000000e+00, 9.68768609e-59],
       [1.00000000e+00, 8.47218068e-14],
       [2.18182413e-01, 7.81817587e-01],
       [9.99999974e-01, 2.55698052e-08]])

 

 

from sklearn.metrics import roc_auc_score, roc_curve

 

import matplotlib.pyplot as plt

fpr, tpr, _ = roc_curve(y_test, y_pred_proba[:, 1]) # 열이 갖고 있는 의미 spam일 확률
auc = roc_auc_score(y_test,y_pred_proba[:,1])
plt.plot(fpr, tpr,"r-",label="MultinomialNB")
plt.plot([0,1],[0, 1], "b--", label="random guess")
plt.xlabel("false positive rate")
plt.ylabel("true positive rate")
plt.title("ROC Curve: AUC={:.5f}".format(auc))
plt.legend(loc="lower right");

 

# ROC Curve:AUC = 0.96039
# 분류를 잘 하고 있다!
# 나이브 베이즈는 범주형 데이터(스팸 부분)에서 강력한 성능 발휘

 

 

 

3-6. 모델의 성능 개선 

 

from sklearn.model_selection import GridSearchCV

parameters = {
    "alpha": [0.5, 1.0, 1.5, 2.0],
    "fit_prior": [True, False]
}

grid_search = GridSearchCV(naive_bayes, parameters, n_jobs=-1, cv=10, scoring="roc_auc")

grid_search.fit(term_docs_train, y_train)
grid_search.best_params_
{'alpha': 0.5, 'fit_prior': True}

 

naive_bayes_best = grid_search.best_estimator_
y_pred = naive_bayes_best.predict(term_docs_test)
y_pred

naive_bayes_best.score(term_docs_test, y_pred)

y_pred_proba = naive_bayes_best.predict_proba(term_docs_test)
y_pred_proba[:5]

 

fpr, tpr, _ = roc_curve(y_test, y_pred_proba[:,1])
auc = roc_auc_score(y_test, y_pred_proba[:, 1])
plt.plot(fpr, tpr, "r-", label="MultinomialNB")
plt.plot([0,1], [0, 1], "b--", label="randome guess")
plt.xlabel("false ositive rate")
plt.ylabel("true positive rate")
plt.title("ROC Curve: AUC={:.5f}".format(auc))
plt.legend(loc="lower right");

 

# 0.96039>0.96152 약간의 성능이 더 좋아졌다!!!

 

 

 

 

- 나이브 베이즈 분류기는 확률기반 분류기로 특징이 상호 독립적이라는 가정 하에 클래스 전체의 확률 분포 대비 특정 클래스에 속할 확률을 베이즈 정리를 기반으로 계산

- CountVectorizer를 사용해 불용어 제거와 단어의 출현 빈도 특징을 추출할 수 있음

- 벡터 변환 시 train 데이터에 대해서는 fit_transform( )을 사용

- test 데이터에 대해서는 transform( ) 메서드를 사용