IMDB 리뷰 분석 - 2

김원호·2023년 3월 10일
0

데이터를 수집했으니 감성 분석 모델링을 진행했다.
예전 프로젝트 진행 당시에 참고했던 위키독스에서
IMDB데이터를 사용해 감성분석을 진행한 챕터가 기억이 났다.

참고 문서 : https://wikidocs.net/24586

심지어 데이터도 keras에서 바로 다운받아 학습이 가능했다.

데이터 불러오기

import numpy as np
import matplotlib.pyplot as plt
from tensorflow.keras.datasets import imdb

(X_train, y_train), (X_test, y_test) = imdb.load_data()

print('훈련용 리뷰 개수 : {}'.format(len(X_train)))
print('테스트용 리뷰 개수 : {}'.format(len(X_test)))
num_classes = len(set(y_train))
print('카테고리 : {}'.format(num_classes))

실행결과 :

훈련용 리뷰 개수 : 25000
테스트용 리뷰 개수 : 25000
카테고리 : 2

케라스에서 제공하는 데이터는 총 25000개의 리뷰이고 감성분석이기 때문에 긍정(1)과 부정(2) 두 가지의 카테고리로 구성되어있다.

첫 번째 데이터를 출력하면

print('첫번째 훈련용 리뷰 :',X_train[0])
print('첫번째 훈련용 리뷰의 레이블 :',y_train[0])

실행결과 :

첫번째 훈련용 리뷰 : [1, 14, 22, 16, 43, 530, 973, 1622, 1385, 65, 458, 4468, 66, 3941, 4, 173, 36, 256, 5, 25, 100, 43, 838, 112, 50, 670, 22665, 9, 35, 480, 284, 5, 150, 4, 172, 112, 167, 21631, 336, 385, 39, 4, 172, 4536, 1111, 17, 546, 38, 13, 447, 4, 192, 50, 16, 6, 147, 2025, 19, 14, 22, 4, 1920, 4613, 469, 4, 22, 71, 87, 12, 16, 43, 530, 38, 76, 15, 13, 1247, 4, 22, 17, 515, 17, 12, 16, 626, 18, 19193, 5, 62, 386, 12, 8, 316, 8, 106, 5, 4, 2223, 5244, 16, 480, 66, 3785, 33, 4, 130, 12, 16, 38, 619, 5, 25, 124, 51, 36, 135, 48, 25, 1415, 33, 6, 22, 12, 215, 28, 77, 52, 5, 14, 407, 16, 82, 10311, 8, 4, 107, 117, 5952, 15, 256, 4, 31050, 7, 3766, 5, 723, 36, 71, 43, 530, 476, 26, 400, 317, 46, 7, 4, 12118, 1029, 13, 104, 88, 4, 381, 15, 297, 98, 32, 2071, 56, 26, 141, 6, 194, 7486, 18, 4, 226, 22, 21, 134, 476, 26, 480, 5, 144, 30, 5535, 18, 51, 36, 28, 224, 92, 25, 104, 4, 226, 65, 16, 38, 1334, 88, 12, 16, 283, 5, 16, 4472, 113, 103, 32, 15, 16, 5345, 19, 178, 32]
첫번째 훈련용 리뷰의 레이블 : 1

확인해보니 데이터가 토큰화와 정수 인코딩을 모두 끝낸 상태다. (엄청 편함,,,)
첫 번째 리뷰는 긍정 리뷰 였나보다.

이렇게 전처리가 끝난 단어들의 원래 단어를 알기 위해서는 다음과 같은 코드를 통해 index to word에 저장했다. value+3인 이유는 제공하는 데이터가 0~3을 특별 토큰으로 취급하고 있기 때문이다.

word_to_index = imdb.get_word_index()
index_to_word = {}
for key, value in word_to_index.items():
    index_to_word[value+3] = key```

GRU 모델링

단어 집합의 크기를 10000으로 제한하고, 최대 리뷰 길이를 500으로 제한해서 모델링 학습 속도를 높였다. 임베딩 벡터의 차원은 100이고, 은닉층의 크기는 128로 정했다.

import re
from tensorflow.keras.datasets import imdb
from tensorflow.keras.preprocessing.sequence import pad_sequences
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, GRU, Embedding
from tensorflow.keras.callbacks import EarlyStopping, ModelCheckpoint
from tensorflow.keras.models import load_model

vocab_size = 10000
max_len = 500

(X_train, y_train), (X_test, y_test) = imdb.load_data(num_words=vocab_size)

X_train = pad_sequences(X_train, maxlen=max_len)
X_test = pad_sequences(X_test, maxlen=max_len)

embedding_dim = 100
hidden_units = 128

model = Sequential()
model.add(Embedding(vocab_size, embedding_dim))
model.add(GRU(hidden_units))
model.add(Dense(1, activation='sigmoid'))

es = EarlyStopping(monitor='val_loss', mode='min', verbose=1, patience=4)
mc = ModelCheckpoint('GRU_model.h5', monitor='val_acc', mode='max', verbose=1, save_best_only=True)

model.compile(optimizer='rmsprop', loss='binary_crossentropy', metrics=['acc'])
history = model.fit(X_train, y_train, epochs=15, callbacks=[es, mc], batch_size=64, validation_split=0.2)

모델링을 완료한 후 모델을 저장하고, 그 모델을 불러내서 리뷰를 집어넣으면 긍/부정 스코어를 반환하는 함수를 정의했다. 입력받은 리뷰를 정수 인코딩과 패딩을 거쳐서 모델에 집어넣어서 긍/부정을 판다나는 점수를 반환하여주는 함수다.

loaded_model = load_model('GRU_model.h5')

def sentiment_predict(new_sentence):
  # 알파벳과 숫자를 제외하고 모두 제거 및 알파벳 소문자화
  new_sentence = re.sub('[^0-9a-zA-Z ]', '', new_sentence).lower()
  encoded = []

  # 띄어쓰기 단위 토큰화 후 정수 인코딩
  for word in new_sentence.split():
    try :
      # 단어 집합의 크기를 10,000으로 제한.
      if word_to_index[word] <= 10000:
        encoded.append(word_to_index[word]+3)
      else:
      # 10,000 이상의 숫자는 <unk> 토큰으로 변환.
        encoded.append(2)
    # 단어 집합에 없는 단어는 <unk> 토큰으로 변환.
    except KeyError:
      encoded.append(2)

  pad_sequence = pad_sequences([encoded], maxlen=max_len)
  score = float(loaded_model.predict(pad_sequence)) # 예측

  return score

반환된 score를 통해(10점 만점) 점수 리뷰로 환산하는 것이 목표였기 때문에 긍/부정 리뷰를 입력해보았다.

<부정 리뷰>
"Maybe i miss something but i wonder if there's a character you that represents a positive emotion or sympathy. All characters were parasites (and still you can't see a 'cow'! Well, if you think that Almodovar meets Stephen King in Korea that's ok. i want to know, if any one believes this is the best movie ever would he of she will watch the film for a second time? Well, me no! Better from the Old boy? No! Funny? No! So 4 stars only for gasp of classes.-"

score : 0.049

<긍정 리뷰>
Fairly well told story of struggles, hardships and fantasies of south korean lower middle class. Draws some pertinent inferences to cruel class differences capitalism creates and flourishes upon. Attention to details and proximity to reality remains comendable for the first couple of hours and then comes the disastrous climax. The irrational, abrupt, unjustified and plot defying actions of the characters in the dramatic last scene will surely spoil the mood. The climax will also reveal grave weaknesses of the plot which cant help leading to an inevitable low key ending.

score : 0.993

후기

실제로 부정리뷰는 4점 리뷰였고, 긍정 리뷰는 6점짜리 리뷰로, 긍정과 부정에 치우치지 않은 리뷰임에도 0과 가까운, 그리고 1과 가까운 score를 반환했다. 사실 당연한 결과인 것이, 내가 모델링 한 모델은 다중 분류 모델이 아니고 이진 분류 모델이기 때문이다. 개인적으로 치우칠 것으로 예상하기는 했지만, 그렇다고 하더라도 긍/부정에 대한 단어의 개수나 분포로 분석하기 때문에 score가 이렇게까지 치우칠 것이라고는 생각하지 못했다.

그렇기 때문에 생각해본 향후 개선 방안으로는
1. 부정 리뷰로는 1점 리뷰만, 긍정 리뷰로는 10점 리뷰만으로 0과 1로 이진분류하여 긍/부정을 학습한다면 4점, 6점과 같은 리뷰들을 좀더 soft한 분포로 만든다.
2. 가장 좋은 방법은 classification이 아닌 regression을 통해서 리뷰 점수 자체를 예측하는 모델을 만든다. (딥러닝 or 머신러닝)

0개의 댓글