Sam Babichenko

Reading notes / NanoGPT speedrun

Cross-layer connections

Zhou et al., 2024; Xiao et al., 2025

Context. In a standard pre-norm transformer every layer reads one residual stream and writes its output back into it, so all communication between layers goes through a single running sum. Deep models show the symptoms of that bottleneck: token representations grow more alike with depth, the inputs of adjacent late layers become nearly identical (cosine above 0.97 in Pythia-2.8B), and attention piles onto low-information sink tokens. The fix is to let later layers read earlier layers' values or hidden states directly. A cross-layer shortcut helps when it supplies information the residual stream no longer carries cleanly. The value stream is the safest place for it, since it changes what attention moves without changing where it looks, and making the reads per token, the way attention addresses positions, gives the general form. Value residual (Zhou et al.) is the simplest case, MUDD connections (Xiao et al.) the general one.

Value residual mixes the first layer's values into every layer. With Hn−1H_{n-1} the input of layer nn and V1=H0W1VV_1=H_0W_1^V, ResFormer replaces layer nn's values by Vn′=λn,1V1+λn,2 Hn−1WnV,Un=AnVn′,V_n'=\lambda_{n,1}V_1+\lambda_{n,2}\,H_{n-1}W_n^V,\qquad U_n=A_nV_n', so layer nn's own attention pattern carries the first layer's values. On Llama-like models of 82M to 468M parameters it matches the baseline's loss with 16% fewer parameters or 20% fewer tokens (single unseeded runs). The ablations show where the shortcut must enter. The same 0.5/0.5 shortcut added to queries, keys or the attention matrix does nothing or hurts (2.742, 2.746, 2.757 against a 2.739 baseline), added to values it gives 2.712, and adding V1V_1 to the attention output with no mixing across positions is far worse (3.137). Where a deep layer attends should depend on its contextual state; a query or key shortcut brings token-level features into those scores, while a value shortcut only changes what the existing pattern moves. A hidden-state shortcut Hn′=0.5H0+0.5HnH_n'=0.5H_0+0.5H_n hurts (2.781) but helps when started at zero weight (2.722), so a hidden shortcut is safe only if its weight starts at zero.

A shortcut helps only when its source is information the residual stream no longer carries cleanly. In an 8-layer model, value shortcuts from V2V_2 or later layers give little, because the ordinary residual already carries H1H_1 forward, so V2=H1W2VV_2=H_1W_2^V is reachable anyway; when the residual chain is started at H2H_2 instead, a shortcut from V2V_2 becomes useful. So the first layer is not special in itself. Deep layers want it most (a single shortcut works best at layer 7 of 8, and learned weights grow toward the top), and the best weights depend on depth and are found by hand. The paper also ties it to attention sinks: a sink token's value norm is tiny, which makes attending to it a cheap no-op, and mixing in V1V_1 gives it a real value; ResFormer flattens the sink, though that account is correlational.

Dynamic dense connections are the general form. MUDDFormer lets each layer read, separately for each of its four inputs (query, key, value, residual), its own per-token mixture of all earlier outputs: Xˉi=∑j=0iAij⊙Xj,Ai=GELU(RMSNorm(Xi)W1)W2+ai,\bar X_i=\sum_{j=0}^{i}A_{ij}\odot X_j,\qquad A_i=\mathrm{GELU}\big(\mathrm{RMSNorm}(X_i)W_1\big)W_2+a_i, so position tt weights source layer jj by a small MLP of its current state, which is depth-wise attention with weights from the query side alone. The 405M ablation (Pile perplexity) shows where the gain comes from: Transformer++ 11.68, static dense (DenseFormer, one weight per pair of layers) 11.44, dynamic 11.09, dynamic multiway 10.83, and 10.77 after shifting FFN width toward the upper layers. Dynamic weights are the main ingredient. Removing one stream's dense reads gives 10.89 (query), 10.90 (key), 11.05 (value) and 11.14 (residual): the residual reads matter most overall and, among attention's inputs, the value stream does, as in value residual; the mechanism the paper offers is that attention lets a layer pull an old representation into its values and move it to another position without writing it into its own residual. W2W_2 starts at zero and aia_i one-hot on the current layer, so the model starts as an ordinary transformer and learns dense reads only where they help; the paper calls this critical. MUDDFormer-834M matches Transformer++ trained with 1.89 times the compute, and MUDDPythia-2.8B, changing only the connections, matches Pythia-6.9B on 300B tokens; across sizes MUDD trains at 84 to 96% of baseline throughput, since the aggregates are many small memory-bound operations.

The speedrun's paths are static special cases. Record #9 brought value residual as one scalar per layer, a form of Learnable ResFormer with the two weights tied to sum to one:
def attention(x, v1, lamb):          # lamb: learned scalar per layer, init 0.5
    q, k, v = c_q(x), c_k(x), c_v(x)
    if v1 is None:                   # first layer: its own values are V1
        v1 = v
    v = (1 - lamb) * v + lamb * v1   # value residual
    y = causal_attention(q, k, v)
    return c_proj(y), v1
Its per-change logs at 3200 steps go from 3.3274 to 3.3151 with a fixed weight of 0.5 and to 3.3065 with a learned weight, 0.021 of the 0.048 nats the record recovered; these are single runs against a run-to-run spread near 0.005, so the split is indicative. In the speedrun the first block reads the normalized token embedding, so V1=W1Vx0V_1=W_1^Vx_0 depends on the token id alone, and record #14 made the lookup explicit with value embeddings, per-layer token tables mixed into the values (1750 to 1530 steps, 4.66 to 4.41 minutes), a line of changes that continued through record #92. The paper never proposes token-indexed values; that step is the speedrun's own extrapolation. The other hand-built paths read earlier outputs with fixed weights: the embedding shortcut of #9, x←a x+b x0x\leftarrow a\,x+b\,x_0 with bb started at 0, is a two-source static dense connection and ResFormer's zero-start hidden shortcut (another 0.009); U-net skips (#11, later a single 4-to-7 skip) add one more static source, backout (#40) gives an early layer's output a learned negative weight before the head, and records #73 and #77 had late attention layers read a cached layer-7 activation while the residual stream moved on, the multiway idea with fixed sources. All of these share their weights across tokens.

Records #81 and #85: MUDD trimmed to fit a 124M model. Record #81 put MUDD on the last two layers only, with sources {x0,h7,h9}\{x_0,h_7,h_9\} and {x0,h9}\{x_0,h_9\}, only the value and residual channels (the query and key channels "contributed little" at this scale), and W2W_2 and biases at zero so it starts as a no-op. Over 10 runs each, adding it at 1430 steps matched the 1480-step baseline. Later versions folded backout in by initializing one bias so the weight on h7h_7 starts near −0.5-0.5, now per token, and replaced static scalars on layer 10 with per-token gates; in total, 1480 to 1415 steps and 84.70 to 81.91 s at about 1 ms per step of overhead. Record #85 extended this into per-token gates for several attention gates, the embedding and bigram injections and the skip, plus an attention correction on layer 10, lowering the matched-step loss by 0.0076. In #92's ablation, which converts each millinat of loss to 164 ms of training time, the MUDD mix still saves about 0.48 s net. The dynamic version replaced several hand-tuned static paths and still saved more time than its overhead cost, once reduced to a few sources, two streams and an identity start.

Terms

value residual
Vn′=λn,1V1+λn,2Hn−1WnVV_n'=\lambda_{n,1}V_1+\lambda_{n,2}H_{n-1}W_n^V, mixed before layer nn's attention
V1V_1
first layer's values, H0W1VH_0W_1^V: token-level, unmixed across positions; in the speedrun a function of the token id
record #9 losses
3200 steps: 3.3274 base, 3.3151 with value residual, 3.3065 with learned weight
value embeddings (#14 on)
per-layer token-id tables added to V; #14 went from 1750 to 1530 steps, 4.66 to 4.41 min
depth-wise aggregate
Xˉi=∑j≤iAij⊙Xj\bar X_i=\sum_{j\le i}A_{ij}\odot X_j: the next layer's input as a mixture of all earlier outputs
dynamic dense
weights Ai=GELU(RMSNorm(Xi)W1)W2+aiA_i=\mathrm{GELU}(\mathrm{RMSNorm}(X_i)W_1)W_2+a_i, computed per token; DenseFormer uses one static weight per layer pair
multiway
separate aggregates for the query, key, value and residual inputs of each block
identity init
W2=0W_2=0, aia_i one-hot on the current layer: starts as a plain transformer
per-stream removal
−Q-Q 10.89, −K-K 10.90, −V-V 11.05, −R-R 11.14 (from 10.77)
record #81
MUDD on last two layers, V and residual only; 1480 to 1415 steps, 84.70 to 81.91 s

Quiz

  1. In the speedrun, why is the first layer's value V1V_1 a function of the token id alone?
  2. Why do the same 0.5/0.5 shortcuts from the first layer hurt or do nothing when added to queries or keys, while they help when added to values?
  3. Why are W2W_2 and the static prior initialized so that MUDDFormer starts as an ordinary transformer?
  4. Removing the dense connection from one stream gives 10.89 (Q), 10.90 (K), 11.05 (V) and 11.14 (R), against 10.77. What does the table support?

Sources