Transformer 는 "Attention is all you need" 라는 논문에서 등장한 모델로, 인코더 디코더 구조를 유지하지만 RNN 사용 없이 Attention 만으로 구현한 모델이다. 어떻게 가능했을까?
seq2seq 은 말그래도 시퀀스를 시퀀스로, 바꿔주기 위해 인코더-디코더 구조를 사용한다. RNN 을 포함한 seq2seq 는 time step 을 따라 정보를 처리할 수 있다는 점에서 아이디어가 있었지만 입력 시퀀스를 하나의 벡터 표현으로 압축하여 정보의 손실이라는 한계를 마주쳤다. 이에따라, 한계의 벡터 표현이 아닌 현재 시점 예측 때마다 인코더의 시퀀스를 보고, 어디에 집중할 지 결정하는 attention 구조가 등장했고 긴 문장에 대한 성능을 높였다.
트랜스포머는 여기서 아이디어를 따와 인코더, 디코더를 구성할 때 RNN이 아닌 어텐션만 사용하는 구조를 말한다.
트랜스포머 논문에 소개된 하이퍼파라미터 값들을 알아보자.
입력과 출력의 크기, 즉 임베딩 벡터의 차원은 512를 유지한다.
하나의 인코더와 디코더로 이루어진 구조를 층이라고 한다면, 트랜스포머에서는 총 6층을 쌓는다.
트랜스포머에서 어텐션을 할 때 한 번 하는 것보다 분할하여 병렬로 수행한 후, 합친다. 병렬 계산을 하는 단위를 head 라 하며, 병렬의 개수는 8개이다.
어텐션 이외에 트랜스포머에는 feed-foward 신경망이 존재하며 은닉층의 크기는 2048이다.
트랜스포머는 RNN 없이 인코더, 디코더 구조를 유지한다고 하였다. 이전 seq2seq 모델에서는 인코더, 디코더에 각각 1개의 RNN을 사용하고, t개의 시점을 가지는 구조였다. 트랜스포머는 기본적으로 인코더, 디코더를 각각 6개 사용하여 쌓아올린다. (encoders, decoders)
그 중 인코더-디코더 하나씩 이루어진 1층을 보자! 인코더로부터 정보를 전달받아 디코더가 출력을 만들어내는 구조이다.
인코더, 디코더 내부 구조를 보기 전 입력에 대해 알아보자. 단순히 워드임베딩을 가져다 입력으로 쓰는 게 아니라 무엇인가 차이가 있다.
어떻게 위치정보를 인코딩하느냐? 단어에 대한 임베딩 벡터와 positional encoding 값을 더한다. (그 후에 encoder 입력)
실제로는 임베딩 벡터를 모아 문장 행렬을 만들고, positional encoding 또한 matrix 를 만들어 이 둘을 더한다.
구체적으로 positional encoding 은 어떤 값으로 구성되는가? 함수를 보자.
최종적으로 같은 단어라 하더라도 문장 내 어디에 위치하느냐에 따라서 달라지는 임베딩 벡터를 유도하는 것이 positional encoding 이다.
트랜스포머에서 사용하는 어텐션은 3가지가 있다.
Encoder Self-Attention 은 encoder에서, Masked Decoder Self-Attention은 Decoder에서, Encoder-Decoder Attention은 그 후 Decoder에 적용된다. 모든 attention 이름 앞에 달린 Multi-head 라는 것은 어텐션을 병렬적으로 수행하는 것을 의미한다.
![](https://velog.velcdn.com/images/kyungmin1029/post/2fa05237-e99b-4786-ad23-6c0f3b922e24
/image.png)
이제 하나하나씩 뜯어보는 과정을 거치자.
트랜스포머는 인코더, 디코더를 하나의 층으로 하여 이를 여러 개 쌓는다고 하였다. 인코더 하나의 구조는 다음 과 같으며, num_layers 만큼 이를 쌓는다.(논문에선 = 6)
첫번째 요소인 Self-Attention 을 알아보자. 구체적인 계산과정까지 다룰 것이기에 내용이 길어질 것이다.
어텐션 함수는 기본적으로 주어진 쿼리에 대해 모든 키와의 유사도를 구한 다음, 유사도를 가중치로 키에 대한 값을 계산한다. (attention weight) 쿼리와 이 값들을 모두 가중합하면 Attention Value 를 계산할 수 있다.
변형된 Self Attention에서의 Q, K, V는 전부 동일하다.
Q: 입력 문장의 모든 단어 벡터들
K: 입력 문장의 모든 단어 벡터들
V: 입력 문장의 모든 단어 벡터들
이러한 Self-Attention 은 어떤 의도가 있을까? 모든 단어 쌍 사이의 관계를 고려한 후 학습할 수 있게 된다. 예컨대 트랜스포머를 통해 한국어 - 영어로 번역하는 문제를 풀 경우, Self-Attention 을 인코더에서 쓴다는 것은 입력 시퀀스인 한국어만 보고 그 내에서 모든 쌍의 관계를 고려해 학습한다는 것을 말한다.
셀프 어텐션은 임베딩 디멘션의 차원을 가지는 단어 벡터들을 사용하여 어텐션을 수행하는 것이 아니고, 우선 각 단어들로부터 Q, K, V 벡터를 먼저 얻는다. (참고해야 할 것은 이때는 512차원은 아니고 이보다 작은 차원인 64차원의 Q, K, V 벡터를 얻는다.)
64라는 값은 전체 임베딩 디멘션인 512 차원을 num_head, 그러니까 병렬연산의 개수인 8고 나눈 512 / 8 = 64 에서 나온 값이다. 이제 단어 벡터 하나를 Q, K, V로 변환하는 과정을 보자.
단어 벡터에 곱해지는 가중치 행렬 W는 (단어의 차원; 여기서는 4) X (단어의 차원/num_heads) 가 된다. 이 가중치 행렬은 학습 가능하고, 위 그림처럼 student 벡터 하나로부터 서로 다른 가중치를 곱하여 각각의 Q, K, V 벡터를 얻는다. (이를 I am a student 모든 단어에 대해 수행)
각 단어에 대해 Q, K, V 값을 구했으니 각 Q벡터는 모든 K벡터에 대해 어텐션 스코어를 구하고, 분포를 이용하여 V벡터를 가중합하여 컨텍스트 벡터를 구한다. 이 과정을 모든 Q에 대해 반복.
여기선 Scaled dot 이라고 하여 q와 k를 곱한 후 n에 루트를 씌운 값으로 나눈 값을 어텐션 함수로 사용한다. 트랜스포머에서는 k벡터의 차원을 나타내는 에 루트를 씌운 값으로 나누어주었다. 따라서 논문에서는 64에 루트를 씌운 8로 나눈 값을 가진다.
어텐션 스코어 => 어텐션 분포를 구하고, 다시 각 V벡터와 가중합하면 Attention Value 값을 구할 수 있다. (= 단어 I에 대한 Context vector)
I, am, a, student 를 따로 연산하지 않고 행렬로 일괄 계산하자. 각 벡터마다 Q, K, V는 (그림의 예시에서) 1 X 2 였으니 4단어가 붙는다면 Q, K, V 행렬은 각각 4 X 2 가 될 것이다. 문장 행렬에 가중치 행렬을 곱하여 Q,K,V 행렬을 구하는 그림이다.
각 단어에 대해 Q, K, V 를 구한 것이니 쿼리 Q에 대해 키 K 행렬을 곱하면 (곱하고 scale 하는 과정을 거치면) Self 연산에 대한 attention distribution 이 완성될 것이다. 이 값에 각 단어의 V를 곱하면 최종 구하고자 하는 값인 Attention Value Matrix 가 계산된다.
아래의 수식까지 확인하며 계산 과정에 대한 설명을 정리해볼 수 있다.