Week 7 · Sequence Modeling and Transformers

Lecture of 30 March 2026 · guest: Ted Xiao

Course: ETH Robot Learning, Week 7 · Lecture: video · Go deeper: ADM Ch 19, beliefs and ADM Ch 23, a policy that remembers

Big picture

course w01 Week 1 Introduction to Robot Learning w02 Week 2 Robot Control & MDPs w01->w02 formalize w03 Week 3 Imitation Learning w02->w03 learn from demos w04 Week 4 Reinforcement Learning I w02->w04 learn from reward w06 Week 6 Generative Models w03->w06 many modes w05 Week 5 Reinforcement Learning II w04->w05 scale up w08 Week 8 World Models w05->w08 learn a model w07 Week 7 Sequence Modeling and Transformers w06->w07 sequences w09 Week 9 Generalist Robot Policies w07->w09 scale w08->w09 imagine w10 Week 10 Embodied Reasoning and Test-time Scaling w09->w10 reason w11 Week 11 Frontiers & Open Problems w10->w11 what's open w12 Week 12 Guest Lectures w11->w12 guests
Figure 1: The course at a glance. Each box is a week (click to open it); this page’s week is orange. Arrows show how one week builds on another.

Where this week fits

Every policy so far has been reactive: one observation in, one action out ▶ 00:21. Week 3 cloned such policies from demonstrations, and Week 6 made their action distributions richer. This week asks what a policy gains by looking at a sequence: the history behind the current observation, and a whole stretch of future actions instead of one. The tool is the transformer, the architecture behind large language models (LLMs), vision-language models (VLMs) and, from Week 9 on, the vision-language-action models (VLAs) that the rest of the course builds on.

The question this week answers: how do you turn a robot’s observations, instructions and actions into one token sequence that a transformer can learn from, and what goes wrong with actions that does not go wrong with words?

The key ideas at a glance

  • A snapshot is not enough. One camera frame shows neither how fast things move nor what the robot did a moment ago; a robot really acts in a partially observable world and needs history. Predicting one action at a time also tends to produce jerky motion.
  • Sequences factor into conditionals. By the chain rule, any distribution over a sequence is a product of “next element given everything before”. An autoregressive model learns that one conditional.
  • Attention instead of recurrence. A recurrent network squeezes the past into one state vector; attention lets every position look directly at every earlier one, in one step and in parallel. It has no sense of order, so positional encodings add it.
  • Tokens. Text is split into sub-words by byte-pair encoding, images into patches; once everything is a token, one transformer can read both. Vision and language are joined by contrastive alignment (CLIP), by feeding image tokens into a language model (LLaVA, early fusion), by cross-attention (Flamingo, late fusion), or by training on all modalities at once.
  • Scale. Transformers improve predictably with model size, data and compute; the Chinchilla rule says to grow model and data together, about 20 tokens per parameter.
  • Actions as tokens. Bin each action dimension at each step (RT-2, OpenVLA). At high control rates consecutive tokens nearly repeat, and next-token prediction learns little; FAST compresses each action chunk with a discrete cosine transform first.
  • Action chunks. Predict the next \(k\) actions at once (ACT) for smoother motion; a temporal ensemble averages overlapping chunks so the robot keeps reacting.

Vocabulary

Term Plain meaning
Reactive policy Chooses each action from the current observation only
POMDP A decision problem where the robot observes only part of the state
Autoregressive model Generates a sequence one element at a time, each conditioned on all earlier ones
Token One unit of a sequence: a sub-word, an image patch, an action bin
Attention Each position takes a weighted average of other positions’ values, with weights from query-key similarity
Causal mask Stops a position from attending to later positions
Positional encoding Information about order added to tokens or to attention scores
Teacher forcing Training on the true previous tokens, not the model’s own predictions
BPE Byte-pair encoding: repeatedly merge the most frequent adjacent pair into a new token
Scaling law A fitted power law linking loss to model size, data and compute
Early, late fusion Image tokens enter the language model’s input sequence, or reach it through cross-attention layers
Action chunk The next \(k\) actions, predicted together
Temporal ensemble Averaging the overlapping predictions that several chunks make for the same step
DCT Discrete cosine transform: a signal written as a sum of cosines of rising frequency

After this week you can

  • Explain why robots need history and why single-step policies jitter.
  • Compute an attention head by hand, and say what the causal mask and positional encodings are for.
  • Describe teacher forcing, and why a transformer trains on a whole sequence in one pass.
  • Run byte-pair encoding, and explain how images and actions become tokens.
  • State the Chinchilla rule and use \(C \approx 6ND\) to size a model for a compute budget.
  • Explain why per-step action bins fail at high control rates, and how FAST’s compression fixes it.
  • Compare single-step control, action chunks and temporal ensembling.

Intuition

TipAnalogy

Think of a pianist reading music. A beginner looks at one note, plays it, looks at the next: slow, and the phrasing is choppy. An experienced player reads a whole bar ahead and plays it as one gesture, while still glancing at what comes next and at the conductor. They also remember what they have just played: a repeat sign means something only if you know where you have been. Remembering the past is what attention gives a robot; playing a bar at a time is action chunking; still glancing at the page between bars is the temporal ensemble.

One snapshot is not enough

A reactive policy \(\pi(a_t \mid o_t)\) has two problems ▶ 00:21. The first is memory. A single frame does not show how fast a pedestrian is walking, or that the road was icy a few seconds ago. The second is smoothness: a policy that decides every step afresh can switch between slightly different decisions and produce jerky, oscillating motion ▶ 01:34. In the language of Week 2, the robot is not in an MDP but a partially observable one (a POMDP): the observation is not the state, and the state must be inferred from the history ▶ 02:22. The world produces a sequence, but the policy looks at one element of it ▶ 03:21.

This page’s first toy is that problem in its simplest form. A robot receives a stream of 16 tokens. Most are sensor readings with random values; twice, at random moments, a token announces a goal position, and the second announcement replaces the first (an operator corrects themselves). The last token asks: where should I go now? A reactive policy sees only that last token, which says nothing about the goal, so its best guess is 0, and its mean squared error is 1.00, the variance of the goal.

Sequences as products of conditionals

To model a whole sequence, use the chain rule of probability: any joint distribution, however complicated, is the probability of the first element, times that of the second given the first, and so on ▶ 03:53. A model that learns “next element given all earlier ones” can therefore represent any sequence distribution, and generate from it one element at a time. This is an autoregressive model (Equation 1). The hard part is the conditional itself: its input, the whole past, grows with the sequence and can be high-dimensional.

The first answer was the recurrent neural network (RNN): keep a hidden state that summarizes the past, and update it with each new input ▶ 04:56. It handles any length, but has three problems. Information from step 1 reaches step 100 only through 99 updates, so gradients over long ranges explode or vanish (LSTMs helped, but did not cure it) ▶ 05:19. Each step needs the previous one, so training cannot be parallelized over time ▶ 05:41. And however long the history, it must fit in a fixed-size vector ▶ 05:48.

Attention: look anywhere, in one step

A transformer replaces recurrence with attention: any position can draw information from any other position directly, so the path between two tokens has length one, whatever their distance ▶ 06:30. That removes all three RNN problems at once: long-range dependencies are one step away, there is no recurrence to serialize training, and nothing has to squeeze the history into a single vector.

How a position decides where to look ▶ 07:26: each token produces a query (what am I looking for?), a key (what do I offer?) and a value (what I hand over if chosen). The query is compared with every key by a dot product, the scores go through a softmax so they sum to one, and the output is the weighted average of the values ▶ 08:00. It is a soft dictionary lookup: instead of returning the one entry whose key matches, it returns a blend of all entries, weighted by how well each key matches. Because every step is differentiable, the network learns not only the representations but where to look ▶ 08:23.

In the goal toy, one attention head is enough: the last token’s query learns to match the keys of goal tokens, and the value carries the goal’s position. This head is small enough to write out in NumPy with its gradients (see Algorithm & code).

Order has to be added

Attention compares tokens by content only. Shuffle the inputs and every weight is the same: attention is a set operation, so “the robot picked up the spoon” and “the spoon picked up the robot” look identical ▶ 09:09. The fix is to inject order ▶ 09:27. The original transformer adds a fixed vector to each token according to its position, an absolute encoding; a position longer than any seen in training then has no meaningful encoding ▶ 09:52. A relative encoding instead adds a bias to each attention score that depends on the distance between the two tokens, so the same rule applies anywhere in the sequence ▶ 10:12.

The goal toy shows both halves of this. Trained without any positional information, the head finds both goals but cannot tell which came later. Its best option is to average them, and its error over 5 training seeds is 0.50: exactly the error of answering with the mean of two random goals, 0.5. With one learned parameter more, a bias \(-\lambda\) per step of distance on every score, the error falls to 0.0002: the head now prefers the more recent goal.

Does that relative bias let the head work on longer streams? The same trained head runs on streams of 256 tokens without change, which an absolute encoding could not do. It is still nearly perfect (error below 0.0001 in every seed) as long as the later goal is at most 14 steps back. Beyond 15 steps, the furthest training ever showed, it fails, with error above 1.0, worse than not looking at all (Figure 2). The learned weights say why. A goal token out-scores a sensor reading by about 55, the bias is \(\lambda \approx\) 3.6 per step, and their ratio is 15.1 steps, almost exactly the training range: training tuned the head to see goals as far back as it needed to, and no further. Relative encodings make longer sequences possible; they do not by themselves make longer-range dependencies work.

Line chart. The x-axis is how many steps back the later goal is, 1 to 40; the y-axis is mean squared error on a log scale from 0.000001 to 10. A shaded band covers distances 1 to 15. Five orange lines sit between 0.5 and 0.8 at every distance. Five blue lines sit between 0.000003 and 0.0001 from distance 1 to 13, rise steeply through 14 and 15, and from 17 on stay between about 0.9 and 2.2, around or above a dashed horizontal line at 1 that marks the reactive policy.
Figure 2: The goal toy, on streams of 256 tokens, with heads trained on streams of 16. Mean squared error against how far back the later goal is, for the head without positional information (orange) and with a relative-position bias (blue), one thin line per training seed. The shaded band is the range of distances seen in training; the dashed line is a reactive policy’s error.

Training on the whole sequence at once

The original transformer was built for translation, as an encoder-decoder ▶ 11:08. The encoder reads the source sentence with bidirectional attention, every word seeing every other. The decoder writes the translation with causal attention: each position may attend only to itself and earlier positions, enforced by setting the scores of later positions to \(-\infty\) before the softmax ▶ 11:48. Between them, cross-attention takes queries from the decoder and keys and values from the encoder: given what I have written so far, what in the source matters now? ▶ 12:23

Training uses teacher forcing: at every position the model is given the true previous tokens, not its own guesses, and is asked to raise the probability of the correct next token ▶ 12:56. With the causal mask, one forward pass computes the loss at every position of the sequence at once; an RNN needs one step per token ▶ 13:25. (Teacher forcing has a cost that Week 3 already met: at test time the model conditions on its own outputs, which it never saw in training.)

What is a token?

Before any of this applies, the input must be cut into tokens ▶ 13:48. Characters give a tiny vocabulary but very long sequences; whole words give short sequences but a huge vocabulary, and no way to write a word never seen before ▶ 14:03. Sub-words sit in between ▶ 14:51. Byte-pair encoding (BPE) builds them bottom-up: start from single characters, find the most frequent adjacent pair, merge it into a new token, and repeat \(k\) times ▶ 15:18. Frequent pieces such as “the” merge early; a rare word still splits into known pieces. Short sequences matter because attention’s cost grows with the square of the length. The more text BPE is trained on, the more it compresses ▶ 17:08. Example 2 runs it on a sentence.

Scaling to large language models changed a few details ▶ 18:07. The encoder is dropped: a decoder-only model trained on next-token prediction is simpler to scale. BPE runs on bytes instead of characters, so every input, emojis included, can be written ▶ 18:54. RoPE encodes position by rotating queries and keys, so their dot product depends only on the distance between tokens ▶ 19:13. FlashAttention computes attention in tiles in fast on-chip memory, never storing the full \(n \times n\) matrix, so memory grows linearly with length instead of quadratically ▶ 19:46.

What scale buys

The lecture’s history of the GPT models ▶ 20:24: GPT-1 (2018) needed fine-tuning for each task; GPT-2, ten times larger, began to do tasks zero-shot, without being trained on them ▶ 21:02; GPT-3 (175 billion parameters) showed in-context learning, picking up a task from a few examples in the prompt with no gradient update ▶ 21:31. Neither ability was designed in; both appeared with scale. Sutton’s bitter lesson (2019) generalizes this: over 70 years of AI, methods that scale with computation have eventually beaten methods built on hand-engineered knowledge ▶ 21:59. The open question for this course is what emerges when robot data is scaled the same way.

If scale is what matters, how should a fixed compute budget be spent? The Chinchilla study found that model size and training data should grow in equal proportion, and that many large models of the time, GPT-3 among them, were too big for the data they saw ▶ 23:45. The rule of thumb is about 20 training tokens per parameter ▶ 25:09. Because the curves are smooth, small, cheap runs can predict how a large run will turn out before the compute is spent. Some recent models are trained far past that point on purpose (the lecture’s example is Llama 3) ▶ 26:39: a smaller model that saw more data is cheaper to serve to millions of users ▶ 27:09.

Images become tokens too

A robot must see. The vision transformer (ViT) cuts an image into 16 by 16 pixel patches (a 224 by 224 image gives 196), flattens and linearly projects each patch, adds a position embedding, and feeds the patches to a transformer as if they were words ▶ 28:30. Patches play the role for images that sub-words play for text.

Putting image tokens and text tokens in one sequence is not enough: the model must also know that the word “apple” and the patches showing an apple refer to the same thing. The lecture describes four ways to get there (Figure 3):

  • CLIP trains an image encoder and a text encoder side by side, so that matching image-caption pairs end up close in a shared embedding space and mismatched pairs far apart ▶ 30:42.
  • LLaVA (early fusion) runs the image through a frozen CLIP encoder, maps the patch embeddings into the language model’s token space with a small trained MLP, and puts them in front of the text; the language model, fine-tuned on visual instructions, then attends over everything with no change to its architecture ▶ 32:16.
  • Flamingo (late fusion) leaves the language model’s own layers untouched and inserts new cross-attention layers in which text queries look at visual keys and values. Each new layer is gated by \(\tanh(\alpha)\) with \(\alpha\) starting at 0, so at the start of training the model is exactly the original language model, and vision is let in gradually ▶ 33:45.
  • Natively multimodal models are trained from scratch on interleaved text, images, audio and video, with no modality privileged ▶ 35:38.

The trade-off: early fusion lets images and words interact most freely but risks degrading the language model during fine-tuning; late fusion preserves it but restricts the interaction to the cross-attention paths; native training removes the constraints and costs the most. According to the lecture, open-source VLMs mostly use early fusion, while the large proprietary models are converging on native training ▶ 36:58.

fusion e1 EARLY FUSION image → frozen CLIP encoder e2 MLP connector (trained) e1->e2 patches e3 LLM, fine-tuned self-attention over image + text tokens e2->e3 image tokens l1 LATE FUSION image → frozen encoder l2 perceiver resampler l1->l2 l3 frozen LLM + new gated cross-attention l2->l3 keys, values n1 NATIVE text, image, audio, video tokens n3 one transformer trained from scratch n1->n3 one sequence
Figure 3: Three ways to give a language model sight. Early fusion puts projected image tokens into the input sequence; late fusion injects them through gated cross-attention layers; a natively multimodal model is trained on interleaved tokens of every kind from the start.

Actions become tokens

Each step from the transformer to the LLM, the VLM and native multimodal models followed the same play: find a way to tokenize a new modality, feed it to the same architecture, scale. The robotics version adds one more modality, actions, and treats control as multimodal sequence prediction: language tokens, image tokens, then action tokens that drive the motors (Figure 4) ▶ 39:22. Attention over the whole history also answers the memory problem from the start of the lecture.

Actions are continuous, so the first VLAs (RT-2, OpenVLA) bin them, one dimension at a time and one time step at a time ▶ 40:22. Each dimension is normalized to \([-1, 1]\) using its 1st and 99th percentiles over the training data, clipped, and cut into 256 equal bins ▶ 40:46. RT-2 first used the minimum and maximum instead, but a single outlier then stretches the range: in this page’s action toy, one glitched reading among 30,000 makes min-max normalization use only 48 of the 256 bins for the real motions, against all 256 with the quantile bounds. The 256 bins become 256 tokens of the language model’s vocabulary, for example by overwriting its 256 least-used tokens ▶ 42:10, and the model is trained by next-token prediction with its usual loss. Week 1 tokenized a motion this way.

vla instr instruction “pick up the spoon” txt text tokens instr->txt BPE img camera image pat image tokens img->pat patches tf causal transformer one sequence txt->tf pat->tf act action tokens tf->act next token motor motor commands act->motor detokenize bins per-step bins 256 per dimension bins->act training targets fast FAST DCT + BPE per chunk fast->act
Figure 4: Robot control as multimodal sequence prediction: instruction and camera image become tokens, a causal transformer predicts action tokens, and a detokenizer turns them back into motor commands. Per-step bins and FAST are two ways to build the action tokens.

Chunks: plan a short trajectory, not a single step

Attention answers the memory problem; action chunking answers the smoothness problem ▶ 43:21. Instead of one action, the policy predicts the next \(k\), \(\pi(a_{t:t+k} \mid o_t)\), and the robot executes them before asking again. The motion within a chunk is planned at once, so it is coherent. But while a long chunk plays out, the robot is blind: a 100-action chunk at 50 Hz ignores the world for two seconds. The temporal ensemble fixes that: query the policy at every step anyway, keep every chunk’s prediction for the current step, and execute their weighted average ▶ 44:46. ACT, the policy of the ALOHA paper, used both, predicting chunks of 100 joint targets for two 7-joint arms at 50 Hz: 1,400 numbers per forward pass ▶ 45:16, and it learned precise two-arm tasks such as inserting a battery ▶ 45:51.

The third toy measures the trade-off. A robot at 50 Hz moves towards a goal it estimates from a noisy camera at every query; after 1.6 seconds the goal moves. Each planned chunk closes a fixed share of the remaining gap per step. Over 200 runs, with chunks of \(k =\) 25 (half a second):

Scheme Jitter (RMS 2nd difference) Error at rest Time to halfway after the move
single step 0.0075 0.018 0.24 s
chunks, open loop 0.0031 0.051 0.48 s
chunks, temporal ensemble 0.0036 0.017 0.44 s

Single-step control is the most jittery, since every step follows a fresh noisy estimate, but it is accurate at rest, because its small steps average many estimates, and it reacts fastest. Open-loop chunks are 2.4 times smoother, but each chunk trusts one estimate, so they are 2.7 times less accurate, and they notice the move only at the next chunk. The ensemble keeps most of the smoothness and is as accurate as single-step control, because it too averages many estimates. It reacts almost as slowly as open-loop chunks, though, because right after the move most of the predictions it averages were made before it.

One run of the chunk toy, three ways: single steps, open-loop chunks of 25 steps, and the same chunks with a temporal ensemble. The grey line is the goal; it moves partway through.

What to watch for. All three panels show the same run (seed 0), drawn together behind one cursor so the schemes can be compared step by step. In the top panel every step follows a new noisy estimate of the goal, so the orange line is slightly ragged all the way. In the middle panel a faint chunk appears every half second, and the line follows each one smoothly. Where one chunk hands over to the next and a new estimate takes over, it shows a small kink: the typical bend there is 18 times the typical one inside a chunk. When the goal moves, at 1.6 s, the orange line turns at once. The blue line stays level, carrying on with its old chunk until the next query at 2.0 s, and it ends a little off the new goal, since its last chunk trusted one estimate. In the bottom panel a chunk appears at every step, so a fan of overlapping predictions runs ahead of the line. The line is their average: as smooth as the blue one, and closer to the goal. After the move, the newest predictions bend down at once, and the green line starts down with them, but more gently than the orange one, because old predictions still hold it back. A last small kink near 2.1 s marks the step after which no chunk made before the move is left in the average.

Why bins break at high control rates

Chunks of 1,400 numbers were fine for ACT, which outputs them all at once. An autoregressive VLA must generate them one token at a time, and per-step binning breaks down as the control rate rises ▶ 47:06. At high rates consecutive actions barely differ ▶ 47:21. Next-token prediction learns from what each new token adds, and when tokens are this correlated, a model can get a low loss by copying the previous action token, without learning the skill ▶ 47:53.

The action toy measures this on smooth one-second motions in 2 dimensions, each a sum of 3 slow waves (up to 1 Hz). Binned at 5 Hz, “repeat the previous token” predicts 45% of a token’s variation; at 50 Hz it predicts 99.4% (Figure 5, right). Exact repeats remain rare, 11% at 50 Hz, since 256 bins resolve small changes: the token is not literally the same, it is just almost entirely predictable. Meanwhile the sequence gets long: 100 tokens for each second of motion.

The fix is to compress first ▶ 48:26: find units for motion larger than one time step and smaller than a whole trajectory, as sub-words are for text and patches for images. BPE alone suits discrete symbols, not continuous signals, and a learned vector quantizer adds another model to train. FAST borrows from JPEG instead ▶ 49:43. A smooth signal has nearly all its energy at low frequencies, so a discrete cosine transform of each action dimension leaves a few large coefficients and many near zero. Scale and round them, list them lowest frequency first, and run BPE on the resulting integers, which merges the runs of zeros and common patterns into a few dense tokens. In the toy at 50 Hz, a chunk becomes 10.7 FAST tokens on average instead of 100 bins, 9 times fewer, and reconstructs the motion with about the same error (0.049 against 0.044 for the bins, on motions with standard deviation 0.79). At 2 Hz there is little to compress: 2.7 tokens against 4, and a larger error than the bins.

In the paper’s experiments, with a VLA trained on FAST tokens, performance keeps improving at higher control rates where binning collapses ▶ 50:59. An autoregressive version of π0 trained on FAST tokens reached the performance of the flow-matching π0 (the Week 6 kind of action head) five times faster in training ▶ 51:56, followed language commands zero-shot in unseen kitchens after training on the DROID data set ▶ 52:13, and folded laundry at 50 Hz ▶ 52:36. (The lecturer is a co-author of FAST.)

The lecture’s summary ▶ 53:24: treat robotics as sequence modeling, build on transformers because they scale, find the right tokens for each modality, and let data and compute do the rest, which is the bitter lesson applied to robots ▶ 54:54.

Two line charts sharing a log x-axis of control rate: 2, 5, 10, 20 and 50 Hz. Left: tokens per chunk on a log y-axis; the bins line rises in proportion to the rate, from 4 to 100, while the FAST line rises slowly from about 3 to about 11. Right: the share predicted by copying, from about −1.3 at 2 Hz (copying is worse than guessing the average) through about 0.45 at 5 Hz and 0.85 at 10 Hz to 0.99 at 50 Hz.
Figure 5: The action toy at five control rates. Left: tokens per one-second, two-dimensional chunk, for per-step bins and for FAST (DCT, rounding, then BPE). Right: the share of a bin token’s variation that repeating the previous token already predicts.

Key concepts & formulae

Notation follows the lecture: \(o_t\) and \(a_t\) for observation and action, \(x_i\) for a generic sequence element, \(Q, K, V\) for queries, keys and values, \(d\) for their width, \(k\) for a chunk length. The ADM book discusses partial observability in its belief-state chapters, where a policy acts on a belief computed from the history rather than on the raw history itself.

Sequence models

\[ p_\theta(x_1, \dots, x_T) = \prod_{i=1}^{T} p_\theta(x_i \mid x_{1:i-1}) \tag{1}\]

In words. The probability of a whole sequence is the probability of each element given everything before it, multiplied together. Learn the one conditional, and you can both score and generate sequences.

Term by term.

  • \(x_{1:i-1}\): the prefix, all elements before position \(i\); for \(i = 1\) it is empty.
  • \(p_\theta(x_i \mid x_{1:i-1})\): the model’s conditional, the only thing learned. A transformer computes all \(T\) of them in one pass.
  • Why it holds: it is the definition of conditional probability, \(p(a, b) = p(a)\,p(b \mid a)\), applied \(T - 1\) times. Nothing is assumed about the data.

\[ h_t = f(h_{t-1}, x_t) \tag{2}\]

In words. An RNN carries a state vector forward, updating it with each new input; its predictions are read from the state.

Term by term.

  • \(h_t\): a fixed-size summary of \(x_1, \dots, x_t\), the bottleneck.
  • \(f\): the same learned function at every step. Gradients from step \(t\) to step 1 pass through \(t - 1\) applications of \(f\), which is why they vanish or explode.

Attention

\[ \begin{aligned} Q &= E W_Q, \quad K = E W_K, \quad V = E W_V \\ \operatorname{Attention}(Q, K, V) &= \operatorname{softmax}\!\Big(\frac{Q K^\top}{\sqrt{d}}\Big) V \end{aligned} \tag{3}\]

In words. Every token makes a query, a key and a value from its embedding. A token’s output is an average of all the values, weighted by how well its query matches each key.

Term by term.

  • \(E\): the token embeddings, one row per token; \(W_Q, W_K, W_V\): learned matrices.
  • \(Q K^\top\): every query dotted with every key, a score matrix with one row per token.
  • \(\sqrt{d}\): divides the scores so that their spread does not grow with the vector width \(d\), which would push the softmax towards a hard choice and flatten its gradients.
  • Softmax, row by row: the attention weights \(\alpha_{ij} = e^{s_{ij}} / \sum_{j'} e^{s_{ij'}}\), which sum to 1 over \(j\).
  • Multiplying by \(V\): token \(i\)’s output is \(\sum_j \alpha_{ij} v_j\). In practice several such heads run side by side (multi-head attention).
  • Numeric check: Example 1 computes one head of the goal toy by hand.

\[ s_{ij} = \begin{cases} q_i \cdot k_j / \sqrt{d} & j \le i \\ -\infty & j > i \end{cases} \tag{4}\]

In words. Before the softmax, every score for a later position is set to minus infinity, so its weight becomes exactly zero: a token can use only itself and its past.

Term by term.

  • With the mask, the output at position \(i\) depends only on \(x_{1:i}\), which is what Equation 1 requires of the conditional for \(x_{i+1}\).
  • That is also what makes teacher forcing one pass: every position’s prediction is computed at once without seeing its own answer.

\[ \begin{aligned} \text{absolute:}\quad & e_i \;\to\; e_i + \mathrm{PE}(i) \\ \text{relative:}\quad & s_{ij} \;\to\; s_{ij} + b(i - j) \end{aligned} \tag{5}\]

In words. Absolute encodings tag each token with its position before attention; relative encodings bias each attention score by how far apart the two tokens are.

Term by term.

  • \(\mathrm{PE}(i)\): a fixed (sinusoidal) or learned vector for position \(i\). A position beyond the training length has no learned vector, or one the model has never seen.
  • \(b(i - j)\): a bias that depends only on the distance. The goal toy uses the simplest form, \(b(i - j) = -\lambda (i - j)\), one learned number \(\lambda\).
  • Numeric check: the toy’s trained \(\lambda\) is 3.61 (mean of 5 seeds), and a goal’s content score is 15.1 times larger: the distance at which the bias cancels it matches the 15-step range seen in training (Figure 2).

\[ (R_{i\omega}\, q)^\top (R_{j\omega}\, k) = q^\top R_{(j - i)\omega}\, k \tag{6}\]

In words. Rotate the query and the key by angles proportional to their positions; their dot product then depends only on the difference of positions.

Term by term.

  • \(R_{i\omega}\): a rotation by angle \(i\omega\), applied to each pair of coordinates (with a different \(\omega\) per pair).
  • Why it holds: rotations satisfy \(R_a^\top = R_{-a}\) and \(R_{-a} R_b = R_{b-a}\).

Training and tokens

\[ \mathcal{L}(\theta) = -\frac{1}{T} \sum_{t=1}^{T} \log p_\theta(x_t \mid x_{<t}) \tag{7}\]

In words. Average, over the positions of a training sequence, the negative log-probability the model gives to the true next token, with the true earlier tokens as input.

Term by term.

  • \(x_{<t}\): the ground-truth prefix, not the model’s own samples; that is teacher forcing.
  • The sum is computed in one forward pass of a causally masked transformer.
  • It is the cross-entropy of Week 1’s action tokens, applied at every position.

Byte-pair encoding has no equation, only a loop: start with all characters as the vocabulary; \(k\) times, find the most frequent adjacent pair \((t_L, t_R)\) in the corpus, add \(t_L t_R\) to the vocabulary, and replace every occurrence. To tokenize new text, apply the learned merges in the order they were learned (Example 2).

Scaling

\[ \begin{aligned} C &\approx 6 N D \\ N^* &\propto C^{0.5}, \quad D^* \propto C^{0.5}, \quad D^* \approx 20\, N^* \end{aligned} \tag{8}\]

In words. Training compute is about six operations per parameter per token. For a given budget, the best model size and the best amount of data both grow as the square root of the budget, at about 20 tokens per parameter.

Term by term.

  • \(C\): training compute in floating-point operations; \(N\): parameters; \(D\): training tokens.
  • The 6 counts the forward pass (about 2 operations per parameter per token) and the backward pass (about 4).
  • \(N^*\), \(D^*\): the compute-optimal choices, fitted from hundreds of training runs.
  • Numeric check: with \(D = 20 N\), \(C = 120 N^2\), so a budget of \(10^{21}\) operations gives \(N^* \approx 2.9\) billion parameters and \(D^* \approx 58\) billion tokens (Problem 3).

Vision and language

\[ \mathcal{L}_{\text{CLIP}} = -\frac{1}{2N} \sum_{i=1}^{N} \bigg[ \log \frac{e^{s_{ii}/\tau}}{\sum_{j=1}^{N} e^{s_{ij}/\tau}} + \log \frac{e^{s_{ii}/\tau}}{\sum_{j=1}^{N} e^{s_{ji}/\tau}} \bigg] \tag{9}\]

In words. In a batch of \(N\) image-caption pairs, each image should pick out its own caption among all \(N\), and each caption its own image.

Term by term.

  • \(s_{ij}\): cosine similarity between image \(i\)’s embedding and caption \(j\)’s; the diagonal \(s_{ii}\) holds the true pairs.
  • The first term is a softmax classification over captions for each image; the second over images for each caption.
  • \(\tau\): a learned temperature; small \(\tau\) sharpens the softmax (here \(\tau\) is not a trajectory).

\[ x \;\leftarrow\; x + \tanh(\alpha) \cdot \operatorname{CrossAttn}(Q{=}x,\; K, V{=}\text{visual tokens}) \tag{10}\]

In words. Each new layer adds what the text tokens find in the image, scaled by a learned gate that starts closed.

Term by term.

  • \(x\): the text tokens’ hidden states inside the frozen language model.
  • \(\alpha\): one learned scalar per layer, initialized to 0, so \(\tanh(0) = 0\) and the model starts as the original language model.

Actions

\[ \begin{aligned} u &= \operatorname{clip}\!\Big(2\, \frac{a - q_{01}}{q_{99} - q_{01}} - 1,\; -1,\; 1\Big) \\ \text{token} &= \Big\lfloor \tfrac{u + 1}{2} \cdot 256 \Big\rfloor \quad (\text{capped at } 255) \end{aligned} \tag{11}\]

In words. Map each action dimension’s 1st-to-99th-percentile range onto \([-1, 1]\), clip the rest, and cut \([-1, 1]\) into 256 equal bins.

Term by term.

  • \(q_{01}\), \(q_{99}\): percentiles of that dimension over the training data, so a few outliers cannot stretch the range.
  • An action with \(D\) dimensions at \(f\) Hz costs \(D f\) tokens per second.
  • Bin width is \(2/256 \approx 0.008\) in normalized units; decoding returns the bin’s centre.

\[ \begin{aligned} &\pi_\theta(a_{t:t+k} \mid o_t) \\ &a_t = \frac{\sum_i w_i\, A_t[i]}{\sum_i w_i}, \quad w_i = e^{-m i} \end{aligned} \tag{12}\]

In words. The policy predicts the next \(k\) actions from the current observation. With a temporal ensemble, the action executed at step \(t\) is a weighted average of every prediction that some earlier chunk made for step \(t\).

Term by term.

  • \(A_t[i]\): the predictions for step \(t\), from up to \(k\) chunks; \(i = 0\) is the oldest.
  • \(m \ge 0\): how fast weights fall with \(i\). With \(m = 0\) all predictions count equally. Larger \(m\) weights the oldest prediction most. In the ALOHA paper, a smaller \(m\) incorporates new observations faster.
  • Numeric check: in the chunk toy with \(k =\) 25, jitter grows with \(m\): 0.0036, 0.0062, 0.0097, 0.0177 for \(m =\) 0, 0.05, 0.1, 0.2. Here, the oldest prediction changes every step as it expires, so weighting it heavily passes on that change. Weighting the newest most (\(m < 0\), outside the paper’s definition) was smoother still, 0.0014 at \(m = -0.1\), because each new chunk starts from where the robot actually is. Whether that carries over to learned policies, this toy cannot say.

\[ c_j = \nu_j \sum_{n=0}^{N-1} u_n \cos\!\Big(\frac{\pi\, j\, (2n + 1)}{2N}\Big), \qquad q_j = \operatorname{round}(10\, c_j) \tag{13}\]

In words. Write a normalized action dimension \(u_0, \dots, u_{N-1}\) as a weighted sum of cosines of rising frequency \(j\), and round the scaled weights to integers. FAST then flattens the integers, lowest frequency first, and compresses them with BPE.

Term by term.

  • \(c_j\): how much of frequency \(j\) the chunk contains; \(c_0\) is proportional to its mean.
  • \(\nu_j\): normalization, \(\sqrt{1/N}\) for \(j = 0\) and \(\sqrt{2/N}\) otherwise, which makes the transform orthonormal and exactly invertible.
  • 10: the rounding scale, which trades token count against precision.
  • Numeric check: at 50 Hz, a toy chunk has on average 12.5 non-zero rounded coefficients out of 100 (Example 3).

Worked examples

Example 1: one attention head, by hand

Six tokens for the trained head of seed 0: a reading, a goal at np.float64(0.9), a reading, a correction to np.float64(-1.3), a reading and the query. For each token, the table lists its content score (query dotted with key, divided by \(\sqrt{d}\)), the bias \(-\lambda \times\) steps back with \(\lambda =\) 3.59, the total score, the softmax weight and the value.

token steps back content score bias score weight α value v
reading (+0.7) 5 -1.2 -18.0 -19.2 0.0000 +1.32
goal (+0.9) 4 53.1 -14.4 38.8 0.0007 +0.90
reading (-0.4) 3 -1.2 -10.8 -12.0 0.0000 +0.22
goal (-1.3) 2 53.2 -7.2 46.0 0.9993 -1.31
reading (+0.2) 1 -1.2 -3.6 -4.8 0.0000 +0.82
query (+0.0) 0 -1.2 -0.0 -1.2 0.0000 +0.62

Both goals score about 54 more than the readings on content; the bias then subtracts 3.6 per step back, so the later goal, two steps closer, wins by about 7.3, a weight ratio of about 1,416 to 1. Its weight is 0.9993, and the output \(\sum_i \alpha_i v_i =\) -1.308, the corrected goal. The values are the token’s value feature plus a learned multiple of (is goal − 1), which is zero for goal tokens; for the readings the multiple does not matter, since their weights are tiny.

Example 2: byte-pair encoding, merge by merge

Train BPE on the single sentence “the robot put the red cup by the red mat” (40 characters, spaces included) for 8 merges. Each merge takes the most frequent adjacent pair; ties go to the pair that occurs first.

merge new token tokens left
0 (characters) 40
1 th 37
2 the 34
3 the␣ 31
4 the␣r 28
5 t␣ 26
6 the␣re 24
7 the␣red 22
8 the␣red␣ 20

The final tokens are: the␣r · o · b · o · t␣ · p · u · t␣ · the␣red␣ · c · u · p · ␣ · b · y · ␣ · the␣red␣ · m · a · t. The two most repeated phrases, “the” and “the red”, became single tokens after a few merges, while words that occur once stay as characters. A tokenizer trained on a large corpus would merge those too, since they are frequent there; this one, trained on one sentence, could still write any new text, only in more tokens.

Example 3: one chunk as bins and as DCT coefficients

The first test chunk of the action toy at 10 Hz, first dimension: 10 normalized actions \(u_n\), their bin tokens, and the rounded DCT coefficients \(q_j = \operatorname{round}(10\, c_j)\).

n or j action u_n bin token DCT coefficient q_j
0 +0.102 141 +18
1 +0.528 195 +4
2 +0.879 240 -10
3 +1.000 255 -3
4 +1.000 255 -1
5 +0.919 245 -1
6 +0.649 211 -1
7 +0.362 174 +0
8 +0.140 145 +0
9 +0.030 131 +0

The bin tokens are large integers that change smoothly from step to step: each says almost the same as the one before. The DCT column puts the information up front: the first three coefficients carry 97% of the chunk’s energy (the sum of squared coefficients), the rest are small, and the last 3 are 0. Small values and zeros are what BPE then merges away. At 10 Hz the saving is modest; it grows with the rate, because a faster sampling of the same motion adds samples but hardly any frequencies (Figure 5).

Interactive

Where does the head look?

A stream of 32 tokens with the earlier goal (+1.0) near the start and the later goal (-0.8) a chosen number of steps before the query. The bars are the attention weights of the two trained heads (seed 0); the outlined bars mark the goal tokens.

Figure 6: Attention weights over a 32-token stream for the head without positional information (orange) and the head with a relative-position bias (blue). Outlined bars are the two goal tokens; the text below gives each head’s answer.

What to try. Start at 5 steps back: the orange head splits its attention between the two goals and answers 0.02, near their average; the blue head puts almost all its weight on the later goal and answers -0.81. Move the slider: the orange chart never changes shape, since without position the head cannot tell the goals apart by when they came. The blue head stays correct up to 14 steps back. Past the training range, from 17 steps back, its weight has moved to the query token itself, the one token with no recency penalty, and it answers that token’s learned value, 0.62, whatever the goals were.

From motion to tokens

Figure 7: One test chunk of the action toy (first dimension) at the chosen control rate. Top: the motion (grey), its bin tokens decoded to bin centres (orange dots) and the FAST reconstruction (blue). Bottom: the rounded DCT coefficients, lowest frequency first.

What to try. At 2 Hz there are two samples per second: neither scheme has much to work with, and the FAST curve misses the shape between them. Move to 20 and 50 Hz: the orange dots crowd together, each nearly where the previous one was, while the coefficient chart keeps the same few large bars at the left and adds zeros. The zeros are free after BPE; the dots are not.

Chunk size

Figure 8: One run of the chunk toy (seed 0) for the chosen chunk size k, executed open loop (blue) and with a temporal ensemble (green). The dashed line is the goal, which moves at 1.6 s. The legend gives each scheme’s averages over 200 runs.

What to try. At \(k = 1\) both schemes are single-step control, and the lines coincide. Increase \(k\): the open-loop line turns into smooth segments joined at kinks, its error at rest grows (each segment trusts one noisy estimate), and after the goal moves it carries on for up to a whole chunk before turning. The ensemble line gets smoother with \(k\) and stays close to the goal, but it also turns later: at \(k = 50\), a full second of old predictions has to be averaged out. Neither scheme is best on all three numbers at once.

Algorithm & code

One attention head and its gradient

def attend(params, X, relative=True) -> tuple[Array, tuple]:
    """One attention head, read out at the last token: (n,) and a cache.

    score_i = q . k_i / sqrt(d) - lam * (how many steps back token i is).
    The last token is the query and every other token is in its past, so
    the causal mask removes nothing here. relative=False drops the bias,
    and the head then sees its inputs as an unordered set.
    """
    (W_q, W_k), (w_v, lam) = params
    L = X.shape[1]
    q = X[:, -1] @ W_q              # (n, d): what the query asks for
    K = X @ W_k                     # (n, L, d): what each token offers
    back = np.arange(L)[::-1]       # (L,): L-1, ..., 1, 0 steps back
    s = np.einsum("nld,nd->nl", K, q) / np.sqrt(D_K)
    if relative:
        s = s - lam[0] * back       # the same bias at every length
    a = softmax(s, axis=1)          # (n, L): weights, each row sums to 1
    v = X @ w_v                     # (n, L): what each token contributes
    return (a * v).sum(axis=1), (q, K, a, v, back)

def attention_grads(params, X, y, relative=True) -> tuple[float, list]:
    """Mean squared error and its gradient for every parameter, by hand."""
    yhat, (q, K, a, v, back) = attend(params, X, relative)
    dy = 2 * (yhat - y) / len(y)                  # (n,)
    da, dv = dy[:, None] * v, dy[:, None] * a     # yhat = sum_i a_i v_i
    # Softmax backward: ds_i = a_i (da_i - sum_j a_j da_j).
    ds = a * (da - (a * da).sum(axis=1, keepdims=True))
    dw_v = np.einsum("nl,nlf->f", dv, X)
    dlam = np.array([-(ds * back).sum()]) if relative else np.zeros(1)
    dK = ds[:, :, None] * q[:, None, :] / np.sqrt(D_K)     # (n, L, d)
    dq = np.einsum("nl,nld->nd", ds, K) / np.sqrt(D_K)     # (n, d)
    dW_k = np.einsum("nlf,nld->fd", X, dK)
    dW_q = X[:, -1].T @ dq
    loss = float(np.mean((yhat - y) ** 2))
    return loss, [(dW_q, dW_k), (dw_v, dlam)]
import numpy as np
import torch

from rlc.toys import w07_sequence as w7

torch.manual_seed(0)
rng = np.random.default_rng(0)
params = w7.init_attention(rng)
X_np, y_np, _ = w7.cue_batch(64, 10, rng)


def head(X, W_q, W_k, w_v, lam):
    """The page's head in torch: X (n, L, 3) -> answer (n,)."""
    q = X[:, -1] @ W_q                               # (n, d)
    K = X @ W_k                                      # (n, L, d)
    back = torch.arange(X.shape[1] - 1, -1, -1)      # steps back, (L,)
    s = torch.einsum("nld,nd->nl", K, q) / K.shape[-1] ** 0.5
    a = torch.softmax(s - lam * back, dim=1)         # dim, not axis
    return (a * (X @ w_v)).sum(dim=1)


# Leaf tensors with requires_grad: autograd will fill .grad for each.
(W_q, W_k), (w_v, lam) = [(torch.tensor(a, requires_grad=True),
                           torch.tensor(b, requires_grad=True))
                          for a, b in params]
X, y = torch.tensor(X_np), torch.tensor(y_np)
loss = ((head(X, W_q, W_k, w_v, lam[0]) - y) ** 2).mean()
loss.backward()

# The hand-written NumPy gradients agree with autograd.
np_loss, np_grads = w7.attention_grads(params, X_np, y_np)
assert abs(loss.item() - np_loss) < 1e-9
torch_grads = [W_q.grad, W_k.grad, w_v.grad, lam.grad]
for g_t, g_np in zip(torch_grads, [g for pair in np_grads for g in pair]):
    assert np.allclose(g_t.numpy(), g_np, atol=1e-9)

The NumPy version spells out the softmax’s backward pass, \(\partial s_i = \alpha_i (\partial \alpha_i - \sum_j \alpha_j \partial \alpha_j)\); the PyTorch version gets the same numbers from autograd, which the snippet checks. In a real transformer this head is one of many, inside layers that also have a feed-forward network, residual connections and layer normalization.

Next-token prediction on action tokens

def quantile_bounds(A, lo=1.0, hi=99.0) -> Array:
    """Per-dimension 1st and 99th percentiles over all samples: (2, dims)."""
    return np.percentile(A.reshape(-1, A.shape[-1]), [lo, hi], axis=0)

def normalize(A, bounds) -> Array:
    """Map [low, high] to [-1, 1] per dimension, clipping the rest."""
    low, high = bounds
    return np.clip(2 * (A - low) / (high - low) - 1, -1, 1)

def bin_tokens(A, bounds, bins=N_BINS) -> Array:
    """One integer in 0..bins-1 per dimension per step, same shape as A."""
    u = normalize(A, bounds)
    return np.minimum(((u + 1) / 2 * bins).astype(int), bins - 1)

def dct_coefficients(A, bounds, scale) -> Array:
    """Normalize, DCT along time, scale and round: integers (n, f, dims).

    A smooth signal puts nearly all its energy in the first few
    frequencies, so most rounded coefficients are zero.
    """
    c = dct(normalize(A, bounds), axis=1, norm="ortho")
    return np.round(scale * c).astype(int)

def bpe_merge(seq, pair, new) -> list:
    """Replace each non-overlapping occurrence of pair, left to right."""
    out, i = [], 0
    while i < len(seq):
        if i + 1 < len(seq) and seq[i] == pair[0] and seq[i + 1] == pair[1]:
            out.append(new)
            i += 2
        else:
            out.append(seq[i])
            i += 1
    return out

def bpe_train(corpus, k) -> tuple[list, list]:
    """Up to k merges of the most frequent adjacent pair.

    Returns the merges and the merged corpus. Ties go to the pair counted
    first (Counter keeps insertion order). Stops early when no pair occurs
    twice.
    """
    corpus, merges = [list(s) for s in corpus], []
    for _ in range(k):
        counts = Counter(p for s in corpus for p in zip(s, s[1:]))
        if not counts:
            break
        pair, c = counts.most_common(1)[0]
        if c < 2:
            break
        corpus = [bpe_merge(s, pair, pair) for s in corpus]
        merges.append(pair)
    return merges, corpus
import torch.nn as nn
import torch.nn.functional as F

# Bin tokens of the action toy at 20 Hz: one dimension, 20 steps per chunk.
P = w7.signal_params(256, np.random.default_rng(1), dims=1)
A = w7.sample_actions(P, 20)                             # (256, 20, 1)
VOCAB = 64                                       # coarse bins keep it tiny
tokens = torch.tensor(w7.bin_tokens(A, w7.quantile_bounds(A), VOCAB)[..., 0])
L = tokens.shape[1]


class TinyGPT(nn.Module):
    """Token + position embeddings, one causal layer, a linear head."""

    def __init__(self, d=48):
        super().__init__()
        self.tok, self.pos = nn.Embedding(VOCAB, d), nn.Embedding(L, d)
        self.layer = nn.TransformerEncoderLayer(d, nhead=4, dim_feedforward=96,
                                                batch_first=True, dropout=0.0)
        self.out = nn.Linear(d, VOCAB)

    def forward(self, x):                                # x: (B, T) token ids
        T = x.shape[1]
        h = self.tok(x) + self.pos(torch.arange(T))      # (B, T, d)
        # Causal mask: True above the diagonal = may not attend to the future.
        mask = torch.triu(torch.ones(T, T, dtype=torch.bool), diagonal=1)
        return self.out(self.layer(h, src_mask=mask))    # (B, T, VOCAB) logits


model = TinyGPT()
opt = torch.optim.Adam(model.parameters(), lr=3e-3)
inputs, targets = tokens[:, :-1], tokens[:, 1:]  # teacher forcing: shift
losses = []
for step in range(150):
    logits = model(inputs)                       # one pass: all positions
    # cross_entropy wants (N, classes): flatten batch and time together.
    loss = F.cross_entropy(logits.reshape(-1, VOCAB), targets.reshape(-1))
    opt.zero_grad(); loss.backward(); opt.step()
    losses.append(loss.item())

# A uniform guess costs log(64) = 4.16 nats; learning beats it by far.
assert losses[0] > 3.5 and losses[-1] < 0.5 * losses[0], losses[-1]

On the NumPy side are the two tokenizers this page compares: bins, and DCT coefficients that BPE then compresses. The PyTorch sketch trains a one-layer causal transformer on bin tokens by teacher forcing: inputs are the tokens shifted by one, and a single forward pass scores every position. This is the recipe of RT-2 and OpenVLA in miniature, without the images and the language model.

Executing chunks

def plan_chunk(x, goal_seen, k) -> Array:
    """k planned positions closing RATE of the remaining gap per step."""
    return goal_seen + (x - goal_seen) * (1 - RATE) ** np.arange(1, k + 1)

def run_chunks(k, mode, rng, m=0.0, noise=OBS_NOISE, t_move=T_MOVE,
               keep=False):
    """Positions x_0..x_T under one execution scheme.

    The robot tracks each action exactly. mode "chunk": query every k
    steps and execute the whole chunk open loop (k = 1 is ordinary
    single-step control). mode "ensemble": query every step and execute
    the weighted average of every chunk that covers this step, weight
    exp(-m i) with i = 0 the oldest prediction, as in ACT. keep=True also
    returns every query's (time, planned chunk).
    """
    x = np.zeros(T_RUN + 1)
    preds = [[] for _ in range(T_RUN + k)]   # step t: predictions, oldest 1st
    plan, plans = None, []
    for t in range(T_RUN):
        goal = GOAL_A if t < t_move else GOAL_B
        if mode == "ensemble" or t % k == 0:
            # Each query sees the goal through a new noisy observation.
            seen = goal + noise * rng.standard_normal()
            plan = plan_chunk(x[t], seen, k)
            plans.append((t, plan))
            for j in range(k):
                preds[t + j].append(plan[j])
        if mode == "ensemble":
            p = np.array(preds[t])
            w = np.exp(-m * np.arange(len(p)))
            x[t + 1] = (w * p).sum() / w.sum()
        else:
            x[t + 1] = plan[t % k]
    return (x, plans) if keep else x
from collections import defaultdict

K_CHUNK, ACT_DIM, OBS_DIM = 10, 14, 32               # ACT: two 7-joint arms


class ChunkPolicy(nn.Module):
    """Observation features -> the next K_CHUNK actions, (B, K_CHUNK, ACT_DIM).

    ACT itself is a transformer trained as a conditional VAE; a plain MLP
    head shows the shape of the output and of the loss."""

    def __init__(self):
        super().__init__()
        self.net = nn.Sequential(nn.Linear(OBS_DIM, 128), nn.ReLU(),
                                 nn.Linear(128, K_CHUNK * ACT_DIM))

    def forward(self, obs):
        return self.net(obs).view(-1, K_CHUNK, ACT_DIM)


policy = ChunkPolicy()
obs = torch.randn(256, OBS_DIM)
demo_chunks = torch.randn(256, K_CHUNK, ACT_DIM)     # stand-in demonstrations
opt = torch.optim.Adam(policy.parameters(), lr=1e-3)
first = None
for step in range(100):
    loss = F.l1_loss(policy(obs), demo_chunks)   # ACT's reconstruction loss
    opt.zero_grad(); loss.backward(); opt.step()
    first = first or loss.item()
assert loss.item() < first


def temporal_ensemble(policy, observations, m=0.0):
    """Query every step; average all stored predictions for this step."""
    buffer = defaultdict(list)                   # step -> preds, oldest first
    executed = []
    with torch.no_grad():                        # inference: no graph needed
        for t, o in enumerate(observations):
            chunk = policy(o[None])[0]               # (K_CHUNK, ACT_DIM)
            for j in range(K_CHUNK):
                buffer[t + j].append(chunk[j])
            preds = torch.stack(buffer.pop(t))       # (n_preds, ACT_DIM)
            w = torch.exp(-m * torch.arange(len(preds)))
            executed.append((w[:, None] * preds).sum(0) / w.sum())
    return torch.stack(executed)                     # (T, ACT_DIM)


acts = temporal_ensemble(policy, torch.randn(30, OBS_DIM))
assert acts.shape == (30, ACT_DIM)

The toy’s run_chunks does the same bookkeeping with plain lists. In ACT the policy is a transformer over camera features and joint positions, trained as a conditional VAE (the ALOHA paper’s card below has the details); the execution loop is the same.

Papers

Papers discussed in class

Decision Transformer: Reinforcement Learning via Sequence Modeling
Chen et al. · NeurIPS 2021

Treats offline reinforcement learning as sequence modeling. A GPT-style causal transformer reads the recent history of (return-to-go, state, action) tokens and is trained, like a language model, to predict the action. At test time you ask for a high return, and it generates actions meant to achieve it: no value function, no policy gradient.

  • Conditions on returns-to-go, the sum of future rewards, instead of past rewards, so the desired return is set at the start and decremented as rewards arrive (Sec. 3).
  • Matches or exceeds model-free offline RL baselines such as CQL on Atari, OpenAI Gym and Key-to-Door, with almost no change to a language-model architecture (Abstract, Sec. 4).
  • Context matters: with only the current step (K = 1) Atari scores drop sharply, e.g. Pong from 106.1 to 2.5 (Table 5).

On this page: This page’s view of a robot’s history as a token sequence, and its attention toy, which also needs past tokens to act. It also links back to Weeks 4 and 5: return is now an input rather than something to maximize.

Learning Fine-Grained Bimanual Manipulation with Low-Cost Hardware
Zhao, Kumar, Levine & Finn · RSS 2023

ALOHA is a two-arm teleoperation system built for under $20k, with leader arms a person backdrives and follower arms that mirror them. ACT (Action Chunking with Transformers) learns from its demonstrations: a transformer predicts the next k joint targets at once from camera images, and overlapping predictions are averaged at execution.

  • Action chunking: predicting k = 100 actions per query at 50 Hz shortens the task’s effective horizon k-fold; success rose from 1% at k = 1 to 44% at k = 100, averaged over four simulated settings without the ensemble (Sec. IV-A, VI-A).
  • Temporal ensembling: query every step and average all predictions for the current step with weights exp(-m i), i = 0 the oldest (Sec. IV-A, Algorithm 2).
  • Trained as a conditional VAE to cope with noisy, multimodal human demonstrations, which the paper finds essential for precise tasks (Sec. IV-B).
  • 80-90% success on tasks such as slotting a battery or opening a translucent condiment cup, from 10 minutes of demonstrations per task (Abstract).

On this page: The action-chunk toy and the hero clip on this page, which compare single-step control, open-loop chunks and ACT’s temporal ensemble.

Humanoid Locomotion as Next Token Prediction
Radosavovic et al. · NeurIPS 2024

Casts humanoid walking as next-token prediction over sensorimotor trajectories. A causal transformer predicts both the next observation and the next action, so it can also learn from trajectories with missing parts, such as human videos without motor commands, by replacing the missing tokens with a learned mask token.

  • Training data from four sources: neural-network policies, model-based controllers, human motion capture and YouTube videos of people (Sec. 4).
  • A full-sized humanoid (Agility Robotics’ Digit) walks around San Francisco zero-shot, and the model transfers even when trained on only 27 hours of walking data (Abstract).
  • Follows commands never seen in training, such as walking backward (Abstract).
  • Predicting states as well as actions lowers tracking error compared with predicting actions only (Table 1d).

On this page: The lecture’s claim that robotics is a sequence-modeling problem, taken literally: the robot’s own trajectory is the sentence, and every token is predicted.

Key papers from the lecture

Attention Is All You Need
Vaswani et al. · NeurIPS 2017

Introduces the Transformer, an encoder-decoder for sequence-to-sequence tasks built only from attention and feed-forward layers, with no recurrence or convolution. Every token can draw on every other token in one step, and training runs in parallel over the whole sequence.

  • Scaled dot-product attention and multi-head attention as the only way information moves between positions (Abstract).
  • Better translation quality than the best recurrent and convolutional models, while being more parallelizable and much cheaper to train (Abstract).
  • The template that GPT-style language models, ViT, VLMs and VLAs all reuse.

On this page: The attention equation boxed on this page and the attention toy, which implements one head and its gradient in NumPy.

An Image is Worth 16x16 Words: Transformers for Image Recognition at Scale
Dosovitskiy, Beyer et al. · ICLR 2021

Cuts an image into 16 by 16 pixel patches, projects each patch linearly into a token, adds a position embedding, and feeds the sequence to a standard Transformer. Without any convolutional backbone, the result competes with the best convolutional networks once pre-trained on enough data.

  • Image patches as tokens: the same interface as words, so images and text can share one sequence (Abstract).
  • When pre-trained on large data and transferred to benchmarks such as ImageNet and CIFAR-100, matches or beats strong convolutional networks with substantially less compute to train (Abstract).

On this page: This page’s section on tokenizing images, the step from language models to vision-language models.

Training Compute-Optimal Large Language Models
Hoffmann et al. · NeurIPS 2022

Asks how to split a fixed compute budget between model size and training tokens. From over 400 training runs, the answer is to grow both in equal proportion; most large language models of the time were too big for the data they saw.

  • Over 400 language models, from 70 million to over 16 billion parameters, trained on 5 to 500 billion tokens (Abstract).
  • For every doubling of model size, double the training tokens too (Abstract).
  • Chinchilla, 70B parameters on 4 times Gopher’s data at the same compute, outperforms Gopher (280B) and GPT-3 (175B) on a wide range of tasks (Abstract).

On this page: The scaling-law equations on this page and the lecture’s question of what scale buys in robotics.

FAST: Efficient Action Tokenization for Vision-Language-Action Models
Pertsch, Stachowicz et al. · RSS 2025

Per-dimension, per-step binning, the usual way to turn robot actions into tokens, fails for autoregressive VLAs trained on high-frequency, dexterous data. FAST compresses each action chunk first: a discrete cosine transform, rounding, then byte-pair encoding, which gives a few dense tokens per chunk.

  • Shows that simple binning performs poorly when learning dexterous skills from high-frequency robot data (Abstract).
  • A compression-based tokenizer built on the discrete cosine transform, which lets autoregressive VLAs train on tasks where binning fails completely (Abstract).
  • FAST+, a universal tokenizer trained on 1M real robot action trajectories (Abstract).
  • With the pi0 VLA, scales to 10k hours of robot data and matches diffusion VLAs while training up to 5 times faster (Abstract).

On this page: The action-token toy on this page, which runs a small version of the same pipeline next to plain binning. The lecturer is one of the paper’s authors.

Guest spotlight

Ted Xiao (Project Prometheus, previously Google DeepMind) told the last decade of robot learning as three eras, mostly through the work of the Google teams he was part of, and said so ▶ 03:11. The starting thesis, around 2016, was that robot hardware was nearly ready (a teleoperated robot could already tidy a room decades ago) and that the missing piece was intelligence, which deep learning was then supplying in games such as Atari and Go ▶ 02:34.

The first era, existence proofs (about 2016 to 2021), asked whether end-to-end learning on real robots could work at all. Google built “arm farms” of many robots collecting grasps around the clock ▶ 05:38, and QT-Opt applied Q-learning to continuous actions and camera images, trained on over half a million grasps ▶ 06:25. Online RL at that scale proved hard to run, so the team bet more on imitation (BC-Z) ▶ 08:14 and on learning from unstructured play data ▶ 09:03, and then started over with a fleet of robots collecting demonstrations of hundreds of tasks for a year and a half ▶ 09:56.

The second era, foundation models, borrowed the recipe this lecture describes: treat everything as tokens, reuse pre-trained models, and learn offline from large demonstration sets ▶ 12:19. RT-1 was a 35-million-parameter transformer from images and instructions to discretized action tokens, running at 3 Hz across hundreds of tasks ▶ 13:34. Language models were also used off the shelf: as planners whose suggestions were grounded by a value function saying which skills the robot could actually do now ▶ 15:59, and for relabeling robot data with richer instructions ▶ 17:08. RT-2 then made a VLM itself the policy, answering “what should the robot do?” with action tokens, and co-trained on web and robot data so web knowledge transferred to new objects ▶ 18:11. The Open X-Embodiment project pooled data from many labs and robots (“over 30 research labs”, in his words) ▶ 19:35.

The third era, scale, is the present: large investments in turning those prototypes into products ▶ 22:01. His examples include Gemini Robotics, which combined a frontier multimodal model with dexterous data from the ALOHA platform (this week’s discussion paper) ▶ 22:52 and, in its 1.5 version, one policy trained across several robot bodies with a shared action space ▶ 23:45; new evaluation methods, since real-world comparisons need thousands of trials ▶ 25:52; and many competing bets on data: wearable gloves, egocentric video, deployment data ▶ 26:04. He closed with open questions: the role of world models, the value of robot-free data, reasoning models, and how to unite manipulation, learned mostly by imitation, with locomotion, learned by RL in simulation ▶ 27:29. In the Q&A he expects high-quality teleoperation data to stay essential until training on cheaper, broader data is better understood ▶ 29:31, and he sees video-pretrained models as promising but doubts they replace text-based reasoning ▶ 32:41.

How it connects to this week. The talk is the lecture’s thesis told as history: each era moved closer to “one transformer, tokens in and tokens out, trained on as much data as possible”. RT-1 and RT-2 are the per-step action tokens of this page, and the move from them to FAST and to diffusion and flow heads (Week 6) is the move this week’s last section explains. Week 9 takes up the generalist policies of the third era.

Solved problems

1. Attention by hand. Three tokens have values \(v = (1, -1, 3)\), and the query’s scores against their keys (already divided by \(\sqrt{d}\)) are \((2, 0, 1)\). Compute the attention weights and the output. Then apply a causal mask that hides the third token, and recompute.

\(e^2 = 7.389\), \(e^0 = 1\), \(e^1 = 2.718\), summing to 11.107. Weights: 0.665, 0.090, 0.245. Output: \(0.665 - 0.090 + 3 \times 0.245 = 1.31\). With the third score set to \(-\infty\), its weight is 0 and the others renormalize: \(7.389 / 8.389 = 0.881\) and \(0.119\), so the output is \(0.881 - 0.119 = 0.76\). Masking changes the answer because the hidden token carried the largest value.

2. Why order needs encoding. Show that without positional information, permuting the input tokens permutes the attention outputs the same way, so a readout at the last position cannot depend on the order of the earlier tokens. What does this imply for the goal toy?

Each output \(\sum_j \alpha_{ij} v_j\) depends on the set of (key, value) pairs and on token \(i\)’s own query; the weights \(\alpha_{ij}\) depend only on the contents \(q_i \cdot k_j\). Reordering the other tokens reorders the terms of the sum without changing it. In the goal toy, the query is the last token, so its output is the same whichever goal came first: the head can only treat the two goals symmetrically. The symmetric answer with the least squared error is their average, and with independent standard normal goals its error is \(\mathbb{E}[(g_2 - g_1)^2] / 4 = 2/4 = 0.5\), which is what training reached (0.50).

3. Sizing a model. You have a training budget of \(10^{21}\) floating-point operations. Using \(C \approx 6ND\) and \(D \approx 20 N\), how many parameters and tokens are compute-optimal? If you instead train a model 4 times smaller with the same budget, how many tokens per parameter does it see?

Substituting, \(C = 6 N \cdot 20 N = 120 N^2\), so \(N = \sqrt{10^{21}/120} \approx 2.9 \times 10^9\) parameters and \(D = 20 N \approx 5.8 \times 10^{10}\) tokens. A model of \(N/4 \approx 7.2 \times 10^8\) parameters at the same compute sees \(D = C/(6 \cdot N/4) = 4 \times 5.8 \times 10^{10} \approx 2.3 \times 10^{11}\) tokens: 320 tokens per parameter, 16 times the Chinchilla ratio. It is worse to train for this budget, but cheaper to run, which is the Llama 3 trade-off.

4. The token bill of per-step bins. A VLA controls a 7-joint arm plus a gripper (8 action dimensions) at 50 Hz with one-second chunks, binned per dimension and per step. How many action tokens does one second of motion take? If the model decodes 200 tokens per second, can it keep up? What compression factor would let it plan each one-second chunk in a fifth of a second?

\(8 \times 50 = 400\) tokens per second of motion. At 200 tokens per second the model needs 2 seconds to plan 1 second of motion: it falls behind. To plan in 0.2 s it may use 40 tokens, a compression factor of 10. In this page’s toy, FAST compressed 50 Hz chunks by about 9 times; real motions with more dimensions and sharper features would compress differently, so this is only an indication.

5. Ensemble weights. With chunks of \(k = 4\) and a temporal ensemble queried every step, how many predictions are averaged at step 10? With \(m = 0.5\), what share of the weight does the oldest get, and what share the newest? After the goal moves, how many steps pass before every averaged prediction was made after the move?

Chunks from the queries at steps 7, 8, 9 and 10 cover step 10: four predictions. Weights \(e^{-0.5 i}\) for \(i = 0, \dots, 3\) are 1, 0.607, 0.368, 0.223, summing to 2.198; the oldest gets \(1/2.198 = 45\%\), the newest \(0.223/2.198 = 10\%\). After a move, the prediction made at the move’s step enters at once, but the oldest made before the move leaves only \(k - 1 = 3\) steps later, so from the fourth step on all four predictions know about the move. Weighting the oldest most, as larger \(m\) does, slows the reaction further.

Recap

  1. Robots need sequences, in and out. A single observation hides velocities and past events, and single-step decisions jitter. Why it matters: it is the reason the rest of the course uses history-conditioned, chunk-predicting policies.
  2. Attention gives direct access to the whole history. Queries, keys and values implement a soft lookup; a causal mask makes it autoregressive; positional encodings supply order, and, as the goal toy shows, reach only as far as training required. Why it matters: this one mechanism underlies LLMs, VLMs and VLAs.
  3. Everything becomes tokens. Sub-words by BPE, images by patches, vision and language aligned by contrastive training, early fusion, late fusion or native training. Why it matters: a robot policy built on a VLM inherits its tokenization and its knowledge.
  4. Scale is predictable. Loss falls smoothly with model size, data and compute; Chinchilla’s rule splits a budget at about 20 tokens per parameter. Why it matters: it frames the course’s open question of what robot data at scale will buy.
  5. Per-step action bins break at high control rates. Neighbouring tokens become almost entirely predictable from each other, and sequences become long; FAST compresses chunks with a DCT and BPE. Why it matters: high rates are what dexterous manipulation needs.
  6. Chunks trade reactivity for smoothness. Open-loop chunks are smooth but blind and trust one estimate each; a temporal ensemble is smooth and as accurate as single-step control, but still reacts late. Why it matters: nearly every modern robot policy predicts action chunks.

Cheat sheet & connections

Name Formula Where
Chain rule ★ \(p(x_{1:T}) = \prod_i p(x_i \mid x_{1:i-1})\) Equation 1
RNN \(h_t = f(h_{t-1}, x_t)\) Equation 2
Attention ★ \(\operatorname{softmax}(QK^\top/\sqrt{d})\,V\) Equation 3
Causal mask \(s_{ij} = -\infty\) for \(j > i\) Equation 4
Positional encodings \(e_i + \mathrm{PE}(i)\); \(s_{ij} + b(i - j)\) Equation 5
RoPE \((R_i q)^\top (R_j k) = q^\top R_{j-i} k\) Equation 6
Teacher forcing \(-\frac{1}{T}\sum_t \log p(x_t \mid x_{<t})\) Equation 7
Chinchilla ★ \(C \approx 6ND\), \(D^* \approx 20 N^*\) Equation 8
CLIP symmetric softmax over \(s_{ij}/\tau\) Equation 9
Flamingo gate \(x + \tanh(\alpha)\,\text{CrossAttn}\) Equation 10
Action bins quantile-normalize, 256 bins Equation 11
Chunk, ensemble \(\pi(a_{t:t+k} \mid o_t)\), \(w_i = e^{-mi}\) Equation 12
DCT (FAST) \(c_j = \nu_j \sum_n u_n \cos(\cdot)\) Equation 13

Common pitfalls.

  • Forgetting the causal mask in training: the model sees each answer and learns to copy it, then fails at generation.
  • Assuming relative positional encodings give long-range memory for free: they let the model run on longer inputs, not use what lies beyond the distances it learned.
  • Min-max normalizing actions: one outlier wastes most of the bins.
  • Judging an action tokenizer by next-token loss alone: at high rates a low loss can mean “copy the last token”.
  • Executing long chunks open loop in a changing world: the robot is blind until the next query.
  • Comparing chunk sizes on smoothness only: accuracy and reaction time move the other way.

Connections. Week 1 introduced actions as tokens; this week explains the transformer that reads them and why the simplest tokenization fails at speed. Week 3’s behavior cloning is what ACT does with chunks, and chunking shortens the effective horizon over which Week 3’s errors compound. Weeks 4 and 5 maximize return, while the Decision Transformer conditions on it. Week 6’s diffusion and flow policies are the main alternative to autoregressive action tokens, and π0’s flow head is the comparison point for FAST. Week 9 builds generalist VLAs from all of these pieces. In the ADM Companion: beliefs and the belief update, the principled way to act on a history, and controllers that remember.

Resources