본문 바로가기
AI란 무엇인가/AI 기본

Transformer 어텐션 메커니즘 완전 해부

by brainstormingai 2026. 4. 1.

 

 

AI Architecture Deep Dive · Series 05

Attention Transformer 어텐션 메커니즘 — Q·K·V부터 Masked·Cross·Multi-Head까지 완전 해부

ChatGPT가 긴 문장을 이해하는 원리, BERT가 문맥을 파악하는 비결, 번역 AI가 원문과 번역문을 연결하는 방법 — 그 모든 것의 뿌리가 바로 어텐션 메커니즘이다. 수식과 시각화로 낱낱이 분해한다.

Query (Q) Key (K) Value (V) Attention Score Multi-Head 2025 · 읽기 약 18분
01Why Attention
 

어텐션은 왜
필요한가

Transformer 이전의 seq2seq 모델은 입력 전체를 고정 크기의 단일 벡터(Context Vector)에 압축했다. 100단어짜리 문장도, 1000단어짜리 문서도 같은 크기의 벡터 하나에 욱여넣어야 했다. 이 병목이 긴 문장에서 치명적인 성능 저하를 일으켰다.

기존 seq2seq — Context Vector 병목 나는 파리에 살았다 그래서 프랑스어를 Encoder (RNN) — 순차 처리 Context Vector ⚠ 모든 정보 압축 → 손실! Decoder — 단일 벡터 의존 Attention — 모든 토큰을 직접 참조 나는 파리에 살았다 그래서 프랑스어를 0.62 ↑ 높음 0.21 0.09 Attention Layer — 모든 쌍 동시 계산 문맥 반영 표현 (Context-aware) ✓ "파리에"와 "프랑스어를"의 직접 연결!
Fig.1 — 기존 Context Vector 병목(좌) vs Attention의 직접 참조(우)

02Query · Key · Value
 

Q · K · V
세 벡터의 정확한 의미

어텐션의 모든 것은 세 벡터로 시작한다. Q(Query), K(Key), V(Value). 이 이름은 데이터베이스 검색 시스템에서 차용한 비유다.

Q
Query — 질의

"내가 찾는 정보는?"

현재 처리 중인 토큰이 무엇을 알고 싶은지를 나타내는 벡터. 검색 엔진의 검색어에 해당한다.

예: "그녀는" → 다음에 오는 동사 정보 탐색
K
Key — 색인

"나는 어떤 정보를 가졌나?"

각 토큰이 자신이 어떤 정보를 제공할 수 있는지를 광고하는 벡터. 데이터베이스의 색인 키에 해당한다.

예: "달렸다" → 동사·움직임 정보 보유
V
Value — 실제 값

"실제 전달할 내용"

Q와 K의 유사도가 높을 때 실제로 전달되는 정보의 벡터. Key가 광고라면 Value는 실제 상품이다.

예: "달렸다"의 의미 표현 벡터
입력 X (d_model 차원) x₁ "나는" x₂ "AI를" x₃ "공부" x₄ "한다" ⋮ [seq_len × d_model] W_Q 학습 파라미터 [d_model × d_k] W_K 학습 파라미터 [d_model × d_k] W_V 학습 파라미터 [d_model × d_v] × × × Q Query Matrix [seq × d_k] K Key Matrix [seq × d_k] V Value Matrix [seq × d_v] ① QKᵀ / √d_k 유사도 점수 행렬 ② Softmax 가중치 정규화 ③ Attn × V 가중 합산 → 출력 Attention Output [seq × d_v] 입력 학습 파라미터 Q·K·V 행렬 연산 파이프라인
Fig.2 — 입력 X에서 Q·K·V 생성 → Attention 연산 전체 파이프라인

03Scaled Dot-Product Attention
 

핵심 수식 —
Scaled Dot-Product Attention

// 핵심 공식 — Attention(Q, K, V)
Attention(Q, K, V) = softmax( QKᵀ / √d_k ) · V
d_k: Query와 Key의 차원 수 (스케일링 팩터) — GPT-3: d_k = 128, GPT-4: d_k = 추정 160~256

이 수식이 작동하는 4단계를 하나씩 정밀 해부한다.

01

내적 계산 (Dot Product) — QKᵀ

Q 행렬과 K 행렬의 전치(Transpose)를 내적한다. 결과는 [seq_len × seq_len] 정방 행렬 — 모든 토큰 쌍 간의 유사도 점수. 두 벡터가 같은 방향을 향할수록(의미가 유사할수록) 내적값이 크다. "파리에"(Key)가 "프랑스어"(Query)와 높은 점수를 가진다면, 두 단어가 강하게 연관됨을 의미한다.

02

스케일링 (Scaling) — ÷ √d_k

d_k의 제곱근으로 나눈다. 이유: d_k가 클수록 내적값이 기하급수적으로 커져 Softmax가 매우 작은 기울기(포화 구간)에 빠진다. 예를 들어 d_k = 64√64 = 8로 나누어 값을 안정화한다. 이 "scaled" 처리가 없으면 학습이 불안정해진다.

03

Softmax — 확률 분포로 정규화

각 행(Query 토큰)에 대해 Softmax를 적용한다. 결과: 각 행의 합이 1이 되는 확률 행렬 — 이것이 어텐션 가중치(Attention Weights)다. 각 Query가 다른 모든 토큰에 얼마나 주의를 기울일지의 확률. 값이 클수록 "이 토큰에 더 집중"한다는 의미.

04

Value 가중 합산 — × V

어텐션 가중치와 V 행렬을 곱한다. 결과: 각 Query 토큰의 최종 표현 — 주의를 많이 받은 토큰의 Value가 더 많이 반영된다. "프랑스어"의 새 표현에는 "파리에"의 Value가 높은 비중으로 섞이고, "나는"의 Value는 낮은 비중으로 섞인다. 이것이 문맥이 반영된 표현(Context-aware Representation)이다.

Q [4×4] 0.8 0.2 0.5 0.1 0.3 0.9 0.1 0.7 0.6 0.4 0.8 0.2 0.1 0.5 0.3 0.9 [seq=4, d_k=4] · Kᵀ Kᵀ [4×4] 0.7 0.3 0.5 0.2 0.2 0.8 0.3 0.6 0.6 0.1 0.9 0.4 0.3 0.7 0.2 0.8 [d_k=4, seq=4] ÷ √4 =2 Score [4×4] 0.82 0.31 0.54 0.18 0.22 0.89 0.14 0.45 0.38 0.27 0.91 0.16 0.12 0.29 0.25 0.78 Softmax Attn Weights [4×4] 0.48 0.18 0.23 0.11 합=1.00 ✓ 0.14 0.51 0.10 0.25 0.22 0.17 0.53 0.08 0.09 0.18 0.16 0.57 × V Output [4×4] 문맥 반영 표현 벡터 ✓ [seq × d_v] 높은 유사도 낮은 유사도 각 행의 Softmax 합 = 1.0
Fig.3 — QKᵀ 행렬 연산 → Score 히트맵 → Softmax → V 가중 합산 전체 흐름

04Softmax & Attention Weights
 

Softmax가 만드는
어텐션 가중치의 의미

√d_k 스케일링 없을 때 (d_k=512) T₁ ≈ 1.0 T₃ ⚠ 하나에 집중 → 기울기 소실, 정보 손실 Softmax 포화 구간 → 학습 불안정 0.0 0.5 1.0 √d_k 스케일링 후 (÷√512≈22.6) 0.22 0.26 0.28 0.24 ✓ 균형 잡힌 분포 → 안정적 학습 다양한 토큰에 분산 주의 → 더 풍부한 표현 0.0 0.5 1.0
Fig.4 — √d_k 스케일링의 효과: 포화된 분포(좌) vs 안정적 분포(우)

05Interactive Attention Heatmap
 

어텐션 가중치를
직접 체험하다

실제 문장에서 어텐션이 어떻게 형성되는지 인터랙티브 히트맵으로 확인해보자. 버튼을 클릭해 다양한 시나리오를 탐색할 수 있다.

// Attention Weight Heatmap
행(Row) = Query 토큰, 열(Column) = Key 토큰 | 진할수록 높은 어텐션 가중치
 
 

06Multi-Head Attention
 

여러 시각 —
Multi-Head Attention

단일 어텐션은 하나의 관점만 본다. Multi-Head Attention은 Q, K, V를 h개의 부분으로 나눠 h개의 독립적인 어텐션을 병렬 계산하고 합친다.

Input X Head 1 W_Q¹ W_K¹ W_V¹ → 문법 관계 주어↔동사 d_k = d_model/h Head 2 W_Q² W_K² W_V² → 의미 관계 동의어↔유사어 d_k = d_model/h Head 3 W_Q³ W_K³ W_V³ → 지시어 해소 대명사↔선행사 d_k = d_model/h Head 4 W_Q⁴ W_K⁴ W_V⁴ → 위치 관계 인접 토큰 참조 d_k = d_model/h Head h W_Qʰ W_Kʰ W_Vʰ → 기타 관계 ... 자율 학습 d_k = d_model/h ··· Concat(head₁, ..., headₕ) [seq × (h × d_v)] = [seq × d_model] Linear (W_O) — 출력 프로젝션 [seq × d_model] — 입력과 동일 차원 Multi-Head Attention Output
Fig.5 — Multi-Head Attention: h개 헤드 병렬 → Concat → 선형 변환
// Multi-Head Attention 수식
MultiHead(Q,K,V) = Concat(head₁,...,headₕ) · W_O
여기서 headᵢ = Attention(QW_Qⁱ, KW_Kⁱ, VW_Vⁱ)
GPT-3: h=96헤드, d_model=12288 → d_k = 12288/96 = 128 | GPT-4o: h=128 추정

07Masked Self-Attention
 

미래를 가리다 —
Masked Self-Attention

GPT 같은 디코더 전용 모델이 텍스트를 생성할 때, 아직 생성하지 않은 미래 토큰을 미리 보는 것은 "정답지 보기"와 같다. Masked Self-Attention은 Score 행렬의 상삼각 부분을 -∞로 마스킹해 미래 토큰 참조를 차단한다.

QKᵀ Score (마스킹 전) 나는 AI를 공부 한다 나는 AI를 공부 한다 0.72 0.38 0.81 0.21 0.44 0.88 0.16 0.29 0.52 0.91 -∞ -∞ -∞ -∞ -∞ -∞ → Softmax (-∞ → 0) Softmax 후 어텐션 가중치 나는 AI를 공부 한다 나는 AI를 공부 한다 1.00 0 0 0 0.32 0.68 0 0 0.18 0.33 0.49 0 0.13 0.24 0.38 0.25 각 행의 합 = 1.0 미래 토큰 = 0 (정보 차단)
Fig.6 — Masked Self-Attention: 상삼각 -∞ 마스킹 → Softmax 후 미래 토큰 가중치 = 0

08Cross-Attention
 

인코더와 디코더를 잇다 —
Cross-Attention

번역 모델처럼 인코더-디코더 구조에서, 디코더는 자신이 생성할 출력뿐 아니라 인코더가 처리한 원문 정보를 참조해야 한다. Cross-Attention이 이 역할을 한다.

인코더 출력 (원문) "I studied AI yesterday" → K, V 생성 (원문 정보) W_K · Encoder_out = K W_V · Encoder_out = V K (원문) V (원문) 디코더 출력 (번역 중) "나는 어제 AI를..." → Q 생성 (번역문 정보) W_Q · Decoder_out = Q Q (번역문) Cross-Attention Q ← 디코더 (번역문) K, V ← 인코더 (원문) Attention(Q_dec, K_enc, V_enc) "공부했다"를 생성할 때 "studied"에 높은 어텐션 Cross-Attention 출력 원문 문맥이 반영된 번역 표현 벡터 "공부했다" ← "studied" 참조
Fig.7 — Cross-Attention: 디코더의 Q가 인코더의 K·V를 참조해 원문-번역 연결

09Three Types Compared
 

세 가지 어텐션
완전 비교

구분 Self-Attention Masked Self-Attention Cross-Attention
Q 출처 자기 자신(같은 시퀀스) 자기 자신(디코더) 디코더 출력
K·V 출처 자기 자신(같은 시퀀스) 자기 자신(디코더) 인코더 출력
마스킹 없음 — 양방향 있음 — 미래 차단 없음 — 전체 원문 참조
사용 위치 인코더 전체 디코더 첫 번째 서브레이어 디코더 두 번째 서브레이어
대표 모델 BERT, RoBERTa GPT, Claude, LLaMA 원본 Transformer, T5, BART
주요 역할 양방향 문맥 이해 순차적 텍스트 생성 원문↔번역 정보 연결
어텐션 패턴 완전한 정방 행렬 하삼각 행렬 직사각 행렬 (seq_dec × seq_enc)

10PyTorch Implementation
 

코드로 완성하는
Attention 구현

 
attention_mechanism.py — 전체 어텐션 구현
import torch
import torch.nn as nn
import torch.nn.functional as F
import math


# ════════════════════════════════════════════════
#  핵심: Scaled Dot-Product Attention
#  Attention(Q, K, V) = softmax(QKᵀ / √d_k) · V
# ════════════════════════════════════════════════
def scaled_dot_product_attention(
    Q: torch.Tensor,          # [B, H, S, d_k]  Query
    K: torch.Tensor,          # [B, H, S, d_k]  Key
    V: torch.Tensor,          # [B, H, S, d_v]  Value
    mask: torch.Tensor = None  # Masked Attention용
):
    d_k = Q.size(-1)

    # ① QKᵀ 내적 + 스케일링 → [B, H, S_q, S_k]
    scores = torch.matmul(Q, K.transpose(-2, -1)) / math.sqrt(d_k)

    # ② Masking: 미래 토큰 -∞ 처리 (Masked Self-Attention)
    if mask is not None:
        scores = scores.masked_fill(mask == 0, float('-inf'))

    # ③ Softmax → 어텐션 가중치 [B, H, S_q, S_k], 각 행의 합 = 1
    attn_weights = F.softmax(scores, dim=-1)

    # ④ 어텐션 가중치 × V → 최종 출력 [B, H, S_q, d_v]
    output = torch.matmul(attn_weights, V)
    return output, attn_weights  # 가중치도 반환 (시각화용)


# ════════════════════════════════════════════════
#  Multi-Head Attention (Self / Masked / Cross 통합)
# ════════════════════════════════════════════════
class MultiHeadAttention(nn.Module):
    def __init__(self, d_model: int, n_heads: int):
        super().__init__()
        assert d_model % n_heads == 0, "d_model은 n_heads로 나눠져야 합니다"

        self.d_model  = d_model
        self.n_heads  = n_heads
        self.d_k      = d_model // n_heads  # 헤드당 차원

        # Q·K·V 투영 행렬 (학습 파라미터)
        self.W_Q = nn.Linear(d_model, d_model, bias=False)  # W_Q: d_model→d_model
        self.W_K = nn.Linear(d_model, d_model, bias=False)  # W_K: d_model→d_model
        self.W_V = nn.Linear(d_model, d_model, bias=False)  # W_V: d_model→d_model
        self.W_O = nn.Linear(d_model, d_model, bias=False)  # 출력 투영

    def split_heads(self, x: torch.Tensor) -> torch.Tensor:
        # [B, S, d_model] → [B, H, S, d_k] 로 헤드 분리
        B, S, _ = x.shape
        x = x.view(B, S, self.n_heads, self.d_k)
        return x.transpose(1, 2)  # [B, H, S, d_k]

    def forward(
        self,
        query: torch.Tensor,           # Q 소스: Self→자기자신, Cross→디코더
        key:   torch.Tensor = None,  # K 소스: Self→자기자신, Cross→인코더
        value: torch.Tensor = None,  # V 소스: Self→자기자신, Cross→인코더
        mask:  torch.Tensor = None   # Masked Self-Attention용
    ):
        # Cross-Attention이면 key/value를 인코더 출력으로, 아니면 자기 자신
        if key is None: key = query    # Self-Attention
        if value is None: value = query  # Self-Attention

        B = query.size(0)

        # Q·K·V 투영 + 헤드 분리
        Q = self.split_heads(self.W_Q(query))  # [B, H, S_q, d_k]
        K = self.split_heads(self.W_K(key))    # [B, H, S_k, d_k]
        V = self.split_heads(self.W_V(value))  # [B, H, S_k, d_v]

        # 어텐션 계산 (모든 헤드 병렬)
        attn_out, attn_weights = scaled_dot_product_attention(Q, K, V, mask)

        # 헤드 합치기: [B, H, S, d_k] → [B, S, d_model]
        attn_out = attn_out.transpose(1, 2).contiguous()
        attn_out = attn_out.view(B, -1, self.d_model)

        return self.W_O(attn_out), attn_weights  # 출력 + 어텐션 가중치


# ════════════════════════════════════════════════
#  Causal Mask 생성 (Masked Self-Attention용)
# ════════════════════════════════════════════════
def make_causal_mask(seq_len: int, device: str = 'cpu'):
    """하삼각 마스크: 미래 토큰 참조 차단"""
    mask = torch.tril(torch.ones(seq_len, seq_len, device=device))
    # [[1,0,0,0],   ← "나는"은 자신만 참조
    #  [1,1,0,0],   ← "AI를"은 자신+이전만
    #  [1,1,1,0],   ← "공부"는 앞 3개
    #  [1,1,1,1]]   ← "한다"는 모두 참조
    return mask.unsqueeze(0).unsqueeze(0)  # [1, 1, S, S] 브로드캐스트용


# ════ 사용 예시 ════
B, S, d_model, n_heads = 2, 8, 512, 8
mha = MultiHeadAttention(d_model, n_heads)

x_enc = torch.randn(B, S, d_model)  # 인코더 입력
x_dec = torch.randn(B, S, d_model)  # 디코더 입력
causal_mask = make_causal_mask(S)

# 1. Self-Attention (인코더용 — 마스크 없음)
out_self, w_self = mha(x_enc)
print(f"Self-Attention 출력: {out_self.shape}")       # [2, 8, 512]
print(f"어텐션 가중치 형태: {w_self.shape}")            # [2, 8, 8, 8]

# 2. Masked Self-Attention (디코더 첫 번째 레이어)
out_masked, _ = mha(x_dec, mask=causal_mask)
print(f"Masked Self-Attention 출력: {out_masked.shape}") # [2, 8, 512]

# 3. Cross-Attention (디코더 두 번째 레이어)
# Q=디코더, K·V=인코더 (핵심 차이!)
out_cross, _ = mha(query=x_dec, key=x_enc, value=x_enc)
print(f"Cross-Attention 출력: {out_cross.shape}")        # [2, 8, 512]
// 핵심 정리

어텐션을 이해하면
AI가 보인다

어텐션 메커니즘의 본질은 단순하다 — 모든 토큰이 다른 모든 토큰과의 관련성을 직접 계산한다. Q·K 내적으로 유사도를 측정하고, Softmax로 정규화하고, V를 가중 합산하는 세 단계가 전부다.

Self-Attention은 양방향 문맥을 이해하고(BERT), Masked Self-Attention은 과거만 보며 순차 생성하고(GPT), Cross-Attention은 두 시퀀스를 연결한다(번역). 이 세 가지 변형이 현대 AI의 모든 기능을 가능하게 한다.

다음 시리즈에서는 Positional EncodingLayer Normalization의 수학적 원리를 깊이 파고든다.

#어텐션메커니즘 #SelfAttention #QKV #Transformer #MultiHeadAttention #MaskedAttention #CrossAttention #GPT #BERT #딥러닝 #PyTorch #LLM #AI아키텍처 #개발자블로그

가치 보강: 2026년 5월 23일 기준

이 글은 독자가 바로 적용할 수 있는 기준을 더하기 위해 2026년 5월 23일 기준으로 보강했습니다. 단순 정보 나열보다 실제 예시, 확인 순서, 관련 글 연결을 함께 보는 것이 블로그 글의 가치를 높입니다.

실전 적용 예시

상황 어떻게 보면 좋은가
처음 읽을 때 글의 결론과 적용 대상을 먼저 확인합니다.
실제로 쓸 때 내 상황에 맞는 예시만 골라 적용하고, 숫자나 정책은 원문을 확인합니다.
다음 행동 관련 글을 이어 읽어 주제 전체 흐름을 잡습니다.

읽고 바로 확인할 것

  • 내 상황에 적용 가능한 글인지 확인했는가?
  • 날짜, 정책, 요금, 게임 정보처럼 바뀌는 내용은 다시 확인했는가?
  • 관련 글을 함께 읽어 맥락을 보완했는가?
  • 글의 예시를 그대로 복사하지 않고 내 상황에 맞게 바꿨는가?

추가 참고자료: G, h

반응형