30 min

Action chunking: deciding less often

Predicting a run of future actions from a single observation cuts the number of decision points, and the number of decision points is what compounding error is quadratic in.

Where you are. You can clone a policy from demonstrations, you know why it drifts off the data it was trained on, and you know what the textbook repairs cost. This lesson is the repair that actually shipped.

Sign your name three times

Sign your name on a scrap of paper. One motion, no thinking.

Now sign it again, but stop after every stroke. Lift the pen, look at what you have drawn so far, decide where the next stroke goes, put the pen back down. About a second per stroke.

The second signature is not your signature. It wobbles, the proportions drift, the letters do not join. Nothing about your hand changed between the two attempts. The only thing that changed is how often you stopped to decide.

Now a third one. Sign in a single committed motion while somebody slowly slides the paper sideways. That signature runs off the edge of the page, and the stop-and-look version would have followed the paper.

Those three signatures are the whole lesson, including the part where it goes wrong.

The idea in one paragraph

A policy that emits one action per observation is asked to decide TT times over an episode, and every single decision is a fresh opportunity to be slightly wrong in a way that puts the robot somewhere the demonstrations never visited. Action chunking emits the next kk actions from one observation and executes all of them before looking again. It does not make any individual decision better. It makes there be kk times fewer of them, and the count is exactly what the compounding-error bound is quadratic in. The bill arrives as blindness: between queries the robot cannot react to anything at all, so the chunk length trades compounding error against reactivity. Where the best trade sits depends on how bad your policy is relative to how surprising your world is, and the answer is different for a toy and for a real arm.

The arithmetic you are attacking

Lesson 4 gave you the shape of the problem. Here it is as a number.

Let ε\varepsilon be the policy’s error rate on the states the expert actually visited, and let TT be the number of control steps in an episode. Ross, Gordon and Bagnell proved in 2011 that naive behaviour cloning does not cost you TεT\varepsilon, which is what you would get if the mistakes were independent. It costs you

regret=O ⁣(T2ε)\text{regret} = O\!\left(T^{2}\varepsilon\right)

and an interactive expert who can relabel the states the policy visits brings that back down to O(Tε)O(T\varepsilon), linear.

Now look at where TT enters. It is the number of times the policy is asked. If the policy answers with kk actions instead of one, the number of decision points is

decisions per episode=Tk\text{decisions per episode} = \frac{T}{k}

and a hundred-step chunk turns a five-hundred-decision episode into a five-decision episode. That is the entire mechanism. There is nothing cleverer going on.

one action per query 24 decision points, never blind query step eight actions per query 3 decision points, 7 blind steps each nothing observed in here
Two execution timelines over the same twenty-four control steps: one that queries the policy every step, and one that queries every eighth step and runs the seven steps in between with no observation at all

Wider than the screen; scroll it sideways.

What it costs, stated before you like it

Between the moment a chunk is predicted and the moment it finishes, the robot’s sensors are decoration. An object rolls; the policy does not know. Your grasp slips; the policy does not know. The chunk keeps executing a smooth, confident motion computed from a photograph of a world that has moved on.

An August 2026 paper on operator intervention puts the failure mode better than I can: perception errors and execution drift move the robot outside the demonstration distribution, “while the policy continues to produce smooth action chunks that are inconsistent with the observed state” (arXiv:2608.07065). Smooth and wrong is a much harder failure to catch than jittery and wrong, because it looks deliberate.

Measuring it instead of believing it

solutions/chunking_sweep.py runs the experiment. A point mass with momentum has to trace a circle. The expert is a PD controller - the one you built in Module 1 - and it is genuinely good. We clone it with a small MLP that emits kk actions at a time, execute each chunk open-loop, and measure how far off the circle the robot wanders.

One knob matters: how noisy the policy’s view of the world is. That is the stand-in for everything that makes a real observation imperfect.

image/svg+xml Matplotlib v3.11.1, https://matplotlib.org/ 0 50 100 150 200 250 300 control steps since the episode started 0.0 0.1 0.2 0.3 0.4 distance from the path it should trace the expert it was cloned from one action per query chunks of 5 chunks of 20
Distance from the target path over one 300-step episode, for chunks of 1, 5 and 20 actions, at a moderate observation-noise level

Ignore the first fifty steps. Every version overshoots there while the cloned policy settles into its own groove, and that transient is about the start conditions rather than about chunking. After it, averaged over 25 episodes:

chunk sizesteps 50-149steps 150-299direction
1 action0.0800.112creeping up
5 actions0.0820.057still settling
20 actions0.1940.284leaving

The per-step policy is drifting away from the path as the episode runs on. Nothing about the network changed while that happened; its held-out accuracy was fixed the moment training ended. That upward creep is compounding error, visible. Chunks of five start level with it and go the other way. Chunks of twenty are worse everywhere and getting worse, and that is not compounding - that is a robot that has not looked at anything for a full second at a time.

image/svg+xml Matplotlib v3.11.1, https://matplotlib.org/ 1 2 3 5 8 12 20 35 60 actions predicted per query 1 0 1 1 0 0 mean distance from the path (band is 3 seeds) a clean view of the world a slightly noisy view a badly noisy view
Mean distance from the path against chunk size, at three levels of observation noise, with the spread across three training seeds

Interactive: drag kk and watch the two costs pull against each other. Distance from the path rises with chunk length, because a longer chunk is a longer stretch with the eyes closed. Command jitter falls and then rises, because a short chunk re-reads a noisy observation every single step while a long one lands one large discontinuity at each seam. Turn the observation noise down to zero and the jitter minimum walks back to one action per query; turn it up and it walks out past ten. The widget runs its own toy and prints no success rate; the measured numbers on this page are the ones below. Static version: at the moderate noise level the deviation curve stays nearly flat from one action per query out to about twelve, climbs steeply after that, and by sixty it is roughly twenty times worse than one.

Averaged over whole episodes, three seeds each, at the moderate noise level: one action per query gives 0.093±0.0090.093 \pm 0.009, chunks of three give 0.074±0.0170.074 \pm 0.017, and chunks of sixty give 1.831.83. I will not claim chunking wins here, because those first two spreads overlap and three seeds is not enough to separate them. What the sweep does say unambiguously is that the collapse past about twelve is real, huge, and gets worse the cleaner your observations are.

That last part is the interesting one. With a clean view of the world, one action per query is the best setting on the board and every chunk size is worse. Feedback is free and perfect, so of course you want as much of it as possible.

Why my best chunk is 3 and ACT’s is 100

Take this seriously rather than assuming the toy is wrong or the paper is.

My policy clones a PD controller from four clean numbers. Its per-step error is tiny and roughly symmetric around the right answer, so consecutive errors partly cancel and feedback mops up the rest. ACT clones a human from camera pixels. Its per-step error is large and systematic - wrong in the same direction for a stretch of the motion, because the visual feature it misread is wrong for a stretch of the motion. Errors like that do not cancel, and re-querying does not fix them; it re-commits to them.

Two more differences matter. Human demonstrations are not a function of the current frame: a person hesitates, pauses, and approaches the same object at different speeds on different takes, so a policy that sees only the current image has an error it cannot train away. A chunk absorbs some of that because it commits to one coherent motion instead of averaging over what might come next. And my task is pure stabilisation, where feedback is the entire job; manipulation is mostly ballistic motion punctuated by contact, where it is not.

The obvious question - why not predict a long chunk and re-query every step anyway, taking the best of both - has an answer, a name, and a cost. It is called temporal ensembling, the next lesson takes it apart, and it is switched off in LeRobot by default.

Meanwhile the reactivity tax is the single most active research thread around chunking right now. Real-time flow policies state the problem in their abstract: chunks “run open-loop, so the policy cannot react to sensory input arriving mid-execution, sacrificing reactivity” (arXiv:2607.26055, July 2026). Work on training-time action conditioning (arXiv:2512.05964) is chasing the same gap. Nobody thinks the current answer is finished.

Review

Fewer decisions, not better ones

A policy that emits one action per observation is asked to decide once per step, and every decision is a fresh opportunity to be slightly wrong in a way that puts the robot somewhere the demonstrations never visited. Action chunking predicts the next several actions from one observation and executes all of them before looking again. It does not make any individual decision better. It makes there be fewer of them, and the count is exactly what the compounding-error bound is quadratic in. You can watch the thing it fights: a per-step policy drifts from the path as the episode runs on, from eight hundredths early to eleven hundredths later, while nothing about the network changed. That upward creep is compounding error made visible.

The bill arrives as blindness

Between the moment a chunk is predicted and the moment it finishes, the robot’s sensors are decoration. An object rolls and the policy does not know. Your grasp slips and the policy does not know. The chunk keeps executing a smooth, confident motion computed from a photograph of a world that has moved on, and smooth and wrong is much harder to catch than jittery and wrong, because it looks deliberate. Put a number on it: a widely used configuration ships one hundred actions per query and executes all hundred before re- querying, which at thirty frames per second is three point three seconds of robot time with the eyes closed. For a scripted pick from a fixture that is fine. For a person walking past your workspace it is not.

Why the best chunk length is a property of your world

The knob that decides the trade is how noisy the policy’s view of the world is. With a clean view, one action per query is the best setting on the board and every chunk size is worse, because feedback is free and perfect so you want as much of it as possible. Add noise and the optimum walks outward. On one measured sweep, chunks of three were slightly better than single steps and chunks of sixty were twenty times worse, but the first two spreads overlapped across three seeds, so the honest claim is not that chunking won. What the sweep says unambiguously is that the collapse past about twelve actions is real, large, and gets worse the cleaner your observations are. Measure it on your own task rather than copying a default.

Check yourself

1. Chunking does not lower the policy’s error rate at all. Why does it help?

Because the compounding-error bound is quadratic in the number of decision points, not in the quality of each one. Predicting kk actions per query reduces the count from TT to T/kT/k. Each decision is exactly as good or bad as it was; there are simply fewer places for the policy to be evaluated at a drifted state and make things worse.

2. Your arm runs at 30 Hz with chunk_size = 100 and n_action_steps = 100. A cat walks into the workspace. What is the worst case, in seconds?

About 3.3 seconds of executing a plan made before the cat existed. Which of the two numbers matters is worth being precise about: chunk_size is how much the network predicts, and n_action_steps is how much of it you actually execute before looking again. Predicting 100 and executing 20 gives you a 0.67-second blind window; the config’s default executes the whole thing.

3. Of the three chunk sizes in the drift plot, 20 had the lowest training error and the worst closed-loop behaviour. How can both be true?

They are measurements of different things. A network predicting 20 actions at once gets 20 supervised targets per sample and a smoother learning problem, so its training error averaged over the chunk is genuinely lower: measured at the moderate noise level, 0.053 against 0.114 for one action per query. Closed-loop deviation instead measures what happens when those actions are executed with no feedback for 20 steps, and the disturbances that arrive during those 20 steps never appear in the training loss at all. Note the scope: 20 is the worst of the three sizes plotted, not of the nine in the sweep - 35 and 60 are three and nine times worse again, and their training error is lower still, which only sharpens the point. This gap between offline loss and rollout behaviour is the reason Lesson 12 exists.

4. With a perfectly clean observation, one action per query beat every chunk size. Does that make chunking a bad idea?

No - it locates when chunking pays. Chunking buys you fewer decision points at the price of blindness. If observations are clean and the policy is accurate, decisions are cheap and blindness is the only thing you are buying, so you lose. The trade turns favourable when a single decision is expensive: noisy or partial observations, a policy with systematic errors, demonstrations that were not a function of the current frame. That describes visuomotor imitation and does not describe cloning a PD controller.

5. Why can you not settle the chunk-size question by picking whichever run has the lowest held-out loss?

Because held-out loss is measured on the expert’s states and the robot is evaluated on its own. Held-out loss also gets easier as the chunk gets longer, so it is not even comparable across the values you are choosing between. The measurement that answers the question is a rollout, and Lesson 13 builds the harness for running enough of them to mean anything.

6. Give the software analogy back, including where it breaks.

Many small transactions versus one batched write. Per-step control re-reads state before every write, so consecutive writes can drift apart; a chunk takes one snapshot and applies kk writes that are internally consistent by construction. It breaks in the direction you would expect: the batch cannot be aborted partway through when the snapshot turns out to be stale, and unlike a database there is no rollback, which was true on the first page of this course and is still true here.

Do this

About twenty minutes, all on CPU.

1. Fill in the two holes. code/chunking_sweep.py gives you the task, the expert and the data pipeline. You write train_policy (normalise, fit an MLP, un-normalise) and the chunked rollout loop. Then:

python chunking_sweep.py --quick

Three chunk sizes, one seed, two noise levels. Confirm you reproduce the shape before spending time on the full sweep.

2. Find your own optimum. Run the whole thing and read the best kk off each row:

python chunking_sweep.py

Then change one thing about the world and rerun. Good candidates: raise obs_noise past 0.2, drop n_demos from 15 to 4, or add a mid-episode shove by perturbing the velocity once at step 150. Predict which direction the optimum moves before you run it, then check. Getting a prediction wrong here is worth more than getting one right.

3. Compute your own blind window. For the robot you plan to use, write down the control frequency and the number of actions you intend to execute per query, and turn them into seconds. Then write one sentence naming a specific thing in your workspace that could move within that window. That sentence is your chunk-size argument, and it is the one to bring to a review.

What you can now do

You can explain why compounding error scales with the number of decision points and how predicting a chunk of actions cuts that count, without claiming a theorem that does not exist. You can state the cost in seconds for a given control rate and chunk length, measure where the trade balances on your own task, and say why the answer for a hand-tuned controller in simulation is nothing like the answer for a visuomotor policy trained on human demonstrations.

What you can now do

You can explain why chunking fights compounding error, measure the chunk length that suits your own task, and say exactly what the robot cannot see while a chunk is running.