TAI BUI
Back to curriculum
BuildPython

Attention Variants — Sliding Window, Sparse, Differential

Full attention scores each token against the whole prefix, and memory pays the price. Four variants change which pairs get scored, or how the scores combine, and recover half the cost.

TIME: ~60 minutesPREREQUISITES: Phase 7 · 02 (Self-Attention), Phase 7 · 03 (Multi-Head), Phase 7 · 12 (KV Cache / Flash Attention)

Learning Objectives

  • Describe how sliding window, sparse, and differential attention each change which query-key pairs get scored or how the weights combine.
  • Build causal, sliding-window, and local+strided masks in plain Python and read them as 8-token grids.
  • Explain the attention sink and how subtracting a second attention map targets it.
  • Estimate KV cache size at 128K context for full, SWA, 5:1 mixed, and differential attention.
  • Choose between full, SWA plus global, sparse, and differential attention for a given context length and retrieval need.

The Problem

Full attention costs O(N²) memory and O(N²) compute in sequence length. For a 128K-context Llama 3 70B that is 16 billion attention entries per layer, times 80 layers. Flash Attention (Lesson 12) hides the O(N²) activation memory but does not change the arithmetic cost: each token still attends to the full prefix.

An attention variant is a change to the structure of the attention matrix: which (query, key) pairs get scored, or how the resulting weights are combined. Three classes of variants do this:

  1. Sliding window attention (SWA). Each token attends to a fixed window of neighbors instead of the full prefix. Memory and compute drop to O(N · W) where W is the window. Used in Gemma 2/3, Mistral 7B's first layers, and Phi-3-Long.
  2. Sparse / block attention. Only selected pairs (i, j) get scored, and the rest are forced to zero weight. Longformer, BigBird, and the OpenAI sparse transformer work this way.
  3. Differential attention. Compute two attention maps with separate Q/K projections and subtract one from the other. The subtraction targets the "attention sink" that bleeds weight into the first few tokens. This is Microsoft's DIFF Transformer (2024).

These can be combined. A 2026 frontier model often mixes them: most layers are SWA-1024, a periodic layer is global full attention, and a handful are differential heads that clean up retrieval. Gemma 3's 5:1 SWA-to-global ratio is the current textbook default.

Warm-up0/3
1. In ordinary causal self-attention over N tokens, how does the number of scored query-key pairs grow as N doubles?Warm-up
Left: a 4 by 4 causal score grid with its 10 scored pairs counted. Right: an empty 8 by 8 grid for a sequence twice as long, with the pair count marked as a question mark.
Filled cells are scored pairs. The N = 8 grid is left blank.
2. A decoder caches something per layer so it does not recompute earlier tokens at each generation step. What does it cache?Warm-up
A decoder layer at one generation step: four earlier tokens each have a dashed box marked as a question mark for what is stored, and one new token is being processed.
Illustrative tokens, typed, not captured.
3. A softmax is applied to one row of attention scores. What do the resulting weights in that row add up to?Warm-up
Four typed attention scores go through a softmax box and come out as four weights shown as question marks, with the row sum also a question mark.
Scores typed for illustration.
Check: The Problem0/2
1. What is an attention variant, as this lesson uses the term?Define
An 8 by 8 causal score matrix with scored pairs filled, next to a dashed empty box reading attention variant equals a question mark.
Cells rendered from the lesson's causal_mask(8).
2. Flash Attention already avoids storing the full N by N activation matrix. Why do long-context models still need attention variants?Explain
A dashed 8 by 8 causal matrix outline labeled N by N, with notes that the activation matrix is not stored in memory and question marks for the arithmetic and the pairs scored per token.
Schematic.

The Concept

Sliding Window Attention (SWA)

Sliding window attention limits each query at position i to the positions in [i - W, i] (causal SWA) or [i - W/2, i + W/2] (bidirectional). Tokens outside the window get -inf in the score matrix.

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

For N = 8192 and W = 1024, each of the 8192 queries scores at most 1024 keys instead of up to 8192, an 8× reduction in scored pairs.

KV cache shrinks with SWA. Only the last W tokens of K and V need to be kept per layer. For a Gemma-3-ish config (1024 window, 128K context), KV cache drops 128×.

SWA shrinks the cache along the sequence axis. Grouped-query attention (GQA, Lesson 03) shrinks it along the head axis by letting several query heads share one K/V head, and the two savings can be combined. The figure below shows the head-axis side.

Quality cost. SWA-only transformers struggle with long-range retrieval. The fix is to interleave SWA layers with full-attention layers, and Gemma 3 uses 5:1 SWA:global. Mistral 7B used a causal-SWA stack where information "flows forward" through overlapping windows. Each layer extends the effective receptive field by W, so after L layers the model can attend L × W tokens back.

Sparse / Block Attention

Sparse attention scores a pre-chosen subset of (i, j) pairs and gives the rest zero weight. You pick an N × N sparsity pattern ahead of time. Three canonical shapes:

  • Local + strided (OpenAI sparse transformer). Attend to the last W tokens plus every stride-th token before that. This captures both local and long-range structure at O(N · sqrt(N)) compute.
  • Longformer / BigBird. A local window, plus a small set of global tokens (e.g. [CLS]) that attend to everyone and are attended by everyone, plus random-sparse links. Empirically this gives 2× context at matched quality.
  • Native Sparse Attention (DeepSeek, 2025). Learn which blocks of (Q, K) matter, and skip the zero blocks at kernel level. It stays FlashAttention-compatible.

Sparse attention is a kernel-engineering story. The math is simple (mask the score matrix), and the win comes from never loading the zero entries into SRAM. FlashAttention-3 and the 2026 FlexAttention API make custom sparse patterns first-class in PyTorch.

Differential Attention (DIFF Transformer, 2024)

Differential attention computes two softmax attention maps from separate Q/K projections and subtracts a scaled copy of the second from the first. The goal is to cancel the attention sink.

Regular attention has a sink problem. Softmax forces each row to sum to 1, so tokens that don't want to attend to anything in particular dump weight on the first token (or the first few). That steals capacity that should have gone to real content.

A1 = softmax(Q1 K1^T / √d)
A2 = softmax(Q2 K2^T / √d)
DiffAttn = (A1 - λ · A2) V

Here λ is a learned scalar (typically 0.5–0.8). A1 captures real content weights and A2 captures the sink, so subtracting cancels the sink and reallocates weight to relevant tokens.

Reported results (Microsoft 2024): 5–10% lower perplexity, 1.5–2× longer effective context at the same trained length, sharper needle-in-haystack retrieval.

Variant Comparison

VariantComputeKV cacheQuality vs fullProduction use
Full attentionO(N²)O(N) per layerbaselineevery model's default layer
SWA (window 1024)O(N·W)O(W) per layer-0.1 ppl, good with global layersGemma 2/3, Phi-3-Long
Local + strided sparseO(N·√N)mixedsimilar to SWAOpenAI sparse transformer, Longformer
BigBird (local + global + random)O(N) approxmixedmatches full at 2× contextearly long-context BERT
Native Sparse (DeepSeek-V3.2)O(N · active fraction)O(N)within 0.05 pplDeepSeek-V3.2, 2025
DifferentialO(2·N²)O(2N)-5 to -10% pplDIFF Transformer, early 2026 models
Check: The Concept0/15
1. What is sliding window attention (SWA)?Define
A 10 by 10 sliding-window mask with W = 3 drawn as a banded grid, with a legend for scored and masked pairs.
Computed with swa_mask(10, 3).
2. What is the effective receptive field of an L-layer SWA stack with window W?Define
A stack of layers labeled layer 1 to layer L over a row of tokens, with the last token t and a window of W tokens marked; a dashed arrow asks how far back it can be influenced.
Schematic, no numbers.
3. What do Longformer and BigBird add to a purely local attention window?Define
An 8 by 8 causal local-window grid on the left, a plus sign, and a dashed empty box on the right marked with a question mark for what Longformer and BigBird add.
Window grid computed with swa_mask(8, 3).
4. How does Native Sparse Attention differ from Longformer and BigBird?Define
Left: an 8 by 8 causal grid with a local band and a global first column. Right: a 4 by 4 causal block grid whose blocks are each marked with a question mark.
Left grid is a hand-built sketch of the idea; the block grid is a schematic.
5. In DiffAttn = (A1 - λ · A2) V, what do the two maps do?Define
Two parallel paths, Q1 K1 and Q2 K2, each through a softmax to give A1 and A2 (each labeled role question mark), joined by the subtraction A1 minus lambda times A2 and then multiplied by V.
Layout of the formula in the stem; the roles are left blank.
6. What is the attention sink problem that differential attention targets?Define
The differential attention layout: two softmax maps A1 and A2 from separate Q and K pairs, combined as A1 minus lambda times A2, with a dashed box asking what the attention sink problem is.
Layout of the formula; the problem box is left blank.
7. What is a layer type mix?Define
A column of 12 layers, each labeled windowed with W = 1024 or full attention, with a colour legend.
Illustrative layer list, not a captured config.
8. What KV cache cost does the variant comparison table give for differential attention relative to full attention?Define
A bar for full attention at 42.9 GB and a dashed empty bar marked with a question mark for differential attention, next to the config of 80 layers, 8 KV heads, d_head 128, fp16 and N 131072.
Full-attention size computed with kv_cache_bytes.
9. Why must SWA-only transformers be interleaved with full-attention layers?Explain
Two 6-layer stacks side by side: all layers windowed on the left, five windowed layers and one full-attention layer on the right, with a question mark asking why the left must change.
Schematic stacks, typed layer labels.
10. Why can a token in a causal SWA stack be influenced by tokens more than one window back?Explain
Three rows of 10 positions for layers 1 to 3. In layer 3 the last three positions are filled as the window for position 9; position 3 in layer 1 is outlined and marked token 3, with a question about whether it can influence position 9.
Illustrative sizes, not from the lesson.
11. SWA and grouped-query attention (GQA) both shrink the KV cache. How do they differ?Explain
A grid of stored entries with columns for cached tokens (sequence axis) and rows for heads (head axis), and two lines asking along which axis SWA and GQA shrink it.
Schematic grid, sizes not from the lesson.
12. Where does the speed win of a sparse attention pattern come from?Explain
An 8 by 8 local plus strided mask grid, a vertical path from scores to softmax to weights times V, and a dashed box asking where the speed win comes from.
Mask computed with strided_mask(8, 2, 3).
13. A 12-layer causal SWA model uses W = 1024. Up to how many tokens back can information reach the last position?Apply
A column of 12 layers, each labeled window 1024, with a question asking how many tokens back information can reach the last position.
14. A model uses SWA with W = 1024 in every layer and no global layers. It fails to recall a fact from far back in a long prompt. Which diagnosis fits?Analyze
Left: a 6-layer stack where every layer is windowed with no global layers. Right: a long prompt with a fact early on and a question at the end, the answer missing the fact and the diagnosis marked with a question mark.
Schematic.
15. Your long-context RAG model puts heavy weight on the first token even when the answer sits mid-prompt. Which variant targets this?Analyze
A bar chart of the last position's attention weight by prompt part: the first token 0.55, passage A 0.10, the answer passage 0.06, passage C 0.09 and the question 0.20.
Illustrative values typed by hand.

Build It

The code is in main.py, a standalone Python script that uses only the standard library. It renders full, SWA, and local+strided masks on an 8-token sequence (strided uses window=2, stride=3), runs a single-row differential attention demo, and prints KV cache sizes.

Step 1: full causal mask (baseline)

python
def causal_mask(n):
    return [[0.0 if j <= i else float("-inf") for j in range(n)] for i in range(n)]

This is the baseline: lower triangular, with zero weight above the diagonal.

Step 2: sliding window causal mask

python
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

One parameter: window. For window >= n, you recover full causal attention. For window = 1, each token attends just to itself.

Step 3: local + strided sparse mask

python
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

A dense local window plus each stride-th token back to the start of the sequence. main.py renders this pattern with window=2, stride=3 on 8 tokens. (Prose claim, not computed in main.py: the receptive field grows in log steps as you add layers.)

Step 4: differential attention

python
def diff_attention_row(q1, q2, K1, K2, V, mask_row, lam):
    _, w1 = attention_row(q1, K1, V, mask_row)
    _, w2 = attention_row(q2, K2, V, mask_row)
    diff = [a - lam * b for a, b in zip(w1, w2)]
    d_v = len(V[0])
    out = [0.0] * d_v
    for w, v in zip(diff, V):
        for j in range(d_v):
            out[j] += w * v[j]
    return out, diff

Two attention passes over one query row, subtracted with a mixing coefficient. The DIFF Transformer learns that coefficient; the script fixes lam=0.5 (Exercise 4 is the learned version). The demo runs one query against 8 random tokens, builds Q2 and K2 as small random perturbations of Q1 and K1, and prints the single-attention weights next to the differential ones. Because the data is random, no real sink shows up (the largest weight is at position 2), so this demo shows the arithmetic of the subtraction, not a collapsing sink. Differential weights can go negative.

Step 5: KV cache sizes

The script prints total KV cache across all 80 layers at N = 131072 for a Llama-3-70B-ish config (8 KV heads, d_head = 128, fp16). Full attention is 42.9 GB. Pure SWA drops to 1.3 GB at W = 4096 (32×) and 0.3 GB at W = 1024 (128×). The Gemma-3 mix (5:1, W = 1024) lands at 7.4 GB (5.8×), because the one global layer in six still caches the full 128K. Differential attention doubles full to 85.9 GB. There is no sparse or strided KV line in the script. Know what you're paying in memory before you pick one.

Check: Build It0/12
1. Why do the Build It masks put float('-inf') in disallowed positions?Explain
A row of 8 placeholder scores, the mask row from swa_mask(8, 4) for position 7 showing -inf four times then 0.0 four times, and 8 weights after softmax marked with question marks.
Mask row computed with swa_mask(8, 4).
2. Why can a differential attention row contain negative weights when an ordinary softmax row cannot?Explain
Left: eight positive bars, the single-attention weights from the Build It demo. Right: eight dashed empty bars around a zero line, marked with question marks for the differential row.
Left bars are real output of main.py.
3. What does `swa_mask(8, 4)` allow in row 6, the query at position 6?Apply
A strip of eight key positions 0 to 7 for row 6 of swa_mask(8, 4), each cell marked with a question mark for allowed or masked.
4. What does `strided_mask(8, 2, 3)` allow in row 7?Apply
A strip of eight key positions 0 to 7 for row 7 of strided_mask(8, 2, 3), each cell marked with a question mark.
5. How does `swa_mask(8, 12)` compare with `causal_mask(8)`?Apply
Two empty 8 by 8 grids labeled swa_mask(8, 12) and causal_mask(8), with a question mark between them asking whether they are the same.
Grids deliberately left blank.
6. With lam = 0.5, `w1 = [0.30, 0.50, 0.20]` and `w2 = [0.70, 0.10, 0.20]`, what diff weights does `diff_attention_row` compute?Apply
Two bar charts over positions 0 to 2: w1 with 0.30, 0.50, 0.20 and w2 with 0.70, 0.10, 0.20, and three empty boxes for the diff weights computed with lam 0.5.
7. In the Build It printout, pure SWA at W = 4096 uses 1.3 GB at 128K context. What should W = 1024 use, and why?Apply
A terminal with the Build It config line and the W = 4096 result of 1.3 GB, the W = 1024 result hidden as a question mark, and a two-bar chart with the second bar unknown.
First line is real output of main.py; the second is hidden.
8. At N = 8192 and W = 1024, how much does SWA cut scored pairs, and how much does KV cache shrink for a 1024 window at 128K context?Apply
Two cards: sequence length 8192 with window 1024 and the reduction in scored pairs as a question mark; context 131072 tokens with window 1024 and the KV cache shrink as a question mark.
9. What does the `strided_mask` function in Build It combine?Apply
An empty dashed 8 by 8 grid for strided_mask(8, 2, 3) with a question mark, next to the arguments n = 8, window = 2, stride = 3.
Grid deliberately left blank.
10. A teammate's 5:1 SWA mix at 128K with W = 1024 reports 7.4 GB of KV cache, while pure SWA at W = 1024 reports 0.3 GB. They suspect a bug. What explains the gap?Analyze
Left: two bars, pure SWA at 0.3 GB and the 5:1 mix at 7.4 GB. Right: a group of six layers in the mix, five windowed and one full-attention, each with its cache size marked as a question mark.
Bar values are real output of main.py.
11. In the Build It demo, single-attention weights show no sink at position 0 (the largest weight is at position 2). What follows?Analyze
A terminal line with the eight single-attention weights from main.py and a bar chart of them; position 2 is the tallest at 0.335 and position 0 is 0.018.
Real output of main.py.
12. A 128K-context model has a 42.9 GB KV cache with full attention. You must cut it a lot but keep some long-range retrieval. Which change fits best?Design
A bar for the full-attention KV cache at 42.9 GB and a dashed box stating the goal: cut it a lot and keep some long-range retrieval.
Bar computed with kv_cache_bytes.

Use It

Two patterns from 2026 production code. The first is a model that mixes layer types:

python
from transformers import AutoModelForCausalLM
# Gemma 3 mixes SWA (window=1024) and global layers at 5:1.
model = AutoModelForCausalLM.from_pretrained("google/gemma-3-27b-it")
# print(model.config.sliding_window, model.config.layer_types)

The second is FlexAttention in PyTorch 2.5+, which accepts a mask function:

python
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)

This compiles to a custom Triton kernel that runs within 10% of FlashAttention-3 speed for common patterns, and the mask function is a Python callable.

When to pick each:

  • Pure full attention. All layers up to ~16K context, or when retrieval quality matters most.
  • SWA + global mix. Long context (>32K), where training and inference are memory-bound. This is the 2026 default above 32K.
  • Sparse block attention. Custom kernel, custom pattern. Reserved for specialized workloads (retrieval, audio).
  • Differential attention. Workloads where attention-sink contamination hurts, such as long-context RAG and needle-in-haystack retrieval.
Check: Use It0/6
1. What does PyTorch's FlexAttention let you do, and how fast is it?Define
An 8 by 8 window-4 mask grid goes into a box named FlexAttention, whose output and whose speed against FlashAttention-3 are both marked with question marks.
Pattern grid computed with swa_mask(8, 4).
2. When is an SWA plus global mix the 2026 default?Explain
Two 6-layer stacks: all full-attention on the left, five windowed layers and one full-attention layer on the right, with a question mark asking when the right one is the default.
Schematic stacks.
3. Per the guidance on when to pick each variant, when do you pick pure full attention or sparse block attention?Explain
Left: a stack of six full-attention layers labeled pure full attention. Right: an 8 by 8 local plus strided mask labeled sparse block attention. Each has a pick-it-when question mark.
Mask computed with strided_mask(8, 2, 3).
4. A model targets 8K context, retrieval quality matters most, and memory is not the bottleneck. Which attention layout fits?Design
A design brief with four rows: target context 8K tokens, retrieval quality matters most, memory not the bottleneck, and attention layout marked with a question mark.
5. A 128K RAG model suffers sink contamination, and its KV budget is only slightly above the 7.4 GB of a 5:1 mix. What is the sensible plan?Design
A bar for the 5:1 mix at 7.4 GB with a dashed budget line just above it, the symptom sink contamination, and the plan marked with a question mark.
Bar computed with main.py's formula; the budget line height is illustrative.
6. You want to try a custom attention pattern, such as one tuned for audio, in PyTorch without writing a kernel. What fits?Design
An 8 by 8 local plus strided mask grid as an example custom pattern, an arrow to a dashed box marked with a question mark for the tool, and an arrow to a box reading attention run, no kernel written.
Pattern computed with strided_mask(8, 2, 3); the audio use in the stem is not drawn.

Ship It

The skill in outputs/skill-attention-variant-picker.md picks an attention topology for a new model given target context length, retrieval demands, and training/inference compute profile.

Check: Ship It0/1
1. What does this lesson's Ship It skill, attention-variant-picker, do?Define
A skill file card with the name attention-variant-picker and a dashed box asking what the skill does.
Author's Note

I'd start from the SWA-plus-global mix for anything past 32K context, and I'd want a concrete reason before choosing pure full attention there. The comparison in main.py backs the memory side: at 128K it prints a 128× KV shrink for a pure 1024 window and a far smaller shrink for the 5:1 mix.

My bet is that the global layers are what you actually pay for. The gap between those two comes from the one global layer in six, which still caches the full 128K in that script's formula. I'd read that as the price of keeping long-range retrieval, and I'd tune the ratio before I touched the window size.

Differential attention is a bill you choose to pay. The script puts it at 2× the full-attention cache, and its sink demo is a single 8-token row with a fixed lam=0.5, so it shows the arithmetic of the subtraction and not a quality result. Before adopting it I'd run Exercise 4's retrieval task.

Exercises

  1. Easy. Run main.py. Verify SWA at window=4 zeroes everything outside the last 4 tokens per row. Verify window=n reproduces full causal attention bit-identically.
  2. Medium. Implement causal SWA with window=1024 on top of the Lesson 14 capstone. Train for 1,000 steps on tinyshakespeare. How much does val loss regress vs full attention? How much does peak memory drop?
  3. Hard. Implement a Gemma-3-style 5:1 layer mix (5 SWA, 1 global) in the capstone model. Compare loss, memory, and generation quality against pure-SWA and pure-global baselines at matched parameters.
  4. Hard. Implement differential attention with a learned λ per head. Train on a synthetic retrieval task (one needle, 2,000 distractors). Measure retrieval accuracy vs a single-attention baseline at matched parameters.

Key Terms

TermWhat people sayWhat it actually means
Sliding window attention (SWA)"Local attention"Each query attends to its last W tokens; KV cache shrinks to O(W).
Effective receptive field"How far back the model sees"In an L-layer SWA stack with window W, up to L × W tokens.
Longformer / BigBird"Local + global + random"Sparse patterns with a few always-attending global tokens; early long-context approach.
Native Sparse Attention"DeepSeek's kernel trick"Learn block-level sparsity; skip zero blocks at the kernel level while keeping quality.
Differential attention"Two maps, one subtracts"DIFF Transformer: subtract a learned λ times a second attention map from the first to cancel attention sinks.
Attention sink"Weight bleeds to token 0"Softmax normalization forces rows to sum to 1; uninformative queries dump weight on position 0.
FlexAttention"Mask-as-Python"PyTorch 2.5+ API that compiles arbitrary mask functions into FlashAttention-shape kernels.
Layer type mix"5:1 SWA-to-global"Interleave sparse and full attention layers in a stack to keep quality at lower memory.

Further Reading

Code

Ships With