본문으로 건너뛰기

어텐션과 트랜스포머 (기술 심화)

:::note 전제 03. LLM은 어떻게 동작하나에서 "토큰 → 다음 토큰 예측"의 큰 그림을 봤다는 가정하에, 이 문서는 그 내부의 실제 계산 방식을 다룹니다. 수식은 코드블록으로 표기합니다. :::

1. 입력 표현 — 토큰을 벡터로​

모델은 토큰을 숫자 벡터로 바꿔야 계산할 수 있습니다.

토큰 ID ──(임베딩 테이블 조회)──▶ 임베딩 벡터 (차원 d_model, 예: 4096)
  • 토큰 임베딩: 어휘집 크기 V × d_model 행렬에서 토큰 ID로 행을 조회
  • 위치 정보: 어텐션은 순서를 모르므로 위치 인코딩을 더함
    • 초기 트랜스포머: 사인/코사인 함수(sinusoidal)
    • 최신 LLM: RoPE(회전 위치 임베딩) 를 Q·K에 적용하는 방식이 주류

결과적으로 각 토큰은 x_i ∈ ℝ^(d_model) 벡터가 되고, 문장은 X ∈ ℝ^(n × d_model) 행렬이 됩니다(n = 토큰 수).

2. 셀프 어텐션의 핵심 — Q, K, V​

각 토큰 벡터에 학습된 가중치 행렬 3개를 곱해 세 가지 역할의 벡터를 만듭니다.

Q = X · W_Q (Query : "내가 찾고 싶은 것")
K = X · W_K (Key : "내가 가진 색인")
V = X · W_V (Value : "실제로 전달할 정보")

직관(도서관 비유):

  • Query = 내 검색어
  • Key = 각 책의 색인표
  • Value = 책의 실제 내용
  • 검색어(Q)와 색인(K)이 잘 맞는 책의 내용(V)을 많이 가져온다

3. Scaled Dot-Product Attention​

어텐션의 심장부 수식입니다.

Attention(Q, K, V) = softmax( (Q · Kᵀ) / √d_k ) · V

단계별로:

① 점수 계산 : S = Q · Kᵀ # (n×d_k)·(d_k×n) = n×n 행렬
S[i][j] = 토큰 i의 Query와 토큰 j의 Key의 내적
→ "i가 j에 얼마나 주목할지"의 raw 점수

② 스케일링 : S = S / √d_k # d_k가 크면 내적값이 커져 softmax가
# 한쪽으로 쏠림 → √d_k로 나눠 분산 안정화

③ 정규화 : A = softmax(S) # 각 행의 합 = 1 (주목 비율, attention weight)

④ 가중합 : Z = A · V # 주목 비율대로 Value를 섞음

작은 예(토큰 3개):

A(어텐션 가중치, 3×3) V(값, 3×d)
[0.7 0.2 0.1] [v1] Z1 = 0.7·v1 + 0.2·v2 + 0.1·v3
[0.1 0.8 0.1] · [v2] = Z2 = 0.1·v1 + 0.8·v2 + 0.1·v3
[0.2 0.3 0.5] [v3] Z3 = 0.2·v1 + 0.3·v2 + 0.5·v3

→ 토큰 1은 자기 자신(0.7)에, 토큰 2는 토큰 2에, 토큰 3은 세 토큰을 고르게 참고해 새 표현 Z를 만든다. 문맥에 따라 각 토큰의 의미가 재조합되는 것이 어텐션의 본질입니다.

:::info 왜 √d_k로 나누나 d_k차원 벡터의 내적은 차원이 커질수록 값의 분산이 d_k에 비례해 커집니다. 그대로 softmax를 태우면 가장 큰 값이 1에 가깝게 포화되어 기울기(gradient)가 거의 0이 됩니다. √d_k로 나눠 분산을 ~1로 맞춰 학습을 안정화합니다. :::

4. 멀티헤드 어텐션 (Multi-Head)​

하나의 어텐션만 쓰면 한 종류의 관계만 봅니다. 그래서 여러 개(head)를 병렬로 둡니다.

head_h = Attention(Q·W_Q^h, K·W_K^h, V·W_V^h) # h = 1..H, 각자 다른 부분공간
MultiHead = Concat(head_1, ..., head_H) · W_O
  • 각 head는 서로 다른 관계를 포착(예: 한 head는 문법적 주어–동사, 다른 head는 지시대명사–선행사)
  • 결과를 이어붙여(Concat) 출력 가중치 W_O로 다시 d_model 차원으로 투영

5. 인과 마스킹 (생성의 핵심)​

LLM은 글을 왼쪽부터 한 토큰씩 생성합니다. 따라서 토큰 i는 자신보다 뒤(미래)의 토큰을 보면 안 됩니다. 이를 위해 점수 행렬 S에서 미래 위치를 -∞로 만든 뒤 softmax를 적용합니다.

S' = S + M # M[i][j] = -∞ (j > i), 0 (j ≤ i)
softmax(-∞) → 0 # 미래 토큰의 가중치는 0이 됨

이 causal mask 덕분에 학습 시엔 전체 문장을 한 번에 넣어도 각 위치가 "그 자리까지만" 보고 예측하도록 강제됩니다.

6. 트랜스포머 블록 — 전체 흐름​

어텐션은 블록의 일부이고, 이 블록이 N번(예: 32·80층) 쌓입니다.

입력 X
│
├─▶ LayerNorm ─▶ Multi-Head Attention ─┐
│ + (residual, 잔차 연결)
└───────────────────────────────────────┤
▼
├─▶ LayerNorm ─▶ FFN(피드포워드) ───────┐
│ (Linear → 활성함수(GELU 등) → Linear) + (residual)
└───────────────────────────────────────┤
▼
블록 출력 → 다음 블록
  • 잔차 연결(residual): 입력을 출력에 더해 깊은 층에서도 기울기 소실 방지
  • FFN: 토큰별로 비선형 변환(표현력 담당). 보통 내부 차원이 d_model의 4배
  • N개 블록을 통과한 뒤, 마지막 벡터를 어휘집 크기로 투영 → logits
logits = Z_last · W_vocab # (n × d_model)·(d_model × V) = n × V
P(다음 토큰) = softmax(logits[마지막 위치] / temperature)

→ 이 확률분포에서 다음 토큰을 샘플링. 03 문서의 "다음 토큰 예측"이 바로 이 마지막 단계입니다.

7. 계산량과 컨텍스트 길이​

어텐션 점수 행렬 S 는 n × n → 시간·메모리 복잡도 O(n²)
  • 그래서 컨텍스트가 길어질수록 비용이 제곱으로 증가 → 긴 컨텍스트가 비싼 이유
  • KV 캐시: 생성 시 이미 계산한 K·V를 저장해 토큰마다 재계산을 피함(추론 속도 핵심)
  • 완화 기법: FlashAttention(메모리 효율), GQA/MQA(KV head 공유), 희소·선형 어텐션 등

8. 요약​

요소한 줄
Q·K·V토큰 벡터를 3가지 역할로 투영
Scaled Dot-Productsoftmax(QKᵀ/√d_k)·V — 문맥 기반 재조합
멀티헤드여러 관계를 병렬로 포착
인과 마스킹미래 토큰 차단 → 생성 가능
트랜스포머 블록어텐션 + FFN + 잔차 + LayerNorm, N층 반복
O(n²)긴 컨텍스트가 비싼 근본 이유