Attention Masking › Custom Attention Patterns

Michael BrenndoerferUpdated May 27, 20258 min read
Language AI HandbookAll chapters
Part 13 of 67Self-Attention
Chapter 93Attention Masking › Custom Attention Patterns
Reading section 9 of 13 · Custom Attention Patterns · 8 minSection 9 of 13 · 8 min

Book contents

Section 9 of 13 · 8 min read

Attention Masking › Custom Attention PatternsOne section · Sections in this chapter9 / 13 ↓
Reading tools Definitions & inline links

Choose your expertise level to adjust how many terms are explained. Beginners see more tooltips, experts see fewer to maintain reading flow. Hover over underlined terms for instant definitions.

Article links

Make inline references clickable

Beyond standard padding and causal masks, researchers have explored various attention patterns for efficiency and modeling goals. Masking is a design tool as well as a correctness constraint: by choosing which positions can attend to which others, you shape what information the model can use and at what computational cost.

Think of the full attention matrix as a complete graph: every token is connected to every other token. Masking is the process of removing edges from this graph. The causal mask removes all "forward" edges. The padding mask removes all "noise" edges. Custom patterns remove different subsets of edges to achieve specific computational or modeling goals.

Local Attention

Restrict attention to a fixed window around each position. Instead of attending globally, each token only looks at its ww nearest neighbors on each side. This reduces complexity from O(n2)O(n^2) to O(n⋅w)O(n \cdot w), where:

  • nn: the sequence length (total number of tokens)
  • ww: the window size (number of positions each token can attend to on each side)

The trade-off is expressiveness for efficiency. Local attention cannot capture dependencies between positions that are more than ww steps apart. For tasks where most relevant context is nearby, this is not a significant limitation. For tasks requiring long-range reasoning, it can be a serious bottleneck.

In[30]:
Code
def create_local_attention_mask(seq_len, window_size):
    """
    Create a local attention mask with a fixed window.
    Each position attends to window_size positions before and after.

    Args:
        seq_len: sequence length
        window_size: number of positions to attend on each side

    Returns:
        mask: (seq_len, seq_len) attention mask
    """
    mask = np.full((seq_len, seq_len), -1e9)

    for i in range(seq_len):
        start = max(0, i - window_size)
        end = min(seq_len, i + window_size + 1)
        mask[i, start:end] = 0.0

    return mask


seq_len = 10
window_size = 2
local_mask = create_local_attention_mask(seq_len, window_size)
Out[31]:
Visualization
Diagonal band pattern showing local attention where each position attends to nearby positions only.
Local attention mask with window size 2. Each position attends only to the 2 positions before and 2 after itself, creating a band-diagonal pattern. Positions outside the window receive zero attention weight, concentrating each token's representation on its immediate neighbors.

Local attention is the core mechanism in models like Longformer and BigBird designed to handle very long sequences. The window captures local context efficiently, and special "global" tokens (like [CLS]) can still attend to all positions for tasks that require long-range information. The combination of local and global tokens gives these models both efficiency and expressiveness.

Strided Attention

Attend to every kk-th position, spreading attention across the sequence with fixed intervals. Here, kk is the stride parameter that controls how far apart attended positions are. Where local attention captures fine-grained local context, strided attention captures coarse global patterns by sampling the sequence at regular intervals.

In[32]:
Code
def create_strided_attention_mask(seq_len, stride):
    """
    Create a strided attention mask.
    Each position attends to every stride-th position.

    Args:
        seq_len: sequence length
        stride: attend to every stride-th position

    Returns:
        mask: (seq_len, seq_len) attention mask
    """
    mask = np.full((seq_len, seq_len), -1e9)

    for i in range(seq_len):
        # Attend to positions at stride intervals, aligned to position
        for j in range(0, seq_len, stride):
            mask[i, j] = 0.0
        # Also attend to local neighborhood for current position
        mask[i, i] = 0.0

    return mask


stride = 3
strided_mask = create_strided_attention_mask(seq_len, stride)

Strided attention is most useful when combined with local attention. By itself, strided attention misses the fine-grained context between strided positions. But paired with local attention, it provides the long-range "summary" connections that local windows cannot reach. This is the insight behind Sparse Transformer.

Combined Patterns

Real efficient attention mechanisms often combine multiple patterns. The Sparse Transformer uses local and strided attention together to cover both nearby and distant positions at a fraction of the cost of full attention.

In[33]:
Code
def create_sparse_attention_mask(seq_len, local_window, stride):
    """
    Create a sparse attention mask combining local and strided patterns.

    Args:
        seq_len: sequence length
        local_window: size of local attention window
        stride: stride for global attention

    Returns:
        mask: (seq_len, seq_len) attention mask
    """
    # Start with all blocked
    mask = np.full((seq_len, seq_len), -1e9)

    # Add local attention
    for i in range(seq_len):
        start = max(0, i - local_window)
        end = min(seq_len, i + local_window + 1)
        mask[i, start:end] = 0.0

    # Add strided attention
    for i in range(seq_len):
        for j in range(0, seq_len, stride):
            mask[i, j] = 0.0

    return mask


sparse_mask = create_sparse_attention_mask(seq_len, local_window=1, stride=4)
Out[34]:
Visualization
Fully filled attention matrix showing all positions can attend to all positions.
Full attention: every position attends to every position, giving maximum expressiveness at O(n^2) cost.
Sparse pattern with diagonal band and periodic vertical stripes.
Sparse attention combining local window and stride: the diagonal band captures local context while periodic columns provide global connectivity, used in Sparse Transformer.
Lower triangular band pattern combining causal and local constraints.
Causal plus local attention: the lower triangular structure enforces left-to-right ordering while the local window limits each position to nearby past context, useful for efficient autoregressive models.

Each pattern represents a different trade-off. Full attention has maximum expressiveness but O(n2)O(n^2) cost. Sparse patterns reduce complexity at the cost of some long-range interactions. The choice depends on sequence length, available compute, and task requirements.

In[35]:
Code
# Compare sparsity levels across different mask patterns
def compute_sparsity(mask):
    """Compute the fraction of allowed (non-masked) positions."""
    return (mask == 0).sum() / mask.size


seq_lengths = [64, 128, 256, 512, 1024]
patterns = {
    "Full": [],
    "Causal": [],
    "Local (w=32)": [],
    "Sparse (w=16, s=64)": [],
}

for n in seq_lengths:
    # Full attention: all n^2 pairs
    patterns["Full"].append(1.0)

    # Causal: lower triangle = n(n+1)/2 pairs
    causal = np.tril(np.ones((n, n)))
    patterns["Causal"].append(causal.sum() / causal.size)

    # Local with window 32
    local = create_local_attention_mask(n, window_size=32)
    patterns["Local (w=32)"].append((local == 0).sum() / local.size)

    # Sparse: local window 16 + stride 64
    sparse = create_sparse_attention_mask(n, local_window=16, stride=64)
    patterns["Sparse (w=16, s=64)"].append((sparse == 0).sum() / sparse.size)
Out[36]:
Visualization
Line plot showing attention density decreasing for local and sparse patterns as sequence length increases.
Density of attention patterns across increasing sequence lengths. Full and causal attention maintain roughly constant density as sequences grow, while local and sparse patterns become dramatically more efficient. At 1024 tokens, sparse attention requires less than 10 percent of the connections that full attention uses, enabling much longer context windows on the same hardware.

The efficiency gains are dramatic. Full attention always uses 100% of possible pairs. Causal attention uses approximately 50% (the lower triangle). But local and sparse patterns become increasingly efficient as sequences grow longer: at 1024 tokens, local attention with window 32 uses only a few percent of pairs, and sparse patterns even less. This is why efficient attention variants are essential for processing long documents, where full attention would be computationally prohibitive.