<?xml version="1.0" encoding="UTF-8"?>
<rss  xmlns:atom="http://www.w3.org/2005/Atom" 
      xmlns:media="http://search.yahoo.com/mrss/" 
      xmlns:content="http://purl.org/rss/1.0/modules/content/" 
      xmlns:dc="http://purl.org/dc/elements/1.1/" 
      version="2.0">
<channel>
<title>Residual Thoughts</title>
<link>https://www.residual-thoughts.com/</link>
<atom:link href="https://www.residual-thoughts.com/index.xml" rel="self" type="application/rss+xml"/>
<description>Notes on mechanistic interpretability, geometry, and model behavior.</description>
<generator>quarto-1.10.18</generator>
<lastBuildDate>Sat, 18 Jul 2026 00:00:00 GMT</lastBuildDate>
<item>
  <title>Why Crowding Protects</title>
  <dc:creator>Shubham Srivastava</dc:creator>
  <link>https://www.residual-thoughts.com/posts/why-crowding-protects/</link>
  <description><![CDATA[ 





<p><span class="post-stamp green">paper 2</span> <span class="post-stamp green">preregistered</span> <span class="post-stamp pencil">manuscript in preparation</span></p>
<p>Train a language model on something new and it quietly gets worse at things it already knew. This is catastrophic forgetting, and one blunt but effective defense is to freeze a fraction of the network while the new training runs. The interesting question is which fraction.</p>
<p>In <a href="../how-far-can-you-read-a-model-from-its-weights/">an earlier study</a> I found something that kept bothering me. You can pick the neurons to freeze using no data at all: just look at the weight file, find the neurons whose output directions crowd together, and protect those. On the model where the protocol is cleanest, that zero-data mask recovered most of the benefit of the expensive, gradient-based alternative. A signal that has never seen a task, a token, or a gradient somehow knows which neurons the next task will damage.</p>
<p>That should strike you as suspicious. Geometry does not know what the model was trained on. Why would it know what is fragile?</p>
<p>This post is the answer, run as a detective story, because that is how the investigation actually unfolded: four natural explanations, each eliminated by a preregistered test, and then a mechanism that survives everything we could throw at it. You do not need to have read the earlier posts; the next section rebuilds the setup from scratch.</p>
<div class="callout callout-style-simple callout-note">
<div class="callout-body d-flex">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-body-container">
<p><strong>Code and provenance.</strong> Every experiment here was preregistered before running, with amendments dated and disclosed, on a frozen git history with a SHA256 manifest of all result artifacts. <a href="https://github.com/shubhamx64/residual-thoughts-blog/tree/main/code/live/e5-mechanism"><strong>The mechanism code is public</strong></a>: the curvature probes and sketches, the K2 selector, the interface decomposition, both isolation controls, the rollback surgery, and the reparameterization self-test, alongside the <a href="https://github.com/shubhamx64/residual-thoughts-blog/tree/main/code/live/e4-continual">training and analysis code</a> they build on. The preregistration trail and result artifacts ship with the Paper 2 manuscript, which is in preparation.</p>
</div>
</div>
</div>
<div class="callout callout-style-default callout-tip callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
<span class="screen-reader-only">Tip</span>TL;DR
</div>
</div>
<div class="callout-body-container callout-body">
<ul>
<li>Forgetting damage is a <strong>second-order</strong> phenomenon: at the end of training on a task, gradients are nearly flat, so damage arrives through the loss’s <em>curvature</em>, and specifically through <strong>cross-neuron (off-diagonal) curvature coupling</strong>.</li>
<li>Crowded neuron pairs carry <strong>6.1x</strong> the coupling of orthogonal pairs matched on rate, gradient size, weight norm, update size, and co-firing, in <strong>16 of 16</strong> measured layers. After normalizing away self-curvature the contrast is still <strong>5.1x</strong>: the effect is genuinely between neurons, not within them.</li>
<li>The coupling’s anatomy matches the algebra that predicts it: <strong>81%</strong> flows through read-side changes emitted along <em>existing</em> aligned write vectors, and the only pathway gated by raw co-activation carries <strong>3%</strong>, which is why co-firing tests come up empty.</li>
<li>Measuring the coupling directly (a quantity we call K2) yields the <strong>best retention selector observed in the program</strong>: better than crowding by a wide margin and better than Fisher on <strong>6/6 seeds</strong> (exact p = 0.0156).</li>
<li>The value is off-diagonal <em>specifically</em>: with self-curvature, first-order alignment, Fisher, crowding, and movement all matched (and audited), high- coupling neurons undo <strong>77%</strong> of damage when restored; matched low-coupling neurons undo <strong>18%</strong>, less than random’s 32%.</li>
<li>The account predicts its own boundary: rescale the network in function-preserving ways and protection value swings wildly, because curvature lives in the optimizer’s coordinates. Real fine-tuning holds the parameterization fixed, so the ledger applies there.</li>
</ul>
</div>
</div>
<section id="the-setup-briefly" class="level2">
<h2 class="anchored" data-anchor-id="the-setup-briefly">The setup, briefly</h2>
<p>Everything happens in the MLP blocks of a small language model (TinyLlama-1.1B). Each MLP neuron reads from the residual stream (the vector that flows through the network, accumulating each layer’s contributions), decides how strongly to fire, and then writes its contribution back through its <strong>write vector</strong>: one column of the down-projection matrix. The write vector is the neuron’s handwriting, the direction it adds to the stream.</p>
<p><strong>Crowding</strong> is a property you can compute from the weight file alone: for each neuron, take the largest absolute cosine similarity between its write vector and any other write vector in the same layer. A crowded neuron shares its output direction with a neighbor. An isolated neuron writes somewhere no one else does.</p>
<p>The experiment that produced the mystery is a two-phase curriculum. Phase A teaches the model math. Phase B trains it on code, and math quietly degrades. Before phase B starts, we freeze the top 20% of neurons per layer according to some signal, and afterwards we measure how much held-out math was lost (as negative log-likelihood, NLL: lower degradation is better). The candidate signals form a cost ladder:</p>
<table class="caption-top table">
<colgroup>
<col style="width: 33%">
<col style="width: 33%">
<col style="width: 33%">
</colgroup>
<thead>
<tr class="header">
<th>signal</th>
<th>needs</th>
<th>what it measures</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td>crowding</td>
<td>the checkpoint only</td>
<td>write-direction overlap</td>
</tr>
<tr class="even">
<td>footprint</td>
<td>one short forward capture</td>
<td>how often each neuron fires on math</td>
</tr>
<tr class="odd">
<td>Fisher</td>
<td>task data + backward passes</td>
<td>local loss sensitivity</td>
</tr>
</tbody>
</table>
<p>The <a href="../how-far-can-you-read-a-model-from-its-weights/">Paper 1 field report</a> established that every informed mask beats a random one here, and that crowding, the bottom rung, recovers 72.5% of Fisher’s retention benefit with zero data. (There is also a firm negative: crowding does <em>not</em> predict which neurons mix tasks semantically. That boundary got <a href="../price-of-packing/">its own post</a>.) What Paper 1 could not say is <em>why</em> the cheapest signal works. Time to interrogate suspects.</p>
</section>
<section id="suspect-1-a-tug-of-war-between-tasks" class="level2">
<h2 class="anchored" data-anchor-id="suspect-1-a-tug-of-war-between-tasks">Suspect 1: a tug-of-war between tasks</h2>
<p>The most intuitive story: crowded neurons are where the two tasks fight. If math and code both use a shared direction, their gradients should pull it in conflicting ways, and protecting the battleground prevents the damage.</p>
<p>This is directly measurable: correlate each neuron’s crowding with the misalignment between task gradients (the cosine between its math gradient and its code gradient, negated so that bigger means more conflict). Across three models the correlation is <strong>-0.001, -0.043, -0.020</strong>. Nothing. Crowded neurons are not gradient battlegrounds. The first-order tug-of-war does not exist at the neuron level, let alone explain the protection.</p>
</section>
<section id="suspect-2-crowding-is-secretly-fisher" class="level2">
<h2 class="anchored" data-anchor-id="suspect-2-crowding-is-secretly-fisher">Suspect 2: crowding is secretly Fisher</h2>
<p>Maybe geometry is just a blurry photocopy of loss sensitivity: crowded neurons are important neurons, and we have merely rediscovered Fisher information at a discount.</p>
<p>If so, the two signals should rank neurons similarly. They do not. Rank alignment between crowding and Fisher sits between <strong>0.1 and 0.36</strong> depending on the checkpoint, and the top-20% masks they select overlap barely above the chance floor. Crowding protects <em>while disagreeing with Fisher about which neurons matter</em>. Whatever it knows, it is not a cheap Fisher estimate.</p>
</section>
<section id="suspect-3-standing-in-the-optimizers-path" class="level2">
<h2 class="anchored" data-anchor-id="suspect-3-standing-in-the-optimizers-path">Suspect 3: standing in the optimizer’s path</h2>
<p>Perhaps crowded neurons are simply where phase B writes its update. Protection would then be trivial: freeze the neurons the optimizer was about to bulldoze.</p>
<p>Correlationally, crowding barely tracks movement (about <strong>-0.05</strong> on TinyLlama). But the causal test is better, and it is the most instructive elimination in the program. After phase B finishes, take the damaged model and surgically restore 20% of neurons to their pre-B values (a “rollback”), choosing the neurons by different signals, and see how much math comes back. Restoring the 20% that <em>moved most</em> brings back about <strong>52%</strong> of the lost math; crowding brings back <strong>67%</strong>; Fisher <strong>75%</strong>; a random set, <strong>27%</strong>. Movement is the <em>worst</em> informed selector. The damage does not live where B wrote. It lives where the signals that know something about task A point, and crowding is one of those signals.</p>
</section>
<section id="suspect-4-fire-together-forget-together" class="level2">
<h2 class="anchored" data-anchor-id="suspect-4-fire-together-forget-together">Suspect 4: fire together, forget together</h2>
<p>The last natural story: crowded neurons co-activate. If two neurons fire on the same tokens, an update that damages one context damages both, and their joint usage is the transmission channel.</p>
<p>We measured co-firing directly (how often both neurons cross their layer’s 99th-percentile activation threshold together, relative to independence) and tested whether it carries or gates the damage-relevant quantity. It does not: in a regression with geometry, co-firing and its interaction with geometry are both null (interaction permutation p = 0.67), and within every geometry stratum co-firing is inert. In case the threshold was the problem, we re-ran the test with continuous co-activation statistics. Null again. Whatever links crowded pairs, it is not that they fire at the same time.</p>
</section>
<section id="the-turn-forgetting-is-a-second-order-crime" class="level2">
<h2 class="anchored" data-anchor-id="the-turn-forgetting-is-a-second-order-crime">The turn: forgetting is a second-order crime</h2>
<p>Here is the reframe that cracks the case. At the end of phase A, training has converged: the gradient of the math loss is nearly flat. To first order, <em>nothing</em> can hurt the model. The damage from phase B’s update <img src="https://latex.codecogs.com/png.latex?%5Cdelta%5Ctheta"> arrives through the second-order term,</p>
<p><img src="https://latex.codecogs.com/png.latex?%0A%5CDelta%20L_A%20%5C;%5Capprox%5C;%20%5Ctfrac%7B1%7D%7B2%7D%5C,%20%5Cdelta%5Ctheta%5E%5Ctop%20H_A%5C,%20%5Cdelta%5Ctheta,%0A"></p>
<p>where <img src="https://latex.codecogs.com/png.latex?H_A"> is the curvature (Hessian) of the math loss. Curvature is a matrix over pairs of parameters, and that plural is the whole point. Its diagonal blocks are <strong>self-curvature</strong>: how sensitive the loss is to moving one neuron alone, which is roughly what Fisher tracks. Its off-diagonal blocks are <strong>coupling</strong>: whether moving neuron <img src="https://latex.codecogs.com/png.latex?j"> changes the price of having moved neuron <img src="https://latex.codecogs.com/png.latex?i">. If a pair couples positively, updating both together hurts more than the sum of updating each alone. Forgetting, in this frame, is not a list of individually fragile neurons. It has cross terms.</p>
<p>Work out what that coupling looks like for two MLP neurons (using the Gauss-Newton form of the curvature, the positive-semidefinite part that comes from the network’s output geometry), and the dominant term factorizes:</p>
<p><img src="https://latex.codecogs.com/png.latex?%0AS_%7Bij%7D%20%5C;%5Csim%5C;%20%5Cunderbrace%7B(w_i%20%5Ccdot%20w_j)%7D_%7B%5Ctext%7Bgeometry%20clock%7D%7D%0A%5C;%5Ctimes%5C;%0A%5Cunderbrace%7B%5Ctextstyle%5Csum_t%20m_t%5C,%20c_i(x_t)%5C,%20c_j(x_t)%7D_%7B%5Ctext%7Busage%20clock%7D%7D%0A"></p>
<p>The first factor is the overlap of the two neurons’ <em>existing</em> write vectors, which is exactly what crowding measures. The second is the co-response of their activation <em>changes</em> under the update: not whether they fire together, but whether the update moves their firing together, weighted by how much the output cares. Two clocks, one geometric and one usage-driven, multiplied.</p>
<figure aria-labelledby="hero-caption" style="margin:1.5rem 0" class="figure">
<svg viewbox="0 0 680 320" xmlns="http://www.w3.org/2000/svg" style="max-width:680px;width:100%;height:auto;font-family:inherit">
  <defs>
    <marker id="arr" viewbox="0 0 10 10" refx="8" refy="5" markerwidth="7" markerheight="7" orient="auto-start-reverse">
      <path d="M 0 0 L 10 5 L 0 10 z" style="fill:var(--write)"></path>
    </marker>
    <marker id="arrp" viewbox="0 0 10 10" refx="8" refy="5" markerwidth="6" markerheight="6" orient="auto-start-reverse">
      <path d="M 0 0 L 10 5 L 0 10 z" style="fill:var(--pencil)"></path>
    </marker>
  </defs>
  <text x="110" y="26" text-anchor="middle" font-size="12.5" font-weight="600" style="fill:var(--ink)">what the weights show</text>
  <circle cx="45" cy="120" r="13" style="fill:var(--paper-raise);stroke:var(--write);stroke-width:2"></circle>
  <text x="45" y="124" text-anchor="middle" font-size="11" style="fill:var(--ink)">i</text>
  <circle cx="45" cy="200" r="13" style="fill:var(--paper-raise);stroke:var(--write);stroke-width:2"></circle>
  <text x="45" y="204" text-anchor="middle" font-size="11" style="fill:var(--ink)">j</text>
  <line x1="60" y1="116" x2="185" y2="146" style="stroke:var(--write);stroke-width:2.2" marker-end="url(#arr)"></line>
  <line x1="60" y1="196" x2="185" y2="172" style="stroke:var(--write);stroke-width:2.2" marker-end="url(#arr)"></line>
  <text x="140" y="122" font-size="11" style="fill:var(--write)">w<tspan baseline-shift="sub" font-size="8">i</tspan></text>
  <text x="140" y="202" font-size="11" style="fill:var(--write)">w<tspan baseline-shift="sub" font-size="8">j</tspan></text>
  <text x="110" y="248" text-anchor="middle" font-size="10.5" style="fill:var(--pencil)">crowded pair:</text>
  <text x="110" y="264" text-anchor="middle" font-size="10.5" style="fill:var(--pencil)">nearly aligned write vectors</text>

  <text x="345" y="26" text-anchor="middle" font-size="12.5" font-weight="600" style="fill:var(--ink)">two clocks, multiplied</text>
  <rect x="255" y="70" width="180" height="58" rx="8" style="fill:var(--paper-raise);stroke:var(--write);stroke-width:1.6"></rect>
  <text x="345" y="94" text-anchor="middle" font-size="11.5" font-weight="600" style="fill:var(--write)">geometry clock</text>
  <text x="345" y="112" text-anchor="middle" font-size="10.5" style="fill:var(--ink)">w<tspan baseline-shift="sub" font-size="8">i</tspan> · w<tspan baseline-shift="sub" font-size="8">j</tspan> overlap of base writes</text>
  <text x="345" y="160" text-anchor="middle" font-size="17" style="fill:var(--ink)">×</text>
  <rect x="255" y="182" width="180" height="58" rx="8" style="fill:var(--paper-raise);stroke:var(--read);stroke-width:1.6"></rect>
  <text x="345" y="206" text-anchor="middle" font-size="11.5" font-weight="600" style="fill:var(--read-deep)">usage clock</text>
  <text x="345" y="224" text-anchor="middle" font-size="10.5" style="fill:var(--ink)">co-response of the update</text>
  <line x1="445" y1="155" x2="490" y2="155" style="stroke:var(--pencil);stroke-width:1.8" marker-end="url(#arrp)"></line>

  <text x="575" y="26" text-anchor="middle" font-size="12.5" font-weight="600" style="fill:var(--ink)">what forgetting feels</text>
  <rect x="510" y="90" width="65" height="65" style="fill:var(--paper-raise);stroke:var(--hairline)"></rect>
  <rect x="575" y="90" width="65" height="65" style="fill:var(--write);fill-opacity:0.28;stroke:var(--hairline)"></rect>
  <rect x="510" y="155" width="65" height="65" style="fill:var(--write);fill-opacity:0.28;stroke:var(--hairline)"></rect>
  <rect x="575" y="155" width="65" height="65" style="fill:var(--paper-raise);stroke:var(--hairline)"></rect>
  <text x="542" y="127" text-anchor="middle" font-size="11" style="fill:var(--pencil)">S<tspan baseline-shift="sub" font-size="8">ii</tspan></text>
  <text x="607" y="127" text-anchor="middle" font-size="12.5" font-weight="700" style="fill:var(--ink)">S<tspan baseline-shift="sub" font-size="9">ij</tspan></text>
  <text x="542" y="192" text-anchor="middle" font-size="12.5" font-weight="700" style="fill:var(--ink)">S<tspan baseline-shift="sub" font-size="9">ji</tspan></text>
  <text x="607" y="192" text-anchor="middle" font-size="11" style="fill:var(--pencil)">S<tspan baseline-shift="sub" font-size="8">jj</tspan></text>
  <text x="575" y="248" text-anchor="middle" font-size="10.5" style="fill:var(--pencil)">off-diagonal curvature:</text>
  <text x="575" y="264" text-anchor="middle" font-size="10.5" style="fill:var(--pencil)">joint damage beyond the sum</text>

  <text x="340" y="304" text-anchor="middle" font-size="10.5" style="fill:var(--pencil)">crowding is the zero-data shadow of the geometry clock</text>
</svg>
<figcaption id="hero-caption" class="figure-caption">The mechanism in one picture. Crowding reads the geometry factor of cross-neuron curvature straight from the weight file; forgetting pays the full product.</figcaption>
</figure>
<p>If this account is right, crowding works because it is the zero-data shadow of the geometry clock: it reads one factor of the off-diagonal curvature straight from the checkpoint. And the account makes predictions sharp enough to falsify. Crowded pairs should carry outsized coupling <em>at matched everything else</em>. The coupling should flow through the existing write vectors, not through co-activation. Measuring the coupling directly should beat every proxy of it, including Fisher. And the advantage should survive matching on every diagonal quantity. All four were registered before the tests ran.</p>
</section>
<section id="exhibit-a-crowded-pairs-carry-the-coupling" class="level2">
<h2 class="anchored" data-anchor-id="exhibit-a-crowded-pairs-carry-the-coupling">Exhibit A: crowded pairs carry the coupling</h2>
<p>Measuring off-diagonal curvature naively is hopeless (the matrix is billions-by-billions), so the estimator probes it: perturb one neuron’s parameters along its actual phase-B update direction, and read out how the curvature responds at every other neuron in the layer. Three forward passes and one backward pass per probe, validated against an exact reference implementation on a small oracle model, plus a seven-gate sanity battery. We ran 1,200 probes.</p>
<p>Then the matched contrast: take crowded pairs and orthogonal pairs that agree on firing rate, gradient magnitude, weight norm, update norm, and co-firing (202 matched contrasts), and compare their coupling.</p>
<figure class="theme-figure figure" aria-labelledby="contrast-caption">
  <img class="theme-plot theme-plot-light figure-img" src="https://www.residual-thoughts.com/posts/why-crowding-protects/figs/fig_contrast_light.png" alt="Bar chart of the crowded versus orthogonal coupling ratio per layer, on a log scale. All sixteen measured layers sit above the parity line, ranging from about 2x to 46x, with the median at 6.12x." loading="lazy">
  <img class="theme-plot theme-plot-dark figure-img" src="https://www.residual-thoughts.com/posts/why-crowding-protects/figs/fig_contrast_dark.png" alt="Bar chart of the crowded versus orthogonal coupling ratio per layer, on a log scale. All sixteen measured layers sit above the parity line, ranging from about 2x to 46x, with the median at 6.12x." loading="lazy">
  <figcaption id="contrast-caption">Crowded pairs versus matched orthogonal pairs, per layer. Treating layers as the inference unit (pairs share neurons): 16/16 above parity, one-sided sign p = 1.5e-05.</figcaption>
</figure>
<p>The median crowded pair carries <strong>6.12x</strong> the coupling of its matched orthogonal counterpart, and every one of the 16 measured layers shows the effect. The obvious deflationary reading, that crowded neurons just have more curvature of every kind, fails: normalize each pair’s coupling by its members’ self-curvature and the contrast is still <strong>5.06x</strong>. The excess is genuinely <em>between</em> the neurons.</p>
<p>Two more registered checks. Crowding correlates with a neuron’s total within-layer coupling load at <strong>+0.32</strong>, passing the registered bar; the same correlation against a <em>global</em> load that includes cross-layer terms drops to +0.18, a registered miss we report as found. Crowding is a within-layer statistic and sees within-layer load. And the coupling is <strong>polarity-blind</strong>: pairs whose write vectors point in <em>opposite</em> directions couple exactly like aligned ones (both predominantly positive, 69% and 71%). That refuted our own registered sign prediction, and it vindicates the convention of measuring crowding with absolute cosines: opponent neurons interfere like duplicates.</p>
</section>
<section id="exhibit-b-the-anatomy-matches-the-algebra" class="level2">
<h2 class="anchored" data-anchor-id="exhibit-b-the-anatomy-matches-the-algebra">Exhibit B: the anatomy matches the algebra</h2>
<p>The two-clock factorization makes a structural claim: the coupling should be carried by read-side activation changes emitted along the <em>existing</em> write vectors (the <img src="https://latex.codecogs.com/png.latex?w_i%20%5Ccdot%20w_j"> terms), not by update-write overlap gated by co-activation. That is checkable, because each pair’s coupling can be decomposed into blocks by which interface sends and which receives.</p>
<p>On the 50 registered validation pairs, the read-by-read block flowing through base write vectors carries a median <strong>0.812</strong> of each pair’s coupling mass (95% CI [0.737, 0.855]), and no single pair drives the result. The one block that raw co-activation actually gates is the smallest of all nine, at <strong>0.031</strong>. This is why Suspect 4 walked free: the tests were correct, and the channel they tested barely exists.</p>
<p>Better, the decomposition contains its own discriminating prediction. The base-write terms scale with <img src="https://latex.codecogs.com/png.latex?w_i%20%5Ccdot%20w_j">, so read-side dominance should be a property of <em>crowded</em> pairs specifically. It is: median share <strong>0.872</strong> for crowded pairs, dissolving to <strong>0.437</strong> for orthogonal ones. The algebra predicted a fingerprint and the data shows that fingerprint.</p>
</section>
<section id="exhibit-c-measure-the-coupling-and-it-beats-everything" class="level2">
<h2 class="anchored" data-anchor-id="exhibit-c-measure-the-coupling-and-it-beats-everything">Exhibit C: measure the coupling and it beats everything</h2>
<p>If off-diagonal curvature is the mechanism, then the mechanism quantity should be a better protection signal than any of its proxies. So we built one: <strong>K2</strong>, each neuron’s off-diagonal curvature row load along the actual update directions, estimated with randomized sketches. K2 needs one sacrificial unprotected run of phase B to know the update directions; it then selects the top 20% per layer, and we re-run protection with that mask on fresh data seeds. Evaluation is leakage-clean: K2 was built on one half of the held-out math set, outcomes are scored on the other half, and the mask transfers across update seeds.</p>
<figure class="theme-figure figure" aria-labelledby="selector-caption">
  <img class="theme-plot theme-plot-light figure-img" src="https://www.residual-thoughts.com/posts/why-crowding-protects/figs/fig_selector_light.png" alt="Strip plot of final held-out math NLL degradation per seed for four selectors. No protection is worst near 1.23, crowding improves to 1.09, Fisher to 0.87, and the measured coupling selector K2 is best at 0.68." loading="lazy">
  <img class="theme-plot theme-plot-dark figure-img" src="https://www.residual-thoughts.com/posts/why-crowding-protects/figs/fig_selector_dark.png" alt="Strip plot of final held-out math NLL degradation per seed for four selectors. No protection is worst near 1.23, crowding improves to 1.09, Fisher to 0.87, and the measured coupling selector K2 is best at 0.68." loading="lazy">
  <figcaption id="selector-caption">Freeze top-20%/layer by each signal during phase B; final degradation on the leakage-clean outcome half. Horizontal bars are per-arm means.</figcaption>
</figure>
<p>K2 beats crowding by <strong>-0.40 NLL</strong> and beats Fisher by <strong>-0.183 on every one of six seeds</strong> (exact one-sided p = 0.0156). The registered “strong result” band was merely matching Fisher; the data went past it. This is the strongest retention selector observed anywhere in this program, and it overlaps the crowding mask at only 0.32, so it is not crowding in disguise.</p>
<p>One fairness note, registered up front: K2 uses information Fisher does not have (that one unprotected B-run). But Suspect 3 already showed that update information <em>alone</em> is the worst selector in the program. Raw movement fails; movement weighted by curvature wins. The curvature weighting, not the data access, is what converts a B-run into the best mask.</p>
</section>
<section id="exhibit-d-the-value-is-off-diagonal-specifically" class="level2">
<h2 class="anchored" data-anchor-id="exhibit-d-the-value-is-off-diagonal-specifically">Exhibit D: the value is off-diagonal, specifically</h2>
<p>The remaining skeptical reading: maybe K2-high neurons are just fragile in every measurable way, and “off-diagonal” is along for the ride. Our first attempt at this control was itself caught by an adversarial audit: the matched sets turned out unbalanced on exact self-curvature and first-order alignment, so we relabeled that result and registered a stricter design with a stop-if-confounded gate.</p>
<p>Version 2 uses a near-exact diagonal estimator (agreement with exact self-curvature: rho 0.993) and builds three sets of 28,443 neurons matched on crowding, self-curvature, first-order alignment, Fisher, and movement, with a post-construction audit on exact quantities. The audit passed with the balance tilting slightly <em>against</em> the hypothesis (the high set’s self-curvature is 0.84x the low set’s). Then the causal test: restore each set to its pre-B values inside the damaged model and measure how much math returns.</p>
<figure class="theme-figure figure" aria-labelledby="isolation-caption">
  <img class="theme-plot theme-plot-light figure-img" src="https://www.residual-thoughts.com/posts/why-crowding-protects/figs/fig_isolation_light.png" alt="Bar chart of splice recovery for three matched sets. High off-diagonal coupling recovers 0.768 of the damage, size-matched random recovers 0.323, and matched low coupling recovers 0.184." loading="lazy">
  <img class="theme-plot theme-plot-dark figure-img" src="https://www.residual-thoughts.com/posts/why-crowding-protects/figs/fig_isolation_dark.png" alt="Bar chart of splice recovery for three matched sets. High off-diagonal coupling recovers 0.768 of the damage, size-matched random recovers 0.323, and matched low coupling recovers 0.184." loading="lazy">
  <figcaption id="isolation-caption">Restore each set to pre-damage values and measure the fraction of held-out math damage undone. Same crowding, same self-curvature, same first-order alignment, same Fisher, same movement; only the off-diagonal load differs.</figcaption>
</figure>
<p>High-coupling neurons undo <strong>+0.768</strong> of the damage. Their matched-on-everything-else low-coupling twins undo <strong>+0.184</strong>, <em>less than a random set</em> (+0.323). With every diagonal explanation held fixed and audited, the off-diagonal information alone separates recovery by +0.58. This is the finding the whole case rests on, and it survived the audit that killed its first version.</p>
<p>Two smaller pieces round out the picture. The neuron behaves as the natural unit: freezing its three weight matrices contributes protection almost exactly additively (shares 0.39 + 0.13 + 0.44, summing to 0.96), with no single interface acting as the mechanism’s home. And nothing here says forgetting is <em>only</em> off-diagonal; self-curvature is real, which is why Fisher is a strong selector. The claim is that the cross terms carry protection-relevant information that no diagonal signal contains, and that crowding is a free estimate of where those cross terms concentrate.</p>
</section>
<section id="the-confession-doubles-as-a-prediction" class="level2">
<h2 class="anchored" data-anchor-id="the-confession-doubles-as-a-prediction">The confession doubles as a prediction</h2>
<p>A good mechanism should tell you where it stops working. This one does.</p>
<p>Curvature in the optimizer’s coordinates is not a property of the abstract function the network computes. You can rescale a SwiGLU neuron (multiply its input row by a power of two, divide its output column by the same factor) and the network’s outputs do not change at all; with power-of-two factors the logits are bitwise identical. But the curvature account says the <em>damage process</em> should change, because the rescaling scales one damage pathway’s curvature by <img src="https://latex.codecogs.com/png.latex?%5Calpha%5E2"> and the other’s by <img src="https://latex.codecogs.com/png.latex?%5Calpha%5E%7B-2%7D">.</p>
<p>So we ran the final registered experiment as a self-test: if protection value is invariant under these function-preserving rescalings, the curvature account is wrong. It is not invariant. Rescaling the protected neurons by 4 or by 1/4 raises protection value by <strong>+163%</strong> and <strong>+99%</strong>; a global random rescale swings it by <strong>-126%</strong> (mostly by stalling learning: Adam’s per-parameter state is calibrated to the old scales). All three land far outside the registered +-25% invariance band, in the direction the algebra points.</p>
<p>The scope condition, stated plainly: crowding’s protection value is a joint property of the computation’s geometry <em>and</em> the optimizer’s coordinates, not of the function alone. Any deployment claim must hold the parameterization fixed. Real fine-tuning does exactly that, so the ledger applies where it is used; but nobody should carry these numbers across a reparameterization.</p>
<div class="callout callout-style-default callout-important callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
<span class="screen-reader-only">Important</span>Boundaries
</div>
</div>
<div class="callout-body-container callout-body">
<p>The causal chain is established in one primary regime: TinyLlama-1.1B, one math-to-code curriculum, 20% freezing budget, neuron granularity. The curvature object is the Gauss-Newton form (concordance with the full Hessian +0.67 on large entries; indefiniteness of the true Hessian documented). Crowding tracks within-layer coupling load (+0.32) but not the global cross-layer version (+0.18, a registered miss). The K2-versus-crowding paired comparison sits at the n = 4 exact-test floor (p = 0.0625); K2-versus-Fisher is the conventionally significant one (n = 6, p = 0.0156). On Qwen2.5-1.5B under a healthy validated protocol, total forgetting is 0.003 NLL, so there is nothing to dissect there; the aggressive-protocol Qwen results are scoped to that regime. “Polarity has no effect” means no <em>detected</em> effect. All amendments, gate stops, and the confounded first isolation control are disclosed in the preregistration trail.</p>
</div>
</div>
</section>
<section id="the-ledger-upgraded" class="level2">
<h2 class="anchored" data-anchor-id="the-ledger-upgraded">The ledger, upgraded</h2>
<p>Where does this leave the practical picture from Paper 1?</p>
<p>The two-signal ledger (weights as a structural prior, a small activation capture as calibration) turns out to have been approximating the two factors of off-diagonal curvature all along: the geometry clock and the usage clock. That reframe explains the program’s stubborn observations in one stroke. Why does zero-data crowding protect? It reads the geometry factor. Why does it protect while disagreeing with Fisher? Fisher is diagonal; the information is in the cross terms. Why did every co-firing test come up null? The co-activation-gated pathway carries 3% of the coupling. And why is protection value fragile under reparameterization? Because curvature is.</p>
<p>It also hands the next project its target. K2, the best selector we have ever measured, costs a full sacrificial training run. The registered follow-up question is a ladder: how close can you get to K2’s value with zero lookahead (a block-coherence ledger built from the weight file), or with 1, 4, or 16 early microbatches of the new task? The decomposition already tells us where to look: read-side co-response against the write-vector Gram matrix, not raw firing statistics. That allocator, a capacity ledger that knows what the next task is about to break before it breaks it, is Paper 3.</p>
<p>The earlier post ended by admitting that why crowding helps at all remained open. It is no longer open. Crowding helps because it is the visible half of the quantity that forgetting actually uses: the off-diagonal curvature that couples a neuron’s fate to its neighbors’. The weights were never a complete ledger. But on this question they were telling the truth, and now we know which truth.</p>


</section>

 ]]></description>
  <category>mechanistic-interpretability</category>
  <category>continual-learning</category>
  <category>weight-geometry</category>
  <category>curvature</category>
  <category>preregistration</category>
  <guid>https://www.residual-thoughts.com/posts/why-crowding-protects/</guid>
  <pubDate>Sat, 18 Jul 2026 00:00:00 GMT</pubDate>
  <media:content url="https://www.residual-thoughts.com/posts/why-crowding-protects/figs/fig_selector_light.png" medium="image" type="image/png" height="78" width="144"/>
</item>
<item>
  <title>How Far Can You Read a Model From Its Weights?</title>
  <dc:creator>Shubham Srivastava</dc:creator>
  <link>https://www.residual-thoughts.com/posts/how-far-can-you-read-a-model-from-its-weights/</link>
  <description><![CDATA[ 





<p><span class="post-stamp green">paper 1</span> <span class="post-stamp green">five-seed interventions</span> <span class="post-stamp pencil">manuscript in preparation</span></p>
<p>There is a tempting version of mechanistic interpretability in which the weights tell you everything. Find the crowded directions, the high-gain blocks, the near-duplicates, and you could read a model’s capacity ledger straight from the checkpoint file: no replay data, no gradients, not even a forward pass. If that worked, a continually adapted model could know what not to overwrite simply by inspecting itself.</p>
<p>Paper 1 asks how far that shortcut actually goes. I took the zero-data weight signal into the field and made it compete with two better-informed rivals: a four-minute activation capture, and a gradient-based reference that gets to see the task data. They compete on four operational questions. Which neurons work together? Which ones are risky to update? Which ones mix tasks? Which ones deserve scarce precision?</p>
<p>The answer is useful precisely because it is not clean. The shortcut comes back from the field bruised but alive: one outright win, one demotion to a tradeoff, one firm wall, and one race it loses to a cheaper rival. Where each of those happens is the story of this post.</p>
<p>You do not need to have read anything else here; the next section builds the setup from scratch. This is the blog version of the Paper 1 manuscript while the arXiv submission is being prepared, and it emphasizes the operational picture rather than reproducing the paper section by section.</p>
<div class="callout callout-style-simple callout-note">
<div class="callout-body d-flex">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-body-container">
<p><a href="https://github.com/shubhamx64/residual-thoughts-blog/tree/main/code/live"><strong>Code snapshot</strong></a>: the footprint capture, weight-geometry, co-use, sequential-update, census, and quantization pipelines. The mirror now also carries <a href="../why-crowding-protects/">Paper 2’s mechanism code</a>; this post is Paper 1 only.</p>
</div>
</div>
</div>
<div class="callout callout-style-default callout-tip callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
<span class="screen-reader-only">Tip</span>TL;DR
</div>
</div>
<div class="callout-body-container callout-body">
<ul>
<li>A four-minute activation capture per model (the <strong>footprint</strong>) is stable within a text regime, with centered cosine about <strong>0.99</strong>, and identifies the regime from a single sequence at 98-100%. Raw token counts do not reproduce its geometry.</li>
<li>Neurons whose write directions crowd together really do fire together: crowded pairs co-activate at <strong>1.6-3.5x</strong> what independence predicts. But all static weight features combined explain only <strong>3.5-17%</strong> of realized co-use.</li>
<li>On TinyLlama, freezing the most crowded 20% of neurons beats a random mask on both retention and new-task learning, recovering <strong>72.5%</strong> of Fisher’s retention benefit with zero task data. The cheap footprint does better still, at 84.4%.</li>
<li>On Qwen, under a healthy validated protocol, no mask beats random on both axes: more protection means less new-task learning. A frontier, not a continual-learning win.</li>
<li>Scale does not close the gap. From Qwen2.5 <strong>1.5B to 14B</strong>, the weight-only signal stays weakly aligned with Fisher.</li>
<li>The firmest negative: crowding does <strong>not</strong> rank task-regime mixing (<a href="../price-of-packing/">Post 3</a> has the preregistered null in full). And below four bits of precision, a short activation capture carries information that static weight geometry misses.</li>
</ul>
</div>
</div>
<section id="the-setup-briefly" class="level2">
<h2 class="anchored" data-anchor-id="the-setup-briefly">The setup, briefly</h2>
<p>The cast is four open checkpoints, small enough to dissect on one machine: Qwen2.5-1.5B, Gemma-2-2B, Pythia-1.4B, and TinyLlama-1.1B. Everything here happens in their MLP blocks. Each MLP neuron reads from the residual stream (the vector that flows through the network, accumulating each layer’s contributions), decides how strongly to fire, and writes its contribution back along its <strong>write vector</strong>: one column of the down-projection matrix, with normalization gain folded in where needed. The write vector is the direction the neuron adds to the stream.</p>
<p>The zero-data instrument is <strong>crowding</strong>. For each neuron, take its write vector and ask how close it sits to its same-layer neighbours: the largest absolute cosine similarity against any other write vector in the layer. A crowded neuron shares its output direction with a neighbour; an isolated neuron writes somewhere no one else does. Nothing but the checkpoint file enters this number. No examples, no labels, no activations, no gradients.</p>
<p>The cheap usage instrument is the <strong>footprint</strong>. Run the model briefly over a small corpus spanning five kinds of text, which I will call regimes: math answers, math prose, code, code prose, and ordinary prose. For each neuron, record how often its activation lands in its layer’s top 1% (the 99th percentile, hence q99). Those five firing rates are the neuron’s footprint. The full capture is roughly 375,000 tokens and takes about four minutes per model on the test machine.</p>
<p>The expensive reference is <strong>diagonal Fisher information</strong>: backward passes on the retained task estimate which parameters the loss is locally sensitive to. It is not ground truth, but it is the data-rich rung that the cheap instruments are trying to approximate.</p>
<table class="caption-top table">
<colgroup>
<col style="width: 33%">
<col style="width: 33%">
<col style="width: 33%">
</colgroup>
<thead>
<tr class="header">
<th>rung</th>
<th>needs</th>
<th>what it knows</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td>weight crowding</td>
<td>checkpoint only</td>
<td>local geometric overlap</td>
</tr>
<tr class="even">
<td>firing footprint</td>
<td>one short forward capture</td>
<td>distribution-conditioned usage</td>
</tr>
<tr class="odd">
<td>diagonal Fisher</td>
<td>task data + backward passes</td>
<td>local loss sensitivity</td>
</tr>
</tbody>
</table>
<p>The central mistake to avoid is collapsing these into one concept called “importance.” They measure different things. The experiments ask when those things happen to agree.</p>
</section>
<section id="first-try-to-break-the-instrument" class="level2">
<h2 class="anchored" data-anchor-id="first-try-to-break-the-instrument">First, try to break the instrument</h2>
<p>Before trusting footprints to protect or quantize anything, I tried to break them. Split a regime in half and compare the halves: the centered footprint cosine stays near <strong>0.99</strong> at every layer, essentially the resampling noise floor. Compare different regimes instead and the worst layer in each model still clears a three-sigma separation criterion by 31-144x. Shuffle the labels and the margin collapses to zero. The instrument held.</p>
<p>A nearest-centroid classifier identifies the regime from a single sequence’s footprint at <strong>98-100%</strong>. That accuracy by itself is not impressive; classifiers built on raw token counts also saturate on these datasets. The more useful test is placement. Plain-English math questions and code descriptions sit closer to their computational siblings in footprint space than token statistics predict, in all four checkpoints, with net swings of +0.16 to +0.75 for math prose and +0.29 to +0.66 for code prose. The footprint knows something the word counts do not.</p>
<p>It is not a pure readout of “computation,” though. The footprint still knows about dataset and register, and footprints taken while the model generates text drift well beyond the reading noise floor, so reading and generation need separate references. The claim is narrower: a tiny forward capture produces stable, useful telemetry that raw token counts do not reproduce.</p>
</section>
<section id="trial-1-which-neurons-work-together" class="level2">
<h2 class="anchored" data-anchor-id="trial-1-which-neurons-work-together">Trial 1: which neurons work together?</h2>
<p>The weight map finds a striking worst case everywhere. Across checkpoints, the 99th percentile of pairwise write overlap is only 0.06-0.17: the bulk of the dictionary looks roomy. Yet the single largest coherence reaches <strong>0.78-1.00</strong>. Near-duplicate write directions exist at every depth in every model.</p>
<p>Those overlaps are operational, not decorative. Roughly 500 pairs per layer were stratified by write cosine and checked against real joint activity. Near-orthogonal pairs fire together at or below what independence predicts, with median lift 0.71-1.06. Crowded pairs fire together at <strong>1.6-3.5x</strong> independence, and the top decile reaches 7-59x. Training has not quietly arranged overlapping writes so that they take turns.</p>
<p>So the geometry is real. It is just not sufficient. Add reader overlap and base firing rates to build the full static model, and it explains only <strong>3.5-17%</strong> of co-use rank variance. Gemma-2 is the most weight-legible case: its reader-set overlap reaches Spearman <img src="https://latex.codecogs.com/png.latex?%5Crho=0.38"> and largely absorbs the incremental geometry term, yet even there the ceiling is 17%. On Qwen, Pythia, and TinyLlama, reader overlap adds little.</p>
<p>One family difference deserves a note. About half the crowded pairs in gated MLPs are anti-parallel opponents (<img src="https://latex.codecogs.com/png.latex?+v"> and <img src="https://latex.codecogs.com/png.latex?-v">), versus 18% in plain-GELU Pythia. Simultaneous overlap is real; whether it is redundancy, cancellation, or harmful interference cannot be read from an absolute cosine alone.</p>
</section>
<section id="trial-2-which-neurons-are-risky-to-update" class="level2">
<h2 class="anchored" data-anchor-id="trial-2-which-neurons-are-risky-to-update">Trial 2: which neurons are risky to update?</h2>
<p>Correlation is cheap, so the centerpiece is causal. Every arm starts from the same checkpoint that has just learned math, then trains its MLP weights on code. Before the code training begins, 20% of neurons per layer are frozen according to one rule: none, random, weight crowding, math footprint, crowding times footprint, or Fisher. The zero- and low-data masks are captured at the base checkpoint and stay fixed. Only Fisher is computed after math.</p>
<p>Both outcomes are reported, because either one alone can lie. <strong>Less math degradation</strong> means better retention; <strong>more held-out code improvement</strong> (code the model never trained on) means better acquisition. Calling a mask good because it forgets less, while hiding that it also learns less, would bury the central tradeoff.</p>
<figure class="theme-figure figure" aria-labelledby="tradeoff-caption">
  <img class="theme-plot theme-plot-light figure-img" src="https://www.residual-thoughts.com/posts/how-far-can-you-read-a-model-from-its-weights/figs/fig_tradeoff_light.png" alt="Two-panel retention versus acquisition plot. On TinyLlama, informed selectors lie below and to the right of random. On Qwen, selectors form a protection versus learning frontier." loading="lazy">
  <img class="theme-plot theme-plot-dark figure-img" src="https://www.residual-thoughts.com/posts/how-far-can-you-read-a-model-from-its-weights/figs/fig_tradeoff_dark.png" alt="Two-panel retention versus acquisition plot. On TinyLlama, informed selectors lie below and to the right of random. On Qwen, selectors form a protection versus learning frontier." loading="lazy">
  <figcaption id="tradeoff-caption">Five-seed means with 95% seed-bootstrap intervals. Lower-right is better. TinyLlama gives a Pareto improvement over random; validation-gated Qwen gives a stability–plasticity frontier. Each theme uses a separately rendered high-contrast asset.</figcaption>
</figure>
<section id="tinyllama-the-shortcuts-best-day" class="level3">
<h3 class="anchored" data-anchor-id="tinyllama-the-shortcuts-best-day">TinyLlama: the shortcut’s best day</h3>
<p>At step 100, before repeated passes over the small code corpus begin to overfit, every arm improves held-out code. And here all four informed selectors beat the equal-budget random mask on both axes at once.</p>
<table class="caption-top table">
<thead>
<tr class="header">
<th>arm</th>
<th style="text-align: right;">math degradation</th>
<th style="text-align: right;">code change</th>
<th style="text-align: right;">Fisher retention recovered</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td>Fisher</td>
<td style="text-align: right;">+42.7%</td>
<td style="text-align: right;">−39.5%</td>
<td style="text-align: right;">100.0%</td>
</tr>
<tr class="even">
<td>footprint</td>
<td style="text-align: right;">+51.1%</td>
<td style="text-align: right;">−39.4%</td>
<td style="text-align: right;">84.4%</td>
</tr>
<tr class="odd">
<td>join</td>
<td style="text-align: right;">+51.5%</td>
<td style="text-align: right;">−39.5%</td>
<td style="text-align: right;">83.6%</td>
</tr>
<tr class="even">
<td>weights</td>
<td style="text-align: right;">+57.4%</td>
<td style="text-align: right;">−38.4%</td>
<td style="text-align: right;">72.5%</td>
</tr>
<tr class="odd">
<td>random</td>
<td style="text-align: right;">+77.3%</td>
<td style="text-align: right;">−33.9%</td>
<td style="text-align: right;">35.2%</td>
</tr>
<tr class="even">
<td>baseline</td>
<td style="text-align: right;">+96.3%</td>
<td style="text-align: right;">−25.4%</td>
<td style="text-align: right;">0.0%</td>
</tr>
</tbody>
</table>
<p>The weight-only mask cuts math degradation by <strong>20.0 percentage points</strong> relative to random while improving code by another 4.5 points. Across five phase-B data-order seeds it recovers <strong>72.5% [70.7, 74.3]</strong> of Fisher’s retention benefit, without ever seeing a single token of task data. The forward-only footprint is stronger still at 84.4% and nearly matches Fisher on code learning.</p>
<p>That is the clean positive result. Raw geometry contains an intervention-relevant signal, and cheap usage adds more.</p>
</section>
<section id="qwen-the-complication" class="level3">
<h3 class="anchored" data-anchor-id="qwen-the-complication">Qwen: the complication</h3>
<p>The first Qwen schedule was too aggressive. Held-out code worsened from the first evaluation in every arm, which means the impressive-looking mask ranking it produced was only a damage stress test, not evidence of continual learning. I repaired the protocol before looking at any protected-arm endpoints: a frozen train/validation split, a tenfold lower learning rate, and validation-selected checkpoints within fixed budgets. Both the math acquisition gate and the phase-B code gate passed.</p>
<p>Under that healthy protocol, every arm learns code, and none of the informed masks is Pareto-superior to random. The weight-only mask reduces math degradation from <strong>0.228% to 0.147%</strong>, but gives up 0.689 points of code improvement. Fisher retains best and learns least. The unprotected baseline learns most and forgets most.</p>
<p>The honest reading is a stability-plasticity frontier, not a win. Fixed-step retention alone cannot distinguish “protected old knowledge” from “the optimizer was prevented from learning as much.” A matched-update or matched-acquisition design would be needed to make the stronger claim.</p>
</section>
<section id="does-scale-rescue-the-shortcut" class="level3">
<h3 class="anchored" data-anchor-id="does-scale-rescue-the-shortcut">Does scale rescue the shortcut?</h3>
<p>Perhaps geometry is merely noisy at 1-2B parameters and becomes Fisher-like in larger models. Across the tested Qwen2.5 series, it does not. The median rank agreement between crowding and Fisher is <strong>+0.12, +0.10, +0.17, +0.13</strong> at 1.5B, 3B, 7B, and 14B. The top-20% salient-set overlap stays at 0.17-0.20, only modestly above the 0.11 chance floor.</p>
<figure class="theme-figure figure" aria-labelledby="scale-caption">
  <img class="theme-plot theme-plot-light figure-img" src="https://www.residual-thoughts.com/posts/how-far-can-you-read-a-model-from-its-weights/figs/fig_scale_light.png" alt="Two-panel Qwen scale plot showing weak, non-monotonic crowding to Fisher correlation and flat top-20-percent overlap from 1.5B to 14B." loading="lazy">
  <img class="theme-plot theme-plot-dark figure-img" src="https://www.residual-thoughts.com/posts/how-far-can-you-read-a-model-from-its-weights/figs/fig_scale_dark.png" alt="Two-panel Qwen scale plot showing weak, non-monotonic crowding to Fisher correlation and flat top-20-percent overlap from 1.5B to 14B." loading="lazy">
  <figcaption id="scale-caption">Static alignment at the base checkpoint. The 7B point is the high point, not the start of a trend: 14B falls back. The intervention itself recovers less of Fisher at 3B and has too little arm separation to identify recovery at 7B.</figcaption>
</figure>
<p>Under the fixed stress protocol, weight-only recovery falls from 90% at Qwen 1.5B to about 20% at 3B, and at 7B the baseline and Fisher arms barely separate, so the recovery ratio is not identifiable there. This is not a scaling law; the effective update and task competence are not matched across sizes. But it is clear evidence against the hopeful claim that the proxy gap closes over the tested range.</p>
</section>
</section>
<section id="trial-3-which-neurons-mix-tasks" class="level2">
<h2 class="anchored" data-anchor-id="trial-3-which-neurons-mix-tasks">Trial 3: which neurons mix tasks?</h2>
<p>This trial ends at a wall, and the wall is the program’s firmest fact. It gets only a summary here because <a href="../price-of-packing/">Post 3</a> reports it in full. Neuron crowding does <strong>not</strong> provide a practically useful rank of task-regime mixing on either MLP interface in any checkpoint: all eight primary measurements sit inside the pre-registered <img src="https://latex.codecogs.com/png.latex?%7C%5Crho%7C%3C0.10"> null band.</p>
<p>The pipeline itself is not to blame. The same code recovers a planted law at <strong>+0.52</strong>, and its entropy proxy tracks known mixing in a trained toy model at +0.83. The null is about the world, not the instrument. Weight geometry can predict an intervention without yielding a semantic atlas.</p>
</section>
<section id="trial-4-which-neurons-deserve-scarce-precision" class="level2">
<h2 class="anchored" data-anchor-id="trial-4-which-neurons-deserve-scarce-precision">Trial 4: which neurons deserve scarce precision?</h2>
<p>Quantization supplies a different actuator and a useful consistency check. Keep every weight at four bits, then give eight bits to the top 1% of neurons under each signal. Random protection recovers nothing. Usage and gradient signals recover <strong>47-85%</strong> of the four-bit damage. And because quantization noise is not conditioned on any task, the footprint gets promoted here: it recovers about 85% of the gap on TinyLlama and can match or beat the gradient-guided rung on these checkpoints.</p>
<p>With sequential GPTQ (a standard error-correcting quantizer), the ranking is nearly saturated at four bits; every rung lands within 0.16 perplexity. Differences emerge at three bits and below, where activation energy recovers <strong>55%, 12%, and 19%</strong> of the three-bit damage at Qwen2.5 1.5B, 3B, and 7B. None of this is a new quantizer. It is a practical hint: when the precision budget is genuinely tight, a short activation capture carries information that static weight geometry misses.</p>
</section>
<section id="the-ledger-after-contact-with-data" class="level2">
<h2 class="anchored" data-anchor-id="the-ledger-after-contact-with-data">The ledger after contact with data</h2>
<p>The zero-data dream does not survive intact. It does not collapse either.</p>
<p>Weight geometry comes out of the trials as <strong>a prior, not a verdict</strong>. It locates crowded writes, predicts elevated co-use, and produces a causally useful protection mask in one clean checkpoint. It does not replace observed usage or loss sensitivity, and it does not read task semantics at all. The footprint, meanwhile, turns out to be an unusually good middle rung: stable, distribution-conditioned, cheap enough to refresh, and often competitive with gradient-based signals whenever the intervention depends on usage magnitude.</p>
<p>The Qwen repair is the part I would most want a practitioner to carry away. Pair every retention number with an acquisition number, because protection that merely stops the optimizer from learning is not protection. The repair changed the story from “geometry protects” to “geometry moves the operating point along a frontier.” That is still useful. It is a different claim.</p>
<p>And heterogeneity is the rule, not the exception. Gemma-2 is unusually weight-legible, TinyLlama gives the clean Pareto result, Qwen gives a frontier. Architecture, scale, tokenizer, and training recipe are all confounded across these checkpoints, so none of this is a family law.</p>
<p>The practical design I carry forward is a two-signal ledger: use the weights as the always-available structural prior, calibrate them with a small footprint captured in the execution mode you care about, and escalate to gradients only when the decision is important enough to pay for them.</p>
<p>One question survives every trial untouched. Why does crowding help at all? A signal that has never seen a token somehow points at the neurons the next task will damage, and nothing in Paper 1 explains that. The trials narrow the possibilities; they do not settle the mechanism. That question belongs to Paper 2, and to <a href="../why-crowding-protects/">a later post</a>.</p>


</section>

 ]]></description>
  <category>mechanistic-interpretability</category>
  <category>mlp-neurons</category>
  <category>continual-learning</category>
  <category>weight-geometry</category>
  <category>activation-footprints</category>
  <guid>https://www.residual-thoughts.com/posts/how-far-can-you-read-a-model-from-its-weights/</guid>
  <pubDate>Tue, 14 Jul 2026 00:00:00 GMT</pubDate>
  <media:content url="https://www.residual-thoughts.com/posts/how-far-can-you-read-a-model-from-its-weights/figs/fig_tradeoff_light.png" medium="image" type="image/png" height="76" width="144"/>
</item>
<item>
  <title>Polysemanticity Is Not the Price of Packing</title>
  <dc:creator>Shubham Srivastava</dc:creator>
  <link>https://www.residual-thoughts.com/posts/price-of-packing/</link>
  <description><![CDATA[ 





<p><span class="post-stamp green">pre-registered</span> <span class="post-stamp green">null result</span> <span class="post-stamp green">positive control passed</span></p>
<p>Superposition theory has a folk corollary that almost everyone reaches for at some point: the neurons that <strong>mix meanings</strong> ought to be the ones whose weight vectors <strong>crowd together</strong>, because crowding is what packing many features into few dimensions looks like. It is a clean, testable claim, and if it held, weight geometry alone would hand you a zero-data polysemanticity map – find the crowded neurons, you have found the mixed ones.</p>
<p>These are lab notes on testing that claim directly. Dictionary-free (no SAE), pre-registered, on both faces of the MLP, across four model families spanning gated and plain architectures. The short version: it is false at neuron granularity, on both interfaces, in every family – and because a null is only worth as much as the instrument behind it, I built a positive control that recovers the same law at <img src="https://latex.codecogs.com/png.latex?%5Crho%20=%20+0.52"> when the law is planted by construction.</p>
<div class="callout callout-style-simple callout-note">
<div class="callout-body d-flex">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-body-container">
<p><a href="https://github.com/shubhamx64/residual-thoughts-blog/tree/main/code/price-of-packing"><strong>Code</strong></a> – the census (<code>census.py</code>, <code>census_read.py</code>), the statistics (<code>stats_e5.py</code>), and the two toy controls (<code>planted_control.py</code>, <code>toy_control.py</code>). All quantities come from a ~4-minute forward capture per model plus the weights; no activations are stored.</p>
</div>
</div>
</div>
<div class="callout callout-style-default callout-tip callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
<span class="screen-reader-only">Tip</span>TL;DR
</div>
</div>
<div class="callout-body-container callout-body">
<ul>
<li><strong>The claim:</strong> in superposition, geometrically crowded neurons should be the polysemantic ones. Made measurable without a dictionary – mixing as regime entropy, crowding as neighbourhood density – and tested with a partial correlation that removes the firing-rate confound.</li>
<li><strong>Write interface (output directions):</strong> median partial <img src="https://latex.codecogs.com/png.latex?%5Crho"> between mixing and crowding is <strong>+0.00 to +0.05</strong> across four families. Null.</li>
<li><strong>Read interface (input directions):</strong> the last place a neuron-level law could hide, since recruitment is decided on the read side. <strong>-0.005 to +0.033</strong>. Also null.</li>
<li><strong>The two interfaces disagree per neuron</strong> (<img src="https://latex.codecogs.com/png.latex?%5Crho"> 0.21–0.47), so there is no single “this neuron is crowded” for mixing to track. That decorrelation is a finding, not a footnote.</li>
<li><strong>The only above-noise effects flip sign between families</strong> – Gemma-2’s isolated neurons are its cleanest, TinyLlama’s are its most mixed. That is the signature of noise around zero, not a weak law.</li>
<li><strong>Positive control:</strong> the identical statistic recovers a <em>planted</em> crowding-&gt;mixing law at <strong>+0.52</strong>, and the coarse entropy proxy tracks ground-truth neuron mixing at <strong>+0.83</strong> in a trained toy. The instrument is not blind; there is simply nothing to see in the real models.</li>
<li><strong>Scope:</strong> neurons, not features; task-regime mixing, not fine semantics; MLP, not attention. This bounds a shortcut, it does not refute feature-level superposition.</li>
</ul>
</div>
</div>
<section id="the-claim-made-measurable-without-a-dictionary" class="level2">
<h2 class="anchored" data-anchor-id="the-claim-made-measurable-without-a-dictionary">The claim, made measurable without a dictionary</h2>
<p>In superposition a network represents more features than it has dimensions by letting feature directions overlap. The intuitive reading at the <em>neuron</em> level: a neuron in a geometrically crowded neighbourhood is being asked to serve many masters, so it should fire across many unrelated contexts – it should be polysemantic.</p>
<p>Both sides become concrete without ever touching an SAE. Mixing is the Shannon entropy of a neuron’s firing distribution over five task regimes (math, math-prose, code, code-prose, prose), with per-class rates normalised by token counts first:</p>
<p><img src="https://latex.codecogs.com/png.latex?H_i%20=%20-%5Csum_%7Bc%7D%20p_%7Bic%7D%5C,%5Clog_2%20p_%7Bic%7D,%20%5Cqquad%20p_%7Bic%7D%20=%20%5Cfrac%7Br_%7Bic%7D%7D%7B%5Csum_%7Bc'%7D%20r_%7Bic'%7D%7D."></p>
<p>Crowding is the neighbourhood density of a neuron’s weight vector – the number of same-layer neurons within <img src="https://latex.codecogs.com/png.latex?%7C%5Ccos%7C%20%3E%200.4"> of it (with <img src="https://latex.codecogs.com/png.latex?%5Cmax%7C%5Ccos%7C"> and the mean of the top-10 neighbours as secondaries).</p>
<p>Every correlation below is a <strong>partial</strong> Spearman <img src="https://latex.codecogs.com/png.latex?%5Crho">, controlling for log firing rate. A busy neuron is mechanically both more mixed and more likely to look crowded; rank-residualising on rate before correlating removes that confound. Neurons with fewer than 50 firing events are excluded. Seeds fixed; four families – Qwen2.5-1.5B, Gemma-2-2B, Pythia-1.4B, TinyLlama-1.1B – chosen so a Gemma-only quirk cannot masquerade as a law.</p>
</section>
<section id="everything-hugs-zero-on-both-faces-of-the-neuron" class="level2">
<h2 class="anchored" data-anchor-id="everything-hugs-zero-on-both-faces-of-the-neuron">Everything hugs zero – on both faces of the neuron</h2>
<div class="callout callout-style-default callout-important callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
<span class="screen-reader-only">Important</span>Verdict: null, 4/4 families, both interfaces
</div>
</div>
<div class="callout-body-container callout-body">
<p>The median-across-layers partial <img src="https://latex.codecogs.com/png.latex?%5Crho"> between mixing and crowding is <strong>+0.00 to +0.05 on the write interface</strong> (down-projection columns) and <strong>-0.005 to +0.033 on the read interface</strong> (gate-projection rows) – every one inside the pre-registered <img src="https://latex.codecogs.com/png.latex?%7C%5Crho%7C%20%3C%200.10"> null band. No couple-type stratum separates by more than 0.12 bits at matched firing rate. Robust across firing thresholds (q98/q99.5), a four-class entropy variant, and an outside-top-class proxy; the null <em>strengthens</em> when universal-neuron candidates are removed.</p>
</div>
</div>
<figure style="margin:1.6rem 0;padding:0;" class="figure">
<svg viewbox="0 0 680 340" xmlns="http://www.w3.org/2000/svg" style="max-width:680px;width:100%;height:auto;font-family:inherit"><text x="176" y="20" font-size="13.5" font-weight="600" fill="#16202b">Partial ρ(regime mixing, crowding | firing rate) – eight real measurements, one positive control</text><rect x="229.8" y="40" width="107.6" height="260.0" fill="#eef1f3"></rect><line x1="283.6" x2="283.6" y1="40" y2="300.0" stroke="#5c6b7a" stroke-width="1"></line><line x1="364.2" x2="364.2" y1="40" y2="300.0" stroke="#16202b" stroke-width="1" stroke-dasharray="4 4"></line><text x="364.2" y="34" font-size="9.5" fill="#16202b" text-anchor="middle">detection floor +0.15</text><text x="283.6" y="326.0" font-size="10" fill="#5c6b7a" text-anchor="middle">null band |ρ| &lt; 0.1</text><text x="176.0" y="314.0" font-size="10" fill="#5c6b7a" text-anchor="middle">-0.2</text><text x="283.6" y="314.0" font-size="10" fill="#5c6b7a" text-anchor="middle">+0</text><text x="391.1" y="314.0" font-size="10" fill="#5c6b7a" text-anchor="middle">+0.2</text><text x="498.7" y="314.0" font-size="10" fill="#5c6b7a" text-anchor="middle">+0.4</text><text x="606.2" y="314.0" font-size="10" fill="#5c6b7a" text-anchor="middle">+0.6</text><text x="164" y="56.5" font-size="11.5" fill="#16202b" text-anchor="end">Qwen · write</text><line x1="283.6" x2="303.9" y1="53.0" y2="53.0" stroke="#2a78d6" stroke-width="1.5" opacity="0.5"></line><circle cx="303.9" cy="53.0" r="5.5" fill="#2a78d6"></circle><text x="313.9" y="56.5" font-size="10" fill="#5c6b7a">+0.04</text><text x="164" y="82.5" font-size="11.5" fill="#16202b" text-anchor="end">Qwen · read</text><line x1="283.6" x2="298.7" y1="79.0" y2="79.0" stroke="#2a78d6" stroke-width="1.5" opacity="0.5"></line><circle cx="298.7" cy="79.0" r="5.5" fill="#fff" stroke="#2a78d6" stroke-width="2"></circle><text x="308.7" y="82.5" font-size="10" fill="#5c6b7a">+0.03</text><text x="164" y="108.5" font-size="11.5" fill="#16202b" text-anchor="end">Gemma-2 · write</text><line x1="283.6" x2="312.5" y1="105.0" y2="105.0" stroke="#eda100" stroke-width="1.5" opacity="0.5"></line><circle cx="312.5" cy="105.0" r="5.5" fill="#eda100"></circle><text x="322.5" y="108.5" font-size="10" fill="#5c6b7a">+0.05</text><text x="164" y="134.5" font-size="11.5" fill="#16202b" text-anchor="end">Gemma-2 · read</text><line x1="283.6" x2="295.6" y1="131.0" y2="131.0" stroke="#eda100" stroke-width="1.5" opacity="0.5"></line><circle cx="295.6" cy="131.0" r="5.5" fill="#fff" stroke="#eda100" stroke-width="2"></circle><text x="305.6" y="134.5" font-size="10" fill="#5c6b7a">+0.02</text><text x="164" y="160.5" font-size="11.5" fill="#16202b" text-anchor="end">Pythia · write</text><line x1="283.6" x2="283.6" y1="157.0" y2="157.0" stroke="#4a3aa7" stroke-width="1.5" opacity="0.5"></line><circle cx="283.6" cy="157.0" r="5.5" fill="#4a3aa7"></circle><text x="293.6" y="160.5" font-size="10" fill="#5c6b7a">+0.00</text><text x="164" y="186.5" font-size="11.5" fill="#16202b" text-anchor="end">Pythia · read</text><line x1="283.6" x2="281.1" y1="183.0" y2="183.0" stroke="#4a3aa7" stroke-width="1.5" opacity="0.5"></line><circle cx="281.1" cy="183.0" r="5.5" fill="#fff" stroke="#4a3aa7" stroke-width="2"></circle><text x="291.1" y="186.5" font-size="10" fill="#5c6b7a">-0.00</text><text x="164" y="212.5" font-size="11.5" fill="#16202b" text-anchor="end">TinyLlama · write</text><line x1="283.6" x2="302.7" y1="209.0" y2="209.0" stroke="#1baf7a" stroke-width="1.5" opacity="0.5"></line><circle cx="302.7" cy="209.0" r="5.5" fill="#1baf7a"></circle><text x="312.7" y="212.5" font-size="10" fill="#5c6b7a">+0.04</text><text x="164" y="238.5" font-size="11.5" fill="#16202b" text-anchor="end">TinyLlama · read</text><line x1="283.6" x2="301.6" y1="235.0" y2="235.0" stroke="#1baf7a" stroke-width="1.5" opacity="0.5"></line><circle cx="301.6" cy="235.0" r="5.5" fill="#fff" stroke="#1baf7a" stroke-width="2"></circle><text x="311.6" y="238.5" font-size="10" fill="#5c6b7a">+0.03</text><line x1="176" x2="660" y1="248.0" y2="248.0" stroke="#d7dde2"></line><text x="164" y="264.5" font-size="11.5" fill="#16202b" text-anchor="end">planted law (control)</text><line x1="283.6" x2="563.6" y1="261.0" y2="261.0" stroke="#e34948" stroke-width="1.5" opacity="0.5"></line><rect x="558.1" y="255.5" width="11" height="11" rx="2" fill="#e34948"></rect><text x="573.6" y="264.5" font-size="10" fill="#5c6b7a">+0.52</text><text x="164" y="290.5" font-size="11.5" fill="#16202b" text-anchor="end">same control, shuffled</text><line x1="283.6" x2="301.9" y1="287.0" y2="287.0" stroke="#5c6b7a" stroke-width="1.5" opacity="0.5"></line><rect x="296.4" y="281.5" width="11" height="11" rx="2" fill="#5c6b7a"></rect><text x="311.9" y="290.5" font-size="10" fill="#5c6b7a">+0.03</text><text x="176" y="334" font-size="10" fill="#5c6b7a">write interface ●</text><text x="306" y="334" font-size="10" fill="#5c6b7a">read interface ○</text></svg>
<figcaption style="font-size:0.85rem;color:#6b7280;line-height:1.5;margin-top:0.6rem;">Each point is the median-across-layers partial correlation for one model on one interface. Filled = write, open = read. The grey band is the pre-registered null; the dashed line is the +0.15 threshold a real effect had to clear. Eight of eight real measurements sit in the band. The red square is the same statistic run on synthetic data with a crowding-to-mixing law planted by construction -- it lands at +0.52.</figcaption>
</figure>
<p>The write side was the natural place to look: a neuron’s output direction is what collides in the residual stream. But recruitment is decided on the <em>read</em> side – whether a neuron fires is a property of its input weights, not its output. So the read interface was the last place a neuron-level packing law could hide. It does not hide there either. And the medians are not papering over a wide spread:</p>
<figure style="margin:1.6rem 0;padding:0;" class="figure">
<svg viewbox="0 0 680 270" xmlns="http://www.w3.org/2000/svg" style="max-width:680px;width:100%;height:auto;font-family:inherit"><text x="120" y="20" font-size="13.5" font-weight="600" fill="#16202b">Every layer, not just the median – write and read density, four families</text><rect x="205.3" y="40" width="113.7" height="190.0" fill="#eef1f3"></rect><line x1="262.1" x2="262.1" y1="40" y2="230.0" stroke="#5c6b7a"></line><line x1="557.7" x2="557.7" y1="40" y2="230.0" stroke="#e34948" stroke-width="1.5" stroke-dasharray="5 3"></line><text x="557.7" y="34" font-size="9.5" fill="#e34948" text-anchor="middle">planted law +0.52</text><text x="148.4" y="244.0" font-size="10" fill="#5c6b7a" text-anchor="middle">-0.2</text><text x="262.1" y="244.0" font-size="10" fill="#5c6b7a" text-anchor="middle">+0</text><text x="375.8" y="244.0" font-size="10" fill="#5c6b7a" text-anchor="middle">+0.2</text><text x="489.5" y="244.0" font-size="10" fill="#5c6b7a" text-anchor="middle">+0.4</text><text x="603.2" y="244.0" font-size="10" fill="#5c6b7a" text-anchor="middle">+0.6</text><text x="108" y="67.2" font-size="11" fill="#16202b" text-anchor="end">Qwen2.5</text><circle cx="271.7" cy="59.8" r="2.6" fill="#2a78d6" opacity="0.55"></circle><circle cx="278.3" cy="59.8" r="2.6" fill="#2a78d6" opacity="0.55"></circle><circle cx="261.9" cy="59.8" r="2.6" fill="#2a78d6" opacity="0.55"></circle><circle cx="261.6" cy="59.8" r="2.6" fill="#2a78d6" opacity="0.55"></circle><circle cx="279.4" cy="59.8" r="2.6" fill="#2a78d6" opacity="0.55"></circle><circle cx="282.8" cy="59.8" r="2.6" fill="#2a78d6" opacity="0.55"></circle><circle cx="283.0" cy="59.8" r="2.6" fill="#2a78d6" opacity="0.55"></circle><circle cx="293.5" cy="59.8" r="2.6" fill="#2a78d6" opacity="0.55"></circle><circle cx="294.5" cy="59.8" r="2.6" fill="#2a78d6" opacity="0.55"></circle><circle cx="291.9" cy="59.8" r="2.6" fill="#2a78d6" opacity="0.55"></circle><circle cx="283.8" cy="59.8" r="2.6" fill="#2a78d6" opacity="0.55"></circle><circle cx="294.9" cy="59.8" r="2.6" fill="#2a78d6" opacity="0.55"></circle><circle cx="290.4" cy="59.8" r="2.6" fill="#2a78d6" opacity="0.55"></circle><circle cx="319.4" cy="59.8" r="2.6" fill="#2a78d6" opacity="0.55"></circle><circle cx="301.0" cy="59.8" r="2.6" fill="#2a78d6" opacity="0.55"></circle><circle cx="304.5" cy="59.8" r="2.6" fill="#2a78d6" opacity="0.55"></circle><circle cx="297.0" cy="59.8" r="2.6" fill="#2a78d6" opacity="0.55"></circle><circle cx="280.5" cy="59.8" r="2.6" fill="#2a78d6" opacity="0.55"></circle><circle cx="276.7" cy="59.8" r="2.6" fill="#2a78d6" opacity="0.55"></circle><circle cx="285.2" cy="59.8" r="2.6" fill="#2a78d6" opacity="0.55"></circle><circle cx="288.0" cy="59.8" r="2.6" fill="#2a78d6" opacity="0.55"></circle><circle cx="283.4" cy="59.8" r="2.6" fill="#2a78d6" opacity="0.55"></circle><circle cx="294.3" cy="59.8" r="2.6" fill="#2a78d6" opacity="0.55"></circle><circle cx="277.7" cy="59.8" r="2.6" fill="#2a78d6" opacity="0.55"></circle><circle cx="278.4" cy="59.8" r="2.6" fill="#2a78d6" opacity="0.55"></circle><circle cx="280.4" cy="59.8" r="2.6" fill="#2a78d6" opacity="0.55"></circle><circle cx="310.3" cy="59.8" r="2.6" fill="#2a78d6" opacity="0.55"></circle><circle cx="274.3" cy="59.8" r="2.6" fill="#2a78d6" opacity="0.55"></circle><circle cx="267.8" cy="67.8" r="2.6" fill="#2a78d6" opacity="0.4"></circle><circle cx="255.1" cy="67.8" r="2.6" fill="#2a78d6" opacity="0.4"></circle><circle cx="260.5" cy="67.8" r="2.6" fill="#2a78d6" opacity="0.4"></circle><circle cx="251.2" cy="67.8" r="2.6" fill="#2a78d6" opacity="0.4"></circle><circle cx="251.9" cy="67.8" r="2.6" fill="#2a78d6" opacity="0.4"></circle><circle cx="255.3" cy="67.8" r="2.6" fill="#2a78d6" opacity="0.4"></circle><circle cx="278.0" cy="67.8" r="2.6" fill="#2a78d6" opacity="0.4"></circle><circle cx="283.3" cy="67.8" r="2.6" fill="#2a78d6" opacity="0.4"></circle><circle cx="275.2" cy="67.8" r="2.6" fill="#2a78d6" opacity="0.4"></circle><circle cx="276.5" cy="67.8" r="2.6" fill="#2a78d6" opacity="0.4"></circle><circle cx="266.7" cy="67.8" r="2.6" fill="#2a78d6" opacity="0.4"></circle><circle cx="270.4" cy="67.8" r="2.6" fill="#2a78d6" opacity="0.4"></circle><circle cx="280.8" cy="67.8" r="2.6" fill="#2a78d6" opacity="0.4"></circle><circle cx="310.4" cy="67.8" r="2.6" fill="#2a78d6" opacity="0.4"></circle><circle cx="285.1" cy="67.8" r="2.6" fill="#2a78d6" opacity="0.4"></circle><circle cx="280.1" cy="67.8" r="2.6" fill="#2a78d6" opacity="0.4"></circle><circle cx="272.3" cy="67.8" r="2.6" fill="#2a78d6" opacity="0.4"></circle><circle cx="279.2" cy="67.8" r="2.6" fill="#2a78d6" opacity="0.4"></circle><circle cx="289.1" cy="67.8" r="2.6" fill="#2a78d6" opacity="0.4"></circle><circle cx="298.2" cy="67.8" r="2.6" fill="#2a78d6" opacity="0.4"></circle><circle cx="282.7" cy="67.8" r="2.6" fill="#2a78d6" opacity="0.4"></circle><circle cx="290.2" cy="67.8" r="2.6" fill="#2a78d6" opacity="0.4"></circle><circle cx="278.2" cy="67.8" r="2.6" fill="#2a78d6" opacity="0.4"></circle><circle cx="277.0" cy="67.8" r="2.6" fill="#2a78d6" opacity="0.4"></circle><circle cx="273.8" cy="67.8" r="2.6" fill="#2a78d6" opacity="0.4"></circle><circle cx="286.1" cy="67.8" r="2.6" fill="#2a78d6" opacity="0.4"></circle><circle cx="309.2" cy="67.8" r="2.6" fill="#2a78d6" opacity="0.4"></circle><circle cx="300.7" cy="67.8" r="2.6" fill="#2a78d6" opacity="0.4"></circle><line x1="283.6" x2="283.6" y1="54.8" y2="72.8" stroke="#2a78d6" stroke-width="2.4"></line><text x="108" y="114.8" font-size="11" fill="#16202b" text-anchor="end">Gemma</text><circle cx="269.0" cy="107.2" r="2.6" fill="#eda100" opacity="0.55"></circle><circle cx="333.0" cy="107.2" r="2.6" fill="#eda100" opacity="0.55"></circle><circle cx="278.8" cy="107.2" r="2.6" fill="#eda100" opacity="0.55"></circle><circle cx="296.6" cy="107.2" r="2.6" fill="#eda100" opacity="0.55"></circle><circle cx="287.4" cy="107.2" r="2.6" fill="#eda100" opacity="0.55"></circle><circle cx="306.5" cy="107.2" r="2.6" fill="#eda100" opacity="0.55"></circle><circle cx="316.7" cy="107.2" r="2.6" fill="#eda100" opacity="0.55"></circle><circle cx="290.3" cy="107.2" r="2.6" fill="#eda100" opacity="0.55"></circle><circle cx="295.3" cy="107.2" r="2.6" fill="#eda100" opacity="0.55"></circle><circle cx="291.4" cy="107.2" r="2.6" fill="#eda100" opacity="0.55"></circle><circle cx="294.2" cy="107.2" r="2.6" fill="#eda100" opacity="0.55"></circle><circle cx="294.9" cy="107.2" r="2.6" fill="#eda100" opacity="0.55"></circle><circle cx="283.1" cy="107.2" r="2.6" fill="#eda100" opacity="0.55"></circle><circle cx="286.9" cy="107.2" r="2.6" fill="#eda100" opacity="0.55"></circle><circle cx="272.0" cy="107.2" r="2.6" fill="#eda100" opacity="0.55"></circle><circle cx="280.7" cy="107.2" r="2.6" fill="#eda100" opacity="0.55"></circle><circle cx="277.1" cy="107.2" r="2.6" fill="#eda100" opacity="0.55"></circle><circle cx="285.3" cy="107.2" r="2.6" fill="#eda100" opacity="0.55"></circle><circle cx="285.0" cy="107.2" r="2.6" fill="#eda100" opacity="0.55"></circle><circle cx="288.7" cy="107.2" r="2.6" fill="#eda100" opacity="0.55"></circle><circle cx="294.0" cy="107.2" r="2.6" fill="#eda100" opacity="0.55"></circle><circle cx="316.6" cy="107.2" r="2.6" fill="#eda100" opacity="0.55"></circle><circle cx="308.0" cy="107.2" r="2.6" fill="#eda100" opacity="0.55"></circle><circle cx="302.3" cy="107.2" r="2.6" fill="#eda100" opacity="0.55"></circle><circle cx="318.5" cy="107.2" r="2.6" fill="#eda100" opacity="0.55"></circle><circle cx="338.3" cy="107.2" r="2.6" fill="#eda100" opacity="0.55"></circle><circle cx="185.2" cy="115.2" r="2.6" fill="#eda100" opacity="0.4"></circle><circle cx="253.2" cy="115.2" r="2.6" fill="#eda100" opacity="0.4"></circle><circle cx="275.6" cy="115.2" r="2.6" fill="#eda100" opacity="0.4"></circle><circle cx="283.4" cy="115.2" r="2.6" fill="#eda100" opacity="0.4"></circle><circle cx="269.3" cy="115.2" r="2.6" fill="#eda100" opacity="0.4"></circle><circle cx="289.0" cy="115.2" r="2.6" fill="#eda100" opacity="0.4"></circle><circle cx="266.6" cy="115.2" r="2.6" fill="#eda100" opacity="0.4"></circle><circle cx="244.6" cy="115.2" r="2.6" fill="#eda100" opacity="0.4"></circle><circle cx="265.2" cy="115.2" r="2.6" fill="#eda100" opacity="0.4"></circle><circle cx="268.4" cy="115.2" r="2.6" fill="#eda100" opacity="0.4"></circle><circle cx="268.1" cy="115.2" r="2.6" fill="#eda100" opacity="0.4"></circle><circle cx="266.7" cy="115.2" r="2.6" fill="#eda100" opacity="0.4"></circle><circle cx="271.6" cy="115.2" r="2.6" fill="#eda100" opacity="0.4"></circle><circle cx="279.2" cy="115.2" r="2.6" fill="#eda100" opacity="0.4"></circle><circle cx="268.6" cy="115.2" r="2.6" fill="#eda100" opacity="0.4"></circle><circle cx="270.6" cy="115.2" r="2.6" fill="#eda100" opacity="0.4"></circle><circle cx="276.4" cy="115.2" r="2.6" fill="#eda100" opacity="0.4"></circle><circle cx="280.5" cy="115.2" r="2.6" fill="#eda100" opacity="0.4"></circle><circle cx="274.1" cy="115.2" r="2.6" fill="#eda100" opacity="0.4"></circle><circle cx="276.1" cy="115.2" r="2.6" fill="#eda100" opacity="0.4"></circle><circle cx="279.4" cy="115.2" r="2.6" fill="#eda100" opacity="0.4"></circle><circle cx="279.3" cy="115.2" r="2.6" fill="#eda100" opacity="0.4"></circle><circle cx="291.0" cy="115.2" r="2.6" fill="#eda100" opacity="0.4"></circle><circle cx="300.0" cy="115.2" r="2.6" fill="#eda100" opacity="0.4"></circle><circle cx="328.3" cy="115.2" r="2.6" fill="#eda100" opacity="0.4"></circle><circle cx="360.1" cy="115.2" r="2.6" fill="#eda100" opacity="0.4"></circle><line x1="292.7" x2="292.7" y1="102.2" y2="120.2" stroke="#eda100" stroke-width="2.4"></line><text x="108" y="162.2" font-size="11" fill="#16202b" text-anchor="end">Pythia</text><circle cx="268.2" cy="154.8" r="2.6" fill="#4a3aa7" opacity="0.55"></circle><circle cx="266.3" cy="154.8" r="2.6" fill="#4a3aa7" opacity="0.55"></circle><circle cx="262.0" cy="154.8" r="2.6" fill="#4a3aa7" opacity="0.55"></circle><circle cx="266.4" cy="154.8" r="2.6" fill="#4a3aa7" opacity="0.55"></circle><circle cx="256.5" cy="154.8" r="2.6" fill="#4a3aa7" opacity="0.55"></circle><circle cx="255.6" cy="154.8" r="2.6" fill="#4a3aa7" opacity="0.55"></circle><circle cx="260.8" cy="154.8" r="2.6" fill="#4a3aa7" opacity="0.55"></circle><circle cx="256.8" cy="154.8" r="2.6" fill="#4a3aa7" opacity="0.55"></circle><circle cx="262.4" cy="154.8" r="2.6" fill="#4a3aa7" opacity="0.55"></circle><circle cx="261.3" cy="154.8" r="2.6" fill="#4a3aa7" opacity="0.55"></circle><circle cx="257.7" cy="154.8" r="2.6" fill="#4a3aa7" opacity="0.55"></circle><circle cx="254.2" cy="154.8" r="2.6" fill="#4a3aa7" opacity="0.55"></circle><circle cx="253.4" cy="154.8" r="2.6" fill="#4a3aa7" opacity="0.55"></circle><circle cx="263.8" cy="154.8" r="2.6" fill="#4a3aa7" opacity="0.55"></circle><circle cx="263.5" cy="154.8" r="2.6" fill="#4a3aa7" opacity="0.55"></circle><circle cx="259.4" cy="154.8" r="2.6" fill="#4a3aa7" opacity="0.55"></circle><circle cx="263.1" cy="154.8" r="2.6" fill="#4a3aa7" opacity="0.55"></circle><circle cx="264.0" cy="154.8" r="2.6" fill="#4a3aa7" opacity="0.55"></circle><circle cx="262.5" cy="154.8" r="2.6" fill="#4a3aa7" opacity="0.55"></circle><circle cx="263.7" cy="154.8" r="2.6" fill="#4a3aa7" opacity="0.55"></circle><circle cx="259.8" cy="154.8" r="2.6" fill="#4a3aa7" opacity="0.55"></circle><circle cx="258.1" cy="154.8" r="2.6" fill="#4a3aa7" opacity="0.55"></circle><circle cx="272.3" cy="154.8" r="2.6" fill="#4a3aa7" opacity="0.55"></circle><circle cx="337.8" cy="154.8" r="2.6" fill="#4a3aa7" opacity="0.55"></circle><circle cx="265.0" cy="162.8" r="2.6" fill="#4a3aa7" opacity="0.4"></circle><circle cx="259.1" cy="162.8" r="2.6" fill="#4a3aa7" opacity="0.4"></circle><circle cx="259.2" cy="162.8" r="2.6" fill="#4a3aa7" opacity="0.4"></circle><circle cx="259.3" cy="162.8" r="2.6" fill="#4a3aa7" opacity="0.4"></circle><circle cx="246.3" cy="162.8" r="2.6" fill="#4a3aa7" opacity="0.4"></circle><circle cx="253.8" cy="162.8" r="2.6" fill="#4a3aa7" opacity="0.4"></circle><circle cx="257.6" cy="162.8" r="2.6" fill="#4a3aa7" opacity="0.4"></circle><circle cx="255.4" cy="162.8" r="2.6" fill="#4a3aa7" opacity="0.4"></circle><circle cx="262.3" cy="162.8" r="2.6" fill="#4a3aa7" opacity="0.4"></circle><circle cx="260.1" cy="162.8" r="2.6" fill="#4a3aa7" opacity="0.4"></circle><circle cx="254.6" cy="162.8" r="2.6" fill="#4a3aa7" opacity="0.4"></circle><circle cx="252.8" cy="162.8" r="2.6" fill="#4a3aa7" opacity="0.4"></circle><circle cx="253.8" cy="162.8" r="2.6" fill="#4a3aa7" opacity="0.4"></circle><circle cx="261.0" cy="162.8" r="2.6" fill="#4a3aa7" opacity="0.4"></circle><circle cx="261.5" cy="162.8" r="2.6" fill="#4a3aa7" opacity="0.4"></circle><circle cx="257.4" cy="162.8" r="2.6" fill="#4a3aa7" opacity="0.4"></circle><circle cx="262.6" cy="162.8" r="2.6" fill="#4a3aa7" opacity="0.4"></circle><circle cx="262.0" cy="162.8" r="2.6" fill="#4a3aa7" opacity="0.4"></circle><circle cx="260.4" cy="162.8" r="2.6" fill="#4a3aa7" opacity="0.4"></circle><circle cx="261.6" cy="162.8" r="2.6" fill="#4a3aa7" opacity="0.4"></circle><circle cx="260.1" cy="162.8" r="2.6" fill="#4a3aa7" opacity="0.4"></circle><circle cx="259.6" cy="162.8" r="2.6" fill="#4a3aa7" opacity="0.4"></circle><circle cx="256.8" cy="162.8" r="2.6" fill="#4a3aa7" opacity="0.4"></circle><circle cx="272.5" cy="162.8" r="2.6" fill="#4a3aa7" opacity="0.4"></circle><line x1="262.2" x2="262.2" y1="149.8" y2="167.8" stroke="#4a3aa7" stroke-width="2.4"></line><text x="108" y="209.8" font-size="11" fill="#16202b" text-anchor="end">TinyLlama</text><circle cx="317.8" cy="202.2" r="2.6" fill="#1baf7a" opacity="0.55"></circle><circle cx="267.8" cy="202.2" r="2.6" fill="#1baf7a" opacity="0.55"></circle><circle cx="270.6" cy="202.2" r="2.6" fill="#1baf7a" opacity="0.55"></circle><circle cx="270.0" cy="202.2" r="2.6" fill="#1baf7a" opacity="0.55"></circle><circle cx="267.5" cy="202.2" r="2.6" fill="#1baf7a" opacity="0.55"></circle><circle cx="270.0" cy="202.2" r="2.6" fill="#1baf7a" opacity="0.55"></circle><circle cx="284.9" cy="202.2" r="2.6" fill="#1baf7a" opacity="0.55"></circle><circle cx="303.3" cy="202.2" r="2.6" fill="#1baf7a" opacity="0.55"></circle><circle cx="300.4" cy="202.2" r="2.6" fill="#1baf7a" opacity="0.55"></circle><circle cx="304.6" cy="202.2" r="2.6" fill="#1baf7a" opacity="0.55"></circle><circle cx="282.4" cy="202.2" r="2.6" fill="#1baf7a" opacity="0.55"></circle><circle cx="282.1" cy="202.2" r="2.6" fill="#1baf7a" opacity="0.55"></circle><circle cx="297.4" cy="202.2" r="2.6" fill="#1baf7a" opacity="0.55"></circle><circle cx="296.9" cy="202.2" r="2.6" fill="#1baf7a" opacity="0.55"></circle><circle cx="277.0" cy="202.2" r="2.6" fill="#1baf7a" opacity="0.55"></circle><circle cx="275.5" cy="202.2" r="2.6" fill="#1baf7a" opacity="0.55"></circle><circle cx="274.1" cy="202.2" r="2.6" fill="#1baf7a" opacity="0.55"></circle><circle cx="276.2" cy="202.2" r="2.6" fill="#1baf7a" opacity="0.55"></circle><circle cx="279.2" cy="202.2" r="2.6" fill="#1baf7a" opacity="0.55"></circle><circle cx="283.5" cy="202.2" r="2.6" fill="#1baf7a" opacity="0.55"></circle><circle cx="292.9" cy="202.2" r="2.6" fill="#1baf7a" opacity="0.55"></circle><circle cx="317.5" cy="202.2" r="2.6" fill="#1baf7a" opacity="0.55"></circle><circle cx="132.1" cy="210.2" r="2.6" fill="#1baf7a" opacity="0.4"></circle><circle cx="265.5" cy="210.2" r="2.6" fill="#1baf7a" opacity="0.4"></circle><circle cx="265.0" cy="210.2" r="2.6" fill="#1baf7a" opacity="0.4"></circle><circle cx="264.1" cy="210.2" r="2.6" fill="#1baf7a" opacity="0.4"></circle><circle cx="266.5" cy="210.2" r="2.6" fill="#1baf7a" opacity="0.4"></circle><circle cx="268.5" cy="210.2" r="2.6" fill="#1baf7a" opacity="0.4"></circle><circle cx="277.7" cy="210.2" r="2.6" fill="#1baf7a" opacity="0.4"></circle><circle cx="295.1" cy="210.2" r="2.6" fill="#1baf7a" opacity="0.4"></circle><circle cx="296.2" cy="210.2" r="2.6" fill="#1baf7a" opacity="0.4"></circle><circle cx="287.8" cy="210.2" r="2.6" fill="#1baf7a" opacity="0.4"></circle><circle cx="306.8" cy="210.2" r="2.6" fill="#1baf7a" opacity="0.4"></circle><circle cx="278.7" cy="210.2" r="2.6" fill="#1baf7a" opacity="0.4"></circle><circle cx="290.6" cy="210.2" r="2.6" fill="#1baf7a" opacity="0.4"></circle><circle cx="294.0" cy="210.2" r="2.6" fill="#1baf7a" opacity="0.4"></circle><circle cx="282.0" cy="210.2" r="2.6" fill="#1baf7a" opacity="0.4"></circle><circle cx="280.8" cy="210.2" r="2.6" fill="#1baf7a" opacity="0.4"></circle><circle cx="281.4" cy="210.2" r="2.6" fill="#1baf7a" opacity="0.4"></circle><circle cx="273.5" cy="210.2" r="2.6" fill="#1baf7a" opacity="0.4"></circle><circle cx="274.8" cy="210.2" r="2.6" fill="#1baf7a" opacity="0.4"></circle><circle cx="285.8" cy="210.2" r="2.6" fill="#1baf7a" opacity="0.4"></circle><circle cx="297.6" cy="210.2" r="2.6" fill="#1baf7a" opacity="0.4"></circle><circle cx="328.8" cy="210.2" r="2.6" fill="#1baf7a" opacity="0.4"></circle><line x1="282.3" x2="282.3" y1="197.2" y2="215.2" stroke="#1baf7a" stroke-width="2.4"></line><text x="262.1" y="264" font-size="10" fill="#5c6b7a" text-anchor="middle">each dot = one layer · thick tick = median · upper row write, lower row read</text></svg>
<figcaption style="font-size:0.85rem;color:#6b7280;line-height:1.5;margin-top:0.6rem;">The medians are not hiding a wide spread: every individual layer's partial ρ (write and read density), all four families. The cloud sits on zero at every depth. The red dashed line marks where the planted-law control lands -- the entire real distribution is an order of magnitude short of it.</figcaption>
</figure>
</section>
<section id="a-neuron-is-not-in-a-crowded-place.-it-is-crowded-per-interface." class="level2">
<h2 class="anchored" data-anchor-id="a-neuron-is-not-in-a-crowded-place.-it-is-crowded-per-interface.">A neuron is not “in a crowded place.” It is crowded per interface.</h2>
<p>The read-side test was not a redundant re-run of the write-side test, and the reason is a finding in itself. If a neuron simply occupied a crowded or an uncrowded region of the model, its read-crowding and write-crowding would move together – the correlation would be near one. It is <img src="https://latex.codecogs.com/png.latex?%5Crho"> <strong>0.21 to 0.47</strong>.</p>
<figure style="margin:1.6rem 0;padding:0;" class="figure">
<svg viewbox="0 0 680 210" xmlns="http://www.w3.org/2000/svg" style="max-width:680px;width:100%;height:auto;font-family:inherit"><text x="120" y="20" font-size="13.5" font-weight="600" fill="#16202b">Is a neuron “in a crowded place”? Read-vs-write crowding agreement per neuron</text><line x1="120.0" x2="120.0" y1="40" y2="180.0" stroke="#d7dde2"></line><text x="120.0" y="194.0" font-size="10" fill="#5c6b7a" text-anchor="middle">0</text><line x1="245.0" x2="245.0" y1="40" y2="180.0" stroke="#d7dde2"></line><text x="245.0" y="194.0" font-size="10" fill="#5c6b7a" text-anchor="middle">0.25</text><line x1="370.0" x2="370.0" y1="40" y2="180.0" stroke="#d7dde2"></line><text x="370.0" y="194.0" font-size="10" fill="#5c6b7a" text-anchor="middle">0.5</text><line x1="495.0" x2="495.0" y1="40" y2="180.0" stroke="#d7dde2"></line><text x="495.0" y="194.0" font-size="10" fill="#5c6b7a" text-anchor="middle">0.75</text><line x1="620.0" x2="620.0" y1="40" y2="180.0" stroke="#d7dde2"></line><text x="620.0" y="194.0" font-size="10" fill="#5c6b7a" text-anchor="middle">1</text><line x1="620.0" x2="620.0" y1="40" y2="180.0" stroke="#16202b" stroke-dasharray="4 4"></line><text x="620.0" y="34" font-size="9" fill="#16202b" text-anchor="end">“one place” = 1.0</text><text x="108" y="61.0" font-size="11" fill="#16202b" text-anchor="end">Qwen2.5</text><rect x="120" y="50.5" width="135.4" height="14" rx="3" fill="#2a78d6"></rect><text x="261.4" y="61.0" font-size="10.5" fill="#16202b">0.27</text><text x="108" y="96.0" font-size="11" fill="#16202b" text-anchor="end">Gemma</text><rect x="120" y="85.5" width="120.2" height="14" rx="3" fill="#eda100"></rect><text x="246.2" y="96.0" font-size="10.5" fill="#16202b">0.24</text><text x="108" y="131.0" font-size="11" fill="#16202b" text-anchor="end">Pythia</text><rect x="120" y="120.5" width="236.2" height="14" rx="3" fill="#4a3aa7"></rect><text x="362.2" y="131.0" font-size="10.5" fill="#16202b">0.47</text><text x="108" y="166.0" font-size="11" fill="#16202b" text-anchor="end">TinyLlama</text><rect x="120" y="155.5" width="106.7" height="14" rx="3" fill="#1baf7a"></rect><text x="232.7" y="166.0" font-size="10.5" fill="#16202b">0.21</text></svg>
<figcaption style="font-size:0.85rem;color:#6b7280;line-height:1.5;margin-top:0.6rem;">Per-neuron agreement between read-side and write-side neighbourhood density, per family. Recruitment geometry and expression geometry are substantially independent properties of the same unit -- so there is no single scalar ‘this neuron is crowded.’</figcaption>
</figure>
<p>Recruitment geometry and expression geometry are substantially independent properties of the same unit. The folk picture assumes one geometric fact per neuron; there are at least two, they disagree, and neither predicts what the neuron actually does across regimes. That is the quiet mechanism behind the whole null.</p>
</section>
<section id="the-effects-that-clear-the-noise-point-in-opposite-directions" class="level2">
<h2 class="anchored" data-anchor-id="the-effects-that-clear-the-noise-point-in-opposite-directions">The effects that clear the noise point in opposite directions</h2>
<p>A weak-but-real law would at least be <em>consistent</em>: crowded neurons a little more mixed in every family. Instead the largest excursions cancel. Across both interfaces the two measures that graze the band – Gemma-2’s write-side local-crowding at <img src="https://latex.codecogs.com/png.latex?+0.12"> and Pythia’s read-side top-10 at <img src="https://latex.codecogs.com/png.latex?-0.12"> – have opposite signs, one family each, two hundredths past a threshold chosen as the noise floor. The stratum medians tell the same story in a picture.</p>
<figure style="margin:1.6rem 0;padding:0;" class="figure">
<svg viewbox="0 0 680 250" xmlns="http://www.w3.org/2000/svg" style="max-width:680px;width:100%;height:auto;font-family:inherit"><text x="120" y="20" font-size="13.5" font-weight="600" fill="#16202b">The only above-noise effects flip direction between families</text><line x1="137.1" x2="137.1" y1="40" y2="216.0" stroke="#d7dde2"></line><text x="137.1" y="230.0" font-size="10" fill="#5c6b7a" text-anchor="middle">1.70</text><line x1="302.0" x2="302.0" y1="40" y2="216.0" stroke="#d7dde2"></line><text x="302.0" y="230.0" font-size="10" fill="#5c6b7a" text-anchor="middle">1.80</text><line x1="467.0" x2="467.0" y1="40" y2="216.0" stroke="#d7dde2"></line><text x="467.0" y="230.0" font-size="10" fill="#5c6b7a" text-anchor="middle">1.90</text><text x="108" y="65.5" font-size="11" fill="#16202b" text-anchor="end">Qwen2.5</text><line x1="343.7" x2="375.8" y1="62.0" y2="62.0" stroke="#2a78d6" stroke-width="2"></line><circle cx="343.7" cy="62.0" r="5.5" fill="#fff" stroke="#2a78d6" stroke-width="2"></circle><circle cx="375.8" cy="62.0" r="5.5" fill="#2a78d6"></circle><text x="596" y="65.5" font-size="9.5" fill="#5c6b7a">crowded more mixed →</text><text x="108" y="109.5" font-size="11" fill="#16202b" text-anchor="end">Gemma</text><line x1="153.0" x2="316.9" y1="106.0" y2="106.0" stroke="#eda100" stroke-width="2"></line><circle cx="153.0" cy="106.0" r="5.5" fill="#fff" stroke="#eda100" stroke-width="2"></circle><circle cx="316.9" cy="106.0" r="5.5" fill="#eda100"></circle><text x="596" y="109.5" font-size="9.5" fill="#5c6b7a">crowded more mixed →</text><text x="108" y="153.5" font-size="11" fill="#16202b" text-anchor="end">Pythia</text><line x1="330.6" x2="433.0" y1="150.0" y2="150.0" stroke="#4a3aa7" stroke-width="2"></line><circle cx="330.6" cy="150.0" r="5.5" fill="#fff" stroke="#4a3aa7" stroke-width="2"></circle><circle cx="433.0" cy="150.0" r="5.5" fill="#4a3aa7"></circle><text x="596" y="153.5" font-size="9.5" fill="#5c6b7a">crowded more mixed →</text><text x="108" y="197.5" font-size="11" fill="#16202b" text-anchor="end">TinyLlama</text><line x1="557.0" x2="416.7" y1="194.0" y2="194.0" stroke="#1baf7a" stroke-width="2"></line><circle cx="557.0" cy="194.0" r="5.5" fill="#fff" stroke="#1baf7a" stroke-width="2"></circle><circle cx="416.7" cy="194.0" r="5.5" fill="#1baf7a"></circle><text x="596" y="197.5" font-size="9.5" fill="#5c6b7a">← isolated more mixed</text><text x="120" y="244" font-size="10" fill="#5c6b7a">○ isolated neurons   ● crowded neurons · rate-matched median entropy (bits)</text></svg>
<figcaption style="font-size:0.85rem;color:#6b7280;line-height:1.5;margin-top:0.6rem;">Rate-matched median mixing for a family's most-isolated vs most-crowded neurons. A universal law would lean every bar the same way. Gemma-2's isolated neurons are its cleanest; TinyLlama's are its most mixed -- the effect reverses between architectures.</figcaption>
</figure>
<p>Gemma-2 showing the lone sub-threshold positive is exactly the pattern to expect: across this whole line of work Gemma-2 is repeatedly the most weight-legible model, so a whiff of structure-to-function signal there, and nowhere else, reads as a property of Gemma rather than of transformers. It was pre-registered as not counting.</p>
</section>
<section id="a-null-is-only-as-good-as-the-instrument-behind-it" class="level2">
<h2 class="anchored" data-anchor-id="a-null-is-only-as-good-as-the-instrument-behind-it">A null is only as good as the instrument behind it</h2>
<p>The obvious attack on any negative result: maybe the pipeline could not detect the effect even if it existed – maybe five coarse regimes cannot resolve real polysemanticity. That objection is answerable in-house, and cheaply, with two toy systems.</p>
<div class="callout callout-style-default callout-important callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
<span class="screen-reader-only">Important</span>Positive control: pass, both flanks
</div>
</div>
<div class="callout-body-container callout-body">
<p><strong>Detection.</strong> On synthetic data with a graded crowding-&gt;mixing law planted by construction, the <em>identical</em> statistic recovers it at partial <img src="https://latex.codecogs.com/png.latex?%5Crho"> <strong>+0.52</strong> (density) / <strong>+0.63</strong> (top-10), while the same test on label-shuffled data stays at q95 <img src="https://latex.codecogs.com/png.latex?%7C%5Crho%7C"> 0.094 – inside the null band.</p>
<p><strong>Proxy validity.</strong> In a trained Toy-Models-style autoencoder where each neuron’s true feature composition is known, the coarse five-regime entropy tracks ground-truth mixing at <img src="https://latex.codecogs.com/png.latex?%5Crho"> <strong>+0.83</strong>. The measure sees mixing; the pipeline sees the law when the law is there.</p>
</div>
</div>
<table class="caption-top table">
<caption>The real-model numbers sit an order of magnitude below what the same instrument returns when the law is real.</caption>
<colgroup>
<col style="width: 46%">
<col style="width: 14%">
<col style="width: 18%">
<col style="width: 22%">
</colgroup>
<thead>
<tr class="header">
<th>quantity</th>
<th>value</th>
<th>bar</th>
<th>reading</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td>real census, write (best family)</td>
<td>+0.05</td>
<td>–</td>
<td>the null</td>
</tr>
<tr class="even">
<td>planted law, density</td>
<td>+0.52</td>
<td><img src="https://latex.codecogs.com/png.latex?%5Cgeq"> +0.15</td>
<td>detected, 3–4<img src="https://latex.codecogs.com/png.latex?%5Ctimes"> over floor</td>
</tr>
<tr class="odd">
<td>planted law, top-10</td>
<td>+0.63</td>
<td>agrees in sign</td>
<td>detected</td>
</tr>
<tr class="even">
<td>planted law, labels shuffled (q95)</td>
<td>0.094</td>
<td>&lt; 0.10</td>
<td>no false positive</td>
</tr>
<tr class="odd">
<td>census entropy vs ground-truth mixing</td>
<td>+0.83</td>
<td>–</td>
<td>proxy is faithful</td>
</tr>
</tbody>
</table>
<div class="callout callout-style-default callout-note callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
<span class="screen-reader-only">Note</span>An artifact worth naming
</div>
</div>
<div class="callout-body-container callout-body">
<p>The first planted control used a clean two-group split (isolated vs crowded), which made the crowding variable perfectly bimodal. Pushed through the rank-residualisation, a near-binary regressor inflated the permutation null to q95 <img src="https://latex.codecogs.com/png.latex?%5Capprox%200.16"> – which would have made the instrument look <em>less</em> sensitive than it is. The fix was to regrade the planted data to a continuous crowding level matching the real census’s density spread; the null fell back to <img src="https://latex.codecogs.com/png.latex?%5Capprox%200.04"> and the detection signal was unchanged (bimodal <img src="https://latex.codecogs.com/png.latex?+0.83">, graded <img src="https://latex.codecogs.com/png.latex?+0.52">). The regrade fixed the <em>null estimate</em>, not the <em>signal</em> – the kind of thing a control has to get right, and the kind of thing that is only visible if you keep both runs.</p>
</div>
</div>
<div class="callout callout-style-default callout-note callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
<span class="screen-reader-only">Note</span>The mechanistic “why,” seen in a toy
</div>
</div>
<div class="callout-body-container callout-body">
<p>The trained autoencoder carries feature-level superposition by construction – 2560 sparse features in 512 neurons, 5<img src="https://latex.codecogs.com/png.latex?%5Ctimes"> compression, dense packing, clean reconstruction. Yet its neuron write-columns barely crowd at all: the maximum pairwise <img src="https://latex.codecogs.com/png.latex?%7C%5Ccos%7C"> across the whole layer is <strong>0.15</strong>. Here is a minimal system where superposition is present and heavy, and it still does not imprint as neuron-level geometric crowding – because features and neurons are simply different objects. That is the decoupling shown where you can see both levels at once.</p>
</div>
</div>
</section>
<section id="what-this-does-and-does-not-say" class="level2">
<h2 class="anchored" data-anchor-id="what-this-does-and-does-not-say">What this does and does not say</h2>
<p>This is a claim about <strong>neurons</strong>, not features. Superposition theory is fundamentally about features as linear combinations of neurons; an SAE feature can be crowded and polysemantic in ways no single neuron reveals. Nothing here refutes feature-level superposition. What it bounds is the <em>neuron-granularity shortcut</em> – the hope that you could read polysemanticity off raw weight geometry without a dictionary. You cannot, on either interface, in any of four families.</p>
<p>It is also a claim about <strong>task-regime</strong> mixing (five coarse classes), not fine-grained semantic mixing, and about <strong>MLP</strong> geometry, not attention. Within those bounds it is as airtight as I could make it: pre-registered thresholds, both faces of the neuron, four architectures, a positive control at 3–4<img src="https://latex.codecogs.com/png.latex?%5Ctimes"> the detection floor, and a demonstrated-faithful mixing proxy.</p>
<p>Which neurons mix regimes is set by the statistics of the data a model was trained on, not by the geometric necessity of packing. The ledger of a model’s capacity has to be <em>read from usage</em>; it is not sitting latent in the Gram matrix of its weights.</p>
<p>This is the negative half of a longer weights-first program. Where the <a href="../weight-space-map-of-attention-heads/">earlier</a> <a href="../correcting-the-weight-space-map/">notes</a> mapped what weight geometry <em>does</em> know, this one marks a firm boundary on what it does not.</p>


</section>

 ]]></description>
  <category>mechanistic-interpretability</category>
  <category>superposition</category>
  <category>polysemanticity</category>
  <category>mlp-neurons</category>
  <category>negative-results</category>
  <guid>https://www.residual-thoughts.com/posts/price-of-packing/</guid>
  <pubDate>Sun, 05 Jul 2026 00:00:00 GMT</pubDate>
  <media:content url="https://www.residual-thoughts.com/posts/price-of-packing/figs/og.png" medium="image" type="image/png" height="76" width="144"/>
</item>
<item>
  <title>Correcting the Weight-Space Map: Three Bugs and a Cleaner Picture</title>
  <dc:creator>Shubham Srivastava</dc:creator>
  <link>https://www.residual-thoughts.com/posts/correcting-the-weight-space-map/</link>
  <description><![CDATA[ 





<p><span class="post-stamp red">errata</span> <span class="post-stamp pencil">corrects the <a href="../weight-space-map-of-attention-heads/">2026-03-22 map</a></span></p>
<p>The <a href="../weight-space-map-of-attention-heads/">first post</a> built a weight-space map of Gemma-2-2B attention heads – routing (<img src="https://latex.codecogs.com/png.latex?W_Q/W_K">), writing (<img src="https://latex.codecogs.com/png.latex?W_O/W_V">), and feature programs – in a fixed SAE coordinate system, and validated those weight-space predictions against real attention on a handful of prompts. The headline structure held up well in early and middle layers, then appeared to <em>invert</em> in late layers: weight-space predictions went anti-correlated with real attention past about L17. I called that the third of three depth regimes.</p>
<p>That regime was not real. While building follow-up experiments I found three bugs in the pipeline, each small in code and large in consequence. After fixing them and re-running the full 26-layer analysis, the late-layer inversion disappears, every layer is solidly predictive, and two of the original post’s sharper claims need softening. The good news: the core method works <em>better</em> than the first post reported, and two new diagnostics fell out of the cleanup.</p>
<p>These are lab notes on what broke, what the corrected map says, and what I now believe about how load-bearing the SAE dictionary actually is.</p>
<div class="callout callout-style-simple callout-note">
<div class="callout-body d-flex">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-body-container">
<p><a href="https://github.com/shubhamx64/residual-thoughts-blog/tree/main/code/weight-space-map"><strong>Code</strong></a> – the fixes live in <code>weight_extraction.py</code>, <code>rope_utils.py</code>, and <code>activation_validation.py</code>, each now guarded by a parity test (<code>test_gamma_folding.py</code>, <code>test_rope_parity.py</code>). Corrected run: <code>analysis_20260610_233407.json</code>.</p>
</div>
</div>
</div>
<div class="callout callout-style-default callout-tip callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
<span class="screen-reader-only">Tip</span>TL;DR
</div>
</div>
<div class="callout-body-container callout-body">
<ul>
<li>Three bugs – <strong>RMSNorm gamma folding</strong> (missing the <img src="https://latex.codecogs.com/png.latex?+1">), <strong>RoPE pairing convention</strong> (interleaved vs rotate-half), and <strong>validating against the wrong activations</strong> – corrupted the published numbers.</li>
<li><strong>There is no late-layer anti-predictive regime.</strong> It was an artifact. After the fixes, mean Spearman <img src="https://latex.codecogs.com/png.latex?%5Crho"> between predicted and real attention rises from <strong>+0.011 (noise)</strong> to <strong>+0.229</strong> across all 26 layers, every layer positive, with 100% sign stability. The old “Regime 3” is retracted.</li>
<li><strong>The Layer-6 selectivity spike survives but is no longer unique</strong> – L8, L12, L14, and L22 sit in the same band. The identity/copy-routing peak actually lives at <strong>L10</strong>, not L6.</li>
<li><strong>New:</strong> the L13 oddity is a <em>Gemma Scope checkpoint</em> problem, not a model problem – a dictionary-free basis predicts L13 fine while the SAE fails.</li>
<li><strong>New:</strong> a mean-centered <strong>token-embedding basis with no SAE</strong> validates <em>slightly better overall</em> (0.260 vs 0.229) and wins 15/26 layers. For routing, the SAE dictionary is not load-bearing.</li>
<li><strong>The L23 causal result stands</strong> (ablation still helps retrieval +6.2 pts) but its <em>explanation</em> changes: L23H0 is well-predicted now, so “the map can’t see this head” is wrong.</li>
</ul>
</div>
</div>
<section id="the-three-bugs" class="level2">
<h2 class="anchored" data-anchor-id="the-three-bugs">The three bugs</h2>
<p>All three are convention mismatches with Hugging Face’s Gemma-2 implementation. None changed the <em>shape</em> of the method; all changed the <em>numbers</em>.</p>
<section id="rmsnorm-gamma-folding-missed-the-1" class="level3">
<h3 class="anchored" data-anchor-id="rmsnorm-gamma-folding-missed-the-1">1. RMSNorm gamma folding missed the <img src="https://latex.codecogs.com/png.latex?+1"></h3>
<p>I fold the pre-attention RMSNorm gain into the projection weights so the SAE basis lives in the right coordinate system. HF’s <code>Gemma2RMSNorm</code> computes</p>
<p><img src="https://latex.codecogs.com/png.latex?y%20=%20%5Cfrac%7Bx%7D%7B%5Coperatorname%7Brms%7D(x)%7D%5C,(1%20+%20%5Cgamma),"></p>
<p>but the pipeline folded <img src="https://latex.codecogs.com/png.latex?W%20%5Ccdot%20%5Cgamma"> instead of <img src="https://latex.codecogs.com/png.latex?W%20%5Ccdot%20(1%20+%20%5Cgamma)">. Gemma’s learned <img src="https://latex.codecogs.com/png.latex?%5Cgamma"> values are small, so this multiplied every folded weight by roughly <img src="https://latex.codecogs.com/png.latex?%5Cgamma"> instead of roughly <img src="https://latex.codecogs.com/png.latex?1"> – a large, layer-dependent distortion of every <img src="https://latex.codecogs.com/png.latex?B"> matrix and every selectivity number in the first post. Per-layer cosine similarity between the legacy and corrected <img src="https://latex.codecogs.com/png.latex?B"> matrices ranges from <strong>0.71 at L0 to 0.97</strong> deeper in the stack.</p>
<p>The fix is one term, but it touches everything, so it is now pinned by an algebraic parity test (<code>test_gamma_folding.py</code>) that checks <img src="https://latex.codecogs.com/png.latex?q_%5Ctext%7Bproj%7D(%5Ctext%7BRMSNorm%7D(x))%20=%20(x/%5Coperatorname%7Brms%7D(x))%5C,W_%5Ctext%7Bfolded%7D%5E%5Ctop"> to relative error <img src="https://latex.codecogs.com/png.latex?%3C%2010%5E%7B-4%7D">, and prints the legacy cosine per layer so the bug magnitude is reproducible.</p>
</section>
<section id="rope-pairing-convention" class="level3">
<h3 class="anchored" data-anchor-id="rope-pairing-convention">2. RoPE pairing convention</h3>
<p>The weight-space routing analysis rotates queries and keys analytically rather than sampling positions. I had assumed RoPE pairs <em>interleaved</em> dimensions <img src="https://latex.codecogs.com/png.latex?(2i,%202i%7B+%7D1)">; HF Gemma-2 uses the <strong>rotate-half</strong> convention, pairing <img src="https://latex.codecogs.com/png.latex?(i,%5C,%20i%20+%20d/2)"> with <img src="https://latex.codecogs.com/png.latex?d=256">. The two conventions agree on a scalar dot product only by accident, so this quietly corrupted every RoPE stability curve and the distance-binned validation.</p>
<p>Working the logit out from first principles, <img src="https://latex.codecogs.com/png.latex?%5Ctext%7Blogit%7D(t,s)%20=%20q%5E%5Ctop%20R(-%5CDelta)%5C,%20k"> with <img src="https://latex.codecogs.com/png.latex?%5CDelta%20=%20t%20-%20s">, so the <em>key</em> must be rotated by <img src="https://latex.codecogs.com/png.latex?-%5CDelta">. The rewrite is checked against HF’s own <code>apply_rotary_pos_emb</code> (<code>test_rope_parity.py</code>, max diff <img src="https://latex.codecogs.com/png.latex?%3C%2010%5E%7B-3%7D">), with the old interleaved formula kept as a <strong>negative control</strong> that differs by one to two orders of magnitude – so the test cannot pass by tautology. Under the corrected rotation, RoPE-stability AUCs rise everywhere (0.63–0.73 pre-fix to 0.65–0.89 fixed; peaks at L22 = 0.89, L14 = 0.88, L8 = 0.87).</p>
</section>
<section id="validation-encoded-the-wrong-activations" class="level3">
<h3 class="anchored" data-anchor-id="validation-encoded-the-wrong-activations">3. Validation encoded the wrong activations</h3>
<p>The first post’s validation compared weight-space predictions against SAE encodings of <em>post-layernorm</em> activations. But the Gemma Scope residual SAEs are trained on the <strong>raw residual stream</strong>, not the normalized one, and the mismatch between the two grows with depth – which is exactly the shape of the spurious late-layer inversion. The fix captures the raw block input via a <code>forward_pre_hook</code>, encodes it with per-token <img src="https://latex.codecogs.com/png.latex?1/%5Coperatorname%7Brms%7D"> scaling, and keeps the <img src="https://latex.codecogs.com/png.latex?(1+%5Cgamma)"> factor in the weights where it belongs. Key sampling is now seeded per head so before/after comparisons are clean A/Bs rather than re-rolls.</p>
</section>
</section>
<section id="retraction-there-is-no-anti-predictive-regime" class="level2">
<h2 class="anchored" data-anchor-id="retraction-there-is-no-anti-predictive-regime">Retraction: there is no anti-predictive regime</h2>
<p>This is the big one. Here is the per-layer mean Spearman <img src="https://latex.codecogs.com/png.latex?%5Crho"> between weight-space predicted attention and real attention (all 8 query heads, 20 prompts), pre-fix versus the corrected pipeline in two bases:</p>
<div id="fig-prefix-vs-fixed" class="quarto-float quarto-figure quarto-figure-center anchored">
<figure class="quarto-float quarto-float-fig figure">
<div aria-describedby="fig-prefix-vs-fixed-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
<img src="https://www.residual-thoughts.com/posts/correcting-the-weight-space-map/figs/fig_prefix_vs_fixed_spearman.png" class="img-fluid figure-img">
</div>
<figcaption class="quarto-float-caption-bottom quarto-float-caption quarto-float-fig" id="fig-prefix-vs-fixed-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
Figure&nbsp;1: Per-layer mean Spearman between predicted and real attention. The pre-fix curve (grey) dips firmly negative in the upper-middle layers – the “anti-predictive regime” of the first post. After the fixes (blue), every layer is positive. The shaded red regions mark where the old pipeline was anti-correlated.
</figcaption>
</figure>
</div>
<div id="tbl-spearman" class="quarto-float quarto-figure quarto-figure-center anchored">
<figure class="quarto-float quarto-float-tbl figure">
<figcaption class="quarto-float-caption-top quarto-float-caption quarto-float-tbl" id="tbl-spearman-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
Table&nbsp;1: Selected layers; bold marks the pre-fix values the first post read as “anti-predictive.” The full 26-layer table is in the errata note.
</figcaption>
<div aria-describedby="tbl-spearman-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
<table class="caption-top table">
<thead>
<tr class="header">
<th>layer</th>
<th>pre-fix</th>
<th>fixed (SAE)</th>
<th>fixed (token)</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td>5</td>
<td>0.143</td>
<td>0.289</td>
<td>0.176</td>
</tr>
<tr class="even">
<td>9</td>
<td><strong>-0.156</strong></td>
<td>0.255</td>
<td>0.375</td>
</tr>
<tr class="odd">
<td>10</td>
<td><strong>-0.193</strong></td>
<td>0.164</td>
<td>0.351</td>
</tr>
<tr class="even">
<td>12</td>
<td><strong>-0.184</strong></td>
<td>0.311</td>
<td>0.329</td>
</tr>
<tr class="odd">
<td>13</td>
<td><strong>-0.248</strong></td>
<td>0.022</td>
<td>0.377</td>
</tr>
<tr class="even">
<td>16</td>
<td><strong>-0.113</strong></td>
<td>0.238</td>
<td>0.379</td>
</tr>
<tr class="odd">
<td>24</td>
<td><strong>-0.064</strong></td>
<td>0.334</td>
<td>0.327</td>
</tr>
<tr class="even">
<td>25</td>
<td>0.244</td>
<td>0.220</td>
<td>0.288</td>
</tr>
</tbody>
</table>
</div>
</figure>
</div>
<p>The grand means tell the story: pre-fix <strong>+0.011</strong> (statistical noise), fixed SAE basis <strong>+0.229</strong>, fixed token basis <strong>+0.260</strong>. Every layer the first post labelled anti-predictive – L9, L10, L12, L13, L16, L24 – is solidly positive after the fixes, with <strong>100% sign stability</strong> across heads. Best single heads reach <img src="https://latex.codecogs.com/png.latex?%5Crho%20=%200.50"> overall (L11H3) and <img src="https://latex.codecogs.com/png.latex?%5Crho%20=%200.63"> on local distances (L6H0).</p>
<div class="callout callout-style-default callout-important callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
Important
</div>
</div>
<div class="callout-body-container callout-body">
<p><strong>Regime 3 is retracted.</strong> The first post’s “three depth regimes” framing had a real early/transport regime and a real mid-stack routing band, but the third “late-layer anti-predictive” regime was entirely an artifact of bug #3 interacting with depth. There is no regime where the weight map stops working.</p>
</div>
</div>
</section>
<section id="the-layer-6-spike-survives-its-uniqueness-does-not" class="level2">
<h2 class="anchored" data-anchor-id="the-layer-6-spike-survives-its-uniqueness-does-not">The Layer-6 spike survives, its uniqueness does not</h2>
<p>The first post made a lot of a Layer-6 selectivity spike that appeared to tower roughly <img src="https://latex.codecogs.com/png.latex?105%5Ctimes"> over a uniform baseline – two orders of magnitude above everything else. The spike is real, but the <em>contrast</em> was inflated by the gamma bug. Corrected selectivity (<img src="https://latex.codecogs.com/png.latex?%5Ctext%7BSel%7D%5Ctimes%5Ctext%7BU%7D">, top-1 softmax mass over uniform): L6 still peaks globally at <strong>1451</strong>, but <strong>L8 (1176)</strong>, <strong>L22 (1113)</strong>, <strong>L12 (866)</strong>, and <strong>L14 (912)</strong> sit in the same band, all 3–25<img src="https://latex.codecogs.com/png.latex?%5Ctimes"> above a random-weights baseline. The corrected story is a <em>band of selective layers</em> from L6 to L14 plus a late peak at L22 – not a lone spike.</p>
<p>And the identity/copy-routing peak – the diagonal mass of the routing matrix – isn’t even at L6. It’s at <strong>L10</strong>:</p>
<div id="fig-diag-mass" class="quarto-float quarto-figure quarto-figure-center anchored">
<figure class="quarto-float quarto-float-fig figure">
<div aria-describedby="fig-diag-mass-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
<img src="https://www.residual-thoughts.com/posts/correcting-the-weight-space-map/figs/fig_diag_mass_by_layer.png" class="img-fluid figure-img">
</div>
<figcaption class="quarto-float-caption-bottom quarto-float-caption quarto-float-fig" id="fig-diag-mass-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
Figure&nbsp;2: Mean diagonal softmax mass by layer (how much a head routes each feature to itself). The corrected peak is L10 at 0.124; L6 (the first post’s headline) is well down the list. This matches an independent weight-space finding that 2-hop composition chains relay through L8–L11 into L12/L13 readers.
</figcaption>
</figure>
</div>
</section>
<section id="new-the-l13-anomaly-is-an-sae-checkpoint-issue" class="level2">
<h2 class="anchored" data-anchor-id="new-the-l13-anomaly-is-an-sae-checkpoint-issue">New: the L13 anomaly is an SAE checkpoint issue</h2>
<p>L13 is the one layer where the corrected <em>SAE-basis</em> validation still fails (mean <img src="https://latex.codecogs.com/png.latex?%5Crho%20=%200.022">). That looked alarming until I probed the same layer three ways:</p>
<div id="tbl-l13" class="quarto-float quarto-figure quarto-figure-center anchored">
<figure class="quarto-float quarto-float-tbl figure">
<figcaption class="quarto-float-caption-top quarto-float-caption quarto-float-tbl" id="tbl-l13-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
Table&nbsp;2: Three bases on L13 attention. The conceptually correct SAE is the worst; a dictionary-free basis is the best.
</figcaption>
<div aria-describedby="tbl-l13-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
<table class="caption-top table">
<colgroup>
<col style="width: 50%">
<col style="width: 50%">
</colgroup>
<thead>
<tr class="header">
<th>probe</th>
<th>mean Spearman (8 heads)</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td>layer-12 SAE (standard <img src="https://latex.codecogs.com/png.latex?-1"> offset, conceptually correct tap)</td>
<td>+0.022</td>
</tr>
<tr class="even">
<td>layer-13 SAE (offset 0, conceptually misaligned tap)</td>
<td>+0.176</td>
</tr>
<tr class="odd">
<td>token-embedding basis (no SAE)</td>
<td>+0.377</td>
</tr>
</tbody>
</table>
</div>
</figure>
</div>
<p>The conceptually <em>correct</em> SAE is the <em>worst</em> of the three, and the dictionary-free basis is the best. So attention at L13 is well-predicted by weight space – the <code>gemma-scope-2b-pt-res</code> layer-12 canonical 16k checkpoint is just a poor dictionary for this purpose. The practical lesson: <strong>basis disagreement is a diagnostic.</strong> When three reasonable bases disagree this sharply on one layer, suspect the dictionary, not the model. Any SAE analysis leaning on that particular checkpoint deserves a second look.</p>
</section>
<section id="new-a-dictionary-free-basis-is-competitive" class="level2">
<h2 class="anchored" data-anchor-id="new-a-dictionary-free-basis-is-competitive">New: a dictionary-free basis is competitive</h2>
<p>That L13 result generalizes. I re-ran the whole validation using a <em>mean-centered token-embedding matrix</em> as the probe basis – no SAE at all – and it validates <strong>slightly better overall</strong> than the Gemma Scope SAEs:</p>
<div id="fig-dual-basis" class="quarto-float quarto-figure quarto-figure-center anchored">
<figure class="quarto-float quarto-float-fig figure">
<div aria-describedby="fig-dual-basis-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
<img src="https://www.residual-thoughts.com/posts/correcting-the-weight-space-map/figs/fig_dual_basis.png" class="img-fluid figure-img">
</div>
<figcaption class="quarto-float-caption-bottom quarto-float-caption quarto-float-fig" id="fig-dual-basis-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
Figure&nbsp;3: Per-layer mean Spearman, SAE basis vs a dictionary-free token-embedding basis. The token basis (green) wins 15/26 layers, including nearly everything from L7 to L20. The SAE basis wins at L0, the L5–L6 band, and L21–L24.
</figcaption>
</figure>
</div>
<p>Grand mean: <strong>token 0.260 vs SAE 0.229</strong>, with the token basis winning 15/26 layers. The SAE wins at L0, the L5–L6 selectivity band, and L21–L24. The honest reading is uncomfortable for the original framing: <strong>for routing analysis, the SAE dictionary is not load-bearing.</strong> A free, interpretable-enough basis does as well or better through most of the stack. The SAE earns its keep where you want <em>named</em> features for the OV/writing story, not for predicting <em>where</em> attention goes.</p>
</section>
<section id="the-l23-causal-result-re-explained" class="level2">
<h2 class="anchored" data-anchor-id="the-l23-causal-result-re-explained">The L23 causal result, re-explained</h2>
<p>The first post’s one causal experiment – ablating individual Layer-23 heads on a factual-retrieval task – still replicates: removing <strong>L23H0 improves accuracy by +6.2 points</strong> (0.595 to 0.657, 800 prompts; L23H2 gives +3.2). That finding stands.</p>
<p>What does not stand is the <em>explanation</em>. The first post tied the improvement to the late-layer anti-predictive regime – “the map flagged a high-leverage layer but couldn’t read its sign.” Under the corrected map, L23H0 is <strong>well-predicted</strong> (overall <img src="https://latex.codecogs.com/png.latex?%5Crho%20=%200.32">, local <img src="https://latex.codecogs.com/png.latex?0.57">), so the map <em>can</em> see it. The corrected read of what L23H0 <em>is</em>: a <strong>REPULSION</strong> routing archetype (strong negative affinities, min <img src="https://latex.codecogs.com/png.latex?%5Capprox%20-37">) wearing a high copy-dominance OV circuit (<code>copy_dominance</code> <img src="https://latex.codecogs.com/png.latex?=%200.95">). A head that copies content under repulsion-shaped routing plausibly drags non-answer context into the final residual on cluttered prompts – consistent with ablation <em>helping</em> on a filler-heavy retrieval task. Prediction quality and causal helpfulness are orthogonal; the first post conflated them. Independent corroboration that L23 is special: instruction tuning moves L23H0/H1 more than almost any other heads in the model (base vs <code>-it</code> weight diff).</p>
</section>
<section id="what-id-take-away" class="level2">
<h2 class="anchored" data-anchor-id="what-id-take-away">What I’d take away</h2>
<ul>
<li><strong>Pin your conventions with parity tests.</strong> All three bugs were silent agreements-by-accident with HF that only a direct numeric cross-check exposes. The two parity tests now make these regressions loud.</li>
<li><strong>A negative result on a basis is a result about the basis.</strong> The L13 “failure” was the most informative single layer once I stopped assuming the SAE was ground truth.</li>
<li><strong>Don’t over-credit the dictionary.</strong> For routing geometry, a dictionary-free basis matched or beat Gemma Scope across most of the stack. SAEs are still the right tool for <em>naming</em> what a head writes – just not for deciding whether the weight map can predict attention at all.</li>
</ul>
<div class="callout callout-style-simple callout-note">
<div class="callout-body d-flex">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-body-container">
<p>One pipeline-health caveat for circuit claims: 21.6% of feature programs in the full-depth run used a fallback self-write injection, so the explicit-only program histogram is the trustworthy one. Full corrected numbers, all 26 layers, and the dual-basis breakdown are in <code>errata_full_depth.md</code> alongside the code.</p>
</div>
</div>
</div>
</section>
<section id="appendix-full-table" class="level2">
<h2 class="anchored" data-anchor-id="appendix-full-table">Appendix: full table</h2>
<p>The complete per-layer Spearman table (pre-fix, fixed SAE, fixed token) for all 26 layers, plus the corrected selectivity, RoPE-stability AUC, and dual-basis per-layer winners, is in <a href="https://github.com/shubhamx64/residual-thoughts-blog/tree/main/code/weight-space-map"><code>errata_full_depth.md</code></a> with the accompanying run JSON. The figures above are generated from <code>analysis_20260610_233407.json</code> and the 20-prompt validation outputs.</p>


</section>

 ]]></description>
  <category>mechanistic-interpretability</category>
  <category>gemma-2</category>
  <category>sae</category>
  <category>attention-heads</category>
  <category>errata</category>
  <guid>https://www.residual-thoughts.com/posts/correcting-the-weight-space-map/</guid>
  <pubDate>Sun, 14 Jun 2026 00:00:00 GMT</pubDate>
  <media:content url="https://www.residual-thoughts.com/posts/correcting-the-weight-space-map/figs/hero.png" medium="image" type="image/png" height="75" width="144"/>
</item>
<item>
  <title>A Weight-Space Map of Attention Heads in Gemma-2 (with SAEs)</title>
  <dc:creator>Shubham Srivastava</dc:creator>
  <link>https://www.residual-thoughts.com/posts/weight-space-map-of-attention-heads/</link>
  <description><![CDATA[ 





<p><span class="post-stamp red">partially retracted → corrected</span> <span class="post-stamp pencil">see the <a href="../correcting-the-weight-space-map/">errata post</a> (2026-06-14)</span></p>
<p>This post uses model weights plus a fixed SAE feature basis to map what kinds of routing and writing patterns are available inside Gemma-2 attention heads. The emphasis is on <em>available linear structure</em> in a shared feature coordinate system, not on claiming that a head uses that structure on typical tokens.</p>
<p>The payoff is a depth-wise map of head behavior without collecting new activations or running generation for the main analysis. That map makes it possible to ask a narrower question than “what does this head do?”: <em>what kinds of computations do the weights make available here, and where do those capabilities concentrate with depth?</em></p>
<p>This is a map-making exercise with explicit measurement objects and clear failure modes, not a causal claim. Sign and task relevance still require experiments, which is why the later causal section is a preview.</p>
<div class="callout callout-style-simple callout-note">
<div class="callout-body d-flex">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-body-container">
<p><a href="https://github.com/shubhamx64/residual-thoughts-blog/tree/main/code/weight-space-map"><strong>Code</strong></a> – snapshot of the analysis pipeline accompanying this post.</p>
</div>
</div>
</div>
<div class="callout callout-style-default callout-important callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
<span class="screen-reader-only">Important</span>Errata (2026-06-14): some claims below are retracted
</div>
</div>
<div class="callout-body-container callout-body">
<p>After publishing, I found <strong>three pipeline bugs</strong> – RMSNorm gamma folding (missing the <img src="https://latex.codecogs.com/png.latex?+1">), the RoPE pairing convention, and validating against the wrong (post-layernorm) activations. After fixing them and re-running, two headline claims on this page <strong>do not hold</strong>:</p>
<ul>
<li>The <strong>late-layer “anti-predictive regime” (Regime 3) was an artifact.</strong> Every layer is solidly predictive once the bugs are fixed (grand-mean Spearman <img src="https://latex.codecogs.com/png.latex?%5Crho"> rises from <img src="https://latex.codecogs.com/png.latex?+0.011"> to <img src="https://latex.codecogs.com/png.latex?+0.229">, every layer positive). There is no anti-predictive regime.</li>
<li>The <strong>unique Layer-6 selectivity spike is overstated</strong> – the spike survives but L8/L12/L14/L22 share the band, and the identity-routing peak is actually at L10.</li>
</ul>
<p>The Layer-23 causal result still replicates, but its explanation changes. The corrected map, two new studies (an SAE-checkpoint diagnostic at L13 and a dictionary-free dual-basis comparison), and the full numbers are in the follow-up: <a href="../correcting-the-weight-space-map/"><strong>Correcting the Weight-Space Map</strong></a>. The body below is left as originally published for the record.</p>
</div>
</div>
<div class="callout callout-style-default callout-tip callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
<span class="screen-reader-only">Tip</span>TL;DR
</div>
</div>
<div class="callout-body-container callout-body">
<ul>
<li>We map 200 attention heads (layers 1–25 of Gemma-2-2B) using weight-space analysis with SAE decoder directions – no activations required.</li>
<li><strong>Weight-space maps are reliable in early/mid layers</strong> (local <img src="https://latex.codecogs.com/png.latex?%5Crho"> up to 0.82, 144/200 heads show local structure), <strong>but become anti-predictive in late layers</strong> (mean <img src="https://latex.codecogs.com/png.latex?%5Crho"> = -0.14 after L17). The overall mean <img src="https://latex.codecogs.com/png.latex?%5Crho"> = 0.07 averages out this sign flip.</li>
<li>Layer 6 shows an extreme selectivity spike (105<img src="https://latex.codecogs.com/png.latex?%5Ctimes"> uniform baseline) – two orders of magnitude above other layers.</li>
<li>Depth-dependent program shifts: early layers carry high SUPPRESS share alongside CROSS_COPY (filtering-like), mid layers mix CROSS_COPY/SUPPRESS/REINFORCE (composing-like), late layers lean CROSS_COPY (propagating-like).</li>
<li>Long-range routing structure weakens but persists: 114/200 heads clear <img src="https://latex.codecogs.com/png.latex?%7C%5Crho%7C%20%5Cgeq%200.10"> at 128–256 tokens, 108/200 at 256+.</li>
<li>OV negatives are late-layer outliers, not a layer-wide flip; strongest outlier is L20H7 at -0.64.</li>
<li><strong>Causal preview</strong>: ablating Layer 23 heads <em>improved</em> retrieval accuracy by +8.7% – the map identified a high-leverage layer, but not the sign of its behavioral effect. Full results in Post 2.</li>
</ul>
</div>
</div>
<div class="callout callout-style-default callout-note callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
<span class="screen-reader-only">Note</span>Roadmap: Three Depth Regimes
</div>
</div>
<div class="callout-body-container callout-body">
<p>The post builds three measurement objects (QK routing, OV writing, feature programs) and validates them against activations. The punchline is that depth organizes attention into three regimes:</p>
<ol type="1">
<li><strong>Early layers (L1–L5):</strong> feature transport and filtering – modest selectivity, validated routing, SUPPRESS-heavy programs.</li>
<li><strong>Mid layers (L6–L12):</strong> a routing bottleneck – selectivity spikes (Layer 6 at 105<img src="https://latex.codecogs.com/png.latex?%5Ctimes"> baseline), the map is most predictive here, strongest validation-confirmed heads cluster.</li>
<li><strong>Late layers (L17+):</strong> the map becomes anti-predictive at runtime (<img src="https://latex.codecogs.com/png.latex?%5Crho"> flips negative) – still shows structure in weights, but the fixed SAE basis stops being a reliable behavioral proxy.</li>
</ol>
<p>The sections below build up to this picture. If you want the conclusion first, jump to Three Depth Regimes.</p>
</div>
</div>
<section id="setup" class="level2">
<h2 class="anchored" data-anchor-id="setup">Setup</h2>
<p>We use the base model Gemma-2-2B (26 layers).</p>
<p>It uses Grouped Query Attention (GQA): 8 query heads and 4 KV heads, so each KV head is shared by 2 query heads. This means each query head has its own output projection (<img src="https://latex.codecogs.com/png.latex?W_O">) but reads from a shared value signal (<img src="https://latex.codecogs.com/png.latex?W_V">). We analyze query heads (not KV heads), since each has its own routing and writing behavior.</p>
<p>One implication is worth keeping in mind later: sister query heads share the same value source but can still look very different in the map. When that happens, the specialization is unlikely to come from <img src="https://latex.codecogs.com/png.latex?W_V"> alone; it has to be coming from routing (<img src="https://latex.codecogs.com/png.latex?W_Q/W_K">), the per-head output projection (<img src="https://latex.codecogs.com/png.latex?W_O">), or both.</p>
<ol type="1">
<li><p>Gemma-2 scales attention logits by <img src="https://latex.codecogs.com/png.latex?%5Ctext%7Bquery%5C_pre%5C_attn%5C_scalar%7D%5E%7B-1/2%7D">. With <code>query_pre_attn_scalar</code> = 256, we have <img src="https://latex.codecogs.com/png.latex?s%20=%20%5Csqrt%7B%5Cfrac%7B1%7D%7B256%7D%7D%20=%20%5Cfrac%7B1%7D%7B16%7D%20=%200.0625">.</p></li>
<li><p>Softcap value: <img src="https://latex.codecogs.com/png.latex?c%20=%2050.0">.</p></li>
<li><p>Gemma-2 uses sliding window attention in alternating layers starting at index 0: even layers are sliding-window with size 4096, odd layers are global (full causal attention up to the model’s max context length, 8192 tokens).</p></li>
</ol>
<p>To talk about “features” rather than neurons, I use Google’s Gemma Scope Sparse Autoencoders (SAEs). SAEs decompose model activations into sparse, interpretable directions. Each learned “feature” is a direction in residual-stream space that tends to activate for a coherent pattern. This gives us a model-aligned coordinate system for analyzing attention: instead of asking “which neuron fires?”, we can ask “which interpretable feature does this head route or write?” These SAEs are trained on base Gemma-2 models.</p>
<p>Analysis choice: I use the SAEs trained at the “Residual SAE” tap point. Attention in block L consumes the residual stream produced by block L-1. Thus, in my runs I use an SAE layer offset (-1) so the SAE basis matches what the attention block actually sees. Each SAE feature has a decoder direction (a vector in residual space). I’ll treat those decoder directions as a convenient, model-aligned basis for analysis. Please note that SAE decoder directions are not orthogonal and activations are nonnegative.</p>
<div class="callout callout-style-default callout-note callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
<span class="screen-reader-only">Note</span>Gemma Scope SAEs
</div>
</div>
<div class="callout-body-container callout-body">
<p>For details on the available SAEs, see <a href="https://ai.google.dev/gemma/docs/gemma_scope">Google’s Gemma Scope documentation</a>.</p>
</div>
</div>
<div class="callout callout-style-default callout-warning callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
<span class="screen-reader-only">Warning</span>Key Approximation: RMSNorm Folding
</div>
</div>
<div class="callout-body-container callout-body">
<p>Throughout, I fold the learned RMSNorm scale (<img src="https://latex.codecogs.com/png.latex?%5Cgamma">) into weights but ignore the token-dependent normalization factor (<img src="https://latex.codecogs.com/png.latex?1/%5C%7Cx%5C%7C_%7B%5Ctext%7BRMS%7D%7D">). This makes the analysis strictly linear-in-direction rather than exact-in-activation. The approximation is tighter when the residual stream has stable norms (early/mid layers) and more suspect in layers with high statistical heterogeneity – which may partly explain the late-layer degradation discussed later.</p>
</div>
</div>
<p>Using the base model avoids instruction-tuning artifacts and keeps the focus on the raw attention mechanism.</p>
<div class="callout callout-style-default callout-note callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
<span class="screen-reader-only">Note</span>Feature Subset Note
</div>
</div>
<div class="callout-body-container callout-body">
<p>The main weight-space metrics (selectivity, program distribution, write archetypes) use a <strong>4,096-feature random subset</strong> of the 16,384 SAE features for computational tractability. Qualitative patterns are stable across seeds. The activation validation run (Sanity Check section) uses the <strong>full 16,384-feature set</strong> with RoPE-aware affinity matrices. When absolute numbers differ between sections, this is usually why.</p>
</div>
</div>
</section>
<section id="weight-space-object-1-content-only-routing-in-feature-space" class="level2">
<h2 class="anchored" data-anchor-id="weight-space-object-1-content-only-routing-in-feature-space">Weight-Space Object #1: Content-Only Routing in Feature Space</h2>
<p>For a single attention head, I want a matrix that answers:</p>
<p>“If the query looks like feature i, which key-feature j does this head prefer?”</p>
<p>Let:</p>
<ul>
<li><p><img src="https://latex.codecogs.com/png.latex?D"> be the SAE decoder matrix for a subset of features (shape <img src="https://latex.codecogs.com/png.latex?n%20%5Ctimes%0Ad_%7B%5Ctext%7Bmodel%7D%7D">), one row per feature direction. (Convention: I treat decoder directions as row vectors; if you store decoders as columns, transpose accordingly.)</p></li>
<li><p><img src="https://latex.codecogs.com/png.latex?W_%7BQ%7D">, <img src="https://latex.codecogs.com/png.latex?W_%7BK%7D"> be the head’s query/key projection matrices</p></li>
</ul>
<p>Project decoder directions into Q-space and K-space:</p>
<p><img src="https://latex.codecogs.com/png.latex?Q_f%20=%20D%20%5Ccdot%20W_Q%5ET"></p>
<p><img src="https://latex.codecogs.com/png.latex?K_f%20=%20D%20%5Ccdot%20W_K%5ET"></p>
<p>Define the content-only affinity matrix (what attention logits would look like if RoPE and masking weren’t present):</p>
<p><img src="https://latex.codecogs.com/png.latex?B%20=%20(Q_f%20%5Ccdot%20K_f%5ET)%20%5Ccdot%20s"></p>
<p>Where <img src="https://latex.codecogs.com/png.latex?s%20=%20%5Csqrt%7B%5Cfrac%7B1%7D%7B256%7D%7D%20=%20%5Cfrac%7B1%7D%7B16%7D%20=%200.0625"> (Gemma-2’s attention logit scaling).</p>
<p>Gemma-2 also applies a tanh soft-cap to attention logits; when matching the model’s behavior, I apply the same cap:</p>
<p><img src="https://latex.codecogs.com/png.latex?%5Ctext%7Bsoftcap%7D(x)%20=%20c%20%5Ccdot%20%5Ctanh%5Cleft(%5Cfrac%7Bx%7D%7Bc%7D%5Cright)"></p>
<p>This <img src="https://latex.codecogs.com/png.latex?B"> matrix is the backbone of our routing analysis. From <img src="https://latex.codecogs.com/png.latex?B"> we can define simple, interpretable routing metrics:</p>
<ul>
<li><p><strong>Selectivity</strong> (peakiness): per row i, softmax over keys and take max probability; average over i – “top-1 softmax mass”</p></li>
<li><p><strong>Identity sensitivity</strong>: how much softmax probability lands on the diagonal entry (i attends to i) – “diagonal softmax mass”</p></li>
<li><p><strong>Max-gap</strong>: difference between best key score and runner-up (in logits), averaged over rows</p></li>
<li><p><strong>Avoidance</strong>: Negative entries in <img src="https://latex.codecogs.com/png.latex?B"> indicate low dot-product affinity. I treat extreme negative tails as a signature of selective exclusion, but it’s not the same thing as suppression unless the <img src="https://latex.codecogs.com/png.latex?OV"> path also writes negative feature directions.</p></li>
</ul>
<p>Important: rows/cols of <img src="https://latex.codecogs.com/png.latex?B"> are SAE features, not tokens. When I compute “top-1 softmax mass”, the softmax is over key features j, not over token positions.</p>
<p>A few examples:</p>
<div id="fig-routing-diagonal" class="quarto-float quarto-figure quarto-figure-center anchored">
<figure class="quarto-float quarto-float-fig figure">
<div aria-describedby="fig-routing-diagonal-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
<img src="https://www.residual-thoughts.com/posts/weight-space-map-of-attention-heads/figs/FIG1_a_diag_SELF_MATCH_L10H5.png" class="img-fluid figure-img">
</div>
<figcaption class="quarto-float-caption-bottom quarto-float-caption quarto-float-fig" id="fig-routing-diagonal-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
Figure&nbsp;1: Diagonal head (SELF_MATCH): Layer 10, Head 5. The B matrix (query features <img src="https://latex.codecogs.com/png.latex?%5Ctimes"> key features) shows strong diagonal structure – this head preferentially routes each feature to itself. High top-1 softmax mass indicates peaked routing. (Red = positive content affinity, blue = negative. Axes are SAE feature indices.)
</figcaption>
</figure>
</div>
<div id="fig-routing-selective" class="quarto-float quarto-figure quarto-figure-center anchored">
<figure class="quarto-float quarto-float-fig figure">
<div aria-describedby="fig-routing-selective-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
<img src="https://www.residual-thoughts.com/posts/weight-space-map-of-attention-heads/figs/FIG1_b_offdiag_selective_L15H0.png" class="img-fluid figure-img">
</div>
<figcaption class="quarto-float-caption-bottom quarto-float-caption quarto-float-fig" id="fig-routing-selective-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
Figure&nbsp;2: Selective off-diagonal head: Layer 15, Head 0. Strong selectivity (peaked rows) but the peaks are off-diagonal – this head routes features to <em>specific other</em> features rather than to themselves.
</figcaption>
</figure>
</div>
<div id="fig-routing-avoidance" class="quarto-float quarto-figure quarto-figure-center anchored">
<figure class="quarto-float quarto-float-fig figure">
<div aria-describedby="fig-routing-avoidance-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
<img src="https://www.residual-thoughts.com/posts/weight-space-map-of-attention-heads/figs/FIG1_c_repulsion_L6H3.png" class="img-fluid figure-img">
</div>
<figcaption class="quarto-float-caption-bottom quarto-float-caption quarto-float-fig" id="fig-routing-avoidance-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
Figure&nbsp;3: Avoidance head: Layer 6, Head 3. The B matrix shows strong columnar structure (certain key features attract many queries) alongside negative entries (dark blue), indicating a mix of selective attraction and avoidance.
</figcaption>
</figure>
</div>
<section id="sanity-check-does-b-predict-real-attention-routing" class="level3">
<h3 class="anchored" data-anchor-id="sanity-check-does-b-predict-real-attention-routing">Sanity Check: Does B Predict Real Attention Routing?</h3>
<p>Although the map in this post is computed purely from weights plus a fixed SAE basis, I ran an activation-grounding sanity check to confirm that the main routing object (the feature affinity matrix <img src="https://latex.codecogs.com/png.latex?B">) predicts runtime attention patterns.</p>
<p>Across 200 heads (25 layers x 8 heads), using all 16,384 SAE features and RoPE-aware affinity matrices <img src="https://latex.codecogs.com/png.latex?B_%5CDelta">, the activation-grounding run covers about 4.45M token pairs per head. The headline result is mixed in exactly the way that turned out to matter for interpretation: the map is usefully predictive in early and mid layers, but degrades sharply in late layers. The layerwise spread and threshold counts are shown in Figure&nbsp;4 and Figure&nbsp;5.</p>
<div id="fig-validation-box" class="quarto-float quarto-figure quarto-figure-center anchored">
<figure class="quarto-float quarto-float-fig figure">
<div aria-describedby="fig-validation-box-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
<img src="https://www.residual-thoughts.com/posts/weight-space-map-of-attention-heads/figs/fig01_local_spearman_box.png" class="img-fluid figure-img">
</div>
<figcaption class="quarto-float-caption-bottom quarto-float-caption quarto-float-fig" id="fig-validation-box-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
Figure&nbsp;4: Local (0–4 token) Spearman <img src="https://latex.codecogs.com/png.latex?%5Crho"> by layer (16K validation)
</figcaption>
</figure>
</div>
<div id="fig-validation-thresholds" class="quarto-float quarto-figure quarto-figure-center anchored">
<figure class="quarto-float quarto-float-fig figure">
<div aria-describedby="fig-validation-thresholds-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
<img src="https://www.residual-thoughts.com/posts/weight-space-map-of-attention-heads/figs/fig02_local_thresholds.png" class="img-fluid figure-img">
</div>
<figcaption class="quarto-float-caption-bottom quarto-float-caption quarto-float-fig" id="fig-validation-thresholds-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
Figure&nbsp;5: Heads with <img src="https://latex.codecogs.com/png.latex?%7C%5Ctext%7Blocal%20%7D%20%5Crho%7C"> above thresholds by layer
</figcaption>
</figure>
</div>
<p>For ~25 diverse prompts, I captured the pre-attention residual stream at the target layer, encoded it into the same SAE feature subset used in the weight-space map, and predicted attention preferences using the bilinear form <img src="https://latex.codecogs.com/png.latex?a(t)%5ET%20B%5C,%20a(s)">. For the runtime target, I used log attention weights. I compute correlations per query row and then average (to avoid cross-row normalization artifacts), and I also break correlations out by distance bins; log-softmax equals logits up to a per-row constant, so rank correlation is meaningful even though each row has its own normalization.</p>
<p>To account for RoPE, I also computed RoPE-aware affinity matrices <img src="https://latex.codecogs.com/png.latex?B_%5CDelta"> per distance bin by rotating K (<img src="https://latex.codecogs.com/png.latex?%5CDelta"> set to the bin center) and applying Gemma’s tanh softcap, then compared predicted vs runtime attention within each distance bin.</p>
<p>Summary statistics for the same run:</p>
<ul>
<li>~4.5M token pairs per head</li>
<li>Mean overall Spearman <img src="https://latex.codecogs.com/png.latex?%5Crho"> = 0.07 (std=0.21)</li>
<li>Mean local (0–4 tokens) Spearman <img src="https://latex.codecogs.com/png.latex?%5Crho"> = 0.15 (max=0.82)</li>
<li>144 heads (72%) show <img src="https://latex.codecogs.com/png.latex?%7C%5Ctext%7Blocal%20%7D%20%5Crho%7C%20%3E%200.1"> (‘structured’ routing)</li>
<li>Best local correlation: L5H4 with <img src="https://latex.codecogs.com/png.latex?%5Crho"> = 0.82</li>
<li>Best overall correlation: L6H4 with <img src="https://latex.codecogs.com/png.latex?%5Crho"> = 0.57</li>
</ul>
<p>The important caveat is that correlation is not uniform across depth. In early layers (L1–L5), 35 of 40 heads show positive overall Spearman <img src="https://latex.codecogs.com/png.latex?%5Crho"> (mean +0.16), and mid layers (L6–L12) are even stronger (47/56 positive, mean +0.22). But in late layers (L19–L25), 50 of 56 heads show negative overall Spearman (mean -0.14), meaning the weight-space <img src="https://latex.codecogs.com/png.latex?B"> matrix predicts the opposite of the observed attention pattern. This degradation likely reflects nonlinear interactions or representational drift that are not captured by the linear SAE decomposition. My best guess is that the fixed residual-stream feature basis becomes a poorer proxy for the computation actually being deployed near the top of the network, where the model is closer to logits and late residual mixing is more consequential. That is an interpretation, not a demonstrated mechanism. The weight-space map remains a useful guide in early/mid layers but should be treated as a capacity map rather than a behavior map past roughly Layer 17.</p>
<p>The distribution is heavy-tailed: many heads are near-zero (diffuse), while a smaller set shows strong, structured routing (e.g., best local <img src="https://latex.codecogs.com/png.latex?%5Crho"> = 0.82). The top 10 heads by overall Spearman (L6H4, L5H4, L3H2, L6H3, L8H1, L8H0, L6H2, L8H6, L12H3, L12H2; all <img src="https://latex.codecogs.com/png.latex?%5Crho"> &gt; 0.45) cluster in layers 3–12.</p>
<p>Please note that this doesn’t prove any head is causally responsible for a behavior; it just checks that the measurement objects used in the map are anchored to runtime attention.</p>
<div class="callout callout-style-default callout-important callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
<span class="screen-reader-only">Important</span>Update (March 2026): Long-Distance Routing
</div>
</div>
<div class="callout-body-container callout-body">
<p>Long-range structure is weaker than local routing, but it is not absent. At 128–256 tokens, 114/200 heads still clear <img src="https://latex.codecogs.com/png.latex?%7C%5Crho%7C%20%5Cgeq%200.10">, and 108/200 do so at 256+; the more important failure mode is the late-layer sign reversal in overall routing correlations rather than a complete disappearance of long-range structure.</p>
</div>
</div>
<div id="fig-long-distance" class="quarto-float quarto-figure quarto-figure-center anchored">
<figure class="quarto-float quarto-float-fig figure">
<div aria-describedby="fig-long-distance-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
<img src="https://www.residual-thoughts.com/posts/weight-space-map-of-attention-heads/figs/fig03_long_thresholds.png" class="img-fluid figure-img">
</div>
<figcaption class="quarto-float-caption-bottom quarto-float-caption quarto-float-fig" id="fig-long-distance-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
Figure&nbsp;6: Head count exceeding correlation thresholds at 128–256 and 256+ token distances. Left panel: 128–256 bin; right panel: 256+ bin.
</figcaption>
</figure>
</div>
</section>
</section>
<section id="baselines-what-counts-as-real-structure" class="level2">
<h2 class="anchored" data-anchor-id="baselines-what-counts-as-real-structure">Baselines: What Counts as “Real Structure”?</h2>
<p>Softmax selectivity is extremely sensitive to logit scale, so we need baselines that match the right thing. I use three baselines, each designed to kill a different kind of structure:</p>
<ol type="1">
<li><p><strong>Random decoder directions</strong> (random D): Replace decoder directions with random unit vectors (matched norms), keeping <img src="https://latex.codecogs.com/png.latex?W_Q">/<img src="https://latex.codecogs.com/png.latex?W_K"> fixed</p></li>
<li><p><strong>Random weights baseline</strong>: Replace <img src="https://latex.codecogs.com/png.latex?W_Q"> and <img src="https://latex.codecogs.com/png.latex?W_K"> with random matrices (same shape), then rescale <img src="https://latex.codecogs.com/png.latex?B"> to match <img src="https://latex.codecogs.com/png.latex?%5Ctext%7Bstd%7D(B)"> before computing softmax-derived metrics. Specifically, we rescale <img src="https://latex.codecogs.com/png.latex?B_%7B%5Ctext%7Bbaseline%7D%7D%20%5Cleftarrow%0AB_%7B%5Ctext%7Bbaseline%7D%7D%20%5Ctimes%20(%5Csigma_%7B%5Ctext%7Breal%7D%7D%20/%20%5Csigma_%7B%5Ctext%7Bbaseline%7D%7D)">, where <img src="https://latex.codecogs.com/png.latex?%5Csigma%20=%20%5Ctext%7Bstd%7D(B)">.</p></li>
<li><p><strong>Permuted-K baseline</strong>: Shuffle key-feature identities by permuting rows of <img src="https://latex.codecogs.com/png.latex?K_f"> (equivalently, permuting columns of <img src="https://latex.codecogs.com/png.latex?B">), so diagonal mass becomes ‘chance self-match’.</p></li>
</ol>
<p>This lets me say things like:</p>
<p>“Selectivity is X times the random-weights baseline.”</p>
<p>“Diagonal mass is X times the permuted-K baseline.”</p>
<p>Why both matter:</p>
<ul>
<li><p>top-1 mass is permutation-invariant (it only cares that you pick something)</p></li>
<li><p>diagonal mass is identity-sensitive (it cares that you pick self)</p></li>
</ul>
</section>
<section id="weight-space-object-2-what-a-head-writes-ov-in-feature-space" class="level2">
<h2 class="anchored" data-anchor-id="weight-space-object-2-what-a-head-writes-ov-in-feature-space">Weight-Space Object #2: What a Head Writes (OV) in Feature Space</h2>
<p>Routing is only half the story. Even if a head can attend cleanly, it only matters if its output projection writes a direction that’s salient in the SAE feature basis into the residual stream.</p>
<p>For a query head, its write is determined by:</p>
<ul>
<li><p><img src="https://latex.codecogs.com/png.latex?W_V"> for the KV group (shared across 2 query heads in Gemma-2 GQA implementation)</p></li>
<li><p><img src="https://latex.codecogs.com/png.latex?W_O"> for the specific query head</p></li>
</ul>
<p>Gemma’s effective OV matrix is implemented as (row-vector convention; <img src="https://latex.codecogs.com/png.latex?W_O"> is <img src="https://latex.codecogs.com/png.latex?%5Bd_%5Ctext%7Bmodel%7D%20%5Ctimes%20d_%5Ctext%7Bhead%7D%5D">, <img src="https://latex.codecogs.com/png.latex?W_V"> is <img src="https://latex.codecogs.com/png.latex?%5Bd_%5Ctext%7Bhead%7D%20%5Ctimes%20d_%5Ctext%7Bmodel%7D%5D">):</p>
<p><img src="https://latex.codecogs.com/png.latex?W_%7BOV%7D%20=%20W_O%20%5Ccdot%20W_V"></p>
<p>For each SAE feature direction <img src="https://latex.codecogs.com/png.latex?d_j"> (row of <img src="https://latex.codecogs.com/png.latex?D">), define the write vector:</p>
<p><img src="https://latex.codecogs.com/png.latex?%5Ctext%7Bwrite%7D_j%20=%20d_j%20%5Ccdot%20W_V%5ET%20%5Ccdot%20W_O%5ET"></p>
<p>Project write vectors back onto decoder directions to get a “write-to-feature” matrix:</p>
<p><img src="https://latex.codecogs.com/png.latex?%5Ctext%7BW2F%7D%5Bj,k%5D%20=%20%5Ccos(%5Ctext%7Bwrite%7D_j,%5C,%20d_k)"></p>
<p>Note: W2F uses cosine similarity, so it measures directional alignment and discards magnitude; later I’ll also report write norms / activation-grounded deltas so ‘aligned-but-tiny’ writes don’t get over-interpreted.</p>
<p>This helps us define archetypes with concrete scoring.</p>
<p>With W2F as the cosine similarity matrix between each feature’s write vector and all decoder directions, we define:</p>
<ul>
<li><p><strong>COPY</strong> score = mean(diag(W2F)): how much feature i writes back to itself</p></li>
<li><p><strong>TRANSFORM</strong> score = max off-diagonal of W2F: strength of the strongest i <img src="https://latex.codecogs.com/png.latex?%5Cto"> j<img src="https://latex.codecogs.com/png.latex?%5Cneq">i mapping</p></li>
<li><p><strong>BROADCAST</strong> score = max column sum of W2F: how many features write to the same target</p></li>
<li><p><strong>SUPPRESS</strong> score = |min off-diagonal of W2F|: strength of the most negative write</p></li>
</ul>
<p>Copy dominance remains low on average (mean 0.186), but the more informative pattern is the upper tail: the strongest copy head is the early outlier L2H2 (0.897), while a secondary cluster of near-copy heads appears in L23–L25. The distribution by layer is shown in Figure&nbsp;7.</p>
<div id="fig-copy-dominance" class="quarto-float quarto-figure quarto-figure-center anchored">
<figure class="quarto-float quarto-float-fig figure">
<div aria-describedby="fig-copy-dominance-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
<img src="https://www.residual-thoughts.com/posts/weight-space-map-of-attention-heads/figs/fig05_copy_dominance.png" class="img-fluid figure-img">
</div>
<figcaption class="quarto-float-caption-bottom quarto-float-caption quarto-float-fig" id="fig-copy-dominance-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
Figure&nbsp;7: Copy dominance distribution per layer (violin) and median curve
</figcaption>
</figure>
</div>
<p>A head is classified by whichever score crosses its threshold first (COPY &gt; 0.3, TRANSFORM &gt; 0.5, BROADCAST &gt; 3x copy, SUPPRESS &gt; 0.5), defaulting to DIFFUSE (no score crosses any threshold). I assign a single label using a precedence rule (COPY <img src="https://latex.codecogs.com/png.latex?%5Cto"> TRANSFORM <img src="https://latex.codecogs.com/png.latex?%5Cto"> BROADCAST <img src="https://latex.codecogs.com/png.latex?%5Cto"> SUPPRESS <img src="https://latex.codecogs.com/png.latex?%5Cto"> DIFFUSE). Heads can score highly on multiple axes; the label is just meant to be a coarse summary. These thresholds are heuristic cutoffs chosen to separate tails in this run, not a claim that the taxonomy has canonical boundaries.</p>
<p>In this taxonomy, SUPPRESS means the head routes positively to some key feature j, and then the OV write is negatively aligned with a target feature k (it is not about negative B/avoidance). Also, please note that COPY can be negative if the head writes anti-aligned to the feature’s decoder direction.</p>
<p>A few examples:</p>
<div id="fig-ov-transform" class="quarto-float quarto-figure quarto-figure-center anchored">
<figure class="quarto-float quarto-float-fig figure">
<div aria-describedby="fig-ov-transform-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
<img src="https://www.residual-thoughts.com/posts/weight-space-map-of-attention-heads/figs/FIG2_transform_L3H2.png" class="img-fluid figure-img">
</div>
<figcaption class="quarto-float-caption-bottom quarto-float-caption quarto-float-fig" id="fig-ov-transform-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
Figure&nbsp;8: TRANSFORM exemplar: Layer 3, Head 2 (high transform_score among TRANSFORM heads)
</figcaption>
</figure>
</div>
<div id="fig-ov-broadcast" class="quarto-float quarto-figure quarto-figure-center anchored">
<figure class="quarto-float quarto-float-fig figure">
<div aria-describedby="fig-ov-broadcast-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
<img src="https://www.residual-thoughts.com/posts/weight-space-map-of-attention-heads/figs/FIG2_broadcast_L20H0.png" class="img-fluid figure-img">
</div>
<figcaption class="quarto-float-caption-bottom quarto-float-caption quarto-float-fig" id="fig-ov-broadcast-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
Figure&nbsp;9: BROADCAST exemplar: Layer 20, Head 0 (highest broadcast_norm among BROADCAST heads)
</figcaption>
</figure>
</div>
<div id="fig-ov-copy" class="quarto-float quarto-figure quarto-figure-center anchored">
<figure class="quarto-float quarto-float-fig figure">
<div aria-describedby="fig-ov-copy-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
<img src="https://www.residual-thoughts.com/posts/weight-space-map-of-attention-heads/figs/FIG2_copy_L2H2.png" class="img-fluid figure-img">
</div>
<figcaption class="quarto-float-caption-bottom quarto-float-caption quarto-float-fig" id="fig-ov-copy-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
Figure&nbsp;10: COPY exemplar: Layer 2, Head 2
</figcaption>
</figure>
</div>
<div class="callout callout-style-default callout-important callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
<span class="screen-reader-only">Important</span>Update (March 2026): OV Sign-Flip Correction
</div>
</div>
<div class="callout-body-container callout-body">
<p>OV validation shows a different failure mode from QK routing: late layers develop negative outliers, but not a layer-wide median inversion. Most late-layer medians stay positive, yet a subset of heads flips sign, peaking at 25% negative heads in L21; the strongest outlier is L20H7 at cosine = -0.64.</p>
</div>
</div>
<div id="fig-ov-signflip" class="quarto-float quarto-figure quarto-figure-center anchored">
<figure class="quarto-float quarto-float-fig figure">
<div aria-describedby="fig-ov-signflip-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
<img src="https://www.residual-thoughts.com/posts/weight-space-map-of-attention-heads/figs/fig10_ov_signflip.png" class="img-fluid figure-img">
</div>
<figcaption class="quarto-float-caption-bottom quarto-float-caption quarto-float-fig" id="fig-ov-signflip-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
Figure&nbsp;11: OV validation: late negative outliers without a layer-wide median flip
</figcaption>
</figure>
</div>
</section>
<section id="weight-space-object-3-composing-feature-programs-i-to-j-to-k" class="level2">
<h2 class="anchored" data-anchor-id="weight-space-object-3-composing-feature-programs-i-to-j-to-k">Weight-Space Object #3: Composing “Feature Programs” (i <img src="https://latex.codecogs.com/png.latex?%5Cto"> j <img src="https://latex.codecogs.com/png.latex?%5Cto"> k)</h2>
<p>Once we have routing (<img src="https://latex.codecogs.com/png.latex?B">) and writing (W2F), we can compose them into simple “programs”:</p>
<p>query feature i <img src="https://latex.codecogs.com/png.latex?%5Cto"> attend key feature j <img src="https://latex.codecogs.com/png.latex?%5Cto"> write feature k</p>
<p>Programs are extracted from the top tail of routing scores and write scores (quantile thresholds), so the taxonomy describes high-confidence motifs rather than all mass. We also track when a self-write is a fallback rather than a measured OV target.</p>
<p>Each triplet is scored by combining route strength and write strength. Then we can classify it into a small motif family:</p>
<div id="tbl-programs" class="quarto-float quarto-figure quarto-figure-center anchored">
<figure class="quarto-float quarto-float-tbl figure">
<figcaption class="quarto-float-caption-top quarto-float-caption quarto-float-tbl" id="tbl-programs-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
Table&nbsp;1: Program types and their flow patterns
</figcaption>
<div aria-describedby="tbl-programs-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
<table class="caption-top table">
<thead>
<tr class="header">
<th>Type</th>
<th>Flow</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td>REINFORCE</td>
<td>i <img src="https://latex.codecogs.com/png.latex?%5Cto"> i <img src="https://latex.codecogs.com/png.latex?%5Cto"> i</td>
</tr>
<tr class="even">
<td>SHIFT</td>
<td>i <img src="https://latex.codecogs.com/png.latex?%5Cto"> i <img src="https://latex.codecogs.com/png.latex?%5Cto"> k (k <img src="https://latex.codecogs.com/png.latex?%5Cneq"> i)</td>
</tr>
<tr class="odd">
<td>CROSS_COPY</td>
<td>i <img src="https://latex.codecogs.com/png.latex?%5Cto"> j <img src="https://latex.codecogs.com/png.latex?%5Cto"> j (i <img src="https://latex.codecogs.com/png.latex?%5Cneq"> j)</td>
</tr>
<tr class="even">
<td>RELAY</td>
<td>i <img src="https://latex.codecogs.com/png.latex?%5Cto"> j <img src="https://latex.codecogs.com/png.latex?%5Cto"> i (i <img src="https://latex.codecogs.com/png.latex?%5Cneq"> j)</td>
</tr>
<tr class="odd">
<td>TRANSFORM</td>
<td>i <img src="https://latex.codecogs.com/png.latex?%5Cto"> j <img src="https://latex.codecogs.com/png.latex?%5Cto"> k (all different)</td>
</tr>
<tr class="even">
<td>SUPPRESS</td>
<td>i <img src="https://latex.codecogs.com/png.latex?%5Cto"> j <img src="https://latex.codecogs.com/png.latex?%5Cto"> -k</td>
</tr>
</tbody>
</table>
</div>
</figure>
</div>
<p>This is the point where the analysis becomes human-legible: you can say “this head’s weight structure is consistent with CROSS_COPY” or “this layer is SUPPRESS-heavy in the weight-space taxonomy,” without running a prompt.</p>
<p>In my run: routing tail <img src="https://latex.codecogs.com/png.latex?%5Capprox"> top 10%; write tail <img src="https://latex.codecogs.com/png.latex?%5Capprox"> top 20%.</p>
<div class="callout callout-style-default callout-warning callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
<span class="screen-reader-only">Warning</span>Methodological Note: Fallback Writes
</div>
</div>
<div class="callout-body-container callout-body">
<p>When a routing pair (i<img src="https://latex.codecogs.com/png.latex?%5Cto">j) passes the route threshold but has no explicit write evidence, the system can inject a synthetic self-write using the head’s copy_score as a fallback. This means some REINFORCE programs may be artifacts of missing write evidence rather than genuine self-reinforcing circuits. The explicit-only program histogram (without fallbacks) is more conservative and should be preferred for circuit-level claims.</p>
</div>
</div>
</section>
<section id="position-robustness-rope-stability" class="level2">
<h2 class="anchored" data-anchor-id="position-robustness-rope-stability">Position Robustness: RoPE Stability</h2>
<p>Gemma-2 uses RoPE, which rotates Q/K vectors as a function of relative position. In weight space you can simulate this by rotating keys (or queries) and recomputing affinity matrices.</p>
<p>Let <img src="https://latex.codecogs.com/png.latex?B_0"> be affinity at relative offset <img src="https://latex.codecogs.com/png.latex?%5CDelta=0">, and <img src="https://latex.codecogs.com/png.latex?B_%5CDelta"> at offset <img src="https://latex.codecogs.com/png.latex?%5CDelta">. Define a stability score (e.g., cosine similarity of flattened matrices):</p>
<p><img src="https://latex.codecogs.com/png.latex?%5Ctext%7Bstability%7D(%5CDelta)%20=%20%5Ccos(%5Ctext%7Bvec%7D(B_0),%5C,%20%5Ctext%7Bvec%7D(B_%5CDelta))"></p>
<p>Then compress the curve across <img src="https://latex.codecogs.com/png.latex?%5CDelta"> into a single number (AUC-style over log-distance). I call this RoPE stability: a summary of how similar the affinity matrix <img src="https://latex.codecogs.com/png.latex?B"> stays as we apply relative-position rotations. Also, we apply the tanh softcap before comparing matrices.</p>
<ul>
<li><p>When this metric is <strong>High</strong> <img src="https://latex.codecogs.com/png.latex?%5Cto"> routing seems to be mostly content-controlled and stays similar across positions. It could also mean that this head doesn’t use positional information much because it’s doing something else entirely.</p></li>
<li><p>And when it is <strong>Low</strong> <img src="https://latex.codecogs.com/png.latex?%5Cto"> routing depends strongly on relative position (more “twitchy”).</p></li>
</ul>
<p>This is not “style steerability,” but it is a useful proxy for “position-invariant content routing.”</p>
<p>One caveat: cosine similarity can look high if <img src="https://latex.codecogs.com/png.latex?B"> is very low-magnitude / diffuse; I interpret stability alongside routing strength/selectivity.</p>
<div id="fig-rope-curves" class="quarto-float quarto-figure quarto-figure-center anchored">
<figure class="quarto-float quarto-float-fig figure">
<div aria-describedby="fig-rope-curves-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
<img src="https://www.residual-thoughts.com/posts/weight-space-map-of-attention-heads/figs/fig04_rope_stability_curves.png" class="img-fluid figure-img">
</div>
<figcaption class="quarto-float-caption-bottom quarto-float-caption quarto-float-fig" id="fig-rope-curves-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
Figure&nbsp;12: High-stability heads such as L24H5 and L17H4 stay much closer to <img src="https://latex.codecogs.com/png.latex?B_0"> across distance than low-stability heads like L25H1 and L5H0
</figcaption>
</figure>
</div>
<div id="fig-rope-heatmap" class="quarto-float quarto-figure quarto-figure-center anchored">
<figure class="quarto-float quarto-float-fig figure">
<div aria-describedby="fig-rope-heatmap-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
<img src="https://www.residual-thoughts.com/posts/weight-space-map-of-attention-heads/figs/fig04b_controllability_heatmap.png" class="img-fluid figure-img">
</div>
<figcaption class="quarto-float-caption-bottom quarto-float-caption quarto-float-fig" id="fig-rope-heatmap-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
Figure&nbsp;13: Per-head RoPE stability (AUC) heatmap across all layers and heads. Blue bands mark sliding-window layers.
</figcaption>
</figure>
</div>
</section>
<section id="results-what-changes-with-depth" class="level2">
<h2 class="anchored" data-anchor-id="results-what-changes-with-depth">Results: What Changes with Depth?</h2>
<p>Once we compute these objects across layers and heads, we can summarize how the model’s attention machinery evolves with depth.</p>
<section id="local-sliding-window-vs-global-layers" class="level3">
<h3 class="anchored" data-anchor-id="local-sliding-window-vs-global-layers">1. Local (Sliding-Window) vs Global Layers</h3>
<p>Gemma-2 alternates local and global attention by layer, giving a built-in contrast:</p>
<p>Sliding-window layers can’t directly attend to tokens outside the window, but they can still transform a residual stream that already contains long-range information injected by earlier global layers.</p>
<p>Global layers are the only layers that can implement direct long-range attention pointers.</p>
<div id="fig-sel-by-type" class="quarto-float quarto-figure quarto-figure-center anchored">
<figure class="quarto-float quarto-float-fig figure">
<div aria-describedby="fig-sel-by-type-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
<img src="https://www.residual-thoughts.com/posts/weight-space-map-of-attention-heads/figs/fig07_selectivity_by_type.png" class="img-fluid figure-img">
</div>
<figcaption class="quarto-float-caption-bottom quarto-float-caption quarto-float-fig" id="fig-sel-by-type-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
Figure&nbsp;14: Routing selectivity: sliding-window vs global layers
</figcaption>
</figure>
</div>
<div id="fig-diagm-by-type" class="quarto-float quarto-figure quarto-figure-center anchored">
<figure class="quarto-float quarto-float-fig figure">
<div aria-describedby="fig-diagm-by-type-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
<img src="https://www.residual-thoughts.com/posts/weight-space-map-of-attention-heads/figs/fig08_diagm_by_type.png" class="img-fluid figure-img">
</div>
<figcaption class="quarto-float-caption-bottom quarto-float-caption quarto-float-fig" id="fig-diagm-by-type-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
Figure&nbsp;15: Identity sensitivity: sliding-window vs global layers
</figcaption>
</figure>
</div>
</section>
<section id="selectivity-and-identity-sensitivity-are-not-monotone" class="level3">
<h3 class="anchored" data-anchor-id="selectivity-and-identity-sensitivity-are-not-monotone">2. Selectivity and Identity-Sensitivity Are Not Monotone</h3>
<p>Two different things can happen as depth grows:</p>
<ol type="1">
<li><p>Peakiness (top-1 softmax mass) can increase because the model learns sharper “addressing”</p></li>
<li><p>Identity sensitivity (diagonal softmax mass) can increase if heads learn an i<img src="https://latex.codecogs.com/png.latex?%5Cto">i “feature self-match” pattern</p></li>
</ol>
<p>In my current run, there’s a very strong spike in these metrics around a mid layer (one layer stands out dramatically on both selectivity and diagonal mass). That suggests a “band” of unusually crisp routing behavior rather than a smooth trend.</p>
<div id="fig-sel-diagm-depth" class="quarto-float quarto-figure quarto-figure-center anchored">
<figure class="quarto-float quarto-float-fig figure">
<div aria-describedby="fig-sel-diagm-depth-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
<img src="https://www.residual-thoughts.com/posts/weight-space-map-of-attention-heads/figs/fig09_sel_diagm_vs_layer.png" class="img-fluid figure-img">
</div>
<figcaption class="quarto-float-caption-bottom quarto-float-caption quarto-float-fig" id="fig-sel-diagm-depth-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
Figure&nbsp;16: Sel<img src="https://latex.codecogs.com/png.latex?%5Ctimes">U and DiagM<img src="https://latex.codecogs.com/png.latex?%5Ctimes">U vs layer. Blue = sliding-window, red = global.
</figcaption>
</figure>
</div>
<div class="callout callout-style-default callout-note callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
<span class="screen-reader-only">Note</span>What Surprised Me
</div>
</div>
<div class="callout-body-container callout-body">
<p>The Layer 6 spike was the single most dramatic finding – Sel<img src="https://latex.codecogs.com/png.latex?%5Ctimes">U of 105 means the routing structure is two orders of magnitude above uniform baseline. I expected a smooth gradient, not a spike. This showed up consistently across different feature subsets and seeds. Layer 6 is a sliding-window layer (even index) in the range where induction-like heads are typically found, and four of its heads (L6H0, L6H2, L6H3, L6H4) are among the top-10 validation-confirmed heads. The spike-rather-than-gradient pattern suggests a sharp regime change in routing behavior at this depth.</p>
</div>
</div>
</section>
<section id="rope-stability-tends-to-rise-with-depth-with-bands" class="level3">
<h3 class="anchored" data-anchor-id="rope-stability-tends-to-rise-with-depth-with-bands">3. RoPE Stability Tends to Rise with Depth (with Bands)</h3>
<p>RoPE stability tends to increase with depth, but it’s not smooth. There are high-stability bands (mid layers and a late cluster near the top of the network).</p>
<div id="fig-rope-depth" class="quarto-float quarto-figure quarto-figure-center anchored">
<figure class="quarto-float quarto-float-fig figure">
<div aria-describedby="fig-rope-depth-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
<img src="https://www.residual-thoughts.com/posts/weight-space-map-of-attention-heads/figs/fig11_rope_vs_layer.png" class="img-fluid figure-img">
</div>
<figcaption class="quarto-float-caption-bottom quarto-float-caption quarto-float-fig" id="fig-rope-depth-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
Figure&nbsp;17: Mean RoPE stability (AUC) per layer with $$1 std across heads
</figcaption>
</figure>
</div>
</section>
<section id="redundancy-vs-uniqueness-where-structure-concentrates" class="level3">
<h3 class="anchored" data-anchor-id="redundancy-vs-uniqueness-where-structure-concentrates">4. Redundancy vs Uniqueness (Where Structure Concentrates)</h3>
<p>I track how redundant heads are within a layer (do they “do the same thing” in program space?). Redundancy = Jaccard similarity of top-20 routing pairs between heads – that is, <img src="https://latex.codecogs.com/png.latex?%7CA%20%5Ccap%20B%7C%20/%20%7CA%20%5Ccup%20B%7C"> where <img src="https://latex.codecogs.com/png.latex?A"> and <img src="https://latex.codecogs.com/png.latex?B"> are each head’s set of top-20 (query <img src="https://latex.codecogs.com/png.latex?%5Cto"> key) feature pairs. Two heads are considered redundant if they route the same feature pairs highly:</p>
<p>Low redundancy: a few specialized heads carry most structure – could be potential larger single-head “levers”</p>
<p>High redundancy: many heads share similar top programs – could mean that the layer is robust to removing any one head</p>
<p>To avoid noise, I primarily interpret redundancy within the subset of heads that show structured routing (e.g., above a small selectivity or correlation threshold).</p>
<div id="fig-redundancy" class="quarto-float quarto-figure quarto-figure-center anchored">
<figure class="quarto-float quarto-float-fig figure">
<div aria-describedby="fig-redundancy-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
<img src="https://www.residual-thoughts.com/posts/weight-space-map-of-attention-heads/figs/fig13_redundancy_vs_layer.png" class="img-fluid figure-img">
</div>
<figcaption class="quarto-float-caption-bottom quarto-float-caption quarto-float-fig" id="fig-redundancy-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
Figure&nbsp;18: Within-layer head redundancy (mean Jaccard of top-20 routing pairs) by layer
</figcaption>
</figure>
</div>
</section>
<section id="working-interpretation-three-depth-regimes" class="level3">
<h3 class="anchored" data-anchor-id="working-interpretation-three-depth-regimes">Working Interpretation: Three Depth Regimes</h3>
<p>Taken together, the plots suggest a cleaner story than any single metric does on its own:</p>
<ol type="1">
<li><p>Early layers mostly look like <em>feature transport and filtering</em>. Routing is validated, selectivity is modest, and OV behavior is often suppressive or transform-light.</p></li>
<li><p>Mid layers, especially the Layer 6 band, look like a <em>routing bottleneck</em>. This is where the map is most predictive, selectivity spikes, identity-sensitive routing appears crisply, and several of the strongest validation-confirmed heads cluster.</p></li>
<li><p>Late layers still show strong structure in the map, but the QK side becomes anti-predictive at runtime. The natural reading is not “late layers are uninterpretable”; it is “late layers are where this fixed linear feature basis stops being a reliable behavioral proxy.”</p></li>
</ol>
<p>I think this is the most important qualitative insight in the post. The headline is not just that certain layers spike, but that the <em>kind</em> of structure the map is surfacing changes with depth.</p>
<p>The program distribution by layer (Figure&nbsp;19) makes this concrete: early layers carry substantial SUPPRESS (peaking in L2/L4–L6) alongside CROSS_COPY, mid layers mix CROSS_COPY / SUPPRESS / REINFORCE, and late layers tilt toward CROSS_COPY. The dominant program motif changes with depth even when the global histogram looks stable.</p>
<div id="fig-program-dist" class="quarto-float quarto-figure quarto-figure-center anchored">
<figure class="quarto-float quarto-float-fig figure">
<div aria-describedby="fig-program-dist-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
<img src="https://www.residual-thoughts.com/posts/weight-space-map-of-attention-heads/figs/fig06_program_distribution.png" class="img-fluid figure-img">
</div>
<figcaption class="quarto-float-caption-bottom quarto-float-caption quarto-float-fig" id="fig-program-dist-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
Figure&nbsp;19: Weight-space program distribution by layer
</figcaption>
</figure>
</div>
</section>
</section>
<section id="interpretation" class="level2">
<h2 class="anchored" data-anchor-id="interpretation">Interpretation</h2>
<p>Weight-space analysis is not telling you “what the model will output.” It’s telling you what kinds of operations are available in the weights, and where in depth those operations concentrate.</p>
<p>Safest takeaways from this weight-space + SAE-basis analysis:</p>
<ul>
<li><p>Local vs global layers are qualitatively different substrates. If a behavior requires long-range access, only global layers can implement it directly.</p></li>
<li><p>There are depth bands with unusually crisp routing (spikes in selectivity and sometimes identity-sensitive diagonal mass).</p></li>
<li><p>Redundancy varies a lot. Some layers look like “few special heads,” others spread structure across many heads.</p></li>
<li><p><strong>Intervention heuristic</strong>: Combining selectivity (0.3 weight), RoPE stability (0.4), head diversity (0.2), and max-gap (0.1) into a composite score, Layer 6 emerges as the strongest routing intervention candidate (score 0.51), followed by Layers 22–23. I now view this as a prioritization heuristic rather than a steering claim: the Layer 23 ablation result shows the map can identify leverage without predicting direction of effect.</p></li>
</ul>
</section>
<section id="behavioral-follow-up-predictions-and-early-results" class="level2">
<h2 class="anchored" data-anchor-id="behavioral-follow-up-predictions-and-early-results">Behavioral Follow-Up: Predictions and Early Results</h2>
<section id="pre-registered-expectations" class="level3">
<h3 class="anchored" data-anchor-id="pre-registered-expectations">Pre-Registered Expectations</h3>
<p>In Post 2, I will test behavior, but driven by pre-registered weight-space predictions.</p>
<p>One candidate hypothesis: heads with high selectivity and strong directional write structure (high COPY/TRANSFORM/etc scores) but low RoPE stability could contribute disproportionately to long-distance errors on retrieval-style tasks; ablating their OV contribution may measurably change accuracy (possibly improve or worsen), especially at large distances.</p>
</section>
<section id="what-already-came-back" class="level3">
<h3 class="anchored" data-anchor-id="what-already-came-back">What Already Came Back</h3>
<div class="callout callout-style-default callout-important callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
<span class="screen-reader-only">Important</span>Update (Jan 2026): Causal Ablation Results
</div>
</div>
<div class="callout-body-container callout-body">
<p>I ran causal ablation experiments on Layer 23 – which the weight-space map flagged as a high-selectivity, high-RoPE-stability late layer – ablating individual heads and multi-head combinations on retrieval-style prompts with symmetry controls (swapped A<img src="https://latex.codecogs.com/png.latex?%5Cleftrightarrow">B) and logits-only evaluation (no generation artifacts).</p>
<p><strong>The surprising result</strong>: ablating 6 of 8 Layer 23 heads (H0, H1, H2, H3, H6, H7) <em>improved</em> retrieval accuracy from 58% to 66.7% (+8.7%) and widened the logprob margin from +0.34 to +0.59. H4 and H5 were excluded because their individual ablation reduced accuracy. The weight-space map correctly identified L23 as a high-leverage layer, but the direction of effect was opposite to the naive prediction – these heads appear to <em>interfere with</em> correct retrieval rather than facilitate it. Whether this reflects genuine suppressive behavior or a limitation in the task design is an open question for Post 2.</p>
<p>I also ran a full OV writing validation across all 200 heads confirming that predicted feature writes match actual head output with mean cosine similarity of +0.50 in early/mid layers, but develop negative outliers in late layers – not a layer-wide median inversion. Most late-layer medians stay positive, yet a subset of heads flips sign, peaking at 25% negative heads in L21; the strongest outlier is L20H7 at cosine = -0.64 (see Figure&nbsp;11). This finding persists with the full 16K feature set. Post 2 is in preparation.</p>
</div>
</div>
<p>Implementation detail note: model constants like attention scaling, RoPE placement, and alternating sliding-window convention are codified in the project config.</p>
<p>This work is inspired by Anthropic’s <a href="https://transformer-circuits.pub/">Transformer Circuits</a> series, especially <a href="https://transformer-circuits.pub/2021/framework/index.html">A Mathematical Framework for Transformer Circuits</a>.</p>
<p>Code for the analysis pipeline is available in the <a href="https://github.com/shubhamx64/residual-thoughts-blog/tree/main/code/weight-space-map">code/weight-space-map</a> directory of this site’s repository (experimental research code snapshot).</p>
<p>Feedback, comments, and discussion are welcome.</p>
</section>
</section>
<section id="limitations" class="level2">
<h2 class="anchored" data-anchor-id="limitations">Limitations</h2>
<p><strong>Linearity assumption.</strong> We fold RMSNorm <img src="https://latex.codecogs.com/png.latex?%5Cgamma"> into weights but ignore the token-dependent <img src="https://latex.codecogs.com/png.latex?1/%5C%7Cx%5C%7C_%7B%5Ctext%7BRMS%7D%7D"> factor, making the analysis linear-in-direction rather than exact-in-activation. This approximation is tighter when residual stream norms are stable (early/mid layers) and degrades where norm variance is high (late layers). This is likely one contributor to the late-layer anti-prediction effect.</p>
<p><strong>SAE basis is not ground truth.</strong> SAE decoder directions are non-orthogonal and learned via reconstruction loss – they may not capture the model’s “true” features. Feature splitting (one concept spread across multiple SAE features) and superposition (multiple concepts encoded in one direction) can distort the affinity matrix <img src="https://latex.codecogs.com/png.latex?B">. We do not apply Gram-matrix correction for superposition in the main analysis, though the infrastructure exists.</p>
<p><strong>Late-layer anti-prediction.</strong> After ~Layer 17, the weight-space <img src="https://latex.codecogs.com/png.latex?B"> matrix predicts the opposite of observed attention (mean Spearman <img src="https://latex.codecogs.com/png.latex?%5Crho"> = -0.14). This is not just noise – it’s systematic. All late-layer metrics (selectivity, program distribution, write archetypes) should be interpreted as “what the weights make available” rather than “what happens at runtime.” The sign flip likely reflects LayerNorm interactions, residual stream composition, or accumulated superposition that the linear analysis cannot capture.</p>
<p><strong>Feature-space selectivity <img src="https://latex.codecogs.com/png.latex?%5Cneq"> token-space selectivity.</strong> A head can have diffuse routing over SAE features while being sharp over token positions, or vice versa. Our selectivity metrics measure peakiness in feature space – this is a different (though correlated) quantity from attention sharpness over positions.</p>
<p><strong>No direct causality.</strong> Weight-space analysis maps available operations, not actual behavior. A head scored as high-COPY might never copy in practice because it never receives the right input distribution. Causal validation (ablation, activation patching) is required to make functional claims – which is why Post 2 is in preparation.</p>
<p><strong>Single-seed sensitivity.</strong> As noted in the Feature Subset Note above, the main weight-space metrics use a 4,096-feature random subset. Absolute values (e.g., exact Sel<img src="https://latex.codecogs.com/png.latex?%5Ctimes">U numbers) shift across seeds. Qualitative patterns (which layers spike, program distribution shape) are stable in spot checks but have not been formally assessed for robustness.</p>
</section>
<section id="appendix-metric-glossary" class="level2">
<h2 class="anchored" data-anchor-id="appendix-metric-glossary">Appendix: Metric Glossary</h2>
<ul>
<li><p><strong>top1_mass</strong> = mean over query features of max softmax probability across keys</p></li>
<li><p><strong>Sel<img src="https://latex.codecogs.com/png.latex?%5Ctimes">U</strong> = top1_mass <img src="https://latex.codecogs.com/png.latex?%5Cdiv"> (1/n)</p></li>
<li><p><strong>diagonal_softmax_mass</strong> = mean softmax mass on the diagonal entry (identity-sensitive)</p></li>
<li><p><strong>DiagM<img src="https://latex.codecogs.com/png.latex?%5Ctimes">U</strong> = diagonal_softmax_mass <img src="https://latex.codecogs.com/png.latex?%5Cdiv"> (1/n)</p></li>
<li><p><strong>MaxGap</strong> = average(top1_logit - top2_logit) per row</p></li>
<li><p><strong>RoPE</strong> (stability) = AUC summary of how stable <img src="https://latex.codecogs.com/png.latex?B"> remains under RoPE-relative rotations (called <code>semantic_controllability</code> in the code)</p></li>
<li><p><strong>Copy</strong> = mean diagonal of W2F (self-write strength)</p></li>
<li><p><strong>Trans</strong> (transform) = max off-diagonal of W2F (strongest i <img src="https://latex.codecogs.com/png.latex?%5Cto"> j<img src="https://latex.codecogs.com/png.latex?%5Cneq">i mapping)</p></li>
<li><p><strong>COPY / TRANSFORM / BROADCAST / SUPPRESS / DIFFUSE</strong> = write archetypes derived from W2F. DIFFUSE = no score crosses any threshold</p></li>
<li><p><strong>Feature programs</strong> = composed triplets i <img src="https://latex.codecogs.com/png.latex?%5Cto"> j <img src="https://latex.codecogs.com/png.latex?%5Cto"> k with a small taxonomy</p></li>
</ul>


</section>

 ]]></description>
  <category>mechanistic-interpretability</category>
  <category>gemma-2</category>
  <category>sae</category>
  <category>attention-heads</category>
  <guid>https://www.residual-thoughts.com/posts/weight-space-map-of-attention-heads/</guid>
  <pubDate>Sun, 22 Mar 2026 00:00:00 GMT</pubDate>
  <media:content url="https://www.residual-thoughts.com/posts/weight-space-map-of-attention-heads/figs/hero.png" medium="image" type="image/png" height="177" width="144"/>
</item>
</channel>
</rss>
