Computer Science/구글 BERT의 정석

[구글 BERT의 정석] 1. 트랜스포머 입문

  • -
728x90
반응형

1. 1 트랜스포머 소개

기존의 RNN과 LSTM 네트워크의 경우 순차적 테스크에서 널리 사용되는 편이다.

CS231n에서 학습한 것처럼 순차적으로 데이터들이 들어오고 이를 활용한다는 측면에서, 시간에 따른 데이터들의 동향을 예측하기에 유리한 측면이 존재한다.

 

하지만, 해당 2개의 네트워크는 장기 의존성 문제를 가지고 있다. 장기 의존성 문제란 hidden state를 통해 과거의 정보를 저장할 때 문장의 길이가 길어지면 앞의 과거 정보가 마지막까지 잘 전달되지 못하는 현상을 말한다. 즉, 문장의 길이가 길어지면 길어질수록 앞쪽 부분에 대한 정보를 거의 잊고, 최근에 들어온 정보를 중점적으로 판단하기에 발생하는 문제라고 이해해주면 된다. 또한 이는 Vanishing gradient problem과도 관련이 되어있는 문제라고 할 수 있겠다.

그림 1 왼쪽 : RNN, 오른쪽 : LSTM

그나마, LSTM의 경우는 long-term-state와 short-term-state를 도입했기는 하지만 근본적으로 해당 문제를 해결하지는 못하였다.

 

이러한 한계점을 극복하기 위해 "Attention Is All You Need" 논문에서 트랜스포머라는 아키텍쳐를 제안하였다.

해당 아키텍쳐는 RNN에서 사용했던 순환 방식이 아닌 어텐션을 활용하게 된다. 해당 개념은 1.2장에서 자세하게 다룰 예정이다.

 

트랜스포머는 크게 2부분으로 구분할 수 있는데, 하나는 인코더이고 다른 하나는 디코더이다.

그림 2 영어 문장을 프랑스어 문장으로 변환하는 트랜스포머

 

작동하는 순서를 요약하면 다음과 같다.

  1. 인코더에 문장을 주입한다.
  2. 인코더가 입력 문장의 표현 방법을 학습한다.
  3. 주입한 문장이 인코더를 통해 나온 결과를 디코더로 보낸다.
  4. 디코더는 인코더에서 받은 결과를 바탕으로 사용자가 원하는 문장을 생성한다.

1. 2 트랜스포머의 인코더 이해하기

앞서 설명한 것처럼 트랜스포머는 크게 2부분으로 구분할 수 있는데, 이 중 인코더에 대해서 먼저 살펴볼 것이다.

N개로 누적된 인코더

그림 2에서는 트랜스포머의 인코더 부분을 단순하게 표현하였지만, 정확하게는 N개의 인코더가 쌓인 형태로 구성되어 있다.

 

각 인코더의 결과값은 이후의 인코더의 입력값으로 들어가는 구조를 띄고 있다. 최종적으로 마지막 인코더의 출력값이 입력 문장이 트랜스포머 인코더 부분을 거쳐 나온 결과가 된다. (앞서 설명한 것처럼, 해당 결과가 디코더의 입력값으로 들어가게 된다.)

참고로 위 아키텍쳐를 처음 제안한 "Attention Is All You Need" 논문에서는 N이 6으로 되어있다.

인코더의 구성 요소

또한 각 인코더 블록 내부는 두 가지 요소로 구성된다.

  • 멀티 헤드 어텐션(multi-head-attention)
  • 피드포워드 네트워크(feedfoward network)

각 과정을 세부적으로 차근차근 하나씩 살펴보도록 하자.

1.2.1  각 단어의 임베딩 추출

임베딩은 각각의 단어를 표현하는 벡터값을 의미하고, 모델 학습과정에서 같이 학습되는 값이다. 즉, 단어를 벡터로 대응시켜주는 개념이라고 생각해주면 된다. 해당 개념을 통해 추상적인 단어를 수학적인 벡터로 변환시키게 되는 것이다.

입력 행렬

문장 내에 속한 단어들의 임베딩 값을 활용해서 문장을 행렬로 표현할 수 있다. 위 그림에서 볼 수 있는 것처럼, 각 임베딩 값을 행 방향으로 concat시켜줌으로써 input matrix를 구할 수 있다. (각 행이 문장 내에 속한 단어들의 임베딩이고, 인베딩의 차원은 512이다.)

1.2.2 위치 인코딩 처리

다만, 위 방식처럼 embedding matrix를 단순히 병렬적으로 입력하게 되면 기존의 RNN이나 LSTM에서와 달리 단어의 순서 정보가 유지되지 않는다는 문제점이 발생하게 된다. 예를 들어 "James was hungry, so he ran to the restaurant"라는 문장이 있다고 가정하자. 만약 단어의 순서 정보가 유지되지 않는다면 he가 무엇을 지시하는지 파악할 수 없을 것이다. 이를 해결하기 위해 위치 인코딩 개념이 등장하게 된다.

위치 인코딩과 입력 행렬 사이의 연산

위치 인코딩(Positional encoding)이란 문장에서 단어의 위치(단어의 순서)를 기억하기 위한 인코딩이다. 앞에서 설명한 기존의 input matrix에 element-wise addition를 처리해서 트랜스포머에 입력값으로 넣어주게 된다. 따라서 트랜스포머에 들어가는 input matrix는 다음과 같은 2개의 정보를 포함하게 된다.

  1. 단어의 임베딩
  2. 문장의 단어 위치 정보

이를 위해서는 위치 인코딩이 만족해야하는 조건이 2가지 있는데, 이는 다음과 같다.

  1. 각 위치 별로 고유한 벡터 값을 가져야 한다.
  2. 서로 다른 시퀀스에 대해서도 적용이 가능해야 한다. (문장의 길이가 달라도 적용가능해야 한다.)
  3. 각 벡터 간 등간격 거리(equidistant)가 되어야 한다

위치 인코딩 공식

논문에서는 위치 인코딩을 계산하기 위해서 사인파 함수(sinusoidal function)을 활용하였다. 정의에서 볼 수 있는 것처럼 해당 벡터의 차원이 올라갈 수록 주파수는 감소하게 된다.

 

따라서 각 단어 별 위치 인코딩은 다음과 같이 표현된다.

$$\vec{p_t} = \begin{bmatrix} \sin({\omega_1}.t)\\ \cos({\omega_1}.t)\\ \\ \sin({\omega_2}.t)\\ \cos({\omega_2}.t)\\ \\ \vdots\\ \\ \sin({\omega_{d/2}.t})\\ \cos({\omega_{d/t}.t}) \end{bmatrix}_{d \times 1}$$

해당 공식을 활용하여 위치 임베딩을 나타내주면 다음과 같다.

추가적으로 기존 임베딩 행렬에 위치 인코딩을 더해주어야 하므로, 두 행렬의 차원은 같아야 한다.

$$d_\text{word embedding} = d_\text{postional embedding}$$

 

수학적인 접근은 다음 링크를 참고하도록 하자.

https://kazemnejad.com/blog/transformer_architecture_positional_encoding/

 

Transformer Architecture: The Positional Encoding - Amirhossein Kazemnejad's Blog

Transformer architecture was introduced as a novel pure attention-only sequence-to-sequence architecture by Vaswani et al. Its ability for parallelizable training and its general performance improvement made it a popular option among NLP (and recently CV)

kazemnejad.com

 

1.2.3 멀티 헤드 어텐션 (Multi-head attention)

먼저 멀티 헤드 어텐션을 설명하기에 앞서, 셀프 어텐션이 무엇인지에 대해 알아보자.

예를 들어 다음과 같은 문장이 있다고 가정하자.

A dog ate the food because it was hungry.

사람은 언어를 이해하고 있기 때문에 it이 지시하는 명사가 dog라는 것을 쉽게 파악할 수 있다. 하지만, 컴퓨터의 경우 언어 자체를 이해하는 것이 아니기 때문에 it이 dog과 food 중 어느 것을 지시하는지 직접적으로는 알지 못한다.

셀프 어텐션

컴퓨터가 이를 인식하기 위해서는 문장 안에 있는 다른 모든 단어들과 연결해서, 해당 단어가 문장 내에서 갖는 의미를 파악해야 한다. 해당 작업을 통해 해당 단어가 문장 내 어느 단어와 관련이 높은지를 파악하고, 이를 통해 단어의 의미를 파악하게 된다.  위의 경우 it을 문장 안에 있는 모든 단어와 연결하는 작업을 수행한 뒤, 컴퓨터는 it이 dog과 연관이 깊다는 것을 이해하게 된다. 이러한 작업을 셀프 어텐션이라고 부른다. 

 

이 과정까지는 쉽게 이해할 수 있을 것이다. 하지만 컴퓨터가 어떻게 모든 단어들과 연결해서 어느 단어와 연결이 높은지를 파악할 수 있단 말인가? 위 궁금증을 해소하기 위해, 내부적으로 어떻게 수학적으로 모델링 되었는지 살펴보도록 하자.

 

설명의 편의를 위해 각 단어의 임베딩 결과와 위치 인코딩 결과를 더한 입력 행렬(X)의 차원을 [3 x 512]라고 가정하자.

 

첫 번째로 입력 행렬(X)로 부터 쿼리(Q) 행렬, 키(K) 행렬, 밸류(V) 행렬을 생성한다. 이는 $W^Q$, $W^K$, $W^V$라는 3개의 가중치 행렬을 생성하고 이를 입력 행렬(X)과 곱해서 구할 수 있다. 초기에는 임의의 값을 가지고, 해당 가중치도 학습을 통해 최적화 된다.

 

이를 도식화하면 다음과 같다.

Query matrix, Key matrix, Value matrix

 

만약, 쿼리, 키, 밸류 벡터의 차원이 64라고 가정하면, 각 쿼리 행렬과 키 행렬, 밸류 행렬의 차원은 [3 x 64]가 된다.

 

두 번째로 쿼리($Q$) 행렬과 키($K^T$) 행렬의 내적연산을 수행한다. 정확하게는 쿼리 행렬과 키 행렬의 전치행렬을 matrix multiplication처리한다. 해당 과정을 도식화하면 다음과 같다.

Query, Key matrix matmul

해당 결과값이 수식적으로 어떠한 의미를 가지는지 살펴보도록 하자.

 

결과값의 첫번째 row vector의 값은 $Q$의 첫번째 행과 $K^T$의 모든 열의 cosine similarity 결과값이다. 잘 생각해보면, 해당 값은 해당 단어가 문장 내에서 어떠한 의미를 가지는지를 존재하는 모든 단어와 유사도를 계산하는 작업과 완벽하게 동일하다. 즉, 각 단어 벡터의 유사도를 모두 포함하고 있는 행렬이 $QK^T$인 것이다.

 

세 번째로 $QK^T$ 행렬을 키 벡터 차원의 제곱근 값으로 나눈다. 이를 통해 안정적인 경삿값을 얻을 수 있다.

쿼리와 키 벡터의 내적을 계산하고 키 벡터 차원의 제곱근 값으로 나눠주기 때문에 스케일 닷 프로덕트 어텐션(Scaled Dot Product Attention)라고도 불린다.

 

네 번째로 소프트맥스 함수를 통과하여 유사도를 0과 1사이의 값으로 normalize한다. 위 결과를 통해 나온 행렬을 스코어(Score) 행렬이라고 부른다. 해당 결과를 통해 해당 단어가 어느 단어와 유사한지를 추정할 수 있게 된다.

Score matrix

 

마지막으로 스코어 행렬과 밸류 행렬(V)을 matrix multiplication 한다. 위 결과를 통해 나온 행렬을 어텐션(Z) 행렬이라고 부른다.

Attention matrix

해당 결과값이 수식적으로 어떠한 의미를 가지는지를 살펴보도록 하자.

이에 앞서, 설명의 편의를 위해 문장에는 3개의 단어만 존재한다고 가정하자. 따라서 어텐션 행렬은 다음과 같이 표현할 수 있다.

Attention matrix result

이 중 I에 해당하는 벡터값의 의미를 분석하고, 이를 통해 어텐션 행렬의 의미를 분석해보도록 하겠다.

Score matrix를 보면 I는 0.9의 정도로 I와 관련되어 있고, am과는 0.07, good과는 0.03의 정도로 관련되어 있다. 해당 정보를 어텐션 값을 구할 때 반영해주는 것을 확인할 수 있다. 0.9의 정도로 I에 해당하는 밸류(V) 벡터값을 가지고, 0.07의 정도로 am에 해당하는 밸류(V) 벡터값을 가지고, 0.03의 정도로 good에 해당하는 밸류(V) 벡터값을 가진다. 즉, 관련되어있는 정도만큼 선형결합된 값을 $z_1$이 가지는 것이다.

 

정리하자면, 셀프 어텐션은 총 5개의 과정을 거쳐서 어텐션 행렬을 구하게 된다.

이를 하나의 수식으로 단순화 하면 다음과 같다.

$$z = \text{softmax}(\frac{QK^T}{\sqrt{d_k}})V$$

 

긴 여정이였다. 앞에서 설명한 셀프 어텐션 개념을 활용해서 멀티 헤드 어텐션(multi-head attention)에 대해서 이해해보도록 하자. 해당 개념이 등장하게 된 배경은 다음과 같다. 의미가 잘 맞게끔 스코어 행렬을 구하고, 이를 통해 어텐션 행렬을 구하게 되면 상관없지만 만약 그렇지 않은 경우에는 정확도가 많이 낮아질 수 있다. 따라서 어텐션 결과의 정확도를 높이기 위해 어텐션을 여러번 수행하여 그 결과값을 더해주었고, 이를 멀티 헤드 어텐션이라고 한다. 단, 각각의 어텐션에 대해서 $W^Q$, $W^K$, $W^V$ 값은 서로 다르게 취급한다. 예를 들어 8개의 어텐션 행렬을 구한다고 하면, 총 24개의 가중치를 초기화하고 튜닝하는 작업을 거치게 된다.

 

추가적으로 각 어텐션을 concat하는 과정을 다음과 같다.

$$\text{Multi-head attention}$$ $$= \text{concatenate}(Z_1, Z_2, \ldots , Z_s)W_0$$

즉, 어텐션을 연결하고 새로운 가중치 행렬($W_0$)를 곱하여 멀테 헤드 어텐션을 구해주게 된다. 선형대수 내용을 활용하여 이를 이해해보자면, 각 어텐션 값을 linear combination처리한 것으로 이해해주면 된다. 즉, 여러 어텐션 결과를 선형 결합해서 나온 결과를 사용하기 때문에 단일 헤드 어텐션의 문제점인 정확도를 보완할 수 있게 되는 것이다.

1.2.4 피드포워드 네트워크

해당 네트워크의 파이프라인은 다음과 같다.

  • 2개의 Dense layer
  • ReLU

수식으로 나타내면 다음과 같다.

$$ FFN(x) = max(0, xW_1 + b_1)W_w + b_2$$

이를 도식화하면 다음과 같다.

Feed-forward network

주의해야할 점은, 각 단어에 대한 어텐션 벡터마다 적용된다는 것이다. 추가적으로 Dense층의 weight와 bias는 동일하게 작용된다. 물론 각 dense layer의 weight와 bias는 다르다. 각 어텐션 벡터를 1개의 input 느낌으로 이해해주면 기존의 알고있는 dense layer 느낌과 완벽하게 동일하게 이해할 수 있다. 

1.2.5 add와 norm 요소

해당 부분은 서브레이어의 입력과 출력 부분에 연결되어 있다. 해당 요소는 다음과 같이 구성된다.

Add / norm

  • 멀티 헤드 어텐션의 입력값과 출력값을 서로 연결한다.
  • 피드포워드의 입력값과 출력값을 서로 연결한다.

위 파이프라인을 보면서 느꼈겠지만, CS231n에서 학습한 ResNet의 Residual connection와 유사한 구조를 띄고 있다. 추가적으로 norm은 layer normalization이다. 레이어 정규화는 각 레이어 값이 크게 변화하는 것을 방지해 모델을 더 빠르게 학습할 수 있도록 도와주게 된다. 단, Batch normalization과 Layer normalization은 엄연히 구분해야하는 개념인데 자세한 내용은 위 블로그를 참고하도록 하자. 간단히 설명하자면 BN은 batch에 있는 "모든" 샘플들에 대해서 각 feature의 평균과 분산을 구하여 정규화하는 것이고, LN은 "각 sample에 대해서" feature의 평균과 분산을 구해 정규화하는 것이다. 즉, BN은 모든 샘플들의 값이 고려되므로 batch size를 고려해야하지만 LN은 해당 샘플에 대해서만 처리하는 것이므로 batch size가 전혀 상관없게 된다.

 

수식으로 보면 조금 더 명확해지는 측면이 있어서, 수식을 통해 해당 두 개념의 차이를 명확히 이해해보자.

 

BN vs LN (출처 : https://yonghyuc.wordpress.com/2020/03/04/batch-norm-vs-layer-norm/)

1.2.5 결론

앞에서 살펴본 과정들을 통합하여 트랜스포머 인코더의 파이프라인을 도식화하면 다음과 같다.

Transformer Encoder

위 그림처럼 인코더를 N개 누적해서 쌓아올리게 된다. 이전 인코더의 출력값이 다음 인코더의 입력값으로 제공되는 구조로 계속 반복되게 된다. 최상위 인코더의 출력값은 주어진 인코더에 대한 출력값(R)이 된다. 해당 결과는 디코더의 입력값으로 들어가게 된다. 다음으로는 트랜스포머의 디코더를 살펴보도록 하자.

1. 3 트랜스포머 디코더 이해하기

위 그림에서 볼 수 있는 것처럼, 각 디코더 블럭은 이전 디코더의 입력과 인코더의 출력값 2개를 입력 데이터로 받는다.

가장 왼쪽 그림인 time step이 1인 경우는 디코더의 입력 부분에 <sos> 토큰을 넣는다. 그 결과 나온 Je를 기존 디코더의 입력값에 추가해서 새로운 디코더의 입력값을 만들고 동일한 과정을 반복한다. 해당 작업은 디코더에서 <eos> 토큰을 생성할 때까지 반복된다.

디코더의 구성 요소

또한 각 디코더 블록 내부는 두 가지 요소로 구성된다.

  • 마스크된 멀티 헤드 어텐션(masked multi-head-attention)
  • 멀티 헤드 어텐션(multi-head-attention)
  • 피드포워드 네트워크(feedfoward network)

각 과정을 세부적으로 차근차근 하나씩 살펴보도록 하자.

1.3.1 출력 임베딩

앞에서 설명한 것처럼 디코더는 <eos> 토큰이 나올 때까지 이전까지의 디코더의 입력값에 이전 디코더의 결과값을 추가하고 다시 통과하는 과정을 반복하게 된다. 이 과정에서 인코더와 동일하게 위치 인코딩 값을 더해주게 된다. 이를 도식화하면 다음과 같다.

Encoder / Decoder embedding

1.3.2 마스크된 멀티 헤드 어텐션

예를 들어 영어를 프랑스어로 번역하는 테스트가 있고, 지도학습인 상황이므로 다음과 같은 학습 데이터가 있다고 가정하자.

입력 문장 타깃 문장
I am good Je vais bien
Good morning Bonjour
Thank you very much Merci beaucoup

앞에서 설명한 것처럼 디코더를 거치면서 한 단어씩 뒤의 단어를 추론해나가는 상황이므로, 지도 학습 특성 상 정답에 해당하는 값이 모두 제공된 상황이 문제가 될 수 있다. 이에 해당 단어를 예측하기 위한 입력값은 자신을 포함한 이후의 모든 단어들을 모르고 있다고 취급해주어야 하고, 이 개념을 적용한 것이 마스크 된 멀티 헤드 어텐션이다.

Masking

즉, 인코더에서 살펴본 셀프 어텐션의 경우는 각 단어의 의미를 이해하기 위해 각 단어와 문장 내 전체 단어를 연결했다면, 마스크된 멀티 헤드 어텐션에서는 문장을 생성할 때 이전 단계에서 생성한 단어만 입력 문장으로 넣는다는 점에서 차이가 발생하는 것이다. 이 과정을 통해 셀프 어텐션에서 입력되는 단어에만 집중해 단어를 정확하게 생성하는 긍정적인 효과를 가져오게 된다.

 

구현 방법은 다음과 같다. 기본적으로 셀프 어텐션때와 마찬가지로 진행하되, 소프트맥스 함수 적용 직전에 아래와 같이 masking된 단어값들에 대해서 $-\infty$를 처리해주면 된다.

위 사진에서 볼 수 있는 것처럼, <sos> 다음 단어를 예측할 때 <sos> 오른쪽에 있는 모든 단어를 참고하지 말아야하므로 오른쪽에 있는 모든 단어들에 대해서 $-\infty$를 처리해준 것이다. 

 

어텐션을 여러개 구해서 concat시키는 작업을 거치는 것은 인코더때와 완벽하게 동일하므로 생략하겠다.

1.3.3 멀티 헤드 어텐션

전체적인 파이프라인은 인코더때와 유사하지만, 인코더때와는 달리 입력값을 2개를 받는다는 점에서 차이를 보이고 있다.

  • 서브레이어의 출력값
  • 인코더의 출력값(R)

위 내용을 도식화하면 다음과 같다.

설명의 편의를 위해 인코더의 출력값을 R, 마스크된 멀티 어텐션의 출력값을 M이라고 하자. 이 과정에서 인코더의 결과와 디코더의 결과 사이의 상호작용이 일어나게 되는데, 이를 인코더-디코더 어텐션 레이어(encoder-decoder attention layer)라고 부른다.

 

쿼리, 키, 밸류 행렬을 만든다는 점에서는 인코더때와 차이가 존재하지 않지만, 쿼리 행렬을 M을 활용해서 만들고 나머지 키 행렬과 밸류 행렬은 R을 활용해서 만든다는 점에서 차이가 있다. 이를 도식화하면 다음과 같다.

encoder-decoder attention layer

먼저 $QK^T$를 하게 되면 인코더때와 마찬가지로 특정 쿼리 벡터와 인코더 단에 들어온 모든 단어에 대해서 유사도를 구할 수 있게 된다.

즉, 다음 그림과 같이 디코더의 특정 쿼리와 맨 처음 인코더에 들어온 문장에 속한 단어들 사이의 상관 관계를 구할 수 있게 되는 것이다.

이를 다시 V와 곱해서 밸류 벡터들 사이의 linear combination을 하게 되는 것으로 이해할 수 있게 된다.

추가적으로 어텐션을 concat하는 과정은 인코더때와 완벽하게 동일하므로 생략하도록 하겠다.

1.3.4 피드포워드 네트워크

완벽하게 인코더때와 동일하므로 생략

1.3.5 add와 norm 요소

완벽하게 인코더때와 동일하므로 생략

1.3.6 선형과 소프트맥스 레이어

디코더에서 얻은 출력값을 선형 및 소프트맥스 레이어에 전달하게 된다.

선형 레이어의 경우, 그 크기가 vocabulary 사이즈과 같다. 소프트맥스 레이어를 통과시켜 확률값을 구하고 그 값이 가장 높은 것을 다음 단어로 예측되게 된다.

1.3.7 결론

앞에서 살펴본 과정들을 통합하여 트랜스포머 디코더의 파이프라인을 도식화하면 다음과 같다.

 

 

반응형
Contents

포스팅 주소를 복사했습니다

이 글이 도움이 되었다면 공감 부탁드립니다.