import keras
keras.__version__
Using TensorFlow backend.
'2.2.4'
이 노트북은 케라스 창시자에게 배우는 딥러닝 책의 6장 2절의 코드 예제입니다. 책에는 더 많은 내용과 그림이 있습니다. 이 노트북에는 소스 코드에 관련된 설명만 포함합니다. 이 노트북의 설명은 케라스 버전 2.2.2에 맞추어져 있습니다. 케라스 최신 버전이 릴리스되면 노트북을 다시 테스트하기 때문에 설명과 코드의 결과가 조금 다를 수 있습니다.
[...]
넘파이로 간단하게 구현한 과정이 실제 케라스의 SimpleRNN
층에 해당합니다:
from keras.layers import SimpleRNN
SimpleRNN
이 한 가지 다른 점은 넘파이 예제처럼 하나의 시퀀스가 아니라 다른 케라스 층과 마찬가지로 시퀀스 배치를 처리한다는 것입니다. 즉, (timesteps, input_features)
크기가 아니라 (batch_size, timesteps, input_features)
크기의 입력을 받습니다.
케라스에 있는 모든 순환 층과 동일하게 SimpleRNN
은 두 가지 모드로 실행할 수 있습니다. 각 타임스텝의 출력을 모은 전체 시퀀스를 반환하거나(크기가 (batch_size, timesteps, output_features)
인 3D 텐서), 입력 시퀀스에 대한 마지막 출력만 반환할 수 있습니다(크기가 (batch_size, output_features)
인 2D 텐서). 이 모드는 객체를 생성할 때 return_sequences
매개변수로 선택할 수 있습니다. 예제를 살펴보죠:
from keras.models import Sequential
from keras.layers import Embedding, SimpleRNN
model = Sequential()
model.add(Embedding(10000, 32))
model.add(SimpleRNN(32))
model.summary()
_________________________________________________________________ Layer (type) Output Shape Param # ================================================================= embedding_1 (Embedding) (None, None, 32) 320000 _________________________________________________________________ simple_rnn_1 (SimpleRNN) (None, 32) 2080 ================================================================= Total params: 322,080 Trainable params: 322,080 Non-trainable params: 0 _________________________________________________________________
model = Sequential()
model.add(Embedding(10000, 32))
model.add(SimpleRNN(32, return_sequences=True))
model.summary()
_________________________________________________________________ Layer (type) Output Shape Param # ================================================================= embedding_2 (Embedding) (None, None, 32) 320000 _________________________________________________________________ simple_rnn_2 (SimpleRNN) (None, None, 32) 2080 ================================================================= Total params: 322,080 Trainable params: 322,080 Non-trainable params: 0 _________________________________________________________________
네트워크의 표현력을 증가시키기 위해 여러 개의 순환 층을 차례대로 쌓는 것이 유용할 때가 있습니다. 이런 설정에서는 중간 층들이 전체 출력 시퀀스를 반환하도록 설정해야 합니다:
model = Sequential()
model.add(Embedding(10000, 32))
model.add(SimpleRNN(32, return_sequences=True))
model.add(SimpleRNN(32, return_sequences=True))
model.add(SimpleRNN(32, return_sequences=True))
model.add(SimpleRNN(32)) # 맨 위 층만 마지막 출력을 반환합니다.
model.summary()
_________________________________________________________________ Layer (type) Output Shape Param # ================================================================= embedding_3 (Embedding) (None, None, 32) 320000 _________________________________________________________________ simple_rnn_3 (SimpleRNN) (None, None, 32) 2080 _________________________________________________________________ simple_rnn_4 (SimpleRNN) (None, None, 32) 2080 _________________________________________________________________ simple_rnn_5 (SimpleRNN) (None, None, 32) 2080 _________________________________________________________________ simple_rnn_6 (SimpleRNN) (None, 32) 2080 ================================================================= Total params: 328,320 Trainable params: 328,320 Non-trainable params: 0 _________________________________________________________________
이제 IMDB 영화 리뷰 분류 문제에 적용해 보죠. 먼저 데이터를 전처리합니다:
from keras.datasets import imdb
from keras.preprocessing import sequence
max_features = 10000 # 특성으로 사용할 단어의 수
maxlen = 500 # 사용할 텍스트의 길이(가장 빈번한 max_features 개의 단어만 사용합니다)
batch_size = 32
print('데이터 로딩...')
(input_train, y_train), (input_test, y_test) = imdb.load_data(num_words=max_features)
print(len(input_train), '훈련 시퀀스')
print(len(input_test), '테스트 시퀀스')
print('시퀀스 패딩 (samples x time)')
input_train = sequence.pad_sequences(input_train, maxlen=maxlen)
input_test = sequence.pad_sequences(input_test, maxlen=maxlen)
print('input_train 크기:', input_train.shape)
print('input_test 크기:', input_test.shape)
데이터 로딩... 25000 훈련 시퀀스 25000 테스트 시퀀스 시퀀스 패딩 (samples x time) input_train 크기: (25000, 500) input_test 크기: (25000, 500)
Embedding
층과 SimpleRNN
층을 사용해 간단한 순환 네트워크를 훈련시켜 보겠습니다:
from keras.layers import Dense
model = Sequential()
model.add(Embedding(max_features, 32))
model.add(SimpleRNN(32))
model.add(Dense(1, activation='sigmoid'))
model.compile(optimizer='rmsprop', loss='binary_crossentropy', metrics=['acc'])
history = model.fit(input_train, y_train,
epochs=10,
batch_size=128,
validation_split=0.2)
Train on 20000 samples, validate on 5000 samples Epoch 1/10 20000/20000 [==============================] - 16s 782us/step - loss: 0.6448 - acc: 0.6115 - val_loss: 0.6156 - val_acc: 0.6736 Epoch 2/10 20000/20000 [==============================] - 15s 747us/step - loss: 0.4175 - acc: 0.8193 - val_loss: 0.3661 - val_acc: 0.8476 Epoch 3/10 20000/20000 [==============================] - 15s 745us/step - loss: 0.3060 - acc: 0.8759 - val_loss: 0.4140 - val_acc: 0.8112 Epoch 4/10 20000/20000 [==============================] - 15s 746us/step - loss: 0.2431 - acc: 0.9046 - val_loss: 0.3548 - val_acc: 0.8496 Epoch 5/10 20000/20000 [==============================] - 15s 746us/step - loss: 0.1911 - acc: 0.9294 - val_loss: 0.3922 - val_acc: 0.8422 Epoch 6/10 20000/20000 [==============================] - 15s 748us/step - loss: 0.1410 - acc: 0.9494 - val_loss: 0.3733 - val_acc: 0.8578 Epoch 7/10 20000/20000 [==============================] - 15s 746us/step - loss: 0.0995 - acc: 0.9662 - val_loss: 0.4511 - val_acc: 0.8466 Epoch 8/10 20000/20000 [==============================] - 15s 746us/step - loss: 0.0647 - acc: 0.9796 - val_loss: 0.4689 - val_acc: 0.8410 Epoch 9/10 20000/20000 [==============================] - 15s 746us/step - loss: 0.0402 - acc: 0.9882 - val_loss: 0.5292 - val_acc: 0.8382 Epoch 10/10 20000/20000 [==============================] - 15s 747us/step - loss: 0.0247 - acc: 0.9930 - val_loss: 0.5893 - val_acc: 0.8314
이제 훈련과 검증의 손실과 정확도를 그래프로 그립니다:
import matplotlib.pyplot as plt
acc = history.history['acc']
val_acc = history.history['val_acc']
loss = history.history['loss']
val_loss = history.history['val_loss']
epochs = range(1, len(acc) + 1)
plt.plot(epochs, acc, 'bo', label='Training acc')
plt.plot(epochs, val_acc, 'b', label='Validation acc')
plt.title('Training and validation accuracy')
plt.legend()
plt.figure()
plt.plot(epochs, loss, 'bo', label='Training loss')
plt.plot(epochs, val_loss, 'b', label='Validation loss')
plt.title('Training and validation loss')
plt.legend()
plt.show()
3장에서 이 데이터셋을 사용한 첫 번째 모델에서 얻은 테스트 정확도는 87%였습니다. 안타깝지만 간단한 순환 네트워크는 이 기준 모델보다 성능이 높지 않습니다(85% 정도의 검증 정확도를 얻었습니다). 이런 원인은 전체 시퀀스가 아니라 처음 500개의 단어만 입력에 사용했기 때문입니다. 이 RNN은 기준 모델보다 얻은 정보가 적습니다. 다른 이유는 SimpleRNN
이 텍스트와 같이 긴 시퀀스를 처리하는데 적합하지 않기 때문입니다. 더 잘 작동하는 다른 순환 층이 있습니다. 조금 더 고급 순환 층을 살펴보죠.
[...]
이제 실제적인 관심사로 이동해 보죠. LSTM 층으로 모델을 구성하고 IMDB 데이터에서 훈련해 보겠습니다(그림 6-16과 6-17 참조). 이 네트워크는 조금 전 SimpleRNN
을 사용했던 모델과 비슷합니다. LSTM 층은 출력 차원만 지정하고 다른 (많은) 매개변수는 케라스의 기본값으로 남겨 두었습니다. 케라스는 좋은 기본값을 가지고 있어서 직접 매개변수를 튜닝하는 데 시간을 쓰지 않고도 거의 항상 어느정도 작동하는 모델을 얻을 수 있습니다.
from keras.layers import LSTM
model = Sequential()
model.add(Embedding(max_features, 32))
model.add(LSTM(32))
model.add(Dense(1, activation='sigmoid'))
model.compile(optimizer='rmsprop',
loss='binary_crossentropy',
metrics=['acc'])
history = model.fit(input_train, y_train,
epochs=10,
batch_size=128,
validation_split=0.2)
Train on 20000 samples, validate on 5000 samples Epoch 1/10 20000/20000 [==============================] - 72s 4ms/step - loss: 0.5098 - acc: 0.7607 - val_loss: 0.3865 - val_acc: 0.8426 Epoch 2/10 20000/20000 [==============================] - 72s 4ms/step - loss: 0.2890 - acc: 0.8881 - val_loss: 0.3126 - val_acc: 0.8628 Epoch 3/10 20000/20000 [==============================] - 72s 4ms/step - loss: 0.2334 - acc: 0.9099 - val_loss: 0.3473 - val_acc: 0.8748 Epoch 4/10 20000/20000 [==============================] - 72s 4ms/step - loss: 0.1988 - acc: 0.9259 - val_loss: 0.5105 - val_acc: 0.8396 Epoch 5/10 20000/20000 [==============================] - 72s 4ms/step - loss: 0.1791 - acc: 0.9357 - val_loss: 0.3094 - val_acc: 0.8860 Epoch 6/10 20000/20000 [==============================] - 72s 4ms/step - loss: 0.1538 - acc: 0.9442 - val_loss: 0.3092 - val_acc: 0.8884 Epoch 7/10 20000/20000 [==============================] - 72s 4ms/step - loss: 0.1429 - acc: 0.9492 - val_loss: 0.4297 - val_acc: 0.8772 Epoch 8/10 20000/20000 [==============================] - 72s 4ms/step - loss: 0.1313 - acc: 0.9534 - val_loss: 0.3242 - val_acc: 0.8802 Epoch 9/10 20000/20000 [==============================] - 72s 4ms/step - loss: 0.1174 - acc: 0.9594 - val_loss: 0.3437 - val_acc: 0.8886 Epoch 10/10 20000/20000 [==============================] - 72s 4ms/step - loss: 0.1116 - acc: 0.9607 - val_loss: 0.3777 - val_acc: 0.8830
acc = history.history['acc']
val_acc = history.history['val_acc']
loss = history.history['loss']
val_loss = history.history['val_loss']
epochs = range(1, len(acc) + 1)
plt.plot(epochs, acc, 'bo', label='Training acc')
plt.plot(epochs, val_acc, 'b', label='Validation acc')
plt.title('Training and validation accuracy')
plt.legend()
plt.figure()
plt.plot(epochs, loss, 'bo', label='Training loss')
plt.plot(epochs, val_loss, 'b', label='Validation loss')
plt.title('Training and validation loss')
plt.legend()
plt.show()