Transformer는 Attention Mechanism 만을 적용하여 Recurrent Network의 한계를 극복한 신경망 아키텍처이다. 2017년에 구글이 발표한 논문인 “Attention is all you need”을 통해 소개되었고 GPT, BERT의 기반이 되는 등 현재까지도 자연어 처리 분야에 큰 영향력을 끼치고 있다.
Transformer 이전의 언어 모델은 순환 신경망(RNN) 기반으로 만들어졌다. RNN은 연속적인 데이터를 입력으로 받고 현재 시점의 hidden state가 다음 hidden state에 영향을 주는 구조이다. 이로써 자연어 문장과 같은 순차적 입력에 대해 순서를 고려한 출력을 얻을 수 있는 것이다.
하지만 이러한 순차적인 구조는 몇 가지 문제점들이 있다. 우선, 길이가 긴 입력에 대해서 뒤로 갈수록 앞선 입력에 대한 정보를 모두 기억하기 어렵다. 또한 순서대로 입력을 처리해야하므로 (병렬 처리가 불가능하여) 연산 시간 단축에 한계가 있다.
예시) 영어-독일어 번역 모델
Transformer는 인코더와 디코더를 N개씩 쌓은 구조로 되어있으며, 마지막 인코더의 출력이 각 디코더에 영향을 미친다. 논문에서는 N=6을 사용하였다.
Decoder에 있는 <sos>는 문장의 시작(start of string)을 나타내는 토큰이다. 문장의 끝(end of string)은 <eos>를 사용한다.
Transformer는 위 그림과 같이 문장 내 단어들을 순서대로 입력하는 것이 아니라 병렬적으로 동시에 입력하게 된다.
Transformer는 입력을 순차적으로 받지 않는다면 단어의 위치 정보를 어떻게 반영할까?
각 단어의 임베딩 벡터에 위치 정보를 더하는 것을 Positional Encoding이라고 한다.
Positional Encoding 함수에서 pos는 입력 문장에서의 임베딩 벡터의 위치를, i는 임베딩 벡터 내의 차원 인덱스를 의미한다. 각 차원 인덱스가 짝수일 경우에는 sin 함수를, 홀수 일 때는 cos 함수를 사용한다.
𝑑𝑚𝑜𝑑𝑒𝑙은 각 출력층의 차원 정보를 나타내는 Transformer의 하이퍼파라미터 값이다. 여기서 임베딩 벡터의 차원이기도 하며 앞으로 계속 등장하는 개념이다. 예시에서는 간단하게 𝑑𝑚𝑜𝑑𝑒𝑙 = 4로 표현하였지만, 논문에서는 512를 사용하였다.
Attention은 단어 사이의 상관관계를 게산하여 중요한 정보에 집중하는 Mechanism이다. Transformer에서 사용되는 어텐션들은 다음과 같다.
Encoder Self-Attention은 인코더 자체의 Query, Key, Value 벡터만을 가지고 모든 정보를 고려하여 Attention을 적용한다. Maked Decoder Self-Attention과 Encoder-Decoder Attention은 디코더에서 이루어지며 적용방식이 조금 다르다. 자세한 내용은 아래서 설명한다.
앞서 소개한 세 종류의 Attention이 어느 위치에 적용되는지 나타낸 그림이다. Mutli-head라는 것은 Attention을 병렬적으로 처리함을 나타낸다.
Position Encoding을 거친 문장은 인코더에 입력되어 num_layers 만큼의(논문에서는 6) 인코더를 통과하게 되며, 각 인코더마다 2개의 Sublayer로 구성되어 있다. Multi-head Self-Attention은 Self-Attention을 병렬적으로 처리하는 구조이며, FFNN은 Feed Foward Neural Network를 말한다. 핵심적인 구조인 Self-Attention를 먼저 이해해보자.
Attention Function은 쿼리(Query)와 키(Key)의 유사도를 구한 후, 이를 가중치로 하여 값(Value)의 Weight Sum을 최종적으로 Return 한다. 여기서 Query는 유사도를 구하고 싶은(또는 궁금한) 단어를 가리킨다고 생각하면 된다.
Self-Attention은 Attention의 한 종류로, 입력 문장의 모든 단어 벡터들에 존재하는 Q, K, V를 가지고 Attention을 수행한다. 다른 문장을 이용하는 것이 아닌 한 문장 내부에서 Attention이 이루어진다고 해서 Self-Attention라 불린다.
유사도 계산을 통해서 입력 문장의 it은 ‘street’이 아닌 ‘animal’을 나타낼 확률이 높다는 것을 확률적으로 나타낸다. 위 예시에서는 it를 Query로 하여 Attention을 수행하였는데, 이런 방식으로 모든 단어에 대해서 Attention을 수행하는 것이 Self-Attention이다.
예시) “I am a student” 문장
Positional Encoding을 거친 입력 문장(𝑑𝑚𝑜𝑑𝑒𝑙)에서 Q, K, V 벡터를 구하기 위해서는 각각의 가중치 행렬을 곱해야 한다. 최종적으로 얻어지는 Q, K, V 벡터는 𝑑𝑚𝑜𝑑𝑒𝑙 / num_heads의 크기를 갖는다. 논문에서는 𝑑𝑚𝑜𝑑𝑒𝑙 = 512, num_head = 8을 사용하였다.
예시에서는 간단하게 𝑑𝑚𝑜𝑑𝑒𝑙 = 4, num_head = 2로 표현하였다.
Q벡터는 전체 문장 중에서 유사도를 구하고 싶은 주체가 되는 단어를 나타낸다. K벡터는 유사도를 구할 대상을 나타내며 벡터 내적을 통해 Attention Score를 구하게 된다.
아래 예시에서는 “I am a student”라는 문장에서 단어 “I’와 다른 단어들 사이의 Attention Score를 구한 것이다.
Attention Score를 구하는 과정에서 dk^1/2로 나누게 되는데 이는 내적 결과를 보정하기 위한 값이며 이 때문에 “Scaled”라는 말이 붙는다. dk는 앞서 구했던 𝑑𝑚𝑜𝑑𝑒𝑙 / num_heads 를 나타낸다. (논문에서는 8)
이렇게 구한 Attention Score에 Softmax Function을 적용하여 Attention Distribution을 구할 수 있다. 이를 가중치로 사용하여 V벡터의 Weight Sum을 구한 결과가 최종적인 Attention Value가 된다.
여태까지는 이해를 돕기 위해서 한 단어에 대해 연산을 구하는 과정을 설명하였다. 실제로는 전체 단어들에 대해 행렬 연산을 통해 Self-Attention을 수행하게 된다.
행렬 연산을 이용하면 전체 단어들에 대한 Q, K, V 벡터를 한번에 구할 수 있다.
벡터 내적도 마찬가지로 전체 내적 결과를 담은 Attention Score를 얻을 수 있는데 이후에 Attention Value 또한 모든 단어에 대해 얻을 수 있다.
전체 계산과정을 수식으로 나타내면 아래와 같다. 여기서의 Attention은 Attention Value Matrix를 말한다.
앞에서 입력 벡터의 𝑑𝑚𝑜𝑑𝑒𝑙 차원을 그대로 사용하지 않고 𝑑𝑚𝑜𝑑𝑒𝑙 / num_heads 차원을 갖는 Q, K, V 벡터로 바꾼 것은 Multi-Head 연산을 위함이다.
논문 저자는 한 번의 Attention보다 여러번의 Attention을 병렬로 사용하는 것이 다양한 관점을 학습하는 데에 더 효과적이라고 판단하였다.
각각의 head에서 Self-Attention을 수행하고 얻은 결과를 임베딩 차원 축으로 병합(Concatenation)을 수행한다. 병합을 통해서 얻은 Attention Value Matrix는 다시 𝑑𝑚𝑜𝑑𝑒𝑙 차원을 갖게 된다.
병합된 행렬은 또 한번의 행렬 곱을 통해 최종적으로 입력과 같은 (Seq_len, dmodel) 크기의 행렬이 되며, Multi-head Self-Attention의 최종 출력이 된다.
이렇게 입력과 출력을 같은 크기게 되도록 하는 것은 동일한 구조의 인코더에 다시 입력하기 위함과 Residual Connection 때문이다.
FFNN은 인코더와 디코더에서 공통적으로 가지고 있는 sublayer이다.
이를 식으로 나타내면 아래와 같다.
x는 앞서 구한 Multi-Head Attention의 출력인 (seq_len, 𝑑𝑚𝑜𝑑𝑒𝑙)의 크기를 가지는 행렬이다. 이 때 가중치 W1은 (𝑑𝑚𝑜𝑑𝑒𝑙, 𝑑ff), W2는 (𝑑ff, 𝑑𝑚𝑜𝑑𝑒𝑙)의 크기를 가진다. 논문에서는 𝑑ff = 2048을 사용한다. 가중치는 인코더마다 하나의 값이 사용된다.
FFNN을 통과한 결과도 Self-Attention과 마찬가지로 (seq_len, 𝑑𝑚𝑜𝑑𝑒𝑙)의 크기가 보존된다. 이를 통해 출력을 다음 인코더의 입력으로 사용할 수 있다.
지금까지 sublayer에 대해 설명하면서 지나친 부분이 있다. 각 sublayer 출력에 연결된 Add & Norm이라고 써있는 부분인데, Add는 Residual Connection을 Norm은 Layer Normalization을 뜻한다.
앞서 각 sublayer의 최종 출력이 입력과 같은 크기여야 한다고 설명했는데, 이는 입력과 출력을 더하는 Residual Connection의 구조 때문이다. Residual Connection은 깊은 신경망 구조에서 학습 난이도를 낮추기 위해 사용된다.
가령 Multi-head Attention에서는 다음과 같이 표현할 수 있다.
Sublayer, Residual Connection을 거쳐 Layer Normalization을 진행한 결과를 다음과 같이 나타낼 수 있다.
층 정규화(Layer Normalization)는 텐서의 마지막 차원의 평균과 분산을 구하고, 정규화를 진행하여 학습을 돕는다. 여기서는 𝑑𝑚𝑜𝑑𝑒𝑙이 마지막 차원에 해당된다.
𝑑𝑚𝑜𝑑𝑒𝑙 차원 방향을 기준으로 평균과 분산을 구한 후, 그 값들로 각 화살표 방향의 벡터들에 대하여 정규화를 진행한다. 정규화된 벡터는 아래처럼 표기한다.
실제 계산을 위해서 우선, 기존 벡터의 원소(스칼라)마다 다음과 같이 정규화를 진행한다.
여기서 epsilon은 분모가 0이 되는 것을 방지하는 값이다.
이제 학습 가능한 파라미터인 두 벡터를 아래처럼 초기값을 설정하고 최종 수식을 완성한다.
지금까지 Encoder Self-Attention과 Position-wise FFNN, Residual Connection, Layer Normalization에 대해 알아보았다. 디코더도 앞서 설명한 구조와 비슷하지만 2개의 Attention Layer가 인코더와 다소 다른 형태를 가지고 있다.
디코더는 Masked Self-Attention, Encoder-Decoder Attention, Position-wise FFNN의 3 sublayer로 구성되어있다. FFNN과 Add & Norm 구조는 동일하므로 2개의 Attention Layer에 대해서만 알아보려한다.
첫번째 sublayer에 주목해보자. 디코더도 인코더와 마찬가지로 Positional Encoding된 문장 행렬이 입력된다. 인코더와 차이점은 인코더는 번역하고 싶은 문장인 “I am a student”를 입력했다면, 디코더는 그의 번역 결과인 “<sos> je suis étudiant” 행렬을 입력한다. 이는 번역을 진행할 때 앞서 번역한 결과 단어들을 참고하기 위함이다.
이 때 한가지 문제점이 있다. 예를 들어 suis를 예측하는 시점에는 <sos>와 je만을 참고하여 번역을 수행하여야 하고, 예측 대상인 suis나 그 뒷 단어를 미리 참고해서는 안된다. RNN 계열의 모델과 달리 Transformer는 순차적으로 입력하지 않으므로 위와 같은 문제를 방지하기 위해 look-ahead mask를 도입했다.
앞서 설명한 것과 같이 Self-Attention을 수행한 후, 예측하는 시점과 같거나 후순위에 있는 단어는 참고하지 못하도록 미리보기(look-ahead)를 막는다(mask).
검은색 부분이 maked 부분이며, 실제로는 해당 부분을 제외한 행렬을 만들 수 없으므로 masking할 부분을 Softmax Fucntion을 거친 결과가 0에 가깝게 나오도록 절댓값이 매우 큰 음수로 설정한다.
이번에는 두번째 sublayer를 살펴보자. Encoder-Decoder Attention은 Query 벡터는 디코더로부터 가져오며, Key, Value 벡터는 인코더로부터 가져온다. 서로 다른 곳에서 벡터들을 가져오므로 Self-Attention이 아니다. 이를 통해 디코더는 인코더의 출력을 참고하게 된다.
이후 다른 연산들은 앞서 설명한 것과 같다.
Attention Is All You Need - 논문
나동빈님 논문 리뷰 - Youtube
딥 러닝을 이용한 자연어 처리 입문 - 위키독스