20 minute read

Abstract

입/출력이 가변이어야 하거나 sequential processing 이 요구되는 경우 유용한 모델인 Recurrent Neural Networks 에 대해 배우며 기존의 CNN 으로는 수행할 수 없으나 RNN 을 활용하면 수행 가능한 Task 인 image captioning, visual question answering 에 대해서 알아본다. 또한 RNN 의 단점을 개선한 모델인 LSTM 에 대해서도 배운다.

RNN’s Potential

지금까지 배운 Architecture는 Network가 Matrix or vector를 input으로 받아 Hidden layer를 거쳐 output을 내보내는 Vanilla Neural Network이다. 하지만 Machine Learning의 관점에서 보면 model이 정해진 형태의 input만이 아니라 다양한 형태의 input을 처리할 수 있도록 유연해질 필요가 있다. 그런 관점에서 Recurrent Neural Network는 다양한 input/output을 다룰 수 있는 여지를 제공한다.

image-20240204190758482

RNN 구조를 활용할 때 가령 “one to many” model은 input이 image와 같은 단일 input이지만 output은 caption과 같은 가변 출력이다. “many to one”같은 model은 입력이 가변 입력이다. 가령 댓글과 같은 문장의 감정이 부정적인지 긍정적인지 분류하는 것이다. 그리고 Computer vision Task의 경우, 가령 입력이 video라고 한다면 video에 따라 전체 Frame 수가 다양할 것이다. 전체 video를 읽으려면 가변 길이의 입력을 받아야 한다. 비디오를 입력으로 받아서, 비디오에 나타나는 activity, 혹은 action을 classification하는 문제를 생각해볼 때 이 경우에는 input, output이 모두 가변 이어야 할 것이다. 또 다른 예시로 가변 입력에 대해 가변 출력을 처리하는 Machine Translation(기계 번역)의 예를 들 수 있을 것이다. RNN은 이런 다양한 상황들을 모델이 잘 처리할 수 있도록 해준다.

그리고 고정 길이의 input/output임에도 sequential processing이 요구되는 경우 RNN이 상당히 중요하다. 어떤 논문에서 말하기를 가령 고정 입력인 어떤 Image를 classification하는 문제라고 하면, input image의 정답을 feed forward pass 한 번만 가지고 결정하는 것이 아니라, Network가 image의 여러 부분을 조금씩 살펴본 후에 숫자가 무엇인지를 최종적으로 판단하는 것이다. 논문은 동일한 방법으로 image generate를 제안하기도 한다. Image generate란 train dataset에서 본 image들을 바탕으로 새로운 image를 생성하는 것이다. 이 모두를 RNN으로 만들어낼 수 있다. 순차적으로 전체 출력의 일부분씩 생성해내는 것이다. 이 경우에도 전체 출력은 고정된 길이지만, RNN을 이용해서 일부분씩 순차적으로 처리할 수 있다. 이런 RNN은 도대체 어떻게 동작하는 것일까

RNN working process

일반적으로 RNN은 작은 “Recurrent Core Cell”을 가지고 있다. Input Xt가 RNN으로 들어가면 내부의 Hidden state가 update되는데 이 hidden state는 model에 feedback되고 이후에 또 다시 새로운 input X가 들어온다. 여기서 X, h는 vector구조이다. RNN이 매 step마다 값을 출력하는 경우를 생각해보자. 그렇다면 이런 식으로 진행된다.

RNN이 input을 받음 => hidden state update => output을 내보냄

img

오른쪽 그림의 초록색 RNN block은 RNN이 recursive relation을 연산할 수 있도록 설계되었다. 함수 fw를 이용하는 것이다. Parameter Matrix W를 가진 function fw는 이전 상태의 hidden state인 ht-1과 현재 상태의 input인 Xt 를 입력으로 받는다. 그리고 update된 다음 상태의 hidden state ht가 출력된다. 그리고 다음 step에서는 ht와 Xt+1이 input이 된다. RNN에서 output 값을 가지려면 ht를 입력으로 하는 FC-layer를 추가해야 한다. FC-layer는 매번 update되는 Hidden state(ht)를 기반으로 출력 값을 결정한다. 중요한 사실은 fw와 parameter W는 매 step 동일하다는 것이다. Image classification에서 하나의 model에 수많은 이미지를 입력하는 것처럼 RNN의 각 Recurrent Core Cell은 독립적인 모델이 아닌 하나의 모델에 다른 입력데이터를 입력하는 것으로 생각할 수 있다. 즉, 같은 model에 hidden state, input 값만 계속 달라지는 것이다. 따라서 각 Core cell 마다 W는 동일해야 한다.

image-20240204190859366image-20240204190903113

위의 수식은 앞서 있던 수식과 동일하다. 이전 hidden state와 현재 input을 받아서 다음 hidden state를 출력하는 수식을 가장 간단하게 표현해보면 가중치 행렬 W_xh와 input X_t의 곱으로 표현할 수 있다. 가중치 행렬 Whh는 이전 hidden state와 곱해진다. 이렇게 두 입력에 대한 행렬 곱 연산이 있고 두 결과값을 더한 다음 System에 non-linearity를 표현하기 위해 tanh를 적용한다. 왜 tanh를 사용하는지는 LSTM을 배울 때 다시 한번 다루도록 하겠다.

image-20240204190933531

이제 우리는 매 step마다 출력 y를 얻고 싶다. 이를 위해서는 hidden state인 ht를 새로운 가중치 행렬 Why와 곱해준다. 가령 매 step에 출력 y는 class score가 될 수 있을 것이다.

image-20240204190942853

Recurrent Neural Network의 진행과정을 이해하기 위해서 Multiple time steps를 unrolling해서 관찰할 수 있다. 쉽게 말하자면 RNN을 시간 단계별로 펼쳐서 이해한다는 의미이다.

첫 step에서는 보통 0으로 초기화되는 initial hidden state인 h0 와 input Xt가 있다. h0와 X1이 함수 fw의 input으로 들어간다. fw(h0, X1)의 output은 h1이다. 이 과정이 계속해서 반복되면서 가변 입력 Xt를 받는다. 이제 조금 더 구체적인 이해를 위해서 행렬 W를 넣어보면 아래와 같다.

image-20240204190957404

img여기서 주목할 점은 동일한 가중치 행렬 W가 매번 사용된다는 점이다. 매번 h와 X는 달라져도 W는 매번 동일하다. 이 RNN model의 backpropagation을 위한 𝐝𝐂𝐨𝐬𝐭/𝐝𝐖를 구하려면 각 step에서의 행렬 W에 대한 gradient를 전부 계산한 뒤에 이 값들을 hidden state에 모두 더해주면 된다. Computational graph에 yt도 넣어볼 수 있다. RNN의 output vetor ht가 또 다른 Network의 input으로 들어가 yt를 만들어 낸다. 가령 yt는 매 step의 class score가 될 수 있다.

image-20240204191123714

RNN의 Loss도 한번 살펴보자. 각 sequence마다 Ground truth label이 있다고 해보자, Ground truth label이란 실제 정답 label을 의미한다. 그럼 각 step마다 개별적으로 yt에 대해 Losses를 연산할 수 있다. 여기서 Loss는 가령 softmax loss function으로 출력할 수 있다. RNN의 최종 Loss는 각 step의 Losses의 합이다. 각 단계에서 Loss가 발생하면 전부 더하여 최종 Network의 Loss를 연산한다.

이 Network의 backpropagation를 생각해보면 model을 학습시키기 위해서 dcost/dw를 구해야 한다. Loss flowing은 각 step에서 이뤄진다. 이 경우 각 step마다 가중치 W에 대한 local gradient를 계산할 수 있다. 이렇게 개별로 계산된 loss gradient를 최종 gradient에 더하는 것이다. 즉, 각 step마다 연산된 -▽Ct를 hidden state 행렬 요소마다 다 더하는 것이다.

RNN types analysis

image-20240204191205544

그렇다면 sentiment analysis(감정 분석)같은 “many to one”이라면 어떨까? 이 경우 Network의 최종 hidden state에서만 결과 값이 나올 것이다. 최종 hidden state가 전체 sequence의 내용에 대한 일종의 요약이 될 수 있기 때문이다.

그렇다면 고정 입력을 받지만 가변 출력이 가능한 Network인 “one to many”인 경우에는 어떨까? 이 경우 대게 고정 입력은 model의 initial hidden state를 초기화시키는 용도로 사용한다. 그리고 RNN은 모든 step에서 output vector를 가진다.

Sequence to sequence model에 대해서 알아보자. Sequence to sequence model이란 자연어 처리 작업에서 주로 사용되는 DNN model의 일종으로 가령 machine translation에 사용 가능한 가변 입력과 가변 출력을 가지는 model이다. 우리는 이 model을 “many to one” model과 “one to many” model의 결합으로 볼 수 있다. 2개의 stage로 구성되는 것이다. 바로 encoder & decoder 구조다. encoder는 English sentence같은 가변 입력을 받는다. 그리고 encoder의 final hidden state를 통해 전체 sentence를 요약한다. Encoder에서는 “Many to one”을 수행한다. 가변 입력을 하나의 vector로 요약한다. 반면 Decoder는 “one to many”를 수행한다. 입력은 앞서 요약한 “하나의 vector”이다. 그리고 decoder는 매 step마다 다른 자연어 문자로 번역된 가변 출력을 내뱉는다. 그리고 output sentence의 각 Losses를 합해서 Backpropagation를 진행한다.

대게 RNN은 Language modeling에서 자주 사용한다. Language modeling 문제에서 하고 싶은 것은 바로 어떻게 앞 글자에 맞는 다음 natural language(자연어)를 만들어낼지 고민하는 것이다. 문자(character)를 출력하는 model이라면 매 step 어떻게 문자를 생성해 낼지를 풀어야 한다. 단어(word)를 출력하는 model이라면 매 step 어떻게 단어를 생성해 낼 지를 풀어야한다.

character level language model

우선은 간단한 예제를 통해 character level language model을 살펴보자. Network는 문자열 sequence를 읽어드리고, 현재 문맥에서 다음 문자가 무엇일지를 예측해야만 한다. 이번 예제에서는 간단하게 글자가 [h, e, l, o]만 있다. 그리고 학습시킬 word는 h, e, l, l, o이다. Train time에서는 training sequence의 각 문자 h, e, l, l, o를 입력으로 넣어줘야 한다. hello가 RNN의 Xt이다. 우선 input은 한 글자씩 넣는다. Network도 적절한 글자들을 출력해야 한다. 우선 vocabulary는 총 4가지이다. [h, e, l, o]. 각 글자는 하나의 vector로 표현할 수 있다. 이 vector는 1이 하나 있고 나머지는 0인 vector이다. 이러한 vector를 “one-hot vector” 라고 한다. 위의 단순한 예제에서는 vocabulary가 h, e, l, o 뿐이다. 따라서 4-d “one hot vector”로 표현할 수 있다. 가령 h를 vector로 표현하는 경우 h를 표현하는 자리만 1이고 나머지는 0이다. 이런 식으로 다른 문자들도 vector로 표현 가능하다. Forward pass에서 Network가 어떻게 동작하는지 살펴보자.

image-20240204191303912

우선 첫 step에서 RNN cell로 입력 문자 “h”가 들어온다. 그러면 Network는 y0를 출력한다. y0는 어떤 문자가 “h”다음에 올 것 같은지 예측한 값이다. 이 예제에서는 “h”다음에는 “e”를 예측해야 정답이다. 하지만 현재 이 model은 다음에 나올 글자가 o일 가능성이 가장 높다고 예측하고 있다. 나쁘게 예측한 것이다. softmax loss가 이 예측이 얼마나 형편없는 예측인지 알려줄 것이다. 다음 step에서는 두 번째 단어 “e”가 입력으로 들어간다. 이런 과정이 반복된다. 결국 model은 이전 문장의 문맥을 참고해 다음 문자가 무엇일지를 학습해야 할 것이다.

backpropagation through time

image-20240204191329931

이렇게 학습시킨 model을 활용할 수 있는 방법들 중 하나는 model로부터 sampling하는 것이다. 다시 말해, train time에 model이 봤을 법한 문장을 model 스스로 생성해내는 것이다. 우선 model에게 문장의 첫 글자만 준다. 이 경우에는 h가 될 것이다. RNN의 첫 step의 input은 h가 될 것이다. h가 주어지면 모든 문자에 대한 score를 output으로 얻을 수 있다. 이 score를 sampling(vocalbulary에서 다음 글자 선택)에 사용해야 한다. Score를 확률 분포로 표현하기 위해 softmax function을 사용할 수 있다. 문장의 2번째 글자를 선택하기 위해서 이 확률 분포를 사용한다. 이 경우에 e가 뽑힐 확률이 되게 낮았음에도(13%) 아주 운 좋게 e가 sampling되♘다. 물론 score만 가지고 다음 글자를 선택할 수 있지만 이 경우에는 e의 score가 낮았기 때문에 확률 분포에서 sampling을 시도했다. 어떤 경우에는 score를 활용해 다음 글자를 바로 예측할 수 있다. 하지만 확률 분포에서 sampling하는 방법을 사용하면 일반적으로 model의 다양성을 확보할 수 있다. 실제로 두 경우 모두 사용할 수 있다. 이제 e를 “one-hot vector”로 변환한 다음 step의 network input으로 넣어 줄 것이다. 이 학습된 모델만 가지고 새로운 문장을 만들어내기 위해 이 과정을 반복한다. 전체 문장을 만들어내기 위해 time step마다 확률 분포에서 문자를 하나씩 뽑아낸다. 이런 model의 경우 train time에 sequence step마다 output 값이 존재하며 각각 Loss를 계산해 final loss를 얻는데 이를 “backpropagation through time”이라고 한다. 이 경우, forward pass의 경우에는 전체 sequence가 끝날 때까지 output이 생성된다. 반대로 backward pass에서도 전체 sequence를 가지고 Loss를 계산해야 한다. 하지만 이 경우 sequence가 아주 긴 경우에는 문제가 될 여지가 있다. 가령 wikipedia 전체 문서로 model을 학습시킨다고 해보자. 이 경우 학습이 정말 느릴 것이다. Gradient를 계산하려면 wikipedia 전체 문서를 다 거쳐야 할 것이다. Wikipedia 문서 전체에 대한 gradient를 계산하고 나면 gradient update가 1회 수행된다. 이 과정은 아주 느릴 것이고 메모리 사용량도 매우 어마어마할 것이다.

Truncated backpropagation through time

실제로는 “truncated backpropagation”을 통해서 backpropagation에 가깝게 근사시키는 기법을 사용한다. 이 방법의 아이디어는 비록 입력 sequence가 엄청나게 길다고 하더라도 train time에 한 step을 일정 단위(ex. 100)로 잘라 각각 mini-batch로 만들고 100step(batch 1)만 forward pass를 하고 이 subsequence의 Loss를 계산하는 것이다. 그리고 gradient step을 진행하는 것이다. 이 과정을 반복한다. 다만 이전 batch에서 계산한 hidden states는 계속 유지되어 다음 batch의 forward pass를 계산할 때는 이전 hidden states를 사용한다. 그리고 gradient step은 현재 batch에서만 진행한다. 이 방법이 “truncated backpropagation through time”이다.

image-20240204191413961image-20240204191416479

image-20240204191422011

이 과정을 반복한다. 이전 batch에서 hidden states를 가져와서 forward pass를 수행하고 backpropagation은 현재 batch만큼만 진행한다. 이 방법은 stochastic gradient descent의 sequence data version이라고 볼 수 있다. . Truncated backpropagation은 very large sequence data의 gradient를 근사시키는 방법이라고 해석할 수도 있다.

RNN that learns unseen patterns

RNN은 어떤 문장이던 학습시킬 수 있다. 가령 셰익스피어의 작품들도 RNN으로 학습시키는 것이 가능하다. 학습 초기에는 model이 의미 없는 문장을 출력하지만 학습을 시키면 시킬수록 의미가 있는 문장을 만들어낸다. 문장만이 아니라 들여쓰기, 문단의 개념도 학습시키는 것이 가능하다. 단순 단어만으로 표현되지 않는 글의 암묵적인 rule도 이해하는 것이 가능한 것이다. 수학적인 수식 또한 생성해낼 수 있다. 단순 수식이 아닌 diagrams, 기호, 심지어 증명 생략까지 출력하는 것이 가능하다. 물론 변수를 선언하고 사용하지 않거나, 선언하지 않은 변수를 사용하는 문제가 존재하긴 하지만 위의 내용만 해도 가능성이 무궁무진하다는 것을 알 수 있다. 우리가 model에게 요청한 것은 sequence의 다음 문자를 예측하라는 것이다. 하지만 model은 학습과정 동안에 시퀀스 data의 latent structure(숨겨진 구조)도 알아서 학습한다.

RNN이 어떤 원리로 이런 보이지 않는 규칙까지 학습하는지는 완벽히 규명되지 않았지만, 가중치 행렬 W의 특정 vector를 분석해보면 vector가 따옴표, 줄 바꿈과 같은 규칙성을 찾아내는 역할을 한다는 것을 알 수 있다.

Image captioning(설명)

image-20240204191552836

앞서 Image captioning Model에 대해 몇 번 언급했다. Image captioning은 Computer vision과 Natural Language Processing(NLP, 자연어 처리)를 결합한 기술로 주어진 image에 대해 자동으로 caption(문장)을 생성하는 작업을 의미한다. 즉, Image captioning은 computer가 image를 이해하고 그 내용을 자연어로 설명할 수 있는 능력을 갖게 하는 기술이다.

우선 caption은 caption마다 다양한 sequence 길이를 갖고 있는 가변 출력을 수행할 수 있어야 한다. 여기에는 RNN Language Model이 매우 적합하다. Model에는 image를 받기 위한 CNN model이 있다. CNN이 요약된 image 정보가 들어있는 Vector를 출력하면 이 vector는 RNN의 초기 step의 input으로 들어간다. 그러면 RNN은 caption에 사용할 문자들을 하나씩 만들어 낸다. 그렇다면 이 model을 학습시킨 후에 Test time에 어떻게 동작하는지 알아보자.

image-20240204191613945

우선 image를 CNN의 input으로 넣는다. 다만 직전의 4096-dim vector를 output으로 한다. 그리고 RNN model에게 여기 4096-dim feature vector가 있으니 이 벡터에 맞는 문장을 만들어달라고 요청해야 한다.

image-20240204191623775

이전까지의 model에서는 RNN model이 2개의 가중치 행렬을 input으로 받았다. 하나는 현재 step의 input값이고 다른 하나는 이전 step의 hidden state이다. 그리고 이 둘을 조합해 다음 hidden state를 얻었다. 하지만 이제는 image 정보도 더해줘야 한다. 사람마다 model에 image 정보를 추가하는 방법이 다르겠지만 가장 쉬운 방법은 3번째 가중치 행렬 W를 추가하는 것이다. 다음 hidden state를 연산할 때 모든 step에 이 image 정보를 추가한다. X0같은 경우 시작을 의미하는 토큰이라고 생각하면 된다. 자 이제는 vocabulary의 모든 scores에 대한 분포를 연산할 차례이다. 문제는 이 문제에서 vocabulary는 “모든 영어 단어들”과 같이 엄청나게 크다는 것이다. Vocabulary 크기의 분포에서 sampling을 하고 그 단어를 다음 step의 input으로 다시 넣어줄 것이다. Sampling된 단어가 벡터 형태로 다시 들어가면 또 vocabulary에 대한 분포를 추정하고 다음 단어를 만들어낸다. 모든 step이 종료되면 다음 단어를 만들어낸다. 이때 라는 특별한 token이 이는데 이는 문장의 끝을 알려준다. 가 sampling되면 model은 더 이상 단어를 생성하지 않으며 image에 대한 captioning이 완성된다. Train time에는 모든 caption의 종료 지점에 토큰을 삽입한다. Network가 학습하는 동안에 sequence의 끝에 token을 넣어야 한다는 것을 알려줘야 하기 때문이다. train이 끝나고 Test time에는 모델이 문장 생성을 끝마치면 token을 sampling한다. 이 model은 완전히 supervised learning(지도 학습)으로 학습시킨다. 따라서 이 model을 학습시키기 위해서는 natural language caption이 있는 image를 가지고 있어야 한다.

대표적인 image captioning을 위한 dataset으로는 Microsoft COCO dataset이 있다. 이 data set과 CNN, RNN을 이용해 Neuraltalk2(2015, MS)라는 model을 사용해 학습시켜보았다.

image-20240204191712273

이런 captioning Model은 상당히 powerful하고 image를 묘사하기 위해 비교적 복잡한 captions도 만들어 낼 수 있다. 그렇다 해도 model이 아주 완벽하지는 않다.

image-20240204191721115

위 사진을 보면 다른 Machine Learning Model들과 같이 Train time에 보지 못한 data에 대해서는 잘 동작하지 않는다. 일반적인 문제에 적용하기에는 무리인 model인 것이다.

Image captioning: Attention

사람은 이러한 captioning문제를 어떻게 해결할까? 사람은 그림 전체를 한번에 보는 것이 아니라 부분부분 보면서 그 영역과 관련된 word를 뽑아내 조합한다. 이 때 word에 관련된 영역에만 집중해서 보고 다른 부분은 주의를 기울이지 않는다. 전체 image가 엄청 크다면 거기엔 captioning에 불필요한 정보들이 많이 존재할 수 있다. 그래서 보다 더 진보된 captioning model 이 탄생하게 된다. 바로 Attention이라는 model이다.

Attention model은 Caption을 생성할 때 image의 중요 부분을 attention(집중)해서 볼 수 있다. CNN을 이용해 feature vector를 만드는게 아니라 각 vector가 공간 정보를 가지고 있는 “grid of vector: (LxD)”를 만들어낸다. 기존의 경우 FC-layer의 연산을 위해서 (1414)512의 Tensor를 1003521크기의 column vector로 모두 concatenate 해주♘다면, 여기서는 196512(L*D)로 각 pixel별로 특징을 추출하는 것이다.

image-20240204191755571

예전에는 열 벡터의 형태였다면 attention model은 L(나뉜 영역의 수)XD(feature vector의 차원) 형태의 Matrix로 표현한 것이다. 이제는 이미지 전체에 대해 추출한 특징 벡터를 쓰는게 아니라 L개의 grid로 나뉘어진 region의 일부를 가져다 쓰겠다는 뜻이다. 이때 CNN의 각 grid, 즉 L들은 각각 속한 영역의 특징에 대한 정보를 담고 있다. 예를 들어 input이 고양이 이미지라면 각 filter는 고양이 눈, 털, 코 등에 대한 정보를 담고 있을 것이다.

image-20240204191807573

Forward pass시에 매 step vocabulary에서 sampling을 할 때 model이 image에서 보고 싶은 위치에 대한 분포 또한 만들어낸다. Image의 각 위치에 대한 분포는 Train time에 model이 어느 위치를 봐야하는지에 대한 attention이라 할 수 있다. 첫 번째 hidden state는 image의 위치에 대한 분포를 연산한다. 이 분포를 다시 grid of vector와 연산하며 image attention을 생성한다. 이 요약된 image attention은 Neural Network의 다음 step의 input으로 들어간다. 그리고 2개의 출력이 생성된다. 하나는 vocabulary의 각 단어들의 분포, 그리고 다른 하나는 image의 위치에 대한 분포이다. 이 과정을 반복하면 매 step마다 값 2개가 계속 만들어진다. Train을 끝마치면 model이 captioning을 생성하기 위해서 image의 attention을 이동시키는 모습을 볼 수 있다.

image-20240204191815347

Model이 Caption을 만들 때 마다 image의 다양한 곳들에 attention을 부여하는 것을 볼 수 있다. 사실 여기에는 hard/soft attention의 개념이 숨어있다. Soft attention의 경우 모든 특징과 모든 이미지 pixel간의 weighted combination을 취하는 경우이다. Hard attention의 경우에는 model이 각 time step 마다 이미지 위치를 딱 하나만 정해야 한다. 그러나 사실상 하나만 정하기 까다롭기 때문에 Hard attention을 학습시키려면 기본 back propagation 보다는 조금 더 흥미로운 방법을 써야만 한다. 이에 대해서는 추후 reinforcement learning 시간에 더 다루겠다.

image-20240204191825456

Attention model을 학습시키고 나서 caption을 생성해보면 실제로 model이 caption을 생성할 때 의미가 있는 부분에 attention을 집중한다는 것을 알 수 있다. image에서도 실제 원반이 위치하는 곳을 정확하게 attention하고 있다.

우리는 model에게 매 step 어디를 주목하라고 명령한 적이 없다. Model이 train time에 특정 영역(원반)에 집중하는 것이 올바른 일이라는 것을 스스로 알아낸 것이다. Model 전체가 미분가능하기 때문에 soft attention 또한 backpropagation이 가능하다. 따라서 이 모든 것은 train time에 나온 것들이다. RNN + Attention 조합은 Image captioning 뿐만 아니라 더 다양한 것들을 할 수 있다. 가령 Visual Question Answering(VQA)와 같은 것이다.

Visual Question Answering

image-20240204191854266

VQA에서는 입력이 2가지이다. 하나는 image이고 다른 하나는 image에 관련된 질문이다. 위쪽과 같은 image에 여러 질문을 던져볼 수 있다. Model은 여러 보기 중에서 정답을 골라야 한다. 이 model 또한 RNN과 CNN으로 만들 수 있다. 이 경우는 “many to one”의 경우이다. Model은 자연어 문장(질문)을 입력으로 받아야 한다. 이는 RNN을 통해 구현할 수 있다. RNN이 질문을 vector로 요약하고 CNN이 image를 요약한다. CNN/RNN에서 요약한 vector를 조합하면 질문에 대한 분포를 예측할 수 있을 것이다.

image-20240204191902328

그리고 간혹 VQA문제를 풀기 위해서 soft special attention 알고리즘을 적용하는 경우도 있다. 이 예시를 보면 model이 정답을 결정하기 위해서 image에 대한 attention을 만들어내는 것을 볼 수 있다. Encoded image와 encoded question을 어떻게 조합하는 가장 쉬운 방법 중 하나는 concatenate시켜 FC-layer의 input으로 만드는 방법이다. 하지만 간혹 사람들이 더 강력한 function을 만들기 위해서 두 vector간의 더 복잡한 조합을 만들어내는 경우도 있다. 어찌되♘든 Concatenate하는 방법도 맨 처음 시도해보기에는 나쁘지 않은 방법이다. 지금까지는 단일 RNN layer를 사용했다. Hidden state가 하나뿐인 model이다. 하지만 우리가 자주 보게 될 layer는 Multi-layer RNN이다.

image-20240204191911057image-20240204191913487

3-layer RNN이 있다. Input이 첫 번째 RNN으로 들어가서 첫 번째 hidden state를 만들어낸다. 이렇게 만들어진 hidden state sequence를 다른 RNN의 input으로 넣어줄 수 있다. 그러면

2번째 RNN layer가 만들어내는 또 다른 hidden states sequence가 생겨난다. 이런 식으로 RNN Layer를 쌓아 올릴 수 있을 것이다. 이렇게 하는 이유는 Model이 깊어질수록 다양한 문제들에 대해서 성능이 좋아지기 때문이다. 많은 경우에 3~4 layer RNN을 활용한다.

RNN은 사용할 때 문제점이 있는데 RNN을 학습시킬 때 어떤 일이 생기는지 아주 신중히 생각해봐야 한다. 여기 지금까지 우리가 봐왔던 일반적인 vanilla RNN cell이 있다.

image-20240204191940747

입력은 현재 입력 Xt와 이전 hidden state ht-1이다. 그리고 이 두 입력을 쌓는다(stack). 이렇게 두 입력을 쌓고 가중치 행렬 W와 행렬 곱 연산을 하고 tanh를 씌워서 다음 hidden state를 만든다. 이는 Vanilla RNN의 기본 수칙이다. 그러면 이 Architecture는 backward pass에 gradient를 계산하는 과정에서 어떤 일이 발생할까?

image-20240204191950451

우선 backward pass 시 ht에 대한 Cost의 미분값을 받는다(global gradient). 그 다음 Loss에 대한 ht-1의 미분값을 계산하게 되는데 backward pass의 전체 과정은 위의 빨간색 통로를 따르게 된다. 우선 gradient가 “tanh gate”를 타고 들어가 “Mat mul gate”(행렬 곱셈 연산 게이트)를 통과한다. “Mat mul gate”의 backpropagation은 결국 이 가중치 행렬 W를 곱하게 되는데 이는 매번 vanilla RNN cells를 하나 통과할 때 마다 가중치 행렬의 일부를 곱하게 된다는 것을 의미한다. RNN의 특성상 RNN이 여러 sequence의 Cell을 쌓아 올리는 것을 생각하면 Cell을 하나 지날 때마다 엄청난 양의 연산이 추가된다. H0에 대한 gradient를 구하고자 한다면 결국 모든 Cell을 거쳐야 하는 것이다. 우리가 스칼라값들이 있고 이 값들을 계속해서 곱해야 하는 상황이 된다고 가정하자, 곱해지는 값들이 1보다 크다면 값이 발산할 것이고 1보다 작다면 0에 수렴할 것이다. 이 두 상황이 일어나지 않으려면 곱해지는 값이 1인 경우 밖에 없다. 그러나 실제로 가중치가 1이 되기는 매우 힘들다. 스칼라에서의 이 intuition은 Matrix에서도 동일하게 적용된다. 가령 W 행렬의 singular value(특이값)이 엄청나게 큰 경우를 생각해볼 수 있다. 이 경우 h0의 gradient가 매우 커지게 되는데 이를 “exploding gradient problem”이라고 한다. Backpropagation 시에 layer가 깊어질수록 gradient가 기하급수적으로 증가하는 현상이다.

반대로 행렬의 singular value(특이값)이 1보다 작은 경우 Gradient가 0이 되는 “vanishing gradient problem”이 발생한다.

image-20240204192012798

그래서 사람들은 “Gradient clipping”이라는 기법을 사용하곤 한다. Gradient clipping은 heuristic (근사적인) method중 하나로 Gradient를 연산한 후 grad배열을 제곱해 더한 값을 grad_norm 으로 지정한다. 이 grad_norm값이 임계값보다 큰 경우 Grad가 최대 임계값을 넘지 못하도록 조정한다. 이렇게 Gradient가 극단적으로 커지지 못하도록 방지하는 것이다. 이 방법은 좋은 방법은 아니지만 많은 사람들이 RNN 학습에 활용한다. Exploding gradient problem을 해결하기에는 비교적 유용한 방법이긴 하지만 vanishing gradient problem을 다루려면 조금 더 복잡한 RNN architecture가 필요하다. 이는 LSTM(Long short term memory)에 관한 것이다.

LSTM

image-20240204192033197

LSTM은 RNN의 fancier version이다. LSTM은 vanishing & exploding gradients problem을 완화하기 위해서 design되었다. “gradient clipping” 같은 hack을 쓰지 말고, Gradient가 잘 전달되도록 Architecture 자체를 디자인한 경우이다. 기존의 fancier한 CNN Architecture들과 유사한 느낌이다. 사실 LSTM은 1997년에 나왔다. 사람들은 90년대에 이미 LSTM을 연구하고 있었고, 시대를 앞서간 연구라고 할 수 있을 것이다.

image-20240204192050174

LSTM의 update 식을 살펴보면 ht-1, Xt 2개의 input을 받는다. 그리고 4개의 i, f, o, g gates를 연산한다. 이 gates를 Ct를 update하는데 사용한다. 그리고 Ct로 다음 step의 hidden state를 update한다. 앞으로 LSTM이 어떻게, 왜 동작하는지 vanishing, exploding gradient의 관점에서 살펴볼 것이다.

image-20240204192109454

LSTM은 이전 hidden state인 ht-1과 현재의 입력인 Xt를 입력으로 받아서 쌓아놓는다. 그리고 4개의 gates 값을 계산하기 위해 가중치 행렬을 곱해준다. 각 gates의 출력은 hidden gates의 크기와 동일하다. 물론 gate의 가중치 행렬의 크기를 다양한 방법으로 디자인할 수도 있다. 어떤 사람들은 모든 값들을 조합해서 하나의 큰 가중치 행렬로 만들기도 한다.

기본적으로는, ht-1과 Xt를 입력으로 받고 gates 4개를 계산해야 한다. Gates 4개는 [i, f, o, g]인데 기억하기 쉽게 ifog라고 부르겠다. i는 input gate로 cell에서의 입력 Xt에 대한 가중치이다. F은 forget gate로 이전 Cell의 정보를 얼마나 망각 할지에 대한 가중치이다. O는 output gate로 Cell state, Ct를 얼마나 밖에 드러내 보일지에 대한 가중치이다. G는 gate gate로 input cell을 얼마나 포함시킬지 결정하는 가중치이다.

중요한 점 중 하나는, 각 gate에서 사용하는 non-linearity가 각양각색이라는 점이다. Input/forget/output gate의 경우는 sigmoid를 사용한다. Gate의 값이 0~1사이라는 의미이다. 반면 gate gate는 tanh를 사용하며 -1~1의 범위를 갖는다. 이 부분이 이상해 보일 수 있지만, 사실은 더 이치에 맞다. gate값이 binary(0,1 or -1,1)라고 생각해보자,

image-20240204192125395

이전 step의 Cell states Ct-1은 forgat gate와 element-wise multiplication(요소별 곱)한다. 결과 vector는 0~1일 것이다. 따라서 forget gate=0인 element는 이전 Cell state를 잊을 것이다. 반면 forget gate=1이라면 cell state의 element를 완전히 기억한다. 지금까지 살펴본 forget gate는 이전 cell state의 gate on/off를 결정했다.

두 번째 수식인 i⊙g에서 vector i의 경우 sigmoid에서 나온 값이므로 0~1이다. Cell state의 각 element에 대해서, 이 cell state를 사용하고 싶으면 1이 된다. 반면 쓰고 싶지 않다면 0이 된다.

Gate gate는 tanh 출력이기 때문에 값이 -1~1이 된다. Ct는 현재 step에서 사용될 수 있는 후보라고 할 수 있다. Cell State(Ct)를 계산하는 전체 수식을 살펴보자. 이 수식은 두 개의 독립적인 scaler 값f, i에 의해 조정된다. 각 값 f, i는 1까지 증가하거나 감소한다.

Ct의 수식을 해석해보면, 우선 이전 cell state(Ct-1)을 계속 기억할지 말지를 결정한다. (f⊙Ct-1) 그런 다음 각 step마다 최대 1까지 cell state의 각 요소를 증가시키거나 감소시킬 수 있다. (i⊙g) 즉 cell state의 각 요소는 scaler integer counters(프로그램 성능 측정에 사용)처럼 값이 줄♘다 늘♘다 하는 것으로 보일 수 있다. Cell state를 계산했다면 이제는 hidden state를 update할 차례이다. ht는 실제 밖으로 보여지는 값이다. 그렇기 때문에 cell state는 counters의 개념으로 해석할 수 있다. 각 step마다 최대 1 또는 -1씩 세는 것이다. tanh를 통과한 후 최종적으로 output gate와 요소별로 곱해진다. Output gate 또한 sigmoid에서 나온 값으로 0~1사이의 값을 가진다. output gate는 각 스텝에서 다음 hidden sate를 계산할 때 cell state를 얼마나 노출시킬지를 결정한다.

image-20240204192136548

우선 왼쪽을 보면 이전의 cell state Ct와 hidden state ht, 현재 입력 Xt를 받는다. 우선 이전 hidden state와 현재 입력을 쌓는다(stack). 그리고 4종류의 가중치 행렬을 곱해 4개의 gates를 만든다. 그리고 forget gate를 이전 cell state와 곱하고 input gate와 gate gate가 element wise로 곱한 후 (f⊙Ct-1)와 더해서 다음 cell state를 만든다. C_t는 tanh를 거친 후 output gate와 곱해져서 다음 hidden state를 만들어낸다. LTSM의 backward pass는 어떨까?

img

앞서 vanilla RNN의 경우 backward pass에서 문제점이 있♘다. 가중치 행렬 w의 gradient와 관련된 문제였다. 하지만 LSTM에서는 상황이 많이 달라졌다. LSTM에서 cell state의 gradient를 계산하는 backward pass를 살펴보게 될 것이다. 우선 addition operation의 backpropagation이 있다. Addition gate에서는 upstream gradient가 그저 2갈래로 복사된다. 따라서 “element wise multiply” 로 직접 전달된다. 따라서 gradient는 upstream gradient와 forget gate의 element wise 곱이다. 결국 Cell state의 backpropagation은 그저 upstream gradient*forget gate이다. 이 특성은 vanilla RNN에 비해 좋은 점이 2가지 있다. 우선 forget gate와 곱해지는 연산이 행렬 곱이 아닌 element-wise라는 점이다. Full matrix multiplication보다는 element wise multiplication이 더 낫다. 두 번째는 element wise multiplication을 통해 매 step 다른 값의 forget gate와 곱해질 수 있다는 점이다. Vanilla RNN같은 경우 동일한 가중치 행렬 ht만을 계속 곱했다. 이는 exploding/vanishing gradient문제를 일으켰다. 반면 LSTM에서는 forget gate가 step마다 계속 변한다. 따라서 LSTM은 exploding/vanishing gradient문제를 더 쉽게 해결할 수 있다. 또한 Vanilla RNN의 backward pass에서는 매 step gradient가 tanh를 거쳐야 했다.

image-20240204192205725

LSTM에서도 hidden state hidden state ht를 출력 yt를 계산하데 사용한다. 가령 LSTM의 최종 hidden state ht를 가장 첫 cell state까지 backpropagation하는 것을 생각해보면 RNN처럼 매 step마다 tanh를 거치는 것이 아니라 tanh를 단 한번만 거치면 된다.

LSTM의 전체적인 모습을 그려보면 Cell state를 통한 backpropagation은 gradient를 위한 고속도로라고 볼 수 있다. 궁극적으로는 W를 update해야 하는데 W에 대한 Local gradient는 해당 step에 해당하는 현재의 cell/hidden state로부터 전달된다. Vanilla RNN의 경우 각 step의 가중치 행렬 W가 서로 영향을 미쳤지만 LSTM의 경우 각 step마다 w에 대한 local gradient가 존재하며 global gradient는 cell/hidden state에서 흘러온다. LSTM의 경우 cell state C가 gradient를 잘 전달해주기 때문에 W에 대한 local gradient도 훨씬 더 깔끔하게 연산된다.

물론 여전히 Non-linearity가 있으므로 gradient vanishing 문제에 민감할 수 있다. Forget gate의 경우 출력이 0~1이므로 gradient가 점점 감소할 수 있다. 그래서 forget gate의 biases를 양수로 초기화하는 방법으로 이 문제를 해결한다. 이 방법을 이용해 학습 초기에 forget gate의 값이 1에 가깝도록 한다. 1에 가까운 값이기 때문에 적어도 학습 초기에는 gradient의 흐름이 비교적 원활할 것이다. 그리고 학습이 진행되어가며 forget gate의 biases가 적절한 자기 자리를 찾아갈 것이다. LSTM에서도 gradient vanishing 위험은 존재하나 매 step function이 가변적이고 LSTM은 element-wise multiplication을 수행하기 때문에 vanilla RNN에 비해 위험성이 매우 낮다. LSTM을 유심히 보면 ResNet스러운 특징이 몇가지 있는데 대표적으로 identity connection (ResNet)과 cell state(LSTM)의 존재이다. Identity mapping이 backward pass과정에서 ResNet gradient를 위한 고속도로 역할을 했다. LSTM도 동일한 intuition으로 보면 된다.

My note

image-20240204192233597

Cell state는 vector 구조이며 컨베이어 벨트와 같이 작은 linear interaction만을 적용시키면서 전체 chain을 계속 구동시킨다. LSTM은 cell state에 뭔가를 더하거나 없앨 수 있는 능력이 있는데 이 능력은 gate라고 불리는 구조에 의해 제어된다. LSTM의 첫 단계는 cell state로부터 어떤 정보를 버릴 것인지를 정하는 것이다. forget gate를 통해 σ(Wf[Whhht-1+WxhXt]+bf)를 연산한 뒤 cell state와 element-wise multiplication을 수행해 원하지 않는 정보는 삭제한다.

image-20240204192255506

다음 단계는 앞으로 들어오는 새로운 정보 중 어떤 것을 cell state에 저장할 것인지를 정하는 것이다. candidate cell state(gate gate)가 후보 cell gate를 만들고 input gate가 candidate cell state의 정보를 얼마나 기존 cell에 추가할지를 결정한다. 최종적으로 만들어진 새로운 cell gate를 기존 cell gate와 더해서 정보를 update한다.

image-20240204192303616

결론적으로 기존 cell state의 어떤 정보를 유지할지 결정해 cell state를 업데이트 한 뒤 새로운 cell state와 기존 cell state를 더해 새로운 정보를 추가하는 과정을 거친다.

image-20240204192312313

마지막으로 새로운 ht를 결정하기 위해 Ct의 값을 [-1,1]로 조정한 후 output gate와 element- wise multiplication 연산을 수행한다.

RNN에서 hidden state의 역할, LSTM에서 새롭게 만든 cell state의 역할, 그리고 LSTM에서 hidden state의 역할이 뭔지 이해하는 것이 매우 중요하다.

Leave a comment