Paged Attention을 이해하기 위해서는 먼저 Transformer의 Self-Attention 메커니즘과 KV Cache가 왜 필요한지를 정확히 알아야 합니다. 이것이 Paged Attention이 등장하게 된 가장 근본적인 이유이기 때문입니다.
1. Self-Attention이란 무엇인가?
정의
Self-Attention은 문장 내의 모든 단어(토큰)가 서로에게 얼마나 관련이 있는지를 계산하는 메커니즘입니다. 즉, 한 단어를 이해할 때 문장의 다른 단어들을 "얼마나 참고할지"를 가중치로 계산합니다.
왜 사용하는가?
RNN은 문장을 순차적으로 읽기 때문에 "멀리 떨어진 단어 간의 관계"를 잘 포착하지 못하고, 병렬 연산이 어렵습니다. Self-Attention은 모든 단어 쌍의 관계를 동시에(병렬로) 계산하여 이 문제를 해결합니다.
2. 수식과 계산 과정
Self-Attention의 핵심 수식은 다음과 같습니다:
Attention(Q,K,V)=softmax(dkQKT)V
각 기호의 의미
- Q (Query): "내가 지금 누구를 찾고 있나?" (현재 토큰의 질문)
- K (Key): "나는 어떤 정보를 가지고 있나?" (모든 토큰의 식별자)
- V (Value): "나의 실제 내용은 무엇인가?" (모든 토큰의 실제 정보)
- dk: Key 벡터의 차원 (값이 클수록 softmax의 gradient가 작아지는 것을 방지하기 위해 dk로 나눔)
3. 예시 데이터로 하나하나 풀어보기
예시 문장
"나는 학교에 간다"
토큰화(간단히 단어별로): ["나는", "학교에", "간다"]
임베딩 차원을 4차원으로 가정하고, 각 토큰의 임베딩 벡터를 다음과 같이 정의합니다:
| 토큰 | 임베딩 벡터 (4차원) |
|---|
| 나는 | [1,0,1,0] |
| 학교에 | [0,1,1,0] |
| 간다 | [1,1,0,1] |
Step 1: Q, K, V 행렬 생성
가중치 행렬 WQ,WK,WV를 간단히 다음과 같이 가정합니다 (실제로는 학습됨):
WQ=⎣⎢⎢⎢⎡10100101⎦⎥⎥⎥⎤,WK=⎣⎢⎢⎢⎡01011010⎦⎥⎥⎥⎤,WV=⎣⎢⎢⎢⎡11001011⎦⎥⎥⎥⎤
각 토큰별 Q, K, V 계산:
1) "나는" [1,0,1,0]
- Q1=[1,0,1,0]×WQ=[1×1+0×0+1×1+0×0,1×0+0×1+1×0+0×1]=[2,0]
- K1=[1,0,1,0]×WK=[1×0+0×1+1×0+0×1,1×1+0×0+1×1+0×0]=[0,2]
- V1=[1,0,1,0]×WV=[1×1+0×1+1×0+0×0,1×1+0×0+1×1+0×1]=[1,2]
2) "학교에" [0,1,1,0]
- Q2=[0,1,1,0]×WQ=[0×1+1×0+1×1+0×0,0×0+1×1+1×0+0×1]=[1,1]
- K2=[0,1,1,0]×WK=[0×0+1×1+1×0+0×1,0×1+1×0+1×1+0×0]=[1,1]
- V2=[0,1,1,0]×WV=[0×1+1×1+1×0+0×0,0×1+1×0+1×1+0×1]=[1,1]
3) "간다" [1,1,0,1]
- Q3=[1,1,0,1]×WQ=[1×1+1×0+0×1+1×0,1×0+1×1+0×0+1×1]=[1,2]
- K3=[1,1,0,1]×WK=[1×0+1×1+0×0+1×1,1×1+1×0+0×1+1×0]=[2,1]
- V3=[1,1,0,1]×WV=[1×1+1×1+0×0+1×0,1×1+1×0+0×1+1×1]=[2,2]
정리된 Q, K, V 행렬
Q=⎣⎢⎡211012⎦⎥⎤,K=⎣⎢⎡012211⎦⎥⎤,V=⎣⎢⎡112212⎦⎥⎤
4. Attention Score 계산 과정
Step 2: QKT 계산 (유사도 점수)
KT=[021121]
QKT=⎣⎢⎡2×0+0×21×0+1×21×0+2×22×1+0×11×1+1×11×1+2×12×2+0×11×2+1×11×2+2×1⎦⎥⎤=⎣⎢⎡024223434⎦⎥⎤
Step 3: dk로 나누기 (dk=2이므로 2≈1.414)
2QKT≈⎣⎢⎡01.4142.8281.4141.4142.1212.8282.1212.828⎦⎥⎤
Step 4: Softmax 적용 (행 단위)
첫 번째 행 [0,1.414,2.828]:
- e0=1, e1.414≈4.11, e2.828≈16.92
- 합계: 1+4.11+16.92=22.03
- Softmax: [1/22.03,4.11/22.03,16.92/22.03]≈[0.045,0.187,0.768]
두 번째 행 [1.414,1.414,2.121]:
- e1.414≈4.11, e1.414≈4.11, e2.121≈8.34
- 합계: 4.11+4.11+8.34=16.56
- Softmax: [4.11/16.56,4.11/16.56,8.34/16.56]≈[0.248,0.248,0.504]
세 번째 행 [2.828,2.121,2.828]:
- e2.828≈16.92, e2.121≈8.34, e2.828≈16.92
- 합계: 16.92+8.34+16.92=42.18
- Softmax: [16.92/42.18,8.34/42.18,16.92/42.18]≈[0.401,0.198,0.401]
softmax(2QKT)≈⎣⎢⎡0.0450.2480.4010.1870.2480.1980.7680.5040.401⎦⎥⎤
Step 5: Attention Output 계산 (×V)
Output=softmax(⋯)×V=⎣⎢⎡0.0450.2480.4010.1870.2480.1980.7680.5040.401⎦⎥⎤×⎣⎢⎡112212⎦⎥⎤
첫 번째 행 결과 (나는):
- 첫 번째 값: 0.045×1+0.187×1+0.768×2=0.045+0.187+1.536=1.768
- 두 번째 값: 0.045×2+0.187×1+0.768×2=0.09+0.187+1.536=1.813
두 번째 행 결과 (학교에):
- 첫 번째 값: 0.248×1+0.248×1+0.504×2=0.248+0.248+1.008=1.504
- 두 번째 값: 0.248×2+0.248×1+0.504×2=0.496+0.248+1.008=1.752
세 번째 행 결과 (간다):
- 첫 번째 값: 0.401×1+0.198×1+0.401×2=0.401+0.198+0.802=1.401
- 두 번째 값: 0.401×2+0.198×1+0.401×2=0.802+0.198+0.802=1.802
Output≈⎣⎢⎡1.7681.5041.4011.8131.7521.802⎦⎥⎤
5. 시각화: Self-Attention 흐름도

6. Attention Score 해석
위 히트맵을 보면 다음과 같은 의미를 알 수 있습니다:
- "나는"은 자기 자신(0.045)보다 "간다"(0.768)에 더 높은 가중치를 줍니다. → '나는'이라는 주어가 '간다'라는 동사와 가장 밀접하게 연결됨
- "학교에"는 "간다"(0.504)에 가장 높은 가중치를 줍니다. → '학교에'라는 부사어가 '간다'라는 동사와 연결됨
- "간다"는 "나는"(0.401)과 "간다"(0.401)에 비슷한 가중치를 줍니다. → 동사가 주어와 자신을 동시에 참조
이처럼 Self-Attention은 문장 내에서 의미적으로 연결된 단어들끼리 높은 가중치를 부여하는 것을 확인할 수 있습니다.
7. 왜 KV Cache가 필요한가? (Paged Attention의 전 단계)
지금까지 살펴본 Self-Attention은 모든 토큰이 한 번에 입력되는 경우입니다. 이를 Prefill(또는 Prompt Processing) 단계라고 합니다.
하지만 LLM(대형언어모델)은 실제로 문장을 생성할 때 한 토큰씩 순차적으로 생성합니다. 예를 들어:
입력: "나는 학교에"
출력: "간다" → "." → ...
이미 입력된 "나는", "학교에"의 Key와 Value를 매번 다시 계산하면 엄청난 중복 연산이 발생합니다. 따라서 한 번 계산한 Key와 Value를 메모리에 저장해두고 재사용하는 것이 바로 KV Cache입니다.
정리
| 개념 | 정의 | 왜 사용하는가 |
|---|
| Self-Attention | 문장 내 모든 토큰 쌍의 관련도를 가중치로 계산 | 병렬 처리 + 장거리 의존성 포착 |
| Q (Query) | 현재 토큰이 "무엇을 찾는가" | 다른 토큰과의 관련도 측정 기준 |
| K (Key) | 각 토큰의 "식별자" | Query와의 유사도 계산 대상 |
| V (Value) | 각 토큰의 "실제 정보" | 최종 출력의 가중합 재료 |
| Softmax | 유사도를 확률(합=1)로 변환 | 해석 가능한 가중치 생성 |
| KV Cache | 이전 토큰의 K, V를 메모리에 저장 | 중복 연산 제거, 생성 속도 향상 |