초기 언어 모델에서 많이 사용되었던 RNN에 대해서 알아보고자 한다. RNN은 Recurrent Neural Network의 줄임말로 한국어로는 순환신경망이라 부른다. RNN은 순차적으로 연산하는데 매우 효과적인 모델이다. 언어 모델에 집중하여 RNN부터 LSTM, Transformer까지 정리해보고자 한다.
RNN
우리가 여태까지 배운 기본적인 인공신경망의 경우 순차적인 input이 아닌, 1개의 input에 대해서 1개의 output을 도출하는 network이었다. 하지만 RNN은 순차적으로 입력과 출력을 도출하기에, input과 output의 형태가 다양하다. RNN을 통해 어떻게 output이 계산되고, 어떻게 parameter을 학습시키는지 알아보자.
1. 기본 구조
RNN은 순차적으로 데이터를 입력받고, 출력하기에 입력과 출력에 따라 여러 구조를 나타난다. 다음 예시를 통해 자세히 설명하겠다.
- one to one : 매 input마다 hidden state마다 output을 출력하는 가장 기본적인 구조로 Vanilla RNN이라고 불린다.
- one to many : 하나의 input에 대해 여러개의 output을 출력하는 구조이다. 이미지에 대해 문장을 생성하는 이미지 캡셔닝이 해당 구조이다.
- many to one : 여러 input을 입력받아, 1개의 output을 출력하는 구조이다. 보통 문장의 경우 어려 token을 나누어 입력하기에, 문장에 대한 감정 분류 또는 문장을 바탕으로 그림을 생성해주는 DALL E와 같은 예시들이 이에 해당한다.
- many to many : 여러 input에 대해 여러개의 output을 출력하는 구조이다. Natural Language Processing에 가장 많이 사용되는 구조로, 위의 그림처럼 한번에 출력하는 경우 또는 즉시 생성해주는 구조가 존재한다. ChatGPT, 기계번역 등에서 사용되는 표준 형태이다.
위의 설명처럼 RNN은 input sequence와 output sequence를 필요에 맞게 조절할 수 있다.
2. 연산 과정
그림에서 초록색 부분을 Cell이라 하는데, 각 Cell에서 이전 상태의 값을 저장하고 있기에, 일종의 메모리 역할을 수행한다. 따라서 메모리 셀또는 RNN셀이라 불린다.
메모리 셀은 출력층 $y_t$방향으로 출력의 값을 보내거나, 다음 은닉층의 방향으로 현재 state 정보인 $h_t$를 전달한다.
즉 hidden state $h_t$는 이전 hidden state값 $h_{t-1}$과, input값 $x_t$의 영향을 받아 갱신된다.
이를 수식적으로 표현하면 다음과 같다.
은닉층 : $h_t = tanh(W_{xh}x_t + W_{hh}h_{t-1})$
출력층 : $y_t = W_{hy}h_t$
여기서 사용된 $W_{xh},\ W_{hh},\ W_{hy},\ bias$는 모든 hidden state에 대해서 동일한 값을 사용한다.
각 지점에서의 은닉층의 형태는 동일해야 하기에, 연산이 진행될 때의 각 형태는 다음과 같다. $d$는 input의 vector size, $D_h$는 hidden state의 input size이다.
$x_t\ :\ (d\times 1)$
$W_x\ :\ (D_h \times d)$
$W_h\ :\ (D_h \times D_h)$
$h_{t-1}\ :\ (D_h\times 1)$
$b\ :\ (D_h \times 1)$
그렇다면 왜 RNN에서는 Sigmoid function대신 tanh function을 사용하였을까?
Sigmoid의 경우 output의 range가 [0, 1]인데 반해, tanh function은 [-1, 1]이다. RNN은 동일한 hidden state를 반복적으로 사용하기에, 음의방향 또는 양의 방향으로 치우쳐질 수 있다.
우리가 기존에 사용했던 Fully Connected Layer의 서로 다른 parameter을 사용하여 Bias로 값을 보정해주면되기에 효과가 있었지만, RNN의 경우 동일한 Weight와 Bias를 사용하기에, 각 상황에 맞는 Parameter로 맞추기 어렵다.
또다른 의견으로는 RNN은 Vanishing Gradient가 가장 큰 문제이기에, 기울기를 최대한 오래 보존 하기 위해서는 0에서 기울기가 더 가파른 tanh함수를 사용하는 것이 더 올바르다고 주장한다.
그림을 보면 tanh의 기울기가 더 가파른것을 확인할 수 있다.
위와 같은 문제로, ReLU등 다른 Activation function은 배제되었다.
그림과 같이 RNN은 각 time step에서 동일한 weight를 사용하고, Loss는 모든 output들의 Loss값을 더하여 계산한다.
3. Seq-to-Seq Example
실제로 RNN이 많이 사용되는 형태로, Encoder와 Decoder을 이용하는 모델을 seq2seq model이라 한다. input의 정보를 hidden state에 저장하는 과정을 Encoding과정, hidden state에 저장된 정보를 바탕으로 output 형태로 만드는 과정을 Decoding 방식이라 한다.
그림을 통해 이해해보자. "hell"이후의 문자를 예측하는 것이 우리의 목표이다. 반복적으로 hidden state의 값이 update되면서, 마지막 word에 해당하는 "o"를 추론하는 것을 알 수 있다. 보통 추론을 시작할때 <START> Token을 input으로 시작하고, 마지막 문장의 끝을 알리는데에는 <END> Token으로 끝을 알린다.
결과를 위해 SoftMax 함수를 사용하고, 정답값이 다른 값보다 얼마나 높은지 추정하고, 다른 값들과 상호 배타적(mutually Exclusive)하게 비교가 가능하다. 이를 바탕으로 Cross Entropy를 통한 학습이 가능하다.
4. Forward Propagation & Back Propagation
RNN을 재대로 이해하기 위해서 내부적으로 연산이 어떻게 이루어지는지 짚어볼 필요가 있다.
여기에 그림으로 잘 표현해주셔서 이를 사용하겠다.
Forward Process
$h_t = tanh(W_{hh}h_{t-1} + W_{xh}x_t+b_h)$, $y_t=W_{hy}h_t+b_y$에 대한 computational graph이다.
- $x_t, h_{t-1}$에 Weight를 곱하여 $h_{raw}$를 만들고, 위에서 설명했던 대로 non-linear function으로 $tanh$ function을 사용한다.
- output $y_t$는 $h_t$에 output layer을 더하여 최종적으로 계산된다.
forward Process를 통해 output $y_t$를 구했으니, 이제 각 weight의 gradient를 구할 수 있다!
Backward Process
Backward Process 를 재대로 이해하기 위해서는 예전에 작성한 포스트를 참조하면 좋다.
정리하면 다음과 같은 성질을 보인다.
- 덧셈에서는 Global Gradient가 각 weight에 그대로 전달된다.
- 곱셈에서는 타 Weight의 local variable값을 현재 Gradient값에 곱한다.
- 복잡한 연산은 모듈단위로 묶어 Gradient를 구할 수 있다.
$h_t$는 알지 못한다. 우리는 $y_t$의 Loss 바탕으로 back propagation을 진행한다.
$tanh$을 미분하면 $1-tanh^2(x)$이다. 나머지의 gradient를 구하는 과정은 크게 어렵지 않다.
이를 바탕으로 $h_{t-1}$까지의 gradient를 구할 수 있다.
그러나 아래 그림은 조심할 필요가 있다. 위의 back propagation과정에서는 $h_t$의 값에 대한 정보가 없었지만, 계산을 통해 $h_{t-1}$의 graident를 구할 수 있다.
따라서 $h_{t}$의 계산을 통해 구해진 $h_{t-1}$와, $y_{t-1}$로 부터 구한 $h_{t-1}$을 더하여 $h_{t-2}$를 구한다.
하지만 그림과 같이 모든 $h_t$를 구하는데는 시간이 많이 걸린다.
5. Truncated Backpropagation
RNN은 동일한 hidden state를 이용하여 계산하기에 다음과 같은 한계점을 가진다.
- RNN은 앞에 hidden state를 이용하여 진행하여 계산시간과 학습시간이 느리다.
- RNN은 많은 memory를 사용한다.
- RNN은 Markov Assumption(미래는 현재와 과거와 무관하다)을 만족하지 않는다.
RNN의 모든 출력을 계산하면서 학습을 진행하면 너무 느리기에, 학습 방법으로 Truncated Backpropagation 방법을 사용한다.
다음과 같이 한번에 Loss를 구해 계산하는 것이 아닌, Batch 단위로 학습을 진행한다.
6. Conclusion
RNN은 순차적으로 데이터가 들어오는 비디오, 영상, 텍스트에 많이 사용되었던 모델이다. 그러나 RNN은 다음과 같은 치명적인 단점이 존재한다.
- 학습을 하기 위해서 모든 $h_t$의 값을 구해야 하기에 느리다.
- Exploding Gradient 문제가 발생한다. 이러한 문제를 해결하기 위해 Gradient Clipping 방식이 존재한다.
- Vanishing Gradient 문제의 경우 RNN 방식만으로는 해결이 불가능하다.
하지만 CNN과 더불어 매우 중요한 기본 인공신경망 모델로 꼭 알고가야 한다.
다음은 RNN의 Vanishing Gradient를 해결하기 위해 소개된 LSTM에 대해서 post를 써보고자 한다.
7. Reference
- cs231n - Lecture 10
- https://sooho-kim.tistory.com/62
- https://velog.io/@fbdp1202/CS231n-%EC%A0%95%EB%A6%AC-10.-RNN-Neural-Networks#%EC%B0%B8%EC%A1%B0
- https://www.facebook.com/groups/TensorFlowKR/permalink/478174102523653/
- https://ratsgo.github.io/natural%20language%20processing/2017/03/09/rnnlstm/
'공부 > 머신러닝' 카테고리의 다른 글
Optimizer (0) | 2024.04.06 |
---|---|
Weight Initialization (0) | 2024.03.25 |
Convolution Neural Network (0) | 2024.03.19 |
Activation Function (1) | 2024.03.17 |
Backpropagation (0) | 2024.03.17 |