
트랜스포머(Transformer)의 어텐션(Attention), 단계별 이해 | 딥러닝(Deep Learning) 제6장
요약
트랜스포머(Transformer) 모델의 핵심인 어텐션(Attention) 메커니즘의 원리를 단계별로 설명합니다. 초기 토큰 임베딩이 문맥 정보를 얻기 위해 어텐션 과정을 통해 어떻게 풍부한 문맥적 의미를 갖게 되는지 시각적으로 풀어냅니다.
핵심 포인트
- 트랜스포머의 핵심 기술인 어텐션 메커니즘의 작동 원리 이해
- 초기 토큰 임베딩이 문맥 정보를 반영하기 전의 상태 파악
- 고차원 벡터 공간에서의 방향과 의미론적 관계 설명
- 문맥에 따라 동일한 단어(예: mole)가 다르게 해석되는 과정
영상: 트랜스포머(Transformer)의 어텐션(Attention), 단계별 이해 | 딥러닝(Deep Learning) 제6장
채널: 3Blue1Brown
재생 시간: 26분 9초
출처: 자막 (수동, 영문)
지난 장에서 당신과 저는 트랜스포머(Transformer)의 내부 작동 방식을 단계별로 살펴보기 시작했습니다. 이것은 거대 언어 모델(Large Language Models, LLM) 내부의 핵심 기술 중 하나이며, 현대 AI의 물결 속에 있는 다른 많은 도구들의 핵심이기도 합니다. 이것은 'Attention is All You Need'라는 이름의 이제는 매우 유명해진 2017년 논문에서 처음 등장했습니다. 이번 장에서 당신과 저는 이 어텐션(Attention) 메커니즘이 무엇인지 파헤치고, 그것이 데이터를 어떻게 처리하는지 시각화해 볼 것입니다. 빠른 복습을 위해, 여러분이 염두에 두었으면 하는 중요한 맥락을 말씀드리겠습니다. 우리가 공부하고 있는 모델의 목표는 텍스트 조각을 입력받아 다음에 올 단어가 무엇인지 예측하는 것입니다.
입력 텍스트는 토큰(Token)이라고 부르는 작은 조각들로 나뉘는데, 이것들은 매우 빈번하게 단어 또는 단어의 일부입니다. 하지만 이 영상의 예시를 여러분과 제가 더 쉽게 생각할 수 있도록, 토큰이 항상 단순히 단어라고 가정하여 단순화해 보겠습니다. 트랜스포머(Transformer)의 첫 번째 단계는 각 토큰을 고차원 벡터(High-dimensional vector)와 연관시키는 것이며, 이를 임베딩(Embedding)이라고 부릅니다. 제가 여러분이 염두에 두었으면 하는 가장 중요한 아이디어는, 가능한 모든 임베딩이 존재하는 이 고차원 공간에서의 방향(Direction)이 어떻게 의미론적 의미(Semantic meaning)와 대응될 수 있는가 하는 점입니다.
지난 장에서 우리는 방향이 어떻게 성별(Gender)에 대응될 수 있는지에 대한 예를 보았습니다. 즉, 이 공간에서 특정 단계(Step)를 더하는 것이 남성 명사의 임베딩에서 그에 대응하는 여성 명사의 임베딩으로 이동할 수 있다는 의미였습니다. 그것은 단지 하나의 예시일 뿐이며, 이 고차원 공간의 다른 수많은 방향이 단어 의미의 다른 수많은 측면들에 어떻게 대응될 수 있을지 상상해 볼 수 있습니다. 트랜스포머(Transformer)의 목표는 이러한 임베딩들을 점진적으로 조정하여, 그것들이 단순히 개별 단어를 인코딩(Encode)하는 데 그치지 않고, 대신 훨씬 더 풍부한 문맥적 의미(Contextual meaning)를 내포하도록 만드는 것입니다.
미리 말씀드리자면, 많은 사람들이 트랜스포머(Transformer)의 핵심 요소인 어텐션(Attention) 메커니즘을 매우 혼란스러워합니다. 그러니 내용을 이해하는 데 시간이 좀 걸리더라도 걱정하지 마세요. 계산적인 세부 사항이나 모든 행렬 곱셈(Matrix multiplications)을 파고들기 전에, 우리가 어텐션이 가능하게 만들고자 하는 행동의 유형에 대해 몇 가지 예시를 생각해 보는 것이 가치가 있다고 생각합니다. "American shrew mole(미국땃쥐두더지)", "one mole of carbon dioxide(이산화탄소 1몰)", 그리고 "take a biopsy of the mole(그 점의 생검을 실시하다)"라는 문구들을 생각해 봅시다. 당신과 저는 문맥(Context)에 따라 이 각각의 문구에서 "mole"이라는 단어가 서로 다른 의미를 가진다는 것을 알고 있습니다.
하지만 텍스트를 분해하고 각 토큰(Token)을 벡터(Vector)와 연관시키는 트랜스포머의 첫 번째 단계 이후에는, 이 모든 경우에서 "mole"과 연관된 벡터가 동일할 것입니다. 왜냐하면 이 초기 토큰 임베딩(Token embedding)은 사실상 문맥에 대한 참조가 없는 룩업 테이블(Lookup table)이기 때문입니다. 주변의 임베딩(Embeddings)들이 이 임베딩으로 정보를 전달할 기회를 갖는 것은 오직 트랜스포머의 다음 단계에서뿐입니다. 여러분이 머릿속에 그릴 수 있는 그림은, 임베딩 공간(Embedding space) 안에 "mole"이라는 단어의 여러 가지 서로 다른 의미를 인코딩(Encoding)하는 여러 개의 뚜렷한 방향들이 존재하며, 잘 훈련된 어텐션 블록(Attention block)은 문맥의 함수로서 일반적인 임베딩을 이러한 특정 방향 중 하나로 이동시키기 위해 무엇을 더해야 하는지를 계산한다는 것입니다.
또 다른 예를 들어, "tower"라는 단어의 임베딩을 생각해 봅시다. 이것은 아마도 공간 내에서 다른 많은 크고 높은 명사들과 연관된, 매우 일반적이고 비특정적인 방향일 것입니다. 만약 이 단어 바로 앞에 "Eiffel"이 있었다면, 여러분은 이 메커니즘이 이 벡터를 업데이트하여 에펠탑(Eiffel tower)을 더 구체적으로 인코딩하는 방향, 즉 아마도 파리(Paris), 프랑스(France), 그리고 강철로 만들어진 것들과 연관된 벡터들과 상관관계가 있는 방향을 가리키도록 만들기를 원한다고 상상할 수 있습니다. 만약 그 앞에 "miniature(미니어처)"라는 단어도 있었다면, 벡터는 더 나아가 더 이상 크고 높은 것들과 상관관계가 없도록 업데이트되어야 할 것입니다.
단순히 단어의 의미를 정교화하는 것보다 더 일반적으로, 어텐션 (Attention) 블록은 모델이 하나의 임베딩 (Embedding)에 인코딩된 정보를 다른 임베딩으로, 잠재적으로는 상당히 멀리 떨어져 있는 임베딩으로, 그리고 잠재적으로는 단일 단어보다 훨씬 더 풍부한 정보를 가진 임베딩으로 이동할 수 있게 해줍니다. 지난 장에서 우리가 보았던 것은, 많은 서로 다른 어텐션 (Attention) 블록들을 포함하여 모든 벡터가 네트워크를 통해 흐른 후, 다음 토큰 (Token)의 예측을 생성하기 위해 수행하는 연산이 시퀀스 (Sequence)의 마지막 벡터의 완전한 함수라는 점이었습니다.
예를 들어, 당신이 입력한 텍스트가 거의 완결된 추리 소설이며, 결말 직전의 내용이 "그러므로 살인범은"이라고 읽힌다고 상상해 보십시오. 만약 모델이 다음 단어를 정확하게 예측해야 한다면, 단순히 "was"라는 단어를 임베딩하며 생명을 시작했던 시퀀스의 그 마지막 벡터는, 모든 어텐션 (Attention) 블록에 의해 업데이트되어 어떤 개별 단어보다 훨씬 더 많은 것을 나타내야 하며, 다음 단어를 예측하는 데 관련 있는 전체 컨텍스트 윈도우 (Context Window)의 모든 정보를 어떻게든 인코딩하고 있어야 할 것입니다.
하지만 연산을 단계별로 살펴보기 위해, 훨씬 더 단순한 예시를 들어보겠습니다. 입력에 "폭신폭신한 파란색 생명체가 푸른 숲을 배회했다"라는 문구가 포함되어 있다고 상상해 보십시오. 그리고 잠시 동안, 우리가 관심을 갖는 유일한 유형의 업데이트가 형용사가 그에 대응하는 명사의 의미를 조정하는 것이라고 가정해 봅시다. 제가 설명하려는 것은 우리가 단일 어텐션 헤드 (Single Head of Attention)라고 부를 것이며, 나중에 우리는 어텐션 (Attention) 블록이 병렬로 실행되는 많은 서로 다른 헤드 (Heads)들로 구성되어 있음을 보게 될 것입니다. 다시 말하지만, 각 단어에 대한 초기 임베딩 (Embedding)은 문맥 없이 해당 특정 단어의 의미만을 인코딩하는 어떤 고차원 벡터 (High Dimensional Vector)입니다.
사실, 그것은 완전히 정확하지 않습니다. 이들은 단어의 위치(Position) 또한 인코딩합니다. 위치가 인코딩되는 구체적인 방식에 대해서는 더 많은 이야기를 할 수 있겠지만, 지금 여러분이 알아야 할 전부는 이 벡터의 항목들이 해당 단어가 무엇인지와 문맥(Context) 내에서 어디에 존재하는지를 모두 알려주기에 충분하다는 점입니다. 이제 이 임베딩(Embedding)들을 문자 e로 표기해 봅시다. 우리의 목표는 일련의 계산을 통해 새롭게 정제된 임베딩 세트를 생성하는 것이며, 예를 들어 명사(Noun)에 해당하는 임베딩들이 그에 대응하는 형용사(Adjective)로부터 의미를 흡수하도록 만드는 것입니다.
그리고 딥러닝(Deep Learning) 게임을 수행함에 있어, 우리는 관련된 대부분의 계산이 행렬-벡터 곱(Matrix-vector product)의 형태를 띠기를 원합니다. 여기서 행렬들은 데이터에 기반하여 모델이 학습하게 될 조정 가능한 가중치(Tuneable weights)들로 가득 차 있습니다. 명확히 하자면, 형용사가 명사를 업데이트한다는 이 예시는 어텐션 헤드(Attention head)가 수행할 수 있는 동작의 유형을 설명하기 위해 제가 임의로 만든 예시일 뿐입니다. 수많은 딥러닝 사례와 마찬가지로, 실제 동작을 파악하는 것은 훨씬 더 어렵습니다. 왜냐하면 어떤 비용 함수(Cost function)를 최소화하기 위해 엄청난 수의 파라미터(Parameter)를 미세하게 조정하고 튜닝하는 것에 기반하기 때문입니다.
다만 이 과정에 관여하는 파라미터로 가득 찬 다양한 행렬들을 하나씩 살펴볼 때, 모델이 무엇을 하고 있는지에 대한 상상 속의 예시를 갖는 것이 모든 과정을 더 구체적으로 유지하는 데 정말 도움이 된다고 생각합니다. 이 과정의 첫 번째 단계로, 'creature'와 같은 각 명사가 "저기, 내 앞에 있는 형용사가 있나요?"라고 질문하는 것을 상상해 볼 수 있습니다. 그리고 'fluffy'와 'blue'라는 단어들이 각각 "네, 저는 형용사이고 그 위치에 있습니다"라고 대답할 수 있는 상황 말입니다.
그 질문은 일종의 또 다른 벡터, 즉 우리가 이 단어에 대한 쿼리 (query)라고 부르는 숫자들의 리스트로 인코딩 (encoding)됩니다. 하지만 이 쿼리 (query) 벡터는 임베딩 (embedding) 벡터보다 훨씬 작은 차원을 가지며, 예를 들어 128차원이라고 가정해 봅시다. 이 쿼리 (query)를 계산하는 과정은 특정 행렬 (matrix)을 취하고, 이를 $W_Q$라고 명명한 뒤 임베딩 (embedding)과 곱하는 것처럼 보입니다. 내용을 조금 압축하자면, 이 쿼리 (query) 벡터를 $q$라고 씁시다. 그리고 제가 화살표 옆에 행렬 (matrix)을 배치할 때마다, 이는 해당 행렬을 화살표 시작점에 있는 벡터와 곱하면 화살표 끝점에 있는 벡터가 나온다는 것을 의미합니다.
이 경우, 여러분은 이 행렬 (matrix)을 컨텍스트 (context) 내의 모든 임베딩 (embedding)과 곱하여, 각 토큰 (token)에 대해 하나의 쿼리 (query) 벡터를 생성하게 됩니다. 이 행렬 (matrix)의 요소들은 모델의 파라미터 (parameter)이며, 이는 실제 동작이 데이터로부터 학습된다는 것을 의미합니다. 실제로 특정 어텐션 헤드 (attention head)에서 이 행렬 (matrix)이 정확히 무엇을 하는지 파악하는 것은 매우 어렵습니다. 하지만 우리의 이해를 돕기 위해, 모델이 학습하기를 바라는 예시를 상상해 봅시다. 우리는 이 쿼리 (query) 행렬 (matrix)이 명사의 임베딩 (embedding)을 앞선 위치에서 형용사를 찾는다는 개념을 어떤 방식으로든 인코딩 (encoding)하는 더 작은 쿼리 (query) 공간 내의 특정 방향으로 매핑 (mapping)한다고 가정하겠습니다.
다른 임베딩 (embedding)들에 대해서는 무엇을 하는지 알 수 있을까요? 아마도 그것들을 통해 동시에 다른 목표를 달성하려고 시도할지도 모릅니다. 지금 우리는 명사에만 온전히 집중하고 있습니다. 동시에, 이와 연관되어 키 (key) 행렬 (matrix)이라고 불리는 두 번째 행렬 (matrix)이 있으며, 이 또한 모든 임베딩 (embedding)에 곱해집니다. 이는 우리가 키 (key)라고 부르는 두 번째 벡터 시퀀스 (sequence)를 생성합니다. 개념적으로, 여러분은 키 (key)를 쿼리 (query)에 잠재적으로 답을 제공하는 존재로 생각해야 합니다. 이 키 (key) 행렬 (matrix) 역시 조정 가능한 파라미터 (parameter)들로 가득 차 있으며, 쿼리 (query) 행렬 (matrix)과 마찬가지로 임베딩 (embedding) 벡터를 동일한 작은 차원의 공간으로 매핑 (mapping)합니다.
여러분은 키 (key)가 쿼리 (query)와 서로 밀접하게 정렬될 때마다 서로 일치한다고 생각하면 됩니다. 우리의 예시에서는, 키 (key) 행렬 (matrix)이 'fluffy'나 'blue'와 같은 형용사 (adjective)들을 'creature'라는 단어에 의해 생성된 쿼리 (query)와 밀접하게 정렬된 벡터 (vector)로 매핑 (mapping)한다고 상상할 수 있습니다. 각 키 (key)가 각 쿼리 (query)와 얼마나 잘 일치하는지 측정하기 위해, 가능한 모든 키-쿼리 (key-query) 쌍 사이의 내적 (dot product)을 계산합니다. 저는 수많은 점들로 가득 찬 그리드 (grid)를 시각화하는 것을 좋아하는데, 여기서 점이 클수록 내적 (dot product) 값이 크며, 이는 키 (key)와 쿼리 (query)가 정렬되는 지점을 의미합니다.
우리의 형용사-명사 (adjective-noun) 예시의 경우, 다음과 같은 모습이 될 것입니다. 만약 'fluffy'와 'blue'에 의해 생성된 키 (key)들이 'creature'에 의해 생성된 쿼리 (query)와 정말로 밀접하게 정렬된다면, 이 두 지점의 내적 (dot product) 값은 어떤 큰 양수 (positive number)가 될 것입니다. 전문 용어로, 머신러닝 (machine learning) 연구자들은 이것을 'fluffy'와 'blue'의 임베딩 (embedding)이 'creature'의 임베딩에 어텐드 (attend) 한다고 말합니다. 이와 대조적으로, 'the'와 같은 다른 단어의 키 (key)와 'creature'의 쿼리 (query) 사이의 내적 (dot product)은 서로 관련이 없음을 반영하여 어떤 작은 값이나 음수 (negative value)가 될 것입니다.
따라서 우리는 음의 무한대 (negative infinity)에서 양의 무한대 (infinity) 사이의 어떤 실수 (real number)가 될 수 있는 값들의 그리드 (grid)를 갖게 되며, 이는 각 단어가 다른 모든 단어의 의미를 업데이트하는 데 얼마나 관련이 있는지에 대한 점수 (score)를 제공합니다. 우리가 이 점수 (score)들을 사용할 방식은 각 열 (column)을 따라 관련성에 따라 가중치를 둔 특정 가중 합 (weighted sum)을 구하는 것입니다. 즉, 값의 범위가 음의 무한대에서 양의 무한대까지인 대신, 우리가 원하는 것은 이 열 (column)들의 숫자들이 0과 1 사이가 되고, 마치 확률 분포 (probability distribution)인 것처럼 각 열 (column)의 합이 1이 되도록 하는 것입니다.
지난 장에서 이어지는 내용이라면, 우리가 무엇을 해야 하는지 알고 계실 것입니다. 우리는 값들을 정규화 (normalize) 하기 위해 이 각 열 (column)을 따라 소프트맥스 (softmax)를 계산합니다. 우리의 그림에서, 모든 열에 소프트맥스를 적용한 후에는 이 정규화된 값들로 그리드를 채우게 됩니다. 이 시점에서 여러분은 각 열이 왼쪽의 단어가 상단의 해당 값과 얼마나 관련이 있는지에 따라 가중치 (weights)를 부여하는 것으로 생각해도 안전합니다. 우리는 이 그리드를 어텐션 패턴 (attention pattern)이라고 부릅니다. 이제 원래의 트랜스포머 (transformer) 논문을 살펴보면, 이 모든 것을 기록하는 매우 간결한 방법이 있습니다.
여기서 변수 $q$와 $k$는 각각 쿼리 (query) 및 키 (key) 벡터의 전체 배열을 나타내며, 이는 임베딩 (embeddings)에 쿼리 및 키 행렬을 곱하여 얻은 작은 벡터들입니다. 분자에 있는 이 표현식은 키와 쿼리 쌍 사이의 가능한 모든 내적 (dot product) 그리드를 나타내는 매우 간결한 방법입니다. 제가 언급하지 않은 작은 기술적 세부 사항은, 수치적 안정성 (numerical stability)을 위해 이 모든 값들을 해당 키-쿼리 공간의 차원 (dimension)의 제곱근으로 나누는 것이 도움이 된다는 점입니다.
그 후 전체 표현식을 감싸고 있는 이 소프트맥스는 열 단위로 적용되는 것으로 이해해야 합니다. $v$ 항에 대해서는 잠시 후에 이야기하겠습니다. 그 전에, 제가 지금까지 건너뛴 또 다른 기술적 세부 사항이 하나 있습니다. 훈련 (training) 과정 중에, 주어진 텍스트 예시에 대해 이 모델을 실행하고, 문맥 내의 실제 다음 단어에 할당하는 확률이 얼마나 높은지에 따라 보상을 주거나 벌을 주도록 모든 가중치 (weights)가 미세하게 조정되고 튜닝될 때, 이 문맥 내의 각 초기 토큰 부분 시퀀스 (subsequence) 다음에 올 수 있는 모든 가능한 다음 토큰을 동시에 예측하게 하는 것이 전체 훈련 과정을 훨씬 더 효율적으로 만든다는 사실이 밝혀졌습니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 YouTube 3Blue1Brown (수학/ML)의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기