트랜스포머(Transformer)

현주·2025년 1월 1일
0
post-thumbnail

트랜스포머(Transformer)

트랜스포머는 2017 구글이 발표한 "Attention is all you need" 논문에서 나온 모델이다.
기존의 seq2seq의 구조 인코더-디코더를 따르면서도 어텐션만으로 구현한 모델이다.
RNN을 사용하지 않고 인코더 디코더 구조를 설계했는데도 더 높은 번역 성능을 보여줬다.


트랜스포머 구조


트랜스포머는 RNN을 사용하지 않지만 인코더-디코더 구조를 유지한다.

이전 seq2seq 구조에서는 인코더와 디코더에서 각각 하나의 RNN이 t개의 시점을 가지는 구조였지만 여기서는 인코더와 디코더라는 단위가 N개로 구성되는 구조이다.


위 그림은 트랜스포머 구조를 간단히 나타낸 그림이다. 인코더로부터 정보를 전달받아 디코더로가 출력 결과를 만들어낸다. seq2seq 구조처럼 sos토큰을 입력받고 eos토큰이 나올 때까지 연산한다.

트랜스포머의 인코더와 디코더는 각 단어의 임베딩 벡터를 입력받는게 아닌 임베딩 벡터에서 조정된 값을 입력받는다.


포지셔널 인코딩

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

하지만 트랜스포머는 순차적으로 입력받는게 아니고 병렬로 한번에 처리하기 때문에 다른 방법으로 단어의 위치를 알려줘야 하는데,
각 단어의 임베딩 벡터에 위치 정보를 더해 모델의 입력으로 사용하는 방법이다.

이 방법을 포지셔널 인코딩(positional encoding)이라고 한다.

임베딩 벡터에 포지셔널 인코딩 값이 더해지는 과정을 시각화한 사진이다.

트랜스포머는 위치 정보를 부여하기 위해 사인함수와 코사인 함수를 사용한다.

PE(pos,2i)=sin(pos/100002i/dmodel)PE_{(pos, 2i)} = sin(pos/10000^{2i/d_{model}})
PE(pos,2i+1)=cos(pos/100002i/dmodel)PE_{(pos, 2i+1)} = cos(pos/10000^{2i/d_{model}})

pos=pos = 임베딩 벡터의 위치
i=i = 임베딩 벡터 내 각 차원의 인덱스

사인 함수와 코사인 함수를 사용하는 이유

  • 1과 -1 사이를 반복하는 주기함수이기 때문에 벡터값이 너무 크지 않아서 의미정보가 변질되지 않음
  • 다양한 주기의 사인, 코사인 함수를 동시에 사용하여 각각의 단어 벡터가 각각의 차원마다 서로 다른 위치 정보를 갖게 됨

이렇게 임베딩 벡터에 위치 정보를 부여하면 문장 내에서 같은 단어여도 순서를 고려한 임베딩 벡터가 된다.


어텐션

트랜스포머에서 사용되는 어텐션은 세가지가 있다. 셀프 어텐션은 본질적으로 Query, Key, Value가 동일한 경우를 말한다.

트랜스포머에서의 어텐션은 인코더의 셀프 어텐션, 디코더의 마스크드 셀프어텐션, 디코더의 인코더-디코더 어텐션 세가지가 있다.

그림에서 볼 수 있는 어텐션에 붙은 멀티헤드라는 단어는 병렬적으로 어텐션을 수행한다는 의미이다.


인코더


트랜스포머는 하이퍼파라미터인 num_layers 개수의 인코더 층을 쌓는다.

인코더를 하나의 층이라고 본다면 하나의 인코딩 층은 총 두개의 서브 층으로 구성된다. 바로 셀프 어텐션과 피드포워드 신경망이다.

위 그림에서 멀티헤드 셀프어텐션은 셀프어텐션을 병렬적으로 사용했다는 의미이다. 포지션 와이즈 FFNN은 일반적인 피드 포워드 신경망이다.

먼저 셀프 어텐션에 대해서 알아보자.


셀프 어텐션

트랜스포머에서 셀프 어텐션이라는 어텐션 기법이 등장한다.

이전글에서 이미 어텐션 기법에 대해 다루었지만 다시 복습하고 셀프어텐션은 무엇이 다른지 알아보겠다.

먼저 어텐션은 주어진 쿼리에 대해서 모든 키와의 유사도를 각각 구하고, 구한 유사도를 가중치로 키와 맵핑되어있는 값에 반영한다. 그리고 유사도가 반영된 값을 모두 가중하여 리턴한다.

seq2seq에서 사용한 어텐션에서 Q, K, V는 각각 다음과 같다.
Q = Query : t 시점의 디코더 셀에서의 은닉 상태
K = Keys : 모든 시점의 인코더 셀의 은닉 상태들
V = Values : 모든 시점의 인코더 셀의 은닉 상태들
Q와 K가 서로 다른 값이다.

하지만 셀프 어텐션은 자기 자신에게 어텐션을 사용하기 때문에 Q, K, V가 각각 입력문장의 모든 단어 벡터이기 때문에 서로 같다.


위 문장에서 it이 의미하는 것이 animal인 것을 우리는 바로 알 수 있지만, 기계는 그러지 못한다.

하지만 셀프 어텐션을 수행하면 문장 내의 단어들끼리 유사도를 구하기 때문에 it이 의미하는 것이 무엇인지 알아낼 확률이 높아진다.

셀프 어텐션 동작 메커니즘

먼저 셀프 어텐션을 하기 위해 각 단어 벡터들로부터 Q벡터, K벡터, V벡터를 얻어야한다.

이때, 모델의 초기 입력인 dmodeld_{model}의 차원을 가졌던 각 단어 벡터들을 또 다른 입력인 num heads로 나눈 값을 Q, K, V벡터의 차원으로 결정한다.

위 그림은 입력 문장이 I am a student일 때 student라는 단어를 Q, K, V벡터로 변환하는 모습을 보여준다.

dmodeld_{model}이 512이고 num heads가 8이라면 서로 다른 가중치 행렬을 곱하여 64의 크기를 가지는 Q, K, V벡터를 얻어낸다.

이 과정을 반복하여 입력문장의 모든 단어의 Q, K, V벡터를 얻는다.

모든 Q,K,V벡터를 얻었다면 기존의 어텐션 메커니즘과 동일하다.

각 Q벡터는 모든 K벡터에 대해 어텐션 스코어를 구하고 어텐션 분포를 구한 후 이 분포를 사용해 모든 V벡터를 가중합하고 어텐션 값을 구한다.

이 과정을 모든 Q벡터에 대해 반복한다.
어텐션 함수는 어텐션 스코어를 구하는 방법에 따라 종류가 다양하다.

트랜스포머에서는 이전글에서 설명한 닷-프로덕트 어텐션에서 특정값으로 나눠주는것이 추가된 스케일드 닷-프로덕트 어텐션(Scaled dot-product Attention)을 사용한다.

scorefuntion(q,k)=qknscorefuntion(q,k) = \frac{q\cdot k}{\sqrt n}

위 그림은 입력문장 I am a student에서 I에 대한 Q벡터가 모든 K벡터에 대해 어텐션 스코어를 구하는 과정이다.

dkd_k는 앞에서 언급한 K벡터의 차원이다. 앞에서 dmodeld_{model}은 512, num_heads는 8이었다. 둘이 나누면 dkd_k는 64가 되고 64의 제곱근은 8이다.

따라서 q벡터와 k벡터를 내적한 값을 8로 스케일링한 값이 어텐션 스코어이다.
이 어텐션 스코어는 단어 I가 입력 문장 I am a student에서 모든 단어들과 얼마나 연관이 있는지 보여주는 값이다.

이제 구한 어텐션 스코어에 소프트맥스 함수를 사용해 어텐션 분포를 구하고 각 V벡터와 가중합하여 어텐션 값을 구한다. 어텐션 값은 컨텍스트 벡터라고도 할 수 있다.

지금까지 원활한 이해를 위해 벡터 연산으로 셀프 어텐션 메커니즘을 알아봤다. 어텐션 값 연산은 벡터 연산이 아닌 행렬 연산을 사용하면 일괄적으로 계산이 가능하다.

행렬 연산으로 다시 알아보자.

각 단어 벡터마다 일일히 가중치 행렬을 곱하지 않고 문장 행렬에 가중치 행렬을 곱해 Q, K, V행렬을 구한다.


다음으로 Q행렬에 전치한 K행렬을 곱해준다. 그리고 내적한 결과 행렬 값에 dk\sqrt {d_k}를 전체에 나눠주면 어텐션 스코어 행렬이 된다. 이후 어텐션 스코어 행렬에 소프트맥수 함수를 사용하고 V행렬을 곱하면 어텐션 값 행렬이 된다.

모두 정리하면 아래 그림과 같다.

Attention(Q,K,V)=softmax(QKTdk)VAttention(Q,K,V)=softmax(\frac{QK^T}{\sqrt{d_k}})V

멀티 헤드 어텐션


앞에서 Q, K, V벡터의 차원을 dmodeld_{model}과 num head의 초기 입력값으로 결정된다고 언급했었다.

트랜스포머 연구진은 한번의 어텐션을 수행하는 것보다 여러번의 어텐션을 병렬로 사용하는것이 효과적이라고 판단했다.

그래서 dmodeld_{model}의 차원을 num heads개로 나누어 Q,K,V벡터의 차원을 갖게하고 num heads개의 병렬 어텐션을 수행한다.

num heads가 8이라면 8개의 병렬 어텐션이 수행되는데 이때 나온 8개의 어텐션 값 행렬을 어텐션 헤드라고 한다. 이때 가중치 행렬 8개 각각의 값은 어텐션 헤드마다 모두 다르다.

어텐션을 병렬로 수행하면 얻을 수 있는 이점은 여러 시각에서 입력 문장을 볼 수 있다는 것이다.

예를 들어 민수는 학교에 가지 않았다. 그는 감기에 걸렸기 때문이다.에서 첫번째 어텐션 헤드는 민수와 그를 연관도가 높게 본다면, 두번째 어텐션 헤드는 민수와 학교의 연관도를 높게 볼 수 있다.

각 헤드는 다른 시각에서 보고있기 때문이다.


병렬 어텐션을 모두 수행하면 모든 어텐션 헤드를 연결한다.

헤드를 모두 연결한 행렬에 또 다른 가중치 행렬 WoW^o를 곱하면 멀티-헤드 어텐션의 최종 결과물이다.

결과물인 멀티-헤드 어텐션 행렬은 인코더의 입력이었던 문장 행렬의 크기와 동일하다. 트랜스포머는 동일한 구조의 인코더를 쌓은 구조이다. 인코더의 입력의 크기가 출력에서도 같은 크기로 유지되어야, 다음 인코더에서 다시 입력될 수 있기 때문이다.


패딩 마스크


패딩 마스크는 입력 문장에 PAD 토큰이 있을 때 어텐션에서 제외하기 위한 연산이다. PAD 토큰은 실질적인 의미를 가지는 단어가 아니기 때문이다. 그래서 이에 대한 유사도를 구하지 않기 위해 마스킹해주는 것이다.


어텐션 스코어 행렬에서 Key에 PAD 토큰이 있다면 해당 열 전체를 마스킹해준다.

마스킹하는 방법은 행렬의 마스킹 위치에 매우 작은, 무한대에 가까운 음수값을 넣어준다.

그러면 어텐션 스코어 행렬이 소프트맥스 함수를 지나면서 아주 작은 음수값은 0이 되면서 제외된다.

여기까지 인코더의 첫번째 서브층인 멀티 헤드 어텐션에 대해 알아보았다. 이제 두번째 서브층인 포지션-와이즈 피드 포워드 신경망에 대해 알아본다.


포지션-와이즈 피드 포워드 신경망

포지션 와이즈 FFNN은 인코더뿐만 아니라 디코더에도 가지고 있다.

FFNN(x)=MAX(0,xW1+b1)W2+b2FFNN(x)=MAX(0,xW_1+b1)W_2+b_2


여기서 x는 멀티 헤드 어텐션의 결과로 나온 행렬을 말한다. 매개변수들은 하나의 인코더 층 내에서 동일하게 사용된다. 각 인코더 층마다는 다른 값을 가진다.


인코더의 입력을 벡터단위로 봤을 때 첫번째 서브층을 지나 FFNN층을 지나는 모습이다. 두번째 서브층의 출력 결과도 인코더의 입력 크기로 보존된다. 하나의 인코더 층을 지난 행렬은 다음 인코더 층으로 전달되고 동일한 연산이 반복된다.


잔차 연결과 층 정규화


잔차연결과 층 정규화(Add & Norm)는 인코더에 추가적으로 사용하는 기법이다.


잔차연결은 서브층의 입력과 출력을 더하는것이다. 위 그림은 멀티 헤드 어텐션의 입력과 멀티 헤드 어텐션의 결과가 더해지는 과정이다.

그 후에는 층 정규화를 한다. 잔차 연결의 입력을 xx, 잔차 연결과 층 정규화를 수행한 결과 행렬을 LNLN이라고 하면 수식은 다음과 같다.

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

트랜스포머에서 층 정규화는 텐서의 마지막 차원인 dmodeld_{model}의 차원에 대해서 평균과 분산을 구해 값을 정규화하여 학습을 돕는다.


화살표는 dmodeld_{model} 차원의 방향이다. 먼저 화살표 방향으로 평균과 분산을 구한다.


각 화살표 방향의 벡터를 xix_i라고 할 때 층 정규화를 수행하면 lniln_i라는 벡터로 정규화된다.

lni=LayerNorm(xi)ln_i=LayerNorm(x_i)


지금까지 인코더에 대해 알아보았다. 인코더는 num layers만큼 순차적으로 층 연산한 결과를 디코더에 전달한다. 그리고 디코더 역시 num layers만큼의 연산을 하는데 이때마다 인코더가 전달한 출력을 각 디코더 층 연산에서 사용한다.

디코더에 대해 알아보자.


디코더


디코더는 세개의 서브층을 가진다.

디코더도 인코더처럼 임베딩층과 포지셔설 인코딩을 거친 후의 문장 행렬이 입력된다.

트랜스포머는 교사강요를 사용하여 훈련되기 때문에 I am a student의 번역 문장이자 입력에 대한 정답이라고 할 수 있는 <sos> je suis étudiant 문장 행렬을 한번에 입력받는다.

여기서 RNN 신경망은 입력 단어를 시점마다 순차적으로 입력받아 다음 단어 예측에 현재 시점을 포함한 이전시점에 입력받은 단어만 참고할 수 있다.

하지만 트랜스포머는 정답 문장 행렬을 한꺼번에 입력받기 때문에, 현재 시점의 단어를 예측할때 미래 시점의 단어도 참고할 수 있어진다. 쉽게말하면 컨닝을 할 수 있다는것이다.

이 컨닝 현상을 막기위해 디코더의 첫번째 서브층에서 룩-어헤드 마스크(look-ahead mask)를 도입한다.


룩-어헤드 마스크

디코더의 멀티 헤드 어텐션 층에서 룩-어헤드 마스크가 이루어지는데 이는 인코더의 첫번째 서브층인 멀티 헤드 어텐션 층과 동일한 연산을 수행한다. 동일한 연산에 마스킹을 추가한 것이다.

먼저 셀프 어텐션을 통해 어텐션 스코어 행렬을 구한다.

그리고 자신보다 미래에 있는 단어들은 참고하지 못하게 마스킹한다. 이제 자기 자신과 이전 단어들만 참고할 수 있게 된다.


인코더-디코더 어텐션

디코더의 두번째 서브층은 인코더-디코더 어텐션층이다. 이전의 어텐션들과 같이 멀티 헤드 어텐션을 수행하지만 셀프 어텐션은 아니다.

앞에서 언급했지만 셀프 어텐션은 Qeury, Key, Value가 같은 어텐션을 말한다. 하지만 인코더-디코더 어텐션은 Query는 디코더 행렬이고 Key와 Value는 인코더 행렬이다.

두개의 빨간 화살표는 인코더 마지막층에서 전달된 Key와 Value이다. 검정 화살표는 디코더의 첫번쨰 서브층의 결과 행렬인 Query이다.

어텐션 스코어 행렬을 구하는 과정이다. 이후에 멀티 헤드 어텐션을 수행하는 과정은 다른 어텐션들과 같다.

마지막 서브층인 와이즈 포지션 FFNN층은 앞에서 정리했기 때문에 다시 설명하지 않겠다. 세개의 서브층 모두 연산 후에 잔차연결, 층 정규화가 수행된다.


정리

현재 AI의 중심이라고 할 수 있는 트랜스포머의 핵심은 Self-Attention(입력 내 단어 간 관계를 계산하여 문맥을 이해), Multi-Head Attention(다양한 관점에서 어텐션 학습), Positional Encoding(시퀀스의 순서 정보를 추가), Feed-Forward Network(비선형 변환으로 표현력 강화)이라고 할 수 있다. 또한 병렬 처리가 가능하기 때문에 학습 속도가 빠르고 성능이 뛰어나다.


마무리

다양한 자연어 처리 태스크에서 사용되었던 RNN 계열의 신경망 LSTM, GRU가 트랜스포머로 대체되어간다. "Attention is All You Need"가 보여준 트랜스포머는 단순한 설계로도 강력한 모델을 만들 수 있다는 것을 보여준다.

트랜스포머 정리했으니 다음 글은 BERT로 결정했다.🐛

레퍼런스

profile
공부하는 블로그😎

0개의 댓글