비버놀로지

[나이브 베이즈 분류] 네이버 영화평 감정분석 본문

인공지능 머신러닝

[나이브 베이즈 분류] 네이버 영화평 감정분석

KUNDUZ 2022. 11. 3. 09:33
728x90

네이버 영화평 감정분석

실제 영화 리뷰를 이용해 나이브 베이즈 분류기를 학습시키고, 입력 받은 영화평이 긍정 또는 부정적일 확률을 구하는 감정 분석(Sentiment Analysis)을 해보겠습니다.

데이터는 네이버 개발자 Lucy Park님의 Naver Sentiment Movie Corpus v1.0를 사용합니다. 네이버 영화의 140자 영화평을 모은 것으로 총 100,000개의 부정 리뷰, 100,000개의 긍정 리뷰로 구성되어 있습니다.

id, document, label은 각각 사용자 아이디(정수), 리뷰 본문(문자열), 긍정·부정(1·0)을 나타내며 탭(\t)으로 나누어져 있습니다.

id    document    label
9251303    와.. 연기가 진짜 개쩔구나.. 지루할거라고 생각했는데 몰입해서 봤다.. 그래 이런게 진짜 영화지    1
10067386    안개 자욱한 밤하늘에 떠 있는 초승달 같은 영화.    1
2190435    사랑을 해본사람이라면 처음부터 끝까지 웃을수 있는영화    1
 

실습

  1. 데이터 입력을 위해 read_data() 함수를 구현합니다. ratings.txt 파일의 데이터를 사용해야 합니다. read_data()는 두 개의 문자열이 담긴 리스트를 리턴합니다. 첫번째 문자열은 모든 부정 리뷰의 집합, 두번째 문자열은 긍정 리뷰의 집합입니다. 리뷰 사이는 공백으로 나뉘어 집니다. 긴 문자열을 만드는 방법에 따라 속도 차이가 많이 나니 주의하세요!
    • α 값은 0.1 을 사용합니다.
    • P(pos)=0.5
    • P(neg)=0.5 를 사용하겠습니다.
["유치하고 촌스러운 애니메이션 도대체 왜 찍었는지 의문이 가는 영화 ㅎ 튼튼한 각본을 살리지 못한 그때의 기술력을 원망할뿐",
"안개 자욱한 밤하늘에 떠 있는 초승달 같은 영화. 사랑을 해본사람이라면 처음부터 끝까지 웃을수 있는영화"]

2. create_BOW() 함수를 작성합니다. 실습4를 참고하세요.

3. calculate_doc_prob() 함수를 작성합니다. 이 함수는 training_sentence(문자열), testing_sentence(문자열), 그리고 alpha 값을 입력 받아, training_sentence로 학습된 모델에서 testing_sentence를 만들어 낼 로그 확률을 반환합니다. 학습된 모델에 없는 단어가 나올 경우, 확률은 alpha 전체 토큰의 개수로 합니다.

 

4. normalize_log_prob() 함수를 작성합니다. 이 함수는 입력 받은 두 개의 로그 확률값을 표준화하여 로그값이 아닌 실숫값으로 변환합니다. 부정적 문장들로 학습된 모델과 긍정적 문장들로 학습된 모델에서 이 함수를 이용해 training_sentence가 만들어질 확률값을 구합니다.

 

5.마지막으로 [부정적일 확률, 긍정적일 확률] 형태의 리스트를 변수 prob_pair에 저장합니다.

 

 

import io
import numpy
import matplotlib as mpl
mpl.use("Agg")
import matplotlib.pyplot as plt
import numpy as np
import re
import math
import elice_utils

special_chars_remover = re.compile("[^\w'|_]")
def remove_special_characters(sentence):
    return special_chars_remover.sub(' ', sentence)

def main():
    training_sentences = read_data()
    testing_sentence = "어설픈 연기들로 몰입이 전혀 안되네요"
    prob_pair = naive_bayes(training_sentences, testing_sentence)
    
    plot_title = testing_sentence
    if len(plot_title) > 50: plot_title = plot_title[:50] + "..."
    visualize_boxplot(plot_title,
                  list(prob_pair),
                  ['Negative', 'Positive'])

def naive_bayes(training_sentences, testing_sentence):
    log_prob_negative = calculate_doc_prob(training_sentences[0], testing_sentence, 0.1) + math.log(0.5)
    log_prob_positive = calculate_doc_prob(training_sentences[1], testing_sentence, 0.1) + math.log(0.5)
    prob_pair = normalize_log_prob(log_prob_negative, log_prob_positive)
    
    return prob_pair

def read_data():
    training_sentences = [[], []]
    
    '''
    숙제 1
    여기서 파일을 읽어 training_sentences에 저장합니다.
    '''
    with open("./ratings.txt",mode='r') as file:
        next(file)
        for f in file:
            arr = f.split('\t')
            if arr[-1] == '0\n':
                training_sentences[0].append(arr[1])
            elif arr[-1] == '1\n':
                training_sentences[1].append(arr[1])
    
    return [' '.join(training_sentences[0]), ' '.join(training_sentences[1])]

    
    return [' '.join(training_sentences[0]), ' '.join(training_sentences[1])]

def normalize_log_prob(prob1, prob2):
    
    '''
    숙제 4
    로그로 된 확률값을 표준화합니다.
    이 부분은 이미 작성되어 있습니다.
    '''
    
    maxprob = max(prob1, prob2)

    prob1 -= maxprob
    prob2 -= maxprob
    prob1 = math.exp(prob1)
    prob2 = math.exp(prob2)

    normalize_constant = 1.0 / float(prob1 + prob2)
    prob1 *= normalize_constant
    prob2 *= normalize_constant

    return (prob1, prob2)

def calculate_doc_prob(training_sentence, testing_sentence, alpha):
    logprob = 0

    training_model = create_BOW(training_sentence)
    testing_model = create_BOW(testing_sentence)

    '''
    숙제 3
    training_sentence로 만들어진 모델이,
    testing_sentence를 만들어 낼 **로그 확률** 을 구합니다.
    일반 숫자에서 로그값을 만들기 위해서는 math.log() 를 사용합니다.
    
    일반 숫자에서의 곱셈이 로그에서는 덧셈, 나눗셈은 뺄셈이 된다는 점에 유의하세요.
    예) 3 * 5 = 15
        log(3) + log(5) = log(15)
        
        5 / 2 = 2.5
        log(5) - log(2) = log(2.5)
    '''

    Sum = sum(training_model.values())
    for i in testing_model:

        if i in training_model:

            logprob+=math.log(training_model[i]/Sum)
        else:

            logprob+=math.log(alpha/Sum)
    return logprob

def create_BOW(sentence):
    bow = {}
    
    '''
    숙제 2
    이전 실습과 동일하게 bag of words를 만듭니다.
    '''
    sentence = sentence.lower()
    
    sentence = remove_special_characters(sentence)
    
    sentence = sentence.split()
    
    for s in sentence:
        if s in bow:
            bow[s] +=1
        else:
            bow[s] = 1

    return bow

'''
이 밑의 코드는 시각화를 위한 코드입니다.
궁금하다면 살펴보세요.
'''
def visualize_boxplot(title, values, labels):
    width = .35

    print(title)
    
    fig, ax = plt.subplots()
    ind = numpy.arange(len(values))
    rects = ax.bar(ind, values, width)
    ax.bar(ind, values, width=width)
    ax.set_xticks(ind + width/2)
    ax.set_xticklabels(labels)

    def autolabel(rects):
        # attach some text labels
        for rect in rects:
            height = rect.get_height()
            ax.text(rect.get_x()+rect.get_width()/2., height + 0.01, '%.2lf%%' % (height * 100), ha='center', va='bottom')

    autolabel(rects)

    plt.savefig("image.svg", format="svg")
    elice_utils.send_image("image.svg")

if __name__ == "__main__":
    main()

 

 

 

728x90
Comments