이러한 feed-forward network는 single-input(like single-image)을 받아 여러 형태의 hidden layer들을 거쳐 single-output을 내보내는 형태이고, 이러한 형태의 classical한 예로는 single-image를 input으로 받아 category label을 output으로 내보내는 image classification 이 있다.
우리는 deep neural network를 이용하여 feed-forward network형태 말고도 다른 많은 타입의 problem들을 해결하고자 한다.
첫번째 예로는 앞에서 본 one-to-one 형태가 아닌 one-to-many형태가 있다.
One-to-many는 single-input(image)를 입력으로받아 output을 더이상 lable형태가 아닌 sequence형태로 내보내는 것 이며, 한 예로 single image를 input으로 읽어들여 image 내용을 설명하는 sequence of words를 output으로 내보내는 Image Captioning 이 있다.
위 그림처럼 input이 sequence이고 output도 sequence인 many-to-many 형태에서는 왼쪽 그림처럼 input sequence와 output sequence의 길이가 다른 경우인 Machine Translation 형태를 볼 수 있으며 이를 sequence-to-sequence problem이라고도 한다.
또다른 sequence 2 sequence problem에서는 many-to-one과 다르게 video frame별 classification label을 결정하여 첫 3개의 frame에서는 드리블치는 사람 다음 몇 frame에서는 슛하는 사람 과 같이 sequence of images를 input으로 받아 sequence of labels을 출력으로 보내는 Per-frame video classification 을 한 예로 들 수 있다.
우리는 neural network를 one-to-one 형태가 아닌 seq-to-seq 형태로 만들어 사용하여 훨씬 더 일반적이고 다양한 문제들을 다룰 수 있으며 seq-to-seq process의 general tool중 하나인 Recurrent Neural Network 를 이번강의에서 다룬다.
Recurrent Neural Network 는 Non-Sequential data에도 유용하게 사용되기도 한다.
위 그림은 Recurrent Neural Network 을 사용해 non-Sequential data를 가지고 sequential processing을 수행하도록 한 예시로 image classification problem에 feed-forward network 대신 위 그림처럼 초록색 박스인 "multiple glimpses"를 취하는 neural network를 사용한다.
이러한 형태는 step마다 이미지의 한 부분(glimpses)을 보는 형태인데, 어느 부분을 볼건지는 이전 step에서 수행한 정보를 가지고 판단하여 최종적으로 network가 각 image에 대해 classification decision을 출력하는 형태이다.
Non-Sequential data를 가지고 sequential processing을 사용하는 또다른 예로 digits image를 생성하는 neural network가 있다.
이전 예시와 다르게 network는 매 step마다 output canvas의 어느 point에 그려야할지 무엇을 그려야할지 판단하게 되며 writing decisions는 이전 예시처럼 시간마다 이전 정보가 통합되어 사용된다.
Recurrent neural network의 architecture를 정의하기 위해 위 식의 보라생 박스인 recurrent fomula 를 사용한다.
위 그림의 식은 t-1 시점의 state와(초록박스) t시점의 input x를(빨간박스) 입력으로 받아 learnable weights W에 의존적인 function f(보라박스)를 통해
new state(파란박스)를 계산하게 되는 식이다.
이때 를 계산하기 위한 모든 time step에서 같은 function과 같은 weights matrix를 사용한다는 것인데, single weights matrix만을 갖게 하는 이러한 구조가 arbitrary length를 갖는 sequence를 처리할 수 있게 해준다. (*처음 들었을때는 뭔소린가 싶었지만 다시 생각해보니 당연한 소리다...)
RNN의 simplest version인 Vanilla RNN에서 output을 구하는 방식은 위 그림과 같다.
hidden state를 구하기위하여 과 에 각각 곱해지는 두개의 learnable matirx 가 있으며 두개의 행렬곱을 더해주어 non-linearlity인 tanh를 취해주게된다.
출력 를 구하기위해 에 또다른 weight matrix 를 곱해주게 된다.
RNN은 위 그림처럼 먼저 첫번째 hidden state인 를 0로 initilize하고 와 을 통해 을 출력하는 이러한 과정을 same function, same weight W 를통해 input으로 들어오는 sequence의 길이만큼 반복한다.
이때 backporp과정에서 W는 이전 (lec6. backprop) 강의에서 보았던 copy gate로 볼 수 있으며 이는 각 노드(여기선 )의 gradient를 summation 해주는 gradient adder로 볼 수 있다.
다시 말하지만 모든 time step in sequence에서 동일한 weight matrix를 사용하기 때문에 하나의 recurrent neural network에서 어떠한 길이의 sequence든 처리할 수 있게된다.
이러한 형태는 rnn의 기본 연산이며 이를통해 (one-to-many, many-to-one등)다른 유형의 sequential processing task에 적용시켜보자.
Many-to-many는 위 그림처럼 input을 sequence로 받아 각 sequence의 point(time step)마다 output을 출력하는 형태로 이전에 말했던 Per-frame video classification이 이러한 network 형태를 갖는다.
그림에는 나와있지 않지만 매 time step마다 output 를 출력하기 위해 weight matrix 가 사용되며 각 output 는 classification을 위해 loss function을 통해 time step별 loss인 가 계산되며 이를 다 더해주어 final loss를 계산하게 된다.
Many-to-one network는 위 그림과 같이 sequence를 input으로 받아 하나의 output을 출력하는 형태로 전체 image를 받아 single label을 출력하는 network로 Video classification이 이러한 형태의 network를 갖는다.
이때 final hidden state는 전체 input sequence에 영향을 받고 마지막에 하나의 classification decision을 위해 network에서 전체 sequece의 정보들을 encapsulate한 것으로 생각해 볼 수 있다.
위 그림의 RNN의 또다른 application중 하나로 seqence to seqence가 있는데 이는 machine translation 에서 사용된다.
Seq2seq network는 encoder 역할을 하는 many-to-one network에서 sequence를 input으로 받아 decoder 역할을하는 one-to-many network로 feeding해주어 decoder에서 sequence를 output하는 형태이다.
이는 영어를 프랑스어로 번역하는 network로 보았을때 encoder에서 english sentence를 sequence로 입력받아 이를 요약한 형태인 하나의 hidden vector를 출력하고, decoder에선 encoder의 출력인 hidden single vector를 입력으로 받아 프랑스어 sentence를 output sequence로 출력하는 형태이다.
Encoder와 decoder로 network를 분리하여 두개의 weight matrix를 갖게한 이유는 input tokens과 output tokens의 length(num of wards)가 다를 것이기 때문이라고 한다.
이러한 seq2seq network가 어떻게 동작하는지 Language Modeling task의 예시를 통해 구체적으로 살펴보자.
RNN language model에서 long sequence를 학습하고자 할때 backporp에서 많은 메모리를 필요로한다는 단점이있다.
이러한 문제를 해결하기위해 Truncated backpropagation이라는 alternative approximate algorithmn을 사용한다.
위 그림처럼 전체 seqeunce를 학습하지 않고 sequence의 subset(chunk)으로 나누어 학습시키는 방식으로 각 chunk별 마지막 hidden state만 기억하여 다음 chunk로 전달하는 방식이다.
결국 각 chunk별 loss를통해 해당 chunk의 weight matrix를 학습하는 형태가 되는 것이다.
이와같이 각 chunk만큼의 메모리만 필요로 하기에 한정된 GPU memory 에서도 학습을 수행할 수 있게된다.
듣기에는 굉장히 복잡해보이지만 아래 깃헙링크의 코드와같이 pytorch없이 python만으로 122 line만에 구현할 수 있다고 한다.
수학적으로 봤을때 말도안되는 형태이지만 위 그림처럼 diagram도 표현하고 증명도 서술되며 그럴듯한 형태로 나타내고있다.
이를통해 어떤 종류의 data도 character level rnn을 통해 학습시킬 수 있다는 것을 보여준다.
변수명, 함수, 인덴트, 메크로 정의 등등 정말 그럴듯한 code를 생성해낸다.
그렇다면 이러한 data들을 학습한 rnn은 어떻게 동작하는 것이며 어떤 종류의 representations일까?
Image captioning은 transfer learning을 사용하여 CNN의 마지막 두layer를 제거한 형태로 사용한다.
이 전에봤던 language model이 stream of data에대한 연산과 관련있었다면 Image captioning에서의 language model은 sequece의 START token과 End token에 집중하여 학습하기를 원한다.
CNN과 RNN을 연결시키기위해 recurrent formula를 위 그림처럼 변경해야한다. 기존 input x와 previous state를 linear transform시켜 더해주는 식()에서 CNN에서 출력으로 나온 feature vector v에도 linear transform시켜 () 더해주는 형태이다.
즉, 3가지 input의 가중치합을 모두 더해주어 tanh를 취하여 output을 내보내는 형태이다.
Vanilla RNN에서 의 gradient를 구하기 위해 loss로부터 backprop해서 gradient를 계산해야한다. 하지만 이러한 backprop에는 2가지 문제가있다
많은 time step으로 이루어진 network를 통해 위 문제를 살펴보자
위 그림에서 직관적으로 볼 수 있듯이 backprop시에 매 cell마다 계속해서 동일한 weight matrix X가 upstream gradient와 계속해서 곱해지는 형태로 (backprop 에서의 mul gate pattern을 생각해보면 쉽다) 이는 두가지 안좋은 문제를 발생시킨다.
직관적으로 보기위해 weight matrix를 largest sigular value로 판단하였을때 이 값이 1보다 클경우 exploding gradient문제를 발생시키고 1보다 작을경우 vanishing gradient문제를 발생시킨다.
이때 largest sigular value가 1일경우에만 제데로 학습이된다고 볼 수 있다.
Exploding gradient가 발생할 경우 첫번째 그림처럼 clipping을 통해 해결할 수도 있지만 실제 gradient가 아니기에 여전히 문제가 있다고 한다.
Vanishing gradient가 발생할 경우는 딱히 방법이 없어 아키텍쳐를 바꿔야한다고 한다.
Vanilla RNN에 비해 LSTM의 functional form을 보면 이게뭔지 어질어질 할텐데 LSTM의 기본적인 직관은 매 step마다 single hidden state vector를 기억하는 대신에 위 그림처럼 (csll state)와 (hidden state)라는 두개의 state vector를 기억한다.
LSTM에선 이전 hidden state 와 current input 를 통해 4개의 다른 gate values를 구하는데 이를통해 위 식처럼 cell state와 hidden state를 계산하게 된다.
이러한 functional form이 실제로 무슨일을 하게되는지 자세히 살펴보자.
위 그림처럼 기존의 Vanilla RNN에서는 intput vector x와 previous hidden vector 을 concatenate시켜 weight matrix W에 multiply시킨 후 tanh를 거쳐 를 구해줬다면
LSTM에선 와 을 W와 곱한 결과를 4개의 gate로 출력하는 형태로 이 4개의 gate의 출력값을 가지고 cell state와 hidden state를 계산하게 된다.
식에 대한 해석은 다음과같다.
먼저 f 와 를 element-wise 곱셈 하는데 이때 forget gate인 f는 sigmoid를 거쳐 0~1의 값을 가지므로 f가 0일 경우 를 element-wise하게 0으로 만들어 전파하고 1일경우 온전한 값을 전파한다는 의미이다.
와 의 element-wise곱 에서는 g gate가 -1~1값을 갖으므로 값을 빼고싶은지 더하고싶은지 결정하고, i gate는 0~1값을 갖으므로 g gate 값의 크기를 정하게된다. 즉, 와 의 element-wise곱은 값을 cell state에 얼마나 반영할지를 결정한다.
위 그림처럼 여러개의 cell을 통해 보아도 cell state는 weight matrix인 W와 곱해지는 부분도 없고 어떠한 non-linearlity도 거치지 않는다.
이는 resnet의 shortcut의 개념과 유사한 형태로 가 어떠한 W와 non-linearlity에 영향을 받지않고 directly하게 propagate된다는 것을 보장하여 Vanilla RNN의 Long-Term Dependency 문제(resnet 논문에서의 plain net과 동일한 문제)를 해결하였다.