이번 포스트는 cs231n 10강 리뷰입니다. RNN(Recurrent Neural Network)의 기존 구조부터 backprop시 문제를 해결하기 위해 나온 LSTM에 대한 내용입니다.

들어가기 앞서,,,

가정해봅시다. 특정한 날에 저녁으로 저는 중국집에 전화를합니다.

중국집 배달 규칙 = 짜장 짬뽕 볶음밥 순으로 시킵니다. 저번주에 짬뽕을 시켰다면 오늘은 볶음밥을 시켜야 할테고 저번 주에 짜장을 시켜먹었다면 오늘은 짬뽕을 시켜먹는 날이 될 것입니다.

여기서 한 가지 더 나아가보자면 2일 후으로 넘어가는 것입니다.

중식배달규칙

2일 전에는 짜장을 먹었습니다. 그럼 하루 전에는 짬뽕을 먹어야한다고 예측 할 테고 오늘은 볶음밥을 먹어야한다고 예측할 것입니다.

왜냐면 저희의 중국집 배달 규칙은 그대로 이기 때문입니다.

컴퓨터에서 오늘 볶음밥을 먹어야한다는 것을 예측한 것을 one_hot vector로 표현하자면 [0,0,1]이 됩니다.


저희가 지금 까지 배워왔던 여러 가지 구조들이 있지만 공통적인 것이 하나 있습니다. 하나의 입력을 받아 하나의 출력을 내보냈습니다.(좌측 one-to-one)
RNN은 입력값이 여러 개 일수도 있고 출력값이 여러 개 일 수도 있습니다. 즉, RNN은시퀀스 길이에 관계없이 인풋과 아웃풋을 받아들일 수 있는 네트워크 구조며, 필요에 따라 다양하고 유연하게 구조를 만들 수 있습니다.
먼저 활용 예시를 보고 RNN에 대해서 말씀드리겠습니다.

  1. One to Many: Image Captioning(Image 1개로 문장 출력)
  2. Many to One: Sentiment Classification(감정 분석)
  3. Many to Many1: Machine Translation이나 (기계 번역)
  4. Many to Many2: Video classification on frame level(프레임 레벨에서의 영상분류)

문장이나 단어들은 전부 길이가 다르고, 영상 데이터는 프레임의 연속이기에 시퀀스가 제각각입니다..
RNN은 이렇게 입/출력이 가변길이를 지닌 데이터에 강점이 있습니다.

또한 입/출력이 고정된 길이라 해도 ‘가변 과정(Processing)’인 경우에 매우 유용합니다.


  • h_t: 현재 State
  • f_w: 가중치 벡터가 있는 함수
  • h_t-1: 이전 State
  • x_t: input

이전 state와 input를 활용해서 현재 state를 업데이트 시킨다.

Notice: set의 parameter와 가중치는 모든 time step에서 똑같은 것을 반복해서 사용합니다.

이제 수식을 통해 Vanlila RNN을 보겠습니다. Weight는 총 3개입니다.

  • W_hh: 이전스테이트에 사용되는 가중치
  • W_xh: input에 사용되는 가중치
  • W_hy: RNN cell에서 y로 넘어갈 때 사용되는 가중치

아까 말씀드렸듯이 이 가중치는 변하지 않고 사용됩니다.
비선형성(non-linearity)을 위해 tanh 함수를 사용한다

방금 봤던 Vanila RNN을 옆으로 쭉 이어붙이는 모습입니다. 이렇게 그리면 공간을 너무 차지하니까 수식에서 봤던 되돌리기 표시를 사용합니다.

각각의 히든레이어에서 어떤 작업을 하고 있는지 확인해 본 결과입니다. ""를 학습하기도하고 단어의 개수를 세고 코드인 경우에는 if문, for문을 학습합니다.

Image Captioning

Image Captioning이란 하나의 사진을 보고 이 사진이 무엇인지 설명하는 문장을 만들어 내는 딥러닝 모델을 말한다.

CNN은 요약된 이미지정보가 들어있는 Vector를 출력합니다 이 Vector가 RNN의 초기 Step의 입력으로 들어가게 됩니다
그러면 RNN은 Caption에 사용할 문자들을 하나씩 만들어냅니다

Test time에 어떻게 동작하는지 차근차근 살펴보겠습니다.

CNN의 구조에서 최종 출력 layer를 제거하여, 직전의 FC layer(4,096-dim Vector)에서 이미지의 vector값만을 가져옵니다.

이 Vector에는 전체 이미지 정보를 요약되어 있습니다

RNN Language Models에서 배웠듯이 문장을 생성해 내기에 앞서 초기 값을 넣어줘야합니다.

이제 모델에게 말하세요
“이미지 줬으니까 조건에 맞느 문장 만들어줘!”

이전에는 h_t를 구하기 위해 h_t-1과 x_t만 있었으면 됐지만 여기서 이미지 정보 W_ih * v가 추가됩니다.
기억나실지 모르겠지만 W와 set of parameters는 매 step 같은 것을 사용합니다.

이제 vocabulary의 모든 스코어들에 대한 분포를 계산할 차례입니다.(그림[1])
vocabulary=’모든 영어 단어들’

  1. 단어장 분포에서 샘플링
  2. 샘플링된 단어를 다음 스텝의 입력으로 다시 넣어줄 것

샘플링된 단어(y0)가 들어가면 다시 vocab에 대한 분포 추정 다음 단어 생성 무한 반복…

다 생성된 이후에 END 라는 특별한 토큰으로 문장의 끝을 알려줍니다.(그림[3])
END가 샘플링되면 더 이상 단어 생성을 멈추고 이미지에 대한 caption이 완성됩니다.
Train time에는 종료지점에 END 토큰을 삽입합니다.
Test time에는 모델이 문장 생성을 끝마치면 END 토큰을 샘플링합니다.

결과

Image Attention

Caption을 진행할 때 좀 더 집중해서 보는 것입니다.

그림[1] Basic: CNN으로 벡터 하나가 아닌 공간정보를 가진 Grid of Vector를 만듭니다(LxD)

그림[2] 매 스텝 vocab에서 샘플링할 때 모델이 이미지에서 보고싶은 위치에 대한 분포도 만듭니다. 이 각 위치에 대한 분포는 train time에 모델이 어딜봐야하는지에 대한 attention이라 할 수 있습니다. h0은 이미지 위치에 대한 분포 계산 LxD(벡터 집합)과 연산 하여 z1(이미지 attention)을 생성 요약된 z1은 다음 스텝의 입력으로 들어감

그림[3] 출력 2개 1.vocab 분포(d1) 이미지 위치 분포(a2)

그림[4] 매 스텝마다 값(an, dn)이 계속 생성

Train을 끝마치면 모델이 caption 생성을 위해 이미지의 attention을 이동시키는 모습을 볼 수 있다.

지금까진 1 layer RNN 이였지만 모델이 깊어질수록 다양한 문제에서 성능이 좋아지기 때문에 좀 더 깊게 쌓게 됩니다. 일반적으로3~4 layer RNN을 사용합니다.

RNN의 문제점

RNN은 이전 state와 입력값을 stack해줍니다.

그렇게하면 backward시에 tanh함수를 지나쳐 mul게이트에서 가중치 행렬을 다시 곱해주게 됩니다.

cell을 지날때마다 가중치를 계속 곱해줍니다.

RNN 특성상 여러 시퀀스의 Cell을 stack한다는 것을 고려한다면 곱해지는 값 1보다 크면 점점 커질 것이고 1보다 작으면 점점 작아져 0이 될 것입니다.

value > 1: gradient clipping은 휴리스틱한 기법중 하나입니다. gradient를 계산하고 gradient의 L2 norm이 임계값보다 큰 경우 최대 임계값을 넘지 못하도록 조정해줍니다. exploding에서는 효과적일 수 있다. value < 1: ==> LSTM

LSTM(Long Short Term Memory)

RNN의 Fancy version
여기에 C_t는 LSTM 내부에만 존재하며 밖에는 노출되지 않습니다.

Notice: 우측 수식을 봐주세요

LSTM도 똑같이 2개의 input
그리고 4개의 gates 계산

  • input gate: cell 에서의 입력 x_t에 대한 가중치
  • forget gate: 이전 스텝을 얼만큼 까먹을건가
  • gate gate: 얼만큼..input cell에 포함시킬건가
  • output gate: 얼만큼 내보낼건가

이 4개의 gate는 C_t(cell states)를 업데이트하는데 이용합니다.
그리고 c_t로 다음 스텝의 hidden state를 업데이트 합니다.
(W가 하나처럼 보이지만 4개짜리고 각각 들어갑니다.

여기서 중요한 점은 gate에서 사용하는 non-linearity가 각양각생인 점
i,f,o = sigmoid = 0 ~ 1사이
g = tanh= -1 ~ 1 사이

이 구조가 더 이치에 맞는 이유는 binary라고 생각했을 때 위 3개는 0또는 1 값을 가집니다. 만약 f가 0이면 이전 cell state를 잊습니다. 반대로 1이면 cell state를 전부 기억합니다. i는 cell state의 각가에 대해 사용하고 싶으면 1 버리고 싶으면 0이 됩니다. gsms tanh는 -1또는 1이됩니다. c_t는 현재 스텝에서 사용될 수도 있는 후보라고 할 수 있습니다.

ct는 2개로 나눌수 있습니다
f * ct
1: 이전 state를 얼마나 잊을 것인가
i * g: 현재 state를 얼마나 사용할 것인가

h_t를 계산하는데 이 값은 밖에 보입니다.

LSTM Forward pass

LSTM Backward pass

사실 이 부분을 전부 이해하지 못했습니다.. 그래서 일단 적기라도..

backporp를 하게되면 +연산은 두갈래로 나뉘어지고 gradient는 upstream gradient와 forget gate의 각 행렬 원소 곱입니다.
결국 Cell state의 backprop는 upstream gradient* forget gate입니다
기본 RNN보다 훨씬 간단해 졌습니다.

좋은점1: forget gate와 곱해지는 연산이 matrix multiplication이 아닌 element-wise라는 점 후자가 더 나음
좋은점2: 후자이기 때문에 다른 값의 forget gate와 곱해질 수 있다 RNN은 항상 같은 값을 곱했었죠?
또한 forget gate는 sigmoid이기 때문에 element wise multiply가 0~1사이의 값입니다.
RNN처럼 tanh를 매스텝 거치는게 아니라 한 번만 거치면 된다 y_t에서부터

질문: 가중치 W는 어떻게 학습하나요? Local gradient는 해당 스텝에 해당하는 현재의 cell/Hidden state로부터 전달됩니다. LSTM의 경우 cell state c가 gradient를 잘 전달해주기 때문에 w에 대한 local gradient도 훨씬 깔끔하게 전달됩니다.

질문: 여전히 sigmoid때문에 vanishing gradient 문제에 민감할 수 있나요? 물론 그럴수도 있다 forget gate의 경우 항상 1보다 작으니까 점점 감소할 것이다. 그래서 trick으로 forget gate의 양수의 biases를 추가한다 1에 가깝기 때문에 초기에 잘 될것 아무튼 RNN보단 덜 위험

Resnet과 비슷합니다. ResNet에선 Identity Mapping이 고속도로 역할을 했습니다. LSTM의 경우에는 Cell state의 element-wise multiply가 gradient를 위한 고속도로 역할을 합니다.