본문 바로가기

Deep Learning/NLP

트랜스포머

자료 출처

 

그 유명한 "Attention is All You Need" 논문에서 제안한 구조로 아이디어는 "어텐션만으로 인코더와 디코더를 만들어보면 어떨까?"이다.

 

1. 트랜스포머의 주요 하이퍼파라미터

아래에서 정의하는 수치는 트랜스포머를 제안한 논문에서 사용한 수치로 사용자가 모델 설계시 임의로 변경할 수 있다.

 

  • $d_{model} = 512$ : 트랜스포머의 인코더와 디코더에서의 정해진 입력과 출력의 크기를 의미한다. 임베딩 벡터의 차원 또한 $d_{model}$이며, 각 인코더와 디코더가 다음 층의 인코더와 디코더로 값을 보낼 때에도 이 차원을 유지한다.
  • num_layer = 6 : 트랜스포머에서 하나의 인코더와 디코더를 층으로 생각하였을 때, 인코더와 디코더가 총 몇층으로 구성되었는지를 의미한다.
  • num_heads = 8 : 트랜스포머에서는 어텐션을 사용할 때, 한 번 하는 것보다 여러 개로 분할해서 병렬로 어텐션을 수행하고 결괏값을 다시 하나로 합치는 방식을 택했다. 이때 이 병렬의 개수를 의미한다.
  • $d_{ff} = 2048$ : 트랜스포머 내부 피드 포워드 신경망의 은닉층의 크기를 의미이고, 피드 포워드 신경망의 입출력 크기는 $d_{model}$이다.
  • $d_k$ : 하이퍼파라미터는 아니고 $d_{model}$과 num_layer에 의해 결정되는 값으로 $d_k = d_{model} /$ num_layer이다.

 

 

2. 트랜스포머(Transformer)

fig. 1

 

위의 그림은 인코더로부터 정보를 전달받아 디코더가 출력 결과를 만들어내는 트랜스포머의 구조를 간단하게 보여준다. 인코더와 디코더가 각각 여러개 쌓였다는 의미로 Encoders와 Decoders로 표현하였다.

 

트랜스포머의 인코더와 디코더는 단순히 각 단어의 임베딩 벡터들을 입력받는 것이 아니라 임베딩 벡터에서 조정된 값을 입력받는다. 이 부분을 알아보자.

 

 

3. 포지셔널 인코딩(Positional Encoding)

RNN이 자연어 처리에서 유용했던 이유는 단어의 위치에 따라 단어를 순차적으로 입력받아서 처리하는 RNN의 특성으로 인해 각 단어의 위치 정보를 가질 수 있다는 점에 있다.

 

하지만 트랜스포머는 단어 입력을 순차적으로 받는 방식이 아니므로 단어의 위치 정보를 다른 방식으로 알려주어야 한다. 트랜스포머는 단어의 위치 정보를 얻기 위해서 각 단어의 임베딩 벡터에 위치 정보들을 더하여 모델의 입력으로 사용하는데, 이를 포지셔널 인코딩(positional encoding)이라고 한다.

 

포지셔널 인코딩 값은 아래 두 함수를 사용한다.

 

$PE(pos, 2i) = \sin (pos / {10000}^{2i \over d_{model}})$

$PE(pos, 2i+1) = \cos (pos / {10000}^{2i \over d_{model}})$

 

 

fig. 2

 

임베딩 벡터와 포지셔널 인코딩의 덧셈은 임베딩 벡터가 모여 만들어진 문장 행렬과 포지셔널 인코딩 행렬의 덧셈 연산을 통해 이루어진다.

 

pos는 입력 문장에서의 임베딩 벡터의 위치를 나타내며, i는 임베딩 벡터 내의 차원의 인덱스를 의미한다. 인덱스가 짝수일 때는 사인 함수를 사용하고 홀수일 때는 코사인 함수를 사용한다.

 

위와 같은 포지셔널 인코딩 방법을 트랜스포머의 입력은 순서 정보가 고려된 임베딩 벡터가 된다.

 

 

4. 어텐션(Attention)

트랜스포머에서는 세 가지의 어텐션이 사용된다.

 

  • 인코더의 셀프 어텐션 : Q = K = V
  • 디코더의 마스크드 셀프 어텐션 : Q = K = V
  • 디코더의 인코더-디코더 어텐션 : Q : 디코더 / K = V : 인코더

Q = K = V의 의미는 벡터의 값이 같다는 것이 아니라 벡터의 출처가 같다는 의미이다.

fig. 3

 

위 그림은 트랜스포머 구조에서 세 가지 어텐션이 각각 어디에서 수행되는지 보여준다. 추가적으로 세 가지 어텐션 모두 '멀티 헤드'라는 이름이 붙어있는데, 이는 어텐션을 병렬적으로 수행하는 방법을 의미한다.

 

 

5. 인코더(Encoder)

fig. 4

 

인코더의 구조는 num_layer 개수만큼 인코더 층을 쌓는다. 인코더를 하나의 층으로 생각한다면, 하나의 인코더는 크게 2개의 서브층(sublayer)으로 나뉘어진다. 셀프 어텐션과 피드 포워드 신경망이다.

 

 

6. 인코더의 셀프 어텐션

1) 셀프 어텐션의 의미와 이점

셀프 어텐션은 간단하게 Q, K, V의 출처가 모두 같은 것이라고 이해할 수 있다.

 

셀프 어텐션의 효과는 다음과 같다.

fig. 5

 

위의 예시 문장을 번역하면 '그 동물은 길을 건너지 않았다. 왜냐하면 그것은 너무 피곤하였기 때문이다.' 라는 의미가 된다. 그런데 여기서 "it"에 해당하는 것은 과연 "street"일까? "animal"일까? 우리는 피곤한 주체가 동물이라는 것을 아주 쉽게 알 수 있지만 기계는 그렇지 않다. 하지만 셀프 어텐션은 입력 문장 내의 단어들끼리 유사도를 구하므로서 "it"이 "animal"과 연관되었을 확률이 높다는 것을 찾아낸다.

 

2) Q, K, V 벡터 얻기

셀프 어텐션은 인코더의 초기 입력인 $d_{model}$의 차원을 가지는 단어 벡터들을 사용하여 셀프 어텐션을 수행하는 것이 아니라 우선 각 단어 벡터들로부터 Q벡터, K벡터, V벡터를 얻는 작업을 거친다. 이때 Q, K, V는 $d_{model}$보다 더 작은 차원인 $d_k$를 가진다. 논문에서는 $d_{model}=512$이고 num_layer가 6이므로 $d_k=64$가 된다.

 

단어 벡터를 Q, K, V의 벡터로 변환하는 과정을 보자.

fig. 6

 

기존의 벡터로부터 더 작은 벡터는 가중치 행렬을 곱하여 완성한다. 각 가중치 행렬의 크기는 $d_{model} \times d_k$이다. 이 가중치 행렬은 훈련 과정에서 학습된다.

 

위 그림은 student 벡터로 부터 Q, K, V 벡터를 얻는 모습이고, 모든 단어 벡터에 위와 같은 과정을 거치면 I, am, a, student는 각각의 Q, K, V를 얻는다.

 

3) 스케일드 닷-프로덕트 어텐션(Scaled dot-product Attention)

어텐션 스코어를 구하기 위해 닷-프로덕트를 수행할 때 내적한 값을 특정 값으로 나눠주는 어텐션이다. 여기서 사용되는 특정 값은 $\sqrt {d_k}$이다.

 

fig. 7

 

위 그림은 "I" 단어에 대한 어텐션 스코어를 구하는 과정을 보여준다. 이후 어텐션 값을 구하는 과정은 일반적인 어텐션과 동일하며 다음과 같다.

fig. 8

 

소프트맥스 함수를 적용하여 어텐션 분포를 구한 뒤 모든 단어의 각 V벡터와 가중합 하여 어텐션 값을 구한다.

 

이 과정은 행렬 연산으로 모든 단어에 대해 일괄 처리가 가능하다.

 

4) 행렬 연산으로 일괄 처리하기

fig. 9

 

fig.6에서는 단어 벡터 하나에 대해서만 Q, K, V 값을 구하였는데 행렬 연산으로 모든 단어 벡터에 대한 Q, K, V 값을 계산할 수 있다.

fig.10

 

그런 다음 Q와 전치한 K 벡터를 내적하여 모든 단어에 대한 어텐션 스코어를 계산할 수 있다. 그런 다음 이를 $\sqrt {d_k}$으로 나눠주고 소프트맥스 층에 통과 시킨 뒤 다시 모든 단어에 대한 V 벡터와 내적하여 모든 단어에 대한 어텐션 값 행렬을 얻을 수 있다.

fig.11

 

위의 행렬 연산에서 사용된 행렬의 크기를 모두 정리해보자. 우선 입력 문장의 길이를 seq_len이라고 해보자.

 

  • 문장 행렬의 크기 : (seq_len, $d_{model}$)

 

Q, K 벡터의 차원을 $d_k$, V 벡터의 차원을 $d_v$라고 하면

 

  • Q, K 행렬 : (seq_len, $d_k$)
  • V 행렬 : (seq_len, $d_v$)
  • $W^Q, W^K$ : $(d_{model}, d_k)$
  • $W^V$ : $(d_{model}, d_v)$

논문에서는 $d_k = d_v$이다.

 

결과적으로 어텐션 값 행렬 $a$의 크기는 (seq_len, $d_v$)가 된다.

 

5) 멀티 헤드 어텐션(Multi-head Attention)

fig.12

 

지금까지 배웠던 것 처럼 트랜스포머에서는 $d_{model}$의 차원을 가진 단어 벡터를 가지고 어텐션을 하지 않고 차원을 축소시킨 벡터로 어텐션을 수행하였다. 그리고 이를 num_heads 만큼 수행하여 총 num_heads개의 어텐션 값 행렬을 얻게 된다.

 

이러한 방식의 장점은 병렬로 수행하여 다른 시각으로 다양한 정보들을 수집할 수 있다는 것이다.

 

병렬 어텐션을 모두 수행하였다면 모든 어텐션 헤드를 연결(concatenate)한다. 모두 연결된 어텐션 헤드 행렬의 크기는 (seq_len, $d_{model}$)이 된다.

fig.13

 

그런 다음 또 다른 가중치 행렬 $W^O$를 곱하게 되는데, 이렇게 나온 결과 행렬이 멀티-헤드 어텐션의 최종 결과물이다.

fig.14

 

멀티 헤드 어텐션의 결과물의 크기는 인코더의 입력이었던 문장 행렬의 (seq_len, $d_{model}$) 크기와 동일하다. 인코더의 첫번째 서브층인 멀티 헤드 어텐션 단계를 끝마쳤을 때, 인코더의 입력으로 들어왔던 행렬의 크기가 계속 유지되고 있음을 기억해두자.

 

6) 패딩 마스크(Padding Mask)

입력 문장에 <PAD> 토큰이 있을 경우 어텐션에서 제외하기 위한 방법이다.

fig.15

 

어텐션 스코어 행렬에서 행에 해당하는 문장은 Query이고, 열에 해당하는 문장은 Key이다. 그리고 Key에 <PAD>가 있는 경우 해당 열 전체를 굉장히 작은 음수값으로 대체하여 마스킹을 해준다.

fig.16

 

그런 다음 소프트맥스 함수를 적용하여 어텐션 분포를 계산하면 패딩 마스크가 된 값은 사실상 0에 가까워 어텐션 연산에서 제외된다.

 

 

7. 포지션-와이즈 피드 포워드 신경망(Postion-wise FFNN)

포지션 와이즈 FFNN은 인코더와 디코더에서 공통적으로 가지고 있는 서브층이다.

fig.17

 

  • x : 멀티 헤드 어텐션의 결과물
  • $W_1$ : $(d_{model}, d_{ff}$
  • $W_2$ : $(d_{ff}, d_{model}$

각 매개변수는 하나의 인코더 층 내에서는 다른 문장, 다른 단어들마다 동일하게 사용되고, 인코더 층마다는 다른 값을 가진다.

 

 

8. 잔차 연결(Residual connection)과 층 정규화(Layer Normalization)

fig.18

 

Add & Norm은 잔차 연결과 층 정규화를 의미한다. 잔차 연결은 출력 값에 입력을 더한 것으로 다음과 같이 표현할 수 있다.

fig.19

 

층 정규화는 텐서의 마지막 차원에 대해서 평균과 분산을 구하고, 이를 가지고 어떤 수식을 통해 정규화하여 학습을 돕는다. 여기서 텐서의 마지막 차원이라는 것은 트랜스포머에서는 $d_{model}$에 해당하는 차원을 의미한다. 어떤 수식이란 다음과 같다.

 

$\hat{x}_{i, k} = \frac{x_{i, k}-μ_{i}}{\sqrt{σ^{2}_{i}+\epsilon}}$

 

k는 $x_i$의 각 차원을 의미하며, 최종 수식은 다음과 같다.

 

$LayerNorm(x_i) = \gamma \hat{x}_{i} + \beta$

 

감마와 베타는 학습 가능한 파라미터이다.

 

 

9. 인코더에서 디코더로

fig.20

 

이렇게 구현된 인코더는 총 num_layers만큼의 층 연산을 순차적으로 한 후에 마지막 층의 인코더의 출력을 디코더에게 전달한다. 인코더 연산이 끝났으면 디코더 연산이 시작되어 디코더 또한 num_layers만큼의 연산을 하는데, 이때마다 인코더가 보낸 출력을 각 디코더 층 연산에 사용한다. 여기서 빨간색 화살표가 2개인데 이는 각각 Key와 Value를 의미하여 인코더의 마지막 층에서 온 행렬로 부터 얻는다.

 

 

10. 디코더의 첫번째 서브층 : 셀프 어텐션과 룩-어헤드 마스크

fig.21

 

디코더도 인코더와 동일하게 임베딩 층과 포지셔널 인코딩을 거친 후의 문장 행렬이 입력된다. 디코더는 학습 과정에서 문장 행렬을 한 번에 입력받는다. 그리고 이 문장 행렬로부터 각 시점의 단어를 예측하도록 훈련된다.

 

여기서 문제점이 문장 행렬을 한 번에 입력받으므로 현재 시점을 단어를 예측할 때 미래 시점의 단어까지도 참고할 수 있는 현상이 발생한다. 그래서 이를 위해 미래 시점의 단어를 참고하지 못하도록 룩-어헤드 마스크(look-ahead mask)를 사용한다.

fig.22

 

우선 위와 같이 셀프 어텐션을 통해 어텐션 스코어 행렬을 계산한 뒤 다음과 같이 자신보다 미래에 있는 단어들은 참고하지 못하도록 마스킹한다.

fig.23

 

 

11. 디코더의 두번째 서브층 : 인코더-디코더 어텐션

인코더-디코더 어텐션에서는 Query는 디코더 행렬이고, Key, Value는 인코더 행렬이다. 어텐션 스코어 행렬을 구하는 과정은 다음과 같다.

fig.24

 

그런 다음 멀티 헤드 어텐션을 수행하는 방법은 위에서 설명한 방법과 동일하다. 디코더도 FFNN을 통과한 뒤 최종 결과물을 final layer에 입력으로 사용하여 $\hat{y}$인 vocab_size 크기를 가진 소프트맥스 결과물을 얻는다.

'Deep Learning > NLP' 카테고리의 다른 글

BERT  (0) 2024.02.07
ELMo(Embeddings from Language Model)  (0) 2024.02.06
어텐션 메커니즘  (0) 2024.01.11
서브워드 토크나이저 (Subword Tokenizer)  (0) 2023.10.05