Python/ML & DL 공부

[DL] 09-3 LSTM과 GRU 셀

dori_0 2022. 4. 7. 15:38

혼자 공부하는 머신러닝+딥러닝 책을 바탕으로 공부한 내용입니다.

 

CH9 텍스트를 위한 인공 신경망 ③

LSTM과 GRU 셀로 훈련

 


 

순환 신경망에서 빼놓을 수 없는 핵심 기술인 LSTM과 GRU 셀을 사용한 모델을 만들어보자

 

▶ LSTM 신경망 훈련하기

  • LSTM 셀
    1. 타임스텝이 긴 데이터를 효과적으로 학습하기 위해 고안된 순환층
    2. 입력 게이트, 삭제 게이트, 출력 게이트 역할을 하는 작은 셀이 포함되어 있음
    3. 은닉 상태 외에 셀 상태를 출력함 - 셀 상태는 다음 층으로 전달되지 않으며 현재 셀에서만 순환됨

 

먼저 이전 절처럼 IMDB 리뷰 데이터를 로드하고 샘플의 길이를 100으로 맞춰주자

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)
    
# 길이 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)

 

이제 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'))

model.summary()

  • SimpleRNN 클래스의 모델 파라미터 개수는 200개였고, LSTM 셀에는 작은 셀이 4개 있으므로 모델 파라미터 개수가 800개이다.

 

 

모델을 컴파일하고 훈련해보자

배치 크기는 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',
                                                save_best_only=True)
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])

 

훈련 손실과 검증 손실 그래프를 그려보면

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이 과대적합을 억제하면서 훈련을 잘 수행한 것으로 보인다.

 

 

▶ LSTM 신경망 훈련하기

완전 연결 신경망과 합성곱 신경망에서 우리는 드롭아웃을 적용해 과대적합을 막아주었다.

순환층에서도 드롭아웃을 적용하고 모델을 훈련해보자

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-dropout-model.h5',
                                                save_best_only=True)
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()

  • LSTM 층에 적용한 드롭아웃이 효과를 발휘한 것으로 보인다.
  • 훈련 손실과 검증 손실 간의 차이가 좁혀진 것을 확인할 수 있다.

 

 

▶ 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'))

model.summary()

 

이 모델을 이제 훈련하고 손실 그래프를 확인해보면

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',
                                                save_best_only=True)
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()

  • 그래프를 보면 과대적합을 제어하면서 손실을 최대한 낮춘 것을 확인할 수 있다.

 

지금까지는 LSTM 셀을 사용한 훈련과 드롭아웃을 적용해 보았고 2개의 층을 쌓은 순환 신경망을 만들어 보았다.

다음으로는 유명한 또 다른 셀인 GRU 셀을 사용해보자

 

 

▶ GRU 신경망 훈련하기

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'))

model4.summary()

  • LSTM 클래스를 GRU 클래스로 바꾼 것 외에는 이전 모델과 동일하다

 

 

이제 GRU 셀을 사용한 순환 신경망을 훈련해보자

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',
                                                save_best_only=True)
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])

  • LSTM과 거의 비슷한 성능을 내는 것을 확인할 수 있다.

 

이 모델의 손실을 그래프로 그려보면

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

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

 

 

▶ LSTM과 GRU 셀로 훈련

이 절에서는 순환 신경망에서 가장 인기 있는 LSTM과 GRU 셀을 배웠다.

순환층에 드롭아웃을 적용해봤고 2개의 순환층을 쌓는 방법에 대해 알아보았다.

 

 

가장 좋았던 2개의 순환층을 쌓은 모델을 다시 로드하여 테스트 세트에 대해 성능을 확인해보자

# 성능 확인
test_seq = pad_sequences(test_input, maxlen=100)
rnn_model = keras.models.load_model('best-2rnn-model.h5')
rnn_model.evaluate(test_seq, test_target)

  1. 테스트 세트를 훈련 세트와 동일한 방식으로 변환
  2. load_model() 함수를 사용해 best-2rnn-model.h5 파일 읽어들이기
  3. evaluate() 메서드로 테스트 세트에서 성능 계산하기

 

이 모델은 드롭아웃을 적용하여 과대적합을 잘 억제했기 때문에 테스트 세트와 검증 세트가 크게 차이 나지 않는 것을 확인할 수 있다.