Loading [MathJax]/jax/output/CommonHTML/jax.js
article thumbnail image
Published 2022. 5. 29. 17:13

모두의 인공지능 기초 수학 Chapter 14 복습

 

 

1. # 베이지안 확률

 

B일 때 A일 확률

 

P(A|B)=A(B|A)P(A)P(B)

 

P(B)=P(BA)+P(BA)

 

P(A|B)=P(B|A)P(A)P(B|A)P(A)+P(B|A)P(A)

 

 

# 베이지안 이론

 

  • 스팸메일 필터

- 메일본문(입력 테스트)을 이용하여 메일이 스팸인지 구분하는 예시를 살펴보자

 

- 메일의 스팸 유무

P(|)=

P(|)=

 

 

 

* 나이브베이즈

 

      베이즈 모델 :

 

P(A|B)=P(B|A)P(A)P(B)

 

      이 조건부 확률을 이용 (B가 있을 때 A일 확률)

      A를 문서 카테고리, B를 문서라고 가정했을 때, 해당 문서가 있을 때, 해당 카테고리일 확률

      P(A1|B)와 P(A2|B)를 비교해서 확률이 더 높은 값을 분류하기 위함

     

      이런 비교 계산을 하기 위해 공통되는 P(B) 제거하면

     

P(A|B)=P(B|A)P(A)

 

      A 카테고리일 때, W1, W2, ..., Wn 단어가 있다고 가정하면

 

P(A|B)=P(W1,W2,...,Wn|C)P(C)

 

      >> Naive Bayes : 각각의 단어가 A 카테고리에 독립적이라는 나이브한 가정을 둠으로써 계산

 

P(C|D)=P(W1|C)P(W2|C)...P(Wn|c)P(C)=P(W|C)P(C)

 

 

 

- 나이브 베이즈를 이용하여 정리하면

P(|)=P(|)XP()P()

P(|)=P(|)XP()P()

 

   입력 텍스트가 주어졌을 때, P(정상메일|메일본문)이 P(스팸메일|메일본문)보다 크면 정상, 그렇지 않으면 스팸

   본문에 단어가 두 개 있다고 가정했을 때 단어를 W1, W2라고 하고,

   이 식을 간단히 하면

 

P(|)=P(W1|)XP(W2|)XP()

P(|)=P(W1|)XP(W2|)XP()

 

스팸메일 필터 예시

- 입력 테스트 "my free lottery" 있다고 하면

P(|)=P(my|)XP(free|)XP(lottery|)XP()

P(|)=P(my|)XP(free|)XP(lottery|)XP()

 

 

- P(정상메일) = P(스팸메일) = 총 메일여섯 개 중 세 개 = 0.5

  >> 두 식 확률 같으므로 생략 가능

$$P(정상메일 | 메일본문) = P(my | 정상메일) X P(free | 정상메일) X P(lottery | 정상메일) $$

P(|)=P(my|)XP(free|)XP(lottery|)

 

 

- P(my | 정상메일)을 구하는 방법

my

 

 

- 이런 원리로 "my free lottery"를 분류해보면

P(|)=010×210×010=0

$$P(스팸메일 | 메일본문) = \frac{1}{10} \times \frac{3}{10} \times \frac{3}{10} = 0.009$$

 

    >> "my free lottery" 는 스팸메일

 

# 라이브러리 호출
import numpy as np
import pandas as pd
from nltk.corpus import stopwords
import string
import nltk

# corpus : 말뭉치
# stopwords : 불용어

df = pd.read_csv('spam.csv')

 

>> 라이브러리 호출하고 csv 파일을 불러온다

 

>> from nltk.corpus import stopwords :

     데이터에서 유의미한 단어 토큰만 선별하기 위해서는 큰 의미가 없는 토큰을 제거하는 작업이 필요. 

     ' I, my, me, over, 조사, 접미사' 같은 단어들은 실제 의미 분석하는데 거의 기여X

     >> 이런 단어들을 불용어(stopword)

 

 

 

def process_text(text):
    # text에서 구두점(punctuation) 삭제
    nopunc = [char for char in text if char not in string.punctuation]
    nopunc = ''.join(nopunc)
    
    # text에서 불용어(접미사, 조사 등) 삭제
    cleaned_words = [word for word in nopunc.split()
    if word.lower() not in stopwords.words('english')]
    
    return cleaned_words

>> process_text를 정의하여 데이터 프레임 'df'에 있는 'text'를 토큰화 시키고자 함

 

>> 1. text에서 구두점(punctuation) 삭제

     string.punctuaion : 영어로 구두점으로 간주되는 모든 문자를 포함

     이 목록을 제외함으로써 문자열에서 모든 구두점을 제외 할 수 있음

 

     text에서 string.punctuation이 아닌 char만 char에 넣고 nopunc라고 해줘

     join() 함수를 이용해서 nopunc를 정제해서 nopuc에 저장해줘

 

>> 2. text에서 불용어 삭제

     stopwords.words('english') : 영어사전에서 제공하는 영어의 불용어 리스트

     이 리스트를 제외하고 소문자로 만들어 리스트에 넣음 

 

     punctuation이 없는 정제된 nopunc에서 word를 불러오는데 불용어(stopwords)가 없는 것만 뽑아서 word에 넣어줘 대신

     lower(소문자)로 만들어서 리스트에 저장해줘. 

     process_text에 text에 넣고 반환

 

  

 

 

df['text'].apply(process_text)

>> 정의한 process_text의 text 결과

     punctuation과 불용어가 삭제됨

 

 

 

# text를 토큰화한 뒤, 토큰 수의 행렬 변환

from sklearn.feature_extraction.text import CountVectorizer
messages_bow = CountVectorizer(analyzer = process_text).fit_transform(df['text'])

 

>> Scikit-Learn의 서브 패키지 feature_extraction에서 CountVectorizer 클래스 호출 

     문서 집합에서 단어 토큰을 생성하고 각 단어의 수를 세어 BOW 인코딩 벡터를 만드는 클래스

     (BOW 인코딩 : 문서를 숫자 벡터로 변환하는 방법)

 

    1. 문서를 토큰 리스트로 변환

    2. 각 문서에서 토큰의 출현 빈도를 셈

    3. 각 문서를 BOW 인코딩 벡터로 변환 

 

    analyzer 인수로 사용할 토큰 생성기를 'process_text'로 함

    fit_transform() 은 fit()과 transform()을 한번에 처리할 수 있게 하는 메서드

    fit()은 데이터를 학습시키는 메서드이고, transform()은 실제로 학습시킨 것을 적용하는 메서드

 

    CountVectorizer 클래스를 이용해 process_text로 단어 토큰을 생성하고 각 단어의 수를 셈

    이때, 학습시키고 적용할 문서는 df['text']

    단어의 수를 행렬로 변환시켜서 messages_bow라고 함

 

 

# train data, test data 8:2 비율로 구분

from sklearn.model_selection import train_test_split 
X_train, X_test, y_train, y_test = train_test_split(messages_bow, df['label_num'], test_size = 0.2, random_state = 42)
# test_size는 0.2 train size는 자동적으로 0.8
# 랜덤하게 섞은것 중에 여러 세트가 있는데 42번으로 했을 떄 제일 잘 나옴
# 랜덤하게 섞은 데이터로 다른 알고리즘으로 적용해보고 싶을 때 random_state를 꼭 해줘야함

>> Scikit-Learn의 서브 패키지 model_selection에서 train_test_split 클래스 호출 

 

>> train / test를 분리하는 이유는 머신러닝 모델에 train 데이터를 100% 학습시킨 후 test데이터에 모델을 적용했을 떄 성능이 생각보다 않나오는 경우가 많음 >> overfitting

     모델이 내가 가진 학습 데이터에 너무 과적합되도록 학습한 나머지, 이를 조금이라도 벗어난 케이스는 예측률이 현저히 떨어지기 때문

     >> overfitting을 방지하기 위해 validation set으로 모델 평가

 

>> test_size를 0.2, train_size를 0.8로 지정

     random_state : 세트를 섞을 때 해당 int 값을 보고 섞는데, 이 값을 고정해야 매번 데이터 셋이 변경되지 않음

 

>> x_train : train data set의 feature

     x_test : test data set의 feature

     y_train : train data set의 label

     y_test : test data set의 label

 

     messages_bow를 feature 부분으로, df['label_num']을 label 부분으로 하여 test와 train 비율을 2:8로 하여 모델 학습

 

     (label_num = spam mail 이면 1, 정상메일이면 0)

 

 

# 나이브 베이즈 모델 적용, 훈련
# 훈련(train)>> 모델 생성

from sklearn.naive_bayes import MultinomialNB
classifier = MultinomialNB()
classifier.fit(X_train, y_train) # 훈련시, 반드시 train data 사용

>>  Scikit-Learn의 서브 패키지 naive_bayes에서 MultinomialNB 클래스 호출

 

>> MultinomialNB 모델을 classifier에 저장하고 X_train, y_train 데이터를 학습

 

 

 

# 훈련용 데이터로 예측해서 비교해 봄
classifier.predict(X_train) # 예측값

y_train.values  # 실제값

>> predict를 사용하면 0과 1만 반환하는데 0은 정상메일 1은 스팸메일이다.

 

 

 

 

- 학습 데이터셋에서 모델의 정확도를 표현해보자

# 학습 데이터셋에서 모델의 정확도 표현하기

from sklearn.metrics import classification_report
from sklearn.metrics import confusion_matrix, accuracy_score

pred = classifier.predict(X_train)

# metrics pkg >> prescision(정밀도), recall(재현율), f1 score(f1 스코어) 점수 구하기
print(classification_report(y_train, pred))

>> Scikit-Learn의 matrics 패키지에서 classification_report를 보면

     y_train(실제값)과 classifier.predict(X_train) (예측값)을 비교했을 때 정밀도, 재현율, f1 스코어 모두 1로 높은 값으로 나왔다.

 

 

# 혼동행렬(confusion matrix)

print('confusion Matrix: \n', confusion_matrix(y_train, pred))
print()
print('Accuracy: ', accuracy_score(y_train, pred))

>> 혼동행렬

      

실제\예측     N    P

       N         TN   FN

       P         FN    TP

* 정상메일 = 0, 스팸메일 = 1

TN = True Negative, 정상메일을 정상메일으로 예측 (정답) 3

FP = False Positive, 정상메일이 아닌 것을 정상메일이라고 예측 0

FN = False Negative, 정상메일을 정상메일이 아니라고 예측 0

TP = True Positive, 정상메일이 아닌 것을 정상메일이 아니라고 예측(정답) 1

 

 

>> 1) precision = 정밀도

Positive 라고 예측한 샘플(TP + FP) 중 실제로 Positive인 샘플(TP) 비율

'예측값' 기준 '정답인 예측값'

 

precision=TPTP+FP

 

>> 2) Recall = 재현율

실제 Positive인 샘플(TP+FN) 중 Positive라고 예측한 샘플(TP) 비율

'실제값' 기준 '정답인 예측값'

 

Recall=TPTP+FN

 

>> 3) Accuracy = 정확도

전체 샘플 중 맞게 예측한 샘플의 비율

 

Accuracy=TP+TNTN+TP+FN+FP

 

 

>> 4) F1-score

F-score 란 precision과 recall의 가중 조화평균

 

Fβ=(1+β2)(precision×recall)β2precision+recall

 

이때 가중치를 1인 경우를 F1-score

 

F1=(1+12)(precision×recall)12precision+recall

>>

 

F1=(2)(precision×recall)precision+recall

 

>> 5) support = 각 라벨의 실제 샘플 개수

4개의 샘플 중 라벨이 0 (정상메일)인 샘플은 3개

 

 

+ 민감도(Sensitivity)

Sensitivity=TruePositiveTruePositives+FalseNegatives

 

+ 특이도 (Specificity)

Specificity=TrueNegativesTrueNegatives+FalsePositives

 

 

>> 6) macro avg : 단순 평균

 

>> 7) weight avg : 가중 평균

 

 

 

- test data에 적용

# test data에 적용 (모델의 성능)
classifier.predict(X_test)  # 예측값

print('Actual value: ',y_test.values) # 실제값

>> train 데이터와 달라짐

 

from sklearn.metrics import classification_report
from sklearn.metrics import confusion_matrix, accuracy_score

pred = classifier.predict(X_test)
print(classification_report(y_test, pred))

print(confusion_matrix(y_test,pred))

accuracy_score(y_test, pred)

>> 실제 test 데이터에 적용했을 때는 precision, recall, f1-score 모두 0으로 나옴

 

 

복사했습니다!