(3강) Recurrent Neural Network and Language Modeling

💡
RNN을 활용하는 다양한 방법과 이를 이용한 Language Model에 대해 알아본다.

Basic RNN Problem Settings

기본적인 RNN의 Task는 Sequential Data를 입력으로 받고 결과를 출력하는 형태이다. Sequential Data는 Time Step에 따라서 입력되는데, 이때 Time Step과 무관하게 동일한 가중치 파라미터가 사용된다.

RNN은 Time Step에 따라 입력되는 xtx_t에 대해서 hth_t를 출력한다. 출력된 값은 다음 Cell로 전달되어 ht+1h_{t+1}을 계산할 때 활용된다.

Time Step과 무관하게 동일한 가중치 A가 활용된다.

Model Architecture and How It Works

구체적으로 RNN이 어떻게 작동하는지 살펴보자. 위의 예시와 같이 반복적으로 활용되는 가중치 WW가 RNN이라고 할 수 있으며, 해당 값은 다음과 같은 수식으로 표현될 수 있다.

Time Step별 Hidden State Value를 구하는 수식

각각의 요소를 설명하면 다음과 같다.

  • ht1h_{t-1} : 이전 Time Step에서 생성된 Hidden State Vector
  • xtx_t : 현재 Time Step에서 입력된 Input Data Vector
  • hth_t : 현재 Time Step에서 계산된 Hidden State Vector
  • fWf_W : Input을 Output으로 연산하는 RNN 함수
  • yty_t : 계산된 hth_t를 Task에 맞게 연산한 최종 Output Data

fWf_W에 대해서 조금 더 세분화하여 생각하면 다음과 같이 표기할 수 있다.

fWf_W{Whh,Wxh,Why}\{W_{hh}, W_{xh}, W_{hy}\}로 구성되어 있다.

WhhW_{hh}는 이전의 Hidden State 값에 대한 연산이다. 동일하게 Hidden State의 Dimension으로 출력된다.

WxhW_{xh}는 입력 Data를 Hidden State의 Dimension으로 변환하는 연산이다. 이 두 값의 합을 비선형 변환한 것이 해당 Time Step의 Hidden State Value가 된다.

WhyW_{hy}는 Task에 따라 출력값을 변환하는 역할을 수행한다. 예를 들어, Classification의 경우는 각 Class에 대한 확률로 변환하는 연산일 것이다.

Types of RNNs

RNN은 Task에 따라 입력값과 출력값의 형태가 다르다. 이를 Input-to-Output으로 표기하면 다음과 같다.

  • One-to-One : Input과 Output이 1개인 경우는 양쪽 모두 Sequential Data가 아니므로, RNN 구조보다는 일반적인 Neural Network 구조를 사용한다.
  • One-to-Many : Output이 Sequential Data인 대표적인 Task는 Image Captioning이다. 한 장의 Image를 입력받아 관련된 단어의 나열(문장)을 출력하는 Task이다.
  • Many-to-One : Input이 Sequential Data인 대표적인 Task는 Sentiment Classification이다. 문장을 입력받아 해당 문장이 긍정인지 부정인지에 대한 값을 출력하는 Task이다.
  • Many-to-Many (Delayed) : 대표적인 Task는 Machine Translation이다. 문장을 끝까지 입력받은 후에 다른 문장을 출력하는 Task이다.
  • Many-to-Many (Undelayed) : 대표적인 Task는 POS Tagging이다. 각 단어를 입력받을 때마다 해당 단어의 품사를 출력하는 Task이다.

Character-level Language Model

Language Model의 가장 기본적인 Task는 Many-to-Many로, 입력받은 문자 뒤에 올 문자를 예측하는 형태로 구성된다.

단어 'hello'를 Character 단위로 입력하는 Task를 예시로 들면 다음과 같다. 먼저, 각 단어를 One Hot Encoding하여 입력으로 사용한다.

각 문자를 Vector로 표현하여 Input Layer에 입력값으로 사용한다.

각 Time Step의 hth_ttanh(𝑊hhh𝑡1+𝑊𝑥h𝑥𝑡+𝑏)tanh(𝑊_{ℎℎ}ℎ_𝑡−1 + 𝑊_{𝑥ℎ}𝑥_𝑡 + 𝑏)로 구해진다.

해당하는 연산을 통해 Hidden Dimension의 Vector로 변환된다.

각 Time Step의 최종 출력은 Logit=𝑊h𝑦h𝑡+𝑏Logit = 𝑊_{ℎ𝑦}ℎ_𝑡 + 𝑏로 구해진다.

입력 Vector와 동일한 Dimension으로, 각 값은 확률값이 된다. 동일한 입력 'l'에도 다른 출력이 나오는 것은, Hidden State의 값 덕분이다.

최종적으로 구해진 Logit과 Ground Truth의 비교를 통해 Loss를 구하게 되고, 이를 기반으로 역전파와 학습이 진행된다.

RNN Example

각 Time Step에서 구해진 출력을 다음 Time Step의 입력값으로 사용하는 경우, 무한한 길이의 예측이 가능해진다.

먼 미래의 주식 가격 예측과 같은 Task를 이와 같은 방법을 통해 수행할 수 있다.

특정한 형태를 기반으로 하는 글도 생성할 수 있다.

인물의 대사와 같은 형태의 글 출력
논문 형식의 글 출력

더 나아가서 Code와 같은 형태의 글도 생성할 수 있다.

C언어 Code를 학습한 RNN의 출력

Searching for Interpretable Cells

위와 같은 출력을 내기 위해선 Hidden State에 여러 값을 지속적으로 저장하고 있어야 한다. 예를 들어, 이전에 어떤 값을 출력했는지, 따옴표나 괄호가 열려있는지 등등의 정보를 가지고 있어야 한다. 이를 확인하기 위해 Hidden State의 한 Dimension을 고정하고 값의 변화를 Tracking해보면 다음과 같다.

Hidden State의 한 Dimension의 값 Tracking
"의 여부에 따라 변화하는 값 시각화
if의 여부에 따라 변화하는 값 시각화

이를 통해 RNN은 Hidden State에서 특정 상태를 저장하는데 활용하는 것을 알 수 있다.

Back Propagation Through Time (BPTT)

RNN의 역전파 시 모든 출력에 대해 Loss값을 구하게 되면, Resource 상의 문제가 생길 수 있다. 이를 방지하기 위해 모든 출력에 대해서가 아니라, 일정 거리만큼 Truncate하여 Loss를 구하고 역전파를 수행한다.

모든 출력에 대해 Loss를 구하는 경우의 Memory
구간을 나누어 Loss를 구하는 경우의 Memory

RNN의 Gradient는 Chain Rule에 의해 계산되는데, 이때 값이 Vanishing되거나 Exploding되는 문제가 발생할 수 있다. Hidden State에 지속적으로 곱해졌던 WhhW_{hh}가 무수히 미분되면서 값에 문제가 생기는 것이다. Vanilla RNN은 위와 같은 문제를 해결할 수 없었기에, 이를 해결하고자 하는 발전된 모델이 개발되었다.

실습

GitHub Gist Link


(4강) LSTM and GRU

💡
Vanilla RNN의 Gradient Flow를 개선한 LSTM과 이를 경량화 한 GRU에 대해 알아본다.

Long Short-Term Memory (LSTM)

LSTM은 Vanilla RNN Gradient Flow의 문제를 해결하기 위해 등장한 모델이다. 또한, 이름에서 알 수 있듯 Time Step이 먼 Data에 대해서도 정보를 잘 저장하고 학습하는데 활용할 수 있도록 구현되어 있다. LSTM의 Core Idea는 Cell State를 도입하여, 중요한 정보를 지속적으로 업데이트하고 출력에 활용하는 것이다.

Cell State는 매 Time Step의 Cell에서 동작하지만, Output으로 출력되지는 않는다.

LSTM의 Core Function은 {ct,ht}=LSTM(xt,ct1,ht1)\{c_t, h_t\} = LSTM(x_t, c_{t-1}, h_{t-1})로 표현할 수 있다. RNN의 Core Function인 ht=fW(xt,ht1)h_t = f_W(x_t, h_{t-1})과 달리, cc로 표현되는 Cell State가 추가되었다. Cell State와 Hidden State는 각각의 게이트와의 연산을 통해 구해진다.

LSTM은 다음과 같은 순서로 연산이 이루어진다. 이를 자세히 살펴보자.

각 Gate의 Vector는 동일한 Dimension h를 갖는다.

특정 Time Step인 t를 기준으로, xtx_tht1h_{t-1}를 선형변환한다. 이를 통해 얻은 4h4 h Dimension의 Vector를 각각 Gate의 값으로 사용하게 된다.

Forget Gate는 𝑓𝑡=σ(𝑊𝑓[h𝑡1,𝑥𝑡]+𝑏𝑓)𝑓_𝑡 = \sigma(𝑊_𝑓 [ℎ_{𝑡−1}, 𝑥_𝑡] + 𝑏_𝑓)로 표현된다. 이 Gate은 0~1의 값을 가진다. Cell State와 곱연산을 통해 Cell State의 값을 줄이는 역할을 수행한다.

Forget Gate는 Sigmoid를 통과한다.

Input Gate는 it=σ(Wi[ht1,xt]+bi)i_t=\sigma(W_i[h_{t−1},x_t]+b_i)로 표현된다. 이 Gate도 0~1의 값을 가지며, Gate Gate와의 곱 연산을 통해 최종적으로 추가될 값을 제어한다.

Gate Gate는 C~t=tanh(WC[ht1,xt]+bC)\tilde C_t = tanh(W_C[h_{t-1},x_t] + b_C)로 표현된다. Cell State에 추가될 값을 증폭시켜서 저장하고 있다.

해당 Time Step의 C는 Ct=ftCt1+itC~tC_t = f_t \cdot C_{t-1} + i_t \cdot \tilde C_t로 계산된다.

두 Gate를 통해 Cell State가 CtC_t로 업데이트 된다.

Output Gate는 ot=σ(Wo[ht1,xt]+bo)o_t = \sigma(W_o [h_{t-1}, x_t] + b_o)로 표현된다. Time Step의 Input의 강도를 조절하는 역할을 수행한다.

해당 Time Step의 Hidden State는 ht=ottanh(Ct)h_t = o_t \cdot tanh(C_t)로 계산되어, 출력 및 다음 Cell로 전달된다.

Time Step에서의 Hidden State 계산

Gated Recurrent Unit (GRU)

GRU는 LSTM의 구조를 경량화 한 모델이다. 더 적은 메모리 사용과 연산이 이루어지며, LSTM과 유사한 성능을 내는 것으로 알려져 있다.

GRU는 LSTM의 Cell State와 Hidden State를 한 개의 Hidden State로 통합하였다. 또한, Forget Gate를 없애고, 이를 다른 방법으로 구현하였다.

GRU의 Cell 예시

GRU의 요소는 다음과 같다.

  • ztz_t : LSTM의 Input Gate와 유사한 역할을 한다.
  • rtr_t : LSTM의 Output Gate와 유사한 역할을 한다.
  • h~t\tilde h_t : 해당 Time Step의 Input의 강도로 조절된 값이다.
  • hth_t : 해당 Time Step의 Output이다.

최종 Output인 hth_t의 계산식은 ht=(1zt)ht1+zth~th_t = (1-z_t) \cdot h_{t-1} + z_t \cdot \tilde h_t 이다. 여기에서 (1zt)(1-z_t) Term이 가중 평균의 Idea를 차용하여 LSTM의 Forget Gate의 역할을 수행하는 것을 알 수 있다.

실습

GitHub Gist Link


Reference

The Unreasonable Effectiveness of Recurrent Neural Networks

CS231n Lecture 10 : RNN

Explain about LSTM

'네이버 부스트캠프 AI Tech' 카테고리의 다른 글

[U] Day 19 - NLP IV  (0) 2021.03.26
[U] Day 18 - NLP III  (0) 2021.03.26
[U] Day 16 - NLP I  (0) 2021.03.26
[U] Day 15 - Generative Model  (0) 2021.03.26
[U] Day 14 - Recurrent Neural Network  (0) 2021.03.26

+ Recent posts