본문 바로가기
Data Science/Paper Review

[Paper Review] Transformer - Attention is all you need

by VAMOSSS 2022. 3. 28.
반응형

 

본 글은 카이스트 최윤재 교수님의 Programming for AI (AI504, Fall 2020), Class 9: Recurrent Neural Networks WikiDocs의 딥 러닝을 이용한 자연어 처리 입문 바탕으로 정리한 글입니다.

https://jalammar.github.io/illustrated-transformer/ 도 참고하여 작성하였습니다!

Transformer

기존 seq2seq의 구조인 인코더, 디코더를 발전시킨 딥러닝 모델입니다.

가장 큰 차이점은 RNN을 사용하지 않고 Attention만으로 구현했다는 데 있습니다.

 

Transformer를 한 단어로 표현하자면 Parallelization(병렬화)입니다.

RNN의 순차적인 계산을 행렬곱으로 한 번에 처리합니다.

 

전체 과정을 자세히 표현하는 인코더와 디코더를 2개씩 사용한 Transformer입니다.

Transformer of stacked 2 encoders and decoders

 

기본 구조

transformer

트랜스포머는 기존의 seq2seq처럼 인코더에서 입력 시퀀스를 입력받고,

디코더에서 출력 시퀀스를 출력하는 인코더-디코더 구조를 유지합니다.

디코더는 마치 기존의 seq2seq 구조처럼 시작 심볼 를 입력으로 받아 종료 심볼 가 나올 때까지 연산을 진행합니다.

 

이전 seq2seq 구조에서는 인코더와 디코더에서 각각 하나의 RNN이 t개의 시점(time step)을 가지는 구조였다면

이번에는 인코더와 디코더라는 단위가 N개로 구성되는 구조입니다.

트랜스포머를 제안한 논문에서는 인코더와 디코더의 개수를 각각 6개 사용합니다. 

 

포지셔널 인코딩(Positional Encoding)

 

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

하지만 트랜스포머는 단어 입력을 순차적으로 받는 방식이 아니므로 단어의 위치 정보를 다른 방식으로 알려줄 필요가 있습니다.

 

트랜스포머는 단어의 위치 정보를 얻기 위해서 각 단어의 임베딩 벡터에 위치 정보들을 더하여 모델의 입력으로 사용하는데, 이를 포지셔널 인코딩(positional encoding)이라고 합니다.

 

이와 같은 포지셔널 인코딩 방법을 사용하면 순서 정보가 보존되는데, 각 임베딩 벡터에 포지셔널 인코딩의 값을 더하면 같은 단어라고 하더라도 문장 내의 위치에 따라서 트랜스포머의 입력으로 들어가는 임베딩 벡터의 값이 달라집니다.

이에 따라 트랜스포머의 입력은 순서 정보가 고려된 임베딩 벡터가 됩니다.

  • \(pos\) : 입력 문장에서의 임베딩 벡터의 위치를 나타냄
  • \(i\) : 임베딩 벡터 내의 차원의 인덱스를 의미

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

 

트랜스포머는 위치 정보를 가진 값을 만들기 위해서 아래의 두 개의 함수를 사용합니다.

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

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

 

sin cos 함수를 사용한 포지셔널 인코딩의 장점

  • 항상 -1부터 1까지 값
  • 모든 상대적인 포지셔널 인코딩
  • 길어도 가능

위의 식에 따르면 임베딩 벡터 내의 각 차원의 인덱스가 짝수인 경우에는 사인 함수의 값을 사용하고 홀수인 경우에는 코사인 함수의 값을 사용합니다.

  • 위의 수식에서 \((pos, 2i)\)일 때는 사인 함수를 사용
  • 위의 수식에서 \((pos, 2i+1)\)일 때는 코사인 함수를 사용
  • \(d_{model}\) : 트랜스포머의 모든 층의 출력 차원을 의미하는 트랜스포머의 하이퍼파라미터

임베딩 벡터 또한 \(d_{model}\)의 차원을 가지는데 위의 그림에서는 마치 4로 표현되었지만 실제 논문에서는 512의 값을 가집니다.

 

Attention

Transformer에는 다른 종류의 세 Attention이 있습니다.

인코더의 셀프 어텐션 : Query = Key = Value
디코더의 마스크드 셀프 어텐션 : Query = Key = Value
디코더의 인코더-디코더 어텐션 : Query : 디코더 벡터 / Key = Value : 인코더 벡터
  • 주의할 점 : 여기서 Query, Key 등이 같다는 것은 벡터의 값이 같다는 것이 아니라 벡터의 출처가 같다는 의미
    • 즉, 디코더의 인코더-디코더 어텐션에서의 Key, Value는 인코더로부터 오는 것이고, Query는 디코더로부터 오는 것입니다.
  • 세 개의 어텐션에 추가적으로 '멀티 헤드'라는 이름은 트랜스포머가 어텐션을 병렬적으로 수행하는 방법을 의미

 

Transformer의 Encoder

인코더를 하나의 층이라는 개념으로 생각한다면, 하나의 인코더 층은 크게 총 2개의 서브층(sublayer)으로 나누어집니다.

  • 멀티 헤드 셀프 어텐션(Multi-head Self-Attention)
  • 피드 포워드 신경망(FFNN)

 

멀티 헤드 셀프 어텐션 (Multi-head Self-Attention)

어텐션의 개념

  • 어텐션 함수는 주어진 '쿼리(Query)'에 대해서 모든 '키(Key)'와의 유사도를 각각 구함
  • 그리고 구해낸 이 유사도를 가중치로 하여 키와 맵핑되어있는 각각의 '값(Value)'에 반영
  • 그리고 유사도가 반영된 '값(Value)'을 모두 가중합 하여 리턴

Attention에 대한 더 자세한 설명은 https://wannabenice.tistory.com/28 을 참고하시면 되겠습니다.

 

트랜스포머의 셀프 어텐션에서의 Q, K, V

Q : 입력 문장의 모든 단어 벡터들로부터의 Query
K : 입력 문장의 모든 단어 벡터들로부터의 Key
V : 입력 문장의 모든 단어 벡터들로부터의 Value

각 단어에 대한 Q, K, V 벡터를 구하고 스케일드 닷-프로덕트 어텐션을 수행하였던 과정들은 벡터 연산이 아니라

행렬 연산을 사용하면 일괄 계산이 가능합니다.

이것이 어텐션을 사용한 병렬 처리에 가장 큰 장점입니다.

Self-Attention in Detail

$$Attention(Q, K, V) = softmax({QK^T\over{\sqrt{d_k}}})V$$

우선, 각 단어 벡터마다 일일이 가중치 행렬을 곱하는 것이 아니라

문장 행렬에 가중치 행렬을 곱하여 Q행렬, K행렬, V행렬을 구합니다.

  1. Q행렬을 K행렬을 전치한 행렬과 곱해주면 각각의 단어의 Q벡터와 K벡터의 내적이 각 행렬의 원소가 되는 행렬이 결과로 나옵니다.
  2. 위의 그림의 결과 행렬의 값에 전체적으로 \(\sqrt{d_k}\)를 나누어주면 이는 각 행과 열이 어텐션 스코어 값을 가지는 행렬이 됩니다.
  3. 어텐션 스코어 행렬에 소프트맥스 함수를 사용하고, V행렬을 곱하면 각 단어의 어텐션 값을 모두 가지는 어텐션 값 행렬이 결과로 나옵니다.
  4. 병렬 어텐션을 모두 수행하였다면 모든 어텐션 헤드를 연결(concatenate)합니다. (모두 연결된 어텐션 헤드 행렬의 크기는 (seq_len, \(d_{model}\))가 됩니다.)
  5. 어텐션 헤드를 모두 연결한 행렬은 또 다른 가중치 행렬 \(W^o\)을 곱하게 되는데, 이렇게 나온 결과 행렬이 멀티-헤드 어텐션의 최종 결과물입니다.
병렬 처리에 대한 어텐션 레이어를 Multi Head Attention이라 합니다.
  • 트랜스포머 연구진은 한 번의 어텐션을 하는 것보다 여러 번의 어텐션을 병렬로 사용하는 것이 더 효과적이라고 판단
  • 그래서 \(d_{model}\)의 차원을 num_heads개로 나눈 의 차원을 가지는 Q, K, V에 대해서 num_heads개의 병렬 어텐션을 수행
    • num_heads : head의 수 (attention의 수)
    • 논문에서는 하이퍼 파라미터인 num_heads의 값을 8로 지정하였고, 8개의 병렬 어텐션이 이루어짐
    • 어텐션이 8개로 병렬로 이루어지게 되는데, 이때 각각의 어텐션 값 행렬을 어텐션 헤드라고 부릅니다.
    • 이때 가중치 행렬 \(W^Q\), \(W^K\), \(W^V\)의 값은 8개의 어텐션 헤드마다 전부 다릅니다.
  • 어텐션을 병렬로 수행하여 다른 시각으로 정보들을 수집하겠다는 의도도 있습니다.
    • 각 어텐션 헤드가 전부 다른 시각에서 보고 있어 다양한 정보를 수집해서 정확도 올린다는 의도
    • 기계 번역에 큰 도움을 줌
    • 사람의 문장은 모호할 때가 상당히 많고 1개의 어텐션으로 이 모호한 정보를 충분히 인코딩하기 어려움
    • 멀티 헤드 어텐션을 사용해서 되도록 연관된 정보를 다른 관점에서 수집해서 이 점을 보완
결과물인 멀티-헤드 어텐션 행렬은 인코더의 입력이었던 문장 행렬의 (seq_len, \(d_{model}$\))크기와 동일합니다.
  • 다시 말해 인코더의 첫 번째 서브층인 멀티-헤드 어텐션 단계를 끝마쳤을 때, 인코더의 입력으로 들어왔던 행렬의 크기가 아직 유지되고 있음
  • 첫번째 서브층인 멀티-헤드 어텐션과 두 번째 서브층인 포지션 와이즈 피드 포워드 신경망을 지나면서 인코더의 입력으로 들어올 때의 행렬의 크기는 계속 유지되어야 합니다.
  • 트랜스포머는 동일한 구조의 인코더를 쌓은 구조입니다. 논문 기준으로는 인코더가 총 6개입니다.
  • 인코더에서의 입력의 크기가 출력에서도 동일 크기로 계속 유지되어야만 다음 인코더에서도 다시 입력이 될 수 있습니다.

 

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

지금은 인코더를 설명하고 있지만, 포지션 와이즈 FFNN은 인코더와 디코더에서 공통적으로 가지고 있는 서브층입니다.

포지션-와이즈 FFNN는 쉽게 말하면 완전 연결 FFNN(Fully-connected FFNN)이라고 해석할 수 있습니다.

 

$$FFNN(x) = MAX(0, x{W_{1}} + b_{1}){W_2} + b_2$$

  • 여기서 \(x\)는 앞서 멀티 헤드 어텐션의 결과로 나온 (seq_len, \(d_{model}\))의 크기를 가지는 행렬을 말합니다.
  • 가중치 행렬 \(W_1\)은 \((d_{model}, d_{ff})\)의 크기를 가지고, 가중치 행렬 \(W_2\)은 \((d_{ff}, d_{model})\)의 크기를 가집니다.
    • 논문에서 은닉층의 크기인 \(d_{ff}\)는 앞서 하이퍼 파라미터를 정의할 때 언급했듯이 2,048의 크기를 가집니다.
    • 여기서 매개변수 \(W_1\), \(b_1\), \(W_2\), \(b_2\)는 하나의 인코더 층 내에서는 다른 문장, 다른 단어들마다 정확하게 동일하게 사용됩니다. 하지만 인코더 층마다는 다른 값을 가집니다.

 

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

트랜스포머에서는 이러한 두 개의 서브층을 가진 인코더에 추가적으로 사용하는 기법이 있는데, 바로 Add & Norm입니다.

잔차 연결(residual connection)과 층 정규화(layer normalization)를 의미합니다.

  • 딥러닝 모델을 학습하다 보면 역전파에 의해 포지셔널 인코딩이 많이 손실될 수 있음
  • 이를 보완하기 위해서 Residual Connection으로 입력된 값을 다시 한번 더해주는 것도 중요
  • Residual Connection 이후에는 레이어 Normalization을 사용해 학습의 효율을 증진

 

잔차 연결(Residual connection)

$$ H(x) = x+Sublayer(x)$$

  • 잔차 연결은 서브층의 입력과 출력을 더하는 것
  • 트랜스포머에서 서브층의 입력과 출력은 동일한 차원을 갖고 있으므로, 서브층의 입력과 서브층의 출력은 덧셈 연산을 할 수 있음
  • 잔차 연결은 컴퓨터 비전 분야에서 주로 사용되는 모델의 학습을 돕는 기법
  • 서브층이 멀티 헤드 어텐션이었다면 잔차 연결 연산은 다음과 같습니다.
    • \(H(x) = x+Multi-head\ Attention(x)\)

 

층 정규화 (Layer Normalization)

잔차 연결을 거친 결과는 이어서 층 정규화 과정을 거치게 됩니다.

잔차 연결의 입력을 \(x\), 잔차 연결과 층 정규화 두 가지 연산을 모두 수행한 후의 결과 행렬을 LN 이라고 하였을 때, 잔차 연결 후 층 정규화 연산을 수식으로 표현하자면 다음과 같습니다.

$$LN = LayerNorm(x+Sublayer(x))$$

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

층 정규화 수식

  • 평균과 분산을 통한 정규화
    • \(\hat{x}_{i, k} = \frac{x_{i, k}-μ_{i}}{\sqrt{σ^{2}_{i}+\epsilon}}\)
    • ε(입실론)은 분모가 0이 되는 것을 방지하는 값
  • 감마와 베타를 도입한 정규화
    • γ(감마)와 β(베타)라는 벡터를 준비합니다. 단, 이들의 초기값은 각각 1과 0 
    • γ와 β를 도입한 층 정규화의 최종 수식은 다음과 같으며 γ와 β는 학습 가능한 파라미터입니다.
      • \($ln_{i} = γ\hat{x}{i}+β = LayerNorm(x{i})$\)

 

Encoder Layer 정리

  • 인코더 레이어의 입력 벡터와 출력 벡터의 차원의 크기가 같다
  • 이 말은 즉슨 인코더 레이어를 여러 개 붙여서 또 사용할 수 있다
  • 중요한 점은 각각의 인코더 레이어는 서로의 모델 파라미터 즉 가중치를 공유하지 않고 따로 학습
  • Transformer encoder의 최종 출력 값은 6번째 인코더 레이어의 출력 값

 

Transformer의 Decoder

인코더는 총 num_layers만큼의 층 연산을 순차적으로 한 후에 마지막 층의 인코더의 출력을 디코더에게 전달합니다.

인코더 연산이 끝났으면 디코더 연산이 시작되어 디코더 또한 num_layers만큼의 연산을 하는데, 이때마다 인코더가 보낸 출력을 각 디코더 층 연산에 사용합니다.

 

인코더와 상당히 유사하게 생겼고, 인코더와 같이 6개의 동일한 레이어로 구성되어있습니다.

디코더는 기존 인코더 디코더의 작동 방식과 같이 최초 단어부터 끝 단어까지 순차적으로 이 단어를 출력합니다.

 

디코더도 역시 어텐션 병렬 처리를 적극 활용합니다.

 

셀프 어텐션(Self-Attention)과 룩-어헤드 마스크(look-ahead mask)

디코더도 인코더와 동일하게 임베딩 층과 포지셔널 인코딩을 거친 후의 문장 행렬이 입력됩니다.

 

seq2seq의 디코더에 사용되는 RNN 계열의 신경망은 입력 단어를 매 시점마다 순차적으로 입력받으므로 다음 단어 예측에 현재 시점을 포함한 이전 시점에 입력된 단어들만 참고할 수 있습니다.

 

반면, 트랜스포머는 문장 행렬로 입력을 한 번에 받으므로 현재 시점의 단어를 예측하고자 할 때, 입력 문장 행렬로부터 미래 시점의 단어까지도 참고할 수 있는 현상이 발생합니다.

 

이를 위해 트랜스포머의 디코더에서는 현재 시점의 예측에서 현재 시점보다 미래에 있는 단어들을 참고하지 못하도록 룩-어헤드 마스크(look-ahead mask)를 도입했습니다. 직역하면 '미리보기에 대한 마스크'입니다.

  • 룩-어헤드 마스크(look-ahead mask)는 디코더의 첫 번째 서브층에서 이루어집니다.
    • 디코더의 첫 번째 서브층인 멀티 헤드 셀프 어텐션 층은 인코더의 첫 번째 서브층인 멀티 헤드 셀프 어텐션 층과 동일한 연산을 수행합니다.
    • 오직 다른 점은 어텐션 스코어 행렬에서 마스킹을 적용한다는 점만 다릅니다
  • 자기 자신보다 미래에 있는 단어들은 참고하지 못하도록 다음과 같이 마스킹합니다.
  • 마스킹된 후의 어텐션 스코어 행렬의 각 행을 보면 자기 자신과 그 이전 단어들만을 참고할 수 있음을 볼 수 있습니다.
  • 그 외에는 근본적으로 셀프 어텐션이라는 점과, 멀티 헤드 어텐션을 수행한다는 점에서 인코더의 첫 번째 서브층과 같습니다.

 

인코더-디코더 어텐션 (Encoder-Decoder Attention)

디코더의 두 번째 서브층은 멀티 헤드 어텐션을 수행한다는 점에서는 이전의 어텐션들(인코더와 디코더의 첫번째 서브층)과는 공통점이 있으나 이번에는 셀프 어텐션이 아닙니다.

인코더의 첫번째 서브층 : Query = Key = Value
디코더의 첫번째 서브층 : Query = Key = Value
디코더의 두 번째 서브층 : Query : 디코더 행렬 / Key = Value : 인코더 행렬
  • 인코더의 멀티 헤드 어텐션과의 가장 큰 차이점은 디코더의 인코더-디코더 어텐션은 현재 디코더의 입력 값을 쿼리로 사용하고 인코더의 최종 출력 값을 키와 밸류로 사용
  • 디코더의 현재 상태를 쿼리로 취급, 인코더의 출력 값에서 중요한 정보를 키와 밸류로 획득해 디코더에 다음 단어에 가장 적합한 단어를 출력하는 과정

Query가 디코더 행렬, Key가 인코더 행렬일 때, 어텐션 스코어 행렬을 구하는 과정은 다음과 같습니다.

 

FFNN

디코더도 인코더와 마찬가지로 피드 포워드 레이어를 통해 최종 값을 벡터로 출력합니다.

 

벡터를 어떻게 실제 단어로 출력할까

실제 단어로 출력하기 위해서 디코더 최종 다음에는 Linear layer와 Sofrmax layer가 존재합니다

  • Linear layer : 소프트맥스의 입력 값으로 들어갈 로직을 생성
  • Softmax layer : 모델이 알고 있는 모든 단어들에 대한 확률 값을 출력하게 되고 가장 높은 확률을 지닌 값이 다음 단어가 됨

Label Smoothing

  • Transformer는 최종 단계도 Label Smoothing이라는 기술을 사용해서 모델의 퍼포먼스를 다시 한번 한 단계 업그레이드 시킴
  • 보통 딥러닝 모델을 소프트맥스로 학습하는 경우에는 레이블을 원핫인코딩으로 전환해주는데 transformer는 1인 거 같지만 1에 가깝고, 0인 거 같지만 0에는 가까운 값으로 표현
  • 이걸 Label smoothing이라 하는데 0 또는 1이 양인 정답은 1에 가까운 값, 오답은 0에 가까운 값으로 살짝살짝 변환
  • 모델 학습 시에 모델이 너무 학습 데이터에 치중하여 학습하지 못하도록 보완
  • 레이블이 Noisy 한 경우 도움이 많이 됨 (같은 입력 값인데 다른 출력 값들이 학습 데이터에 많을 경우)
반응형

댓글