트랜스포머(Transformer)는 2017년 구글이 발표한 논문인 "Attention is all you need"에서 나온 모델로 기존의 seq2seq의 구조인 인코더-디코더를 따르면서도, 논문의 이름처럼 어텐션(Attention)만으로 구현한 모델이다. 이 모델은 RNN을 사용하지 않고, 인코더-디코더 구조를 설계하였음에도 성능도 RNN보다 우수하다는 특징을 갖고있다.
기존의 seq2seq 모델은 인코더-디코더 구조로 구성되어져 있었다. 여기서 인코더는 입력 시퀀스를 하나의 벡터 표현으로 압축하고, 디코더는 이 벡터 표현을 통해서 출력 시퀀스를 만들어냈다. 하지만 이러한 구조는 인코더가 입력 시퀀스를 하나의 벡터로 압축하는 과정에서 입력 시퀀스의 정보가 일부 손실된다는 단점이 있었고, 이를 보정하기 위해 어텐션이 사용되었다.
그런데 어텐션을 RNN의 보정을 위한 용도가 아니라 아예 어텐션으로 인코더와 디코더를 만든 것이 Transformer이다.
트랜스포머는 RNN을 사용하지 않지만 기존의 seq2seq처럼 인코더에서 입력 시퀀스를 입력받고, 디코더에서 출력 시퀀스를 출력하는 인코더-디코더 구조를 유지하고 있다. 다만 다른 점은 인코더와 디코더라는 단위가 N개가 존재할 수 있다는 점이다.
인코더로부터 정보를 전달받아 디코더가 출력 결과를 만들어내는 트랜스포머 구조 디코더는 마치 기존의 seq2seq 구조처럼 시작 심볼 <sos>를 입력으로 받아 종료 심볼 <eos>가 나올 때까지 연산을 진행한다. 이는 RNN은 사용되지 않지만 여전히 인코더-디코더의 구조는 유지되고 있음을 보여준다.
- dmodel : 트랜스포머의 인코더와 디코더에서의 정해진 입력과 출력의 크기를 의미
- num_layers : 트랜스포머 모델에서 인코더와 디코더가 총 몇층으로 구성되었는지를 의미
- num_heads : 트랜스포머에서는 어텐션을 사용할 때 병렬로 어텐션을 수행하고결과값을 다시 하나로 합치는 방식을 택했는데, 이 병렬의 개수를 의미
- dff : 트랜스포머 내부에는 피드 포워드 신경망이 존재하는데, 그 은닉층의 크기를 의미
단어를 순차적으로 입력받아 단어의 위치 정보를 갖는 RNN과 달리 트랜스포머는 단어 입력을 순차적으로 받는 방식이 아니므로 단어의 위치 정보를 다른 방식으로 알려줄 필요가 있다. 트랜스포머는 단어의 위치 정보를 얻기 위해서 각 단어의 임베딩 벡터에 위치 정보들을 더하여 모델의 입력으로 사용하는데, 이를 포지셔널 인코딩(positional encoding)이라고 한다.
위의 그림은 입력으로 사용되는 임베딩 벡터들이 트랜스포머의 입력으로 사용되기 전에 포지셔널 인코딩값이 더해지는 것을 보여준다.
- pos는 입력 문장에서의 임베딩 벡터의 위치를 나타내며, i는 임베딩 벡터 내의 차원의 인덱스를 의미한다.
- 위의 식에서 dmodel은 트랜스포머의 모든 층의 출력 차원을 의미하는 트랜스포머의 하이퍼파라미터이다.
포지셔널 인코딩 값- 트랜스포머는 사인 함수와 코사인 함수의 값을 임베딩 벡터에 더해주므로서 단어의 순서 정보를 더하여 준다.
- 위의 식에 따르면 임베딩 벡터 내의 각 차원의 인덱스가 짝수인 경우에는 사인 함수의 값을 사용하고 홀수인 경우에는 코사인 함수의 값을 사용한다.
각 임베딩 벡터에 포지셔널 인코딩값을 더하면 같은 단어라고 하더라도 문장 내의 위치에 따라서 트랜스포머의 입력으로 들어가는 임베딩 벡터의 값이 달라진다. 결국 트랜스포머의 입력은 순서 정보가 고려된 임베딩 벡터라고 보면 된다.
트랜스포머에서 사용되는 세 가지의 어텐션
주의할 점은 여기서 Query, Key 등이 같다는 것은 벡터의 값이 같다는 것이 아니라 벡터의 출처가 같다는 의미이다.
- 위 그림은 트랜스포머의 아키텍처에서 세 가지 어텐션이 각각 어디에서 이루어지는지를 보여준다.
- 세 개의 어텐션에 추가적으로 '멀티 헤드'라는 이름이 붙어있다. 이는 트랜스포머가 어텐션을 병렬적으로 수행하는 방법을 의미한다.
인코더의 구조
트랜스포머는 하이퍼파라미터인 num_layers 개수의 인코더 층을 쌓는다. 인코더를 하나의 층이라는 개념으로 생각한다면, 하나의 인코더 층은 크게 총 2개의 서브층(sublayer)으로 나뉘어집니다. 바로 셀프 어텐션과 피드 포워드 신경망이다.
셀프 어텐션의 의미
어텐션 함수는 주어진 '쿼리(Query)'에 대해서 모든 '키(Key)'와의 유사도를 각각 구한다. 그리고 구해낸 이 유사도를 가중치로 하여 키와 맵핑되어있는 각각의 '값(Value)'에 반영해준다. 그리고 유사도가 반영된 '값(Value)'을 모두 가중합하여 리턴한다.
어텐션 중에서는 셀프 어텐션(self-attention)이라는 것이 있다. 단지 어텐션을 자기 자신에게 수행한다는 의미한다.
seq2seq에서 어텐션을 사용할 경우의 Q, K, V의 정의
트랜스포머의 셀프 어텐션
셀프 어텐션에서는 Q, K, V가 전부 동일하다.
Q, K, V 벡터 얻기
셀프 어텐션은 인코더의 초기 입력인 dmodel의 차원을 가지는 단어 벡터들을 사용하여 셀프 어텐션을 수행하는 것이 아니라 우선 각 단어 벡터들로부터 Q벡터, K벡터, V벡터를 얻는 작업을 거칩니다. 이때 이 Q벡터, K벡터, V벡터들은 초기 입력인 dmodel의 차원을 가지는 단어 벡터들보다 더 작은 차원을 가진다.
기존의 벡터로부터 더 작은 벡터는 가중치 행렬을 곱하므로서 완성된다. 각 가중치 행렬은 dmodel×(dmodel/num_heads)의 크기를 가진다. 이 가중치 행렬은 훈련 과정에서 학습된다.
스케일드 닷-프로덕트 어텐션(Scaled dot-product Attention)
어텐션 함수의 종류는 매우 다양한데, 트랜스포머에서는 내적만 사용하지않고 내적값을 특 정값으로 나눠준 어텐션함수(score(q,k) = q⦁k/n^1/2) 즉 스케일드 닷-프로덕트 어텐션을 사용한다.
행렬 연산으로 일괄 처리하기
각 단어 벡터마다 일일히 가중치 행렬을 곱하는 것이 아니라 문장 행렬에 가중치 행렬을 곱하여 Q 행렬, K 행렬, V행렬을 구한다.
행렬 연산을 통해 어텐션 스코어를 구한다. Q 행렬을 K 행렬을 전치한 행렬과 곱해준다.
이제 어텐션 분포를 구하고, 이를 사용하여 모든 단어에 대한 어텐션 값을 구한다. 이는 어텐션 스코어 행렬에 소프트맥스 함수를 사용하고, V 행렬을 곱하는 것으로 해결된다.
멀티 헤드 어텐션(Multi-head Attention)
트랜스포머 연구진은 한 번의 어텐션을 하는 것보다 여러번의 어텐션을 병렬로 사용하는 것이 더 효과적이라고 판단하였다. 그래서 dmodel의 차원을 num_heads개로 나누어 dmodel/num_heads의 차원을 가지는 Q, K, V에 대해서 num_heads개의 병렬 어텐션을 수행한다. 이때 각각의 어텐션 값 행렬을 어텐션 헤드라고 부른다.
패딩 마스크(Padding Mask)
트랜스포머에서는 Key의 경우에 <PAD> 토큰이 존재한다면 이에 대해서는 유사도를 구하지 않도록 마스킹(Masking)을 해주기로 했다. 여기서 마스킹이란 어텐션에서 제외하기 위해 값을 가린다는 의미이다. 어텐션 스코어 행렬에서 행에 해당하는 문장은 Query이고, 열에 해당하는 문장은 Key이다. 그리고 Key에 <PAD>가 있는 경우에는 해당 열 전체를 마스킹을 해준다.
마스킹을 하는 방법은 어텐션 스코어 행렬의 마스킹 위치에 -무한대에 가까운 매우 작은 음수값을 넣어주는 것이다.
현재 마스킹 위치에 매우 작은 음수 값이 들어가 있으므로 어텐션 스코어 행렬이 소프트맥스 함수를 지난 후에는 해당 위치의 값은 0에 굉장히 가까운 값이 되어 단어 간 유사도를 구하는 일에 <PAD> 토큰이 반영되지 않게 된다.
포지션 와이즈 FFNN은 인코더와 디코더에서 공통적으로 가지고 있는 서브층이다. 완전 연결 FFNN(Fully-connected FFNN)이라고 해석 할 수 있다.
매개변수 W1, b1, W2, b2는 하나의 인코더 층 내에서는 다른 문장, 다른 단어들마다 정확하게 동일하게 사용된다. 하지만 인코더 층마다는 다른 값을 가진다.
두번째 서브층을 지난 인코더의 최종 출력은 여전히 인코더의 입력의 크기였던 (seq_len, dmodel)의 크기가 보존되고 있다.
트랜스포머에서는 이러한 두 개의 서브층을 가진 인코더에 추가적으로 사용하는 기법이 있는데, 바로 Add & Norm입니다. 더 정확히는 잔차 연결(residual connection)과 층 정규화(layer normalization)를 의미한다.
잔차 연결(Residual connection)
트랜스포머에서 서브층의 입력과 출력은 동일한 차원을 갖고 있으므로, 서브층의 입력과 서브층의 출력은 덧셈 연산을 할 수 있다.
층 정규화(Layer Normalization)
층 정규화는 텐서의 마지막 차원에 대해서 평균과 분산을 구하고, 이를 가지고 어떤 수식을 통해 값을 정규화하여 학습을 돕는다.
인코더는 총 num_layers만큼의 층 연산을 순차적으로 한 후에 마지막 층의 인코더의 출력을 디코더에게 전달한다.
디코더도 인코더와 동일하게 임베딩 층과 포지셔널 인코딩을 거친 후의 문장 행렬이 입력된다.
seq2seq의 디코더에 사용되는 RNN 계열의 신경망은 입력 단어를 매 시점마다 순차적으로 받으므로 다음 단어 예측에 현재 시점 이전에 입력된 단어들만 참고할 수 있다. 반면, 트랜스포머는 문장 행렬로 입력을 한 번에 받으므로 현재 시점의 단어를 예측하고자 할 때, 입력 문장 행렬로부터 미래 시점의 단어까지도 참고할 수 있는 현상이 발생한다.
이를 위해 트랜스포머의 디코더에서는 현재 시점의 예측에서 현재 시점보다 미래에 있는 단어들을 참고하지 못하도록 룩-어헤드 마스크(look-ahead mask)를 도입했다.
미래에 있는 단어들은 참고하지 못하도록 다음과 같이 마스킹한다.
디코더의 두번째 서브층은 멀티 헤드 어텐션을 수행한다는 점에서는 이전의 어텐션들(인코더와 디코더의 첫번째 서브층)과 같지만, 셀프 어텐션은 아니다.
셀프 어텐션은 Query, Key, Value가 같은 경우를 말하는데, 인코더-디코더 어텐션은 Query가 디코더인 행렬인 반면, Key와 Value는 인코더 행렬이기 때문이다.
디코더의 두번째 서브층을 확대해보면
두 개의 화살표는 각각 Key와 Value를 의미하며, 이는 인코더의 마지막 층에서 온 행렬로부터 얻는다. 반면, Query는 디코더의 첫번째 서브층의 결과 행렬로부터 얻는다는 점이 다르다.
이 때, 어텐션 스코어 행렬을 구하는 과정
Transformer 잘 보았습니다.
궁금한 점이 있는데 I am a student 에서 je suis etudiant 를 예측하기 위해서 Transformer 모델이 어떻게 동작하는지 궁금한데,
어떻게 Decoder의 input에 I am a student의 스페인 번역 je suis etudiant를 바로 넣는지 이해가 되지 않습니다.
그래서 고민해봤는데 Transformer 모델의 동작이
Encoder의 input에는 I am a student 행렬을 넣은 후,
첫 번째로, Decoder의 input에는 만 넣으면 Decoder의 output에는 je가 출력되고,
두 번째로, 첫 번째 Decoder의 output에 출력된 je를 Decoder의 input에 합쳐서 je 행렬을 넣고, Decoder의 output에는 je suis가 출력되고,
세 번째로, 두 번째 Decoder의 output에 출력된 suis를 Decoder의 input에 합쳐서 je suis 행렬을 넣고, Decoder의 output에는 je suis etudiant가 출력되고,
이 것을 계속 진행해서 Decoder의 output의 맨 마지막 단어가 가 나올 때까지, 즉, 네 번째로 Decoder의 input에 je suis etudiant 행렬을 넣고, Decoder의 output에는 je suis etudiant 가 출력될 때까지 작동한다고 추측했습니다.
제가 생각한 Transformer 모델의 동작 방식이 맞는지 궁금합니다