BotManifold
Tutorial

Toolkit: attention and transformers

The part of transformers a driving engineer needs for this course: attention as a soft lookup, queries, keys and values, heads, positions, causal decoding and the key-value cache.

Why this is in a driving course

Every learned component in the rest of the course is built from the same block: sensor encoders that fuse cameras, predictors that relate agents, the language model that writes Alpamayo-R1's reasoning and the expert that reads its cache. This page gives you the working knowledge in one sitting. The full treatment, with exercises, is the Transformers module.

Attention is a soft lookup

Think of a dictionary lookup. You have a query; each stored item has a key and a value; you return the value whose key matches. Attention does the same thing softly: it scores the query against every key, turns the scores into weights that sum to one, and returns the weighted average of the values.

Attention(Q,K,V)=softmax ⁣(QK⊤dk)V\text{Attention}(Q, K, V) = \text{softmax}\!\left(\frac{QK^\top}{\sqrt{d_k}}\right) V

In a transformer, every token produces all three from its own vector, through three learned matrices. Each token then asks "which other tokens are relevant to me?" and mixes their information into its own representation. The dk\sqrt{d_k} keeps the scores from growing with the vector size, so the softmax does not saturate.

Diagram of shapes: X of n by 512 is multiplied by three 512 by 64 matrices to give Q, K and V of n by 64; Q and K give an n by n score matrix, then softmax, then the weights multiply V to give an n by 64 output Z.
The shapes in one attention head, using the original paper's sizes. Only the n by n matrices grow with sequence length, which is where the quadratic cost comes from.

The whole computation is four lines:

libs/tutorial/attention.py (attention)
def scaled_dot_product_attention(
    q: torch.Tensor,
    k: torch.Tensor,
    v: torch.Tensor,
    causal: bool = False,
) -> tuple[torch.Tensor, torch.Tensor]:
    """Computes softmax(q k^T / sqrt(d_k)) v.

    Args:
        q: Queries, shape (batch, heads, n, d_k).
        k: Keys, shape (batch, heads, m, d_k).
        v: Values, shape (batch, heads, m, d_v).
        causal: If True, position i may only attend to positions <= i.

    Returns:
        The attended values (batch, heads, n, d_v) and the weights (batch, heads, n, m).
    """
    scores = q @ k.transpose(-2, -1) / math.sqrt(q.shape[-1])
    if causal:
        future = torch.triu(torch.ones_like(scores, dtype=torch.bool), diagonal=1)
        scores = scores.masked_fill(future, float("-inf"))
    weights = scores.softmax(dim=-1)
    return weights @ v, weights

Two properties matter for driving models.

  • Cost grows with the square of the sequence. The score matrix is n×nn \times n. Doubling the number of camera tokens quadruples attention's cost, which is why image tokens get compressed (the Sensor encoders and Deploying a driving VLA modules).
  • Attention has no notion of order. Shuffle the tokens and each output shuffles the same way. Order has to be added as a positional encoding. That property is also what makes a set of agents or map polylines easy to encode: their order should not matter.

Heads and blocks

One attention head learns one kind of relevance. Multi-head attention runs several heads in parallel, each in a smaller space, and concatenates their outputs, so one head can track the car in front while another tracks the lane. A transformer block is attention followed by a small MLP applied to each token, each wrapped with a residual connection and a normalisation. Models stack tens of blocks.

There are three ways to arrange them, and the course uses all three:

ArrangementWho attends to whatIn this course
EncoderEvery token sees every tokenSensor and map encoders, the tiny VLM
Decoder (causal)Each token sees only earlier onesThe language model that writes reasoning
Cross-attentionOne set of tokens reads anotherObject queries reading image features, an action expert reading a cache

Generating one token at a time

A language model writes text by repeating one step: run the transformer on everything so far, read a probability for every possible next token, pick one, append it, repeat. A causal mask makes sure each position can only see earlier positions, so training on whole sentences teaches exactly this one-step-ahead prediction.

Two six-by-six heatmaps of attention weights. In the left one every row has weight spread over the whole sequence; in the right one every weight above the diagonal is zero.
Attention weights for six tokens (random projections, so only the structure matters). Left: each row sums to 1 and may look anywhere. Right: with a causal mask, everything above the diagonal is exactly zero.

The key-value cache

When the model generates token 101, the keys and values of tokens 1 to 100 are the same as they were one step ago. The key-value cache stores them, so each step only computes the new token's query, key and value and reads the rest.

Left: stacks of cached key rows and cached value rows, with one new row highlighted at the bottom and an arrow from a box saying only the new token's query, key and value are computed. Right: a plot of key projections computed against tokens generated, quadratic when recomputing every step and a nearly flat straight line with a cache.
Left: each decoding step appends one key and one value and reads all the cached ones. Right: how many token rows pass through the key projection while generating n tokens. Recomputing the prefix does n(n+1)/2 of them; the cache does n.

Hold on to this idea. In Alpamayo-R1 the cache is more than a speed-up: it is the interface between the reasoning model and the action expert, which attends to the keys and values the reasoning produced (Reading the Alpamayo-R1 code). It is also why decoding speed is limited by memory: every step reads the whole cache and all the weights.

Where this shows up in the course

  • Sensor encoders: BEV queries attending to camera features, tokens per camera.
  • Prediction: agents attending to each other and to the map.
  • VLAs and reasoning: a decoder writing text, the cost of each reasoning token.
  • Deployment: the quadratic cost, the cache and memory bandwidth.

Check yourself

  1. In your own words, what do the query, key and value of a token each represent?
  2. Why does attention's cost grow with the square of the number of tokens?
  3. A set of map polylines is fed to attention in a random order. Does the output depend on the order?
  4. What does a causal mask prevent, and why is it needed to train a model that writes text?
  5. What does the key-value cache store, and what does it save at each generation step?
Answers
  1. The query is what the token is looking for, a key is what each token offers to be found by, and a value is the information a token passes on if it is attended to.
  2. Every token scores every other token, so the score matrix has n × n entries: twice the tokens, four times the work.
  3. No. Attention without positional encodings is unaffected by order: shuffling the inputs shuffles the outputs the same way, so the set is encoded the same.
  4. It stops each position from attending to later positions. Text is generated left to right, so training must teach each position to predict the next token from the past only.
  5. The keys and values of every earlier token. At each step only the new token's query, key and value are computed, instead of recomputing the whole prefix.

Quiz

Answer each question; feedback appears as soon as you choose.

Question 1 of 5

What does attention return for a query?

Question 2 of 5

Why do driving models compress their image tokens?

Question 3 of 5

What does a causal mask do?

Question 4 of 5

What does the key-value cache save during generation?

Question 5 of 5

Which arrangement does an action expert reading a reasoning model's cache use?

0 of 5 answered

Go deeper

The Transformers module builds attention, heads, positions and a tiny GPT step by step, with an exercise and the original paper's details.

On this page