RNN 이해하기



RNN이란?

Recurrent Neural Network으 약자로 순환신경망이라는 의미를 갖습니다. RNN은 주로 언어 데이터나 시계열 데이터 처리에 주로 사용되는 모델이며, 데이터를 순환시킴으로써 정보를 공유할 수 있는 모델입니다.

정보를 순환할 수 있는 기술들은 LSTM,GRU등 다양하며, 그중 가장 기초가 되는 RNN모델을 다뤄보겠습니다.

rnn_img_1

(Xt : 입력) / (A : RNN 층) / (ht : 출력)

위 사진은 RNN의 가장 기본적인 사진입니다.
사진을 입력값 $X_{t}$가 A층으로 들어가 $h_{t}$라는 출력값을 구하는 동시에 A층은 또 다시 다음 A층에 들어갑니다. 우리가 집중해야할 부분은 A에서 나온 결과가 또 다시 A 층에 들어간다는 것입니다.

그렇다면 A층에서 나온 $h_{t}$와 옆면에서 나온 ht는 서로 다른 값인가? 라는 생각을 할수도 있지만, RNN에 입력되는 시퀀스 길이만큼 $h_{t}$가 RNN모델에 들어가는 동시에 출력 시키기에 같은 값으로 이해하셔도 됩니다.

Example

입력값으로 “i am a boy”과 같은 문장이 들어간다 가정하고, 입력과정을 단계별로 설명해보겠습니다.

  • STEP 1) 첫번째 입력값(“i”)이 A라는 층에 들어간다.
  • STEP 2) A층을 통해 결과값($h_{0}$)이 나온다.
  • STEP 3) 두번째 입력값(“am”)과 첫번째 결과값(h0)이 A라는 층에 들어간다.
  • STEP 4) A층을 통해 결과값($h_{1}$)이 나온다.
  • STEP 5) 세번째 입력값(“a”)과 두번째 결과값(h1)이 A라는 층에 들어간다.
  • STEP 6) A층을 통해 결과값($h_{2}$)이 나온다.
  • STEP 7) 네번째 입력값(“boy”)과 세번째결과값(h2)이 A라는 층에 들어간다.
  • STEP 8) A층을 통해 결과값($h_{3}$)이 나온다.

위 내용처럼 RNN의 기본적인 메커니즘은 현재 입력값과 전 단계 결과 RNN층에 들어가서 현재 단계 결과를 출력하고, 이 현재 단계 결과는 또 다음단계 입력값과 함꼐 RNN층에 들어가고 결과를 출력합니다.

1d

RNN모델 그림을 풀어 해석하면 위와 같은 그림입니다.
사진속의 ($X_{0}$ : “i”) / ($X_{1}$ : “am”) / … 등으로 이해할 수 있겠죠? 그럼 A층은 어떻게 생겼길래 이런 구조를 내보내는 것일까요?

RNN Architecture

지금부터는 RNN 구조를 파헤쳐보겠습니다.
inside_rnn

이 사진은 A에 해당하는 부분의 구조입니다. 왼쪽에서 들어오는 화살표는 $h_{t-1}$(전단계의 결과)이고, 밑에서 올라는 화살표는 입력값($X_{t}$)입니다.

이 두값을 행렬 곱한 후, 하이퍼볼릭탄젠트(tanh)에 값을 넣습니다. 이 결과값을 $h_{t}$라 부르고 입력값이 끝날때 까지 순환합니다.

$h_{t}$ = tanh( $h_{t-1}$ * $W_{h}$ + $X_{t}$ * $W_{x}$ + b )

Backpropagation of RNN

RNN은 데이터를 처리하는 과정에서 하이퍼볼릭탄젠트(tanh)와 행렬 곱을 사용해 연산을 합니다.
먼저 하이퍼볼릭탄젠트(tanh)을 미분하는 과정을 살펴보겠습니다.
rnn_img_4

이를 미분한 값은 증가하지 않는다는 것을 볼 수 있습니다. 이런 과정은 많이 거칠 수록 기울기 손실(Gradient Vanshing)이 일어날 확률이 높습니다.

마찬가지로 행렬 곱하는 부분에서도 많은 과정을 거칠수록 기울기 폭발(Gradient Explosion) / 기울기 소실이(Gradient Vanshing) 일어납니다.

이처럼 RNN은 길이가 긴 데이터를 처리하지 못한다는 한계가 있습니다. 이 문제를 해결하기 위해 우리는 RNN에 게이트를 추가한 LSTM이나 GRU등의 순환신경망 모델을 사용합니다. 이는 다음 글에서 설명하는 것을 기약하며 마지막으로 Pytorch RNN모델 코드를 업로드 하며 마치겠습니다.

Pytorch Code

import torch
import torch.nn as nn

class RNN(nn.Module):
  def __init__(self, input_size, hidden_size, output_size):
    super(RNN, self).__init__()
    self.rnn_layer = nn.RNN(input_size, hidden_size)
    self.lienar = nn.Liear(hidden_size, output_size)
  def forward(self, input,):
    out, hide = self.rnn_layer(input)
    out = self.linear()
    return out


Reference
  • https://slidetodoc.com/lecture-6-smaller-network-rnn-this-is-our/
  • https://smartstuartkim.wordpress.com/2019/02/09/vanishing-gradient-problem/
  • 밑바닥부터 시작하는 딥러닝 2 / 사이토 고키 / 한빛 미디어