Transformer 밑바닥부터 만들기: 어텐션(Attention)의 실제 작동 원리 (시각 자료 및 코드 포함)
요약
Transformer 아키텍처의 핵심인 셀프 어텐션(Self-Attention) 메커니즘을 제1원리부터 심층 분석합니다. RNN의 한계인 순차적 병목 현상과 장기 의존성 문제를 해결하는 과정을 시각 자료와 코드를 통해 설명합니다.
핵심 포인트
- RNN의 순차적 처리 방식이 가진 병렬성 부재와 병목 현상 지적
- 장기 의존성 문제를 해결하기 위한 Transformer의 핵심 통찰 제시
- Query, Key, Value 메커니즘을 통한 문맥 정보 추출 원리 설명
- 모든 토큰이 서로를 직접 참조하는 셀프 어텐션의 작동 방식
2017년, _"Attention Is All You Need"_라는 제목의 논문은 딥러닝 (deep learning)의 궤적을 바꾸어 놓았습니다. 이 논문에서 소개된 Transformer 아키텍처는 오늘날 GPT, BERT, Claude 및 모든 주요 LLM의 중추일 뿐만 아니라, 비전 모델 (ViT), 오디오 모델 (Whisper), 그리고 멀티모달 (multimodal) 아키텍처의 기반이기도 합니다.
하지만 여기에는 함정이 있습니다. 대부분의 튜토리얼은 "왜"를 건너뛰고 곧바로 "어떻게"로 넘어갑니다. Q, K, V에 대한 다이어그램과 몇 가지 방정식, 그리고 "그냥 믿으세요, 작동합니다"라는 설명만 듣게 됩니다.
이 글은 정반대의 접근 방식을 취합니다. 우리는 Transformer를 제1원리 (first principles)부터 구축할 것입니다. 해결하려는 문제부터 시작하여, 시각 자료와 실행 가능한 코드를 통해 각 구성 요소를 하나씩 쌓아 올릴 것입니다.
1. 문제점: RNN이 한계에 부딪히는 이유
Transformer 이전의 시퀀스 모델링 (sequence modeling)은 순환 신경망 (RNNs), LSTM, 그리고 GRU를 의미했습니다. 이러한 아키텍처는 토큰 (tokens)을 한 번에 하나씩 처리합니다:
입력: "The cat sat on the ..."
RNN: h₀ → h₁ → h₂ → h₃ → h₄ → ...
(순차적 — 각 단계가 이전 단계를 기다림)
여기에는 두 가지 근본적인 한계가 있습니다:
① 순차적 병목 현상 (Sequential bottleneck). 토큰 #1~99가 완료될 때까지 토큰 #100은 처리될 수 없습니다. 병렬성 (parallelism)이 없다는 것은 병렬 연산에 최적화된 GPU에서 특히 훈련 속도가 느려짐을 의미합니다.
② 장기 의존성 망각 (Long-range forgetting). 이론적으로 LSTM은 수백 단계 동안 정보를 기억할 수 있습니다. 하지만 실제로는 50번째 단계에 이르면 1번째 단계의 신호가 크게 약화됩니다. 모델은 중간에 30개의 토큰이 있을 때 "She was born in Paris"와 "She speaks fluent ___"를 연결하는 데 어려움을 겪습니다.
이러한 문제들은 사소한 엔지니어링 이슈가 아니라 아키텍처의 한계입니다. Transformer는 이 두 가지를 동시에 해결하며, 그 핵심 통찰은 기만적일 정도로 단순합니다:
모든 토큰은 단 한 번의 단계로 다른 모든 토큰을 직접 바라볼 수 있어야 한다.
2. 핵심 혁신: 셀프 어텐션 (Self-Attention)
셀프 어텐션을 처음부터 단계별로 구축해 봅시다.
2.1 우리가 하려는 것은 무엇인가?
입력 벡터 (input vectors) 시퀀스가 주어졌을 때:
x₁ = "The" → 벡터 (vector)
x₂ = "cat" → 벡터 (vector)
x₃ = "sat" → 벡터 (vector)
...
우리는 각 벡터가 전체 시퀀스의 문맥 (context)을 포함하는 새로운 벡터 세트를 생성하고자 합니다. 예를 들어, "mat"에 대한 새로운 벡터는 그것이 고양이가 앉아 있는 명사라는 사실을 알고 있어야 합니다.
2.2 Query-Key-Value 메커니즘
셀프 어텐션 (Self-attention)은 각 토큰에 대해 세 가지 질문을 던짐으로써 작동합니다:
- Query (Q): "내가 찾고 있는 것은 무엇인가?"
- Key (K): "나는 어떤 정보를 담고 있는가?"
- Value (V): "매칭되었을 때, 어떤 정보를 전달해야 하는가?"
시퀀스의 각 토큰에 대해 우리는 다음 과정을 수행합니다:
- 해당 토큰의 Query 벡터를 계산합니다.
- 이를 모든 토큰의 Key 벡터(자기 자신 포함)와 비교합니다.
- 유사도 점수 (similarity scores)를 사용하여 각 토큰의 Value 벡터에 가중치를 부여합니다.
- 가중치가 적용된 값들을 모두 더합니다 — 이것이 새로운 표현 (representation)이 됩니다.
도서관을 생각해보세요: 당신은 Query("신경망에 관한 책")를 가지고 들어가고, 사서는 Key(도서 제목/태그)를 확인한 뒤, 당신에게 Value(실제 책)를 건네줍니다.
2.3 수학적 원리 (생각보다 간단합니다)
X를 입력 행렬 (입력 시퀀스 길이 × 임베딩 차원)이라고 합시다.
1단계: 학습된 가중치 행렬 (weight matrices)을 통해 Q, K, V를 계산합니다:
Q = X · W_Q (queries)
K = X · W_K (keys)
V = X · W_V (values)
2단계: 어텐션 점수 (attention scores)를 계산합니다 — 모든 Query와 모든 Key의 내적 (dot product)입니다:
S = Q · Kᵀ (형태: seq_len × seq_len)
3단계: 스케일링 (scaling) 및 소프트맥스 (softmax)를 통한 정규화:
S_scaled = S / √d_k
A = softmax(S_scaled, dim=-1)
√d_k 스케일링은 내적 값이 너무 커지는 것을 방지합니다 (값이 너무 커지면 소프트맥스가 기울기(gradient)가 극도로 작은 영역으로 밀려나게 됩니다).
4단계: 값들의 가중합 (weighted sum):
Output = A · V
끝입니다. 네 번의 행렬 연산뿐입니다. 여기 코드가 있습니다:
import numpy as np
def self_attention(X, W_q, W_k, W_v):
...
2.4 이것이 모든 것을 바꾸는 이유
무슨 일이 일어났는지 주목하세요: 모든 토큰(token)이 단 한 번의 연산으로 다른 모든 토큰과 직접 상호작용합니다. 순차적인 의존성(sequential dependency)이 없습니다. 병렬성(parallelism)이 행렬 곱셈(matrix multiplication) 안에 내장되어 있습니다.
또한 거리 패널티(distance penalty)도 없습니다. 토큰 #1과 토큰 #1000은 서로를 어텐드(attend)할 수 있는 능력이 정확히 동일합니다. 장기 의존성 소실(long-range forgetting) 문제요? 사라졌습니다.
3. Multi-Head Attention: 다양한 관점
단일 어텐션 레이어(attention layer)는 한 가지 종류의 관계만을 포착할 수 있습니다. 하지만 실제 언어에는 구문(syntax), 의미(semantics), 상호 참조(coreference), 위치 관계(positional relationships) 등 매우 많은 관계가 존재합니다.
멀티 헤드 어텐션(Multi-head attention)은 여러 개의 어텐션 연산을 병렬로 실행하며, 각 헤드는 자신만의 Q, K, V 투영(projection)을 가집니다:
Head 1: "어떤 단어들이 동사인가?"
Head 2: "이 대명사는 어떤 명사를 지칭하는가?"
Head 3: "이 문장의 주어는 무엇인가?"
...
class MultiHeadAttention:
def __init__(self, d_model, num_heads):
assert d_model % num_heads == 0
...
모든 헤드의 출력은 하나로 결합(concatenate)된 후 한 번 더 투영됩니다. 이를 통해 모델은 **서로 다른 유형의 관계를 동시에 어텐드(attend)**할 수 있습니다. 이는 그 어떤 단일 RNN 상태(state)도 할 수 없었던 일입니다.
4. Positional Encoding: 순서 정하기
여기서 중요한 주의 사항이 있습니다: 셀프 어텐션(self-attention)은 순열 불변(permutation-invariant)입니다. 집합 {"cat", "sat", "mat"}는 {"mat", "sat", "cat"}와 동일한 어텐션 패턴을 생성하는데, 이는 내적(dot product)이 순서를 알지 못하기 때문입니다.
하지만 "the cat sat on the mat"와 "the mat sat on the cat"은 매우 다른 의미를 가집니다. 따라서 위치 정보(positional information)를 주입해야 합니다.
오리지널 Transformer는 **사인 함수 기반 위치 인코딩(sinusoidal positional encoding)**을 사용합니다:
def positional_encoding(seq_len, d_model):
pe = np.zeros((seq_len, d_model))
for pos in range(seq_len):
...
왜 사인(sine)과 코사인(cosine)일까요? 두 가지 우아한 특성이 있습니다:
- 상대적 위치가 자연스럽게 나타남 (Relative positions emerge naturally) —
PE(pos+k)를PE(pos)의 선형 함수로 표현할 수 있으므로, 모델이 상대적 위치 관계를 학습할 수 있습니다. - 제한 없는 시퀀스 길이 (Unbounded sequence length) — 학습된 임베딩 (learned embeddings)과 달리, 사인/코사인 인코딩 (sinusoidal encodings)은 훈련 과정에서 본 것보다 더 긴 시퀀스로 외삽 (extrapolate)할 수 있습니다.
최신 모델들 (GPT, BERT, Llama)은 대신 학습된 위치 임베딩 (learned positional embeddings) 또는 **회전 위치 인코딩 (Rotary Position Encoding, RoPE)**을 사용하지만, 원리는 동일합니다: 순열에 무감각한 메커니즘 (permutation-blind mechanism)에 순서 정보를 주입하는 것입니다.
5. 종합하기: 트랜스포머 블록 (The Transformer Block)
단일 트랜스포머 블록 = 멀티 헤드 어텐션 (Multi-Head Attention) + 피드 포워드 네트워크 (Feed-Forward Network) + 잔차 연결 (Residual Connections) + 레이어 정규화 (LayerNorm).
입력 (Input) → [LayerNorm → Multi-Head Attention → 더하기 (잔차)] → [LayerNorm → FFN → 더하기 (잔차)]
각 구성 요소는 고유한 목적을 수행합니다:
| 구성 요소 | 역할 | 중요성 |
|---|---|---|
| 멀티 헤드 어텐션 (Multi-Head Attention) | 토큰들이 정보를 교환할 수 있게 함 | 핵심적인 추론 메커니즘 |
| ... |
class TransformerBlock:
def __init__(self, d_model, num_heads, d_ff):
self.attention = MultiHeadAttention(d_model, num_heads)
...
이러한 블록을 6개, 12개, 또는 96개 쌓으면 트랜스포머 (Transformer)가 됩니다.
6. 전체 그림: 인코더 (Encoder) vs. 디코더 (Decoder)
오리지널 트랜스포머는 두 개의 스택을 가집니다:
인코더 (Encoder) (6개 블록, 양방향 (bidirectional)):
- 각 토큰이 모든 토큰(과거와 미래 모두)을 참조합니다 (attends to).
- 이해(understanding) 작업에 사용됩니다: 분류 (classification), 감성 분석 (sentiment), 개체명 인식 (NER).
- BERT는 인코더만 사용합니다.
디코더 (Decoder) (6개 블록, 자기회귀적 (autoregressive)):
- 각 토큰은 오직 자기 자신과 이전 토큰들만 참조합니다 (masked attention).
- 생성(generation) 작업에 사용됩니다: 번역 (translation), 텍스트 생성 (text generation).
- GPT는 디코더만 사용합니다.
인코더-디코더 아키텍처 (Encoder-Decoder Architecture) (오리지널):
- 인코더가 입력을 처리하고, 디코더는 인코더의 표현 (representations)을 참조하면서 출력을 생성합니다.
- 번역, 요약 (summarization)에 사용됩니다.
- T5는 이 구조를 사용합니다.
┌──────────────────┐ ┌──────────────────┐
│ Encoder │ │ Decoder │
│ (bidirectional) │ │ (autoregressive) │
│ ... │ │ ... │
└──────────────────┘ └──────────────────┘
디코더(decoder)의 크로스 어텐션(cross-attention)은 모델이 출력을 생성하는 동안 입력을 "바라볼" 수 있게 해줍니다. 예를 들어, "I am a student"를 읽으면서 "Je suis étudiant"로 번역하는 과정과 같습니다.
7. Transformer가 승리한 이유 (그리고 그 대가)
우리가 얻은 것
| 기능 | RNN/LSTM | Transformer |
|---|---|---|
| 병렬성 (Parallelism) | ❌ 순차적 (Sequential) | ✅ 완전 병렬 (Full parallel) |
| ... | ... | ... |
우리가 치러야 할 대가
O(n²) 복잡도. 모든 토큰이 모든 토큰을 참조합니다. 1,000개 토큰 시퀀스의 경우 100만 개의 어텐션 쌍이 발생합니다. 100,000개 토큰(책 한 장 분량)의 경우 100억 개에 달하며, 이는 실행 불가능한 수준입니다.
이것이 현대적인 최적화 기법들이 존재하는 이유입니다:
- 희소 어텐션 (Sparse attention) (로컬 토큰 + 소수의 글로벌 토큰만 참조)
- 슬라이딩 윈도우 어텐션 (Sliding window attention) (Mistral, Gemma)
- FlashAttention (전체 행렬을 실제로 생성하지 않는 하드웨어 효율적 어텐션)
- KV-캐시 (KV-cache) (생성 과정 중 계산된 키(key)/값(value) 재사용)
8. 여기서 GPT까지: 무엇이 변했는가
방금 구축한 Transformer는 기초입니다. 현대의 LLM(대규모 언어 모델)은 다음을 추가했습니다:
- 규모 (Scale) — GPT-3: 175B 파라미터, 96개 레이어. GPT-4: 추정치 1.8T 파라미터 (8×220B 전문가 모델)
- 사전 학습 + 미세 조정 (Pre-training + Fine-tuning) — 인터넷 텍스트로부터 학습한 후 특정 용도에 맞게 전문화
- RLHF — 출력을 인간의 선호도에 맞춤 (Claude를 유용하고, 정직하며, 해롭지 않게 만드는 요소)
- 아키텍처 수정 (Architecture tweaks) — GQA (grouped query attention), RoPE, SwiGLU, LayerNorm 대신 RMSNorm 사용
하지만 핵심 혁신인 모든 토큰이 병렬로 다른 모든 토큰을 직접 참조한다는 점은 2017년 이후 변하지 않았습니다. 셀프 어텐션 (self-attention)을 이해한다면, AI 혁명을 이끄는 엔진을 이해하는 것입니다.
9. 직접 해보기
다음은 문자 단위(character-level) 텍스트 생성을 위한 완전하고 최소한의 Transformer 코드입니다 (~100줄):
import numpy as np
def softmax(x):
...
어떤 옵티마이저(optimizer)를 사용하든 1MB 크기의 텍스트 파일로 이를 학습시켜 보세요. 단 몇 분 만에 모델이 언어 구조를 처음부터 학습하는 과정을 지켜볼 수 있습니다.
핵심 요약 (The Bottom Line)
- **셀프 어텐션 (Self-attention)**은 모든 토큰이 다른 모든 토큰과 직접 상호작용할 수 있게 합니다. 즉, 순차적 병목 현상(sequential bottleneck)이나 거리 감쇠(distance decay)가 없습니다.
- **멀티 헤드 어텐션 (Multi-head attention)**은 다양한 유형의 관계를 동시에 포착합니다.
- **포지셔널 인코딩 (Positional encoding)**은 모델에 순서 정보를 제공합니다 (어텐션은 순열에 불변(permutation-blind)하기 때문입니다).
- **잔차 연결 (Residual connections) + 레이어 정규화 (LayerNorm)**는 수십 개의 레이어를 쌓는 것을 가능하게 합니다.
- 이들의 조합은 매우 강력하여 RNN, 비전 분야의 CNN을 대체했으며, 이제는 오디오와 비디오 분야로까지 확장되고 있습니다.
Transformer가 등장한 지 8년이 지났지만, 우리는 여전히 이것이 무엇을 할 수 있는지 발견해 나가는 중입니다. 어텐션(attention)의 시대는 이제 막 시작되었습니다.
이 내용이 도움이 되었나요? 저는 LLM, RAG 시스템, 그리고 프로덕션 AI에 대한 심층 튜토리얼을 작성합니다. 단순한 표면적 개요가 아닌, 실제적인 이해를 구축할 수 있는 더 많은 콘텐츠를 원하신다면 저를 팔로우해 주세요.
또한 저는 Transformer, RAG, 에이전트(Agents), 그리고 프로덕션 배포 패턴을 다루는 300개 이상의 AI/ML 시스템 디자인 질문이 담긴 인터뷰 가이드를 운영하고 있습니다. 이는 이론과 실제 시스템 디자인 사이의 간극을 메우는 데 도움을 주기 위해 설계되었습니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기