1. What we achieved—and what remains unsolved
We built a real-game reinforcement-learning system for the first five levels of Crash Bandicoot: Warped, a shared model that can act in all five levels, demonstration-based training, evaluation and films, cloud infrastructure, and two distinct distributed-training experiment tracks.
The strongest completed real-game systems result is 3.32× higher end-to-end training throughput with four GPUs, compared with one, at matching global experience and optimizer-update budgets. Two GPUs achieved approximately 1.86×. Both results repeated closely across two seeds.
This does not establish improved playing ability. All six models in that scaling comparison scored 0/20 evaluation completions. Separate demonstration-trained models complete Orient Express from the demonstrated starting state, but small changes in starting timing still break that success. The longer overnight campaign had not produced an additional level completion at this snapshot.
The project therefore teaches three separate questions:
| Question | Appropriate evidence |
|---|---|
| Can the system train faster? | Throughput at a controlled workload, repeated measurements |
| Can the system fit a larger workload? | Memory peaks and tested capacity boundaries |
| Does the trained policy perform better? | Completion, meaningful progress and robustness evaluations |
A successful answer to one does not imply a successful answer to the others.
2. The journey through the project
We first extended PS-Env beyond one platforming level. The first five levels involve platforming, swimming, tiger riding, a chase, and jet-ski control. That required level-specific actions, memory interpretation, starting states and completion checks—not merely five names in a menu.
We added named presets, local/cloud execution, checkpoints, model loading, recorded gameplay and a hosted console. We then explored one shared visual model with separate action outputs for each level, alongside independent level-specific training.
Poor returns and repeated failures led us to inspect reward components, build scripted searches for candidate demonstrations, replay those candidates, and train policies to imitate their actions. We compared demonstration initialization, continued demonstration replay, learning from scratch, and practice from route sections.
A separate transformer project explored distributed systems in a more conventional neural-network workload: single-device training, DDP, FSDP, DeepSpeed ZeRO, NCCL communication, Megatron tensor parallelism, memory limits, tuning, profiling and recovery. We then returned those systems lessons to real PS-Env training using synchronous gradient sharing.
Finally, we expanded the real-game comparison to one, two and four GPUs with fixed update counts, and launched longer reward experiments and additional demonstration searches overnight.
The UI and presentation are different deliverables. At this snapshot the live experiments page contained the new six-run scaling comparison; the Machenta slide deck still contained the older 1.64× pilot result. A live dashboard updating does not automatically update a static presentation.
3. Vocabulary: the units that matter
| Term | Meaning in this project |
|---|---|
| Frame | One emulated game frame |
| Observation | The processed images the policy receives |
| Decision | One selected action, applied across eight game frames in these runs |
| Action | A level-specific button combination or macro |
| Transition | Observation, action, reward, next observation and terminal information |
| Episode | One attempt, ending in completion, game over, or an imposed step limit |
| Reward | A numerical feedback signal generated from game events and configured shaping |
| Return | The sum of rewards over an episode |
| Policy | The rule, represented by a neural network, that selects actions |
| Replay buffer | Stored past experience sampled again for learning |
| Batch | A set of replay samples used to calculate an update |
| Loss | The training objective whose gradient changes the network |
| Gradient | The derivative of the loss with respect to model parameters |
| Optimizer update | One application of an update rule such as Adam |
| Checkpoint | Saved model/training state; its exact contents determine what can be resumed |
| Rank | One process participating in distributed training |
| World size | The number of participating distributed processes |
| Local rank | A process's device index within its machine |
| Seed | A setting controlling pseudorandom choices; it does not make different parallel executions identical |
The policy receives four stacked 84×84 grayscale observations. Stacking helps it infer movement from changes between images. The stack contains successive sampled observations, not necessarily four adjacent game frames.
A decision is not necessarily a fresh greedy network choice: exploration can select or repeat an action. The frame-skip setting remains eight frames per decision, with level-specific button-tap behavior where required.
Example: 200,000 decisions correspond to about 1.6 million emulated action frames, excluding reset and evaluation overhead. With four workers, those are 200,000 decisions in total, not 200,000 per worker.
Episode return and training loss are also different. A decreasing DQN loss means its current sampled predictions fit its targets more closely. It does not establish that the targets encode desirable behavior, or that the policy can finish a level.
4. How a single PS-Env learner works

Figure 1. Two connected loops: playing creates data; learning uses that data to change the policy.
The model is a convolutional DQN. It predicts a value for each available action: how much future discounted reward taking that action is expected to produce. During greedy evaluation it chooses the highest-valued action. During training an epsilon-greedy exploration mechanism sometimes chooses exploratory actions instead.
Double DQN separates choosing a bootstrap action from evaluating that action using the target network. A delayed target network helps stabilize the moving learning target. In simplified one-step notation:
chosen_next_action = argmax Q_online(next_observation, action)
target = reward + gamma * Q_target(next_observation, chosen_next_action)
For a true terminal transition, there is no future-value term. Our implementation also supports three-step returns: it combines several consecutive rewards and bootstraps after that sequence. Episode and level boundaries must not be crossed accidentally. Artificial time limits need careful treatment because truncating an attempt is different from the game reaching a terminal state.
The implementation uses Adam, Huber loss, gradient clipping and target-network synchronization. These ingredients stabilize optimization; none guarantees successful exploration or an appropriate reward objective.
The actual tensor shapes in our code
B is the number of observations processed together, A is the number of discrete actions, and dimensions below are batch × channels × height × width. One grayscale image has one channel; stacking four images produces four channels. The convolutional network has no language input and is not a transformer.
Operation in agent/model.py |
Output shape | Meaning |
|---|---|---|
| Input; convert uint8 to float and divide by 255 | [B, 4, 84, 84] |
Four sampled grayscale observations |
| Conv: 32 filters, 8×8 kernel, stride 4; ReLU | [B, 32, 20, 20] |
Learn 32 local feature maps |
| Conv: 64 filters, 4×4 kernel, stride 2; ReLU | [B, 64, 9, 9] |
Combine larger visual patterns |
| Conv: 64 filters, 3×3 kernel, stride 1; ReLU | [B, 64, 7, 7] |
Last spatial representation |
| Flatten | [B, 3136] |
64 × 7 × 7 numbers per observation |
| Ordinary single-level head | [B, 512] → [B, A] |
One hidden layer followed by action values |
| Dueling single-level value branch | [B, 512] → [B, 1] |
Predict state value |
| Dueling single-level advantage branch | [B, 512] → [B, A] |
Predict relative action advantages |
| Dueling combination | [B, A] |
Q = V + advantage - mean(advantage) |
The convolutions have no padding. For example, the first spatial dimension is floor((84 - 8) / 4) + 1 = 20. ReLU keeps its input shape. “512 units” describes a hidden vector, not 512 actions or 512 game states. The two single-level dueling branches have separate 3136→512 weight matrices.
The five-level SharedDQN reuses the same convolutional shapes, then has one shared 3136→512 layer. Each level has a small 512→A advantage head and a 512→1 value head. The caller supplies the level identifier to route the head; it is not concatenated as a language input. A forward call for one level produces that level's [B, A] output, not a single concatenation of all five action spaces.
| Network / controller | Output actions | Trainable policy parameters |
|---|---|---|
| Original ordinary NatureDQN, legacy controller | 13 | 1,690,797 |
Single-level dueling, Toad Village / Under Pressure / Bone Yard, level_v2 |
27 each | 3,304,636 each |
Single-level dueling, Orient Express, level_v2 |
7 | 3,294,376 |
Single-level dueling, Makin' Waves, level_v2 |
4 | 3,292,837 |
Five-level shared dueling, level_v2 |
27 / 27 / 7 / 27 / 4 | 1,733,889 total |
These counts were computed from instantiated code, include biases, and count one policy, excluding its delayed target copy, optimizer state, gradients, replay and activations. Historical checkpoints can use different flags: inspect their saved config rather than assuming every Toad Village model uses 27 actions. The shared model is smaller than a single-level dueling model because it shares the large hidden layer rather than maintaining two separate 3136→512 branches.
In DQN the target network has the same shape as the online policy, but is updated by copying weights rather than by its own optimizer. Double DQN concerns target construction; dueling concerns the output branches. They are independent choices. With 1/2/4 GPUs in our PS-Env benchmark, every GPU receives the complete policy and target network; adding GPUs does not add layers or split this model into pieces.
from agent.model import NatureDQN
import torch
net = NatureDQN(num_actions=7, dueling=True)
x = torch.zeros(2, 4, 84, 84, dtype=torch.uint8)
print(net(x).shape) # torch.Size([2, 7])
print(sum(p.numel() for p in net.parameters())) # 3294376
From the original project report · What PS-Env Is
Historical record · September 2026 reconstruction and single-GPU experiments. These results are separate from the later shared-model scaling experiments in this guide.
PS-Env is an OpenEnv-style reinforcement-learning environment for PlayStation 1 games. At the bottom sits the game itself, a Crash Bandicoot: Warped disc image and a PS1 BIOS, neither of which ever enters the repository. The SwanStation libretro core runs them headlessly; it is downloaded as a binary and loaded at runtime with dlopen. A small C++ host exposes exactly the four things we need from the core: press buttons, grab the frame, read memory, and save or restore state. pybind11 lifts that host into Python, where a FastAPI server offers reset() and step(action) over a WebSocket JSON protocol. The agent, a Nature DQN, sits at the far end of that socket and only ever sees stacked frames and rewards.
The Loop
reset() restores a save state, so every episode begins on the same frame of Toad Village with the same four lives. step(a) turns one of 13 discrete actions into a button bitmask, holds it for eight emulator frames (frame skip 8; the recovered design used 2, and the training story below explains the change), and returns the last of those frames as an 84 by 84 grayscale image together with a reward and a done flag. The actor keeps the last four frames, so the network's input is 4 by 84 by 84.
What the Agent Sees
the emulator's frame
t − 3
t − 2
t − 1
tps_env.frames.to_gray84: grayscale, then nearest-neighbour to 84 by 84). Stacked, they are the whole input to the network: 28,224 bytes per step, no counters, no positions.What the Agent Can Do
Thirteen discrete actions, each a PS1 button bitmask whose bit index is the libretro joypad id (Cross is bit 0, Square bit 1, the d-pad bits 4 to 7). Cross jumps, Square spins, and Up is forward, because Toad Village runs away from the camera.
- 0NOOP0x0000
- 1UP0x0010
- 2DOWN0x0020
- 3LEFT0x0040
- 4RIGHT0x0080
- 5JUMP0x0001
- 6SPIN0x0002
- 7JUMP_LEFT0x0041
- 8JUMP_RIGHT0x0081
- 9JUMP_UP0x0011
- 10SPIN_LEFT0x0042
- 11SPIN_RIGHT0x0082
- 12SPIN_UP0x0012
Rewards Live in RAM
Nothing the reward needs is on screen in a usable form. Fruit collected, boxes broken and lives left live somewhere in the console's 2 MB of RAM, at addresses nobody documented in 1998. The team found six of them with a memory scanner (search for a known value, change the game state, search again, until one address survives), and the reward function reads them as single bytes every step with no image processing at all. The 2026 reproduction added Crash's position (CRASH_Z, a 32-bit word) for dense progress shaping and for the level-exit check, since the recovered "78 = level done" value was never observed on the real disc.
| Signal | Reward | Read from |
|---|---|---|
| holding Up (forward) | +0.1 | the action mask |
| pressing Down (backward) | −0.02 | the action mask |
| every step | −0.0002 | always |
| Wumpa fruit collected | +0.5 each | MAIN_WUMPA 0x000AD22D, BONUS_WUMPA 0x000AD239 |
| box broken | +1.0 each | MAIN_BOXES 0x000AD255, BONUS_BOXES 0x000AD241 |
| life lost | −25 | LIVES 0x000AD231 decreased |
| game over (episode ends) | −5,000 | a life lost while LIVES was already 0 |
| level complete (episode ends) | +5,000 | WARP_STATE 0x0006F5E9 goes from 32 to 52 |
| forward progress | +0.01 per unit | new maximum of CRASH_Z travelled since reset (164.5K units for the whole level) |
Two more charges exist only in the trainer, and the training story explains why they were needed: a stall cut (an episode ends after a run of steps without new progress) that costs a lost life, and a cost of 1 on every step that makes no new progress.
The Training Architecture
The talk's architecture diagram shows the distributed design: actors on an epsilon ladder from 1.0 down to 0.01, each with its own emulator, push transitions to Redis; a GPU learner samples batches of 32, trains the policy net (three convolutions, a 512-unit layer, 13 outputs), hard-copies it to the target net and publishes weights; TensorBoard reads its logs. The reproduction below ran the single-process trainer (agent.train) against one emulator server instead, because the emulator, not the GPU, was the bottleneck: about 50 steps per second at frame skip 8 on a g5.xlarge, with the A10G mostly idle.
5. Sharing across levels versus sharing across GPUs
Figure 2. This entire five-head network is copied onto every GPU in a distributed run.
These are independent design choices.
Across levels: our shared model has one visual feature extractor and a shared hidden representation, followed by five level-specific action heads. An action suitable for swimming need not have the same meaning as an action suitable for tiger riding. Balanced replay gives all five levels a contribution to learning.
Across GPUs: synchronous data parallelism places a complete copy of that entire shared model on every GPU. Different workers gather different experience, but synchronize the model updates.
Therefore, four GPUs do not mean four heads, and five levels do not require five GPUs. Every distributed replica contains all five heads. Each worker assigns its next episode to a level with relatively little collected experience.
There is also parallel experimentation: four GPUs can run four unrelated trials with different rewards or methods. Those models never average gradients. That is how the overnight gameplay ablations are scheduled. It is different from the one-/two-/four-GPU scaling trials, which run one distributed experiment at a time.
6. Exactly how our distributed update works
Figure 3. The batch stays at 80 globally. The arrows entering NCCL carry gradients, not raw gameplay.
Our PS-Env implementation uses torchrun to launch one process per GPU. PyTorch initializes the process group; CUDA workers communicate through NCCL. CPU validation uses Gloo.
Each rank owns:
- A complete online network, target network and optimizer.
- A local native emulator and independent exploration randomness.
- Replay buffers partitioned by level.
Initial model parameters are broadcast from rank zero. During training, each rank gathers transitions locally. At the configured update boundary, all ranks check whether their replay partitions are ready. They then sample equal contributions from the five levels.
For the overnight comparison, the global batch is always 80:
| GPUs/ranks | Samples per rank | Samples per level per rank | Total across five levels and all ranks |
|---|---|---|---|
| 1 | 80 | 16 | 80 |
| 2 | 40 | 8 | 80 |
| 4 | 20 | 4 | 80 |
Each rank calculates its local loss and gradients. The implementation flattens the gradients into one tensor, sums it across ranks with all_reduce, divides by the number of ranks, copies the averaged gradients back, clips them, and performs Adam's update.
If the four gradients for one parameter were 2, 4, 6 and 8, the averaged gradient would be 5. Adam then uses that gradient together with its optimizer state; the parameter change is not necessarily just minus five times the learning rate.
Equal local sample counts matter. Averaging worker means is equivalent to averaging all samples only when workers have appropriate equal weights. Unequal batches require weighted aggregation. We validate exact batch divisibility instead of silently dropping samples.
What travels between GPUs? Primarily the gradients, plus synchronization/control information and occasional statistics. Raw replay experience stays local. This is not a central experience database, and replay is not shared simply because the model weights are synchronized.
This implementation uses explicit gradient averaging. It follows the synchronous data-parallel principle but does not wrap the PS-Env model in PyTorch DistributedDataParallel. The production DDP wrapper offers mechanisms such as gradient bucketing and communication/computation overlap that our straightforward flattened implementation does not claim to reproduce. See PyTorch DDP and distributed communication.
7. How we checked correctness
We checked three different things, each answering a different question:
- Reference-gradient agreement: on identical global data, averaged gradients matched a reference learner within approximately 1.27×10⁻⁷ maximum absolute difference.
- Replica agreement: all distributed replicas ended with identical policy hashes. This detects divergence between workers; it does not prove the objective is correct.
- Real-game plumbing: short runs exercised actual emulators, updates, checkpoints, evaluations and films. Short smoke-test episodes are not evidence of mastery.
The latest two-GPU smoke collected 800 global decisions and performed exactly 11 scheduled updates, with identical replica weights. A separate five-level demonstration/reward smoke completed 2,000 decisions, 40 updates and 20 evaluation conditions.
Testing the inner algorithm alone was insufficient: a later command-line validation bug prevented all six search jobs from starting even though the underlying search function had passed a direct smoke. We fixed the CLI and added tests with and without a prefix. Test the actual entry point that automation launches.
8. Reading the scaling result correctly
The improved comparison ran 200,000 global decisions per trial, a global batch of 80 and a global replay capacity of 40,000. Updates started at decision 50,000, inclusive, and occurred every 20 global decisions:
number of updates = (200,000 − 50,000) / 20 + 1 = 7,501
If any replay partition was unready when this fixed schedule began, the trial failed rather than silently changing the update count. All six trials passed.
| GPUs | Seed 42 decisions/s | Seed 43 decisions/s | Approximate speedup | Scaling efficiency |
|---|---|---|---|---|
| 1 | 60.95 | 60.87 | 1× | 100% |
| 2 | 113.47 | 112.81 | 1.86× | 93% |
| 4 | 202.20 | 202.64 | 3.32× | 83% |
Speedup is throughput divided by the one-GPU throughput. Scaling efficiency is speedup divided by GPU count. It describes how much of ideal linear scaling we achieved; it is not GPU utilization.
The workload took approximately 55, 29 and 16½ minutes respectively, before subsequent evaluation. These are end-to-end training measurements including experience collection and training-loop overhead, not isolated neural-network kernel timings. The one-/two-/four-GPU runs used the same four-GPU host with different numbers of active training devices.
The result is encouraging because it repeats across seeds at matching update counts. It is limited because two seeds are few, trajectories still differ, and fixed-start evaluations do not establish broad generalization. All six scored 0/20 completions.
The earlier pilot measured 51.54 versus 84.33 decisions/s, or 1.64×. However, it performed 20,801 versus 18,764 updates. Different replay readiness changed the amount of learning work. That pilot motivated the explicit fixed-update schedule. Do not present the change from 1.64× to 1.86× as an isolated optimization gain: the benchmark protocol also changed.
9. The separate transformer experiments
Figure 4. Both arrangements use four GPUs, but they answer different experimental questions.
The transformer track trained a small synthetic next-token prediction task. A token is a sequence element; tokens/s is not interchangeable with game decisions/s. The task was deliberately simple, not a claim of language-model quality.
| Technique | What is distributed | Main reason to use it |
|---|---|---|
| DDP | Different batches; complete model copies | Increase training throughput when communication is affordable |
| FSDP | Model parameters, gradients and optimizer state are sharded | Fit larger model state across devices |
| DeepSpeed ZeRO-1 | Optimizer state | Reduce optimizer-state duplication |
| ZeRO-2 | Optimizer state and gradients | Save more training memory |
| ZeRO-3 | Optimizer state, gradients and parameters | Further reduce replicated model state |
| Tensor parallelism | Computation within model layers | Split operations too large or costly for one device |
| Pipeline parallelism | Different layer groups | Distribute model depth; not used in our Megatron experiment |
| Activation checkpointing | Some activations are discarded and recomputed | Trade additional compute for lower activation memory |
FSDP and ZeRO are different implementations with related sharding goals. NCCL is the communication library they may use, not another sharding strategy. See PyTorch FSDP, DeepSpeed's ZeRO tutorial and PyTorch's FSDP/activation-memory discussion.
We used genuine Megatron Core tensor parallelism, with pipeline size one. Its model and kernels differed from TinyGPT, so its throughput is not an apples-to-apples framework speed comparison.
The small two-node pilot: more GPUs were slower
Fourteen GPU trials completed on two separate A10G instances using private TCP networking. For the same small FP32 TinyGPT workload:
| Trainer | GPUs | Median tokens/s |
|---|---|---|
| Single device | 1 | 106,132 |
| DDP | 2 | 29,660 |
| FSDP2 | 2 | 17,203 |
| DeepSpeed ZeRO-2 | 2 | 19,677 |
These measurements are consistent with communication overhead outweighing useful parallel work for a small model. They do not prove a universal framework ranking. FSDP had the lowest measured allocation peak in this pilot. DeepSpeed's default communication buckets were large relative to the tiny model, so its memory peak was not a tuned comparison.
A separate 16 MiB NCCL reduction test verified its numerical output and measured about 26.03 ms mean latency, with 0.644 GB/s effective algorithm bandwidth. That is not physical-link bandwidth. A small Megatron run completed at 21,847 tokens/s, but used a different architecture and only one repetition.
The expanded campaign
The retained final phase summaries contain 139 completed trials, 18 OOM outcomes and 3 other failures:
| Phase | Complete | OOM | Other failures |
|---|---|---|---|
| Smoke | 7 | 0 | 0 |
| Scaling | 87 | 3 | 0 |
| Tuning | 15 | 0 | 0 |
| Profiling | 5 | 0 | 0 |
| Memory capacity | 18 | 6 | 0 |
| Expanded Megatron | 0 | 9 | 0 |
| Recovery | 7 | 0 | 3 |
Thus the smaller Megatron pilot succeeded, but the expanded Megatron configurations did not fit. Those statements are compatible and should remain separate.
Actual accelerator work here was on A10G, including a four-GPU g5.12xlarge host. Requested H100 capacity did not turn into an H100 run. We do not claim hands-on H100, H200 or B200 results from these experiments.
GPU reference: what we used, what we did not
A GPU name describes an accelerator. An AWS instance name describes the whole rented machine: GPUs, CPUs, host memory and networking. The two names are not interchangeable.
| Hardware | Project status | Memory per GPU | What that means for our work |
|---|---|---|---|
| NVIDIA A10G | Used for real-game training and transformer benchmarks | 24 GB nominal | The hardware behind our measured throughput, memory and correctness results |
| NVIDIA H100 SXM | Requested, but no completed run; capacity attempts did not produce an H100 experiment | 80 GB HBM3 | More room for model state and activations; unmeasured in this project |
| NVIDIA H200 SXM | Not used | 141 GB HBM3e | More memory capacity and bandwidth than the listed H100 variant; no project benchmark |
| NVIDIA B200 SXM | Not used | 180 GB HBM3e | A larger-memory accelerator platform; no project benchmark |
These are specific product variants. For example, H100 variants do not all have identical memory specifications. The H100/H200/B200 figures above refer to NVIDIA's HGX SXM comparison, not every cloud offering bearing those names. NVIDIA lists their respective device-memory bandwidths as 3.35, 4.8 and up to 8 TB/s. These are manufacturer specifications, not rates measured by our code. Sources: AWS G5/A10G specifications and NVIDIA HGX hardware specifications.
The machines behind the GPU counts
| Machine arrangement we used | GPUs available | Host CPU resources | Host RAM | Role |
|---|---|---|---|---|
| One AWS g5.xlarge | 1 × A10G | 4 vCPUs | 16 GiB | Individual workers; two separate instances formed the early two-node benchmark |
| One AWS g5.12xlarge | 4 × A10G | 48 vCPUs | 192 GiB | Expanded experiments and the real-game 1/2/4-GPU comparison |
A vCPU is an AWS scheduling unit, not a claim of one dedicated physical core. Instance resources are documented in AWS's accelerated-instance specification table. The one-GPU PS-Env scaling baseline used one active GPU inside the four-GPU host. It was not a comparison against the smaller g5.xlarge's CPU and memory resources.
The four-GPU host has four separate device-memory spaces. Sharding changes how software places data across them; it does not turn them into one ordinary local allocation.
Host RAM holds CPU-side data, including our emulator state and replay arrays. GPU memory (VRAM) holds device tensors and neural-network workspaces. Extra host RAM does not automatically fix CUDA out-of-memory errors. Explicit offloading can move selected state to the CPU, but introduces data movement; our PS-Env learner does not claim an offloading implementation.
GB and GiB differ. GB normally denotes billions of bytes; GiB denotes powers of two. AWS markets A10G as 24 GB, while its technical instance table reports roughly 22 GiB per device. Unit conventions, reservations and runtime availability matter. Use the actual runtime memory report and measured allocation headroom when sizing a job, rather than treating the product label as guaranteed allocatable space.
Capacity, bandwidth and compute answer different questions
| Hardware property | Plain-language question | Possible symptom when it is limiting |
|---|---|---|
| Memory capacity | How much can remain on the device at once? | Out-of-memory failure |
| Device-memory bandwidth | How quickly can the GPU read and write its own memory? | Operations spend substantial time moving local data |
| Compute throughput | How much arithmetic can the device execute? | Expensive matrix operations dominate the step |
| GPU-to-GPU connectivity | How quickly can workers exchange gradients or model shards? | Synchronization or parameter transfer dominates |
| CPU and input throughput | How quickly can the rest of the system supply useful work? | GPUs wait while emulators, preprocessing or transfers run |
HBM means high-bandwidth memory. Its bandwidth figure describes access to memory attached to a GPU; it is not internet speed or NCCL collective throughput. NVLink/NVSwitch describe inter-GPU connectivity in applicable systems. The cited NVIDIA HGX systems use these links; using NCCL on our A10G instances does not mean we measured an HGX/NVSwitch fabric. Our early two-node pilot communicated over private TCP networking.
Why A10G was a useful choice here
We could provision it and verify the actual software stack, whereas the H100 capacity attempts did not yield a runnable experiment. It supplied enough GPU memory for the PS-Env network and enough devices to study one-/two-/four-worker synchronization. The four-GPU machine also provided CPU and host-memory resources for emulators and replay.
For the larger transformer configurations, A10G's memory boundary was useful evidence: it let us observe when replication failed, when sharding helped, and when activation checkpointing substantially increased the tested batch size. That does not mean A10G is universally the cheapest or fastest choice.
A larger accelerator would make sense as a new experiment when a representative workload is demonstrably memory-, compute-, or communication-limited. It would not automatically improve the reward function, extend an incomplete demonstration, or teach recovery from unfamiliar game states. We have no measured H100/H200/B200 speedup to substitute into our A10G results.
A sensible next hardware comparison would keep the model and learning protocol fixed, measure end-to-end throughput and quality, and record CPU resources, interconnect topology, GPU memory peaks, precision and software versions. GPU price alone would be insufficient: the relevant practical quantity is the cost and elapsed time to reach the desired evaluated result.
10. Why we deliberately caused out-of-memory errors
Figure 5. This is a conceptual picture of model-state storage. Activations and temporary buffers also consume memory.
GPU memory holds several things: model parameters, gradients, optimizer state, intermediate activations, communication buffers and temporary allocations. Sharding model state reduces some of these categories. It does not automatically eliminate activation memory.
In a controlled capacity probe we increase batch size until the workload no longer fits. A recorded OOM is an expected boundary observation, provided the runner captures it and proceeds safely. An unexpected OOM in a normal scheduled run is still a failure. We must not relabel every failure as deliberate.
For the tested larger BF16 transformer at sequence length 2,048, the retained capacity results were:
| Configuration | Largest tested fitting global batch | Next tested batch: OOM |
|---|---|---|
| Single GPU | 8 | 16 |
| DDP, 4 GPUs | 16 | 32 |
| FSDP / ZeRO-2 / ZeRO-3, 4 GPUs | 32 | 64 |
| FSDP + activation checkpointing, 4 GPUs | 256 | 512 |
These are sampled boundaries for one model/configuration, not exact mathematical maxima or promises for other workloads. Capacity probes were short; fitting does not establish long-run convergence. Activation checkpointing's large improvement shows why identifying the category consuming memory matters.
When reading memory statistics, distinguish allocated tensor memory, allocator-reserved memory and process/device usage reported by nvidia-smi. They measure different things. Likewise, high GPU utilization does not by itself prove high useful throughput, and low utilization does not by itself identify the bottleneck.
Shapes of the separate transformer benchmark
The transformer benchmarks train bench/model.py::TinyGPT on a synthetic next-token task, not gameplay images. Here T is sequence length, D hidden width, H attention heads and V vocabulary size. These attention heads are not our five game-level output heads.
| Stage | Shape | Operation |
|---|---|---|
| Token IDs | [B, T] |
Integer indices |
| Token embedding + position embedding | [B, T, D] |
Look up learned vectors and add them |
| LayerNorm → QKV projection | [B, T, 3D] |
Produce query, key and value vectors |
| Reshape each Q / K / V | [B, H, T, D/H] |
Split the feature dimension across attention heads |
| Causal attention + output projection + residual | [B, T, D] |
Mix information from earlier tokens |
| LayerNorm → MLP → residual | [B, T, 4D] → [B, T, D] |
Expand features, GELU, contract features |
| Repeat transformer block L times; final LayerNorm | [B, T, D] |
Preserve the outer dimensions |
| Vocabulary projection | [B, T, V] |
Predict next-token scores; cross-entropy produces a scalar loss |
| Campaign preset | Blocks L | Width D | Attention heads H | MLP width | Sequence T | Vocabulary V |
|---|---|---|---|---|---|---|
| Small | 4 | 256 | 8 | 1,024 | 128 | 4,096 |
| Medium | 12 | 768 | 12 | 3,072 | 512 | 4,096 |
| Large | 16 | 1,536 | 16 | 6,144 | 1,024 | 4,096 |
| Large memory probes | 16 | 1,536 | 16 | 6,144 | 2,048 | 4,096 |
These are the expanded campaign presets in bench/campaign.py; smoke tests and the early Megatron pilot used smaller overrides. Read each result's configuration before comparing. The conceptual attention matrix is [B,H,T,T], but fused scaled-dot-product attention need not materialize that entire matrix. Doubling sequence length therefore does not justify assuming every measured memory category quadruples.
DDP replicates this architecture. FSDP/ZeRO change storage and communication of model state, without replacing the logical layers. Megatron tensor parallelism partitions computation inside transformer layers; it uses a separate implementation and requires its own equivalence checks. None of these synthetic transformer runs turns TinyGPT into the PS-Env policy.
11. Demonstrations and the gameplay learning experiments
Figure 6. A successful scripted route is an input to learning; it is not itself a learned-policy win.
Our demonstrations were produced by scripted action-sequence search, not by claiming a human expert had played them. Search uses emulator save states to try alternatives, rejects undesirable branches and keeps candidate paths according to heuristics such as survival or spatial coverage. A beam of 12 retains up to 12 candidate branches at each search layer; it does not mean the search tested only 12 sequences overall.
This privileged save-state search is different from a deployed policy acting once through the game. Its output is a candidate action trace. We bind that trace to a starting-state hash and replay it through the actual environment. A warp transition alone is insufficient to claim completion.
Four names that describe different parts of training
These are overlapping descriptions, not four mutually exclusive neural-network architectures. The same image-to-action network can be behavior-cloned, trained with reinforcement learning, and practiced using a reverse curriculum.
| Term | What it describes | Training signal / data | What we did in PS-Env | What it does not establish |
|---|---|---|---|---|
| Demonstration-trained | A broad description of a model's training history | Recorded teacher behavior; the exact objective must be specified | Replayed searched button sequences into observation/action examples; some policies then continued with RL and demo replay | It does not mean a human taught it, that the demonstration finished the level, or that the policy is robust |
| Imitation learning | A family of methods that learn behavior from demonstrations | Examples of behavior; methods differ in how they use them | Our supervised imitation stage used behavioral cloning | It is not the name of a particular layer layout; not every imitation method is BC |
| Behavioral cloning (BC) | A specific imitation-learning method | Supervised prediction of the teacher's action from its observation | 600 updates in the audited learning trials; cross-entropy over discrete action scores | Matching recorded actions does not teach recovery from every unfamiliar state |
| Reverse curriculum | A schedule for choosing training start states | Practice near a goal, then move starts backward as success improves; RL can use rewards without an action-copying loss | Historical Toad Village training used this; newer section drills used verified intermediate states and local section goals | Finishing from near the exit or completing a section is not a full-start level win |
BC is a subset of imitation learning. “Demonstration-trained” is a useful provenance label, but name the objective too: BC only, BC followed by RL, or RL with continued demonstration replay. Reverse curriculum can be combined with any suitable learning objective; it does not require copying teacher actions. Our section-practice implementation is related to reverse curriculum but a local section goal is not always the true level exit.
References: behavioral cloning documentation, imitation-learning methods, and Salimans and Chen's demonstration-based reverse-start method. Implementation: tools/learning_trial.py, agent/curriculum.py, and tools/train_ride_sections.py.
# Our BC objective: demonstrated actions are class labels.
action_scores = policy(demo_images) # [B, A]
loss = torch.nn.functional.cross_entropy(action_scores, demo_actions) # scalar; labels [B]
optimizer.zero_grad()
loss.backward()
optimizer.step()
The scores are used as classification logits during BC and as Q estimates during subsequent DQN learning. BC alone does not calibrate them as expected future rewards.
Comparison 1: what each term commits us to
The important distinction is data source versus method family versus learning objective versus practice schedule. A label such as “demonstration-trained model” leaves several implementation choices unspecified. The following tables make those choices explicit; “depends on the method” is a real distinction, not a missing definition.
| Question | Demonstration-trained | Imitation learning | Behavioral cloning (BC) | Reverse curriculum |
|---|---|---|---|---|
| What kind of label is it? | Description of training history / data provenance | Family of learning methods | Specific supervised learning objective | Schedule for choosing episode starts |
| Main question it answers | Where did some of the learning data come from? | Are we learning behavior from examples? | How do we turn demonstrated actions into weight updates? | Which part of the task should the learner practice next? |
| Core idea | Use recorded behavior to help train a policy | Learn useful behavior from demonstrations | Predict the teacher's action from its observation | Learn easier goal-adjacent starts, then move farther from the goal |
| Does the name specify a network? | No | No | No; it needs a suitable observation-to-action predictor | No; it can reuse the same policy |
| Does the name specify a loss? | No: BC, RL and combinations are possible | No single loss for the whole family | Yes in our discrete-action implementation: cross-entropy | No; our curriculum policies learn using RL and optional demo losses |
| Required data in our implementation | Replayed traces; richer data when continuing RL | Observation/action pairs for our BC stage | Stacked images and matching action IDs | Ordered emulator snapshots and an appropriate success signal |
| Must demonstrations be human? | No; ours were generated by search | No; the demonstrator can be a controller, search or another policy | No; labels can come from any accepted teacher | No; snapshots can come from a searched route |
| Must the demonstration finish the task? | No; ours include partial routes | Not universally, but data scope limits what it teaches | No; it can fit a partial route without learning the missing ending | A true exit-based curriculum needs useful starts toward the exit; section curricula can instead target local goals |
| Are reward values required? | Depends on the objective | Depends on the particular method | Not for the supervised BC loss | Our RL version needs reward feedback; the schedule also needs a success criterion |
| Are teacher action labels required? | Depends on how demonstrations are used | Depends on the method; not every method consumes action labels | Yes for our ordinary BC implementation | No for the start-state schedule itself |
Comparison 2: what happens during training and gameplay
| Question | Demonstration-trained | Imitation learning | Behavioral cloning (BC) | Reverse curriculum |
|---|---|---|---|---|
| Does training need new gameplay? | Not necessarily; BC-only can use stored data, continued RL collects more | Depends on the method | Our initial BC stage uses stored examples, with no new gameplay in its update loop | Yes in our implementation: play from the selected starts |
| What actually changes? | Whatever the chosen method trains; the label alone does not specify it | Usually policy parameters during training; details vary | Network weights change to favor demonstrated action choices | The reset distribution changes as the frontier moves; the chosen learner also updates weights |
| Where do episodes start? | Unspecified by the label | Unspecified by the family | The offline BC update loop has no episode reset; evaluation still needs a defined start | Near the current frontier, with a configured share of full-start attempts |
| What causes advancement? | No universal advancement rule | No universal advancement rule | More optimizer updates; no automatic “move to an earlier state” rule | A window of successful frontier attempts triggers an earlier start |
| How does it handle exploration? | Depends on whether RL follows the demonstrations | Depends on the method | No action exploration is needed to fit the fixed dataset | Shortens the difficult discovery problem by placing the learner closer to a reachable goal |
| What is fed into our policy at deployment? | Four stacked images for our demonstration-trained DQN | Four stacked images in our implementation | Four stacked images; no teacher action labels | Four stacked images; full-start evaluation does not restore helpful intermediate states |
| Are demonstrations supplied during inference? | Not for our policies; the label alone does not promise context conditioning | Method-dependent; our BC policies do not take demonstration context | No in our implementation: their influence is stored in the weights | No action trace is supplied to our evaluated policy |
| Are weights updated during evaluation? | No in our evaluations | No in our evaluations | No | No; evaluating a curriculum-trained policy is still a fixed-policy test |
| Can these be combined? | Yes: BC followed by RL still has demonstration-based training history | Yes: an imitation method can initialize a later RL learner | Yes: BC can precede RL, demo replay and curriculum practice | Yes: practice scheduling is separate from the policy loss |
| Does adding GPUs change the meaning? | No | No | No; distributed BC would average gradients from supervised batches | No; distributed workers would also need a deliberate start-sampling and curriculum-coordination rule |
Comparison 3: benefits, limits and convincing evidence
| Question | Demonstration-trained | Imitation learning | Behavioral cloning (BC) | Reverse curriculum |
|---|---|---|---|---|
| Main benefit | Useful prior experience can reduce blind discovery | Behavior examples supply information a sparse reward may not provide | A direct way to initialize useful action choices from verified examples | Makes long tasks easier to approach by practicing shorter remaining segments |
| Where can cost hide? | Collecting, searching, replaying and validating the demonstrations | Teacher access, data collection and any interactive training loop | Preparing images/action labels and covering enough situations; count this as well as optimizer time | Generating/restoring reliable snapshots, measuring success and replaying solved sections |
| Characteristic failure | “Trained on a winner” is mistaken for “the policy wins reliably” | The learned behavior fails outside the example distribution | Small action errors lead to unfamiliar observations, where further errors accumulate | Section success fails to compose into a full route, or earlier skills are forgotten |
| What if the examples are incomplete? | The model may learn only the demonstrated portion | Coverage remains a limitation | There is no supervised action target for an unrecorded ending | A local section curriculum may improve local control without ever teaching the true finish |
| Can it surpass the teacher? | Possibly with appropriate subsequent learning; not guaranteed | Method-dependent; not guaranteed | It can generalize beyond examples, but ordinary BC provides no explicit reward-based objective to discover a better route | It can learn different actions with RL; snapshots do not require copying the original actions |
| What does low training loss prove? | Nothing without knowing the loss | Fit to that method's objective, not automatic task success | Better fit to the sampled action labels; not reliable full-start completion | The reset schedule has no neural-network loss of its own; the learner's loss must be interpreted separately |
| What should be evaluated? | The actual saved policy, separately from the teacher trace | Policy success and recovery on conditions outside training | Held-out prediction plus closed-loop gameplay, especially deviations from the route | Full-start completion as well as frontier/section success and retention of solved sections |
| What is a misleading metric? | Number of successful teacher traces presented as policy wins | Training objective presented as success rate | Action-label accuracy alone presented as a robust playing policy | Many near-exit wins presented as many full-level wins |
| What did our retained snapshot show? | The three audited routes included one complete route and two partial routes | BC was the implemented supervised imitation method | Orient Express completed the demonstrated start but failed timing-shift checks; later selected policies retained the initial BC checkpoint | Historical Toad training used reverse starts; newer section wins were recorded separately from full-start wins |
| What must a result label include? | Teacher source, data scope, objective and subsequent RL | Specific method, teacher/data source and evaluation protocol | Update budget, input/controller settings and evaluated checkpoint | Start-state construction, frontier rule, full-start share and definition of success |
The “retained snapshot” row refers to the evidence dated in this guide. It is not a live claim about the newer level 1 campaign. That campaign uses the historical 13-action ordinary DQN and full-start training, without reconstructing its earlier curriculum or success buffer.
The concrete recipes we compared in code
These rows are actual experimental combinations. The four broad terms above are not four experiment arms.
| Recipe | Initialization | What data changes the weights afterward? | Episode starts | Why include it? |
|---|---|---|---|---|
Scratch RL (scratch) |
Random network weights | New gameplay transitions, sampled through replay; TD loss | Full starts, with configured timing changes where enabled | Measure what RL discovers without demonstration initialization |
BC only (the saved initial checkpoint) |
600 supervised updates in the audited campaign | No subsequent updates when evaluating that checkpoint | Offline BC has no reset loop; evaluate from defined full starts | Measure what imitation alone accomplished |
BC → RL (warm) |
The BC-initialized policy | New gameplay replay; demonstrations are not continually added to its RL batches | Full starts, with configured timing changes where enabled | Test whether RL improves or damages the initialized behavior |
BC → RL + demo replay (replay) |
The BC-initialized policy | New transitions plus retained demo transitions; TD loss and a demo-action margin term | Full starts, with configured timing changes where enabled | Test whether continued teacher examples help retain useful behavior |
BC → RL + demo replay + section practice (curriculum) |
The BC-initialized policy | The same mixture, while practicing verified route sections | Intermediate snapshots plus a share of full starts | Test whether shorter practice segments help learn control; count section success separately |
In tools/learning_trial.py, a normal per-task replay batch for a task with enough demonstration data uses 16 new-replay samples + 16 demo samples. The combined batch pays the TD objective; flagged demo samples also pay the large-margin action objective. BC pretraining instead uses 32 demonstration examples per task and cross-entropy. These are separate losses and training phases. The configured demo margin is 0.8; it is not an 80% demo sampling share.
For the section arm in this tool, the curriculum uses a five-result window, an 80% success threshold and a 50% full-start share. Historical Toad Village recipes have different settings; do not transfer these numbers to every run labelled “curriculum.” A current frontier can advance on section completion in this arm, so its success signal differs from an exit-based curriculum.
Worked example: an obstacle near the end of a level
Imagine a verified route has 100 decisions, and decision 90 is just before a difficult jump. These numbers are illustrative, not a new measured PS-Env result.
| Approach | What the learner experiences | What one successful result would mean |
|---|---|---|
| BC only | Train on the 100 recorded image/action pairs: at the observation before the jump, predict the demonstrated jump action | The policy can imitate that route if closed-loop evaluation confirms it; action accuracy alone is insufficient |
| RL from scratch | Start at decision 0's game state and discover useful behavior through rewards | A finish establishes that evaluated attempt succeeded without demo initialization |
| Reverse curriculum with RL | Restore the state near decision 90, learn the remaining segment, then move the start earlier after sufficient success | A win from decision 90 proves the suffix was completed; it does not count as playing decisions 0–89 |
| BC + reverse curriculum | Initialize by copying the route, then practice progressively earlier starts with RL | Combines a useful initial policy with easier practice; still requires full-start and recovery evaluations |
| BC + recovery examples | Add examples showing how to act after landing off-center or missing a timing cue | Tests whether broader teacher coverage improves recovery; this is a proposed improvement, not a claimed result |
A minimal sketch of the difference is:
# Behavioral cloning: observations and teacher actions define a supervised loss.
scores = policy(demo_images) # [B, A]
bc_loss = cross_entropy(scores, teacher_actions)
update_weights(bc_loss)
# Reverse curriculum: choose where an RL episode begins.
start = curriculum.pick() # None = full start; index = snapshot
observation = env.reset(start=start)
trajectory = play_episode(policy, observation)
update_from_rl_replay(trajectory)
curriculum.report(start, trajectory.success)
This is pseudocode: play_episode, update_weights, trajectory.success and update_from_rl_replay stand for the real implementations. It demonstrates why BC and reverse curriculum can coexist: one determines a loss, the other determines a reset. For section practice, success must be explicitly identified as section success rather than silently treated as level completion.
How to describe a model precisely in the presentation
| Vague label | More informative description |
|---|---|
| “A demonstration-trained model” | “A seven-action dueling DQN initialized with 600 BC updates on a verified Orient Express completion trace; evaluated separately from the trace” |
| “The imitation model passed” | “The saved BC policy completed the demonstrated full-start condition; timing-shift conditions failed at the retained snapshot” |
| “The curriculum model won many times” | “The policy completed a specified number of frontier/section attempts; full-start outcomes are reported separately” |
| “The four-GPU model is better” | “Four replicas execute one synchronized learner faster; policy quality is determined by the common evaluation protocol” |
The audited demonstration set used in the current experiments contains:
| Level | Recorded teacher decisions | Scope |
|---|---|---|
| Under Pressure | 480 | Partial route |
| Orient Express | 849 | Completion route |
| Makin Waves | 1,050 | Partial route |
We convert replayed observations and actions into imitation data. Behavior cloning trains the network to reproduce those action choices. Continued demonstration replay mixes demonstration transitions with newly collected experience and adds a demonstration action-margin objective. Section practice starts from verified intermediate snapshots; a section success is not a full-start win.
We compared scratch, demonstration initialization, initialization plus continued replay, and replay plus section practice. In an early snapshot all 11 finished demonstration-based trials retained their pre-RL checkpoint under our completion/milestone selector. This means no later checkpoint beat that selection score; ties do not prove every later policy was identical or worse in every respect.
The failure under tiny timing shifts is an important generalization result. Learning the demonstrated sequence of situations is easier than learning robust recovery when the game deviates from that sequence.
From the original project report · The Training Story
Historical record · September 2026 reconstruction and single-GPU experiments. These results are separate from the later shared-model scaling experiments in this guide.
The hackathon team reported the first level completed after roughly 500K steps with the distributed trainer. The 2026-09 reproduction on one g5.xlarge started from that recovered design, single-process, and found that it did not finish Toad Village at all. From scratch at frame skip 2 the agent learned to run forward and spin and never cleared the first pit: 0 completions in 186 episodes and 805K steps. A reverse curriculum at frame skip 2 wedged at the same pit (about 10% pit success for 200K steps with uniform one-step replay, and about 10% for another 200K with prioritized replay and three-step returns), and frame skip 12 never left fine stage 142.
Everything that moved the needle after that is below, in the order it was found. The numbers come from the training logs and from section 15 of the design reference.
| Lever | What it fixed | Evidence | |
|---|---|---|---|
| 1 | Frame skip 8--frame-skip 8 |
A Crash jump is about 24 held frames: 12 identical decisions at frame skip 2, and greedy probes changed their mind mid-air (JUMP, DOWN, JUMP, UP). At eight frames per decision a jump is three. | Pit success from the pre-pit states went from 5 to 15% to 60 to 90%. The curriculum advanced from fine stage 162 to 118 in about 425K steps. |
| 2 | Reverse curriculum over 163 solver states--curriculum 163--curriculum-epsilon 0.02 |
Start next to the exit and move the start back one solver macro (about 900 units) whenever half of the last ten episodes from the current state finish, so the agent only ever has to learn the next few seconds. A beam search (tools/solve_level) produced the states; each reset begins a few random frames into that state's macro so neighbouring states overlap in phase. |
Walked 162 to −1 over 1,275,682 steps and 5,805 episodes (about 7.8K steps per stage) without ever regressing a stage. A wedge at stage 142 under --curriculum-epsilon 0.1 unlocked at 0.02: action noise on a ledge kills. |
| 3 | Success buffer--success-buffer 20000 |
The stage-102 wedge was forgetting, not the game: the curriculum replays only its frontier, so the 100K ring buffer held nothing from solved ground, and greedy probes from starts 102 and 104 walked backwards into a frog on a section completed 50K steps earlier. A second buffer keeps the agent's own level-completing episodes and mixes them into every batch (self-imitation); each one is also written to disk as success_<step>.npz. |
40K steps without an advance before; 25 completions in the next 113K steps after, and the frontier moved from 102 to 101. |
| 4 | Demonstrations with DQfD's margin loss--demos --demo-margin 0.8 |
The beam search re-run in the agent's own 13 actions held for eight frames (three segments of 152, 398 and 117 macros; 1.1 h, 3.1 h and 0.9 h of search), replayed into transitions. Mixing them into batches changed nothing; the large-margin loss makes the demonstrated action the greedy one. | First segment: stage 101 to 85 in 130K steps with 98 completions. All three: 85 to 80 in 75K steps, 24 completions in 199 episodes. |
| 5 | Stall cut as a terminal--stall-steps, commit f544791 |
The stage-80 wedge was the hackathon's spinning exploit again: greedy probes from starts 80 and 81 walked to z of about 325.4K and spun beside a crate for 600 steps. The stall cut had been ending such episodes but bootstrapping through the cut, so the stalled state kept its value. The cut is now a terminal for the learner. | Stage 80 held for 52K steps (137 episodes, 18 completions) and unlocked at step 871,573. The frontier then moved to stage 73 by step 1,011,878, where the next wedge came. |
| 6 | Curriculum goal in progress units--curriculum-goal-units 12000, commit faac0b8 |
The stage-73 wedge was the success test, not the game: every failure from start 73 happened 15K to 70K units downstream, on ground already solved, so the frontier waited for a whole-level completion to happen by chance. Counting a start-state episode as a success after 12K units of progress separates "this stage is solved" from "the rest of the level went right". | Before: stage 73 held for 94K steps of run 9, and the 110 episodes started from state 73 produced 4 completions. After: 19 advances in the first 59K steps of run 10 (73 to 46), then 34 more at about 3.7K steps each. The curriculum reached the level start (stage −1) at step 1,283,694. |
| 7 | Stall penalty--stall-penalty 25, commit 69c14fb |
With every episode now starting at the level start, the agent froze on open ground at 5 to 8K units in most episodes. A lost life cost 25 and a stall cost 0, so standing still beside a hazard was the greedy policy's safest choice. The stall cut now costs a lost life. | Measured together with the idle cost in the next row. |
| 8 | Idle cost--idle-cost 1, commit dbcca7c |
A probe with dqn_01325000 showed the mechanism: at z = 403,573 all 13 Q-values sat within 2.3 of each other (NOOP 74.96, UP 73.39) although UP moved 1,885 units in eight steps with no hazard in sight. A no-op in an unchanging state bootstraps on that state's own value, so the idle value decays only by 1 − γ per target sync (5,000 steps), and the margin loss on the agent's own successes (7 to 19% no-ops) re-taught idling. Every step without new progress since the last respawn now costs 1 and stays out of the success buffer. |
Greedy probes from the level start reached 5.6K units (idling) with dqn_01325000, 24.5K (killed by the first goat) with dqn_01375000 and 49.7K (the pit past the second goat) with dqn_01450000; at the old freezing spot UP now leads the Q-values by 1.0. The frame-skip-8 chain's first completion from the level start followed at step 1,479,301 (run 12, epsilon 0.05, 926 steps). |
| 9 | Start mixture--curriculum-from-start 0.3--curriculum-spread 80 |
Runs 10 to 13 trained at pure stage −1, every episode from the level start, and mostly stalled. Run 14 fixed the mix at about 29% level start, 35% fine state 0 and 35% uniform over states 1 to 80, so the whole level stays in the batch while the start keeps being practised. | Pure stage −1: 2 completions in 375 from-start episodes (0.53%), 69% stalls. Run 14: 16 in 328 (4.88%). |
One earlier completion from the level start exists outside this chain: at step 820,822 of a frame-skip-2 curriculum run, a lone probe episode from the level start at stage 133 finished the level in 3,251 steps (reward 6,887). It was 1 of 39 such probes in that log and was never followed up.
From the original project report · Results
Historical record · September 2026 reconstruction and single-GPU experiments. These results are separate from the later shared-model scaling experiments in this guide.
Run 14
Run 14 resumed the frame-skip-8 chain from step 1,525,322 at epsilon 0.05 with every lever above in place, 24,269 demonstration transitions loaded, and ran to 2,000,000 steps: 475K steps in about 2 h 25 min, at 61 falling to 54 steps per second.
| Episodes | 1,116: 727 ended at a lost life, 225 at the stall cut, 130 in a game over, 29 with the level complete, 5 at the 1,600-step cap. |
|---|---|
| From the level start | 328 episodes: 16 level complete (4.88%), 177 stall, 130 game over, 5 at the cap. These are the only episodes that play the whole level with all their lives. |
| From curriculum states | 788 episodes from states 0 to 80 (394 from state 0 alone): 13 level complete, 727 cut at the first lost life, 48 stall. Cut at the first lost life, so not comparable with the row above. |
| The 16 completions | 908 to 1,552 steps, mean 1,163, median 1,158; mean episode reward about 6,700. Each has a matching success_<step>.npz on the training box. |
| Earlier runs at pure stage −1 | Runs 10 to 13, every episode from the level start: 2 completions in 375 episodes (0.53%), 69% stalls. |
The Plateau
The from-start completion rate did not trend over the run. A logistic fit of completion against step has a slope of +0.05 per 100K steps with p = 0.79, and the quadratic term is negative, an inverted U. The last completion came at step 1,933,153; the final 66K steps (151 episodes) produced none, and the run ended with three consecutive from-start game overs. Stalls stayed at about 54% of from-start episodes in every bucket, while the game-over share went 47%, 32%, 41%.
| Steps | From-start episodes | Completions | Rate |
|---|---|---|---|
| 1.525M to 1.6M | 58 | 1 | 1.7% |
| 1.6M to 1.7M | 65 | 3 | 4.6% |
| 1.7M to 1.8M | 65 | 5 | 7.7% |
| 1.8M to 1.9M | 66 | 4 | 6.1% |
| 1.9M to 2.0M | 74 | 3 | 4.1% |
| of which 1.90M to 1.95M | 35 | 3 | 8.6% |
| of which 1.95M to 2.0M | 39 | 0 | 0% |
Evaluating the Checkpoints
Two checkpoints were evaluated afterwards from the level start with agent.evaluate: no stall cut, a 1,600-step cap, 40 seeds at epsilon 0.05.
| Checkpoint | Epsilon | Result |
|---|---|---|
dqn_final.pt (2.0M) | 0 | One episode: return 527.62, 1,600 steps, hit the cap about a third of the way in, then wandered. Seeds 1 to 7 byte-identical. |
dqn_final.pt | 0.05, 40 seeds | 0 completions: 33 episodes hit the cap, 7 ended in a game over; returns 685 to 1,743 on the capped episodes. |
dqn_01925000.pt | 0 | One episode: return 925.26, 1,600 steps, no completion. |
dqn_01925000.pt | 0.05, 40 seeds | 1 completion (seed 27: return 6,728.46, 1,181 steps), 25 game overs, 14 at the cap. That is 2.5%, with a 95% interval of roughly 0.1% to 13%, consistent with the 4.88% seen in training. |
Greedy Play Is One Episode
Epsilon 0 is deterministic: select_action draws rng.random() < 0, which is never true, so with a deterministic emulator every seed replays the same episode. Filmed with tools.record_episode, the greedy dqn_final.pt reaches the crate beside the first tent, 36,594 units into a 164.5K-unit level (22%), and jumps in place for the remaining 1,300 or so steps until the cap. The recorder's return for that episode is 1,638.64 against the evaluation server's 527.62, because the recorder steps the emulator one frame at a time and the server eight, but both hit the cap without completing. Training at epsilon 0.05 was measuring a policy plus noise; the policy on its own is stuck at one crate.
The Films
Two completions from run 14 were replayed frame for frame by feeding the trainer's recorded actions back through the deterministic emulator (tools.record_episode --actions): success_01929825.npz (154.1 s, 9,245 frames, 1,156 steps) and success_01933153.npz (157.5 s, 9,452 frames, 1,182 steps). They are the first from-start completions on film. The earlier completion at step 1,479,301 was recorded before the success file kept every step, and diverged on replay.
From the original project report · Charts
Historical record · September 2026 reconstruction and single-GPU experiments. These results are separate from the later shared-model scaling experiments in this guide.
Five charts rendered with matplotlib from the trainer's per-episode log lines across the 27 training logs, by tools/plot_training.py (the originals live in docs/charts/). Buckets are by the step at which an episode ended.
From the original project report · Completion Leaderboard
Historical record · September 2026 reconstruction and single-GPU experiments. These results are separate from the later shared-model scaling experiments in this guide.
Every completion the trainer recorded is a replayable action list, so the level's counters can be read back exactly for each one: fruit collected, boxes broken, lives lost and steps taken. The 30 completions of runs 13 and 14 (17 from the level start, 13 from curriculum start states) were replayed one emulator frame at a time with tools/replay_stats.py; every replay matched the trainer's stored observations frame for frame and ended with the level complete after exactly the number of steps the log reports. The records below cover the 17 from the level start, the only ones that played the whole level with all their lives. Steps are agent steps of eight emulator frames; seconds are frames divided by 60.
| Record | Completion | Value | Notes |
|---|---|---|---|
| Most Wumpa fruit | step 1,765,004 | 15 Wumpa | 1,218 steps (162.4 s), 7 boxes, 1 life lost. The median completion collected 8. |
| Most boxes broken | step 1,631,273 | 11 boxes | All 11 still counted at the exit, in 956 steps with 1 life lost. Step 1,760,604 broke 12 in total, but dying restores the boxes broken since the last checkpoint, and after four lost lives it reached the exit with 9. The median is 4 at the exit. |
| Fewest lives lost | step 1,851,811 and six others | 0 lives lost | Seven of the 17 lost none: 1,659,684; 1,709,019; 1,810,922; 1,819,393; 1,851,811 (the shortest of them, 935 steps); 1,929,825; and 1,933,153, the filmed one. Six lost one life, one lost two, two lost three, and 1,760,604 lost four, finishing the level on its last life. |
| Fastest completion | step 1,665,906 | 908 steps, 121.1 s | 7,264 frames; 1 life lost, 6 Wumpa, 3 boxes. The median is 1,160 steps (154.6 s); the slowest, 1,760,604, took 1,552 steps (206.9 s). |
The 13 completions from curriculum start states are kept out of the records: those episodes are cut at the first lost life, and most began part-way into the level. Among them, step 1,742,347 (from fine state 0, which is at the level start) collected 31 Wumpa in 1,476 steps, step 1,777,001 (state 53) broke 9 boxes, and step 1,658,605 (state 76) reached the exit in 472 steps (62.9 s). All 30 records, each with a progress curve sampled every 10 steps, are in assets/completions.json (63 KB), a copy of docs/data/completions.json.
From the original project report · Honest Status and Next Steps
Historical record · September 2026 reconstruction and single-GPU experiments. These results are separate from the later shared-model scaling experiments in this guide.
Run 14 ended on a plateau. The from-start completion rate showed no trend over its 475K steps, the tail degraded (no completion after step 1,933,153, and 0 of the last 39 from-start episodes), and stalls stayed at about 54% of from-start episodes in every bucket. The greedy policy by itself does not finish the level from the start; the 4.88% is a policy plus epsilon 0.05 noise. Training longer at these settings is not the next step. The stall segment is.
What the evidence weakly favours:
- Ship a checkpoint near 1.93M steps rather than
dqn_final.pt. The last completions came at 1.92M to 1.93M,dqn_final.ptloops in place instead of dying, and over 40 seeds at epsilon 0.05 it completed 0 wheredqn_01925000.ptcompleted 1. - Attack the 54% stall share directly: find where the from-start stalls happen, and put curriculum states, demonstrations or a stronger idle charge there, before spending more steps on the whole level.
- A second level. Bone Yard is playable: its save state is scripted from the Warp Room, it runs toward the camera so progress carries the opposite sign, its in-level
WARP_STATEis 38 and its counters sit 0x27C above Toad Village's. The solver covered 40,275 units in 38 macros, and a 1,000,000-step run finished 2,138 episodes with zero completions, most likely because that segment mapped only the first 40,275 units, so the reverse curriculum never had a mapped path to an exit to walk back from. The next attempt should map the level through to its exit before training on it.
12. Reward design: what the results actually tell us
The early level-one recipe included a particular direction/progress assumption and large death penalties. Applying it unchanged to swimming or curved routes could produce inappropriate guidance. Later presets disabled direction-specific shaping and introduced capped exploration bonuses. Exploration helped provide feedback, but exploring new cells does not necessarily mean progressing toward an exit.
One clear mismatch under the previous reward setup was Under Pressure: a policy reaching 8/8 reference landmarks earned about -3.5, while a policy reaching only 1/8 and dying earned about +6.2. Makin Waves could earn more than 200 with little reference-route progress because exploration contributed heavily.
The overnight reward package reduced exploration, reduced the per-step cost, strengthened life/game-over penalties, and added +5 for reaching each ordered verified landmark once per episode. Completion detection remained unchanged. Demonstration rewards were recomputed through the same calculator, with every resulting observation checked against the stored data.
This changes several coefficients together. It tests a reward package, not the isolated effect of one coefficient. Within each new paired comparison the other settings match; comparisons against older campaigns also involve changes to learning rate, exploration and starting-state variation.
At this snapshot, greedy checkpoint evaluation showed:
| Level | At 100k decisions | Later checkpoint | Interpretation |
|---|---|---|---|
| Under Pressure | Reward 26.1; 5 landmarks | Reward 41.6 at 400k; 8 landmarks | Recovers the known partial route, no completion |
| Makin Waves | Reward 92.1; 17 landmarks | Reward 86.2 at 400k; 17 landmarks | No sustained return gain or completion |
| Orient Express | Reward 568.1; completes | Same return and completion at 500k | Preserves demonstrated success; shifts still fail |
A route reaching 8/8 or 17/17 is at the end of our reference, not necessarily the level. That metric then saturates and cannot distinguish progress beyond the recorded route. Likewise, a level without calibrated landmarks should show “not calibrated,” not a fabricated zero-percent completion metric.
More experience can help, but it can also reinforce a poor objective or fail to discover a narrow successful trajectory. We have evidence that reward ranking is imperfect and robustness is weak. We have not proved that reward is the sole cause, or that the current revision solves it.
13. Infrastructure is part of the experiment
The stack includes Terraform-managed workers, a pinned Python/CUDA environment, native emulator builds, private game assets, restricted SSH access, systemd campaign services, checkpointing, a private S3 artifact destination, a separate collector and the hosted console.
On the GPU workers, presigned upload URLs grant access to specific artifact objects rather than placing broad AWS credentials inside training jobs. The console collects metrics and selected models independently of the operator's laptop. Secrets and private assets are not exposed through the metrics API.
We publish checkpoints by atomic rename so readers do not mistake a partially written file for a usable model. Campaign summaries distinguish queued, running, completed, failed, interrupted and unrun work. Missing evaluation is not zero completions.
A checkpoint containing network and optimizer state still may not be an exact restart. Our PS-Env distributed checkpoint does not preserve replay contents and active emulator state. Loading it starts from learned parameters with new experience collection; it is a warm start. Exact recovery requires identifying and restoring every relevant source of state, including randomness and sampling position.
The overnight controller has a finite runtime, per-job limits, periodic uploads and a separate host shutdown timer. Instance-initiated shutdown terminates the GPU instance. At the snapshot it was still running, with hard termination scheduled for 09:01 Qatar. That is a schedule, not evidence that termination has already occurred.
From the original project report · Cost and Provenance
Historical record · September 2026 reconstruction and single-GPU experiments. These results are separate from the later shared-model scaling experiments in this guide.
Everything above ran on one on-demand g5.xlarge in us-east-1 (4 vCPU, one A10G, a 100 GB gp3 root volume) at $1.006 per hour, over four sessions. The first ran 59.17 h ($59.52), of which about 42 h were idle: run 14 had finished at 08:51 UTC on 2026-09-06 and ssh from a new network was blocked by the security group until the instance was stopped on 2026-09-08. A second session of 1.17 h ($1.17) read the results, filmed the completions and ran the evaluations; a third of 0.33 h ($0.34) came up impaired and was force stopped; a fourth of 8.75 h ($8.80) ported Bone Yard and ran its solver and its training run. The running time is measured from the 833 CloudWatch CPU datapoints the instance publishes only while it runs. Total: 69.42 h, about $69.8, plus about $8 a month of EBS while the instance sits stopped.
The original source code of PS-Env is gone. It lived in two repositories on the hackathon laptop, which is no longer accessible. The code on GitHub is a reconstruction from what survived: the team's writeup, a blog draft and its figures, the recording of the April 2026 talk, the architecture diagram shown during it, two whiteboard photos from the design session, and the pre-hackathon proposal and emulator notes. Every address, protocol field and hyperparameter in the code is traced to one of those sources, and the design reference marks what had to be reconstructed. The reconstruction was then run end to end on the real core and disc, which is where the numbers on this page come from.
The repository on GitHub, and docs/DESIGN_REFERENCE.md, the single source of truth for the recovered facts and the record of the 2026-09 training.
14. Failures and the lessons they gave us
| Failure or misleading result | What changed / what we learned |
|---|---|
| More GPUs slowed the tiny two-node transformer task | Measure communication relative to useful work; adding devices is not automatically beneficial |
| Optional dependencies upgraded the CUDA/PyTorch stack unexpectedly | Pin compatible versions and retain runtime/package records |
| Small numerical discrepancies upset a CPU weight comparison | Investigate the computation rather than silently widening tolerances |
| Service inactivity during restart was mistaken for completion | Require completion evidence or sustained failure, not one transient state |
| Evaluation lengths varied across ranks | Close the training process group and use atomic evaluation-completion files rather than holding a collective open through long evaluations |
| Local disk exhaustion broke collection and cleanup logging | Cleanup must remain possible even when logging or artifact collection fails; original evidence was recovered and checkpoints verified |
| The first real-game scaling pair had unequal updates | Add a fixed global update schedule and validate actual counts |
| Shared daytime trials hit a 90-minute limit | Budget for all levels and evaluation/film time; preserve partial checkpoints and mark interruptions honestly |
| All six overnight searches failed at CLI validation | Correct optional-prefix validation, test the real entry point and relaunch; these were not unsuccessful game searches |
| Three transformer recovery trials failed | Report recovery separately; a training run finishing does not prove interruption recovery works |
| Reward increased without a completion | Inspect reward components, route progress and films rather than optimizing the dashboard number |
| Presentation lagged the live results | Treat static slides as versioned evidence snapshots and refresh them deliberately |
The three expanded recovery failures were DDP with four GPUs and ZeRO-3 with two/four GPUs. Seven other recovery trials completed. It would be inaccurate to summarize this as universally successful checkpoint recovery.
15. How to evaluate the next experiment
Start with one question and the evidence that could change your mind.
For systems scaling, control model, global batch, total decisions/tokens, update count, precision, replay capacity, checkpoint frequency and timing scope. Repeat seeds or measurements, reverse run order where useful, and preserve failures. Report speedup and efficiency alongside quality.
For learning quality, compare against both scratch and demonstration-only baselines. Keep rewards fixed when comparing training methods, or keep training settings fixed when comparing rewards. Evaluate unseen starting conditions and review films. A deterministic repeat from the same state is not a new independent robustness trial.
For cost, distinguish active-GPU efficiency from the cloud bill. These one-/two-/four-GPU PS-Env measurements all ran on the same four-GPU instance: using one GPU did not reduce that host's hourly price. A 3.32× speedup is not automatically a 3.32× bill reduction. End-to-end cost includes idle time, setup, evaluation, storage and artifact transfer. This document is not an audited AWS invoice.
For memory, identify whether parameters, optimizer state, activations or temporary buffers are limiting the workload before choosing sharding, checkpointing or a larger accelerator.
16. Exercises using our own project
- Calculate global batch. Four GPUs each sample four transitions per level across five levels. How many samples contribute to one update? Answer: 80.
- Calculate update count. Start at 50,000 inclusive, stop at 200,000 inclusive, update every 20. Answer: 7,501. If you omit “inclusive,” you miss one update.
- Calculate efficiency. Four GPUs give 3.32× speedup. Answer: 3.32/4=83% scaling efficiency, not 83% GPU utilization.
- Spot a misleading claim. “Four GPUs got 0/20, one GPU got 0/20, so the models are equally good.” Unsupported: this test only failed to observe a completion in either; other behavior and untested conditions may differ.
- Inspect synchronization. Read
average_gradientsand find where summing becomes averaging. Predict what forgetting the division would do to gradient magnitude. - Inspect restart semantics. List what
core.savepreserves and what the emulator/replay loop keeps only in memory. Explain why loading weights is not an exact continuation. - Design a reward ablation. Change only the exploration coefficient while holding data, starts, seeds and budgets fixed. Decide beforehand which gameplay metrics would count as improvement.
- Design a robustness check. Choose starting perturbations excluded from training. Explain why replaying the exact winning start twenty times is inadequate.
Do not begin by launching every framework at once. First reproduce a small correct single-device loop, then identical-data gradient checks, then two-device execution, then a controlled scaling measurement. Memory-sharding and tensor-parallel experiments become easier to understand once that baseline is solid.
17. Code and evidence map
Paths in this table are relative to the corresponding repository. Generated run files are generally ignored by Git; retain collected evidence separately.
| PS-Env file | Read it to understand |
|---|---|
agent/model.py |
Convolutional models, shared features and level-specific heads |
agent/dqn.py |
Action selection, loss, optimizer and checkpoint state |
agent/replay.py |
Replay samples and n-step transitions |
agent/distributed.py |
Rank setup, local collection, gradient averaging, scheduling and evaluation |
agent/progress.py |
Ordered landmarks and diagnostic metrics |
agent/route_reward.py |
Experimental reward package and route bonus |
tools/learning_trial.py |
BC, replay, reward relabelling, checkpoint selection and evaluations |
tools/search_level_demo.py |
Candidate action-sequence search and optional verified prefix |
tools/replay_ride.py |
Independent trace replay and completion evidence |
deploy/distributed/overnight.py |
Scaling trials followed by longer gameplay jobs |
deploy/distributed/overnight_search.py |
Additional bounded CPU search |
tools/collect_learning.py |
Private artifact collection onto the console |
console/learning.py |
Read-only experiment API and artifact allowlist |
docs/OVERNIGHT_EXPERIMENTS.md |
Exact overnight protocol, limits and operational notes |
In the separate distributed-training-benchmarks repository, begin with README.md, evidence/RESULTS.md, bench/, and presentation/evidence/expanded-*.json. The phase summaries support the expanded outcome counts and the per-trial memory files support the tested capacity boundaries. The PS-Env six-run record is overnight-distributed.json in the collected learning artifacts.
The source of project-specific numbers is our retained experiment evidence, not the framework documentation. The linked primary documentation explains the techniques; it does not validate our particular results.
A defensible description of the work: “I built and evaluated a multi-level reinforcement-learning system, implemented synchronous data-parallel training with PyTorch/NCCL, measured 3.32× end-to-end throughput on four A10G GPUs at controlled update counts, and compared replicated, sharded and tensor-parallel training in a separate transformer benchmark. I also investigated memory limits, recovery failures, reward misalignment and demonstration generalization. Faster execution is established; robust multi-level gameplay remains unfinished.”
18. Where open robotics models fit
Research extension checked on 14 September 2026. None of the external models in this chapter was trained or deployed by our PS-Env experiments. These are examples for understanding the field, not a ranking of every available release.
A vision-language model (VLM) combines visual input with language. It might identify objects, answer a question about an image, or describe a goal. Understanding an instruction is different from producing correctly scaled robot controls.
A vision-language-action model (VLA) conditions action prediction on visual information and language, often with robot state as an additional input. Its output may be discrete action tokens or a continuous sequence of movements. A VLA is a kind of policy. A robot policy does not have to be a VLA: our DQN has no language input, and geometric demonstration-conditioned policies need not contain an LLM.
Model roles are distinct from training methods. A VLA may use imitation training, while distributed training determines how its parameter updates are computed across devices.
| Category | Typical inputs and outputs | Where it fits |
|---|---|---|
| Vision encoder | Image → visual features | Supplies representations to another model; not a complete controller |
| VLM | Images + text → language or multimodal features | Perception, interpretation, planning, or a backbone for an action model |
| VLA | Images + instruction + optional robot state → actions | A language-conditioned robot policy |
| Diffusion / flow policy | Observations and conditioning → action sequence | Generates continuous actions through learned refinement; language is optional |
| Demonstration-conditioned policy | Current observation + example trajectories → action | Uses examples as task context if trained/designed to do so |
| Low-level controller | Target pose, joint values or velocities → actuator commands | Executes the policy output under the robot's conventions and constraints |
| Simulator / environment | Action → next observation and outcome | Supplies interaction data and evaluation; PS-Env fills this role for the game |
“Diffusion” here usually describes generating actions, not generating pictures. Action chunking means predicting several future actions together. The controller can execute part of the chunk, observe again, and replan. Longer chunks can amortize inference cost but may delay reaction to unexpected events. This is a control-design tradeoff, not the same as increasing the training batch.
Representative open projects
| Project | What it is | Why it is useful to understand |
|---|---|---|
| OpenVLA | A 7B-parameter VLA; the original model builds on a pretrained VLM and learns robot action tokens | Shows how visual-language representations can be adapted to action prediction. The original release reports 970k robot trajectories. Paper, code |
| SmolVLA | A compact 450M-parameter VLA with an action expert, released through the LeRobot ecosystem | A smaller reference for studying action prediction and execution latency. Authors' introduction, model card |
| π₀ / openpi | Physical Intelligence's open robotics model package; π₀ uses flow-based action generation, while π₀-FAST uses autoregressive action tokenization | Demonstrates different action-generation designs within the VLA family. The repository also includes π₀.₅. Official repository |
| Octo | A generalist transformer diffusion policy supporting language or goal-image conditioning; initial Small/Base versions are 27M/93M parameters | Demonstrates multi-robot data, flexible observations and adaptation to action spaces without requiring a large autoregressive VLM as the policy backbone. Project, code |
| Instant Policy | Vosylius and Johns's graph-diffusion policy conditioned on demonstrations | A direct reference for in-context imitation rather than a standard language-first VLA. ICLR paper, code |
These sizes describe the cited releases, not a claim about every newer checkpoint. LeRobot is a robotics software/data ecosystem, rather than the name of a single policy. Open X-Embodiment denotes a robot-dataset collection used by projects such as OpenVLA and Octo; it is not a model or an optimizer.
The OpenVLA licensing distinction is documented in its official README. For other projects, use their model cards, repository licenses and upstream model terms rather than assuming the word “open” resolves every component.
Why we cannot just load one into Crash Bandicoot
A robot model may expect multiple RGB cameras, joint positions and continuous end-effector actions. PS-Env provides game images and discrete controller actions. The visual distribution, action meaning, timing and training objective differ.
Adapting a representation is plausible; directly sending robot-action outputs to a game controller is not a meaningful integration. We would need a defined observation adapter, an action head or action mapping, task data, and a held-out evaluation. A pretrained model's success on robot manipulation does not establish competence in our game.
19. In-context learning: change the examples, not the weights
Fine-tuning uses new task data to update model parameters. In-context learning (ICL) changes behavior by providing examples in the model's input while keeping its parameters fixed during that adaptation. In robotics, the examples may be demonstrations containing observations and actions, rather than text question-and-answer pairs.
A model can require substantial pretraining to acquire this ability. “No updates for the new task” does not mean “no training ever.”
| Approach | What happens with the new demonstrations? | Weights change for the new task? |
|---|---|---|
| Our behavior cloning | Demonstration observations/actions become supervised training samples | Yes |
| Our continued demonstration replay | Demonstration and online transitions contribute to optimizer updates | Yes |
| Fine-tuning with adapters / LoRA | A smaller trainable parameter set is optimized | Yes, even if the backbone stays frozen |
| In-context imitation | Demonstrations are part of inference-time conditioning | No, in the fixed-weight adaptation setting |
| Simple retrieval/replay | Retrieve a relevant recorded behavior, possibly align it, and replay | Not necessarily; this is not automatically learned ICL |
In notation, fine-tuning changes the parameters: theta → theta_task, then predicts policy(observation; theta_task). In-context adaptation instead predicts policy(observation, demonstrations; theta) with the same theta.
For a model to benefit, it must have an appropriate context interface and the learned ability or algorithmic structure to use it. Appending arbitrary demonstrations to an ordinary DQN does not create ICL. Similarly, an instruction-following VLA does not automatically support few-shot trajectory context just because it understands text.
One way to train that ability is to construct task episodes: provide some demonstrations as context and ask the model to predict actions in another trajectory from the same task. Test on held-out tasks afterward. The context examples and prediction targets must be separated, and evaluation must not feed the answers it is supposed to measure back into the context.
What changes for distributed training?
Distributed pretraining and fixed-weight in-context adaptation can coexist:
- During pretraining, workers receive different context/query task episodes and average gradients to train a common model.
- At deployment, the trained model receives a new context and predicts actions without an optimizer update.
- Serving multiple inference requests or robot streams in parallel is not the same operation as distributed training.
Demonstration context consumes input-processing time and memory. Longer context can increase attention or graph-processing cost. Reusing cached context representations may help when the architecture supports it, but we must measure latency and prediction quality. A larger context is not an unlimited free substitute for training.
20. Connecting this to Edward Johns and Vitalis Vosylius
Edward Johns's lab describes research in robot learning, including imitation and efficient adaptation. For this guide, the strongest connections are demonstrated capabilities in the papers below. I did not establish a dedicated DDP/FSDP/NCCL-method publication by him in the material reviewed. Do not turn “scalable robot learning” into an unsupported claim that he developed a particular distributed-training framework. These academic sources also do not establish Machenta's private model architecture. Lab research index, biography.
| Publication | Research idea | Connection to our experiments—our interpretation |
|---|---|---|
| Instant Policy, Vitalis Vosylius and Edward Johns; ICLR 2025 | Graph-diffusion in-context imitation from one or two demonstrations | Compare task adaptation without new updates against our BC/replay adaptation. Paper |
| Keypoint Action Tokens Enable In-Context Imitation Learning in Robotics, Norman Di Palo and Edward Johns; RSS 2024 | Represents observations/actions using 3D keypoints for LLM-based in-context imitation | Representation can make examples useful; natural-language prompting is not the only way to condition a policy. Project |
| Learning a Thousand Tasks in a Day, Kamil Dreczkowski, Pietro Vitiello, Vitalis Vosylius and Edward Johns; Science Robotics 2025 | Separates alignment and interaction, studying efficient teaching across many tasks | Our policies' failure after small deviations motivates measuring recovery and alignment, rather than only replaying a favorable start. Project |
| MILES: Making Imitation Learning Easy with Self-Supervision, Georgios Papagiannis and Edward Johns; CoRL 2024 | Collects self-supervised augmentation trajectories around a demonstration | We could test recovery examples around failures instead of repeating only the original successful trace. Project |
| Auto-λ: Disentangling Dynamic Task Relationships, Shikun Liu, Stephen James, Andrew J. Davison and Edward Johns; TMLR 2022 | Learns task weights using a meta-learning formulation | Our five-level loss weighting is a testable choice; equal weighting is not proof of optimal transfer. Project |
| Prismer: A Vision-Language Model with Multi-Task Experts, Shikun Liu and collaborators including Edward Johns; TMLR 2024 | Combines pretrained experts in a data-/parameter-efficient VLM | A direct VLM connection: useful representations and selected trainable components matter alongside raw hardware scale. Project |
| Back to Reality for Imitation Learning, Edward Johns; CoRL 2021 blue-sky oral track | Argues for evaluating human time efficiency in robot learning | Include setup, demonstration collection, resets and evaluation time when explaining what our speedup is worth. Essay/project |
A concrete training detail from Instant Policy
The paper's Appendix E reports 2.5 million optimization steps, approximately five days on a single RTX 3080 Ti, followed by a 50k-step learning-rate cooldown, using float16 and compilation. Its scaling study compares 69M, 117M and 178M model variants and training durations. These are the authors' results, not our runs. Parallel generation of pseudo-demonstrations supplies training data; that is distinct from reporting a multi-GPU gradient-synchronization benchmark. Published paper, Sections 4–5 and Appendix E.
The useful comparison is therefore between three kinds of scaling: generating more relevant data, training a larger or longer-trained model, and executing a fixed training workload faster. We measured the third directly; our gameplay results show why the first two and the structure of the task still matter.
What to emphasize in the conversation with Ed
A relevant explanation would be: “We measured a repeatable systems speedup, but the stronger behavior came from demonstrations, and small timing shifts exposed weak recovery. That made us separate throughput from adaptation quality and ask whether demonstration-conditioned representations or recovery-focused data would be more useful than simply increasing steps.”
Useful questions grounded in these publications include:
- When should we prefer retrieval/alignment, fine-tuning or in-context imitation for a new task?
- Which demonstration variations teach recovery rather than just repeat the nominal behavior?
- How should we measure total human time to a usable policy?
- In a shared policy, how do we detect one task harming another?
- When is generating better task/context episodes more valuable than accelerating gradient updates?
These are proposed discussion questions, not claims about Machenta's internal technology or Ed's personal preferences.
21. Small code examples and a practical next experiment
Example A: context can change an output without an optimizer
This executable example uses nearest-example attention over simple vectors. It isolates conditioning; it is not a learned robot policy, a VLA, or an implementation of Instant Policy. Its scalar outputs are illustrative values, not commands for hardware.
import torch
def action_from_context(query, demo_observations, demo_actions):
distances = torch.cdist(query, demo_observations).square()
attention = torch.softmax(-distances / 0.1, dim=-1)
return attention @ demo_actions
observation = torch.tensor([[1.0, 0.0]])
examples = torch.tensor([[1.0, 0.0], [0.0, 1.0]])
context_a = torch.tensor([[0.2], [-0.2]])
context_b = torch.tensor([[0.8], [-0.2]])
with torch.inference_mode():
print(action_from_context(observation, examples, context_a)) # ~0.2
print(action_from_context(observation, examples, context_b)) # ~0.8
The observation and computation are unchanged; the demonstration input changes. A learned context-conditioned policy would learn a much richer interpretation of observations, task relationships and action geometry. This toy does not demonstrate that capability.
The runnable version, with output checks, is tools/learning_examples/robotics_context.py. It was executed locally. No external robotics checkpoint was downloaded to run it.
Example B: training an imitation model with DDP
This is a training-loop sketch, not a standalone launch script or a VLA-specific API. Assume a CUDA process group, one local device per rank, a policy on that device, equal valid sample counts per rank, batch tensors on that device, and a loader that partitions examples across workers. The policy here directly predicts normalized continuous actions.
import torch
from torch.nn.parallel import DistributedDataParallel as DDP
import torch.nn.functional as F
# policy and train_loader are application-specific; setup precedes this snippet.
ddp_policy = DDP(policy, device_ids=[local_rank])
optimizer = torch.optim.AdamW(ddp_policy.parameters(), lr=1e-5)
for batch in train_loader:
optimizer.zero_grad(set_to_none=True)
predicted_actions = ddp_policy(batch["observation"], batch["context"])
loss = F.mse_loss(predicted_actions, batch["target_actions"])
loss.backward() # DDP synchronizes gradients during backward.
optimizer.step()
For discrete actions, a classification loss may be appropriate; diffusion or flow policies use their own noise/velocity prediction objectives. Mixed embodiments also need action-space handling, masks and normalization. This sketch shows where gradient sharing fits, not a universal robotics loss function. PyTorch documents the synchronization behavior in DDP.
At adaptation time an ICL-capable model instead follows the shape action = policy(observation, new_context) inside inference mode, with no backward() or optimizer step. eval() alone does not disable gradient tracking; it selects evaluation behavior for applicable layers.
Example C: an action output still needs the correct interpretation
OpenVLA's original interface includes predict_action and a dataset-specific unnorm_key. The following illustrates the inference call after loading the compatible processor/model and constructing its image-plus-instruction inputs:
# Interface pattern from the original OpenVLA README, not executed here.
# "bridge_orig" is meaningful for the matching dataset/robot convention.
with torch.inference_mode():
action = vla.predict_action(
**inputs, unnorm_key="bridge_orig", do_sample=False
)
The normalization key is not cosmetic. Output coordinates, units, action horizon and robot conventions must match the intended environment. This is why a robot checkpoint does not plug directly into PS-Env. Use the release's official setup and inference example; this fragment intentionally does not claim a complete installation or execution.
A next experiment that connects the ideas
A useful proposed comparison would keep evaluation tasks fixed and compare demonstration-only BC, BC plus recovery examples, and a genuinely context-conditioned policy. Train the latter on separated context/query trajectories across many task variations; evaluate it on withheld variations without adapting weights.
Measure completion, failure recovery, number of demonstrations, adaptation time, inference latency and total operator time. Only afterward compare one-/two-/four-GPU pretraining throughput at matching global work. Otherwise we risk mixing a change in learning method with a change in execution speed again.
For PS-Env, a first step is building a richer collection of task variations and recovery examples. One successful fixed-start trace and two partial routes are insufficient evidence that a general in-context policy will learn robust adaptation. The diagrams and code explain a direction we could test; they do not upgrade the current DQN into a robotics foundation model.
Now you know what’s behind the pixels.
Keep asking what an experiment actually measures. Then build the next one.
Try the exercises ↑ Back to the beginning ↑
