Recurrent Neural Networks(RNN)

Sequence Model-RNN

Recurrent Neural Network에 대한 기본적인 개념


Notation

간단한 예로 문장 x를 입력받고 그 문장 안에서 이름의 위치 y를 특정해주는 모델이 있다고 하자.

x : Harry potter and Hermione Granger invented a new spell.

이라는 문장이 들어오면, 단어 마다 1개의 출력값 y를 가지며 각 단어가 이름에 포함되는지 알려준다.

y: 1 1 0 1 1 0 0 0 0

물론 더욱 뛰어난 표현법은 아니지만, 간단한 예제로 사용해보자.

그리고 각 입 출력의 단어의 위치 순서를 표시하기 위해 ^< i > 의 notation을 사용한다.

예를들어 Harry = x^<1>, potter = x^<2>... 1=y^<1> ..식으로 적용한다.

앞에서 배운 notation (i)를 함께 쓰면 x^(i)< t >는 i번째 example의 t번째 순서를 나타낼 것이다.

시퀀스의 길이는 모두 다를 수 있기 때문에 Tx^(i), Ty^(i) 는 i번째 example의 시퀀스 길이를 나타낸다.


문장 속의 단어를 표현하기 위해서는 우선 어휘 집합(Vocabulary)를 만드는 것이다.

Vocab을 구성하는 방법에는 Training set를 스캔하며 가장 자주 사용된 단어순으로 정렬하여 일정한 갯수를 정해 만들 수 있다. 혹은 인터넷 영어 사전같은 곳에서 가장 많이 사용된 단어를 추출하여 구성할수도 있을것이다.

vocab

위의 vocab은 1번째인 a부터 4075번째 harry ... 10000번째 zulu까지 들어있는 10000개의 vocab이다.

vocab을 구성한 후에는 단어를 one-hot vector로 표현한다.

예제 문장 x : Harry potter and Hermione Granger invented a new spell. 에서 harry는 4075번째 빼고는 모두 0인 (10000,1) vector로 표현할 수 있다.

이런식으로 표현된 X 를 목표 출력인 y로 Mapping하는 시퀀스 모델을 학습하는것이 모델의 목표이다.


RNN

이러한 Data를 우리가 사용해왔던 일반적인 신경망을 통해 학습하려면 잘 되지 않을 것이다.

우선 각각의 Input example은 서로 다른 길이를 가지고 있다. 또한 학습된 feature가 문장의 다른위치로는 공유되지 않는다.

예를들면 첫번째 example의 x^<1>의 Harry에서 이름의 특성이 강하게 표현된다면, 우리는 x^<4>에 Harry가 나와도 그 특성이 적용되길 원한다. 하지만 그렇지 않다.

또한 앞선 예시에서 각각의 input 은 10000 dimension을 가지기 때문에 첫번째 layer는 매우 큰 수의 parameter를 가지게 된다.

이런 단점들을 해결해 줄 수 있는게 바로 Recurrent Neural Network (RNN)이다.

rnn

example 문장의 단어를 차례대로 신경망에 넣고 예측한다. 그 다음 단어도 마찬가지로 넣는데, 이 때 앞 단어의 activation value를 전달받아 결과 도출에 사용한다. 해당 과정을 x_Tx까지 반복한다.

(X^<1>은 앞의 단어가 없으므로 a<0>을 보통 0으로 초기화한다.)

이러한 RNN의 구조에는 약점이 존재하는데, 바로 뒤쪽의 정보는 참조할 수 없다는 것이다.

He said, "Teddy Roosevelt was great President"

He said, "Teddy bears are on sale "

위의 두 문장을 봤을때, Teddy의 앞 두단어만 가지고는 Teddy가 사람 이름인지 곰돌이인지 구분이 불가능한 맥락과 같다. (해당 개선 방안은 추후에 다룬다.)


RNN Forward Propagation

rnnweight

Input을 Hidden layer로 매개하는 Weight를 Wxa

Layer간의 연결을 매개하는 Weight를 Waa

Output prediction을 매개하는 weight를 Wya 라고한다.

RNN을 forward propagation 하는 식을 나타내면

rnnforward

로 나타낼 수 있을 것이다. 추가적으로 위의 식을 더 간단하게 다음과 같이 공식화 할 수 있다.

  1. Waa, Wax를 수평적으로 쌓은 벡터[Waa : Wax]를 Wa라고 하자. 예를들어 Waa가 (100,100) 이고 Wax가 (100,10000)이면 Wa 는 (100,10100) shape를 가질 것이다.
  2. a^< t-1 >, x^< t >를 수직으로 쌓은 벡터 [a^< t-1 >, x^< t >] 는 10100 dimension일 것이다.
  3. 마찬가지로 Wya 를 Wy라고 표현

위의 변환 과정을 거치면

foward2

와 같이 표현할 수 있다.

activation function g는 대체적으로 tanh를 많이 쓴다.


RNN Back Propagation

Back Propagation 도 기존의 과정에서 크게 벗어나지 않는다.

우선 time step별 각 단어의 입력에 대하여 Loss function을 구한뒤, 모든 y에 대한 Loss fucntion의 합을 구한다.

여러가지 방법이 있을 수 있지만, 우리가 기존에 많이 사용했던 Cross entropy를 사용해 구해보면

backprop

다음과 같이 구할 수 있다.


Different Types of RNNs

앞서 봤던 예제에서는 Input의 길이 Tx와 output의 길이 Ty가 같았다.

하지만 RNN을 어디에 활용하느냐에 따라 Tx와 Ty의 길이가 항상 같지 않다.

diffrnns

예를들어 음악을 생성해주는 모델같은 경우, Input이 하나의 음이거나 Empty set일 수 있다. 또는 영화의 감상평을 통해 평점을 추론하는 모델의 경우, 문장이 Input이고 Output은 1~5사이의 정수일 것이다.

이러한 Input과 Output의 관계에 따라

Many to One, Many to Many, One to One, One to Many 등으로 부를 수 있다.

Many to Many의 또 다른유형 중 하나로는 번역기를 들 수 있다. Many to Many의 구조지만 입력과 출력 길이가 다르며, 모델은 크게 Encoder와 Decoder라는 두 부분으로 이루어 져있다.(이미지의 우하단)

rnns


Language Model and Sequence Generation

Language modelling은 NLP(natural language processing)에 있어서 가장 기본적이고 중요한 요소다.

음성 인식 모델이 있다고 가정하고, "The apple and pear salad" 라는 말을 했을 때 모델은 같은 발음을 가지고 잇는 "The apple and pair salad"라고 들을 수 있다.

우리는 문맥의 흐름 상 당연히 Pear가 맞다는것을 금방 알아채지만, 음성 인식 모델을 어떻게 이 차이를 알 수 있을까?

이러한 상황에서 문장을 분석하고 각각의 가능성을 분석해 확률을 추측하는 역할을 하는것이 바로 'Language Model' 이다.

Language Model은 문장을 y^<1>,y^<2>...등의 Sequence로 받아 확률을 추측해준다.


Language Model을 만드려면 해당 언어의 큰 Corpus(말뭉치) 가 있는 Training set 가 필요하다.

Training set의 한 문장이 "Cat average 15 hours of sleep a day"라고 해보자.

처음 해야 할 작업은 문장을 Tokenize(토큰화) 하는 것이다.

문장의 끝을 알려주는 < EOS > 을 끝에 붙여주고 각 단어를 가지고 있는 vocabulary 와 one-hot vector로 Mapping한다. (one-hot vector 이외에도 여러가지 방법이 있으며, 추후에 소개한다.)

만약 갖고 있는 vocabulary에 없는 단어가 나오면 그 단어 대신 < UNK >를사용한다.

Tokenize가 끝났으면 신경망에 차례로 넣어 학습한다. 신경망은 현재 timestep 전까지의 문장 구성에 대해 다음에 올 수 있는 단어의 확률을 예측한다.

lmtrain

우선 첫번째 입력은 a와 x모두 0벡터를 입력한다. 그럼 Output인 y_hat은 vocabulary 안의 모든 단어에 대해 첫번째 단어로 올 확률을 계산한다.

이어서 cats 가 들어오면, cats 다음에 들어올 단어들에 대한 확률 p( __ | cats) 를 예측하고 , 그 다음은 p( __ | cats average)를 계산하고 ... 마지막 < eos >까지 같은 과정을 반복한다.

위와 같은 예측 과정처럼 Language model을 학습을 할 때에 t번째 step의 y_hat을 t+1 step의 입력으로 주지 않고, 대신 실제 정답을 넘겨주는 것을 teacher forcing이라고 부른다.


Sampling Novel Sequences

Language model을 학습할 때에는 해당 time step의 정답을 input으로 주어 학습시켰다.

모델에서 임의의 문장을 Sampling하는것도 위의 과정과 비슷하지만, 다른점이 있다. t 번째 timestep의 input으로 정답을 주는것이 아닌 t-1번째 step에서 계산된 y_hat을 넘겨준다. 그럼 softmax에 의해 그 다음으로 올만한 단어가 무엇인지 예측할 것이다.

해당 과정을 < eos >가 나올때 까지 반복하거나 정해진 숫자만큼 진행시키면 문장을 Sampling 할 수 있다.

Sampling model은 어떤 Training data(말뭉치)로 학습을 시켰느냐에 따라 만들어내는 문장의 어체도 다를것이고, 내용에도 차이가 있을 것이다.

추가적으로, 우리가 단어 단위로 모델을 학습시키고 문장을 만든 것 처럼, 문자(chararcter) 단위로도 같은 작업을 할 수 있다.

Character 단위로 language model을 구성하면 Unknown word에 대한 문제가 없어진다. 하지만 그만큼 sequence가 길어지고 그에 따른 계산 시간이 증가한다.


Gradients with RNNs

RNN에서도 Vanishing/Exploding gradients는 피해갈 수 없다. 특히 문장이 길어질수록 time step이 더 깊어지는 신경망이 되므로 이러한 문제가 더 심해진다.

문장의 앞쪽에 나온 단수/복수 명사에 따라 알맞은 형태의 동사를 생성하려면 그 중간에 추가적으로 나오는 단어들이 아무리 길어져도 잘 기억해야 할 것이다.

하지만 RNN의 특성상 local influence (가까이 있는것에 영향을 많이 받음)이 많기 때문에 뒤쪽step의 error가 신경망 전체에 걸쳐 앞쪽 step에게 영향을 주기 어렵다.

따라서 Gradient가 exploding 되는것을 조심해야할 것이다. exploding같은 경우는 parameter가 매우 커지기 때문에 NaN을 출력하거나 overflow가 나면 쉽게 감지할 수 있으면 gradient clipping을 적용시킬 수 있다. gradient vector가 한계점보다 크다면 일부를 다시 조절해주는 작업이다.

그와 달리 gradient가 vanishing 하는것을 해결하는건 쉽지않고, 더 복잡한 과정을 거쳐야한다.


Gated Recurrent Unit(GRU)

"The cat, which already ate ...., was full" 이라는 단어에서 우리는 cat에 따른 동사 was를 출력하기 위해 cat을 특별히 기억할 필요가 있다.

이를 위해 c 라는 memory cell 역할을 하는 변수를 만들어 문장의 주제가 복수인지 단수인지 기억한다.

gru

이미지로 표현하면 아래와 같은 연속적인 작업이 진행될 것이다.

gruimg


LSTM(Long Short Term Memory)

LSTM은 GRU와 비슷하지만, update 게이트, forgot 게이트, output 게이트 의 총 3개의 게이트를 사용하여 판단한다.

lstm

LSTM은 GRU보다 역사적으로 더 오래되었고, 검증된 알고리즘이다.

두 알고리즘 중 무엇이 더 우수하다는 정확한 척도는 없지만, 보다 간단한 GRU와 3개의 게이트를 사용해 보다 견고한 LSTM중 설계하는 모델의 특성에 따라서 선택하여 사용하면 된다.


Bidirectional RNN

기존의 RNN에는 앞서 말했던 것 처럼 현재 시점 이후의 정보는 참조할 수 없다는 약점이 있었다. 이러한 단점을 보완하기 위해 만들어진 것이 Bidirectional RNN이다.

개념 자체는 매우 단순하다. 주어진 입력들에 대해서 끝까지 foward 방향으로 활성화 함수를 계산한 뒤, 끝에서부터 다시 해당 지점까지로 backward로 활성화 함수를 계산한 다음 그 두개의 정보를 합쳐서 y_hat을 계산한다.

해당 과정을 식으로 나타내면 다음과 같다.

birnn


Deep RNNs

이때까지 배운 개념들을 합쳐서 더 잘 작동하는 Deep RNN 모델을 구축할 수 있다.

우리가 앞서 배운 신경망에서 여러겹의 Layer를 쌓은것 처럼 RNN에도 여러겹의 Hidden Layer를 쌓을 수 있다.

multi

여기서 notation [i]는 layer를 나타내고 < j >는 time step을 나타낸다.

특정 레이어 i에서 j번째 step의 activation을 계산하려면 같은 레이어 i 에서 j-1번째 step의 input과, 자신의 밑 레이어인 i-1레이어에서 온 input을 가지고 위의 보라색 식과 같이 계산하면 된다.

주의해야할 점은, RNN은 sequence data를 처리하기 때문에 문장의 길이에 따라서 계산비용이 매우 커질 수 있다. 따라서 이전의 신경망들 처럼 매우 많은 수의 layer를 쌓기에는 무리가 있다.

이에 대한 방안으로 적당한 layer를 쌓은 후, 특정 레이어 부터 결과를 도출하는 레이어 까지는 각각의 time step끼리 연결되지 않은 심층 신경망을 구성하여 해결할 수 있다.

연관글