머신러닝 기말고사 대체과제
많이 부족했지만 성적 잘 받은 보고서 !!
* 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( ) 메서드를 사용