본문 바로가기
딥러닝관련/RNN계열

RNN이란

by 머리올리자 2022. 5. 12.

 

RNN : Recurrent Neural Network

 

RNN의 기본 구조

Recurrent : 순환한다

Recurrent Neural Network : 순환하는 뉴럴 네트워크

 

순환을 하기 위해서는 "닫힌 경로" 혹은 "순환하는 경로"가 필요함.

 

데이터가 순환하면서 끊임없이 정보가 갱신됨

 

RNN에 이용되는 계층을 "RNN 계층"이라고 부름

 

RNN의 간단한 구조는 아래와 같음

 

출처 : https://velog.velcdn.com/post-images%2Fdscwinterstudy%2Ffb326df0-4957-11ea-ae72-d1a4dfb08214%2Ffig-5-6.png

 

RNN layer는 순환하는 경로를 포함한다.

 - 이 순환 경로를 따라 데이터를 layer 안에서 순환시킬 수 있음.

 

또한 $x_t$를 입력으로 받으며 $t$는 시각을 뜻함

 

이는 입력으로 $(x_0, x_1, ..., x_t, ...)$ 의 시계열 데이터가,

출력으로 $(h_0, h_1, ..., h_t, ...)$ 가 입력에 대응되어 나오는 것을 확인할 수 있음

 

입력 $x_t$는 vector라고 가정. 문장(단어 순서)을 다루는 경우를 예로 든다면, 각 단어의 분산 표연(단어 벡터)이 $x_t$가 되며, 이 분산 표현이 순서대로 하나씩 RNN의 layer에 입력되는 것.

 

--

위 그림을 보면 출력이 2개의 방향으로 나눠져 있는 것을 확인할 수 있음.

이는 출력이 복제되어 다시 자신의 입력으로 들어가는 것으로 볼 수 있음

--

 

RNN의 자세한 구조

RNN 구조를 한 번 펼쳐보면 아래와 같다

https://velog.velcdn.com/post-images%2Fdscwinterstudy%2F82a3d7f0-4a7b-11ea-a20b-1d8481a30fba%2Ffig-5-8.png

 

펼쳐보면 길게 늘어진 형태의 신경망으로 볼 수 있다.

 

여기서의 RNN layer는 기존 신경망과는 다르게 모두 "같은 layer"를 가진다.

각 시각($t$)의 RNN layer는 layer로의 입력과($x_t$) 1개 전의 RNN layer($h_{t-1}$)의 출력을 받는다.

 

이 두 정보를 바탕으로 아래의 현재 시점의 출력을 계산한다.

 

$h_t = tanh(h_{t-1}W_h + x_tW_x + b)$

 

$W_x$ : input $x$를 출력 $h$로 변환하기 위한 가중치

$W_h$ : 1개의 RNN의 출력($h_{t-1}$)을 다음 시점으로 변환하기 위한 가중치

$x_t$와 $h_{t-1}$은 행벡터

 

위 입력과 가중치를 통해 연산을 진행한 후 tanh(hyperbolic tangent) activation fuction을 이용하여 최종 output을 내보냄.

 

그 결과로 output $h_t$가 출력.

 

이 $h_t$는 다른 layer를 향해 출력되는 동시에, 다음 step($t$)의 RNN layer를 향해서도 출력

 

위 식을 잘 보면 현재의 출력 $h_t$는 한 시점 이전의 출력 $h_{t-1}$에 기초해 계산됨을 알 수 있음.다른 관점으로 보면, RNN은 $h$라는 '상태'를 가지고 있으며, 위 식으로 갱신된다고 해석 가능.

 

따라서 RNN layer를 '상태를 가지는 계층' 혹은 '메모리가 있는 layer'라고 한다.

 

여기서 $h_t$을 hidden state 혹은 hidden state vector라고 표현가능.

 

Backpropagation Through Time(BPTT)

출처 : https://velog.velcdn.com/post-images%2Fdscwinterstudy%2F7a8d9280-4a7c-11ea-bf16-21cfb7538151%2Ffig-5-10.png

 

RNN에는 일반적인 back-propagation을 수행할 수 있음. 

이를 시간에 따라 펼친 오차역전파라고 해서 Backpropagation Through Time이라고 표현

 

그러나 위 방법은 문제가 발생할 수 있음.  -> 긴 시계열 데이터를 학습할 때, 데이터의 시간이 커지는 것에 비례하여 소비하는 컴퓨팅 자원도 증가하기 때문에

  -> 시간의 크기가 커지면 backpropagation 시 gradient가 불안정해질 수도 있음.

 

Truncated BPTT

큰 시계열 데이터를 처리할 때는 흔히 neural network 연결을 적당한 길이로 '끊는다'. 시간축 방향으로 너무 길어진 신경망을 적당한 지점에서 잘라내어 작은 신경망 여러 개로 만든다는 아이디어. 그리고 이 잘라낸 신경망에서 backpropagation 수행.

 

  - "back-propagation의 연결"만 끊어야 한다. (feed-forward는 그대로 유지)

  - back-propagation의 연결은 적당한 길이로 잘라내, 그 잘라낸 신경망 단위로 학습.

 

출처 : https://velog.velcdn.com/images/syi07030/post/3e9e37bd-c0a3-49ee-a202-7538c438223c/image.png

위에서는 RNN layer를 길이 10개 단위로 학습할 수 있도록 back-propagation의 연결을 끊었다.

 

  - 이렇게되면 그보다 미래의 데이터에 대해서는 생각할 필요가 없음.

  - 각각의 블록 단위로, 미래 블록과는 독립적으로 back-propagation을 수행

 

Truncated BPTT의 학습 방식

 

첫 번째 블록의 순전파와 역전파

 - 이 블록 내에선만 역전파 수행

 

출처 : https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc0xvk9%2FbtqTqIGxYUX%2FDgUK6xYg01psNR7LhKvd51%2Fimg.png

 

두 번째 블록의 순전파와 역전파

출처 : https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdVtaM6%2FbtqTvQqc8Qk%2FDh4gdsE5jNJtdxvZO3K24K%2Fimg.png

 

 

마지막 은닉 상태인 $h_9$이 필요하다는 것.

 

전체적인 흐름

출처 : https://velog.velcdn.com/post-images%2Fdscwinterstudy%2Fb4c30ab0-4a7d-11ea-b495-e1bb7f929adb%2Ffig-5-14.png

 

Truncated BPTT의 미니배치 학습

출처 : https://velog.velcdn.com/post-images%2Fdscwinterstudy%2F61115260-4b30-11ea-b709-6773af988252%2Ffig-5-15.png

 

RNN의 구현

RNN에서 다루는 신경망은 결국 아래의 그림과 같다(가로 길이 고정)

출처 : https://velog.velcdn.com/post-images%2Fdscwinterstudy%2F11a6c050-4a88-11ea-ab57-a5ba1cc7f74f%2Ffig-5-16.png

 

1. 신경망은 길이가 $T$인 시계열 데이터를 받는다 ($T$는 임의의 값)

2. 각 시각의 hidden state $T$개 출력

3. 모듈화를 생각해 위의 그림의 신경망을 '하나의 계층'으로 구현

 

출처 : https://velog.velcdn.com/post-images%2Fdscwinterstudy%2F25151dd0-4a88-11ea-ab57-a5ba1cc7f74f%2Ffig-5-17.png

 

위와 같이 상하 방향의 입력과 출력을 각각 하나로 묶으면 옆으로 늘어선 일련의 layer를 하나의 layer로 간주할 수 있다.

이때 Time RNN layer 내에서 한 단계 작업을 수행하는 layer를 'RNN layer'라 하고, $T$ 단계의 작업을 한꺼번에 처리하는 계층을 'Time RNN layer'라고 한다.

 

RNN 계층 구현

 

미니배치로 모아 처리한다고 가정.

$x_t$와 $h_t$에는 각 샘플 데이터를 행 방향으로 저장

 

RNN의 순전파

$h_t = tanh(h_{t-1}W_h + x_tW_x + b)$

 

N : 미니배치

D : 입력차원 수

H : hidden state vector 차원 수

 

 

class RNN:
  def __init__(self, Wx, Wh, b):
    self.params = [Wx, Wh, b] # 인수로 받은 paramters 리스트 저장
    self.grads = [np.zeros_like(Wx), np.zeros_like(Wh), np.zeros_like(b)] # 기울기 초기화
    self.cache = None # 역전파 계산 시 사용하는 중간 데이터 cache 초기화

    
  def forward(self, x, h_prev): # 입력 x, 이전 t의 output h_prev
    Wx, Wh, b = self.params
    t = np.matmul(h_prev, Wh) + np.matmul(x, Wx) + b # RNN 계산
    h_next = np.tanh(t) # 최종 출력
    
    self.cache = (x, h_prev, h_next)
    return h_next

 

RNN의 순전파와 역전파를를 computational graph로 확인

 

RNN의 backward 

def backward(self, dh_next):
  Wx, Wh, b = self.params
  x, h_prev, h_next = self.cache
  
  dt = dh_next*(1-h_next**2)
  db = np.sum(dt, axis=0)
  dWh = np.matmul(h_prev.T, dt)
  dh_prev = np.matmul(dt, Wh.T)
  dWx = np.matmul(x.T, dt)
  dx = np.matmul(dt, Wx.T)
  
  self.grads[0][...] = dWx
  self.grads[1][...] = dWh
  self.grads[2][...] = db
  
  return dx, dh_prev

 

이 글의 내용은 많은 부분 아래 링크인 밑바닥부터 시작하는 딥러닝 2을 참고했으며

영리 목적이 아닌 개인적인 학습을 위해 정리한 내용을 바탕으로 작성했음을 밝힙니다.

 

 

내용 참고

https://book.naver.com/bookdb/book_detail.nhn?bid=14797086 

 

밑바닥부터 시작하는 딥러닝 2

직접 구현하면서 배우는 본격 딥러닝 입문서이번에는 순환 신경망과 자연어 처리다!이 책은 『밑바닥부터 시작하는 딥러닝』에서 다루지 못했던 순환 신경망(RNN)을 자연어 처리와 시계열 데이

book.naver.com