EXCELSIOR

07-2. 순환 신경망 (RNN, Recurrent Neural Network) - (2) 본문

DeepLearning/개념

07-2. 순환 신경망 (RNN, Recurrent Neural Network) - (2)

Excelsior-JH 2018. 10. 26. 15:12

07-2. 순환 신경망 (RNN, Recurrent Neural Network) - (2)


저번 포스팅인 07-1. 순환 신경망 - (1)에서는 시계열 데이터에 적합한 모델인 RNN의 구조와 텐서플로(TensorFlow)의 BasicRNNCellstatic_rnn(), dynamic_rnn()을 이용해 RNN을 구현하는 방법에 대해 알아보았다. 이번 포스팅에서는 RNN을 학습시키는 방법과 심층 RNN에 대해 알아보도록 하자.


1. RNN 학습시키기

1.1 BPTT (BackPropagation Through Time)

RNN은 기존 신경망의 역전파(backprop)와는 달리 타임 스텝별로 네트워크를 펼친 다음, 역전파 알고리즘을 사용하는데 이를 BPTT(BackPropagation Through Time)라고 한다.


BPTT 또한 일반적인 역전파와 같이 먼저 순전파(forward prop)로 각 타임 스텝별 시퀀스를 출력한다. 그런다음 이 출력 시퀀스와 손실(비용)함수를 사용하여 각 타임 스텝별 Loss를 구한다. 그리고 손실 함수의 그래디언트는 위의 그림과 같이 펼쳐진 네트워크를 따라 역방향으로 전파된다. BPTT는 그래디언트가 마지막 타임 스텝인 출력뿐만 아니라 손실함수를 사용한 모든 출력에서 역방향으로 전파된다.

RNN은 각 타임 스텝마다 같은 매개변수 이 사용되기 때문에 역전파가 진행되면서 모든 타임 스텝에 걸쳐 매개변수 값이 합산된다. 이렇게 업데이트된 가중치는 순전파 동안에는 모든 타임 스텝에 동일한 가중치가 적용된다.


1.2 Truncated BPTT

3.1에서 살펴본 BPTT는 전체의 타임 스텝마다 처음부터 끝까지 역전파를 하기 때문에 타임 스텝이 클 수록 계산량이 많아지는 문제가 있다. 이러한 계산량 문제를 해결하기 위해 전체 타임 스텝을 일정 구간(예를들어 3 또는 5 구간)으로 나눠 역전파를 하는 Truncated BPTT를 사용한다.




1.3 RNN을 이용한 분류기 구현

이번에는 RNN을 이용해 MNIST 숫자 이미지 데이터셋을 분류하는 분류기를 구현해보자. MNIST와 같은 이미지 데이터는 이전 포스팅 06. 합성곱 신경망에서 살펴본 이미지의 공간(spatial) 구조를 활용하는 CNN 모델이 더 적합하지만, 인접한 영역의 픽셀은 서로 연관되어 있으므로 이를 시퀀스 데이터로 볼 수도 있다. 아래의 그림처럼 MNIST 데이터에서 28 x 28 픽셀을 시퀀스의 각원소는 28개의 픽셀을 가진 길이가 28 시퀀스 데이터로 볼 수 있다.


아래의 코드는 텐서플로(TensorFlow)의 BasicRNNCelldynamic_rnn()을 이용해 MNIST 분류기를 구현한 코드이다. 아래의 예제에 대한 전체 코드는 ExcelsiorCJH에서 확인할 수 있다.


1.3.1 MNIST Data Load

먼저, MNIST 데이터셋을 tf.keras.datasets.mnist.load_data()를 이용해 아래와 같이 불러온다.



1.3.2 RNN for MNIST

그런 다음, 아래의 코드와 같이 MNIST 데이터셋을 분류하는 RNN 모델을 만들어 준다. MNIST를 분류하는 RNN 모델은 아래의 그림과 같이 RNN모델 중 28개의 타임 스텝(28 pixels)의 시퀀스를 입력받아 하나의 벡터를 출력하는 Sequence-to-Vector 모델이다.




1.4 RNN을 이용한 시계열 데이터 예측

이번에는 주식가격과 같은 시계열(time series) 데이터를 예측하는 RNN 모델을 구현해 보도록 하자.


1.4.1 가상의 시계열 데이터 만들기

RNN 모델에 사용하기 위한 가상의 시계열 데이터를 다음과 같이 만들어 준다.




1.4.2. RNN for Time Series

이제 시계열 데이터를 예측하는 RNN을 모델링 해보자. 100개의 순환뉴런을 가지고, 입력 시퀀스의 길이가 20이므로 20개의 타임 스텝을 가지며, 출력 시퀀스 또한 길이가 20인 시퀀스를 출력한다. RNN의 출력결과는 아래의 그림과 같이 [미니배치, 뉴런 개수] ([batch_size, n_neurons])이기 때문에, 미니 배치를 50으로 할 경우, (50, 100)의 형태를 가지는 출력 벡터가 나오게 된다. 하지만, 실제로 우리가 원하는 출력은 각 타임 스텝 마다 하나의 출력, 즉 (batch_size, 1)의 형태를 원하기 때문에 아래의 그림과 같이 다음의 과정이 필요하다.

  1. 각 타임 스텝마다 (batch_size, n_neurons) 출력 벡터를 tf.reshape()을 이용해 쌓는다(stack).

  2. 그런 다음, 1개의 유닛을 가지는 Fully-Connected Layer를 적용한다. 이때의 FC Layer는 단지 차원 축소를 위한 레이어이기 때문에 활성화 함수를 사용하지 않는다.

  3. 마지막으로, tf.reshape()을 이용해 차원 축소시킨 출력을 다시 타임 스텝별로 풀어준다(unstack).


아래의 코드는 위의 과정을 적용하여 시계열 데이터를 예측하는 RNN 모델을 구현한 코드이다.



위의 코드를 통해 학습 시킨 모델을 테스트 해보면 다음과 같은 결과를 얻을 수 있다(아래 그림의 코드는 ExcelsiorCJH's GitHub 참고).



1.4.3 OutputProjectionWrapper를 사용한 RNN 모델링

1.4.2에서는 RNN의 출력결과를 우리가 원하는 형태로 맞춰주기 위해 stack → FC layer → unstack과정이 필요했다. 텐서플로에서는 tf.contrib.rnn.OutputProjectionWrapper 를 이용해 Cell을 감싸서 아래의 그림과 같은 역할을 해준다. OutputProjectionWrapper의 FC Layer는 출력 시퀀스(outputs)에만 적용되고 state()에는 적용되지 않는다.




아래의 코드는 OutputProjectionWrapper를 이용해 1.4.2의 동일한 예제를 작성한 코드이다.



2. 심층 RNN

심층 RNN(deep RNN)은 아래의 그림처럼 RNN Cell을 여러 층으로 쌓은 것을 말한다. 일반적으로 딥러닝에서는 이러한 Deep RNN 모델이 많이 사용된다.


2.1 텐서플로에서의 Deep RNN

텐서플로에서는 tf.nn.rnn_cell.MultiRNNCell을 이용해 RNN Cell을 여러층으로 쌓을 수 있는데 먼저 BasicRNNCell을 이용해 여러개의 Cell을 만들고, 이것을 MultiRNNCell로 쌓아서 deep RNN을 구현할 수 있다. 아래의 예제는 deep RNN을 구현한 간단한 예제 코드이다.



2.2 Dropout 적용하기

2.1에서처럼 여러 층의 RNN Cell을 쌓게되면 오버피팅(overfitting)되기 쉽기 때문에 RNN layer의 전과 후에 드롭아웃(dropout) layer를 추가할 수 있다. 또한, RNN 층(layer) 사이에도 드롭아웃을 적용할 수 있는데, 텐서플로에서는 tf.nn.rnn_cell.DropoutWrapper를 이용해 RNN 층 사이에도 드롭아웃을 적용할 수 있다. DropoutWrapper는 다음과 같은 인자(파라미터)를 통해 드롭아웃을 적용할 수 있다.

  • input_keep_prob인자를 통해 입력 부분에서의 드롭아웃, 디폴트는 1.0

  • output_keep_prob인자를 통해 출력부분에서의 드롭아웃, 디폴트는 1.0

  • state_keep_prob인자를 통해 state()에서의 드롭아웃, 디폴트는 1.0

아래의 예제는 DropoutWrapper를 이용해 RNN 층에 드롭아웃을 적용한 예제이다. 아래의 코드에서 사용한 데이터는 1.4.1에서 생성한 가상의 시계열 데이터를 사용했다(ExcelsiorCJH's GitHub 참고).




3. 마무리

이번 포스팅에서는 RNN을 학습시키는 방법인 BPTT와 텐서플로를 이용해 MNIST 분류기와 시계열 데이터를 예측하는 RNN 모델을 구현해 보았다. 그리고 심층 RNN을 구현하는 방법과 RNN에 드롭아웃을 적용하는 방법에 대해 알아보았다. 위의 코드에 대한 전체 코드는 https://github.com/ExcelsiorCJH/Hands-On-ML/blob/master/Chap14-Recurrent_Neural_Networks/Chap14_2-Recurrent_Neural_Networks.ipynb에서 확인할 수 있다.

다음 포스팅에서는 타임스텝이 매우 긴 RNN의 학습단계에서의 문제점과 이를 보완한 RNN의 변형인 LSTM과 GRU에 대해 알아보도록 하자.

Comments