RNN이 갖추어야 할 세 가지 필수 기능
RNN의 구조
Short-term dependency
Long-term dependency
위 그림을 보자. LSTM은 기존 RNN의 hidden state 외에도 별도의 cell state와 forget gate 등이 존재한다.
하나씩 분석을 해보면,
는 forget gate로써 과거 정보를 잊을지 말지를 결정하는 gate이다. 와 의 연산 결과를 받아서 sigmoid 활성 함수를 적용하는데, sigmoid 함수는 [0,1]의 값을 가지므로 0에 가까울 수록 정보를 잊고, 1에 가까울 수록 과거 정보를 기억하게 된다.
는 Input gate로써, 계산된 정보가 cell state에 저장될지 말지를 결정한다. forget gate와 마찬가지로 sigmoid 활성함수를 사용한다.
는 기존 RNN과 비슷하게 와 를 input으로 받아서 tanh 함수를 적용하여 생성되고, 이를 와 곱하여 cell에 올릴지 말지를 결정하게 된다.
는 cell state로써 컨베이어 벨트처럼 정보가 바뀌지 않고 흐르도록 해준다.
위에서 설명했듯이 이전 cell인 에 가 input gate에 의해 올려질지 결정이 되고, 이가 다음 Cell state가 된다.
마지막으로 output은 와 를 input으로 받아 sigmoid 활성함수를 거친 를 계산한 후, update된 cell state인 에 tanh 함수를 적용한 값과 곱하여 구한다.
트랜스포머는 구글이 발표한 논문 "Attention is all you need"에서 나온 모델로 기존의 seq2seq의 구조인 인코더-디코더를 따르면서 어텐션으로만 구현한 모델이다.
기존의 seq2seq 모델의 한계
기존 seq2seq 모델의 인코더는 입력 시퀀스를 하나의 벡터 표현으로 압축하고, 디코더는 이 벡터 표현을 통해 출력 시퀀스를 만들어 냈다. 하지만 인코더가 입력 시퀀스를 하나의 벡터로 압축하는 과정에서 입력 시퀀스 정보가 일부 손실되는 단점이 존재했고, 이를 보완하기 위해 어텐션이 사용됐다.
Transformer는 RNN을 사용하지 않지만 인코더에서 입력 시퀀스를 입력받고, 디코더에서 출력 시퀀스를 출력하는 인코더-디코더 구조를 갖는다. 이때 인코더와 디코더는 다음과 같이 여러개 존재할 수 있다.
그렇다면 인코더에서 디코더로 전송되는 정보는 과연 무엇일까?
우선 인코더의 구조를 보면 아래와 같다.
위 그림과 같이 인코더를 하나의 층으로 생각하면, 하나의 인코더 층은 크게 2개의 서브층으로 나뉜다. 바로 Self-Attention과 Feed Forward Neural Network 이다.
위 그림과 같이 Feed-forward 층은 임베딩된 벡터들을 독립적으로 통과시키는 반면, Self-attention 층은 서로 의존적이게 통과시킨다. 이게 무슨 뜻 일까?
다음과 같은 예시를 보자.
위 그림에 나온 문장을 우리는 쉽게 해석을 할 수 있다. 하지만 컴퓨터는 it이 동물인지, 길인지 쉽게 알 수 있을까? 셀프어텐션은 입력 문장 내의 단어들끼리 유사도를 구함으로써 it이 animal과 연관되었을 확률이 높다는 것을 찾아낸다!!
이제 Self-Attention이 어떻게 동작하는지 알아보자.
위 그림과 같이 Thinking과 Machines라는 임베딩된 두 벡터가 있다.
Self-Attention은 아래 그림과 같이 Query , Key , Value 벡터들을 각각의 단어마다 계산을 한다.
자, 이제 첫 번째 단어인 Thinking을 인코딩해보자.
가장 먼저 Thinking의 Query 벡터와 주어진 모든 단어들의 key벡터들을 각각 내적하여 모든 단어마다 Score을 계산한다. 임베딩하고 싶은 단어의 Query 벡터와 모든 단어들의 key 벡터들은 내적을 하므로, Query 벡터와 Key 벡터는 크기가 같아야 한다.
그리고 나서 각 Score을 로 나눠준 다음 Softmax를 적용한다.
최종적으로 softmax 결과값과 각 단어들의 Value 값들을 곱한 다음 Weighted Sum 하여 Z를 구하게 된다.
즉, Self-Attention은 주어진 Query에 대해 모든 Key와의 유사도를 각각 구한다. 이후 해당 유사도를 가중치로 하여 Key와 매핑되어 있는 각각의 Value에 반영을 하고 합하여 리턴한다.
이러한 과정을 matrix form 으로 나타내면 다음과 같다.
멀티 헤드 어텐션 (Multi-head Attention)
멀티 헤드 어텐션은 다음과 같이 여러 번의 어텐션을 병렬로 사용하는 방법이다.
만약 8개의 헤드를 사용한다 하면, 8개의 다른 어텐션값 행렬들을 얻을 수 있는데, 이러한 벡터들을 Attention Heads 라고 한다.
근데 8개의 어텐션 헤드들을 다음 layer에 어떻게 보낼까????
병렬 어텐션을 모두 수행하였다면 모든 어텐션 헤드를 concatenate한다. 어텐션 헤드를 모두 연결한 행렬은 또 다른 가중치 행렬 를 곱하게 되는데, 이렇게 나온 결과 행렬이 멀티-헤드 어텐션의 최종 결과물이다.
정리하면 다음 그림과 같이 진행이 된다.
Positional Encoding
RNN이 자연어 처리에서 유용했던 이유는 단어의 위치에 따라 단어를 순차적으로 입력받아 처리하는 RNN 특성으로 인해 각 단어의 위치 정보(position information)를 가질 수 있기 때문이다.
하지만 트랜스포머는 단어 입력을 순차적으로 받는 방식이 아니므로, 단어의 위치 정보를 다른 방식으로 알려줄 필요가 있다. 이를 위해 각 단어의 임베딩 벡터에 위치 정보들을 더해 모델의 입력으로 사용하는데, 이를 포지셔널 인코딩이라고 한다.
이제까지 인코더에 대해서 정리를 했다. 이렇게 구현된 인코더는 총 num_layers 만큼의 층 연산을 순차적으로 한 후에 마지막 층의 인코더의 출력을 디코더에게 전달한다. 디코더 연산도 num_layers 만큼의 연산을 하는데, 이때마다 인코다가 보낸 출력을 각 디코더 층 연산에 사용한다.
셀프 어텐션과 룩-어헤드 마스크
seq2seq의 디코더에 사용되는 RNN 계열의 신경망은 입력 단어를 매 시점마다 순차적으로 입력받으므로 다음 단어 예측에 현재 시점을 포함한 이전 시점에 입력된 단어들만 참고할 수 있었다.
반면, 트랜스포머는 문장 행렬로 입력을 한 번에 받으므로 현재 시점의 단어를 예측하고자 할 때, 입력 문장 행렬로부터 미래 시점의 단어까지도 참고할 수 있는 현상이 발생한다.
가령, suis를 예측해야 하는 시점이라고 해보자. RNN 계열의 seq2seq의 디코더라면 현재까지 디코더에 입력된 단어는 와 je뿐일 것이다. 반면, 트랜스포머는 이미 문장 행렬로 je suis étudiant를 입력받았다.
이를 위해 트랜스포머의 디코더에서는 현재 시점의 예측에서 현재 시점보다 미래에 있는 단어들을 참고하지 못하도록 룩-어헤드 마스크(look-ahead mask)를 도입했다.
룩-어헤드 마스크는 디코더의 첫 번째 서브층에서 실행된다. 디코더의 첫 번째 서브층인 멀티 헤드 셀프 어텐션 층은 인코더에서와 같은 연산을 수행한다. 다만, 다른 점은 어텐션 스코어 행렬에서 마스킹을 적용한다는 점만 다르다.
디코더의 두번째 서브층
디코더의 두번째 서브층은 멀티 헤드 어텐션을 수행한다는 점에서는 이전의 어텐션들(인코더와 디코더의 첫번째 서브층)과는 공통점이 있으나 이번에는 셀프 어텐션이 아니다.
셀프 어텐션은 Query, Key, Value가 같은 경우를 말하는데, 인코더-디코더 어텐션은 Query가 디코더인 행렬인 반면, Key와 Value는 인코더 행렬이기 때문이다.
디코더의 두 번째 서브층을 확대해 보면, 인코더로부터 두 개의 화살표가 그려져 있다.
두 개의 화살표는 각각 Key와 Value를 의미하며 이는 인코더의 마지막 층에서 온 행렬로부터 얻는다. 반면 Query는 디코더의 첫번째 서브층의 결과 행렬로부터 얻는다는 점이 다르다.