HyeM

6부_3장 베이스정리로 텍스트 분류하기 본문

Study/AI&DeepLearning

6부_3장 베이스정리로 텍스트 분류하기

Hailey_HyeM207 2020. 7. 25. 10:18

 

01. 텍스트 분류

텍스트 분류에는 여러 가지 방법이 있는데, 자주 사용되는 방법인 “베이지안 필터” 를 이용해 실습해본다.

베이지안 필터

- 스팸 메일이나 글을 구분할때 많이 사용됨.

- 교사학습/ 비교사학습/ 강화학습의 3가지 머신러닝 종류중 교사학습에 해당한다.

- 사용자가 따로 키워드를 지정하지 않아도 스팸 메일을 구분할 수 있음

 

 

02. 베이즈 정리

베이즈 정리는 '조건부 확률'과 관련된 이론으로, 토머스 베이즈에 의해 정립된 이론이다.

 

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

 

 조건부 확률

  <어떤 A라는 사건이 일어났다는 조건>에서 <다른 사건 B가 일어날 확률>을 나타냄. = P(B|A)

 

결합확률과 곱셈 법칙

예를 들어  정육면체 주사위를 두 번 던져서 "첫번째가 3", "두 번째가 짝수" 될 확률은

1/6 X 1/2 = 1/12 이다.

 => 동시에( 연속적으로) 2가지 사건이 발생할 때는 두 사건의 확률을 곱하면 된다. 

 

베이즈 정리

"B와 A의 결합 확률"은 "A와 B의 결합 확률" 과 같다는 점을 변형하면 베이즈 정리가 나오게 된다. 

B와 A의 결합 확률 = P(A|B) X P(B)

A와 B의 결합 확률 = P(A|B) X P(A)

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

 

 

03. 나이브 베이즈 분류

베이지안 필터는 나이브 베이즈 분류 알고리즘을 사용하는데, 나이브 베이즈 분류는 베이즈 정리를사용한 분류 방법이다.

 

베이즈 정리는 A라는 사건이 B에 속하는지 판단할 때 사용함.
Ex. 텍스트 분류 : A_텍스트  , B_카테고리 판정 결과
   이메일 스팸 필터 : A_받은 메일, B_ 스팸 메일 판정 결과

 

나이브 베이즈 분류는 어떤 문장을 카테고리 분류할 때, 텍스트 내부에서의 단어 출현 비율을 조사한다. 

그리고 이를 기반으로 해당 테스트를 어떤 카테고리로 분류하는 것이 적합한지 알아본다.

 

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

 (나이브 베이즈 분류 공식 )

 

P(A)는 입력텍스트A가 주어질 확률 ; A는 단어들의 집합=> (단어가 문서의 어떤 위치에 있는지는 고려하지 않음)

P(B)는 각 카테고리로 분류될 확률 (전체 문서에서 해당 카테고리의 문서가 나올 확률)

 

P(A|B) = P(a_a1 | B) P(_a2|B) P(_a3|B) ... P(a_xN |B))

입력텍스트 A를 각 단어(aN)의 집합

P(aN |B)는 단어가 카테고리에 속할 확률

 

<단순한 출현율>=<단어의 출현 횟수>/<카테고리 전체 단어 수>

 

 

 

04. 베이지안 필터 사용해보기

간단한 베이지안 필터를 파이썬 프로그램으로 구현해본다.

다음 코드에서 베이지안 필터를 구현한 클래스는 BayesianFilter이다.

import math, sys
from konlpy.tag import Okt

class BayesianFilter:
    """베이지안 필터"""
    def __init__(self):  #생성자
        self.words = set() #출현한 단어 기록
        self.word_dict ={} #카테고리마다의 단어 출현 횟수 기록; [카테고리][단어]=출현횟수
        self.category_dict ={} #카테고리 출현 횟수 기록

    #형태소 분석하기 ---(#1)
    def split(self, text):
        results =[]
        okt=Okt()  #Okt 형태소 분석기를 이용함. 
        #단어의 기본형 사용
        malist =okt.pos(text, norm=True, stem=True)
        for word in malist:
            #어미/조사/구두점 등은 대상에서 제외
            if not word[1] in ["Josa", "Eomi", "Punctuation"]:
                results.append(word[0]) #형태소를 results 배열에 저장하여,
        return results                  #return 한다. 


    #단어와 카테고리의 출현 횟수 세기 --(#2)
    
     #단어를 카테고리에 추가하기
    def inc_word(self, word, category):
        if not category in self.word_dict: #카테고리마다의 단어 출현 횟수 기록하는 (word_dict)에 카테고리가 없다면,
            self.word_dict[category]={}   #공간 만들어줌.
        if not word in self.word_dict[category]:   #카테고리 목록의 해당 카테고리에 word가 없다면, 
            self.word_dict[category][word] =0
        self.word_dict[category][word] +=1 #그리고, 해당 단어를 해당 카테고리에 추가+1 해준다. 
        self.words.add(word)

 	  #카테고리 계산하기
    def inc_category(self, category):
        if not category in self.category_dict: #카테고리 목록(category_dict)에 카테고리가 없다면,
            self.category_dict[category]=0 #공간 만들어줌.
        self.category_dict[category]+=1  #그리고, 해당 카테고리에 추가+1


    #텍스트 학습하기 --(#3)
    def fit(self,text, category):
        """텍스트 학습"""
        word_list = self.split(text)
        for word in word_list:
            self.inc_word(word, category)
        self.inc_category(category)
        

    #단어 리스트에 점수 매기기 --(#4)
    def score(self, words, category):
        score=math.log(self.category_prob(category))
        for word in words:
            score+=math.log(self.word_prob(word, category))
        return score

    #예측하기 --(#5)
    def predict(self, text):
        best_category = None
        max_score = -sys.maxsize
        words =self.split(text)
        score_list=[]
        for category in self.category_dict.keys():
            score= self.score(words, category)
            score_list.append((category, score))
            if score < max_score:
                max_score =score
                best_category=category
            return best_category, score_list


        #카테고리 내부의 단어 출현 횟수 구하기
    def get_word_count(self, word, category):
        if word in self.word_dict[category]:
            return self.word_dict[category][word]
        else:
            return 0

    #카테고리 계산
    def category_prob(self, category):
        sum_categories=sum(self.category_dict.values())
        category_v=self.category_dict[category]
        return category_v / sum_categories


    #카테고리 내부의 단어 출현 비율 계산 -- (#6)
    def word_prob(self, word, category):
        n=self.get_word_count(word, category)+1
        d= sum(self.word_dict[category].values()) + len(self.words)
        return n/d


        



        

#1. 형태소 분석 _ koNLPy의 Okt 형태소 분석 사용

#2. 단어를 카테고리에 추가하고, 카테고리 내부의 단어 출현 빈도를 구할 수 있게 출현 횟수를 셈

     inc_word() 메서드에서는 word_dict를 사용해 단어 출현 횟수를 세고, inc_category() 메서드에서는 category_dict를 사용해 카테고리의 출현 횟수를 센다.

#3. 텍스트를 학습하는 fit()메서드를 정의함 _ fit메서드에서는 텍스트를 형태소로 분할하고, 카테고리와 단어를 연결함.

#4. 단어 리스트를 주면 점수를 계산해주는 메서드

#5. 텍스트의 카테고리를 구분하는 메서드_ 텍스트가 주어졌을 때 카테고리 점수를 계산, 가장 점수가 높은 카테고리를 결과로 return

#6. 단어 출현률을 계산할 때 학습사전(word_dict)에 없는 단어가 나오면 카테고리의 확률이 0이 됨

 

*__init__ : 자바에서의 생성자를 파이썬에서는 __init__으로 표기한다. 

 

 

이제 베이지안 필터를 구현한 이 클래스를 가지고,

메일의 제목으로 스팸을 구분하는 상황을 가정해서, 메일의 제목을 기반으로 "광고", "중요"메일을 구분하는 예제이다. 

from bayes import BayesianFilter   #위에서 만든 BayesianFilter클래스를 import 해준다.
bf=BayesianFilter()   # 생성자를 호출하여, 객체 생성. 

#텍스트 학습 _ fit 함수를 이용
bf.fit("파격 세일 - 오늘까지만 30%할인", "광고")  
bf.fit("쿠폰 선물 & 무료 배송", "광고")
bf.fit("현데계 백화점 세일", "광고")
bf.fit("봄과 함께 찾아온 따듯한 신제품 소식","광고")
bf.fit("인기 제품 기간 한정 세일" , "광고")
bf.fit("오늘 일정 확인" ,"중요")
bf.fit("프로젝트 진행 상황 보고","중요")
bf.fit("계약 잘 부탁드립니다.","중요")
bf.fit("회의 일정이 등록되었습니다.","중요")
bf.fit("오늘 일정이 없습니다.","중요")

#예측
pre, scorelist =bf.predict("재고 정리 할인, 무료 배송")
print("결과 =", pre)
print(scorelist)

 

적당한 텍스트를 학습시키고,제대로 판정하는지 확인함.

 

 

[실행결과]

bayes_test.py를 실행한 결과

 

 

 

위 글은 [(파이썬을 이용한)머신러닝, 딥러닝 실전 개발 입문] 을 읽고 정리한 글입니다.

Comments