[혼공머신] 순환 신경망 : LSTM, GRU 셀

강민우·2022년 2월 23일
0
post-thumbnail

[혼자 공부하는 머신러닝+딥러닝] 책에 기반한 정리글입니다.
전체 소스코드는 아래 Github 링크에서 확인할 수 있습니다.

Github 링크

0. 개요

일반적으로 기본 순환층은 긴 시퀸스를 학습하기 어렵다. 멀리 떨어져 있는 단어 정보를 인식하기 어려울 수 있는데, 이는 시퀸스가 길수록 순환되는 은닉 상태에 담긴 정보가 점차 희석되기 때문이다. 이를 위해 LSTM과 GRU 셀이 발명되었다.

이번 편에서는 고급 순환층인 LSTM과 GRU 셀을 이용한 다양한 순환 신경망을 만들어 본다.

1. 훈련 데이터 준비, 패딩하기

먼저 훈련에 필요한 데이터를 준비하고, 훈련 세트와 검증세트로 나눈다.

from tensorflow.keras.datasets import imdb
from sklearn.model_selection import train_test_split

(train_input, train_target), (test_input, test_target) = imdb.load_data(num_words=500)
train_input, val_input, train_target, val_target = train_test_split(train_input, train_target, test_size=0.2, random_state=42)

케라스의 pad_sequences() 함수로 각 샘플의 길이를 100으로 맞춘다.

from tensorflow.keras.preprocessing.sequence import pad_sequences

train_seq = pad_sequences(train_input, maxlen = 100)
val_seq = pad_sequences(val_input, maxlen=100)

2. LSTM

LSTM(Long Short-Term Memory)는 단기 기억을 오래 기억하기 위해 고안되었다. 입력과 가중치를 곱하고 절편을 더해 활성화 함수를 통과시키는 구조를 여러 개 가지고 있고, 이런 계산 결과는 다음 타임스텝에 재사용된다.

2-1. LSTM 셀을 사용하여 순환층 만들기

이전 편에서의 SimpleRNN 대신 LSTM을 사용한다.

from tensorflow import keras

model = keras.Sequential()
model.add(keras.layers.Embedding(500, 16, input_length=100))
model.add(keras.layers.LSTM(8))
model.add(keras.layers.Dense(1,activation='sigmoid'))

RMSprop의 학습률을 0.0001로 조정하고 배치 크기는 64개, 에포크 횟수는 100으로 지정한다. 체크포인트와 조기 종료도 포함한다.

rmsprop = keras.optimizers.RMSprop(learning_rate = 1e-4)
model.compile(optimizer=rmsprop, loss='binary_crossentropy', metrics=['accuracy'])

checkpoint_cb = keras.callbacks.ModelCheckpoint('best-lstm-model.h5')
early_stopping_cb = keras.callbacks.EarlyStopping(patience=3, restore_best_weights=True)

history = model.fit(train_seq, train_target, epochs=100, batch_size=64, validation_data=[val_seq, val_target], callbacks=[checkpoint_cb, early_stopping_cb])

2-2. 훈련 손실과 검증 손실 그래프 그리기

import matplotlib.pyplot as plt
plt.plot(history.history['loss'])
plt.plot(history.history['val_loss'])
plt.xlabel('epoch')
plt.ylabel('loss')
plt.legend(['train', 'val'])
plt.show()

그래프를 보면 LSTM이 과대적합을 억제하면서 훈련을 잘 수행한 것을 알 수 있다.

2-3. 순환층에 드롭아웃 적용하기

순환층은 자체적으로 드롭아웃 기능을 제공한다. SimpleRNNLSTM 클래스 모두 dropout 매개변수와 recurrent_dropout 매개변수를 가진다.

dropout 매개변수는 셀의 입력에 드롭아웃을 적용하고, recurrent_dropout는 순환되는 은닉 상태에 드롭아웃을 적용한다. 그러나 recurrent_dropout은 GPU를 사용하여 모델을 훈련하지 못한다.

여기서는 dropout을 0.3으로 지정한다.

model2 = keras.Sequential()
model2.add(keras.layers.Embedding(500, 16, input_length=100))
model2.add(keras.layers.LSTM(8, dropout=0.3))
model2.add(keras.layers.Dense(1, activation='sigmoid'))

rmsprop = keras.optimizers.RMSprop(learning_rate = 1e-4)
model2.compile(optimizer=rmsprop, loss='binary_crossentropy', metrics=['accuracy'])

checkpoint_cb = keras.callbacks.ModelCheckpoint('best-drop-model.h5')
early_stopping_cb = keras.callbacks.EarlyStopping(patience=3, restore_best_weights=True)

history = model2.fit(train_seq, train_target, epochs=100, batch_size=64, validation_data=[val_seq, val_target], callbacks=[checkpoint_cb, early_stopping_cb])

훈련과 검증 손실을 그래프로 그려본다.

plt.plot(history.history['loss'])
plt.plot(history.history['val_loss'])
plt.xlabel('epoch')
plt.ylabel('loss')
plt.legend(['train', 'val'])
plt.show()


훈련 손실과 검증 손실간의 차이가 좁혀진 것을 확인할 수 있다.

2-4. 2개의 층 연결하기

순환층이 은닉 상태는 샘플의 마지막 타임스텝에 대한 은닉상태만 다음 층으로 전달한다. 그러나 순환층을 쌓게되면 모든 순환층에 순차 데이터가 필요하다.

케라스의 순환층에서 모든 타임스텝의 은닉상태를 출력하려면 마지막을 제외한 다른 모든 순환층에서 return_sequences 매개변수를 True로 지정한다.

model3 = keras.Sequential()
model3.add(keras.layers.Embedding(500, 16, input_length=100))
model3.add(keras.layers.LSTM(8, dropout=0.3, return_sequences=True))
model3.add(keras.layers.LSTM(8, dropout=0.3))
model3.add(keras.layers.Dense(1, activation='sigmoid'))

2개의 LSTM 층을 쌓았고, 드롭아웃을 0.3으로 지정했다.
이후 모델을 훈련시킨다.

rmsprop = keras.optimizers.RMSprop(learning_rate = 1e-4)
model3.compile(optimizer=rmsprop, loss='binary_crossentropy', metrics=['accuracy'])

checkpoint_cb = keras.callbacks.ModelCheckpoint('best-2rnn-model.h5')
early_stopping_cb = keras.callbacks.EarlyStopping(patience=3, restore_best_weights=True)

history = model3.fit(train_seq, train_target, epochs=100, batch_size=64, validation_data=[val_seq, val_target], callbacks=[checkpoint_cb, early_stopping_cb])

손실 그래프를 그려본다.

plt.plot(history.history['loss'])
plt.plot(history.history['val_loss'])
plt.xlabel('epoch')
plt.ylabel('loss')
plt.legend(['train', 'val'])
plt.show()

과대적합을 제어하면서 손실을 낮춘 것을 확인할 수 있다.

3. GRU 신경망 훈련하기

이 셀은 LSTM을 간소화한 버전으로 볼 수 있는데, LSTM처럼 셀 상태를 계산하지 않고 은닉상태 하나만 포함한다.

GRU셀은 LSTM보다 가중치가 적기 때문에 계산량이 적지만 LSTM 못지않은 좋은 성능을 내는 것으로 알려져 있다.

model4 = keras.Sequential()
model4.add(keras.layers.Embedding(500, 16, input_length=100))
model4.add(keras.layers.GRU(8))
model4.add(keras.layers.Dense(1, activation='sigmoid'))

rmsprop = keras.optimizers.RMSprop(learning_rate = 1e-4)
model4.compile(optimizer=rmsprop, loss='binary_crossentropy', metrics=['accuracy'])

checkpoint_cb = keras.callbacks.ModelCheckpoint('best-gru-model.h5')
early_stopping_cb = keras.callbacks.EarlyStopping(patience=3, restore_best_weights=True)

history = model4.fit(train_seq, train_target, epochs=100, batch_size=64, validation_data=[val_seq, val_target], callbacks=[checkpoint_cb, early_stopping_cb])

손실 그래프를 출력한다.

plt.plot(history.history['loss'])
plt.plot(history.history['val_loss'])
plt.xlabel('epoch')
plt.ylabel('loss')
plt.legend(['train', 'val'])
plt.show()

드롭아웃을 사용하지 않아 이전보다 훈련과 검증 손실에 차이가 있지만, 훈련 과정이 잘 수렴되고 있는 것을 확인할 수 있다.

profile
어제보다 성장한 오늘

0개의 댓글