sean.theinformed.org / training / dcr / guide

Building a model's inputs from shared parts

The study of dynamic compositional representations, explained for scientists with some machine learning and no background in this study: the question, the tasks, the model, how the runs are trained on the cluster, what result would count, and how to follow it on the live page. The about page has a brief account; the status page has the live numbers.

The question and why it matters

Every language model starts the same way. Text is cut into tokens, each token is replaced by an integer id, and the id selects one row of a learned table. That row, a list of a few hundred numbers called the token's embedding, is what the first layer of the network reads. The table has one row per token and nothing else: the row for a token is the same in every sentence, and two rows share nothing unless training happens to make them similar.

This study replaces the table with a small constructor. The constructor keeps one shared dictionary of K = 128 learned vectors, called atoms. For every position in the input it computes a weight for each atom from the current token and the tokens before it, keeps the k = 4 largest weights and sets the rest to zero, and adds up the four weighted atoms. The result is the vector the network reads. A small shared module can then adjust the weights a few times (J = 2 by default) before the vector is handed on. Everything after the front end is an ordinary transformer and stays the same in every condition.

The question, as the research plan states it: does a sparse, shared-dictionary constraint on a context-conditioned input constructor change accuracy on held-out combinations relative to an equally expensive dense constructor, and does the value of shared iteration depend on that constraint?

The comparison is scored on held-out combinations. Neural networks are good at examples like the ones they trained on and often poor at new combinations of parts they have seen separately. The standard demonstration is SCAN, a toy command language: a model that has seen jump only on its own and has seen walk twice and run twice will often fail on jump twice. The ability to handle such combinations is called compositional generalization. A front end that must build every input from a few shared parts might help with it, because the same parts serve many tokens and so may carry meaning that transfers to new combinations.

Parts of this idea have been tried. A 2026 paper builds embeddings from a dictionary with weights that depend on context. A 2024 paper adds a learned code at the input and reaches 99 % on the SCAN split described above. What has not been measured is the comparison this study runs: sparse against an equally large dense constructor, with and without shared refinement, scored on held-out combinations at matched parameter counts and compute. A gain for the sparse version would point to a cheap change at the input of any model. Equal scores would show that the context, and not the sparsity, does the work. The plan puts the chance that the sparse constructor beats the dense one by more than seed noise at 30 to 40 %.

The models are small on purpose: 10.7 million parameters, 6 layers, 384 numbers wide. A run takes minutes on one graphics processor, so the study can afford 270 runs: three random seeds per setting and a control for each comparison.

The basics, built for this problem

Tokens in, next-token predictions out

Each example is a pair: a source (a program, or a SCAN command) and a target (the program's result, or the action sequence). Both are sequences of tokens from a small vocabulary: 19 symbols for most program tasks, 25 for the rebinding task, 23 for SCAN. Four ids are reserved: padding, a begin mark, a separator and an end mark. The model sees one row of ids: begin, source, separator, target, end.

<bos> 1 S 11 2 6 5 9 rot 17 addx 12 3 7 swap 18 mul 16 2 6 <sep> 2 5 9 2 6 4 8 3 7 <eos> 3 source (the program) target (written by the model) token id Each arrow: the next token is predicted from everything to its left; the loss is taken at these positions.
One training example as the model sees it; the blue boxes are the only positions where mistakes are penalized.

The model is a decoder: at every position it produces a score for each vocabulary entry, predicting the next token from the tokens to its left and never from the ones to its right. That restriction is called causal. During training the model sees the whole row at once and is graded at every target position. At test time it gets only the begin mark, the source and the separator, and writes the target itself, one token at a time, always taking the highest-scoring token (greedy decoding), until it writes the end mark.

What training means here

The model has about 10.7 million adjustable numbers, its parameters. Training repeats one step 12,000 times (15,000 for SCAN): take a batch of 128 examples, compute the cross-entropy (the average of minus the log probability the model gave to each correct target token), work out how each parameter should move to lower it, and move every parameter a little. How far it moves is set by the learning rate. Every model starts from random parameters; nothing is pretrained.

Exact match

A generated target counts as right only if every token matches and the end mark comes in the right place. One wrong digit makes the whole example wrong. The score of a run on a set of examples is the fraction it gets entirely right, its exact match. Per-token accuracy is logged too, but the hypotheses are read on exact match.

What "generalize" means here

Every task has at least two evaluation sets, both disjoint from training (no evaluation source appears among the training sources):

The two can be far apart. In the check run described in section 3, the plain model scored 1.000 on the iid split of the depth task and 0.000 on the held-out depths. The iid split is the control. A front end that raises held-out accuracy and leaves iid accuracy alone has changed how the model generalizes. A front end that raises both may only have given the model more capacity or an easier optimization.

Seeds and seed noise

Each setting is trained three times, with seeds 0, 1 and 2. The seed changes the random starting parameters and the order and sampling of training examples. It does not change which combinations are held out: that design comes from a separate fixed seed, so the three runs are replicates of one design. The spread between seeds, the seed noise, is the yardstick for every comparison in section 6.

The data

Programs on a 7 by 7 grid

Four of the six tasks are generated programs. The state is a pair (x, y) of integers modulo 7, a point on a 7 by 7 grid that wraps around at the edges. A program gives a start state and then a list of operations; the generator works out the answer exactly, so the targets are always right.

operationeffect on (x, y)argument
addx a(x + a, y)a from 0 to 6
addy a(x, y + a)a from 0 to 6
mul a(a·x, a·y)a from 1 to 6
rot(−y, x), a quarter turnnone
flipx(−x, y)none
flipy(x, −y)none
swap(y, x)none
All arithmetic is modulo 7. Digits are the same tokens wherever they appear: in the start state, as arguments and in the answer.

The target is a trace: the state after each operation. For the combination task the target is the state after every second operation (and after the last), so each output step has to compose a pair of operations in one go. An earlier form asked for the final state only; the plain model learned it too slowly (12 % exact after 4,000 steps), and a state after every single operation made the held-out pairs trivial, since no pair ever had to be composed.

step 0 of 4
Step through the program S 2 5 rot addx 3 swap mul 2: the point moves on the wrapped grid, and the target collects the state after steps 2 and 4.

Each program task has 100,000 training programs, 2,000 iid examples and 2,000 examples in each held-out split. The generator balances how often each operation appears in training.

What each task holds out

tasknever in trainingheld-out split
progs-combo8 of the 49 ordered operation pairs, as neighbors: addx→flipy, addy→addy, addy→swap, flipx→rot, flipx→swap, flipy→addx, swap→mul, swap→flipxcombo: programs of 2 to 6 operations with at least one such pair falling on an output step
progs-depthprograms longer than 4 operationsdepth (5 to 8), depth6, depth8
progs-role6 operation-argument pairs: addx 1, addx 3, addy 2, addy 4, mul 5, mul 6; each digit still appears as the argument of other operationsrole: programs with at least one of them
progs-rebind4 alias-operation bindings: fa→addx, fb→rot, fc→swap, fd→flipy rebind: episodes using those bindings; rebind_seen: new episodes with training bindings
scan-addjumpjump inside any longer commandthe official test set, 7,706 commands
scan-lengthaction sequences longer than those in trainingthe official test set, 3,920 commands

The rebinding task is the one the hypothesis speaks to most directly. An episode starts with three demonstrations of a made-up word, for example S 5 5 fa 5 = 5 3 ;, which shows what fa does to a state, and then a short program that uses fa among ordinary operations. The demonstrations are drawn until they rule out every other operation with the same kind of argument. In training each alias stands for any operation except one; the held-out split binds each alias to that one operation. The model must build the meaning of the alias from the context, which is what a context-conditioned constructor is for: the sparse version has to express the new meaning as a mix of atoms it already has.

The two SCAN tasks use the official splits of the public benchmark. A command such as run thrice after jump around left maps to a sequence of actions: I_TURN_LEFT I_JUMP four times, then I_RUN three times. The iid split is 1,000 commands taken out of the official training file, which leaves 13,670 training commands for add-jump and 15,990 for length.

The low-data condition, and why it exists

Before any cluster run, each program task was trained once with the plain table model for 2,500 steps on a desktop computer's graphics processor, to check that the baseline learns it and that the held-out split stays hard.

in-distribution (iid) held-out split 0 0.25 0.5 0.75 1 exact match (fraction of examples entirely right) progs-combo progs-combo, iid: 0.920 0.920 progs-combo, held-out: 0.480 0.480 progs-role progs-role, iid: 1.000 1.000 progs-role, held-out: 0.497 0.497 progs-depth progs-depth, iid: 1.000 1.000 0.000 progs-rebind progs-rebind, iid: 0.998 0.998 progs-rebind, held-out: 0.965 0.965 progs-rebind, 20k progs-rebind, 20k, iid: 0.868 0.868 progs-rebind, 20k, held-out: 0.347 0.347 progs-role, 20k progs-role, 20k, iid: 1.000 1.000 progs-role, 20k, held-out: 0.398 0.398
The plain model learns every task in distribution but stays far lower on held-out examples, scores zero on longer programs, and is near ceiling on rebinding until training is cut to 20,000 episodes.

Two readings follow. The depth task looks like a floor for this kind of backbone: it is kept because the original notes asked for it, but no pass line depends on it. Rebinding at 100,000 episodes leaves little room for any front end to differ (0.965 held-out), while at 20,000 episodes the held-out score falls to 0.347. So the study adds a low-data condition: rebinding and role at 20,000 training examples instead of 100,000, for all nine front ends. These check runs are single runs of one condition. They set the form of the tasks and are not results of the study.

The model

The backbone, the same everywhere

The backbone is a decoder-only transformer of 6 blocks, 384 numbers wide, with 6 attention heads and an MLP of width 1,536 with GELU activation in each block. Normalization comes before each sub-layer (pre-norm), positions are encoded by rotating the attention vectors (rotary positions), attention is causal, and the output head has its own weights, separate from any input table. The blocks hold about 10.6 million parameters. Between the front end and the first block sits a normalization with no learned parameters, the same for every condition, so a front end that produces larger or smaller numbers cannot gain an advantage in optimization from scale alone.

The front end, step by step

The contextual front ends work in three stages. An identity table gives each token a short vector u of 128 numbers. A context block, one causal attention block 128 wide, turns the sequence of u into context vectors c, so that c at a position depends on that token and the ones before it. A composer, a two-layer MLP with a hidden width of 256, reads u and c side by side and produces either 128 atom weights (the sparse versions) or 384 numbers directly (the dense versions).

token ids one integer per token, vocabulary 19 to 25 front end (differs by condition) embedding table one learned row of 384 numbers per token identity table one row of 128 numbers per token: u causal context block 1 attention block, width 128, 4 heads: c [u ; c]: 256 numbers composer (a small MLP) 256 → 256 → 128 atom scores 256 → 256 → 384 numbers keep the 4 largest code: 128 weights, 4 of them nonzero dictionary read weights × D, D = 128 atoms × 384 refiner shared, J = 2 passes add, keep 4 again vector z 384 numbers, all free refiner shared, J = 2 passes add to z normalize no learned parameters; 384 numbers per token 6 transformer blocks width 384, 6 heads, MLP 1,536 causal attention, rotary positions about 10.6 million parameters output head 384 → one score per vocabulary entry next token highest score; scored by exact match

Switch front ends: only the dashed region changes, and the sparse path differs from the dense one by the keep-four step and the dictionary read.

The constraint sits in the sparse code. Of 128 weights, all but the four largest in size are set to zero; the gradient flows through the four that are kept. The vector is then the weighted sum of four atoms.

4

Slide k: with few active atoms the vector is a mix of a handful of shared parts, and the set of reachable vectors shrinks to a union of small flat pieces.

The constraint has a geometric form. With K = 128 atoms and vectors of 384 numbers, every vector the sparse constructor can make lies in the 128-dimensional space spanned by the dictionary, and with k = 4 it lies in one of the 4-dimensional subspaces spanned by four atoms. Such a set is a union of subspaces. The normalization after the front end is a fixed map and does not lift the bound. The sweep over K (32, 128 and 256) moves the dictionary across the 384-dimension mark, and the sweep over k (2, 4 and 8) loosens or tightens the union.

The same idea in two dimensions with three atoms: one active atom confines every vector to three lines, two active atoms free it.

Refinement

The recurrent versions add a refiner, a small MLP shared across passes. On each pass it reads the current code (or, in the dense control, the current vector) together with u and c, and adds a correction. In the sparse version the four largest weights are kept again after every pass, so a pass can swap one atom for another. The refiner starts as the identity: its last layer is zero at initialization, so refinement begins by changing nothing and learns what to change.

J = 2

Step J from 0 to 4 (illustrative numbers): in the sparse version a pass can replace an atom, in the dense version every number shifts a little.
dense constructor (C) sparse constructor (D) sparse + refinement (F) 384 numbers, each free composer: 256 → 256 → 384 4 of 128 atom weights nonzero vector = weights × dictionary 4 of 128 atom weights nonzero vector = weights × dictionary J passes of one shared refiner may swap an atom in or out Cells: blue positive, red negative, gray near zero; in the sparse panels a gray cell is an unused atom.
The three ways the vector is built, side by side: the dense constructor sets 384 numbers freely, the sparse one picks 4 weights out of 128, and refinement revisits that pick J times.

The nine front ends

codenamewhat it doeswhat it isolatesfront-end parameters
Atableordinary embedding tablebaseline7,296
A+table_plustable, and the backbone gets a seventh blockmore compute in the usual place7,296 + 1,772,928
Bstatic_sparsea fixed sparse code per token over the shared dictionary, no contextsharing without context51,584
Cdense_ctxidentity and context → dense vectorthe control for D364,928
Dsparse_ctxidentity and context → sparse code → dictionarythe sparse constructor348,288
Edense_recC plus J refinement passes on the vectorthe control for F627,712
Fsparse_recD plus J refinement passes on the codeiteration under the constraint479,744
D₀sparse_ctx_frozenD with a random dictionary that never trainswhether learning the dictionary matters348,288 (49,152 fixed)
Qvq_statictoken vector plus the nearest of 128 shared codesa simplified version of the 2024 code-at-the-input method56,448
Parameter counts follow from the layer sizes at vocabulary 19; every run also records its own exact count. The backbone adds about 10.65 million in every row.

The comparisons the hypotheses rest on are close in cost. The dense control C has 0.15 % more parameters and 0.15 % more compute per token than D. E exceeds F by 1.3 % in parameters and 2.4 % in compute at J = 2 (4.6 % compute at J = 4). The extra block of A+ costs 16.6 % more, so it is a larger control rather than an equal one. Q is a simplified stand-in for the published method, and its token vector receives gradient through two paths, once directly and once through the matched code.

Every front end must be causal, like the backbone: the vector at a position may depend on that token and earlier ones only. A self-test checks this for all nine before any training on the cluster, by changing later tokens and confirming that vectors and scores at earlier positions stay the same.

The training schema

Objective and optimizer

The loss is the cross-entropy on the target tokens and the end mark. Two front ends add a small extra term: the soft-threshold variant adds an L1 penalty (10−3 times the mean absolute weight) that pushes weights to zero, and Q adds the usual pull between each token vector and its nearest code. The optimizer is AdamW (momentum terms 0.9 and 0.95, weight decay 0.1), gradients are clipped at norm 1.0, and the batch is 128 examples. The learning rate climbs linearly for 500 steps to 0.001 and then follows half a cosine down to 0.0001.

0 0.25e-3 0.5e-3 0.75e-3 1e-3 0 2,000 4,000 6,000 8,000 10,000 12,000 training step peak 0.001 after 500 warmup steps 0.0001 at the end
The learning rate over a 12,000-step program run (SCAN runs stretch the same shape to 15,000 steps); orange dots mark the evaluations and checkpoints every 2,000 steps.

Every 2,000 steps the run scores a fixed random subset of 2,000 examples from each evaluation split. At the end it scores every split in full, saves 24 decoded examples per split so failures can be read by eye, and, for the sparse versions, records how often each atom was used.

The grid of runs

A grid is a table with one row per run: task, front end, size, seed, steps and any extra settings. The study has two.

gridwhatruns
grid16 tasks × 7 front ends × 3 seeds (all but E and F)126
low-data condition: role and rebind at 20,000 examples × 7 × 342
D with K = 32 or 256 and with k = 2 or 8, on combo and rebind at 20,000 × 324
grid1bE and F on the 6 tasks and the low-data condition × 348
E and F with J = 1 and J = 4, on combo and rebind at 20,000 × 324
D with the soft-threshold sparsifier, on the same two tasks × 36
total270
grid1 has 192 runs and grid1b 78. The E, F and soft-threshold runs moved to grid1b after a code review found that the refiner did not start as the identity and the soft threshold started too high; both were fixed before grid1b ran.

How a run is packed onto a node

The cluster's GPU nodes each have 128 CPU cores, 512 GB of memory and four NVIDIA A100 graphics processors with 40 GB each. One job takes a whole node and starts four slots, one per graphics processor. Each slot walks down the grid table and claims the first run that is neither finished nor claimed, by creating a lock folder for it; creating a folder either succeeds or fails in one operation, so two slots can never claim the same run. A slot trains its run, then claims the next, until no rows are left. Runs are short (a little over four minutes on average in grid1), so a node finishes several dozen per hour.

one node: 128 CPU cores, 512 GB memory, four NVIDIA A100 graphics processors (40 GB each) slot 0, GPU 0 slot 1, GPU 1 slot 2, GPU 2 slot 3, GPU 3 0 h 1 h 2 h 3 h 4 h 5 h chunk 1 (4-hour job) chunk 2 carries on time limit: running runs save a checkpoint and stop wait in queue
Four slots on one node each run one short training run after another (made-up run lengths to scale); at the four-hour limit the runs in progress save and stop, and the next job resumes them.

Chunks and resuming

The grids go to the scheduler as a chain of 4-hour jobs, called chunks; each chunk starts when the one before it ends. Short jobs start much sooner: on the day of submission a 4-hour whole-node job was estimated to start in 1.6 hours and a 12-hour one in 18.7 hours. When a chunk reaches its time limit, the scheduler signals every slot, and each run in progress saves a checkpoint (parameters, optimizer state, position in the shuffled data, random number states and elapsed time) and stops. The next chunk's slots find those runs locked by a job that has ended, take them over and continue from the checkpoint with the same data order and random state it had when it stopped. Runs also save a checkpoint every 2,000 steps. A run is finished when it has written its final metrics file, and finished runs are skipped, so submitting the same chain again carries on where it stopped.

Hardware, stage by stage

stagehardwarewhat differs
code, tests and self-testa desktop computer's CPU27 automated tests, tiny models
task check runs (6 runs)the same desktop computer's built-in graphics processor32-bit arithmetic, 2,500 steps, 200 warmup steps, 400 evaluation examples per split; 2.4 to 6.5 minutes per run
timing jobone cluster node, four A100s, 30-minute debug queueself-test on one graphics processor, then four short runs; 49 seconds; estimated grid1 at 537 core-hours (about 4.2 node-hours)
grid1 and grid1bcluster nodes with four A100s eacha container image with CUDA 12.8 and PyTorch; bfloat16 mixed precision; batch 128 unchanged; about 150,000 to 170,000 tokens per second per run
Cost is counted in node-hours; one node-hour is 128 core-hours. A chunk costs at most 4 node-hours.

Moving between machines changes the arithmetic and the speed; the batch, steps and learning rate stay the same. On the A100s the forward and backward passes run in bfloat16, a 16-bit format with the range of 32-bit floats, while the desktop runs used full 32-bit arithmetic. The evaluation uses the same precision as training, so the four atoms chosen at test time are the ones the model trained with. The cluster also has nodes with AMD MI300A accelerators; the same code would run there from a second container image built on ROCm in place of CUDA, and each run records the device it ran on. grid1 and grid1b ran only on the A100 nodes.

Evaluation and the pass lines

Each number in the comparisons is the exact match on the held-out split, from the final model, scored on the whole split, averaged over the three seeds. One condition beats another when the difference between their means is larger than twice the pooled seed standard deviation.

case 1 dense dense seed: 0.47 dense seed: 0.50 dense seed: 0.53 sparse sparse seed: 0.60 sparse seed: 0.62 sparse seed: 0.65 difference 0.123 > 2 × 0.028 = 0.055: sparse beats dense case 2 dense dense seed: 0.44 dense seed: 0.50 dense seed: 0.55 sparse sparse seed: 0.48 sparse seed: 0.55 sparse seed: 0.61 difference 0.050 < 2 × 0.060 = 0.121: sparse does not beat dense 0.3 0.4 0.5 0.6 0.7
The rule on made-up numbers: three seeds per condition, the bar at each mean, and a win only when the gap between means exceeds twice the pooled spread.

The pass lines, quoted from the research plan:

  • H1 (sparse reuse): D beats C on at least two of {combo, role, role-20k, rebind-20k} with iid within 2 points.
  • H2 (learning the dictionary matters): D beats D₀ wherever H1 holds.
  • H3 (iteration × constraint): the gain from J = 1 → 4 is larger for F than for E on the cells that carry the J sweep, progs-combo and progs-rebind-20k (grid1b); a monotone curve in J for F and flat for E is the clean version.
  • H4 (context matters): D beats B on rebind (B cannot do rebinding at the input at all; this is a sanity line, not a result).
  • Reference: Q must reach > 90 % on scan-addjump (the published result for the full method is 99 %); if it does not, our proxy is weak and the published code is run before any claim.

H1 asks whether the sparse constructor generalizes better than its dense twin on the tasks with room to move, while matching it in distribution. H2 asks whether the gain needs a learned dictionary or whether random atoms would do. H3 asks whether refinement helps more when it works on a sparse code. H4 checks that a front end with context can do what a context-free one cannot. The reference line checks that the simplified code-at-the-input baseline is strong enough to stand in for the published method.

Null splits and checks

What a null result would mean

Each way the hypotheses can fail has its own reading, within the parameter and compute budgets tested:

outcomereading
C ≈ Dprocessing the context helps; sparsity adds nothing
B ≈ Dthese tasks do not need the input to depend on context
D₀ ≈ Dthe dictionary does not need to be learned
E ≈ F along Jany gain from refinement belongs to iteration itself, with or without the constraint

If neither D nor F wins anywhere, the study ends with that finding. The next phase, the same comparison at about 110 million parameters on natural text, runs only if one of them wins somewhere.

The status page and the replay

The status page is refreshed from the cluster about every 10 minutes while one of us is logged in. When the figures are more than 30 minutes old the page says so and shows when they were taken.

Finished 121 of 192 bar fills as runs end Chain c1 running c2 to c6 waiting Time left about 1.7 h at the current pace Cost 366 core-h about 579 at the end ‹ › 19:05 UTC 310 core-hours held-out exact match by condition step events c1 started run 100 finished c1 hit its limit c2 resumed
A sketch of the page with values from the evening of 3 October 2026: four tiles summarize the grid, and the replay below plays the training back with the clock and the cost moving together.

The tiles and sections

The replay

The replay plays the training back as it happened. Play, pause and the step buttons move a scrubber through time; a clock shows the time of day on the cluster and a second readout the core-hours spent up to that moment. Below them, one chart per measure (training loss, held-out exact match, iid exact match) draws one line per front end, and a marker moves along the step axis. An event list beside the charts names what happened at each moment: a chunk starting, runs finishing, a chunk reaching its limit, a run resuming. Each line holds at most 500 points, taken evenly from the run's log.

The held-out lines can rise late, or not at all, after the loss has flattened; that gap is the difference between fitting the training set and generalizing. The pass lines are read on the last points of the C and D lines, and of E and F, on the tasks they name.

Glossary

A100
An NVIDIA graphics processor with 40 GB of memory; each GPU node of the cluster has four.
AdamW
The optimizer used: it scales each parameter's step by running averages of its gradient and squared gradient, and shrinks weights slightly each step (weight decay).
atom
One of the K learned vectors in the dictionary; every constructed input is a weighted sum of a few atoms.
batch
The 128 examples used for one training step.
bfloat16
A 16-bit number format with the same range as 32-bit floats and less precision; used for the arithmetic on the A100s.
causal
Depending only on the current and earlier positions, never later ones.
checkpoint
A saved copy of everything a run needs to continue: parameters, optimizer state, data position, random number states.
chunk
One 4-hour job in the chain that carries a grid; the next chunk resumes where it stopped.
code
The 128 atom weights for one position, of which k are nonzero.
composer
The small MLP that turns identity and context vectors into atom weights or a dense vector.
compositional generalization
Handling new combinations of parts that were each seen in training.
core-hour, node-hour
One CPU core for one hour; a whole node of 128 cores for one hour is a node-hour, 128 core-hours.
cross-entropy
The training loss: the average of minus the log probability given to each correct token.
dictionary
The shared set of K atoms, 128 by default, each 384 numbers long.
embedding
The vector of numbers that stands for a token at the input of the network.
exact match
The fraction of examples whose generated target is entirely right.
held-out split
Evaluation examples built around a combination that never occurs in training.
iid split
Evaluation examples drawn the same way as the training examples (independent and identically distributed), none of them seen in training.
J
The number of refinement passes; 2 by default, 1 and 4 in the sweep.
K, k
The number of atoms in the dictionary (128), and the number active at each position (4).
learning rate
How far each training step moves the parameters; here up to 0.001.
MLP
A multilayer perceptron: linear maps with a nonlinearity between them.
node
One computer in the cluster; the GPU nodes have 128 CPU cores, 512 GB of memory and four A100s.
parameter
One adjustable number in the model.
pass line
A condition written into the plan before the runs that a result must meet to count.
refiner
The small shared MLP that adjusts the code or vector on each of the J passes.
rotary positions
A way of telling attention where tokens are by rotating its query and key vectors by an angle that depends on position.
SCAN
A public benchmark of simple navigation commands and their action sequences, with splits that test compositional generalization.
seed
The number that fixes a run's random choices; each setting runs with seeds 0, 1 and 2.
slot
One of the four workers in a job, each bound to one graphics processor and running one training run at a time.
soft threshold
An alternative to keeping the k largest weights: every weight shrinks toward zero by a learned amount, and small ones become zero.
token
One symbol of the input or output: a digit, an operation name, a word of SCAN.
trace
A target listing the state after each operation (or after every second one).
transformer block
One layer of the backbone: causal attention across positions followed by an MLP at each position, each with a normalization and a skip connection.
union of subspaces
The set of vectors reachable when only k atoms may be active: one k-dimensional flat piece for each choice of k atoms.
usage perplexity
The effective number of atoms in use, from how evenly they are used; low values mean a few atoms do most of the work.