[공부 자료 : Must Have 데싸노트의 실전에서 통하는 머신러닝]
# 나이브 베이즈
- 나이브 베이즈 알고리즘
나이브 베이즈 : 베이즈 정리를 적용한 조건부 확률 기반의 분류(classifier) 모델
조건부 확률 : A가 일어났을 때, B가 일어날 확률 (ex. '무료'라는 단어가 들어있을 때, 스팸 메일일 확률)
나이브 베이즈는 스팸 필터링을 위한 대표적 모델
조건부 확률을 기반으로 한 알고리즘으로, 사전확률과 사후확률을 활용
최근에는 딥러닝이 대안으로 등장하여 나이브 베이즈 모델을 사용하는 경우가 많이 감소
하지만 여전히 스팸 메일 필터링처럼 자연어 처리가 목적일 때는 나이브 베이즈 모델이 유용
딥러닝 - 자연어 처리에 탁월
나이브 베이즈 - 딥러닝보다 간단한 방법으로 자연어 처리
특징
범용성이 높지는 않지만, 독립변수들이 모두 독립적일 경우 충분한 경쟁력을 갖춘 알고리즘
딥러닝을 제외하면 자연어 처리(NLP)에 가장 적합한 알고리즘
각 독립변수들이 모두 독립적이고, 중요도가 비슷한 데이터에 적용하기 좋다
범주 형태의 변수가 많을 때 적용하기 좋다
숫자형 변수가 많을 때는 적합하지 않다
장점
비교적 간단한 알고리즘이며, 속도가 빠르다
작은 훈련set으로도 예측을 잘 한다
단점
모든 독립변수가 각각 독립적임을 전제로 한다 (실제로 이러한 경우가 많지는 않다)
- 베이즈 정리
사후확률 : 사건 A와 B가 있을 때, 사건 A가 발생한 상황에서 사건 B가 발생할 확률
사전확률 : 사건 A와 상관 없이 사건 B가 발생할 확률
P(A) : 스팸 메일일 확률 (전체 메일 중 스팸 메일의 비율) → 우리의 목표
P(B) : 전체 메일 중 B라는 단어가 들어있을 확률
P(A|B) : B가 발생했을 때, A가 발생할 확률
메일에 B라는 단어가 들어있을 때, 해당 메일이 스팸 메일일 확률
P(B|A) : A가 발생했을 때, B가 발생할 확률
메일이 스팸 메일일 경우, B라는 단어가 들어있을 확률
우도 (likelihood) 또는 기능도
전체 메일 100건에 대하여
1) P(A) = 30 / 100 = 0.3 → 스팸 메일인 경우
2) P(B) = 22 / 100 = 0.22 → '무료' 단어를 포함한 메일인 경우
3) P(B|A) = 15 / 30 = 0.5 → 스팸 메일 중, '무료' 단어를 포함한 경우
4) P(A|B) = 15 / 22 = 0.6818 → '무료' 단어를 포함한 메일 중, 스팸 메일인 경우
# 나이브 베이즈 - 스팸 메일 판별하기
- 1. 데이터 불러오기
import pandas as pd
import numpy as np
file_url = 'https://media.githubusercontent.com/media/musthave-ML10/data_source/main/spam.csv'
data = pd.read_csv(file_url)
2. 데이터 분석하기
target (종속변수) : 스팸인지 아닌지 정보
text (독립변수) : 스팸 여부를 판별하는데 사용한 문자열
data['target'].unique()
# array(['ham', 'spam'], dtype=object)
종속변수가 ham (스팸이 아님) 과 spam (스팸) 으로 이루어진 이진 분류
- 3-1. 전처리 : 특수기호 제거하기
자연어를 다룰 때 특수기호(쉼표, 마침표 등)은 노이즈로 작용하기 때문에 제거해야 한다
# 특수기호 목록 가져오기
import string
string.punctuation # 특수기호 목록 출력
# !"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~
특수기호 처리 방법
1. 문자열에서 문자를 하나씩 꺼낸다
2. 꺼낸 문자가 특수기호인지 판단한다
3. 특수기호가 아닌 문자만 리스트에 저장한다
4. 리스트에 저장된 문자를 다시 문자열로 합친다
5. 문자열을 문장별로 data의 행에 저장한다
# 문자열 하나 가져오기
sample_string = data['text'].loc[0] # 0번 row의 text에 해당하는 문자열 가져오기
print(sample_string)
# Go until jurong point, crazy.. Available only in bugis n great world la e buffet... Cine there got amore wat...
# 문자열의 문자를 하나씩 뽑아서 특수기호인지 확인하고 리스트에 저장하기
new_string = []
for i in sample_string:
if i not in string.punctuation:
new_string.append(i)
print(new_string)
# ['G', 'o', ' ', 'u', 'n', 't', 'i', 'l', ' ', 'j', 'u', 'r', 'o', 'n', 'g', ' ', 'p', 'o', 'i', 'n', 't', ' ', 'c', 'r', 'a', 'z', 'y', ' ', 'A', 'v', 'a', 'i', 'l', 'a', 'b', 'l', 'e', ' ', 'o', 'n', 'l', 'y', ' ', 'i', 'n', ' ', 'b', 'u', 'g', 'i', 's', ' ', 'n', ' ', 'g', 'r', 'e', 'a', 't', ' ', 'w', 'o', 'r', 'l', 'd', ' ', 'l', 'a', ' ', 'e', ' ', 'b', 'u', 'f', 'f', 'e', 't', ' ', 'C', 'i', 'n', 'e', ' ', 't', 'h', 'e', 'r', 'e', ' ', 'g', 'o', 't', ' ', 'a', 'm', 'o', 'r', 'e', ' ', 'w', 'a', 't']
# list를 문자열로 변환
new_string = ''.join(new_string)
함수로 만들기
def remove_punc(x):
new_string = []
for i in x:
if i not in string.punctuation:
new_string.append(i)
new_string = ''.join(new_string)
return new_string
remove_punc(sample_string)
# Go until jurong point crazy Available only in bugis n great world la e buffet Cine there got amore wat
DataFrame에 함수 적용하기
series.apply(func) : DataFrame Series(변수 한 줄)에 함수를 적용할 때 row 별로 끊어서 적용
data['text'].apply(remove_punc)
# 0 Go until jurong point crazy Available only in ...
# 1 Ok lar Joking wif u oni
# ...
# 데이터셋 업데이트
data['text'] = data['text'].apply(remove_punc)
- 3-2. 전처리 : 불용어 제거하기
불용어(stopword) : 자연어 처리에서 큰 도움이 안되는 단어
목적에 맞게 불용어를 직접 작성하기
제공되는 불용어 목록을 사용하기
import nltk # nltk 라이브러리
nltk.download('stopwords') # 불용어 목록 다운로드
from nltk.corpus import stopwords # 불용어 목록 import
stopwords.words('english') # 불용어 목록 중 영어 불용어
# nltk는 한국어 불용어는 제공하지 않는다
# 한국어 불용어 : www.ranks.nl 등에서 다운로드
nltk가 불용어를 제공하는 언어 목록 확인
from nltk.corpus import stopwords
print(stopwords.fileids()) # 29개국
불용어는 단어 단위이기 때문에 문장을 불러와서 공백을 기준으로 나눠준다
sample_string = data['text'].loc[0]
sample_string.split() # 띄어쓰기를 기준으로 나누기
for i in sample_string.split(): # 띄어쓰기를 기준으로 나누기
if i.lower() not in stopwords.words('english'): # stopwords는 소문자로만 이루어져 있다
print(i)
불용어를 없애서 반환해주는 함수 만들기
def stop_words(x):
new_string = []
for i in x.split():
if i.lower() not in stopwords.words('english'):
new_string.append(i.lower())
new_string = ' '.join(new_string)
return new_string
data['text'] = data['text'].apply(stop_words)
data['text']
# 0 go jurong point crazy available bugis n great ...
# 1 ok lar joking wif u oni
# 2 free entry 2 wkly comp win fa cup final tkts 2...
# ...
- 3-3. 전처리 : 카운트 기반으로 벡터화하기
카운트 기반 벡터화 : 문자를 개수 기반으로 벡터화
1. 데이터에 있는 모든 단어를 확인해 각각 col으로 만든다
2. 각 문장(row)마다 출현한 단어의 수(n)를 확인하여, 표를 채운다
3. 각 단어(col 명)에 index를 부여
4. 각 행마다 해당 index가 몇 번 나왔는지 기록 : (row, index) n
5. 출현하지 않은 단어는 포함하지 않는다
CountVectorizer( ) : 주어진 텍스트를 학습(fit( )) 하여 텍스트를 변환(transform( ))
from sklearn.feature_extraction.text import CountVectorizer
x = data['text'] # series : 소문자
cv = CountVectorizer() # 객체 생성
cv.fit(x) # 학습
cv.vocabulary_ # 단어와 인덱스 출력
# {'go': 3791,
# 'jurong': 4687,
# 'point': 6433,
# ...
x = cv.transform(x)
print(x) # (row, index) n
# (0, 1181) 1
# (0, 1414) 1
# (0, 1879) 1
# ...
검증해보기
data.loc[0]['text']
# go jurong point crazy available bugis n great world la e buffet cine got amore wat
print(cv.vocabulary_['go'])
# 0번 row에 나온 문장의 'go'의 index : 3791
print(x)
# ...
# (0, 3791) 1 : 0번 row의 3791 index에 해당하는 단어는 1번 나온다
# ...
- 3-4. 전처리 : 목표 col (종속변수) 형태 변경하기
목표 col의 성분이 문자(spam과 ham) : 가급적 숫자 형태로 변경하는게 좋다
map( ) : dictionary 타입의 데이터를 사용하여 매칭되는 값을 불러오도록 한다
data['target'] = data['target'].map({'spam':1, 'ham':0}) # 우리가 알고 싶은 것을 1로 한다
# spam -> 1
# ham -> 0
data['target']
y = data['target']
y
- 4. 모델링
Naive Bayes 알고리즘의 종류 (데이터 분포의 특성이 명확하지 않을 경우, 전부 다 사용해 보는 것을 추천)
- MultnomialNB 모듈 : 다항 분포에 대한 Naive Bayes 알고리즘
- GausianNB 모듈 : 정규 분포에 대한 Naive Bayes 알고리즘
- BernoulliNB 모듈 : 베르누이 분포에 대한 Naive Bayes 알고리즘
from sklearn.model_selection import train_test_split
x_train, x_test, y_train, y_test = train_test_split(x, y, test_size = 0.2, random_state = 100)
from sklearn.naive_bayes import MultinomialNB
nb = MultinomialNB()
nb.fit(x_train, y_train)
- 5. 예측
pred = nb.predict(x_test)
pred
- 6. 평가
accuracy_score(실제값, 예측값) : 정확도
confusion_matrix(실제값, 예측값) : 실제값과 예측값을 비교하여 매트릭스 형태로 출력 (오차 행렬, 혼동 행렬)
row : 실제값 0, 실제값 1
col : 예측값 0, 예측값 1
from sklearn.metrics import accuracy_score, confusion_matrix
print(accuracy_score(y_test, pred))
# 0.9856502242152466
print(confusion_matrix(y_test, pred))
# 예측값 0 예측값 1
# 실제값 0 [[965 12]
# 실제값 1 [ 4 134]]
confusion matrix를 heat map으로 시각화 하기
import matplotlib.pyplot as plt
import seaborn as sns
sns.heatmap(confusion_matrix(y_test, pred), annot = True, fmt = '.0f')
# fmt : heatmap 안의 숫자가 표시되는 형식
# - .0f : 소수점 없이
# - .2f : 소수점 둘째 자리까지
accuracy_score( )와 confusion_maxtix( )의 관계
confusion_matrix( )가 보여주는 정보
실제값 0 - 예측값 0 : TN (true negative), 음성을 음성으로 판단 (Correct)
실제값 0 - 예측값 1 : FP (false positive), 음성을 양성으로 판단 (Type 1 Error)
실제값 1 - 예측값 0 : FN (false negatie), 양성을 음성으로 판단 (Type 2 Error)
실제값 1 - 예측값 1 : TP (true positive), 양성을 양성으로 판단 (Correct)
1종 오류와 2종 오류 : 상황에 따라 중요도가 달라진다
양성 (positive, 1) : 우리가 찾고자 하는 것 (spam, 암)
암 진단에서 1종 오류와 2종 오류 : 암 (1)
암 진단에서 1종 오류 : 실제는 0 (암이 아님) 이지만 예측은 1 (암) → 큰 문제가 되지 않는다
암 진단에서 2종 오류 : 실제는 1 (암) 이지만 예측은 0 (암이 아님) → 큰 문제가 된다 (암인데 정상이라고 예측)
스팸 분석에서 1종 오류와 2종 오류 : 스팸 (1)
스팸 분석에서 1종 오류 : 실제는 0 (스팸이 아님) 이지만 예측은 1 (스팸) → 문제가 될 수 있다 (스팸이 아닌데 삭제)
스팸 분석에서 2종 오류 : 실제는 1 (스팸) 이지만 예측은 0 (스팸이 아님) → 삭제하지 않으므로 문제 없다
'머신러닝' 카테고리의 다른 글
09. 지도 (분류, 회귀) - 랜덤 포레스트 : 중고차 가격 예상하기 (0) | 2023.02.04 |
---|---|
08. 지도학습 (분류, 회귀) - 결정 트리 : 연봉 수준 예측하기 (0) | 2023.01.25 |
06. 지도 학습 (분류, 회귀) - K-최근접 이웃 (KNN) : 와인 등급 예측하기 (0) | 2023.01.22 |
05. 지도 학습 (분류) - 로지스틱 회귀 : 타이타닉 생존자 예측하기 (0) | 2023.01.21 |
04. 지도 학습 (회귀) - 선형회귀 : 보험료 예측하기 (0) | 2023.01.21 |