4장. Transformer Decoder와 작은 번역기
Cross-Attention: 원문을 흘끗 보기
번역가는 번역문을 쓰다가 막히면 원문을 다시 들여다봅니다. "지금 학생 다음을 써야 하는데, 원문의 어느 낱말을 봐야 하지?" 이 물음이 Query 이고, 원문의 낱말들이 Key 와 Value 입니다. 1장의 Seq2Seq 에 붙였던 Attention 이 Transformer 에서는 Cross-Attention 이라는 이름으로 Decoder 블록 안에 들어갑니다.
그림 왼쪽 Encoder 상자는 입력 4토큰(I am a student)의 문맥 표현을 만들어 Key 와 Value 로 보냅니다. 오른쪽 Decoder 상자는 지금까지 쓴 3토큰(<시작> 저는 학생)으로 Query 를 만듭니다. 가운데에서 softmax(QKᵀ / √d_k) V 를 계산하면 가중치 표의 shape 은 (3, 4) 입니다. 출력 위치 3개가 각각 입력 4개를 얼마나 참고할지 정한 표입니다. Query 와 Key 의 길이가 달라도 됩니다.
세 Attention 의 계산식은 같습니다. 다른 것은 Q·K·V 를 어디서 가져오는지와 가림판을 쓰는지 둘뿐입니다.
세 가지 Attention 견주기
| 종류 | Query 출처 | Key·Value 출처 | 가림 | 가중치 shape(이 예) |
|---|---|---|---|---|
| Encoder Self-Attention | 입력 문장 | 입력 문장 | 패딩만 | (4, 4) |
| Masked Self-Attention | 출력 문장 | 출력 문장 | 패딩 + 미래 | (3, 3) |
| Cross-Attention | 출력 문장(Decoder) | 입력 문장(Encoder 출력) | 입력의 패딩만 | (3, 4) |
짧은 예시
import numpy as np
# default_rng(0): 씨앗 0 으로 고정한 난수 생성기. 매번 같은 숫자가 나온다
rng = np.random.default_rng(0)
d_k = 4 # 벡터 한 줄의 길이(작게 잡았다)
Q = rng.normal(size=(3, d_k)) # Decoder 쪽 3토큰: <시작> 저는 학생
K = rng.normal(size=(4, d_k)) # Encoder 출력 4토큰: I am a student
V = rng.normal(size=(4, d_k)) # Key 와 같은 4토큰의 내용
# 점수 = Q @ K.T / √d_k. K.T 는 (4, 4) 를 뒤집은 (d_k, 4) 라서 (3, 4) @ (4, 4) = (3, 4)
scores = Q @ K.T / np.sqrt(d_k)
# 행마다 Softmax. Cross-Attention 에서는 원문 전체를 봐도 되므로 미래 가림판이 없다
weights = np.exp(scores) / np.exp(scores).sum(axis=1, keepdims=True)
out = weights @ V # (3, 4) @ (4, 4) = (3, 4)
print(scores.shape, weights.shape, out.shape) # 결과: (3, 4) (3, 4) (3, 4)
print(weights.sum(axis=1).round(2)) # 결과: [1. 1. 1.]이 코드가 뜻하는 것
번역문 3토큰이 원문 4토큰을 얼마나 들춰 볼지 정하는 비율표를 만든 코드입니다. 원문을 다 읽었으므로 미래를 가릴 필요가 없습니다. 여기서 "무엇을 찾나"가 Q, "원문 낱말의 제목표"가 K, "원문 낱말의 내용"이 V 입니다.
| 줄 | 하는 일 | 쉽게 말하면 |
|---|---|---|
Q = rng.normal(size=(3, d_k)) | Decoder 쪽 3행 | 번역문에서 나온 질문 3개 |
K, V 의 size=(4, d_k) | Encoder 쪽 4행 | 원문 낱말 4개의 제목과 내용 |
Q @ K.T / np.sqrt(d_k) | (3, 4) 점수표 | 질문마다 원문 낱말과 맞춰 본 점수 |
weights @ V | 비율대로 내용 섞기 | 원문을 참고해 만든 메모 3장 |
shape 만 보면 됩니다. 행 수는 Query(번역문 길이), 열 수는 Key(원문 길이)를 따릅니다. 실습 2의 번역기는 이 계산을 Decoder 층마다 한 번씩, 4개 헤드로 나눠 합니다.
주의할 점
- Cross-Attention 의 Key·Value 는 Encoder 의 마지막 층 출력에서 옵니다. Decoder 의 모든 층이 같은 Encoder 출력을 참고합니다.
- Decoder-only 모델(GPT, Qwen)에는 Encoder 가 없으므로 Cross-Attention 도 없습니다. 5장에서 이 구조를 봅니다.