어텐션 변형(Attention Variants) — Sliding Window, Sparse, Differential
풀 어텐션(Full attention)은 하나의 원입니다. 모든 토큰(token)이 모든 토큰을 바라보고, 그 비용은 메모리(memory)가 치릅니다. 네 가지 변형(variant)은 이 원의 모양을 휘어서 비용의 절반 가까이를 되찾아 옵니다.
유형: Build
언어: Python
선수 지식: Phase 7 · 02 (Self-Attention), Phase 7 · 03 (Multi-Head), Phase 7 · 12 (KV Cache / Flash Attention)
예상 시간: 약 60분
문제
풀 어텐션은 시퀀스 길이(sequence length)에 대해 메모리도 연산량(compute)도 모두 O(N²) 비용을 치릅니다. 128K 컨텍스트의 Llama 3 70B를 예로 들면, 레이어(layer) 한 장에 어텐션 항목(attention entry)이 약 160억 개이고, 이것이 80개 레이어에 걸쳐 반복됩니다. Flash Attention(Lesson 12)은 O(N²) 크기의 활성값 메모리(activation memory)는 가려주지만 산술 비용(arithmetic cost) 자체를 줄이지는 못합니다. 모든 토큰은 여전히 다른 모든 토큰에 어텐드(attend)합니다.
어텐션 행렬(attention matrix)의 위상(topology) 자체를 바꾸는 세 가지 갈래가 있습니다.
- 슬라이딩 윈도우 어텐션(Sliding Window Attention; SWA). 각 토큰이 전체 접두부(prefix) 대신 고정된 크기의 이웃 창(window)만 바라봅니다. 메모리와 연산량이
O(N · W)로 줄어듭니다(여기서 W는 창 크기). Gemma 2/3, Mistral 7B의 앞쪽 레이어, Phi-3-Long이 사용합니다.
- 희소/블록 어텐션(Sparse / block attention). 미리 선택된 짝
(i, j)에만 점수를 매기고 나머지는 가중치(weight)를 0으로 강제합니다. Longformer, BigBird, OpenAI sparse transformer가 대표적입니다.
- 차분 어텐션(Differential attention). 별도의 Q/K 사영(projection) 두 벌로 어텐션 맵(attention map) 두 장을 계산한 다음, 하나에서 다른 하나를 뺍니다. 가중치가 앞쪽 몇 개의 토큰으로 새어 나가는 "어텐션 싱크(attention sink)"를 제거합니다. Microsoft의 DIFF Transformer(2024)가 대표 사례입니다.
이 세 갈래는 서로 배타적이지 않습니다. 2026년의 프런티어 모델(frontier model)은 보통 이들을 섞어 씁니다. 대부분의 레이어는 윈도우 1024의 SWA로 두고, 다섯 번째마다 한 장씩은 전체 풀 어텐션을 넣으며, 검색(retrieval) 품질을 다듬는 차분 헤드(differential head)도 몇 장 두는 식입니다. Gemma 3가 채택한 5:1 SWA-to-global 비율은 사실상 현재의 표준 교과서 설정입니다.
개념
슬라이딩 윈도우 어텐션(Sliding Window Attention; SWA)
위치 i의 쿼리(query)는 인과적(causal) SWA에서는 [i - W, i] 범위, 양방향(bidirectional)에서는 [i - W/2, i + W/2] 범위의 위치에만 어텐드합니다. 창 바깥의 토큰은 점수 행렬(score matrix)에서 -inf 값을 받습니다.
full causal: sliding window (W=4):
positions 0-7 positions 0-7, W=4
0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7
0 | x 0 | x
1 | x x 1 | x x
2 | x x x 2 | x x x
3 | x x x x 3 | x x x x
4 | x x x x x 4 | x x x x
5 | x x x x x x 5 | x x x x
6 | x x x x x x x 6 | x x x x
7 | x x x x x x x x 7 | x x x x
N = 8192, W = 1024라면 점수 행렬의 0이 아닌 행(row)의 개수가 기대값으로 1024 × 8192가 되어, 풀 어텐션 대비 약 8배 줄어듭니다.
KV 캐시(KV cache)도 함께 줄어듭니다. 레이어마다 K와 V는 마지막 W개 토큰만 보관하면 됩니다. Gemma 3 계열 설정(창 1024, 컨텍스트 128K)에서는 KV 캐시가 128배 작아집니다.
품질 비용(quality cost). SWA만으로 구성된 트랜스포머는 장거리 검색(long-range retrieval)에 약합니다. 대안은 SWA 레이어와 풀 어텐션 레이어를 교차로(interleave) 끼우는 것입니다. Gemma 3는 SWA와 전역(global) 레이어를 5:1 비율로 섞습니다. Mistral 7B는 정보가 겹치는 창들을 통해 앞으로 흐르는(causal-SWA) 스택을 사용했습니다. 각 레이어가 유효 수용 영역(effective receptive field)을 W만큼 늘려 주기 때문에, L개 레이어를 지나면 모델은 사실상 L × W 토큰까지 거슬러 올라갈 수 있습니다.
희소/블록 어텐션(Sparse / Block Attention)
N × N 희소 패턴(sparsity pattern)을 미리 정해 둡니다. 자주 쓰이는 모양은 세 가지입니다.
- 국소 + 스트라이드(Local + strided, OpenAI sparse transformer). 마지막
W개 토큰에 더해, 그 이전 구간에서는 stride 간격으로 한 토큰씩 어텐드합니다. O(N · sqrt(N)) 연산량으로 국소 정보(local)와 장거리 정보(long-range)를 동시에 잡습니다.
- Longformer / BigBird. 국소 창에 더해, 모두에게 어텐드하고 모두로부터 어텐드되는 소수의 전역 토큰(
[CLS] 등)을 두고, 무작위 희소 링크(random-sparse link)까지 더합니다. 같은 품질에서 컨텍스트를 2배로 늘린 사례가 보고되었습니다.
- 네이티브 희소 어텐션(Native Sparse Attention; DeepSeek, 2025). 어떤
(Q, K) 블록(block)이 중요한지를 학습으로 골라내고, 0 블록은 커널(kernel) 수준에서 건너뜁니다. FlashAttention과 호환됩니다.
희소 어텐션의 핵심은 결국 커널 엔지니어링(kernel engineering)입니다. 수학적으로는 점수 행렬에 마스크(mask)를 씌우는 단순한 연산이지만, 실제 이득은 0인 항목을 아예 SRAM에 올리지 않는 데서 나옵니다. FlashAttention-3와 2026년의 FlexAttention API는 PyTorch에서 임의의 희소 패턴을 일급(first-class) 시민으로 다룰 수 있게 해 줍니다.
일반(regular) 어텐션에는 "어텐션 싱크(attention sink)" 문제가 있습니다. 소프트맥스(softmax)가 모든 행의 합을 1로 강제하기 때문에, 딱히 어디에도 어텐드할 이유가 없는 토큰은 첫 토큰(혹은 앞쪽 몇 토큰)에 가중치를 떠넘기게 됩니다. 이렇게 흘러간 가중치는 본래 실제 내용에 쓰여야 할 모델 용량(capacity)을 빼앗아 갑니다.
차분 어텐션은 어텐션 맵을 두 장 계산한 뒤 빼는 방식으로 이 문제를 해결합니다.
A1 = softmax(Q1 K1^T / √d)
A2 = softmax(Q2 K2^T / √d)
DiffAttn = (A1 - λ · A2) V
λ는 학습되는 스칼라(learned scalar)이며, 보통 0.5–0.8 범위입니다. A1은 실제 내용의 가중치를 잡아내고, A2는 싱크 성분을 잡아냅니다. 두 어텐션 맵을 빼면 싱크가 상쇄되고, 그만큼의 가중치는 실제로 의미 있는 토큰으로 재배치됩니다.
보고된 결과(Microsoft 2024): 퍼플렉서티(perplexity)가 5–10% 낮아지고, 같은 학습 길이에서 유효 컨텍스트(effective context)가 1.5–2배 늘어나며, needle-in-haystack 검색이 더 날카로워집니다.
변형 비교(Variant Comparison)
| 변형(Variant) | 연산량(Compute) | KV 캐시(KV cache) | 풀 어텐션 대비 품질 | 실제 사용처 |
|---|
| Full attention | O(N²) | 레이어당 O(N) | 기준선(baseline) | 거의 모든 모델의 기본 레이어 |
| SWA (window 1024) | O(N·W) | 레이어당 O(W) | -0.1 ppl, 전역 레이어와 함께라면 양호 | Gemma 2/3, Phi-3-Long |
| Local + strided sparse | O(N·√N) | 혼합(mixed) | SWA와 유사 | OpenAI sparse transformer, Longformer |
| BigBird (local + global + random) | 대략 O(N) | 혼합 | 2배 컨텍스트에서도 풀 어텐션 수준 | 초기 장기 컨텍스트 BERT |
| Native Sparse (DeepSeek-V3.2) | O(N · active fraction) | O(N) | 0.05 ppl 이내 | DeepSeek-V3.2, 2025 |
| Differential | O(2·N²) | O(2N) | ppl -5 ~ -10% | DIFF Transformer, 2026년 초 모델들 |
직접 만들기
code/main.py를 참고합니다. 장난감(toy) 시퀀스 위에서 풀 어텐션, SWA, 국소 + 스트라이드, 차분 어텐션을 나란히 비교해 보여주는 인과적 마스크 비교기(causal mask comparator)를 구현합니다.
Step 1: 풀 인과적 마스크(full causal mask, baseline)
def causal_mask(n):
return [[0.0 if j <= i else float("-inf") for j in range(n)] for i in range(n)]
Lesson 07에서 다룬 기준선입니다. 하삼각(lower triangular) 형태이며, 대각선 위쪽은 가중치가 0이 됩니다.
Step 2: 슬라이딩 윈도우 인과적 마스크(sliding window causal mask)
def swa_mask(n, window):
M = [[float("-inf")] * n for _ in range(n)]
for i in range(n):
lo = max(0, i - window + 1)
for j in range(lo, i + 1):
M[i][j] = 0.0
return M
매개변수(parameter)는 window 하나뿐입니다. window >= n이면 풀 인과적 어텐션을 그대로 복원하고, window = 1이면 각 토큰은 자기 자신만 바라봅니다.
Step 3: 국소 + 스트라이드 희소 마스크(local + strided sparse mask)
def strided_mask(n, window, stride):
M = [[float("-inf")] * n for _ in range(n)]
for i in range(n):
lo = max(0, i - window + 1)
for j in range(lo, i + 1):
M[i][j] = 0.0
for j in range(0, i + 1, stride):
M[i][j] = 0.0
return M
조밀한 국소 창에 더해, 시퀀스 시작점부터 stride 간격으로 한 토큰씩 추가로 보게 합니다. 레이어가 쌓일수록 수용 영역(receptive field)이 로그 스케일로 성장합니다.
Step 4: 차분 어텐션(differential attention)
def diff_attention(Q1, K1, Q2, K2, V, lam):
A1 = softmax_causal(Q1 @ K1.T / sqrt_d)
A2 = softmax_causal(Q2 @ K2.T / sqrt_d)
return (A1 - lam * A2) @ V
어텐션 패스를 두 번 돌린 뒤, 학습된 혼합 계수(learned mixing coefficient)로 두 번째 어텐션을 빼 줍니다. 코드에서는 단일 어텐션과 차분 어텐션의 어텐션 싱크 히트맵(heatmap)을 비교하면서, 싱크가 사라지는 모습을 함께 관찰합니다.
Step 5: KV 캐시 크기(KV cache sizes)
N = 131072일 때 각 변형의 레이어당 캐시 크기를 출력합니다. SWA와 희소 변형은 10–100배 줄고, 차분 어텐션은 2배가 됩니다. 메모리 비용은 의식적으로 지불합시다.
사용해보기
2026년의 실제 운영 패턴은 다음과 같습니다.
from transformers import AutoModelForCausalLM
model = AutoModelForCausalLM.from_pretrained("google/gemma-3-27b-it")
PyTorch 2.5 이상의 FlexAttention은 마스크 함수(mask function)를 그대로 받습니다.
from torch.nn.attention.flex_attention import flex_attention, create_block_mask
def swa_pattern(b, h, q_idx, kv_idx):
return (q_idx - kv_idx < 1024) & (q_idx >= kv_idx)
mask = create_block_mask(swa_pattern, B=batch, H=heads, Q_LEN=n, KV_LEN=n)
out = flex_attention(q, k, v, block_mask=mask)
이 코드는 내부적으로 맞춤형 Triton 커널로 컴파일됩니다. 흔한 패턴에서는 FlashAttention-3 속도의 10% 이내까지 따라붙으면서도, 마스크 자체는 Python 호출 가능 객체(callable)로 둘 수 있습니다.
각 방식을 언제 고를까:
- 순수 풀 어텐션 — 약 16K 컨텍스트까지 모든 레이어에서 사용하거나, 검색 품질이 최우선일 때 사용합니다.
- SWA + 전역 혼합 — 32K를 넘는 장기 컨텍스트에서 학습과 추론이 모두 메모리에 묶일 때 사용합니다. 32K 이상에서는 2026년 기준 기본값에 가깝습니다.
- 희소 블록 어텐션 — 맞춤형 커널과 맞춤형 패턴이 필요한 특수 작업(검색, 오디오 등)에 한해 사용합니다.
- 차분 어텐션 — 어텐션 싱크 오염이 성능을 깎아먹는 작업, 즉 장기 컨텍스트 RAG나 needle-in-haystack 검색 등에 적합합니다.
산출물 만들기
outputs/skill-attention-variant-picker.md를 참고합니다. 이 스킬(skill)은 목표 컨텍스트 길이, 검색 요구 강도, 학습/추론 연산 프로필을 입력으로 받아 새 모델의 어텐션 위상을 골라 줍니다.
연습문제
- 쉬움.
code/main.py를 실행합니다. window=4인 SWA가 각 행에서 마지막 4개 토큰 바깥을 모두 0으로 처리하는지 확인합니다. window=n이 풀 인과적 어텐션과 비트 단위로 동일한 결과를 내는지도 확인합니다.
- 중간. Lesson 07의 캡스톤(capstone) 위에
window=1024의 인과적 SWA를 구현합니다. tinyshakespeare에서 1,000 스텝(step) 학습하고, 풀 어텐션 대비 검증 손실(val loss)이 얼마나 나빠지는지, 그리고 최대 메모리(peak memory)는 얼마나 줄어드는지 측정합니다.
- 어려움. 캡스톤 모델에 Gemma 3 스타일의 5:1 레이어 혼합(5 SWA, 1 global)을 구현합니다. 같은 매개변수 수에서 순수 SWA, 순수 전역 베이스라인과 손실·메모리·생성 품질을 비교합니다.
- 어려움. 헤드(head)마다 학습되는
λ를 가진 차분 어텐션을 구현합니다. 합성 검색 과제(needle 하나, 방해 토큰 2,000개)로 학습한 뒤, 같은 매개변수 수의 단일 어텐션 베이스라인과 검색 정확도를 비교합니다.
핵심 용어
| 용어 | 흔한 설명 | 실제 의미 |
|---|
| 슬라이딩 윈도우 어텐션(Sliding Window Attention; SWA) | "국소 어텐션(local attention)" | 각 쿼리가 직전 W개 토큰에만 어텐드하고, KV 캐시가 O(W)로 줄어든다. |
| 유효 수용 영역(Effective receptive field) | "모델이 얼마나 멀리 보는가" | 창 크기 W인 SWA 스택에서 L개 레이어를 쌓으면, 정보는 최대 L × W 토큰 전까지 거슬러 올라간다. |
| Longformer / BigBird | "국소 + 전역 + 무작위" | 항상 모든 토큰에 어텐드하는 소수의 전역 토큰을 둔 희소 패턴이며, 초기 장기 컨텍스트 접근법이다. |
| 네이티브 희소 어텐션(Native Sparse Attention) | "DeepSeek의 커널 트릭" | 블록 단위 희소성을 학습하고, 0 블록은 커널 수준에서 건너뛰면서 품질은 유지한다. |
| 차분 어텐션(Differential attention) | "맵 두 장, 하나는 빼서" | DIFF Transformer: 두 번째 어텐션 맵을 학습된 λ만큼 곱해 첫 맵에서 빼서 어텐션 싱크를 상쇄한다. |
| 어텐션 싱크(Attention sink) | "가중치가 0번 토큰으로 샌다" | 소프트맥스가 행의 합을 1로 강제하기 때문에, 정보가 부족한 쿼리는 위치 0에 가중치를 쏟아낸다. |
| FlexAttention | "마스크를 Python으로" | PyTorch 2.5 이상에서 임의의 마스크 함수를 FlashAttention 모양의 커널로 컴파일해 주는 API다. |
| 레이어 유형 혼합(Layer type mix) | "5:1 SWA-to-global" | 희소 어텐션 레이어와 풀 어텐션 레이어를 한 스택 안에서 교차로 끼워, 메모리는 줄이고 품질은 유지한다. |
더 읽을거리