alphaFeedback

WorksComputer Science

How Small Can We Go? Calibrating Activation-Cache Compression for SAE Training

AI contributions
Not recorded for this paper
View details

Abstract

To train a sparse autoencoder (SAE), you need a massive cache of model activations. We look into the effects that compression has on an SAE in two ways: we keep the original fp32-trained SAE and pass the compressed cache into the same SAE to see how many feature activations stay the same (the frozen-SAE test), and we retrain an SAE on the compressed cache and see how many of the original features still exist (the retraining test). We found that in the retraining test, there is no clear scale for retained features. On a GPT-2 cache, rounding everything to fp16 keeps 60.6% of features, but changing one value in a million by the smallest step fp32 allows (a change eight orders of magnitude smaller) keeps only 70.5%. Meanwhile, changing the random seed keeps 23.0%. For the frozen-SAE test, the frozen SAE's correlation with the original increases in order of how precise the codecs are, but plain L2L_2 distance gives nearly the same ordering. We also see the same frozen reading correspond to different percentages retained at different sites. This leaves the question of the right measurement. We build one by calibrating: retraining on identical bytes sets the ceiling, and a reseed sets the floor. We then measure the effect of each compressed codec against its own site's floor. With TopK SAEs on GPT-2 small and Pythia-1.4B at research-scale budgets, naïve per-token int4 loses more features than a reseed everywhere we tested, as do PCA and random projection on GPT-2. Standardizing each channel before quantizing brings int4 back above the floor in terms of retained features, at the same raw file size. At our smallest budget, a bf16 forward pass, which some pipelines use to save memory, loses slightly more features than rotated 8-bit storage. Calibrating a site costs four training runs and two evaluation passes.

Introduction

One way of understanding the internals of LLMs is to train SAEs (sparse autoencoders) on model internal activations. However, these activations take up a lot of space to store. A token of text is two bytes, but its activation vector can be several kilobytes [1]. This means that training a single SAE on Gemma 2 9B can take over 1 GiB/s of disk read to keep the accelerators fed [2]. So, ideally we would be able to cache the activations and compress them. SAELens, a widely used library, stores these activations in fp32 by default [3], but we do not know what the effect of compression is on the features of the SAE. The closest prior work quantizes model weights rather than the cache [4]; separately, Paulo & Belrose established the seed instability we build on [5] (Appendix § 18). An SAE learns a dictionary of sparse features, directions in activation space that fire on some tokens; two SAEs agree on a feature when the other SAE has a feature whose activations over the same tokens correlate with it at r>0.9r > 0.9.

The retraining test turns out to be very sensitive. We took a 20M-token cache and changed one number in it by the smallest amount possible in fp32 (1 ulp, a unit in the last place). When we retrained, 99.98% of the features survived. In contrast, when we changed a millionth of all the numbers by that same smallest step, only 70.5% of the features remained the same (Figure 1). Yet when we rounded every number in the cache to fp16, a change 10810^{8} times larger, we retained 60.6% of the features. So the count responds to almost any change in the bytes and, up to fp16’s distortion, only weakly to its size (§ 3.1). It cannot be read as a level, only against controls built the same way.

Bar chart showing features surviving a retrain for GPT-2 layer 6, seed 0

Figure 1. GPT-2 layer 6, seed 0: one ulp on a millionth of values already costs three quarters of fp16’s loss, so the count needs a floor. Bars: alive features a retrained SAE recovers at best match r>0.9r > 0.9 (grey: control; blue: tiny perturbations; orange: real codecs); dashed line: seed floor.

A measurement can be precise and still answer the wrong question [12, 13]; the frozen-SAE test is one, and the retraining test needs calibration first. Our two contributions:

  1. The ranking of codecs is reliable; the exact percentages are not (§ 3, § 4). The ordering holds across sites, thresholds and budgets (up to 2 pp ties, seed noise; TopK SAEs only), but the percentage moves with every setting we varied, so no single number transfers.

  1. A same-process control lets us compare compression against other reasonable noise, and that is enough (§ 2, § 4). Naïve int4 alters the features more than a seed change in every whole-SAE comparison we ran, and per-channel standardization brings it back above reseeding at the same raw size. A bf16 forward pass loses 1.8 to 3.0 pp more agreement than rotated int8 storage at our 1×1\times budget (three seeds, same direction, two of three outside the 2 pp tie band).

Calibrating the retraining test

The retraining test needs calibrating before it can be used (§ 3.2 turns to the frozen-SAE test), because SAE training is unstable across seeds [5, 6]: a naïve fp32-vs-codec comparison could mix the codec’s effect with seed noise. So we calibrate against the seed change by using it as a unit of measurement, building the bounds directly: retraining on identical bytes gives the upper bound, and retraining with a different seed gives the lower one.

Glossary of terms used in the paper. A channel is one of the dd coordinates of the activation vector. Next we have the codecs, our compressed storage formats. rot-intNN standardizes each channel (by subtracting the mean and dividing by std), applies a fixed rotation and quantizes to NN bits. std-intNN skips the rotation. intNN is naïve per-token quantization (meaning one absmax scale per token). intNN-oMM keeps MM outlier channels in fp16. A codec’s ratio is the percentage of retained features divided by its site’s floor from reseeding. A codec that is above this reseeding floor is considered viable. If two single-seed runs come within 2 percentage points (pp) we consider them tied as seed noise alone shifts every run’s number by up to 1.1 pp (Table 1). Numbers in the text are seed 0, except for the reseeding floors which are three-pair means and the denominator of each ratio. Tables in the appendix add three-seed means written ±\pm half the min–max range. Our 1×1\times budget is 33M token-visits (tokens seen in training, counting repeats; 24.6M on Pythia).

codecB/toksurvived %FVUmean rrstable %Hung. cosfrozen %
fp16153659.3 ± 1.10.13550.86695.70.817100.0
bf16 storage153655.60.13550.85393.30.799100.0
rot-int877252.7 ± 1.10.13560.84594.20.786100.0
int877246.60.13560.82191.50.751100.0
int8-o878050.80.13550.83793.70.773100.0
rot-int658045.9 ± 0.70.13550.82392.10.752100.0
int658038.00.13590.79089.80.69699.9
int6-o859044.2 ± 0.40.13570.81391.60.740100.0
rot-int438837.7 ± 0.30.13590.78988.80.697100.0
int4-o1641237.60.13610.78991.20.69299.9
int438816.0 ± 0.30.19680.66458.90.46136.7
pca2565127.80.36400.57134.80.35137.2
rp2565122.80.86260.48516.30.2654.6
seed floor—23.0 ± 0.10.13550.721(def.)0.577—

Table 1. Full GPT-2 codec panel. ± rows are three-seed means (± half the range), bare cells seed 0, so fp16 reads 59.3 ± 1.1 here and 60.6 (seed 0) in the text. survived: best match above r=0.9r = 0.9 against fp32 at the same seed; FVU: fraction of variance unexplained; mean rr: the retrained SAE’s best-match correlation, averaged over alive features (not the frozen reading); stable: survival among the 774 features reproduced in both other fp32 seeds; Hung. cos: mean matched encoder and decoder cosine over all latents, dead ones included; frozen: the thresholded reading through the frozen fp32 SAE. FVU is flat across every viable codec (0.1355 to 0.1361) while seed-0 agreement falls from 60.6 to 37.6%. The floor row’s FVU is the fp32 reference’s own, and its Hungarian cosine averages the two seed-0 pairs.

Design. We use one fp32 master cache per site from a single forward pass on a model. Every codec store we make is a transcode of this cache, shard for shard. The loader then shuffles based on just the seed, so two SAEs trained with the same seed consume identical tokens in the same order and differ only in bytes. We trained each SAE with Adam at learning rate 3×10−43 \times10^{-4} for 8,000 steps at batch 4,096 (6,000 on Pythia). A site is one model, layer and read-off point; agreement between two SAEs is the fraction of the reference SAE’s alive features whose best match in the other reaches Pearson r>0.9r > 0.9 on held-out fp32 tokens; we train TopK SAEs (JumpReLU results are in Table 5).

codecsurvived %mean rr
fp1626.40.716
rot-int825.30.708
rot-int426.10.708
int413.70.623
seed floor23.7–24.20.699

Table 5. JumpReLU panel (GPT-2, calibrated λ=8.68\lambda= 8.68). The viable codecs land within a few points of the floor (fp16 26.4, rot-int4 26.1, rot-int8 25.3 against 23.7 to 24.2), too close together for a graded verdict. mean rr: the retrained best-match correlation averaged over alive features. The fp32 and viable-codec runs end with about a third of latents dead (the naïve int4 run about a quarter), and the naïve int4 run converged to L0=38.2L_0 = 38.2 against fp32’s 18.6, so its row is shown but excluded from any verdict as confounded.

Ceiling. Retraining with the exact same seed and bytes reproduces the run exactly: r=1.0000r = 1.0000, a 100% agreement on Apple M5 (MPS) and on an A100 (Appendix § 11). Any same-seed difference therefore comes from the codec alone.

Floor. To find the lower bound, how much seed noise alone costs, we retrained an SAE with a different seed. On GPT-2 small, we only retain 23.0 ± 0.1% of features over three seed pairs at layer 6 (20M OpenWebText tokens; TopK SAEs, where the k=32k=32 largest of dsae=12288d_{\mathrm{sae}}=12288 latents fire per token). On Pythia-1.4B (10M Pile tokens, bf16 forward, dsae=16384d_{\mathrm{sae}=16384}), we retain 15.7 ± 0.1% at layer 8 and 18.4 ± 0.1% at layer 16. These floors depend on the site, so every codec must be compared against its own site’s floor.

When we reseeded only the initialization, we got an indistinguishable 23.3 ± 0.8% (Appendix § 11). Over all latents, under a criterion like Paulo & Belrose’s, our floor is 30.6%, below their GPT-2 SAEs’, which differ in width, training length and criterion [5].

Which features we are considering. Every number about agreement is over alive features, those that fire on more than 0.1% of the evaluation tokens in the reference SAE, which ends up being around 40% of the dictionary at each site. Rarer features fire too rarely for a stable correlation. Raising this cut raises agreement and the reseeding floor and lowers rot-int4’s ratio monotonically across the five cuts we swept (Table 8); ours is the loosest of the five, so a ratio should be quoted with its cut.

Table 8. The alive cut, swept. Tightening it raises agreement and the floor together and lowers rot-int4’s ratio monotonically (1.63 to 1.13× on GPT-2); our 0.001 cut is the loosest of the five. rot-int4 only, so this bounds a ratio, not an ordering; the floor here averages the two seed-0 pairs at each site (23.1 against the three-pair 23.0 on GPT-2).

>0.001> 0.001>0.002> 0.002>0.005> 0.005>0.01> 0.01>0.02> 0.02
gpt2_l6
n alive486029271421654201
floor %23.127.033.740.147.5
rot-int4 ×\timesfloor1.631.501.381.281.13
pythia_l8
n alive657340531523506100
floor %15.816.120.924.736.0
rot-int4 ×\timesfloor1.631.621.431.341.14
pythia_l16
n alive70884510152535373
floor %18.519.225.746.258.2
rot-int4 ×\timesfloor1.411.371.301.221.06

Sanity checks. We ran a few checks after every pipeline change: an identity check (a cache against itself returns exactly 1.0), a misalignment check (one token of misalignment collapses the correlation to 0.15–0.38) and a shuffle control on best-match luck (0.06 pp or less) (Appendix § 11).

What each test can and cannot tell us

Our two tests give very different answers for the same codec. Run the frozen-SAE test on fp16 activations and each feature’s activations correlate with its own activations on fp32 at mean r=0.99997r = 0.99997, every feature above 0.9. Run the retraining test on those same fp16 bytes and only 60.6% of features are retained. Neither number can be taken at face value.

The retraining test

We first examine the effect of adding isotropic noise to the fp32 cache, at exact relative L2L_2 magnitudes from 10−210^{-2} down to 10−710^{-7}. Each level is stored in fp32 (nothing is quantized), passed its own identity check, and got a matched SAE retrained on the same tokens in the same order (Table 14 and Figure 2, Appendix § 12). Across the four noise levels at or below fp16’s distortion, agreement barely moves: 60.6% at fp16’s order of distortion (by coincidence the same number fp16 gives), rising only to 66.0% three decades below, while the frozen SAE reads at least 0.99998 throughout; zero perturbation returns exactly 100%.

Table 14: The noise levels and sparse changes (seed 0; pp/decade is the change in agreement per decade of relative L2L_2 to the next row): isotropic noise at an exact relative L2L_2 and one-ulp changes to a fixed count of values, all stored in fp32 on both sides. Everything from n=77n = 77 to 15,335 is flat; the one-value change also differs in when it reaches training (Appendix § 17). The fp16-scale noise and the millionth-of-values change were re-run twice with fresh randomness (60.6/60.4/60.8 and 70.5/69.7/71.6%); the table prints the original runs.

perturbationrel-L2frozen rrsurvived %pp/decade
zero (bit-exact)01.000000100.0—
noise 10−210^{-2}1.00e-020.99855151.2+7.82
noise 10−310^{-3}1.00e-030.99984759.0+2.38
fp16 (codec)2.03e-040.99996960.6+0.07
noise 10−410^{-4}1.00e-040.99998560.6+1.19
noise 10−510^{-5}1.00e-050.99999961.8+1.69
noise 10−610^{-6}1.00e-061.00000063.5+2.50
noise 10−710^{-7}1.01e-071.00000066.0—
1 ulp on 10−610^{-6} of entries (∼15\sim15k)1.59e-121.00000070.5—
1 ulp on 1,526 scalars1.70e-131.00000068.6—
1 ulp on 153 scalars9.57e-151.00000069.3—
1 ulp on 77 scalars5.61e-151.00000069.9—
1 ulp on one scalar6.24e-151.00000099.98—
seed floor——23.0—

Table 14.

Retrained agreement against perturbation size

Figure 2. Retrained agreement against perturbation size. Blue: calibrated isotropic noise, fp32 on both sides; orange: real codecs; star and diamond: one ulp on one scalar and on a millionth of them; the shaded band marks fp32’s own precision, below which no dense fp32 noise can go. Retraining barely responds to the size of tiny perturbations but separates the real codecs clearly. GPT-2 L6, seed 0; the fp16-scale noise and the millionth-of-values change were each run three times and are plotted at their original run.

Dense noise cannot go below fp32’s own precision, so for the remaining experiments we changed only a few values instead of all of them. When we changed 15,335 scalars (a millionth of the cache’s 15 billion values) by one ulp each, 70.5% of features survived. When we moved a single scalar in the whole corpus by this amount, 99.98% survived. These two experiments differ in how many values moved and in when the change reaches training: the single value’s lasting change most likely enters late (Appendix § 17), so the 29.5 pp gap cannot be put down to count alone. Most of the features lost at r>0.9r > 0.9 have shifted rather than disappeared: lowering the threshold to r>0.7r > 0.7 raises agreement from 70.5 to 89.8% (Table 16).

Table 16: How far the two smallest perturbations sit above the seed floor at each counting threshold. The margin is positive everywhere (11–43 pp dense, 12–47 sparse) but its size is threshold-dependent.

criterionperturbed %floor %margin (pp)below ceiling (pp)
10−710^{-7} dense
r>0.50r > 0.5095.683.8+11.84.4
r>0.70r > 0.7088.359.4+28.911.7
r>0.85r > 0.8576.232.9+43.323.8
r>0.90r > 0.90 (ours)66.023.0+43.034.0
r>0.95r > 0.9546.310.9+35.453.7
r>0.99r > 0.9912.20.8+11.487.8
sparse, 1 ulp on 10−610^{-6}
r>0.50r > 0.5096.183.8+12.33.9
r>0.70r > 0.7089.859.4+30.410.2
r>0.85r > 0.8579.332.9+46.420.7
r>0.90r > 0.90 (ours)70.523.0+47.429.5
r>0.95r > 0.9552.110.9+41.347.9
r>0.99r > 0.9915.10.8+14.284.9

Table 16.

Three checks (Appendix § 12) confirm that these numbers are real. Re-running the two headline perturbations with fresh randomness moves them by at most 1.1 pp. The full drop is already present at 77 touched values out of 15 billion (n<77n < 77 remains open), and every perturbation retains features far above the reseeding floor.

With near losslessness in values, agreement barely responds to the size of the change (5.4 pp over three decades, against ±1.1\pm1.1 pp of seed noise). However, over the quantizers’ range it falls from 60.6 to 15.6% (Figure 2). Any slope of agreement against perturbation size depends on the range it is fitted over (Table 15), so we use the retraining test only against the reseeding floor.

Table 15: The noise levels and codecs on the seed-stable stratum (n=774n = 774) beside all alive features. The response is flatter still on the stable stratum (0.35 pp per decade from noise 10−710^{-7} to 10−410^{-4}, against 2.6 from fp16 to rot-int4), and the ratio of the two rates depends on the window, so we quote no single ratio.

armrel-L2all alive %seed-stable %
noise 10−710^{-7}1.01e-0766.096.4
noise 10−610^{-6}1.00e-0663.597.0
noise 10−510^{-5}1.00e-0561.896.1
noise 10−410^{-4}1.00e-0460.695.3
noise 10−310^{-3}1.00e-0359.095.9
noise 10−210^{-2}1.00e-0251.293.4
fp162.03e-0460.695.7
rot-int85.51e-0353.894.2
rot-int49.99e-0237.788.8
naïve int44.55e-0115.658.9

Table 15.

The frozen-SAE test

The frozen-SAE test asks the other question: with the same SAE, how big is the difference in feature activations between the original and the compressed activations? As expected, the codecs that are most precise replicate the feature activations best. However, this is nearly equivalent to measuring the L2L_2 distance from the original activations. The frozen reading orders codecs as retraining does (Spearman ρ=0.982\rho= 0.982 over 15 GPT-2 conditions, 1.000 at both Pythia sites), but so do the plain L2L_2 distance between caches (0.979) and mean cosine (0.982), which need no SAE; at n=15n = 15 the three are indistinguishable (Table 21; one exception in Appendix § 16).

Table 21: Spearman rank agreement of three cheap readings with retrained agreement over every released condition per site. The 15 GPT-2 conditions are the 13 storage codecs of Table 1 plus the bf16 forward and the std-int4 ablation. None of the three needs a retrained SAE, and none ranks better than the others.

sitennfrozen rr ρ\rhorel-L2L_2 ρ\rhocosine ρ\rho
GPT-2 L6150.9820.9790.982
Pythia L851.0001.0001.000
Pythia L1651.0001.0001.000

Table 21.

But the order of the codecs does not tell us how replicated the results are. Using the frozen-SAE test’s thresholded count (features whose frozen correlation exceeds 0.9), the fp16 cache gets 100.0% of features intact and int6 and int4-o16 keep 99.9% (Table 1). Retraining on those same bytes, however, returns 60.6, 38.0 and 37.6% respectively, and the same frozen reading of 0.9992 corresponds to keeping 54% of features at one site and 38% at another (Figure 5, Table 4). No L2L_2 distance to the original cache can be calibrated around the reference perturbation, because a seed change changes no bytes, so every distance would return 0, yet only 23.0% of features survive a reseed.

codecGPT-2 rrretr. %L8 rrretr. %L16 rrretr. %
fp160.9999760.61.00000100.01.00000100.0
rot-int80.9991953.80.9992243.00.9992438.4
int80.9963246.60.9948832.90.9891627.7
rot-int40.9853637.70.9867825.80.9869626.1
int40.8400515.60.563332.80.156330.8
seed floor—23.0—15.7—18.4

Table 4. Frozen-SAE mean rr beside retrained agreement at three sites. Within a site the frozen SAE ranks the codecs as retraining does; across sites the same reading carries no level (rot-int8 reads within 0.00005 everywhere and retrains to 2.09–2.73× the local floor). Codec rows are seed 0; floors are three-pair means.

Plot of frozen-SAE reading against retraining at three sites

Figure 5. The frozen-SAE reading against the retraining it stands in for, at three sites; the xx axis is frozen disagreement 1−r1-r, log scale, improving rightward. Every series is monotone (the frozen SAE ranks codecs correctly), but the series are offset from each other (the same reading corresponds to different retained percentages at different sites); the viable codecs crowd into a span a linear axis would collapse to a point. Dotted lines are each site’s seed floor. Pythia’s fp16 point is omitted: the bf16 master makes it bit-lossless.

We found this pattern of viable codecs passing and naïve int4 failing to be not SAE-specific: linear probes and a causal steering test (difference-in-means vectors injected into the residual stream), each scored against its own control, got the same results with three exceptions (PCA-256 keeps probe accuracy on one of two tasks while its directions changed, and passes the steering test on average; int4-o16 fails one probe floor; Appendix § 16).

Calibrated verdicts

Table 1 has the full thirteen-codec GPT-2 result and Fig. 3 (Appendix § 13) plots the codecs run at every site against each site’s reseeding floor. Four conclusions follow, then the bf16 forward.

For viable codecs, most of the features lost would have been lost under reseeding anyway. Naïve int4 loses far more. Under rot-int8, only 0.13% of features that survive seed change (seed-stable) are lost in all three seeds. 0.90% are lost in rot-int4 while 24.2% are lost in naïve int4 (Table 19). Each viable codec retains 88.8–95.7% of the seed-stable features on GPT-2 and 82–89% on Pythia. Seed-unstable features are lost to a greater degree: 27–54% are retained on GPT-2 and 19–38% are retained on Pythia. This gap stays regardless of our threshold for outlier stability (Table 20). Reconstruction error sees none of this: the fraction of variance unexplained (FVU) stays flat, 0.1355 at fp16 through 0.1361 at int4-o16, while agreement falls from 60.6 to 37.6% (Table 1).

Table 19: Is codec damage a shared bias or seed-like noise? On the fp32 seed-stable set (n=774n = 774), losses are enriched above independence for every codec, so a shared component exists, but under rot-int8 only 0.13%0.13\% of seed-stable features are lost in all three seeds against 24.2%24.2\% under naïve int4. Recomputed from released pair files; no training.

codeclost/seed %P(lost s1lost s0) %indep. %lost all 3 %indep. %
rot-int86.117.86.30.130.023
rot-int411.125.312.00.900.134
int441.075.542.924.166.876

Table 19.

Table 20: Survival among seed-stable / seed-unstable features under four definitions of stability: activation-space matching at three thresholds, and weight-space Hungarian matching on encoder+decoder cosine. Under every definition the viable codecs keep far more of the stable features than of the unstable ones (stable 62.9 to 98.8%, unstable 19.9 to 57.9%), and naïve int4 is lowest on both; the levels move with the definition, so a stratified percentage is only interpretable with its definition attached. On Pythia the viable codecs keep 18.6 to 38.3% of the unstable features (runs_m4_L8, runs_m4_L16 stability files). GPT-2 L6, seed 0. Sources: stability_fp32_s0_t0p*.json, stability_hungarian.json.

codecr>0.8r > 0.8 %r>0.9r > 0.9 %r>0.95r > 0.95 %Hungarian %
fp1690.1 / 46.195.7 / 54.097.3 / 57.980.7 / 52.7
rot-int886.0 / 38.094.2 / 46.298.8 / 50.576.4 / 44.9
rot-int473.8 / 19.988.8 / 28.095.5 / 33.362.9 / 27.7
int441.3 / 2.958.9 / 7.473.6 / 11.326.9 / 11.1

Table 20.

Per-token quantization fails on outlier channels and standardizing each channel fixes this. A few channels have values much larger than the rest, so one scale per token means that we waste a lot of levels to reach the largest values. Naïve per-token int4 falls below the reseeding floor everywhere we tested: 15.6% against 23.0 on GPT-2, worse at the larger model and the deeper layer, 0.18×0.18 \times the floor at Pythia layer 8 and 0.05×0.05 \times at layer 16, keeping only 13.7 and 5.1% of even the seed-stable features (Table 2, Table 3). Standardizing each channel before quantizing repairs it at identical raw file size: 15.6 to 38.0% (±0.9\pm0.9 over three seeds), 1.65×\times the floor. Our rotated codec standardizes and then applies one fixed random rotation, so we try not rotating to separate the two effects. We find the rotation adds nothing we can detect (37.7 ±\pm 0.3 against 38.0 ±\pm 0.9% over three seeds, a tie, Table 18). Rotation is useful in inference quantization [15, 16, 17]; but here standardization alone is enough (per-channel outlier handling is standard in LLM quantization [15, 16, 17]; what is new is the measured cost of skipping it). The standardized codecs tie or beat the outlier-channel (-oMoM) codecs at equal or fewer bytes, with one exception the other way: int4-o16 keeps more seed-stable features than rot-int4 (91.2 against 88.8%, Table 1). With zstd on top, std-int4 compresses at 303.8B/token against fp16’s 1,419 (Table 11).

codecsurvived %×\times floorstable %
fp16 (bit-identical ceiling)100.06.35 [6.00, 6.71]100.0
rot-int843.02.73 [2.58, 2.87]88.0
int832.92.09 [1.98, 2.20]84.9
rot-int425.81.64 [1.55, 1.73]81.6
int42.80.18 [0.15, 0.20]13.7
seed floor15.7 ± 0.11.00×(def.)

Table 2.

codecsurvived %×\times floorstable %
fp16 (bit-identical ceiling)100.05.44 [5.17, 5.70]100.0
rot-int838.42.09 [1.99, 2.18]89.3
int827.71.51 [1.44, 1.57]83.4
rot-int426.11.42 [1.35, 1.48]83.0
int40.80.05 [0.03, 0.06]5.1
seed floor18.4 ± 0.11.00×(def.)

Table 3.

codecraw B/tokzstd B/tokratio
fp1615361419.01.08×1.08\times
int8772510.91.51×1.51\times
rot-int8772709.11.09×1.09\times
int6580431.91.34×1.34\times
rot-int6580557.01.04×1.04\times
int4388146.42.65×2.65\times
rot-int4388307.51.26×1.26\times
std-int4388303.81.28×1.28\times
int4-o16412309.11.33×1.33\times

Table 11. Lossless zstd-3 over the quantized payloads, measured on one shard per codec. Rotated codecs compress only 1.04–1.26× further; naïve scaling leaves headroom that zstd recovers. Standardization without the rotation lands with the rotated codec (303.8 against 307.5 B/token).

Table 18: Which step repairs 4-bit. std-int4 is byte-identical to rot-int4 with the rotation set to the identity, so the two differ by exactly the rotation: standardization alone gives 38.0±0.9%38.0 \pm0.9\%, adding the rotation 37.7±0.3%37.7 \pm0.3\%, inside the tie band. All three arms are three-seed means over same-seed pairs. The ablation keeps the rotation matmul, so it isolates the rotation’s effect, not its decode cost.

armB/tokrel-L2L_2frozen rrsurvived %×\times floor
naïve int43880.45530.8400516.0±0.316.0 \pm0.30.69×0.69\times
std-int4 (standardize only)3880.10330.9846638.0±0.938.0 \pm0.91.65×1.65\times
rot-int4 (standardize + rotate)3880.09990.9853637.7±0.337.7 \pm0.31.64×1.64\times

Table 18.

On a second model, we get the same ranking but the exact percentages are different. Pythia-1.4B is a model in a different family with 11×\times the parameters, with a different dataset and tokenizer. We see the same codec order in terms of retained features (the only inversions are among tied codecs) and the same 4-bit split between standardized and naïve quantization (Figure 3). Naïve int4 performs worse than reseeding at every site and threshold (Table 13). The exact percentages, however, changed with everything we varied (definition of stability, matching metric, budget, sparsity, firing-rate cut, threshold). The rot codecs’ ratios span 1.06 to 3.55×3.55 \times (Table 9, Table 8, Table 13).

Table 9. Sparsity moves the floor without touching a byte: over a 1.95× swing in the floor, rot-int8’s ratio spreads 1.72–3.23× while its survival among seed-stable features spreads only 92.8–97.1%.

sparsityseed floor %rot-int8 %×\timesfloorstable-only %naliven_{\mathrm{alive}}stable frac %
k=16k = 1628.6 ± 0.849.11.72×92.82,91221.0
k=32k = 3223.0 ± 0.153.82.34×94.24,86015.9
k=64k = 6414.7 ± 0.247.43.23×97.18,26910.1

Table 13: Ratio to floor as the counting threshold moves. Levels change with the threshold; the ordering and the below-floor verdict for naïve int4 do not.

r>0.7r > 0.7r>0.8r > 0.8r>0.85r > 0.85r>0.9r > 0.9r>0.95r > 0.95
GPT-2 L6
floor (survival)59.442.432.923.010.9
fp161.45×1.83×2.15×2.63×3.78×
rot-int81.39×1.71×1.97×2.34×3.19×
rot-int41.22×1.39×1.50×1.63×1.90×
int40.83×0.79×0.75×0.68×0.61×
Pythia L8
floor (survival)56.336.526.315.76.0
fp161.78×2.74×3.80×6.35×16.80×
rot-int81.43×1.86×2.19×2.73×3.55×
rot-int41.20×1.37×1.49×1.64×1.72×
int40.43×0.33×0.25×0.18×0.07×
Pythia L16
floor (survival)56.938.928.918.47.7
fp161.76×2.57×3.46×5.44×12.98×
rot-int81.34×1.60×1.81×2.09×2.50×
rot-int41.14×1.25×1.36×1.42×1.55×
int40.17×0.10×0.08×0.05×0.02×

Table 13. Rows are a four-codec slice of a sweep covering 13 codecs at GPT-2 and these four at each Pythia site. Ordering is invariant across it at both Pythia sites. On GPT-2 the only inversions are among rot-int4, int4-o16 and int6 (the last two not shown here), whose largest inverting gap is 0.45 pp, inside the tie band. Naïve int4 is below floor at every threshold at every site.

Agreement as a multiple of each site's own seed floor against measured bits per dimension

Figure 3. Agreement as a multiple of each site’s own seed floor, against measured bits per dimension, 1×1\times budget (33M token-visits on GPT-2, 24.6M on Pythia). Filled markers: fp16 and the rotated codecs; hollow: naïve per-token at the same rate. The dotted riser is the effect of standardizing and rotating at identical bytes (on GPT-2, standardizing alone gives the same, Table 18), and at 4 bits it spans the floor at every site. Read the ordering, not the levels: at 10×10\times budget rot-int4’s ratio is 1.09×1.09\times. Pythia’s fp16 is its own bf16 master, so it is omitted there.

The reseeding floor rises with training, and every ratio moves with it. When we scale training to 3×3 \times and 10×10 \times, the reseeding floor rises from 23.0 to 42.3%. More optimization makes seeds agree with each other, so every codec agreement ratio above 1 compresses toward 1 (fp16 from 2.63 to 1.20×\times, rot-int4 from 1.63 to 1.09×\times). However, naïve int4’s agreement stays flat while the reseeding floor climbs, so it performs worse relatively, from 0.68 to 0.39×\times the floor. More training does not recover the information the codec lost. At 3×3 \times, fresh text in place of our re-read cache gives the same floor (33.4 against 33.3%) and std-int4 lead (Appendix § 14).

The effect of a bf16 forward pass. Now, we examine the effect of something that is not a codec. If we computed the cache with a bf16 forward pass, as Llama Scope does [1], there is no compression and yet the frozen SAE trained on fp32 has a correlation of r=0.99819r = 0.99819. At our 1×1 \times budget, the bf16 forward costs 1.8 to 3.0 pp more retrained agreement than rotated 8-bit storage at all three seeds (49.7–51.2 against 51.6–53.8%, Table 10; two of the three gaps exceed the tie band), but the forward pass itself runs 2.21×2.21 \times faster. Now the scope is important as our budget is 33M token-visits. Gemma Scope trains each SAE on 4–16B tokens [2] and at 3×3 \times our budget the gap falls inside the tie band (one seed, Appendix § 13).

treatmentB/tokrel-L2frozen rrsurvived %×\times floorstable %Hung. cosFVU
fp16 storage15360.000200.9999760.62.6395.70.8170.1355
rot-int8 storage7720.005510.9991953.82.3494.20.7860.1356
int8-o8 storage7800.009280.9986850.82.2193.70.7730.1355
bf16 forward30720.009800.9981951.22.2294.20.7750.1356
int8 storage7720.026600.9963246.62.0291.50.7510.1356
seed floor———23.01.00(def.)——
bf16 fwd, 3 seeds———49.7–51.2————
rot-int8, 3 seeds———51.6–53.8————

Table 10. A treatment that is not a storage codec: computing the cache with a bf16 forward (stored in fp32) beside the storage codecs. Over three seeds the bf16 forward retains 49.7–51.2% against rot-int8’s 51.6–53.8, disjoint but with n=3n = 3 (a sign test cannot go below p=0.25p = 0.25; within-seed deltas −2.7, −1.8, −3.0 pp). At 3×3\times budget the comparison reads 44.4 against 45.3% (one seed each), inside the tie band. It runs 2.21×2.21\times faster.

Takeaways. If your forward pass is bf16, store the cache as bf16: that is bit-lossless, and the only remaining question is whether to compress to below 16 bits. However, storing an fp32 forward as bf16 is not free (55.6 against fp16’s 60.6%, Table 1). Measured against 16-bit storage rather than fp32, rotated 4-bit is 4.0×4.0 \times smaller before entropy coding, not the 7.9×7.9 \times an fp32 baseline suggests. Standardize each channel before quantizing to 4 bits. Once that is done, 4 bits loses less agreement than a seed change at the budgets we measured, and the rotation on top of it adds nothing we can detect on GPT-2 (Table 18), so skip it. In fp16’s bytes std-int4 holds 95.39 times the fp16 in cross-seed agreement at 3×3 \times, but trains at most 1.09 times faster than fp32; decode on the GPU (Appendices § 14, § 15). Do not reduce dimension by PCA or random projection (7.8 and 2.8% on GPT-2, far below the floor, Table 1), and do not certify a training codec with a frozen SAE or with reconstruction error: the frozen SAE ranks codecs without saying what you will keep, and reconstruction error is flat across the viable codecs. If you only run pre-trained SAEs over the cache, the frozen reading is the right test (fp16, int8 and int4-o16 read 99.9 to 100% on it). Calibrating a site costs four training runs and two evaluation passes (Appendix § 10), and if you already plan multi-seed training [5] the floor’s runs are essentially free.

Scope and limitations

Our compression verdicts concern activation caches for SAE training. More broadly, any change to an SAE’s training data needs a floor and ceiling, because retraining is more sensitive than it looks.

What the count measures. Counted by what features do, the verdict holds: removing a feature and its std-int4 match from GPT-2 shifts predictions the same way for 38.7% of features, against 23.0% for a reseed. Whether one ulp, fp16, std-int4 or a reseed caused a loss, four retrained features rebuild only 12–15% of the lost ones (Appendix § 9), so a viable codec causes less of what a reseed causes. The count cannot say whether these are the model’s own features, only which ones training finds reliably at this budget. Firing rate and activation size in one SAE predict which of its features are seed-stable (AUC 0.85) and which survive a compressed cache (0.83–0.84, Appendix § 17).

Limitations. Two models, residual-stream sites only, unnormalized activations, and research-scale budgets: 10×10 \times is 328M token-visits, and fresh text was tested at 3×3 \times only. Graded claims are TopK only: on JumpReLU the viable codecs sit within a few points of the floor (Table 5). Pythia’s reference is itself a bf16 forward, so already perturbed. All SAEs are evaluated on fp32 activations, and the panel omits fp8 and block-scaled formats. In short: calibrated by its own floor and ceiling, the retraining test finds standardized 4-bit caching viable at our budgets, naïve int4 not.

Acknowledgments and Disclosure of Funding

Modal provided the compute credits for the A100 experiments in Appendix § 15.

References

Full result tables

All appendix table cells are generated programmatically from the released evaluation artifacts (scripts/appendix_tables.py), apart from a few constants written into the generator (such as d=768d = 768). Caption prose is checked against the same artifacts by verify_paper_numbers.py, a lint rather than a proof.

Table 2: Pythia-1.4B layer 8, same construction as Table 1. Codec runs are seed 0 only, so gaps under the 2 pp tie band are unresolved; the bracketed intervals resample features against the seed-0/seed-1 floor pair, not seeds. fp16 is bit-lossless under the bf16 master, so its row is a same-seed ceiling.

Table 3: Pythia-1.4B layer 16. Identical construction, interval convention and bit-identical fp16 ceiling reading to Table 2. Source: runs_m4_L16/eval.

Do matched features do the same thing?

The retraining test matches features by correlation. Here we ask whether a matched pair also does the same thing, and whether a feature counted as lost is gone or has been cut differently. Nothing below needs new training. The ablation and subspace analyses are scored on the second 500k evaluation tokens, which the matching never saw. The activating contexts and the token-type overlap use the first 150k and 200k of the matching tokens. The expected outcomes of the ablation and subspace analyses were written down before the runs (released with the artifact).

Activating contexts. Side-by-side top-activating contexts for sampled matched pairs (fp32 vs rot-int4, and the fp32 cross-seed control) are released with the artifact (docs/appendix-semantic-pairs-*.md). Illustrative excerpts: a survived pair (r=0.98r = 0.98) fires on identical “if you want to” contexts on both sides with 90% top-10 position overlap; a damaged pair (r=0.47r = 0.47) matches a precise “rise/increase of” feature to a coarser “of / fall of” feature: related, visibly degraded. The seed-pair control shows the same qualitative signature: codec damage is seed-shaped. Bands (survived r>0.9r > 0.9, damaged r<0.5r < 0.5) follow [4]. Survived pairs share 64–85% of their top-50 activating contexts while damaged pairs share 20–27%, and the two bands separate in all seven comparisons we measured.

Ablation. We sample 300 alive features of the fp32 SAE. For each, we take the 32 held-out rows where it fires most strongly and remove its own contribution (its activation times its decoder direction) from GPT-2’s residual stream at the SAE site, leaving everything else unchanged so reconstruction error plays no part. We record how next-token log-probabilities change where it fires, then do the same with its best match in each retrained SAE, on the same rows. The pair has the same effect if the two changes have cosine above 0.9 (Table 23). By this causal count std-int4 keeps 38.7% of the sampled features, the same as its correlational count on the sample, against 23.0% for a reseed and 12.7% for naïve int4: a lead over reseeding of 15.7 pp (95% CI 10.5 to 20.8, bootstrap over features). Among pairs the correlation counts as matched, std-int4’s effects are as close as a reseed’s (median cosine difference +0.014, CI −0.018 to +0.042), so the features it keeps act like seed-stable ones. In every condition with pairs on both sides, pairs above r=0.9r = 0.9 have more similar effects than pairs below 0.5. The correlation criterion is not sufficient on its own, though: among pairs above r=0.9r = 0.9 the share with the same effect runs from 94.5% (one pull up on a millionth of values) to 46.8% (naïve int4), and is 59.0% for a reseed. Per feature the two criteria agree 79–86% of the time (Spearman 0.77 between match correlation and effect cosine over 1,800 pairs). The identical-bytes retrain returns cosine 1.000; that checks the plumbing, not the measurement’s own noise, since that retrain is bit-identical.

conditionr>0.9r > 0.9same effectsame | r>0.9r > 0.9effect cosΔlog⁡p\Delta\log ptop-50 ctx
identical bytes100.0100.0100.01.0001.0001.00
1 ulp on 10−610^{-6} of values73.079.794.50.9710.9660.82
fp1663.768.089.50.9420.9350.78
std-int438.738.773.30.8420.8160.62
naïve int415.712.746.80.6510.5840.38
reseed, seed 126.022.753.80.7480.6870.48
reseed, seed 222.323.364.20.7270.6800.46

Table 23. Do matched features do the same thing? 300 sampled alive fp32 features, each removed from the residual stream on its 32 strongest held-out rows, then its best match in each retrained SAE on the same rows. Same effect: cosine above 0.9 between the two changes in next-token log-probabilities; the last three columns are medians. std-int4 exceeds reseeding by 15.7 pp (95% CI 10.5 to 20.8); among pairs matched at r>0.9r > 0.9 its effects are as close as a reseed’s (median difference +0.014, CI −0.018 to +0.042). Seed 0, one sample.

Subspaces. Features that fail to reappear across seeds can still span a subspace that does reappear [6]. If that explained our losses, a lost feature could be rebuilt from a few features of the retrained SAE. For each alive feature we fit a least-squares map from its mm best-correlated partners’ activations on the matching tokens, and count the feature recovered if the map explains more than 81% of its held-out variance (r>0.9r > 0.9 squared, so m=1m = 1 is the retraining count; Table 24). Four partners add about 10 pp for a reseed (23 to 33%), and std-int4’s lead survives: 46.6 against 33.2% with four partners, 37.9 against 22.9% with one (three runs each). Random partners recover at most 0.12%. One up on a millionth of values is not a change of basis: four partners take it only from 70.4 to 74.0% (three draws), well short of the 95% we set in advance as the mark for a pre-cut. For whole sets the answer is closer to the subspace picture: the decoder subspace of the fp32 SAE’s lost features captures the retrained SAE’s unmatched features at 88–93% of its within-SAE value in every condition except naïve int4 (73%); a random subspace gets 26–33% across conditions. The sets differ in size across conditions, so we read that column only against its random value.

conditionm=1m=1m=4m=4m=8m=8lost, m=4m=4set EV
identical bytes100.0100.0100.0——
1 ulp on 10−610^{-6} of values70.4 ± 1.174.0 ± 1.275.2 ± 1.113.2 ± 1.20.89 ± 0.00
fp1660.165.667.314.00.91
rot-int437.746.750.014.80.93
std-int437.9 ± 0.846.6 ± 0.849.7 ± 0.814.3 ± 0.10.91 ± 0.01
naïve int415.622.426.28.50.73
reseed22.9 ± 0.333.2 ± 0.437.8 ± 0.413.5 ± 0.40.92 ± 0.00

Table 24. Is a lost feature gone or re-cut? Share of alive fp32 features recovered on held-out tokens by a least-squares map from their mm best-correlated partners (R2>0.81R^2 > 0.81, so m=1m=1 is the retraining count re-scored on these tokens). Recovering from four partners keeps std-int4 13.4 pp above reseeding (three seeds each). Lost: of the features lost at m=1m=1, the share four partners recover, 12–15% in every condition but naïve int4; four random partners recover at most 0.12%. Set EV: the lost features’ top-64 decoder subspace, applied to the retrained SAE’s unmatched features, as a fraction of its within-SAE value [6]. ± rows are three runs.

What this says about retraining. Two regularities hold whatever caused a loss: one up on a millionth of the values, fp16 rounding, standardized 4-bit storage or a new seed. Four partners recover 12–15% of the lost features in each case, and a lost feature’s best match still has a partly similar effect (median cosine 0.41–0.55 over the 8 to 40 pairs below r=0.5r = 0.5 in each condition) and fires on the same kinds of token far more often than chance (token-type Jaccard 0.17 for the seed pair, against 0.007 for random pairs). Only naïve int4 stands apart, recovering 8.5% and shrinking the shared subspace, though its median effect cosine (0.41) is in the same range. Within this range, then, the size of a perturbation sets how many features retraining relocates, not what a relocated feature looks like. That is what makes a reseed a usable unit: a codec that turns over fewer features than a reseed is causing less of the same thing, not something different. It also limits what surviving a reseed means. A seed-stable feature is one this training process finds reliably at this budget, and the budget tiers show that set growing with training (Appendix § 13). Whether sparse dictionary learning finds the features the model itself uses is a separate question, and no count of retraining agreement, ours included, can settle it.

The protocol as a recipe

To calibrate any treatment TT of SAE training data on a new stack:

  1. Fix the substrate. One master cache per site from a single model forward; derive every treated dataset from it shard-for-shard; derive the loader’s shuffle from the seed alone. Two runs at one seed then consume identical tokens in identical order and differ only in the treatment.

  1. Measure the ceiling (1 extra run). Retrain at the same seed on the same bytes. Bit-reproducible stacks return 1.0 and any same-seed deviation is pure treatment effect; otherwise the measured ceiling is the upper bound every later number is read against.

  1. Measure the floor (2 extra runs). Retrain at three or more seeds on the untreated data and report the mean with the min–max range. This is the unit everything else is quoted in. Two further runs that reseed only the initialization bound how much the data order contributes; on our site the two floors are indistinguishable.

  1. Run the identity and misalignment checks (2 eval passes). Compare a dataset with itself through the entire pipeline (it must return exactly 1), and then against itself misaligned by one token: it must collapse. The first decodes one store twice, so it catches nondeterminism but not a fault symmetric across both sides; the second is what catches a metric too blunt to see drift, and a pipeline that agrees regardless. Rerun both after every pipeline change.

  1. Measure the saturation point (1 extra run). Retrain on the untreated data perturbed by one ulp on a millionth of its scalars, a departure from bit-identity eight orders of magnitude below fp16. On our site that alone costs 29.5 pp of agreement. Without this measurement a practitioner reads fp16’s 39.4 pp gap as codec damage, when a perturbation eight orders of magnitude smaller already costs three quarters as much. Read it as a reference point that other numbers are placed against, not as a term to subtract out, since drift and codec damage need not add: our own locality control moves it 2.6 pp at matched count, and we ran it once at one site.

  1. Stratify, then re-stratify. Report TT separately on features reproduced across the floor’s seeds, and re-derive that split under a second, independent stability definition (we use weight-space Hungarian matching against activation-space matching). Levels move between definitions even when rankings do not, so quote the definition and threshold with every percentage.

  1. Report the ratio as a local unit. Absolute agreement does not transfer across sites, and the ratio itself moves with budget, sparsity and the firing-rate threshold (§ 4, Table 9, Table 8); a number is interpretable only with all of them attached. What travels is the ordering within one test; across tests even that can reverse (§ 3.2, Appendix § 16).

Steps 2–5 are the whole cost of the calibration: four training runs and two evaluation passes, independent of how many treatments are then compared against them.

Pipeline validation in full

Running a cache against itself through the whole pipeline returns exactly 1.0: a mean rr of 1.0000000 over 500k tokens, on raw channels at all three sites and through a frozen SAE at GPT-2 and Pythia layer 8. We then misalign one side by a single token, and the correlation collapses to 0.15–0.38 across the sites and codecs we ran. We rerun both controls after every change we make to the pipeline. The identity check (our null) decodes the same store twice, so it catches nondeterminism but not issues that affect both sides equally. This is why we also run the misalignment check: if our pipeline were broken in a way that returns high agreement no matter what, it would still pass the identity check, but it would fail the misalignment one. On an A100 the same-seed ceiling also returns exactly 100%, the floor pair reads 22.8% against our 23.2 for that pair, and rot-int4 reads 37.1 against 37.7% (runs_cuda_modal/). We also bound one further source of inflation. Since each feature searches 12,288 candidates for its best match, some matches could just be luck. When we shuffle the candidates, every match dies (nothing comes near 0.9); a stricter shuffle that preserves co-activation patterns inflates agreement by at most 0.06 pp; and 96–99.9% of matches are one-to-one for the viable codecs and the seed pair (90.9% for naïve int4, 87.1% for PCA-256; released artifacts).

There are also nuances about what the floor means. Our seed impacts both the initialization and the data order, which is a wider change than Paulo & Belrose’s same-data floor [5]. When we reseeded only the initialization and kept data order fixed, we got 23.3 ± 0.8% across three pairs, indistinguishable from the full floor. With a weight-space metric like Paulo & Belrose’s (Hungarian matching on the mean of encoder and decoder cosine, where they require both above 0.7) we get 30.6% above cosine 0.7 (over all latents, mean of the two seed-0 pairs). Their headline figure of about the same size is for their largest model, Llama 3 8B; their GPT-2 SAEs share roughly twice as many latents (their Figure 5). Our setup differs from theirs in width, matching criterion and training length, and their own sweeps show sharing moving strongly with both width and training tokens, in opposite directions for us (we use fewer latents and far fewer tokens), so we cannot say which difference explains the gap. Measuring seed instability [5] and choosing an L0L_0 [7] both require training many SAEs on the same data, and a floor like this one is what makes such a sweep readable. The floor moves with sparsity and budget (§ 4, Table 9), so it is worth measuring at the configuration you actually use.

Codec details. intNN quantizes each token vector symmetrically to NN bits with one fp32 absolutemaximum scale per token. intNN-oMM first removes the MM channels with the largest mean absolute activation on a fitting sample and stores them in fp16. std-intNN standardizes each channel with a mean and standard deviation fitted on a sample of the cache, then quantizes per token as intNN does; rot-intNN additionally applies one fixed orthogonal matrix drawn by QR-factorizing a seeded Gaussian matrix before quantizing. Decoding inverts every step, so the SAE always trains on fp32 reconstructions. Pythia uses the same TopK kk=32 but trains for 6,000 steps, so its 1× budget is 24.6M token-visits (about 2.5 passes over its 10M tokens); correlations use 500k held-out tokens at every site.

Robustness of the perturbation experiments

How solid are the headline numbers of §3.1? We regenerated the noise at the fp16-scale setting and the value placements at the millionth-of-values setting, twice each, and got 60.6 ± 0.2 and 70.6 ± 1.0% across the three repetitions, so neither number is an accident of one random draw. We also ran an experiment that packs nearly the same count of changes into one shard of the cache (pre-registered before it ran); it lands worse, not better (67.9%, outside the repetition spread), so where a perturbation sits matters even at fixed count. The three experiments at nn = 77, 153 and 1,526 changed values land between 68.6 and 69.9%, beside 70.5% at 15,335: flat across two decades of count, and each was run once. The trained weights agree with these counts: the one-scalar change moves them by 0.056 relative Frobenius against a reseed’s 1.409 (close to 2\sqrt{2}, as expected for unaligned dictionaries; every alive latent of the one-scalar run still best-matches itself, so no permutation enters on that side). Throughout the paper, counts at r>0.9r > 0.9 are the number a practitioner needs; Table 14 gives the frozen SAE’s continuous readouts beside them. The above/below-floor verdict of §4 also survives a held-out-seed stratified floor: rot-int4 sits at 1.15–1.20× that floor at all three sites and naïve int4 at 0.06–0.71× (stable_floor.json). A codec plus a reseed lands at the floor for the viable codecs (fp32 seed 0 against rot-int8 and rot-int4 trained at seed 1: 24.5 and 23.5%) and below it for naïve int4 (14.2%), while the viable codecs add no drift beyond a reseed. Read against the level where retraining saturates (66–70%) rather than against 100, the standardization repair is partial: std-int4 sits about a third of the way up from the floor and rot-int8 about two thirds.

None of this is reseeding in disguise: both of the smallest perturbations sit far above the seed floor at every counting threshold, by 11–43 pp for the smallest dense noise and 12–47 for the millionth-of-values change (Table 16). Nor is it the weight-side story. In supervised networks, one bit changed in one initial weight can destabilize training as much as a seed change [9], but one altered data scalar costs 0.02 pp and 15,335 of them cost 29.5, still far short of a reseed. On the data side, timing mattered in the one seed we tested: a 10−710^{-7} change to the first batch alone cost about as much as the same change on every batch, and at step 4,000 almost nothing (Appendix § 17; the first batch also sets the decoder bias’s starting value).

One more objection deserves its own check: seed noise averages out across seeds, but a codec’s damage might be the same features in every seed, a shared bias that multi-seed protocols cannot average away. Table 19 tests this on the fp32 seed-stable set. Losses are enriched above independence for every codec, so a shared component exists, but under rot-int8 it amounts to 0.13% of the seed-stable features lost in all three seeds, against 24.2% under naïve int4: a multi-seed protocol recovers almost everything under a viable codec and cannot under a failed one.

Budget scaling in full

When we scale training to 3×3 \times and 10×10 \times (98M and 328M token-visits, three fp32 seeds per tier), the floor rises from 23.0 to 33.3 to 42.3%. Every viable codec closes on the rising floor: fp16 reads 49.2 and 50.6% at 3×3 \times and 10×10 \times (1.47 and 1.20× the floor) and rot-int4 46.3% at 10×10 \times (1.09×). Held at a fixed stable population, fp16 is flat while the rotated codecs improve (Figure 4), so the statement we can defend is that the 16-to-4-bit gap does not widen with budget, not that it provably shrinks. Naïve int4’s agreement stays flat (15.6/16.3/16.5%) while the floor climbs, so it falls from 0.68 to 0.49 to 0.39× the floor. Table 6 carries the per-tier numbers and two caveats: the stability threshold rises with budget, and budget was scaled by re-reading one cache, which isolates optimization from data diversity but not from convergence on a fixed corpus. At 3×3 \times the second caveat no longer applies: fresh text gives the same floor and the same std-int4 lead (Appendix § 14). At 3×3 \times the bf16 forward reads 44.4 against rot-int8’s 45.3% (one seed each), a 0.91 pp gap inside the tie band. At 1×1 \times and matched distortion that treatment is indistinguishable from a storage codec that stores a quarter of the bytes of its fp32-stored cache (Table 10).

budgetstab. threshfp16 %rot-int8 %rot-int4 %gap pp
1×1 \times (33M visits)0.90095.794.288.87.0
3×3 \times (98M)0.93695.794.793.52.2
10×10 \times (328M)0.95495.796.394.21.6

Table 6. Budget scaling on a fixed stable set (N=774N = 774 features at every tier, binomial SE about 1.1 pp). fp16 is flat, the rotated codecs improve. fp16’s count is 741 of 774 at all three tiers by coincidence: the three pairs are distinct runs whose all-alive agreement is 60.6, 49.2 and 50.6%. Budget was scaled by re-reading one 20M-token cache (up to about 16 passes over the same tokens), so this isolates optimization from data diversity but not from convergence on a fixed corpus; at 3×3 \times, fresh text gives the same floor (Table 25).

Agreement on a fixed-size stable set against training budget

Figure 4. Agreement on a fixed-size stable set (N=774N = 774, the 1×1\times tier’s stable-feature count, taking the top NN by cross-seed stability at each budget) against training budget; error bars are binomial standard errors. fp16 is flat; the rotated codecs improve, so the gap does not widen.

Fresh text, replay and equal bytes at 3×3 \times

Our budget tiers re-read one 20M-token cache (Appendix § 13), so the rising floor could be seeds converging on one corpus, and every codec was compared at equal tokens rather than equal bytes. We tested both at the 3×3 \times budget (98.3M token-visits), with the outcomes written down before any run (released with the artifact).

Design. We built a 98.5M-token store whose first 77 shards are the existing 20M-token cache and whose other 300 are new OpenWebText documents from the same file, none shared with the training or evaluation rows (19 rows of boilerplate that repeated an existing row verbatim were skipped). Fresh runs read it once; replay runs read the original cache about five times. Two checks show the two differ only in the text: re-caching a stored shard reproduced it bit for bit, and a fresh run’s training log equals its replay twin’s at all 49 logged steps through step 4,800, diverging only once the fresh run reaches new text (reconstruction loss 474.2 on fresh text against 484.4 on replay at the next logged step). For equal bytes, std-int4 trains on the first 79.0M tokens of the fresh store, 99.76% of the fp16 cache’s bytes and 3.95 times its tokens.

Replay does not inflate the floor. The fresh floor is 33.4% against 33.3% on replay, 0.09 pp apart (Table 25), so the rise from 23.0% at 1×1 \times comes from training longer, not from seeds converging on repeated text. Same-seed fp32 SAEs trained on fresh and on replayed text still agree on only 84.1–85.9% of features.

regimeSAEagreement %× floorstable %fixed-774 %
replay (20M, ∼5 passes)floor (fp32 reseed)33.3 ± 0.11.00——
fp1649.0 ± 0.71.4792.0 ± 0.295.8 ± 0.4
std-int438.7 ± 0.61.1687.8 ± 1.993.7 ± 1.7
fresh (98.5M, one pass)floor (fp32 reseed)33.4 ± 0.31.00——
std-int438.8 ± 0.31.1688.0 ± 1.593.6 ± 1.2

Table 25. Fresh text against replay at the 3× budget (98.3M token-visits). Floors are three seed pairs; codec rows are same-seed pairs at three seeds, all ± half the range. Stable: features matched above r=0.9r = 0.9 in both other fp32 seeds of the same regime; fixed-774: the top 774 by cross-seed agreement, as in Table 6. The fresh floor sits +0.09 pp from the replay floor and std-int4 keeps the same lead in both regimes.

std-int4 holds with fresh text. It keeps 38.8% against the fresh floor’s 33.4% (1.16×, every seed above the floor’s highest pair), as it keeps 38.7% on replay (1.16×; both three-seed means). On seed-stable features the two regimes agree within 0.17 pp (88.0 against 87.8%), and on the fixed 774-feature set the budget tiers use, 93.6 against 93.7% (three seeds each).

Equal bytes is a tie. Same-seed agreement would favour the larger store, which shares the reference’s data order through step 19,100 against step 4,800 for the fp16 cache, so each candidate is scored only against fresh fp32 SAEs of the other two seeds. fp16 on 20M tokens and std-int4 on 79.0M both reach 32.6%, 0.974 of the fresh reference’s own cross-seed agreement (Table 26), and their held-out FVU differs by only 0.46%. After seeing the tie we checked, without having planned to, that this measure can separate candidates at all: naïve int4 drops to 15.9%, while fp32 on the 20M cache lands with the others at 32.8%. At this budget neither the extra text nor the extra precision moves cross-seed agreement with a fresh-text SAE; the 4-bit cache holds 3.95 times the text in the same space and buys nothing measurable with it.

candidatetokens (M)stored GBagreement %×ref
fp32, fresh (reference)98.530333.4 (33.2–33.7)1.00
fp16, replayed20.030.732.6 (32.0–33.0)0.974
std-int4, more text79.030.632.6 (31.4–33.3)0.974
fp32, replayed120.061.432.8 (31.8–33.2)0.980
naïve int4, replayed120.07.815.9 (15.7–16.1)0.476

Table 26. Equal stored bytes at 3×: fp16 on the 20M-token cache against std-int4 on the first 79.0M tokens of the fresh store, in 99.76% of fp16’s bytes. Agreement is cross-seed against fresh fp32 (six pairs per candidate, mean and range), so no candidate shares an initialization or data order with its reference; the reference row is fresh fp32 against itself across seeds (three pairs). The two candidates differ by +0.02 pp: a tie. †Post-hoc resolution check, not pre-registered (naïve int4: seed 0 only, two pairs).

Training time

Does a smaller cache train faster? On our laptop it does not: training is compute-bound, and at 3×3 \times the fp32, fp16, rot-int8 and rot-int4 runs took 2.51, 2.79, 2.88 and 2.63 hours (a pre-registered seven-configuration laptop grid was stopped after its first configuration and is not reported). To test the regime where loading matters we trained full 1×1 \times runs on an A100 through Modal, with the outcome bands written down first, in seven loader configurations that feed identical batches to identical training: fp32, fp16 and std-int4 decoded on the CPU as in all our other runs; std-int4 on the CPU without the identity matmul its code path inherits from the rotated codec; and each format decoded and shuffled on the GPU. Within a format every configuration trained bit-identical weights, from local disk and from Modal’s network volume, on both A100 variants Modal assigned.

A smaller cache did not materially speed training (Table 27): the fastest std-int4 configuration ran 1.09 times as fast as the fastest fp32 one from local disk and 1.04 times from the network volume, under the 1.10 we set in advance for a material gain. The loader mattered more than the format. With our CPU decoder std-int4 trains 1.26 to 1.50 times slower than fp32 on the same decoder (1.28 to 2.39 times slower than fp32’s fastest path; our laptop loader rates are in Table 12), and moving decoding and shuffling to the GPU puts every format between 118.5 and 129.1 thousand tokens/s. At that rate fp32 needs about 367 MB/s from storage and std-int4 about 48 MB/s; Modal’s volume delivered 60 to 840 MB/s between runs, and the fp32 run it starved, at 269 MB/s, trained at 0.41 of the fastest fp32 run’s speed. So the case for a compressed cache is storage, 3.95 times the text in the bytes fp16 needs, with the same cross-seed agreement at our budget (Appendix § 14), and speed only where storage delivers well under what fp32 needs; if you compress, decode on the GPU. Two caveats. The sandbox does not let us drop the page cache, so after the copy to local disk a 7.8 GB std-int4 store can be served from memory, which favours it. And Modal placed some runs on the PCIe A100; on SXM4 runs alone the ratios are 1.08 and 1.01.

codecB/tokread disk MB/sdecode MB/sloader tok/s
fp323072309410416383k
fp16153630854748445k
int87722743754319k
rot-int87722917242205k
int65802796208228k
rot-int65802998113147k
int43882677185270k
rot-int4388254386162k
int4-o1641230372555k
pca2565122603402335k

Table 12. Serial loader throughput per store (prefetch = 0, warm page cache, one Apple M5). The 1×1\times training run consumes 10,017 tokens/s, so even the slowest store (int4-o16, 55k tokens/s) outruns training; the standardize-and-rotate decode path costs real time (rot-int8 reads at 64% of int8’s rate). One-machine numbers. In a paired idle-machine measurement (two interleaved runs each), std-int4 decodes at 88 MB/s against rot-int4’s 89; our std-int4 keeps the rotation matmul with an identity matrix, so this does not isolate the rotation’s own cost.

Table 27: Training throughput on an A100 (thousand tokens/s, full 1×1 \times run, mean of two fresh containers) by cache format and where the loader decodes; busy is the share of training time the loader spent reading, decoding and shuffling. Every decode path of a format trains bit-identical weights. Best std-int4 over best fp32: 1.09 from local disk, 1.04 from the network volume, under the pre-registered 1.10 for a material gain. Volume reads varied 60–840 MB/s between runs.

formatdecodelocalbusyvolumebusy
fp32CPU (paper)117.00.8375.10.99
GPU118.50.51119.50.53
fp16CPU (paper)114.60.89115.10.84
GPU121.00.28119.70.33
std-int4CPU (paper)92.90.9950.11.00
CPU, no matmul92.60.9990.90.99
GPU129.10.08123.80.20

Table 27.

The other tests

Linear probes, scored against their own resample floor, pass every viable codec we probed but int4-o16 (below) and fail naïve int4 (Table 7). The exceptions cut both ways: PCA-256 keeps probe accuracy on sst2 (on_ag news its accuracy drops outside the floor) while its learned directions collapse on both (0.50/0.55 cosine), which an accuracy-only check would not show, and int4-o16 fails one probe floor that the retraining test passes, so no single test is uniformly the strictest. A causal steering test, scored against the variation between two disjoint halves of the same data, fails naïve int4 on both tasks and passes the three viable codecs we tested, but it also passes PCA-256 on average, which retraining puts far below the floor (Table 17). Figure 5 shows the cross-site picture for the frozen SAE itself. The frozen SAE does win in one place, across codec families and only on its continuous reading: PCA-256 (projection onto the top 256 principal components) is the smaller perturbation by both magnitude measures yet halves naïve int4’s agreement (7.8 against 15.6%). Frozen mean rr orders that pair correctly (0.810 against 0.840; int4’s value is the GPT-2 column of Table 4); the thresholded reading slightly inverts it (37.2 against 36.7%, the frozen column of Table 1).

Table 7. Linear probes: accuracy change against the fp32-trained probe, and learned-direction cosine, each scored against a seven-split resample floor (0.008/0.002 accuracy and 0.80/0.77 cosine on sst2/ag_news, mean over splits, with cosine minima 0.788/0.759; probe_floor.json). Accuracy resolution is one validation example, so read the deltas as counts. PCA-256 keeps accuracy on sst2 but not on ag_news (its drop there is outside the floor) while its directions collapse to about 0.5 on both; int4-o16 fails the sst2 direction floor (0.778 against a floor minimum of 0.788) on a test the retraining test passes. The two other codecs with probe runs, int8 and rot-int6, clear both floors on both tasks (0.991/0.995 and 0.998/0.998 cosine) and are omitted for width.

taskcodecΔ\Deltaaccdirection cos
sst2fp16+0.00111.000
sst2rot-int8+0.00001.000
sst2rot-int4+0.00110.949
sst2int4-o16-0.00800.778
sst2int4-0.06080.489
sst2pca256-0.00460.500
ag_newsfp16-0.00011.000
ag_newsrot-int8+0.00001.000
ag_newsrot-int4+0.00040.968
ag_newsint4-o16-0.00170.937
ag_newsint4-0.05540.524
ag_newspca256-0.01620.547

Table 17: The causal test: a difference-in-means steering vector injected into the residual stream, scored against the variation between two disjoint halves of the same data (the half-versus-half floor runs about twice a full-data comparison, so it is conservative only for a fail; the like-for-like referent is half the floor). Naïve int4 fails at least 399 of 400 resample splits on both tasks; PCA-256 passes on average but fails 51 of 400 ag_news splits against the like-for-like referent; task-matched prompts and a strength sweep leave every verdict unchanged.

codecdirection coscausal coseffect/floor
ag_news (floor 0.9995 / 0.9998)
fp161.00001.00000.002
rot-int81.00001.00000.007
rot-int41.00001.00000.053
pca2560.99980.99990.276
int40.79010.829413.713 FAIL
sst2 (floor 0.9851 / 0.9877)
fp161.00001.00000.001
rot-int81.00001.00000.003
rot-int40.99991.00000.056
pca2560.99910.99980.245
int40.87630.97022.354 FAIL

Table 17.

One selection comes close to running the other way. Among the 774 features with the highest peak activation in the fp32 SAE (the size of the seed-stable set), naïve int4 keeps 43.8% against that set’s own floor of 42.1% (1.04×, a tie by our 2 pp rule; one seed); among the 774 with the highest mean activation it stays below (0.69×; stratify_singlerun.json). Every whole-SAE comparison of naïve int4 in the paper is below the floor. Which features a codec loses is partly predictable from fp32 statistics alone (Table 22).

Table 22: Predicting per-feature survival from fp32 statistics. CV AUC: a logistic model on all three, 5-fold cross-validated; the other columns are single-statistic AUCs. Mean activation is the strongest single predictor, unlike the peak activation reported for weight quantization.

codecCV AUCmean actpeak actrate
rot-int60.8360.7580.7130.552
rot-int40.8440.7550.7110.570
int4-o160.8310.7490.7110.564
int60.8380.7470.7090.573

Table 22.

Which features retraining recovers, why, and when

A TopK SAE is piecewise affine. The input space splits into regions where the set of active features stays fixed, and inside each region every active feature is an affine function of the input. The regions meet where two pre-activations tie (or where an active one crosses zero, a case our model below leaves out). We call a feature’s margin on a token the gap between its pre-activation and the token’s runner-up, which is the largest pre-activation left out of the top kk. We asked two questions about this geometry and one about training dynamics, and wrote down how we would read each answer before running it (scripts/geom_margins.py, scripts/geom_identifiability.py, scripts/geom_dynamics.py).

What the frozen-SAE test measures. A codec error ee moves every pre-activation by WencTeW_{\mathrm{enc}}^{\mathsf{T}}e. A feature’s frozen activation then either shifts within its region or jumps when the token crosses into another one: the feature drops out if it falls below the runner-up, and a silent feature drops in if it rises past the weakest active one. We predicted each feature’s frozen disagreement 1−r1-r using only the second moment of the codec’s error (estimated on held-out tokens, with no per-token errors) and the SAE’s margins on clean activations, treating the error as Gaussian. For the 11 codecs whose error looks like noise, the prediction ranks features with a median Spearman ρ\rho of 0.980 (0.736 for fp16, up to 0.992), where we had set 0.5 in advance as the mark of an explanation. It beats the best single feature statistic (firing rate, mean or peak activation, median ∣ρ∣|\rho| = 0.830) on all 11, where we had required 9 (Table 28). Peak activation, the strongest single predictor of survival when the weights are quantized [4], still adds a little beyond our prediction: its median absolute partial correlation is 0.113, just above the 0.1 below which we had said in advance it would count as explained. As we expected, the Gaussian model fits worse when the codec removes part of the signal instead of adding noise (0.861 for naïve int4, 0.877 for PCA-256 and 0.608 for random projection), and for naïve int4 and random projection it no longer beats the best single statistic (0.875 and 0.681). For these three, boundary crossings account for a median 84, 46 and 57% of a degraded or damaged feature’s squared error. By the bands we set in advance (a share of 0.5 or more means crossings dominate, 0.3 or less means distortion within a region does), crossings dominate for naïve int4 and random projection, and PCA-256 is mixed. Our code reproduces the frozen test’s per-feature correlations to floating-point rounding.

Table 28: The frozen-SAE test explained by TopK margins (GPT-2 layer 6, the frozen test’s own SAE and tokens). model: Spearman correlation, over alive features, between predicted and measured frozen disagreement 1−r1-r; the prediction uses only the second moment of the codec’s error, estimated on held-out tokens, and the SAE’s margins on clean activations. statistic: the largest ∣ρ∣|\rho| of log firing rate, log mean or log peak activation with 1−r1-r. crossing %: median share of a degraded or damaged feature’s squared error that comes from tokens where it is active on one side only (–: not reported for the noise-like codecs, which have at most 6 degraded or damaged features each). Below the rule, codecs that remove part of the signal rather than add noise. Sources: geom_margins.json, geom_summary.json.

codecfrozen mean rrmodel ρ\rhostatistic ∣ρ∣|\rho|crossing %
fp161.00000.7360.644–
bf160.99980.8880.734–
int80.99630.9890.843–
int8-o80.99870.9660.818–
int60.98320.9910.861–
int6-o80.99480.9790.838–
rot-int80.99920.9590.795–
rot-int60.99690.9840.818–
rot-int40.98540.9880.830–
std-int40.98470.9800.835–
int4-o160.98210.9920.869–
int40.84010.8610.87584
pca2560.80980.8770.82946
rp2560.57960.6080.68157

Table 28.

Which features retraining finds. Results on sparse dictionary learning tie recovery to sparse codes and incoherent atoms [18, 19]. That suggested two causes of seed instability: atoms that mostly fire together (their span can be re-cut at little cost), and atoms that point in nearly the same direction. On held-out tokens we measured both in one SAE (co-firing with the best partner, decoder coherence, and co-firing with the nearest decoder atom), along with the TopK margin relative to the activation and the encoder-decoder cosine, and compared these five with firing rate and mean and peak activation. Seed stability turns out to be predictable. A logistic model on all eight measures reaches a cross-validated AUC of 0.868 on the 1×, k=32k = 32 SAE, where 15.9% of the 4,860 alive features are seed-stable, and the three activation statistics alone reach 0.846. Fit once there and applied unchanged, it predicts stability in the 3×, 10×, fresh-text, k=16k = 16 and k=64k = 64 SAEs at 0.752 to 0.902, all above the 0.65 we set in advance, as are the geometric measures alone (0.696 to 0.842; Table 29). Fit to seed stability, the models also predict which features survive retraining on fp16, rot-int4 and std-int4 caches: 0.743 to 0.762 from the geometric measures (we required 0.65), 0.808 to 0.828 from all eight, and 0.830 to 0.841 from the activation statistics alone. That fits §4, where the viable codecs lose mostly features a reseed also loses. Neither prediction from dictionary learning held. Co-firing with a partner does not mark a feature as unstable (AUC 0.437, slightly the wrong way, where we required 0.60); nor does decoder coherence (0.424, also the wrong way, a measure we set no threshold for); and the geometric measures together do no better than the activation statistics (0.829 against 0.846, where we required a gain of 0.05). The strongest single predictor is the TopK margin (0.734). It is the strongest in five of the six regimes, though often only just ahead of mean or peak activation, and it tracks mean activation (Spearman 0.77). Our reading after the fact is that our budgets the features training finds reliably tend to be strongly active ones that win their TopK slot by a wide margin, and the dictionary-learning conditions we measured do not pick them out.

Table 29: Predicting from one SAE which features retraining recovers (GPT-2 layer 6, AUC). Above the rule the target is seed stability (best match r>0.9r > 0.9 in both other seeds); the logistic model is fit once on the 1×1 \times, k=32k = 32 SAE (its row is cross-validated) and applied unchanged to the other regimes’ own SAEs. Below the rule, the same seed-fit model predicts which features survive retraining on each codec. positive %: seed-stable or retained share of alive features. statistics: log firing rate, log mean and log peak activation. geometry: co-firing share with the best partner, decoder coherence, co-firing of the nearest decoder atom, TopK margin relative to the activation, encoder-decoder cosine. Predictors use held-out eval tokens. Source: geom_identifiability.json.

targetpositive %statisticsgeometryboth
1×1 \times, k=32k = 32 (fit)15.90.8460.8290.868
3×3 \times24.90.7870.7410.794
10×10 \times32.90.7620.6960.752
3×3 \times fresh text24.90.7930.7380.794
k=16k = 1621.00.8170.8010.837
k=64k = 6410.10.8990.8420.902
fp16 survival60.60.8320.7430.808
rot-int4 survival37.70.8410.7600.828
std-int4 survival38.00.8300.7620.824

Table 29.

When features are decided. Why does changing a million of the values cost three quarters of what fp16 costs (§3.1)? To find out, we reran the seed-0 SAE step for step (its weights match Table 1’s fit for bit) alongside copies whose data got one small change and that trained on the same batches. The change we had planned in advance, one ulp on a single value of the first batch, was lost to rounding and the copy stayed bit-identical. The three readings we had planned all depended on how that copy drifted, so none could be evaluated. We amend the plan before running the rest, but after seeing the first 2,500 steps of three dense changes at step 0, which grew roughly linearly and ended close together. Of 200 random single-value changes at step 0, and of 200 at step 4,000, only 58.0% and 85.5% change the weights after one step. Replaying the one-scalar experiment, whose altered value enters training at steps 112 and 4,970, reproduces the SAE bit for bit. Its first entry changes the weights for only a few steps: they are bit-identical to the reference at every logged step from 130 until the second entry (we compared weights, not the optimizer’s moment estimates). So that experiment’s lasting difference most likely comes from the change at step 4,970, and its 99.98% is the effect of a late change, the reading we had written down for this case; it cannot tell the size of a change apart from its timing. A relative change of 10−710^{-7} to a whole batch at step 0 ends at 64.7% agreement, against 66.0% when every batch carries it, and with nearly the same decoder distance (0.656 against 0.645), inside the 5 pp we had set in advance for one perturbed batch costing as much as all of them. The same change at step 4,000 ends at 99.9%, and from step 6,000 on at 100%, above the 90% we had required for the change at step 7,950. Two thousand steps after the change, the copy’s decoder is 0.234 of its norm away from the reference’s when the change came at step 0, but only 0.045 when it came at step 4,000 (Table 30). The changes at steps 0 and 4,000 end 35 pp apart, which takes the label we had set in advance: damage keeps growing with the steps remaining. Our reading after seeing the results is sharper, that in this run features are decided early. At step 0 the perturbed batch also sets the decoder bias’s starting value, so part of the step-0 cost may enter through the initialization. This is one seed, and we did not test this change at any step between 0 and 4,000.

changestepagreement %distance+2,000 steps
10−710^{-7}, one batch064.70.65610.2338
4,00099.90.08300.0452
6,000100.00.03010.0301
7,000100.00.0198–
7,500100.00.0107–
7,800100.00.0041–
7,950100.00.0001–
one ulp, one value112, 4,97099.980.0562–
10−710^{-7}, every batchall66.00.6447–

Table 30. In this run, when a perturbation reaches training decides its cost (GPT-2 layer 6, seed 0, one run per row). Each perturbed run is a copy of the reference run, which reproduces Table 1’s fp32 seed-0 SAE bit for bit, that receives the change at the listed step(s) and trains on the same batches. agreement: the paper’s retained percentage against the reference. distance: relative Frobenius distance of the decoder from the reference’s, at the end and 2,000 steps after the change. The one-ulp row replays the one-scalar experiment (its altered value enters training twice; the weights are bit-identical to the reference at every logged step from 130 until the second entry; optimizer moments were not compared) and reproduces that SAE bit for bit. The last row is not a copy: it is the separately trained noise7 SAE of Table 14, where every batch carries the change, scored against the same reference. Sources: geom_summary.json, pair_geomdyn_*.json.

Extended related work

Duan [4] measures SAE feature survival under weight quantization through a frozen SAE; we adopt its banding and show the frozen protocol cannot be calibrated for the data question. Paulo & Belrose [5] and follow-ups [6] establish seed instability; we turn it into the calibration a compression study needs. MDL-SAEs [8] also frame SAEs as lossy compression but price explanations; our object is the training corpus. On precision itself, Gemma Scope found that switching SAE and model weights to bf16 changes fidelity against sparsity negligibly at inference [2], and Llama Scope computes its activations in bf16 to save memory [1]; neither retrains on a compressed cache against a seed floor. Rotation-based outlier suppression is used in inference quantization [10]; activation compression for training [11] targets in-step memory, not a persistent dataset. The measurement-validity programme [12, 13, 14] supplies our framing. The outlier-channel problem we hit at 4 bits, and the per-channel scaling that fixes it, are well established in LLM activation and KV-cache quantization [15, 16, 17]; we do not claim the mechanism, only its measured price for SAE training.

Reproduction

Every number regenerates from scratch via the restartable drivers, on one laptop except the A100 numbers of Appendices § 11 and § 15, which run on Modal: run_m1.sh (main grid), run_m1_followup.sh (rotation codecs), run_m4.sh (scale check), run_arch.sh (architecture panel), run_m6.sh (sparsity×seed sweep), run_m2.sh (probes), run_controls_a.sh and run_controls_b.sh (controls, including the forward-precision seeds and the four sub-fp16 noise levels), run_noise_overlap.sh (the two noise levels inside the codec range), run_sparse_rung.sh (the millionth-of-values change), run_oneulp.sh (the one-scalar change), run_clump.sh (the one-shard packing), run_stdint4.sh and run_stdint4_seeds.sh (the rotation ablation), run_redraws.sh (the repetitions and the initialization-only floor), run_nbracket.sh (the count-bracket experiments), run_steering.sh (the causal steering test), run_budget_scaling.sh and run_int4_budget.sh (budget tiers), run_fwdprec.sh and run_fwdprec_b3x.sh (the bf16 forward at 1× and 3×), run_ksweep_codec.sh, run_pythia_frozen.sh and run_seeds.sh, plus scripts/figures.py, scripts/appendix_tables.py, scripts/budget_fixedset.py and scripts/paired_feature_tests.py; Appendix § 9 reruns from run_subspace.sh, scripts/feature_ablation.py and scripts/damaged_pair_semantics.py, with no training; Appendix § 14 from run_fresh_b3x.sh (with scripts/fresh_store.py), run_r3_resolution.sh, scripts/fresh_stable.py and scripts/fresh_summary.py; Appendix § 15 from scripts/modal_timing.py and scripts/timing_summary.py, which deploy and read the A100 runs on Modal; scripts/modal_cuda_ceiling.py reruns the CUDA replication on a cloud A100; Appendix § 17 reruns from run_geom_dynamics.sh (the training dynamics), scripts/geom_margins.py and scripts/geom_identifiability.py, summarized by scripts/geom_summary.py. We use GPT-2 small [20] and Pythia-1.4B [21], with text from OpenWebText [22] and the Pile [23]; the probe and steering tasks are SST-2 [24] and AG News [25]. Our SAEs are TopK [26] and JumpReLU [27], Hungarian matching follows [28], and the lossless entropy check uses zstd [29]. A SAE lens drop-in adapter (acc.saelens_adapter, zero added dependency) feeds SAELens’s trainer directly from a compressed store. Compute, summed from the recorded per-run wall-clock in the released artifacts: 218.2 GPU-hours of SAE training across 119 recorded runs on one Apple M5 (plus 7.0 hours for the training-dynamics runs of Appendix § 17), plus 0.47 hours of recorded A100 SAE training for Appendix § 11 and 3.0 hours of A100 time for Appendix § 15. Activation caching adds 2.8 hours that are recorded (both Pythia sites); the GPT-2 cache builds, including the fresh-text store, are not in any released manifest, and transcoding, evaluation and probes are not separately timed. The Pythia misalignment-check values are recorded in runs_m4.log rather than in the eval JSON set, and the frozen-SAE identity check was run at GPT-2 and Pythia layer 8 only. Peak disk was about 450 GB.

Use of AI assistance. We used an AI assistant (Claude, Anthropic) throughout this project for code implementation, for executing pre-registered experiment scripts, and for drafting and editing text, including internal adversarial reviews used during revision. No LLM is part of the method itself: the subject models (GPT-2 and Pythia-1.4B) are the objects of measurement, and no LLM-generated content enters any experimental result. Every number in the paper regenerates from the released artifacts via the included scripts, table cells are generated programmatically rather than transcribed, verify_paper_numbers.py checks every main-text number against those artifacts, and figures are generated by scripts/figures.py from the same JSONs. All citations were verified against the cited papers. We reviewed and take responsibility for all content.

References

  1. [1]Z. He et al. Llama Scope: Extracting Millions of Features from Llama-3.1-8B with Sparse Autoencoders. arXiv:2410.20526, 2024.
  2. [2]T. Lieberum et al. Gemma Scope: Open Sparse Autoencoders Everywhere All At Once on Gemma 2. arXiv:2408.05147, 2024.
  3. [3]J. Bloom, C. Tigges, A. Duong, and D. Chanin. SAELens (software), 2024. https://github.com/decoderesearch/SAELens. Default activation dtype float32: sae_lens/config.py, accessed 2026-08-09.
  4. [4]E. Duan. Perplexity Can Miss SAE Feature Damage Under Quantization. arXiv:2606.03002, 2026.
  5. [5]G. Paulo and N. Belrose. Sparse Autoencoders Trained on the Same Data Learn Different Features. arXiv:2501.16615, 2025.
  6. [6]G. Gerasimov, T. Rusalev, N. Balagansky, D. Laptev, V. Kurochkin, and D. Gavrilov. Unstable Features, Reproducible Subspaces: Understanding Seed Dependence in Sparse Autoencoders. arXiv:2606.12138, 2026.
  7. [7]D. Chanin and A. Garriga-Alonso. Sparse but Wrong: Incorrect L0 Leads to Incorrect Features in Sparse Autoencoders. arXiv:2508.16560, 2025.
  8. [8]K. Ayonrinde, M. T. Pearce, and L. Sharkey. Interpretability as Compression: Reconsidering SAE Explanations of Neural Activations with MDL-SAEs. arXiv:2410.11179, 2024.
  9. [9]C. Summers and M. J. Dinneen. Nondeterminism and Instability in Neural Network Optimization. In ICML, PMLR 139:9913–9922, 2021. arXiv:2103.04514.
  10. [10]S. Ashkboos et al. QuaRot: Outlier-Free 4-Bit Inference in Rotated LLMs. NeurIPS, 2024.
  11. [11]J. Chen et al. ActNN: Reducing Training Memory Footprint via 2-Bit Activation Compressed Training. ICML, 2021.
  12. [12]B. Li, T. Yu, K. J. L. Koa, and K.-W. Huang. The Proxy Presumption: From Semantic Embeddings to Valid Social Measures. arXiv:2605.07409, 2026.
  13. [13]G. König, M. Pawelczyk, U. von Luxburg, and S. Bordt. Validity Threats for Foundation Model Research. arXiv:2606.05029, 2026.arxiv.org/abs/2606.05029
  14. [14]Z. Xiao, S. Zhang, V. Lai, and Q. V. Liao. Evaluating Evaluation Metrics: A Framework for Analyzing NLG Evaluation Metrics using Measurement Theory. EMNLP, 2023.
  15. [15]T. Dettmers, M. Lewis, Y. Belkada, and L. Zettlemoyer. LLM.int8(): 8-bit Matrix Multiplication for Transformers at Scale. NeurIPS, 2022. arXiv:2208.07339.
  16. [16]G. Xiao, J. Lin, M. Seznec, H. Wu, J. Demouth, and S. Han. SmoothQuant: Accurate and Efficient Post-Training Quantization for Large Language Models. ICML, 2023. arXiv:2211.10438.
  17. [17]Z. Liu, J. Yuan, H. Jin, S. Zhong, Z. Xu, V. Braverman, B. Chen, and X. Hu. KIVI: A Tuning-Free Asymmetric 2bit Quantization for KV Cache. ICML, 2024. arXiv:2402.02750.
  18. [18]D. A. Spielman, H. Wang, and J. Wright. Exact Recovery of Sparsely-Used Dictionaries. In COLT, PMLR 23:37.1–37.18, 2012. arXiv:1206.5882.arxiv.org/abs/1206.5882
  19. [19]S. Arora, R. Ge, and A. Moitra. New Algorithms for Learning Incoherent and Overcomplete Dictionaries. In COLT, PMLR 35:779–806, 2014. arXiv:1308.6273.
  20. [20]A. Radford, J. Wu, R. Child, D. Luan, D. Amodei, and I. Sutskever. Language Models are Unsupervised Multitask Learners. OpenAI technical report, 2019.
  21. [21]S. Biderman et al. Pythia: A Suite for Analyzing Large Language Models Across Training and Scaling. ICML, 2023. arXiv:2304.01373.arxiv.org/abs/2304.01373
  22. [22]A. Gokaslan and V. Cohen. OpenWebText Corpus. http://Skylion007.github.io/OpenWebTextCorpus, 2019.
  23. [23]L. Gao et al. The Pile: An 800GB Dataset of Diverse Text for Language Modeling. arXiv:2101.00027, 2020.arxiv.org/abs/2101.00027
  24. [24]R. Socher, A. Perelygin, J. Wu, J. Chuang, C. D. Manning, A. Ng, and C. Potts. Recursive Deep Models for Sentiment Compositionality Over a Sentiment Treebank. EMNLP, 2013.DOI
  25. [25]X. Zhang, J. Zhao, and Y. LeCun. Character-level Convolutional Networks for Text Classification. NeurIPS, 2015. arXiv:1509.01626.arxiv.org/abs/1509.01626
  26. [26]L. Gao, T. Dupré la Tour, H. Tillman, G. Goh, R. Troll, A. Radford, I. Sutskever, J. Leike, and J. Wu. Scaling and Evaluating Sparse Autoencoders. arXiv:2406.04093, 2024.arxiv.org/abs/2406.04093
  27. [27]S. Rajamanoharan, T. Lieberum, N. Sonnerat, A. Conmy, V. Varma, J. Kramár, and N. Nanda. Jumping Ahead: Improving Reconstruction Fidelity with JumpReLU Sparse Autoencoders. arXiv:2407.14435, 2024.arxiv.org/abs/2407.14435
  28. [28]H. W. Kuhn. The Hungarian Method for the Assignment Problem. Naval Research Logistics Quarterly, 2:83–97, 1955.
  29. [29]Y. Collet and M. Kucherawy. Zstandard Compression and the ‘application/zstd’ Media Type. RFC 8878, 2021.

Paper details

vlnc:W_v9un7twg
v1 · latestHistory (1)

published · Fresh — added to the Hub within the last 30 days

added ·

Usage

views · 1

downloads · 0

A view counts once per browser per day, a download each time the PDF is requested.

CC BY 4.0

Original source

  • Computer Science

Not recorded for this paper · View statement →

Looking for related papers…
Contents