Ch.7 Attention — AI의 집중력

Self-Attention 직접 구현

NumPy로 셀프 어텐션을 처음부터 구현한다각 단계의 행렬 크기 변화를 추적한다어텐션 가중치의 의미를 해석한다

6단계로 어텐션을 만든다

'나는 고양이를 좋아한다' — 3개 토큰이 서로에게 얼마나 주목하는지 직접 계산해봅니다.

행렬 곱셈이 이렇게 많은데 차원이 안 꼬일까?

셀프 어텐션 구현 — 6단계 코드를 한 줄씩 따라가며 행렬 크기의 변화를 추적합니다.


article

핵심 내용

6단계로 어텐션을 처음부터 구현합니다

셀프 어텐션 구현 6단계: Step 0: 입력 임베딩 (3 토큰 × 4 차원) Step 1: 가중치 행렬 W_Q, W_K, W_V 생성 Step 2: Q, K, V 계산 (행렬 곱) Step 3: 어텐션 스코어 = Q × Kᵀ Step 4: 스케일링 (÷ √d_k) Step 5: 소프트맥스 → 확률 가중치 Step 6: 가중 합 = Weights × V

'나는 고양이를 좋아한다'로 셀프 어텐션을 계산합니다

import numpy as np

np.random.seed(42)

# Step 0: 입력 — 3개 토큰, 4차원 임베딩
tokens = ["나는", "고양이를", "좋아한다"]
X = np.array([
    [1.0, 0.0, 1.0, 0.0],   # "나는"
    [0.0, 1.0, 0.0, 1.0],   # "고양이를"
    [1.0, 1.0, 0.0, 0.0],   # "좋아한다"
])
d_k = 4

print("=== Step 0: 입력 임베딩 ===")
for token, vec in zip(tokens, X):
    print(f"  {token:6s} → {vec}")

# Step 1: 가중치 행렬
W_Q = np.random.randn(4, 4) * 0.5
W_K = np.random.randn(4, 4) * 0.5
W_V = np.random.randn(4, 4) * 0.5

# Step 2: Q, K, V 계산
Q = X @ W_Q   # (3,4) × (4,4) = (3,4)
K = X @ W_K
V = X @ W_V
print(f"\n=== Step 2: Q, K, V 크기 = {Q.shape} ===")

# Step 3: 어텐션 스코어 = Q × K^T
scores = Q @ K.T   # (3,4) × (4,3) = (3,3)
print(f"\n=== Step 3: 스코어 크기 = {scores.shape} ===")

# Step 4: 스케일링
scores_scaled = scores / np.sqrt(d_k)

# Step 5: Softmax → 확률 가중치
def softmax(x):
    exp_x = np.exp(x - np.max(x, axis=-1, keepdims=True))
    return exp_x / np.sum(exp_x, axis=-1, keepdims=True)

attention_weights = softmax(scores_scaled)

print(f"\n=== Step 5: 어텐션 가중치 ===")
print("         나는   고양이를 좋아한다")
for i, token in enumerate(tokens):
    w_str = "  ".join(f"{w:.3f}" for w in attention_weights[i])
    print(f"  {token:6s} [{w_str}]")

# Step 6: 가중 합
output = attention_weights @ V   # (3,3) × (3,4) = (3,4)
print(f"\n=== Step 6: 최종 출력 크기 = {output.shape} ===")
print("→ 각 토큰에 다른 토큰들의 정보가 섞여 들어갔습니다!")

입력이 (3, 4)이고 W_Q가 (4, 4)일 때 Q = X × W_Q의 행렬 크기는?

어텐션 가중치 행렬의 각 행의 합은 항상 1이다 (소프트맥스를 적용했으므로).

3개 토큰, 4차원 임베딩일 때 어텐션 스코어 행렬(Q × Kᵀ)의 크기는?

다음 셀프 어텐션 코드의 빈칸을 채우세요. scores = Q @ K.T / np.sqrt(___) weights = softmax(scores)

Q @ K.T / np.sqrt(___)

셀프 어텐션에서 마스킹(masking)은 미래 토큰 정보의 유출을 방지하기 위해 사용한다.

셀프 어텐션 구현!

edit_note

정리 노트

셀프 어텐션 6단계 구현 정리

구현 6단계

Step 0
입력 임베딩 — (토큰 수 × 차원) 행렬 준비
Step 1~2
W_Q, W_K, W_V 생성 → 행렬 곱으로 Q, K, V 계산
Step 3
어텐션 스코어 = Q × Kᵀ → (토큰 수 × 토큰 수)
Step 4
÷ √d_k 스케일링으로 값 안정화
Step 5
Softmax → 각 행의 합 = 1인 확률 가중치
Step 6
가중 합 = Weights × V → 문맥이 섞인 출력

차원 추적

Q, K, V
(토큰 수, d_k) — 입력과 동일한 크기
스코어 행렬
(토큰 수, 토큰 수) — 모든 쌍의 유사도
최종 출력
(토큰 수, d_k) — 문맥 정보가 반영된 벡터

Softmax 적용 후 각 행의 합은 항상 1 — 총 100%의 주의를 배분한다

image

시각 자료

다이어그램: py-scene-multi-head
check_circle

핵심 정리

  • 16단계로 셀프 어텐션 구현: 입력→Q,K,V→스코어→스케일→Softmax→가중합
  • 2핵심 차원: (토큰수, 차원) 유지, 스코어는 (토큰수, 토큰수)
  • 3각 토큰의 출력에 다른 토큰들의 정보가 '문맥'으로 섞임

퀴즈와 인터랙션으로 더 깊이 학습하세요

play_circle인터랙티브 레슨 시작