일단은 절반, Encoder 부분만 다룬다
요즘 엄청난 유명인인 챗GPT. 작년 11월에 공개되었는데, 모두가 챗GPT를 알게 된 건 올해 초인 것 같다.
처음 써보고 나서 정말 놀랐다. 정말 똑똑하더라.. 내 졸업논문이 "발음과 문장 구조가 반복되는 광고 문구 만들기"였는데 그걸 엄청 잘하더라????? ㅎ
챗GPT는 따로 공개된 논문이 없어 확실히 알 수 없지만, 챗GPT를 만든 OpenAI의 블로그를 보면 기존 GPT 모델 구조를 크게 바꾸진 않은 것 같다. 양질의 데이터를 만들기 위해 인간이 레이블링을 많이 해줬고, 강화학습을 추가한 정도가 차이점인 것 같다. GPT 모델은 Transformer 모델의 일부를 떼서 만든 모델이다. 그래서 Transformer가 챗GPT의 할머니 정도 되는 셈인데, 이 Transformer 모델을 간단히 소개해보고자 한다! 설명에 사용되는 이미지는 Transformer를 정리한 글 중에 가장 유명한 The Illustrated Transformer에서 가져왔다.
Transformer는 일단 크게 Encoder와 Decoder로 이루어져 있다. Encoder는 말 그대로 입력 문장을 인코딩하는 부분이고, Decoder는 디코딩하여 새로운 문장을 생성하는 부분이다. 예를 들어서, 한영 번역 모델이라면 '트랜스포머는 대단해'라는 입력 문장을 Encoder에서 인코딩해주고, Decoder는 Encoder의 인코딩 결과를 받고 이를 디코딩하여 'Transformer is awesome'이라는 문장을 생성한다.
Encoder와 Decoder는 사실 각각 여러 개의 Encoder layer와 Decoder layer로 이루어져 있다.
그리고 각각의 Encoder layer는 Self-Attention과 Feed Forward Neural Network으로, Decoder layer는 Self-Attention, Encoder-Decoder Attention과 Feed Forward Neural Network으로 이루어져 있다. 여기서 Attention이 Transformer의 핵심이다. 그래서 Transformer의 논문 제목도 "Attention Is All You Need"이다. 간략히 설명하자면 Attention은 "이 단어가 어떤 단어와 가장 관련 있는가?"이다. 예를 들어 "나는 빨간 사과와 파란 바다를 좋아해"라는 문장에서 "사과"라는 단어는 "빨간"이라는 단어와 관련도가 높고 "파란"이라는 단어와는 관련도가 낮다. 따라서 단어 "사과"와 "빨간"의 attention score는 높고, 단어 "사과"와 "파란"의 attention score는 낮게 학습될 것이다.
이제 본격적으로 Transformer의 핵심인 Attention이 어떻게 계산되는지 볼 시간이 되었다.
그런데 그전에 지금까지 두리뭉실하게 넘어간 부분을 다시 짚어보자. 위에서 "'트랜스포머는 대단해'라는 입력 문장을 Encoder에 넣어서 인코딩해준다"라고 했는데, 문장을 Transformer에 어떻게 넣을까? 일단 문장을 단어들로 쪼갠다(tokenize). 그리고 각 단어를 나타내는 벡터를 모아 행렬로 만들어 Transformer에 넣어준다. 예를 들어 단어를 나타내는 벡터가 100차원이고, 입력 문장이 총 10 단어로 이루어졌다고 하면, 첫 단어의 벡터를 첫 행, 두 번째 단어의 벡터를 두 번째 행, ... 식으로 붙여 행은 단어의 수 인 10개, 열은 벡터의 차원인 100개인 행렬이 만들어진다. 이 행렬을 전달해 주면 된다!
그러니까 '단어 "사과"와 "빨간"의 attention score가 높다'는 말은 사실 '단어 "사과"를 나타내는 벡터와 단어 "빨간"을 나타내는 벡터 간의 attention score가 높다'는 의미였다.
그럼 이제 진짜 Attention을 살펴볼 수 있다. "나는 빨간 사과와 파란 바다를 좋아해"라는 문장이 [나는, 빨간, 사과, 와, 파란, 바다, 를, 좋아해] 이렇게 8가지 단어로 쪼개졌다고 하자. Self-Attention은 현재 단어가 속해있는 그 문장의 모든 단어와 Attention을 계산한다. 그러면 자기 자신과의 Attention도 계산하게 되므로 "Self"-Attention이다.
[나는, 빨간, 사과, 와, 파란, 바다, 를, 좋아해] 로 쪼개진 문장에서 Self-Attention을 계산해야 하는 조합은 다음과 같이 8*8=64개다.
(나는, 나는) 사이의 attention
(나는, 빨간) 사이의 attention
(나는, 사과) 사이의 attention
(나는, 와) 사이의 attention
(나는, 파란) 사이의 attention
(나는, 바다) 사이의 attention
(나는, 를) 사이의 attention
(나는, 좋아해) 사이의 attention
(빨간, 나는) 사이의 attention
(빨간, 빨간) 사이의 attention
(빨간, 사과) 사이의 attention
(빨간, 와) 사이의 attention
(빨간, 파란) 사이의 attention
(빨간, 바다) 사이의 attention
(빨간, 를) 사이의 attention
(빨간, 좋아해) 사이의 attention
...
(좋아해, 나는) 사이의 attention
(좋아해, 빨간) 사이의 attention
(좋아해, 사과) 사이의 attention
(좋아해, 와) 사이의 attention
(좋아해, 파란) 사이의 attention
(좋아해, 바다) 사이의 attention
(좋아해, 를) 사이의 attention
(좋아해, 좋아해) 사이의 attention
이 중 "빨간"과 나머지 단어들(나는, 사과, 와, 파란, 바다, 를, 좋아해)의 attention을 계산하는 부분만 생각해 보자. 여기서 "빨간"과 나머지 단어들은 벡터로 주어져 있는데, 이 벡터들은 Self-Attention layer에 들어가기 전에 또 Q(query), K(key), V(value) 라는 3가지 행렬과 곱해져서 3가지 다른 벡터로 나타나진다. 그러니까 "빨간"의 벡터와 Q 행렬이 곱해져서 "빨간"의 query 벡터가 만들어지고, "빨간"의 벡터와 K 행렬이 곱해져서 "빨간"의 key 벡터가 만들어지고, "빨간"의 벡터와 V 행렬이 곱해져서 "빨간"의 value 벡터가 만들어진다. 모든 단어에 대해 이 계산이 이뤄지므로 당연히 "사과"도 query, key, value 벡터 3가지를 가지게 되고, "좋아해"도 query, key, value 벡터 3가지를 가지게 된다.
(아래 그림에서 "Thinking"은 "빨간", "Machines"는 "사과"라고 생각하면 된다.)
"빨간"과 나머지 단어의 attention score를 계산하기 위한 준비물이 드디어 모두 준비되었다! 단어 "빨간"을 기준으로 attention score를 계산하고 있으므로 query 단어는 "빨간"이다. "빨간"의 query 벡터와 나머지 단어들의 key 벡터를 내적한다. 서로 유사한 벡터일수록 내적값이 커지고, 유사하지 않을수록 내적 값은 작아진다. 이렇게 내적까지 한 값을 'attention score'라고 부른다. ( '⋅'는 내적 기호다.)
"빨간"의 query 벡터 ⋅ "나는"의 key 벡터
"빨간"의 query 벡터 ⋅ "빨간"의 key 벡터
"빨간"의 query 벡터 ⋅ "사과"의 key 벡터
"빨간"의 query 벡터 ⋅ "와"의 key 벡터
"빨간"의 query 벡터 ⋅ "파란"의 key 벡터
"빨간"의 query 벡터 ⋅ "바다"의 key 벡터
"빨간"의 query 벡터 ⋅ "를"의 key 벡터
"빨간"의 query 벡터 ⋅ "좋아해"의 key 벡터
이 값들을 key 벡터 차원의 제곱근으로 나누어 정규화(normalize)한 후 Softmax를 씌워 확률로 변환해 준다. Softmax는 숫자들의 목록을 받으면 이들의 원래 대소관계를 반영하여 0과 1 사이의 확률값으로 변환시켜 주는 함수라고 생각하면 된다.
여기까지 하면 "빨간"의 query 벡터와 유사한 key 벡터를 가진 단어일수록 큰 확률 값을 가지게 된다. 이후에 이 확률 값과 각 단어의 value 벡터를 곱한 후, 이를 모두 더하면 "빨간"을 표현하는 새로운 벡터가 만들어진다!! 이걸 여러 layer를 거치며 반복하면 더 정교하게 "빨간"의 의미를 반영하는 벡터를 만들 수 있다.
(빨간, 나는) 사이의 확률값 × "나는"의 value 벡터
(빨간, 빨간) 사이의 확률값 × "빨간"의 value 벡터
(빨간, 사과) 사이의 확률값 × "사과"의 value 벡터
(빨간, 와) 사이의 확률값 × "와"의 value 벡터
(빨간, 파란) 사이의 확률값 × "파란"의 value 벡터
(빨간, 바다) 사이의 확률값 × "바다"의 value 벡터
(빨간, 를) 사이의 확률값 × "를"의 value 벡터
(빨간, 좋아해) 사이의 확률값 × "좋아해"의 value 벡터
그런데 사실 '어떤 단어가 어떤 단어와 관련이 있다'를 판단할 때, 그 관련성의 기준은 다양하다. 단어의 의미 기준으로 "빨간"과 "사과"가 관련도가 높을 수도 있지만, 문법적으로 보면 형용사인 "빨간"은 명사인 "사과"보다 같은 형용사인 "파란"과 관련도가 높다고 볼 수 있다. 이렇게 다양한 관련도의 판단 기준을 반영하기 위해 attention head를 여러 개로 설정한다. 이는 query, key, value 벡터를 만들 때 사용하는 Q, K, V 행렬을 여러 개 만든다는 의미이다. 1번 attention head는 단어의 의미를 반영해서 "빨간"과 "사과"의 attention score를 높게 계산할 수 있지만, 2번 attention head는 문법을 반영해서 "빨간"과 "사과"보다 "빨간"과 "파란"의 attention score를 더 높게 계산할 수 있는 것이다.
이렇게 여러 개의 head로 Attention을 계산한다는 점에서 Self-Attention의 풀네임은 사실 Multi-head Self-Attention이다.
이 외에도 다뤄야 하는 부분은 Positional Encoding, Add & Normalization 부분, Feed Forward 부분, 여러 개의 attention head로 나온 결과물을 어떻게 합칠지 등 많은데!
사실 핵심은 Attention 계산하는 부분이 다다.
여러분 챗GPT는 이런 아이입니다..
• 이 글은 Transformer의 '간략한' 소개글이므로 이해를 돕기 위해 '단어'와 '토큰'을 같은 의미로 사용했다.