[CS231n] 10.RNN, LSTM

Updated:

RNNPermalink

지금까지 봐온 일반적인 one-to-one 네트워크와는 달리 RNN은 output이나 input이 여러개로 sequence를 이루는 경우가 있다.
one-to-many는 image captioning으로 예를 들 수 있다. 이는 이미지 하나를 입력 받고 이미지에 대한 설명을 출력으로 내는 것이다.
many-to-one의 경우는 Sentiment Classification으로 예를 들 수 있다. 이는 감정을 분류해 내는 것으로 단어들로 구성된 시퀀스(티위터 메시지, 편지 등)을 입력받고 이 글에서 나오는 감정이 positive냐 negative냐 등을 분류한다.
many-to-many는 Machine Translation으로 예를 들 수 있다. 예를 들어 영어 단어로 구성된 문장을 입력받으면 이를 한국어로 번역해주는 것이다.
many-to-many의 또 다른 예는 Video classification on frame level이라는 것으로 모든 하나하나의 프레임들을 classify하여 예측이 현재 시점의 프레임에서 국한된 것이 아니라 현재까지 지나온 모든 비디오의 프레임을 기반으로 예측을 한다.

위 이미지는 fixed input을 Sequential하게 처리한 경우이다. 이는 CNN으로 이미지를 받아서 집 번지수를 classify한 것이 아니라 RNN을 이용해서 이미지 하나를 Sequential하게 훑어나간다. 반대로 fixed size의 output을 Sequential하게 처리해서 출력할 수도 있다. 번지 수로 예를들면 번지 수를 한번에 출력하는게 아니라 Sequential하게 사람이 글로 써내려가듯이 출력해준다. 이러한 예시들은 one to one의 경우에도 CNN이 아니라 RNN을 통해 분석할 수 있음을 보여준다.

RNN mechanismPermalink

RNN은 매 time step마다 input vector가 RNN으로 입력이 된다. RNN은 내부적으로 state를 가지게 되고 이 state를 함수로 변형해 줄 수 있다. 이 함수는 매 time step마다 input을 받는 것에 대한 함수이다. 이러한 RNN도 weight로 구성이 되며 weight들을 튜닝하면서 RNN을 학습시키게 된다. 이렇게 하므로써 우리가 얻는 값은 특정 time step에서의 값에 대한 예측값인 것이다. 그래서 위 사진과 같이 입력되는 vector x에 대해서 왼쪽과 같은 recurrence function을 적용할 수 있게 된다. 결론적으로 이 RNN이 우리가 원하는 특정 behavior를 가질 수 있도록 weight값들을 학습시켜 나가는 것이다.

주의할 점은 매 time step마다 동일한 함수와 동일한 파라미터 set이 사용돼야 한다는 것이다. 이렇게 해야 input sequence size와 output sequence size에 무관하게 적용이 가능하게 된다. 다시 말하면 input/output sequence size가 아무리 커도 상관이 없다는 것이다.

Vanilla RNNPermalink

recurrence function을 적용한 가장 간단한 사례가 Vanilla RNN이다. Vanilla RNN에서는 state가 단일의 hidden vector h로만 구성이 된다. Vanilla RNN에서의 state update는 두 번째 식과 같이 된다고 볼 수 있다. xt이 경우는 weight값이 x에서 hidden layer로 가는 Wxh에 영향을 받고, 직전의 상태 ht1의 경우 직전의 hidden layer와 현재의 hidden layer의 영향을 받게 된다. 즉 현재의 state ht는 과거의 상태와 새로운 input으로 바뀌는 것을 알 수 있다.

Character-level language model examplePermalink

Character들의 sequence를 feeding해주고 매 순간 RNN에게 다음 step에 올 Character를 예측하도록 하는 예시이다.

RNN에 feeding 해주는 input은 one-hot encodeing 방식으로 넣어주고 우선 “hell”순으로 순차적으로 feeding해 준다. hidden layer는 임의의 3개의 뉴런으로 구성된다 가정을 했다. 이전의 hidden layer는 다음의 hidden layer에 영향을 주고 이를 Whh라고 표현한다. 그리고 input layer에서 hidden layer로 영향을 주는 것을 Wxh로 표현하였다.

output layer로 나오는 결과를 보면 우리가 원하는 결과는 “ello”가 나와야 하는데 RNN이 정답값으로 예측한 값을 보면 첫 번째의 경우 ‘o’를 4.1로 가장 높게 잘못예측하였다. 그래서 이러한 값들을 정답값과 비교를 하여 loss를 구하고 다시 input layer 방향으로 역전파를 한다. 이런식으로 가중치를 조정하여 학습을 하게 되고 각각의 time step에는 softmax classifier로 loss를 구하게 된다.

그리고 또 살펴볼 점은 앞서 말했듯이 매 time step마다 동일한 함수와 동일한 파라미터 set이 사용된다고 했으므로 사진에서 보이는 각각의 Wxh,Whh,Why들은 동일한 것이라 볼 수 있다.

Image CaptioningPermalink

RNN을 잘 활용한 또 하나의 예는 Image Captioning이다.

Image Captioning은 두 개의 Neural Network로 이루어져 있는데, 첫 번째는 이미지를 처리하는 CNN 두 번째는 sequence를 처리하는 RNN이다. 화살표대로 CNN의 결과를 다시 RNN의 입력으로 넣게 된다.

과정을 자세히 보면 test image가 CNN으로 들어가는데 CNN에서는 FC-1000과 softmax 이 두 개의 layer를 없애버린다. 그리고 이 없앤 부분을 RNN으로 보내버린다. RNN에서는 start라는 벡터를 처음 만들어주는데 이게 하는 역할은 RNN에게 sequence가 시작된다는 것을 알려준다. 이는 항상 최초 iteration 시에 넣어준다. start 다음단계는 이전에 공부한 방법과는 다르게 Wih * v가 추가된다. Wih의 ih는 image to hidden이고 이를 top of the conv인 V와 곱해준 값이다. 이것이 하는 역할은 이미지를 예로 들면 할아버지가 밀짚모자를 쓰고 있는데 CNN이 이러한 밀짚모자의 texture를 인식하게 되고 FC layer에서 Wih를 통해 h0에서 밀짚의 확률이 높아지도록, 결국엔 h0에서 y0으로 전달될 때 밀짚에 괸련된 확률이 높아지는 결과를 가져온다.

그래서 이렇게 밀짚모자(straw)의 확률이 높아지면 straw라는 단어가 다음 단의 input을 쓰이게 된다. 그리고 그 다음 단에서는 h1, y1으로 전달이 되는데 CNN에서 이제 straw말고 hat으로 인식을 하면 또 다시 CNN에서 RNN으로 정보가 넘어와 영향을 주게 되고 그 영향으로 hat이 input으로 들어오게 된다. 이러한 과정이 반복되어 최종적으로 토큰이 오면 끝이나게 된다.

이러한 Image Captioning에서 CNN과 RNN은 각각이 따로 동작하는게 아니라 하나의 네트워크 처럼 동작하기 때문에 역전파도 한꺼번에 진행된다. Image Captioning에 대한 결과 예시는 아래와 같다. 정확히 맞추는 것도 있지만 잘못 판단하는 것도 몇몇가지 보인다.

RNN 이후의 기술이라고 하면 Attention이라고 할 수 있다. RNN은 이미지를 전체적으로 한 번만 보고 끝나지만 Attention은 이미지 특정 부분을 보고 그 부분에 적합한 단어를 추출한다. 또 이미지 다른 부분을 보고 그에 맞는 적합한 단어를 추출한다.

이런 식으로 이미지의 부분부분을 보면서 문장을 추출해 낸다. 단지 단어를 생성하는 것 뿐만 아니라 다음에 어디를 봐야하는지도 알려준다고 한다.

LSTMPermalink

오른쪽을 보면 앞서 RNN에서 설명했듯이 같은 depth끼리는 파라미터가 같고 다른 depth끼리는 다르다. 실제 현업에서 많이 쓰이는 것은 LSTM이라고 할 수 있다. LSTM의 기본적인 원리는 RNN과 동일하다. RNN과의 차이는 hidden state 뿐만아니라 그림에서 노란색으로 표현된 cell state도 함께 존재한다는 것이다. 그래서 single point에 hidden state와 cell state이렇게 2 개의 vector가 존재하는 것이다.

cell state vector는 그림의 왼쪽 아래에 나타나 있는데 각각에 cell에는 gate들이 있다. i(input), f(forget), o(output), g로 4 가지가 있고 i, f, o는 sigmoid로 처리하고 g는 tanh로 처리한다. f는 그아래 식에서 볼 수 있듯이 cell state를 c라 했을 때 현재의 cell state ct가 이전 cell state를 얼마나 잊을 것인가를 정해주는 것이다. 그래서 forget gate가 만약에 1이라면 이전 state 전체를 전달하고 0이면 reset을 하게 된다. 그리고 그 뒤에 term을 보면 input은 sigmoid여서 0 ~ 1이고 tanh가 -1 ~ 1이므로 input값을 cell state에 포함시킬 것인가를 i가 정하고 g는 이러한 i를 cell state에 얼마나 더 더해줄 것인가를 정한다. 그래서 g는 음수가 될 수도 있고 양수가 될 수도 있는 것이다.

이렇게 현재의 cell state를 구하고 이를 tanh를 적용한뒤 output에 반영하여 hidden state vector를 구하게 된다. output gate는 다르게 생각하면 현재의 cell state의 어느 부분을 다음의 hidden cell로 전달할지 결정하는 것이라 할 수 있다. 그래서 그림에서 노란색 부분인 cell state를 구한 다음에 이 구한 값을 연두색 hidden state부분으로 전달하여 hidden state vector를 구하게 되는 것이다.
정리하면 아래 사진과 같다.

hidden state는 위 사진과 같이 2갈래 방향으로 가는데 하나는 상위 layer 혹은 마지막 prediction 단계로 가게 되고 다른 하나는 LSTM의 다음 iteration으로 가게 된다.

RNN은 현업에서 잘 쓰이지 않는데 그 이유는 결과 값인 y의 변동이 매우 클뿐만 아니라 역전파도 vanishing gradient로 인해서 좋지 않다.

코드를 보면 역전파에서 50번 반복하는 동안 맨 마지막 줄에 Whh에 같은 값을 계속 곱하게 되는데, 이렇게 되면 값이 explode하거나 vanish하는 것 둘 중 하나이다. 그래서 언제나 vanish위기에 처할 수 있는 것이다.

그래서 이러한 문제를 방지하는 방법은 gradient clipping을 해주는 것이다. 예를 들면 -5 ~ 5 범위를 넘어선다고 하면 그 안으로 값을 짤라주는 것이다. 그런데 이렇게 하는데도 vanishing gradient 문제를 막기가 쉽지 않기 때문에 일반적으로 LSTM을 많이 사용한다.

LSTM을 보면 RNN과는 다르게 앞서 보았듯이 + 연산이 있다. 이 +연산은 distributor역할을 해주기 때문에 gradient를 그냥 전달하게 된다. 이렇게 하다 보니 vanishing gradient가 발생하지 않는다는 것이다. 하지만 forget gate의 경우 cell state를 잊어버리기 위해서 0을 줘버리면 gradient의 진행이 멈춰버릴 수도 있으므로 이를 방지하기 위해 forget gate에 0이 되지 않도록 하는 bias를 주게 된다.

ReferencePermalink

https://www.youtube.com/watch?v=2ngo9-YCxzY&t=213s

Leave a comment