Bridges: DSA to LLMs
Sliding window to local attention
Bounding the window to tame quadratic cost
The fixed-width window you slide over an array to dodge is the same window that turns full attention into cheap, linear local attention - a band hugging the diagonal.
You already slid this exact window - over an array, in Two pointers and sliding window. There the trick was: don't re-scan the whole array from every start; keep a fixed window of width , move it one step, and patch the summary for the single element that entered and the one that left. Quadratic work collapses to linear. Local attention is that same window applied to the attention grid. Full attention lets every token look at all tokens - an grid, . Local attention restricts each token to a fixed window of neighbours, so only a thin band around the diagonal gets computed: . Same window, same escape from the quadratic wall - just a grid instead of an array.
The idea in one line
You already know this window
The array version and the attention version are the same object seen twice. Read the correspondence term for term:
| Sliding window on arrays | Local attention on tokens (this lesson) |
|---|---|
| window of width over indices | each token attends to nearby tokens |
| window slides one step right | the band shifts down one row of the grid |
| patch summary: one in, one out | each row scores only its in-window keys |
| total work , linear | total attention cells , linear |
The band around the diagonal
Recall the shape of full self-attention: query dots against every key , filling an score grid. Row has entries, and there are rows, so the layer computes scores - that is the “attention wall” from Big-O to the O(n squared) attention wall. Local attention keeps only the entries where the two positions sit within radius of each other, so every off-band cell is simply never computed:
For a fixed window , that is linear in : double the sequence and the cost merely doubles, instead of quadrupling. This is exactly the recipe in the Longformer - a sliding window plus a handful of designated global tokens - and in Mistral 7B, whose causal sliding window lets token attend only to the previous tokens. The masked score becomes:
Softmax then normalises over the surviving in-window keys, so each row still sums to - it is an honest attention distribution, just a local one. Slide the window and watch the op-count below.
Token strip: the focus token is solid; its direct window this layer is teal; the extra reach unlocked by depth is amber. Grid: row i = the token looking, column j = the token looked at. A cell lights only when |i-j| ≤ r - a band on the diagonal. Everything else is skipped work.
j →
Token 5 “jumps” attends to a window of w=3 (fox · jumps · over). Stack L=2 layers and its receptive field widens to tokens 3 - 7 (2rL+1 = 5). This layer lights 28 of 100 cells - 72% of full-attention work skipped.
Two things jump out as you drag. First, the lit-cell count tracks and grows linearly with , while full attention stays pinned at - the savings are real. Second, push up toward () and the band swallows the grid: “skipped” collapses toward . The window only pays off while . So how does a model built from myopic layers ever see the whole sentence?
Depth buys the global view
Here is the second half of the trick, and it is pure stacking. Layer 1 lets token see radius . Layer 2 reads layer 1's outputs - but each of those already summarised its own radius - so through them, token now indirectly senses radius . Every layer you add hops the information one more window outward:
Slide the control in the demo: the grid band never changes - each layer stays exactly as cheap, still - yet the amber receptive field on the token strip fans out with depth. This is the identical mechanism by which stacked convolutions grow a CNN's receptive field. A deep tower of cheap local layers routes context across the whole sequence, one window-hop per layer.
The aha: locality is cheap, depth is global
import numpy as np
def local_attention(Q, K, V, r):
n, d = Q.shape
out = np.zeros((n, d))
scores_computed = 0
for i in range(n): # each query token
lo, hi = max(0, i - r), min(n, i + r + 1) # its window [i-r, i+r]
s = Q[i] @ K[lo:hi].T / np.sqrt(d) # only w = hi-lo scores, not n
a = softmax(s) # normalise over the window
out[i] = a @ V[lo:hi] # blend just those values
scores_computed += hi - lo # grand total ~ n*w, never n*n
return out, scores_computed
# n=1024, r=127 (w=255): ~261k scores vs 1.05M for full attention - 4x fewer.Check yourself
A fixed window of width w makes one local-attention layer cost:
Stack L local layers of radius r. How far can a token now reach?
Why does local attention cost O(n·w) instead of O(n²) - and how can a stack of local layers still mix information across the whole sequence?
Try to state it, then check.
Lock it in
- Local attention is the array sliding window applied to the attention grid: each token attends to a fixed window of neighbours - a band around the diagonal.
- That drops the layer from cells to , linear in sequence length for a fixed window.
- Off-band scores are masked to , so softmax still normalizes each row to 1 - an honest, just-local attention distribution.
- Depth buys the global view: stacking local layers grows the receptive field to tokens, the same way stacked convolutions widen a CNN.
Primary source
Ask your teacher