Boostcamp AI Tech

[Boostcamp Day-9] DL Basic - Transformer

ju_young 2021. 8. 13. 06:32
728x90

Transformer

기계 번역의 경우를 생각했을 때 다음과 같이 하나의 문장을 입력으로 받아 다른 언어로 된 번역을 출력으로 내놓는다고 하자. 이러한 것을 'sequence to sequence'라고 부르기도 한다.

 

transformer에는 아래 그림처럼 크게 encoding을 하는 encoder, decoding을 하는 decoder, 그 사이를 이어주는 connection으로 구성되어있다는 것을 확인할 수 있다.

encoding하는 부분은 다음과 같이 여러 개의 encoder를 쌓아 올린것이고 decoding 부분도 endcoding 부분과 동일한 개수만큼의 decoder를 쌓아올린 것이다.

encoder들은 모두 아래와 같이 self-attention과 feed forward neural network를 가지는 똑같은 구조이다.

우선 다음과 같이 3개의 각 단어들을 embedding을 통해 벡터로 바꿨다 하고 각 크기를 512하고 하겠다.

이 embedding된 벡터는 self-attention의 입력으로 받게 될 것이고 출력된 벡터는 다시 feed forward neural network에 입력되고 출력될 것이다. 그리고 다시 출력된 벡터는 다음 encoder에 입력되며 계속 진행이 될 것이다.

 

그런데 이때 x1 벡터가 self-attention에 들어가게되면 단순히 x1에 대한 정보만을 고려하는 것이 아니라 x2, x3 벡터까지 모두 고려해서 z1이라는 벡터를 출력해주는 것이다. 즉, x1, x2, x3는 서로 dependent하다고 말할 수 있다. 하지만 feed forward neural network에 들어가는 z1에는 각각의 벡터들에 dependency가 없기 때문에 다양한 병렬처리가 될 수 있다.


Self-Attention

이제 self-attention에서 어떻게 계산되는지 살펴보자.

  1. embedding된 벡터 x1, x2는 각각 가중치 행렬 $W^Q$, $W^K$, $W^V$를 곱하여 Queries, Keys, Values라는 3개의 벡터를 만들어낸다.

  1. 다음으로 score를 계산해야한다. x1 벡터를 기준으로 보면 x1의 queries 벡터 q1과 keys 벡터 k1의 내적 $q1 \cdot k1$ 부터 시작해 다른 위치에 있는 단어들의 keys 벡터들과의 내적을 구한다. 내적을 구한다는 것은 서로의 유사성(각도)을 알아낸다는 것이다.

  1. key 벡터의 사이즈가 64라고 했을 때 64의 제곱근인 8로 각 score를 나눈다. 이렇게 하는 이유는 더 안정적인 gradient를 가지기위함이다. 그리고 나눠진 score는 softmax 함수를 적용시켜 확률값을 얻어낸다.

  1. value 벡터에 이전 softmax로 얻은 값을 곱한다. 이렇게 함으로서 우리가 집중하고 싶은 단어들은 남겨두고 필요없는 단어들은 0.001과 같은 작은 숫자를 곱해 없애버릴 수 있다.
  2. 4번까지 진행한 모든 값들을 더한다.


Self-Attention의 행렬 계산

앞서 설명한 self-attention의 계산 단계는 다음과 같이 행렬을 사용해 간단히 하나의 식으로 표현할 수 있다.


Multi-headed Attention(MHA)

MHA는 단지 위에서 계산한 self-attention을 여러번 수행한 것과 같다고 보면 된다. 즉, 위에서한 계산을 하나의 HEAD라고하고 총 2개가 있다면 다음과 같이 2번 연산이 일어나는 것이다.

만약 HEAD가 8개라면 서로 다른 8개의 가중치 행렬들을 통해 8번의 연산이 일어나게 된다. 즉, 서로 다른 Z행렬을 얻게 된다는 것이다.

하지만 이렇게 계산한 8개의 행렬은 feed-forward layer에 보낼 수 없다. 왜냐하면 feed-forward layer는 오직 한 개의 행렬만을 입력받기 때문이다. 그래서 다음과 같이 Z_0부터 Z_7까지 모두 이어 붙인 후 또 다른 행렬 W_0를 곱해 하나의 행렬과 합쳐버린다.


Positional encoding

sequence data는 앞뒤 순서가 중요한데 이전까지는 입력 문장에서 단어들의 순서에 대해서 고려하지 않았다. 이런 문제를 해결하기위해 "positional encoding"이라는 벡터를 embedding한 벡터에 더해준다. 이렇게 더해주면 각 단어의 위치와 sequence 내의 다른 단어 간의 위치 차이에 대한 정보를 알 수 있게 해준다.

예를들어 embedding의 사이즈가 4라고 한다면 positional encoding은 아래 그림과 같다.


Residuals

각 encoder 내의 sub-layer가 residual connection으로 연결되어 있으며 layer-nomalization 과정을 거친다. decoder도 마찬가지로 똑같이 적용되어 있다.

 


Layer Normalization

Batch Normalization의 경우에는 각 covariate shift 현상을 해결하기 위해 제안된 Normalization 중 하나이고 mini-batch 단위로 평균과 분산을 추정하는 방법을 사용하였다. 하지만 mini-batch의 크기에 의존한다는 점과 Recurrent 기반의 모델에 적용했을 때 매 time-step마다 계산하여 훨씬 복잡하게 만드는 문제점이 있다.

 

Layer Normalization가 바로 이러한 문제를 해결하기위한 방법론이다.

위 그림은 Batch Normalization와 Layer Normalization의 차이를 시각적으로 잘 나타낸 그림인데 우선 Batch Normalization은 모든 sample에 있는 각 feature의 평균과 분산을 구하여 normalization을 하는 것이고 Layer Normalization은 각 sample에 있는 모든 feature들에 대해서 평균과 분산을 구하여 normalization을 하는 것이다. 따라서 Layer Normalization는 input에 대해서만 처리되므로 batch와는 상관없게 되고 다음과 같은 특징을 가지게 된다.

  1. 데이터마다 각각 다른 (평균, 표준편차)를 가지므로 서로 다른 길이를 갖는 sequence data가 들어와도 적용이 가능하다.
  2. mini-batch의 크기에 영향을 받지 않는다.

Decoder

encoder의 가장 윗단의 출력은 Key 벡터와 Value 벡터로 변형된다. 왜냐하면 해당 R번째의 query 벡터와 나머지 단어들의 key 벡터를 곱해서 attention을 만들고 value 벡터를 sum하기 때문에 Key 벡터와 Value 벡터가 필요한 것이다.

 

그리고 이 Key 벡터와 Value 벡터가 decoder의 입력으로 들어가서 autoregressive한 방식으로 반복 진행이 된다. 여기서 autoregressive한 방식이라는 것은 현재 위치의 이전 위치들에 대해서만 고려하여 하나씩 출력하는 것을 말한다. 또한 이것은 현재 스텝 이후의 위치(미래의 데이터)들에 대해서 masking을 해줌으로서 가능해진다.

 

728x90