liveBook · Manning

Attention

As explained in LLMs in Production

An attention mechanism dynamically weights relationships between elements of an input sequence according to their relevance to a current computation.

In a long sequence, not every token matters equally to the current computation. Attention lets a model score which parts of the input are relevant and weight them accordingly. It was introduced as a way to retain strong sequence modeling while avoiding the slow training associated with LSTMs; it later became central to transformers, which dispense with recurrence. section 2.2.7 section 2.2.8 section 2.3

Think of a dictionary lookup with an extra ingredient: a query. Input vectors are transformed into queries, keys, and values. Comparing a query with keys produces relevance scores; those scores determine how strongly the corresponding values contribute. In the illustrated dot-product implementation, matrix multiplication produces the scores and softmax turns them into weights. section 2.2.8

Listing 2.8 Multihead attention implementation
import numpy as np
from scipy.special import softmax

x = np.array([[1.0, 0.0, 1.0, 0.0],
            [0.0, 2.0, 0.0, 2.0],
            [1.0, 1.0, 1.0, 1.0]])     #1

w_query = np.array([1,0,1],
             [1,0,0],
             [0,0,1],
             [0,1,1]])          #2
w_key = np.array([[0,0,1],
             [1,1,0],
             [0,1,0],
             [1,1,0]])         
w_value = np.array([[0,2,0],
             [0,3,0],
             [1,0,3],
             [1,1,0]])         

Q = np.matmul(x,w_query)     #3
K = np.matmul(x,w_key)       #3
V = np.matmul(x,w_value)     #3

k_d = 1                                        #4
attention_scores = (Q @ K.transpose())/k_d     #4

attention_scores[0] = softmax(attention_scores[0])     #5
attention_scores[1] = softmax(attention_scores[1])     #5
attention_scores[2] = softmax(attention_scores[2])     #5

attention1 = attention_scores[0].reshape(-1,1)     #6
attention1 = attention_scores[0][0]*V[0]           #6
attention2 = attention_scores[0][1]*V[1]           #6
attention3 = attention_scores[0][2]*V[2]           #6

attention_input1 = attention1 + attention2 + attention3    #7

attention_head1 = np.random.random((3,64))      #8

z0h1 = np.random.random((3,64))     #9
z1h2 = np.random.random((3,64))     #9
z2h3 = np.random.random((3,64))     #9
z3h4 = np.random.random((3,64))     #9
z4h5 = np.random.random((3,64))     #9
z5h6 = np.random.random((3,64))     #9
z6h7 = np.random.random((3,64))     #9
z7h8 = np.random.random((3,64))     #9

Output_attention = np.hstack((z0h1,z1h2,z2h3,z3h4,z4h5,z5h6,z6h7,z7h8))    #10

def dot_product_attention(query, key, value, mask, scale=True):    #11
    assert query.shape[-1] == key.shape[-1] == value.shape[-1], "q,k,v have different dimensions!"
    if scale:
        depth = query.shape[-1]
    else:
        depth = 1
    dots = np.matmul(query, np.swapaxes(key, -1, -2)) / np.sqrt(depth)
    if mask is not None:
        dots = np.where(mask, dots, np.full_like(dots, -1e9))
    logsumexp = scipy.special.logsumexp(dots, axis=-1, keepdims=True)
    dots = np.exp(dots - logsumexp)
    attention = np.matmul(dots, value)
    return attention
def masked_dot_product_self_attention(q,k,v,scale=True):     #12
    mask_size = q.shape[-2]
    mask = np.tril(np.ones((1, mask_size, mask_size), dtype=np.bool_), k=0)
    return DotProductAttention(q,k,v,mask,scale=scale)

The NumPy listing traces input vectors through query, key, and value projections to attention scores and softmax weights. section 2.2.8

The permitted relationships depend on the task:

  • Bidirectional self-attention compares positions within the same sequence in both directions. section 2.2.8
  • Causal attention restricts focus to preceding positions; masking can also hide parts of a sequence for a model to predict. section 2.2.8

Attention is not a free shortcut. Scoring every pair of tokens scales quadratically with sequence length. A larger token limit can therefore mean slower inference, more timeouts, or hardware memory limits. When an application needs a response within a deadline, a longer context window is useful only if its latency and reliability remain acceptable. section 3.2.6

How LLMs in Production covers it

How deep it goes
Works through query–key scoring and value weighting with a NumPy implementation, while deferring encoder–decoder sequence-length details.
Examples
Uses a NumPy multihead-attention listing and a GPT-3-to-GPT-4 upgrade that increased inference time and timeouts.
What it assumes
Introduces queries through a key–value lookup analogy while using embeddings and matrix operations in the worked implementation.
What you can do after
Build it from scratch with the illustrated NumPy operations and evaluate context-length increases against latency and memory constraints.
How it connects
Connects LSTMs, self-attention, causal masking, positional encoding, encoders, and transformers.

Also explained in

Compare how 28 books explain attention →

Related concepts

This explainer was generated by Manning's knowledge graph from the text of LLMs in Production. The section links point to the parts of the book it draws on.