본문 바로가기

Lecture AI/3장.신경망 시작하기

2. 신경망을 이용한 IMDB 영화 리뷰 분류: 이진 분류 예제

반응형

인터넷 영화 데이터베이스로부터 리뷰를 분류하는 내용에 대해 실습을 해보겠습니다. IMDB 데이터셋을 이용해 보겠습니다. 유사한 내용의 Posting은 아래와 같은니 참고바랍니다.

 

 

RNN 실전실습 : 시퀀스 모델링을 위한 다층 RNN구현 : IMDB 영화리뷰 구현

다음은 다대일(Many-to-Once)구조로 감성 분석을 위한 다층 RNN을 구현해 보겠습니다. 1.데이터준비 import pyprind import pandas as pd from string import punctuation import re import numpy as np df = pd.r..

nicola-ml.tistory.com

 

IMDB 데이터셋 로드하기                                                    

from keras.datasets import imdb

(train_data, train_labels), (test_data, test_labels) = imdb.load_data(num_words=1000)

num_words=10000 매개변수는 훈련 데이터에서 가장 자주 나타나는 단어 1만 개만 사용하겠다는 의미입니다. 드물게 나타나는 단어는 무시하겠습니다.

 

리뷰하나를 보는 예제입니다.

#word_index는 단어와 정수 인덱스를 매핑한 딕셔너입니다.
word_index = imdb.get_word_index()

#정수 인덱스와 단어를 매핑하도록 뒤집습니다.
reverse_word_index = dict([(value, key) for (key, value) in word_index.items()])

#리뷰를 디코딩합니다. 
decoded_review = ' '.join([reverse_word_index.get(i-3, '?') for i in train_data[0]])
decoded_review

 


데이터 준비

신경망에 숫자 리스트를 주입할 수는 없습니다. 리스트를 텐서로 바꾸는 두 가지 방법이 있습니다.

  • 같은 길이가 되도록 리스트에 패딩(padding)을 추가하고(samples, sequence_length)크기의 정수 텐서로 변환합니다. 그다음 이 정수 텐서를 다룰 수 있는 층을 신경망의 첫 번째층으로 사용합니다. (embedding층을 말합니다.)
  • 리스트를 원-핫 인코딩(one-hot encoding)하여 0과 1의 벡터로 변환합니다. 예를 들어 시퀀스 [3,5]를 인덱스 3과 5의 위치는 1이고 그 외는 모두 0인 10,000차원의 벡터로 각각 변환합니다. 그다음 부동 소수 벡터 데이터를 다룰 수 있는 Dense층을 신경망의 첫 번째 층으로 사용합니다.

여기서는 두번째 방식을 사용합니다.

 

정수 시퀀스를 이진 행렬로 인코딩합니다.                                                               

import numpy as np

def vectorize_sequences(sequences, dimension=10000):
    
    results = np.zeros((len(sequences), dimension))
    for i, sequence in enumerate(sequences):
        results[i, sequence] = 1.
        
    return results

x_train = vectorize_sequences(train_data)
y_test = vectorize_sequences(test_data)

print(train_data[0])
print(x_train)

 


신경망 모델 만들기

입력 데이터가 vector이고 label은 스칼라(1 또는 0)입니다. 아마 앞으로 볼 수 있는 문제 중에서 가장 간단한 것입니다. 이런 문제에 잘 작동하는 네트워크 종류는 relu활성화 함수를 사용한 완전 연결층을 그냥 쌓는 것입니다. (즉, Dense(16, activation='relu'))을 그냥 쌓은 것입니다.

 

Dense층에 전달한 매개변수(16)는 은닉 유닛(hidden unit)의 개수입니다. 하나의 은닉 유닛은 층이 나타내는 표현 공간에서 하나의 차원이 됩니다.

 

ouput = relu(dot(W, input) + b)

 

16개의 은닉 유닛이 있다는 것은 가중치 행렬 W 크기가 (input_dimention, 16)이라는 뜻입니다. 입력 데이터와 W를 점곱하면 입력 데이터가 16차원으로 표현된 공간으로 투영됩니다. (그리고 편향 벡터 b를 더하고 relu연산을 적용합니다.) 표현 공간의 차원을 '신경망이 내재된 표현을 학습할 때 가질 수 있는 자유도'로 이해할 수 있습니다.

 

은닉 유닛을 늘리면(표현 공간을 더 고차원으로 만들면) 신경망이 더욱 복잡한 표현을 학습할 수 있지만 계산 비용이 커지고 원하지 않는 패턴을 학습할 수도 있습니다.

 

중간에 있는 은닉층은 활성화 함수로 relu를 사용하고 마지막 층은 확률을 출력하기 위해 시그모이드 활성화 함수를 사용합니다. relu는 음수를 0으로 만드는 함수입니다. 시그모이드는 임의의 값을 [0,1] 사이로 압축하므로 출력 값을 확률처럼 해석할 수 있습니다.

 

모델 정의하기                                     

from keras import models
from keras import layers

model = models.Sequential()
model.add(layers.Dense(16, activation='relu', input_shape=(10000,)))
model.add(layers.Dense(16, activation='relu'))
model.add(layers.Dense(1, activation='sigmoid'))

 

손실 함수와 옵티마이저를 선택해야 합니다. 이진 분류 문제고 신경망의 출력이 확률이기 때문에(네트워크의 끝에 시그모이드 활성화 함수를 사용한 하나의 유닛으로 된 층을 놓았습니다.) binary_crossentropy 손실이 적합합니다. 이 함수가 유일한 선택은 아니고 mean_squared_error도 사용할 수 있습니다. 확률을 출력하는 모델을 사용할 때는 크로스엔트로피가 최선의 선택입니다.

 

 

모델 컴파일하기                         

from keras import optimizers

model.compile(optimizer='rmsprop', loss='binary_crossentropy', metrics=['accuracy'])

 

 


훈련 검증

훈련하는 동안 처음 본 데이터에 대한 모델의 정확도를 측정하기 위해서는 원본 훈련 데이터에서 10,000의 샘플을 떼어 검증 세트를 만들어야 합니다. 이제 모델을 512개의 미니 배치를 만들어 20번의 에포크 동안 훈련시킵니다. (x_train과 y_train 텐서에 있는 모든 샘플에 대해 20번 반복합니다.) 동시에 따로 뗴어 놓은 1만 개의 샘플에서 손실과 정확도를 측정할 것입니다. 이렇게 하려면 validation_data매개변수에 검증 데이터를 전달해야 합니다.

 

x_val = x_train[:10000]
partial_x_train =x_train[10000:]
y_val = y_train[:10000]
partial_y_train = y_train[10000:]

model.compile(optimizer='rmsprop', loss='binary_crossentropy', metrics=['accuracy'])
history = model.fit(partial_x_train, partial_y_train, epochs=20, batch_size=512, validation_data=(x_val, y_val))

 

model.fit()메서드는 History객체를 반환합니다. 이 객체는 훈련하는 동안 발생한 모든 정보를 담고 있는 딕셔너리인 history속성을 가지고 있습니다. 이 딕셔너리는 훈련과 검증하는 동안 모니터링할 측정 지표당 하나씩 모두 4개의 항목을담고 있습니다.

results = model.evaluate(x_test, y_test)
history_dict = history.history
history_dict.keys()

정확성은 85%를 나타냅니다.

782/782 [==============================] - 2s 3ms/step - loss: 0.3504 - accuracy: 0.8526 

 

 

 

훈련과 검증 손실 그리기                         

import matplotlib.pyplot as plt

history_dict = history.history
loss = history_dict['loss']
val_loss = history_dict['val_loss']

epochs = range(1, len(loss)+1)

plt.plot(epochs, loss, 'bo', label='Traing loss')
plt.plot(epochs, val_loss, 'b', label='Validation loss')
plt.title('Training and validation loss')
plt.xlabel('Epochs')
plt.ylabel('Loss')
plt.legend()
plt.show()

 

 

훈련과 검증 정확도 그리기                         

import matplotlib.pyplot as plt

history_dict = history.history
acc = history_dict['accuracy']
val_acc = history_dict['val_accuracy']

epochs = range(1, len(loss)+1)

plt.plot(epochs, acc, 'bo', label='Traing acc')
plt.plot(epochs, val_acc, 'b', label='Validation acc')
plt.title('Training and validation accuracy')
plt.xlabel('Epochs')
plt.ylabel('Accuracy')
plt.legend()
plt.show()

 

 

확인할 수 있듯이 훈련 손실이 에포크마다 감소하고 훈련 정확도는 에포크마다 증가합니다. 경사 하강법 최적화를 사용했을 때 반복마다 최소화되는 것이 손실이므로 기대했던 대로입니다. 

 

 


훈련된 모델로 새로운 데이터에 대해 예측하기

모델을 훈련시킨 후에 이를 실전 환경에서 사용하고 싶을 것입니다. predict 메소드를 사용해서 어떤 리뷰가 긍정일 확률을 예측할 수 있습니다.

model.predict(x_test)

 


정리

모델을 훈련시킨 후에 이를 실전 환경에서 사용하고 싶을 것입니다. predict 메소드를 사용해서 어떤 리뷰가 긍정일 확률을 예측할 수 있습니다.

  • 원본 데이터를 신경망에 텐서로 주입하기 위해서는 많은 전처리가 필요합니다. 단어 시퀀스는 이진 베터로 인코딩될 수 있고 다른 인코딩 방식도 있습니다.
  • relu활성화 함수와 함께 Dense층을 쌓는 네트워크는 하나의 유닛과 sigmoid활성화 함수를 가진 Dense층으로 끝나야 합니다. 이 신경망의 출력은 확률을 나타내는 0과 1사이의 스칼라 값입니다.
  • 이진 분류 문제에서 이런 스칼라 시그모이드 출력에 대해 사용할 손실 함수는 binary_crossentropy입니다.
  • rmsprop옵티마이저는 문제에 상관없이 일반적으로 충분히 좋은 선택입니다. 걱정할 거리가 하나 줄은 셈입니다.
  • 훈련 데이터에 대해 성능이 향상됨에 따라 신경망은 과대적합되기 시작하고 이전에 본적 없는 데이터에서는 결과가 점점 나빠지게 됩니다. 항상 훈련 세트 이외의 데이터에서 성능을 모니터링해야 합니다.
반응형