Attribution Stack

Every layer of a transformer, a latent diffusion model, and a text diffusion model — formal and plain — annotated with what it lets you know about which training data paid for which part of the output. The short version: the counterfactual you want is well defined and unreachable; the ledger you can keep is reachable and partial; between them sits a resolution floor set by the training algorithm's own noise, and every honest attribution number has to be reported against it.

3Model families
traced end to end
3Inference regimes
plain / CoT / CoT+code
6Instruments
driven by real formulas
1Live experiment
LOO vs influence vs TracIn

00Two definitions of contribution

There are two different objects people mean by "this training example contributed to this output," and conflating them is the source of nearly every bad attribution claim.

The Platonic object

Fix a training set D = {z₁ … z_N}, a training algorithm A, and a query q. The contribution of unit z_i is the counterfactual difference in some measurement f of the output:

tau_LOO(z_i, q) = E_A[ f(q; A(D)) ] − E_A[ f(q; A(D \ z_i)) ] f measurement on the output (logprob of y, CLIP score, pass@1, …) E_A expectation over the training algorithm's own randomness (seed, data order, dropout, hardware nondeterminism)

This is a real number. It exists whether or not anyone computes it. It is defined for every unit, every query, every model. It is also the thing essentially nobody has ever computed at frontier scale, because evaluating it once requires training the model twice, and evaluating it for the whole corpus requires training the model N+1 times — with enough seeds per arm to see past E_A.

The Aristotelian object

The alternative is not an estimate of the counterfactual. It is a record of what actually happened: which bytes entered which optimizer step, under which learning rate, with which gradient, producing which parameter delta, evaluated under which sampler, conditioned on which context.

ledger : step t → { uid : unit ids in batch B_t }, eta_t, seed_t, clip_t, loss_t(z_i)

This is exact, it costs a couple of bytes per unit per epoch, and it answers a narrower question — could this unit have influenced these parameters, and how hard did it push — rather than would the output change without it. It never becomes the counterfactual. It is also the only part of the whole enterprise that is not an estimate.

Why the gap does not close

The gap is not an engineering backlog. Stochastic gradient descent on a non-convex loss is a chaotic map: two runs differing only in seed diverge, and for most queries the seed-to-seed spread of f is larger than the effect of deleting any single training document. Removing one unit from ten trillion tokens moves the output less than re-rolling the dice does. There is no cleverness that recovers a signal beneath its own measurement noise; you can only average over more runs (cost ~ 1/sqrt(R)) or aggregate units into groups until their combined effect clears the floor.

That is the practical result of the whole piece, and §09 makes it quantitative: individual-unit attribution over a pretraining corpus is, for most units, not a measurable quantity. Group attribution is. Fine-tuning and adapter attribution is. Runtime-context attribution is exact. Tool-execution attribution is exact and deductive. The engineering job is to know which regime you are in and to stop reporting numbers from the wrong one.

01What "contribution" can mean, formally

Three families, in decreasing cost and decreasing fidelity. Each answers a different question; none is a strict approximation of the others.

1 · Counterfactual (retraining) family

Leave-one-out tau_LOO(z_i) = f(q; A(D)) − f(q; A(D \ z_i)) Shapley — the unique value satisfying efficiency, symmetry, null-player, linearity phi_i = SUM over S subset of D\{z_i} [ |S|!(N−|S|−1)! / N! ] · ( v(S ∪ {z_i}) − v(S) ) Datamodels — fit a linear surrogate to the retraining map sample S_1..S_K ~ Uniform(D, alpha), train each, then solve min_w SUM_j ( f(q; A(S_j)) − [ b + SUM_{i in S_j} w_i ] )^2 empirically the linear surrogate is startlingly predictive; cost is K full trainings

Plain: retrain the model without the example and see what changes. Shapley does it for every possible subset so the credit is fairly split among examples that overlap. Datamodels retrains on thousands of random subsets and fits a straight line through the results — which works far better than it has any right to.

2 · Implicit-function (influence) family

Upweight z_i by epsilon and re-solve: theta*(eps) = argmin_theta (1/N) SUM_j L(z_j, theta) + eps · L(z_i, theta) Implicit function theorem at the optimum: d theta*/d eps |_0 = − H^{-1} · grad_theta L(z_i, theta*) H = (1/N) SUM_j grad^2_theta L(z_j, theta*) + lambda·I (damping) Influence on a query measurement: I(z_i, q) = − grad_theta f(q, theta*)^T · H^{-1} · grad_theta L(z_i, theta*) tau_LOO(z_i) ≈ −(1/N) · I(z_i, q)

Plain: instead of retraining, ask the calculus what would have happened. Take the gradient of the training example, bend it through the curvature of the loss surface, and dot it with the gradient of the thing you are trying to explain. Cheap in principle; the curvature matrix has 10^22 entries in practice.

What makes it usable: EK-FAC factors each weight matrix's curvature as a Kronecker product of two small matrices with an eigenvalue correction, turning the inverse into two small inverses per layer. Damping lambda is not optional — the Hessian of a non-converged deep network is not positive definite, and the derivation above assumes an exact optimum that never occurred.

3 · Trajectory family

TracIn — first-order accounting of the actual optimizer run TracIn(z_i, q) = SUM over checkpoints c eta_c · grad L(q, theta_c)^T · grad L(z_i, theta_c) exact identity when z_i appears in exactly one SGD minibatch at step c: the loss change on q caused by that step is −eta·<g_q, g_i> + O(eta^2) Adam correction — the raw identity is wrong under a preconditioner <g_q , g_i / (sqrt(v_hat) + eps)> · c_t c_t = gradient-clip factor at step t TRAK — linearise, randomly project, ensemble over M independently trained models phi(z) = P^T grad_theta f(z, theta*), P in R^{p x k}, P_ij ~ N(0, 1/k) tau_TRAK(q) = (1/M) SUM_m phi_m(q)^T (Phi_m^T Phi_m)^{-1} Phi_m^T Q_m

Plain: influence functions imagine a counterfactual world. TracIn does bookkeeping on the world that happened: every time a training step used this example, did that step help or hurt the thing I am now asking about? It needs no Hessian and no assumption that training converged — but it needs the training log, which is exactly the thing nobody keeps.

The Adam correction and the clip factor are the two lines above that are almost always dropped. Both are free to log at training time and impossible to recover afterwards.

Instrument · what a budget actually buys

Counterfactual cost calculatorarithmetic, not simulation
MethodTraining runsGPU-hoursCostWall clockVerdict
affordable inside the budget of one pretraining run 1–1000× a pretraining run beyond the energy budget of the observable universe

Reading it: exact Shapley is not expensive, it is non-arithmetic — 2^N for N = 10^9 has more digits than there are atoms in the universe. The row that matters is the last one: gradient methods cost zero retrainings, which is why they are the only ones that exist at frontier scale, and also why their outputs must be validated against the retraining rows on a small proxy (§08) before anyone believes them.

02Transformer stack — decoder-only foundation model

Twelve layers of the pipeline, from "what counts as one piece of data" to "how the optimizer turns it into a parameter change." Each carries an attribution hook: what this layer lets you know, whether that knowledge is exact or estimated, and what it costs to keep.

exact recorded, not inferred — a fact about the run estimated a model of the run; needs validation (§08) none no attribution signal at this layer
T0Corpus & unit definitionexact
Formal
D = {z_1 … z_N}, z = (bytes, meta) uid = BLAKE3(canonical(bytes))[0:16] Near-duplicate class by MinHash / LSH: J(a,b) = |H(a) ∩ H(b)| / |H(a) ∪ H(b)| ≥ 0.8 ⇒ same class Attribution atom := the class, never the document.

Plain: decide what "one piece of training data" is before you train. If you do not, near-duplicates split the credit between themselves and every number downstream is understated by however many copies exist.

Hyperparameters. unit length 512–4096 tok; MinHash 128 perms, banded 9×14, Jaccard threshold 0.8; token budget D; per-source mixture weights w_s.
Attribution hook

Content hash, dedup class, license, source, timestamp. ~48 B/unit. The mixture weights w_s are the single largest attribution lever anyone actually pulls — upsampling a source by 4× multiplies every unit's influence in it by roughly 4×, and that is a decision made in a config file, not discovered by an algorithm.

T1Tokenizerexact
Formal
BPE merge list M = (m_1 … m_V) learned by greedy frequency maximisation over a sample S ⊂ D. x = enc(bytes) ∈ {1..V}^L, with byte-level fallback.

Plain: the vocabulary is itself a trained artifact. Everyone forgets this. The merge rules encode which languages, which code styles, which number formats were common in the tokenizer's sample — and that sample is usually a different, undocumented slice of the corpus.

Hyperparameters. V = 32k–256k; byte fallback on; tokenizer sample ~10^9 bytes; digit splitting; whitespace policy.
Attribution hook

Log the tokenizer sample manifest hash and the merge-rank table. This layer is non-differentiable: attribution here is provenance only, never gradient-based. A model that tokenizes a language badly is disadvantaging that language's data before a single gradient step, and no influence function will ever surface it.

T2Embeddingexact
Formal
h^(0) = E[x] , E ∈ R^{V x d} (optionally · sqrt(d)) grad_E L is nonzero only on rows for tokens present in the batch.

Plain: each token id looks up a vector. This is the only layer where a training example touches parameters sparsely — it can only have moved the rows for tokens it actually contained.

Hyperparameters. d = 2048–16384; tied vs untied unembedding; init N(0, 0.02) or 1/sqrt(d).
Attribution hook

The rare case of exact causal support with zero approximation: one roaring bitmap per vocabulary row gives the precise set of units that could have moved it. Cheap, complete, and a hard upper bound — if a unit does not contain token v, it did not move row v. Useful for ruling attribution out, which is most of what auditing needs.

T3Positional encoding — RoPEnone
Formal
rotate pairs (q_{2j}, q_{2j+1}) by angle m · theta_j theta_j = b^(−2j/d_h) <R_m q , R_n k> depends only on (m − n).

Plain: position is baked in by rotating the query and key vectors; the dot product then only sees relative distance. No parameters, so nothing to attribute.

Hyperparameters. base b = 10^4 → 10^6 for long context; NTK/YaRN scaling s; context length 8k–1M.
Second-order hook

Nothing to attribute directly, but context length decides sequence packing — which units get concatenated into one training sequence. Packed units share a gradient and are attributionally confounded. Log the packing map (seq_id → [uid…]); without it, co-packed units are indistinguishable to every gradient method.

T4RMSNorm (pre-norm)estimated
Formal
N(h) = h / sqrt( (1/d)·SUM_i h_i^2 + eps ) ⊙ g

Plain: rescale the activations before each sublayer so the gradients stay in a sane range no matter how deep the stack gets.

Hyperparameters. eps = 1e−5 / 1e−6; gain g init 1.0; norm placement pre-sublayer; optional QK-norm.
Attribution hook

Normalisation makes the per-token gradient scale-invariant, which means raw gradient-dot attribution systematically over-weights long units — a 4096-token document accumulates 8× the gradient norm of a 512-token one for no semantic reason. Divide by token count or by ‖grad‖ before ranking. Skipping this is the most common silent bug in attribution pipelines.

T5Attention — grouped-queryestimated
Formal
A = softmax( Q K^T / sqrt(d_h) + M_causal ) · V GQA: H_q query heads share H_kv key/value heads, g = H_q / H_kv EK-FAC on a weight W with input a, output-gradient s: H_W ≈ E[a a^T] ⊗ E[s s^T] ⇒ H_W^{-1} ≈ A^{-1} ⊗ S^{-1} two small inverses per layer instead of one 10^22-entry inverse

Plain: every token looks back over the sequence and mixes in whatever it finds relevant. Grouped-query attention shares the key/value memory across several query heads to cut memory bandwidth.

Hyperparameters. H_q 32–128, H_kv 8, d_h 128; sliding window w; attention sinks; softcap.
Attribution hook

The Kronecker factorisation above is what makes influence functions tractable at all at scale. Empirically, attention blocks carry most of the influence norm for factual recall while MLP blocks dominate for stylistic and formatting influence — so a single scalar per unit averages two different phenomena. Report influence per block family, not summed.

T6Feed-forward — SwiGLU, or MoEexact routing
Formal
SwiGLU(h) = ( SiLU(h W_1) ⊙ (h W_3) ) W_2 MoE: y = SUM over e in top-k( r(h) ) g_e(h) · E_e(h) aux = alpha · N_e · SUM_e f_e · P_e (load balance)

Plain: a per-token nonlinear transform — the dense knowledge store. A mixture-of-experts version routes each token to a handful of specialists instead of running the whole block.

Hyperparameters. d_ff ≈ (8/3)·d rounded to 256; MoE experts 8–256, top-k 1–8, capacity factor 1.25, aux weight alpha 0.01.
Attribution hook

Mixture-of-experts makes attribution sharper, not harder. The router log (step, token, expert) is an exact record, and an expert's gradient support is restricted to the units that routed into it — shrinking the candidate set by roughly E/k. A 128-expert top-2 model cuts the search space 64×. This is the largest architectural gift to tractable attribution currently available and essentially nobody exploits it. Cost: 1 B/token.

T7Residual stream & depthestimated
Formal
h^(l+1) = h^(l) + Attn( N(h^(l)) ) + MLP( N(·) ) init scaled by 1/sqrt(2L) to keep the stream variance bounded.

Plain: a running sum that every layer reads from and writes to. Layers add corrections rather than replacing the representation.

Hyperparameters. L = 32–126 layers; depth-scaled init; residual dtype fp32.
Attribution hook

Do not confuse two things called attribution. Activation patching and path patching give mechanistic attribution — which circuit computed this. Influence functions give data attribution — which examples shaped those weights. They compose usefully: localising the circuit first narrows which parameter blocks need gradient sketches, cutting influence cost 10–100×. They do not substitute for each other, and a paper reporting one while claiming the other is making a category error.

T8Unembedding & lossexact
Formal
p = softmax( U · N(h^(L)) ) L_CE = − SUM_t log p( x_t | x_{<t} ) z-loss: + zeta · (log Z)^2

Plain: project back to vocabulary size, predict the next token, and score yourself on how surprised you were.

Hyperparameters. z-loss zeta 1e−4; logit softcap 30; label smoothing usually 0.
Attribution hook

The per-unit loss ledger: record L(z_i) every time z_i is seen. 4 B/unit/epoch. Gives loss trajectories per unit, which supports trajectory-simulation attribution and — more immediately — exact memorisation detection: a unit whose loss collapses toward zero after a single exposure has been memorised, and its attribution is not statistical but literal. This is the cheapest high-value thing on this page and almost no one keeps it.

T9Optimizer — AdamWestimated
Formal
m ← beta1·m + (1−beta1)·g v ← beta2·v + (1−beta2)·g^2 theta ← theta − eta · ( m_hat / (sqrt(v_hat) + eps) + wd · theta ) Corrected TracIn under this update: delta L(q) ≈ − eta_t · c_t · < g_q , g_i / (sqrt(v_hat_t) + eps) > c_t = min(1, kappa / ‖g_t‖) gradient-clip factor

Plain: the rule that turns a gradient into an actual parameter change. Adam divides by a running estimate of gradient magnitude, so raw "gradient dot product" attribution is measuring the wrong quantity.

Hyperparameters. beta1 0.9, beta2 0.95, eps 1e−8, weight decay 0.1, clip 1.0; warmup 2k steps; cosine decay to 10% of peak; peak eta ~3e−4 scaled by width.
Attribution hook

This is where trajectory attribution actually lives, and where two corrections are almost always dropped. The preconditioner sqrt(v_hat) reweights every coordinate; the clip factor c_t rescales whole steps. Both are free to log (one scalar per step for clip, the optimizer state you already have for v_hat) and impossible to reconstruct afterwards. Log them or your TracIn numbers are a different quantity than you think.

T10Data order & curriculumexact
Formal
batch B_t ~ pi(D, t) step t sees a multiset of units. Forgetting kernel — influence of a unit seen at step t, measured at T: rho(t) = PROD_{s>t} (1 − eta_s · lambda_eff) ≈ exp( − lambda_eff · INTEGRAL_t^T eta_s ds ) phenomenological; lambda_eff fitted per model, not derived

Plain: when a unit was seen matters as much as whether. Data seen late has more influence because there is less remaining training to wash it out. Anneal-phase data, at low learning rate right at the end, is disproportionately loud.

Hyperparameters. batch 4–16M tokens; epochs 1–4; per-source upsampling; anneal phase last 1–5% of tokens at high quality.
Attribution hook

The step ledger — the single most valuable artifact on this page. { t → set of uids } as a roaring bitmap costs 1–2 bits per unit per epoch. For a 2.5 billion-unit corpus at 2 epochs that is well under a terabyte, and it is the only non-approximate causal record in the entire pipeline. Every gradient method above is an inference; this is a fact. Keep it, and the anneal manifest separately — anneal units individually clear the detection floor while pretraining units do not (§09).

T11Scaling law & budgetestimated
Formal
L(N_p, D) = E + A / N_p^alpha + B / D^beta compute-optimal under C = 6·N_p·D ⇒ D ≈ 20·N_p Chinchilla fit: alpha ≈ 0.34, beta ≈ 0.28, E ≈ 1.69

Plain: the law that tells you how much data to pair with how many parameters. It also silently sets how many training examples are competing for credit in any single output.

Hyperparameters. parameters N_p; tokens D; compute C = 6 N_p D; overtraining ratio D/N_p = 20–2000.
Attribution hook

The consequence nobody states: attribution resolution degrades mechanically with scale. Mean per-unit influence falls like 1/N while the seed-noise floor stays roughly fixed, so each generation of larger models pushes a larger fraction of its own corpus below the threshold of measurability. Attribution does not get harder because the models get more complicated. It gets harder because the denominator grows. §09 makes this exact.

03Latent diffusion stack — text-to-image and image-to-image

A diffusion model is not one model. It is a text encoder, an autoencoder, and a denoiser, each trained on its own corpus, composed by a sampler at inference. Attribution that reports a single ranked list of training images is already conflating at least three provenance chains.

D0Pair corpus & recaptioningexact
Formal
D = { (x_j , c_j) } increasingly: c_j = VLM_phi( x_j ) machine recaption ⇒ a second-order provenance edge: (image_uid, captioner_ckpt_hash)

Plain: image plus caption. Modern pipelines throw away the alt-text and have a vision-language model write new captions — which means the captioner's training data is now upstream of your image, through the text.

Hyperparameters. 100M–5B pairs; aesthetic-score cutoff; resolution buckets; dedup threshold; caption dropout p=0.1.
Attribution hook

Two chains, not one. If captions are synthetic, the caption's provenance is the captioner checkpoint, and tracing further requires that model's ledger. This edge is almost never recorded, and it is the point at which most image-model provenance quietly becomes untraceable.

D1Text encoder (frozen)estimated
Formal
tau_psi(c) ∈ R^{L_c x d_c} CLIP contrastive, or T5 encoder psi frozen during denoiser training ⇒ grad_psi = 0

Plain: turns the prompt into vectors the image model attends to. It was trained separately and is usually not updated afterwards.

Hyperparameters. CLIP-L 768 / OpenCLIP-G 1280 / T5-XXL 4096; max tokens 77–512; frozen.
Attribution hook

Frozen means its training data contributes to every output but receives no gradient during denoiser training — so no influence method run on the diffusion corpus will ever see it. Attributing through it requires a separate influence pass on the encoder's own corpus, with the prompt embedding as the query. This is a real hole in every published image-attribution result.

D2VAE — latent autoencoderestimated
Formal
z = E(x) · s , x_hat = D(z / s) L = ‖x − x_hat‖_1 + L_perceptual + lambda_adv·L_GAN + beta·D_KL

Plain: compress the image 8× so the diffusion model works in a smaller space. The decoder is what actually paints the final pixels — the diffusion model never sees a pixel.

Hyperparameters. downsample f = 8; latent channels 4–16; scale s ≈ 0.18215 (SD) / 0.3611 (SDXL); KL weight beta 1e−6.
Attribution hook

Texture-level attribution lands here, not in the denoiser. Anything below roughly 8 px — grain, brush edge, skin micro-detail, JPEG artifact style — is the decoder's training set speaking. A report that attributes fine detail to a denoiser training image is attributing at the wrong layer. Separate ledger, separate influence run, separate report.

D3Forward noising / scheduleexact
Formal
q(x_t | x_0) = N( sqrt(a_bar_t)·x_0 , (1 − a_bar_t)·I ) cosine: a_bar_t = cos^2( ((t/T)+s)/(1+s) · pi/2 ), s = 0.008 Noise-level variable (EDM): sigma_t = sqrt( (1 − a_bar_t) / a_bar_t ) Flow matching: x_t = (1−t)·x_0 + t·eps, target v = eps − x_0 Objective: L = E_{x0, eps, t} ‖ eps − eps_theta(x_t, t, c) ‖^2

Plain: add noise on a schedule until the image is static, and train the model to predict the noise that was added. The schedule decides how much of the image survives at each point.

Hyperparameters. T = 1000 (discrete) or continuous t; cosine / linear / v-prediction; flow-matching timestep sampling logit-normal(0, 1); resolution shift.
Attribution hook

Timestep is an attribution coordinate. A training image only ever influenced parameters through the (t, eps) draws it happened to receive. Log the per-unit histogram of t. High-t gradients carry layout and semantics; low-t gradients carry texture. A single scalar per training image averages two unrelated influences — report per band (t > 0.7T, 0.3–0.7, < 0.3T).

D4Denoiser — DiT / MMDiT / UNetestimated
Formal
eps_theta( x_t , t , c ) DiT block with adaLN-Zero conditioning: h ← h + alpha(t,c) · Block( gamma(t,c)·N(h) + beta(t,c) ) alpha initialised to 0 ⇒ identity at init MMDiT: joint text+image token streams with shared attention.

Plain: a transformer that looks at a noisy latent, is told the noise level and the prompt, and predicts the noise. Everything else in the stack exists to feed it or to interpret its output.

Hyperparameters. 0.6–12B params; patch size 2; 24–38 blocks; width 1536–4096; heads 24; QK-norm on; EMA decay 0.9999.
Attribution hook

Same EK-FAC / TracIn machinery as the language model, with one difference that breaks naive ports: the loss is a regression, not cross-entropy, so per-example gradients are far less peaked and vary strongly with the sampled (t, eps). Averaging over fewer than ~16 draws per unit yields variance, not signal. Budget for it or do not report.

D5Sampler — the path integralestimated
Formal
DDIM (eta = 0), in the noise-level variable x~ = x / sqrt(a_bar): x~_{t−1} = x~_t + ( sigma_{t−1} − sigma_t ) · eps_theta(x_t, t, c) i.e. DDIM is exactly Euler on dx~/d sigma = eps_theta. Therefore, to first order: d x_0 / d eps_theta(x_t) = ( sigma_{t−1} − sigma_t ) = delta_sigma_t and SUM_t delta_sigma_t = sigma_max − sigma_min (telescopes) Attribution to the final image is a path integral, not a point: tau( z_i , x_0 ) = SUM_t delta_sigma_t · tau_t( z_i , eps_theta(x_t,t,c) )

Plain: the sensitivity of the finished image to the model call at step t is exactly that step's stride in noise level. Steps where the noise level drops a long way matter a lot; steps where it barely moves matter almost not at all. Which means the sampler's step spacing — a quality-tuning choice — is also the attribution weighting, and nobody chose it for that reason.

Hyperparameters. steps 4–50; DPM-Solver++ 2M / Euler / Heun; spacing Karras rho = 7, uniform-t, or uniform-sigma; sigma_max 80, sigma_min 0.002.
Attribution hook

Attributing once, to the final image, is the most common methodological error in image attribution. Per-step attribution (Journey-TRAK and successors) computes tau_t at each denoiser call and weights by delta_sigma_t. Caveat on the derivation above: it is first-order with the path held fixed — the exact Jacobian also propagates through every later call, PROD_s (I + delta_sigma_s · d eps/d x), which is why the honest version measures the propagation empirically rather than assuming it is the identity.

D6Classifier-free guidanceestimated
Formal
eps~ = eps_theta(x_t, null) + w · ( eps_theta(x_t, c) − eps_theta(x_t, null) ) First-order norm accounting, with k = ‖delta‖ / ‖eps_null‖ : prompt-conditional share = w·k / (1 + w·k)

Plain: run the model twice — once knowing the prompt, once blind — and exaggerate the difference. Higher guidance means more prompt obedience and less of whatever the model would have drawn on its own.

Hyperparameters. w = 1–8 for images, 2.5–5 typical; training-time caption dropout p = 0.1; guidance rescale 0.7; interval guidance.
Attribution hook

Guidance is an attribution amplifier. Verbatim replication of training images rises measurably with w, and attribution mass concentrates onto fewer units as w rises. Consequence for reporting: attribution is only valid at the guidance scale it was measured at. Numbers computed at w = 1 and applied to outputs generated at w = 7 understate concentration, sometimes badly.

D7Image-to-image / SDEdit / inpaintingexact
Formal
start at t_0 : x_{t0} = sqrt(a_bar_t0)·x_init + sqrt(1 − a_bar_t0)·eps strength s = t_0 / T (diffusers: skip the first (1−s)·n steps) Two exact coefficients, answering different questions: amplitude retained by x_init : sqrt(a_bar_t0) = 1 / sqrt(1 + sigma_0^2) share of the model's path skipped : 1 − (sigma_0 − sigma_min)/(sigma_max − sigma_min) Inpainting: x_t ← m ⊙ x_t^known + (1 − m) ⊙ x_t

Plain: instead of starting from pure noise, start from your image with some noise stirred in. Low strength keeps your image; high strength throws it away. The share your image keeps is not a vibe — it has a closed form.

Hyperparameters. strength 0.2–0.9; mask blur 4–32 px; ControlNet conditioning scale 0.4–1.0; IP-Adapter scale.
Attribution hook

The initialising image is a runtime data unit with an analytic weight — the only place in this entire document where an attribution coefficient is available in closed form rather than estimated. Log (init_uid, t_0, a_bar_t0, sigma_0). For most commercial image-to-image work at strength ≤ 0.5, the user's own input carries more of the output than any training image does, and that is provable, not asserted.

D8Adapters — LoRA, DreamBooth, ControlNetexact
Formal
W' = W + (alpha / r) · B · A , B ∈ R^{d x r}, A ∈ R^{r x k}, r << d trained on D_adapter with |D_adapter| = 5 … 100 images.

Plain: a small patch bolted onto a big model, trained on a handful of images to teach it one face, one style, one object.

Hyperparameters. rank r 4–128; alpha r or 2r; lr 1e−4; 500–2000 steps; batch 1–4; text-encoder training on/off.
Attribution hook

The best attribution case in the entire field, and the one people bother with least. The adapter's dataset is small, known, and the update is low-rank — you can compute exact leave-one-out for a 20-image LoRA in minutes on one GPU. Report adapter attribution exactly and base-model attribution statistically, and never average the two into one number: they are measurements of different kinds.

D9Step distillation — LCM / ADD / Turboinherited
Formal
consistency: f_theta( x_t , t ) ≈ f_theta_minus( x_t' , t' ) adversarial: + discriminator on 1–4 step outputs student sees D_teacher-outputs , never D.

Plain: train a fast student to copy a slow teacher in one to four steps instead of fifty. The student never sees the original training data at all.

Hyperparameters. steps 1–8; EMA 0.95–0.999; skipping interval; discriminator lr; guidance distilled in at fixed w.
Attribution hook

This is the attribution break. The student's only provenance is a teacher checkpoint hash. Claiming tau_student(z) = tau_teacher(z) requires assuming the student matches the teacher's function — which is false precisely where it matters, since few-step samplers have visibly different mode coverage. Log the teacher hash, state the inheritance assumption in the report, and never present inherited attribution as measured. The same warning applies to every synthetic-data pipeline in §05.

Instrument · where the credit sits along the denoising path

Sampler sensitivity — delta_sigma per stepexact first-order derivation from D5
per-step sensitivity, delta_sigma_t / (sigma_max − sigma_min) noise level sigma_t, log scale steps skipped by image-to-image start x-axis: sampler step index, first call at left
—First step's share of total sensitivity
—Steps holding 80% of sensitivity
—Output attributable to init image (path)
—Amplitude retained, sqrt(a_bar_t0)
—Prompt-conditional share at w

Reading it: at 20 Karras steps the first call carries 25.4% of the image's entire first-order sensitivity, three calls carry half of it, and six carry 80%. Attribution computed uniformly across steps is mis-weighted by roughly an order of magnitude between the ends. Switch spacing to uniform in t and the first call alone takes 84% — the quality-optimal spacing rule and the attribution weighting are the same object, and only one of them was ever chosen deliberately. Drag strength down: at 0.5 the amplitude split between the user's image and the model is about even, and below that the user's input dominates on both measures. The two init-image readouts disagree on purpose — path share and amplitude share are different questions, and a report should say which one it means.

04Text diffusion stack — masked / discrete denoising

Text diffusion is not image diffusion with tokens. The corruption process is discrete, the objective is a re-weighted masked-language-model loss, and — the part that matters here — the output has no causal ordering, which breaks the attribution machinery built for autoregressive models.

X0Discrete corruption processexact
Formal
q( x_t | x_{t−1} ) = Cat( x_t ; x_{t−1} · Q_t ) absorbing-state kernel: Q_t = (1 − beta_t)·I + beta_t · 1 · e_MASK^T marginal: q(x_t | x_0) ⇒ each position independently masked w.p. (1 − a_bar_t) linear schedule: a_bar_t = 1 − t , t ∈ [0,1]

Plain: instead of adding Gaussian noise you replace tokens with [MASK] on a schedule, until the whole sequence is blanks. The model learns to fill them back in.

Hyperparameters. kernel absorbing (dominant) or uniform; T = 1000 discrete or continuous t; clipping t ∈ [1e−3, 1−1e−3].
Attribution hook

Corruption is per position: each time a training unit is seen, a different random subset of its own tokens is masked, so the unit contributes through a different slice of itself every epoch. Log the mask seed per (unit, step) — 8 B — or the unit's per-position contribution is permanently unreconstructible.

X1Objective — weighted NELBOestimated
Formal
NELBO = E_{t ~ U(0,1)} (1/t) · E_{x_t} SUM_{i : x_t^i = MASK} − log p_theta( x_0^i | x_t ) a masked-LM loss with a 1/t weight that makes it a valid likelihood bound (MDLM / LLaDA form). Low t = lightly masked = enormous weight.

Plain: it is the old masked-language-model objective with one specific reweighting that turns it into a proper bound on likelihood. That reweighting is where the attribution problem starts.

Hyperparameters. t sampling U(0,1) or antithetic/low-discrepancy; clip eps 1e−3; loss on masked positions only.
Attribution hook

The 1/t weight means a unit's influence is dominated by whichever low-t draws it happened to receive — a handful of high-variance samples out of thousands. Estimating influence without stratifying by t gives you the variance of the timestep sampler, not the influence of the data. Stratify, or report nothing.

X2Backbone — bidirectional transformerestimated
Formal
no causal mask; p_theta( x_0^i | x_t ) for all masked i in parallel. Attribution is over a (position x step) lattice, not a chain: tau( z_i , y ) = SUM_{s=1..S} SUM_{j ∈ U_s} tau( z_i , y_j | x_{t_s} ) U_s = positions unmasked at reverse step s

Plain: same transformer, no causal mask — it sees the whole sequence at once. Every position is refined jointly, so there is no "this token came before that one" to hang credit on.

Hyperparameters. 1–8B params; context 4k; same GQA / SwiGLU / RMSNorm stack as §02; often initialised from an autoregressive checkpoint.
Attribution hook

Attribution loses the chain. In an autoregressive model, token y_k has exactly one gradient path, through a defined prefix. Here every position is jointly refined over S steps in an order chosen at runtime, so credit lives on a two-dimensional lattice. Every CoT-attribution technique in §06 that relies on "the reasoning preceded the answer" needs rebuilding for this architecture.

X3Reverse sampling & unmasking orderexact
Formal
at reverse step s, unmask a subset by confidence: U_s = top-k over i of max_v p_theta( x_0^i = v | x_{t_s} ) then optionally re-mask a fraction and repeat (ancestral / remdm).

Plain: fill in the blanks a few at a time, most-confident first, then optionally blank some again and take another pass. The order is decided at generation time, not fixed.

Hyperparameters. steps S 32–1024; block length 32–512; remask ratio; temperature; top-p; confidence rule (max-prob / entropy / margin).
Attribution hook

The unmasking order is a latent variable. Two runs on the same prompt take different orders and produce different attribution over the same output text. Log the order; report attribution as conditional on it. This is exact and costs S · log(L) bits.

The structural consequence

Chain-of-thought faithfulness tests — early answering, inserting a mistake mid-trace, truncating — all assume the trace is causally upstream of the answer. A diffusion language model can settle its answer tokens at reverse step 3 and its justification tokens at step 40, inside one denoising path. The stated reasoning can be literally posterior to the answer it claims to produce. The autoregressive faithfulness battery does not transfer, and no replacement has been established. Anyone deploying a text diffusion model with a visible reasoning trace should say so plainly rather than reuse the AR framing.

05Lifecycle — pretraining through post-training, and the human in the loop

Attribution mass does not sit still across the pipeline. It is diluted by scale, concentrated by annealing, re-weighted by preference optimisation, and laundered by distillation. Stage by stage:

StageUnitsPer-unit influenceAttribution statusWhat to log
Pretraining10^9 – 10^10tiny, heavy-tailedgroup-onlystep ledger, loss ledger, MoE routing
Mid-train / anneal10^6 – 10^8disproportionatenear-thresholdseparate manifest; retention rho(t)
SFT10^4 – 10^6largetractableper-example gradients, EK-FAC
Reward model10^4 – 10^6 pairslargetractableannotator id, rubric version, agreement
DPO / IPO10^4 – 10^6 pairslargetractablepair id → gradient; it is a supervised loss
PPO / GRPO10^5 – 10^7 rolloutsdiffuse, self-generatedtwo-hoprollout → reward → advantage chain
Distillationteacher outputs—inheritedteacher ckpt hash + the assumption
Quantise / merge——degradedmerge weights; re-estimate after
Runtime context1 – 10^2often dominantexactablation deltas per source
Tool execution1 – 10^2often dominantdeductiveenvironment hash; rerun to verify
exact / tractable measurable at the stated cost, today group / inherited / degraded requires aggregation or a stated assumption Units = attribution atoms, post-dedup. Per-unit influence is relative within a stage, not across stages.

The reward model and the annotator as a data unit

Bradley-Terry preference model: P( y_w > y_l | x ) = sigmoid( r(x, y_w) − r(x, y_l) ) L_RM = − log sigmoid( r_phi(x, y_w) − r_phi(x, y_l) ) PPO objective with a trust region on the reference policy: max_theta E[ r_phi(x, y) ] − beta · KL( pi_theta ‖ pi_ref ) DPO — the same optimum, as a closed-form supervised loss: L = − log sigmoid( beta·log[pi_theta(y_w|x)/pi_ref(y_w|x)] − beta·log[pi_theta(y_l|x)/pi_ref(y_l|x)] ) GRPO / verifiable rewards: A_hat_i = ( R_i − mean(R) ) / std(R), R ∈ {0,1}

Why DPO is the auditable one. DPO's loss is a plain supervised objective over identified preference pairs, so TracIn and EK-FAC apply directly: influence per preference pair is computable at the same cost as influence per SFT example. PPO is not: the data unit is a rollout, credit inside it is assigned by an advantage estimate, and the human label sits two hops upstream, behind a reward model. End-to-end, that is a product of two ill-conditioned inverse curvatures:

tau( label L , output y ) = SUM over rollouts rho [ grad_theta log pi_theta(y)^T · H_theta^{-1} · grad_theta J(rho) ] policy stage · [ grad_phi r_phi(rho)^T · H_phi^{-1} · grad_phi L_RM(L) ] reward stage

Nobody computes this at scale, and the reason is visible in the expression: two damped inverse Hessians multiplied together, each already the weakest link in its own stage. If auditability of human feedback is a requirement, that is an argument for direct preference optimisation on methodological grounds, not just simplicity ones — and it is an argument rarely made out loud.

Annotators are data units. Every preference label carries (annotator_id, rubric_version, timestamp, time_on_task, inter-annotator agreement). Once you have per-pair influence, you can sum it by annotator and by rubric clause. That turns an unfalsifiable worry — "whose values are in this model?" — into a measurement: if five annotators produced 40% of the influence mass on refusal behaviour, that is a number you can put in a report. Constitutional and RLAIF pipelines get the same treatment with the principle text as the unit, attributed per clause.

The KL coefficient is the dial. In the PPO objective above, beta literally governs how far post-training is permitted to move attribution mass off the pretraining distribution. As beta → infinity the policy collapses to the reference and all credit is pretraining's; as beta → 0 the reward model — and behind it, the annotators — take over. It is the one hyperparameter in the whole stack whose attribution meaning is exactly its optimisation meaning.

Annealing, and why late data is loud

The retention kernel from T10 has a practical consequence worth stating on its own. Under a cosine schedule decaying to 10% of peak, a unit seen in the final 2% of training retains close to all of its parameter delta, while a unit seen in the first 10% has had its contribution repeatedly overwritten. This is why the anneal set — often a few hundred billion tokens of curated data — carries influence per unit that is orders of magnitude above the pretraining average, and it is why anneal data is the one part of a pretraining corpus where individual-unit attribution actually clears the detection floor. Keep its manifest separate from the bulk corpus. It is the part you can answer questions about.

06Three inference regimes — plain, chain-of-thought, chain-of-thought plus code

The architecture is identical in all three. What changes is the shape of the attribution graph between the training corpus and the token you are trying to explain, and how much of the output is attributable to the corpus at all.

A · Non-chain-of-thought — one hop

y ~ p_theta( y | q ) tau( z_i , y ) = grad_theta log p_theta(y|q)^T · H^{-1} · grad_theta L(z_i, theta*)

Plain: the question goes in, the answer comes out, and everything the model knows arrived through its weights. One path, one influence computation, and every claim about the output is a claim about training data. This is the only regime where "which training data produced this?" is a well-posed question with a single answer — and it is also the regime where the answer is least measurable, because all of it has to squeeze through parameters shaped by 10^10 competing units.

B · Chain-of-thought — a chain rule over the trace

r ~ p_theta( · | q ) , y ~ p_theta( · | q , r ) log p(y|q) = log SUM_r p_theta(r|q)·p_theta(y|q,r) ≈ at the sampled trace r_hat log p_theta(y | q, r_hat) + log p_theta(r_hat | q) tau( z_i , y ) = [ g_y + SUM_k c_k · g_{r_k} ]^T · H^{-1} · grad_theta L(z_i) g_y = grad_theta log p_theta( y | q, r_hat ) g_{r_k} = grad_theta log p_theta( r_hat_k | q, r_hat_{<k} ) c_k — causal weight of reasoning step k, by resampling ablation: c_k = log p_theta(y | q, r_hat) − E_{r'_k ~ p_theta} log p_theta( y | q, r_hat_{<k}, r'_k, r_hat_{>k} ) cost: m forward passes per step; m = 8 is usually enough to rank steps

Plain: the model writes its working out, then answers. Credit now flows two ways: some training data shaped the answer directly, and some shaped a reasoning step that then constrained the answer. To weigh a step, resample just that step and see how much the answer's probability moves. Steps that can be swapped freely without changing the answer got no credit, no matter how much text they occupy.

The faithfulness caveat, stated plainly. c_k measures whether the reasoning token was load-bearing for the answer. It does not measure whether the stated reason is the model's actual reason. A trace can be fully load-bearing and still be a post-hoc rationalisation of a bias the prompt introduced — models will reliably follow a hint planted in the question, produce a coherent argument that never mentions the hint, and land on the hinted answer. Attributing through such a trace attributes through a confabulation with high confidence. Test for it before trusting c_k: plant a hint, permute answer order, insert an error mid-trace, and check whether the trace and the answer move together.

C · Chain-of-thought plus code interpreter — a severed chain

y ~ p_theta( · | q , r_{1:k} , o_{1:k} ) , o_j = Exec( c_j ; ENV ) o_j is NOT a function of theta, except through the model's choice of program c_j. tau( z_i , y ) = [ g_y + SUM_k c_k·g_{r_k} + SUM_j c_{o_j}·g_{c_j} ]^T H^{-1} grad L(z_i) + SUM_j c_{o_j} · delta_ENV ← credit owed to the environment Mass conservation over sources: 1 = m_theta + m_ctx + m_exec ENV pinned by hash: env_hash = H( interpreter_version ‖ lockfile ‖ seed ‖ input_file_digests ) rerun ⇒ bit-identical ⇒ the claim is verified, not estimated

Plain: the model writes code, something else runs it, and the numbers that come back were not produced by the model at all. They were produced by CPython, NumPy, and arithmetic. That portion of the answer has a provenance you can prove — pin the environment, rerun, get the same bytes — rather than estimate. The model still owns the decision to write that program, and the library source may well have been in pretraining, but the value 3.14159… is not attributable to any training document.

This is the interesting inversion. Adding a tool does not make attribution harder; it makes a slice of the output deductively attributable for the first time. The interpreter converts an inductive claim about a corpus into a deductive claim about a program. And the limit is exactly the one you would expect: you cannot decide statically what the program will contribute — you have to run it and record what happened. Irreducibility is not an obstacle to the method here. It is the method: the log exists because the shortcut does not.

The measurement protocol — additive credit on the log-probability scale

All three regimes share one cheap, exact decomposition. Every term below is a difference of negative log-likelihoods from a forward pass, so the whole thing costs three extra forwards plus one per ablated source, and it partitions credit additively:

L_unif = log |V| uninformed baseline, nats/token L_ref = NLL under pi_ref (pre-post-training checkpoint), query only L_theta = NLL under pi_theta, query only L_ctx = NLL under pi_theta, query + full context L_unif − L_ctx = (L_unif − L_ref) + (L_ref − L_theta) + (L_theta − L_ctx) pretraining post-training context context term subdivides by source ablation: delta_s = NLL( context \ s ) − NLL( context ) for s ∈ {retrieval, tool, trace}

Terms can go negative, and a negative term is information rather than an error — post-training that makes a correct answer less likely is a real and reportable finding. What this partitions is predictive credit, not moral responsibility: it says how much of the model's confidence in this output came from where, which is the question an audit can actually answer.

Attribution mass by regimedrive it with measured NLLs
—Total credit over baseline (nats/tok)
—Parameters — training data
—Runtime context — exact
—Tool execution — deductive
—Forward passes to measure

Reading it: the amber band is the only part that requires influence functions, seed-noise floors and validated estimators. The mint bands are measured with a handful of forward passes and, for the execution band, verified by rerunning a pinned environment. Move to regime C and watch the amber band shrink: tool use is an attribution technology, not just a capability one. It is also the only lever on this page that moves credit out of the unmeasurable regime and into the measurable one by design rather than by more compute.

07The ledger — what to actually write down

Everything above that is marked exact reduces to one engineering decision: keep a ledger during training, or lose the information permanently. None of it can be reconstructed afterwards. Here is the whole schema.

unit { uid, sha256, dedup_class, source, license, tokens, first_seen, mixture_weight } step { t, lr, seed, grad_norm, clip_factor c_t, units: bitmap[uid], loss: map[uid → f32] } route { t, token_index, expert_ids } MoE only — 1 B/token ckpt { step, param_hash, proj_seed, grad_sketch_uri } stage { name, parent_ckpt, data_manifest_hash, objective, kl_beta } label { pair_id, annotator_id, rubric_version, time_on_task, agreement_kappa } query { q, y, ctx_units, tool_calls, env_hash, sampler, cfg_w, unmask_order }

Instrument · what the ledger costs

Ledger storagebytes, at your scale
RecordRateSizeBuys you
—Attribution units
—Exact ledger, total
—Gradient sketches
—Sketch cost vs exact ledger

Reading it: the exact ledger — the part that is a fact about the run — is small enough to be uncontroversial at any realistic scale. The gradient sketches, which are what actually let you rank units against a query without a retraining pass, are the entire cost, and they scale with checkpoints and sketch width. The group granularity slider is the practical answer: sketch at the shard or source level rather than per unit, and the cost falls linearly while the resolution falls to exactly the level §09 says is measurable anyway. Sketching per unit buys precision you cannot see past the noise floor.

What a query against that ledger looks like

In[1]:= attribution_report(query_id="q-88fa1c", top=5, group="source")
Out[1]= regime : CoT + code interpreter env_hash : 9f2c…a10b [rerun verified — byte-identical] credit split : params 0.31 | context 0.22 | execution 0.47 seed-noise floor: sigma = 0.019 nats (R = 8 reference retrains) EXACT — runtime, per-source ablation tool:pandas.describe() 0.47 verified by rerun ctx:doc#3 (user upload) 0.22 ablation delta 1.44 nats ESTIMATED — parameters, EK-FAC + TracIn, group level shard:code/python-github 0.114 ± 0.021 [above floor] shard:web/stackexchange 0.061 ± 0.019 [above floor] shard:books/technical 0.028 ± 0.020 [AT FLOOR — do not cite] shard:web/commoncrawl-en 0.021 ± 0.022 [BELOW FLOOR — not reported] INHERITED — none (no distillation stage in this lineage)

Three kinds of number in one report, each labelled with which kind it is. That labelling is the whole discipline. A report that prints a single ranked list without saying which rows were measured, which were estimated, and which fell under the noise floor is not an attribution report — it is a ranking with the error bars deleted.

08Validation — how you know the estimator is not lying

Every gradient-based method in §01 is a model of a counterfactual, and models can be wrong in ways that are invisible from inside. Four checks, in increasing order of how much they should be trusted:

1. Linear datamodeling score (LDS) — the standard sample subsets S_1..S_m ~ Uniform(D, alpha), retrain each, then LDS = Spearman( SUM_{i ∈ S_j} tau_i , f(q; A(S_j)) ) over j report with a confidence interval. LDS ≈ 0.2–0.5 is typical and is not "wrong" — it is the honest ceiling once seed noise is in the denominator. 2. Counterfactual removal curve remove top-k by |tau|, retrain, plot metric(k); compare against random-removal. Area between the curves = attribution quality, in the units you care about. 3. Canary injection — gives calibration, not just ranking plant n unique synthetic facts at controlled frequencies f = 1, 10, 100, 1000 measure recall@k of the true source as a function of f. recall collapses as f rises: the more copies, the less any one is attributable. 4. Seed-noise floor — the one nobody runs, and the cheapest retrain R times changing ONLY the seed. sigma = sd_R[ f(q; A(D)) ] any |tau| < z · sigma · sqrt(2/R) is not a measurement. report sigma next to every attribution number, always.

Check 4 is the one that reorders the field. It costs R retrainings once per model, not per query, and it tells you the resolution of every subsequent number you will ever produce. Running it on a proxy model — a 100M-parameter stand-in on a 1% corpus — is enough to establish the shape of the floor and costs nothing.

Instrument · the whole argument, on a model small enough to check exactly

Below is a regularised logistic regression trained by full-batch gradient descent on synthetic data — small enough that exact leave-one-out is computable in the browser, by retraining the model once per training point. The two estimators from §01 are computed on the same run and correlated against that ground truth. Then gradient noise is added, and the correlation is measured again.

Exact LOO vs influence functions vs TracInruns in this page, ~— ms
influence function, −(1/N)·grad_q^T H^{-1} grad_i TracIn over 12 checkpoints identity line — perfect agreement with exact LOO x: exact LOO effect on held-out loss  ·  y: estimate, both rank-normalised
—Spearman rho — influence vs exact LOO
—Spearman rho — TracIn vs exact LOO
—Top-1 unit agreement
—Seed-noise floor, sigma (nats)
—Units above the floor

Reading it: with no gradient noise both estimators track exact leave-one-out closely — influence at rho = 0.999, TracIn at 0.92. The calculus works, on a convex problem at an actual optimum, which is the setting the derivation assumes. Now push gradient noise from 0 to 0.01. Influence stays at rho = 0.998 — it barely moves — while units above the floor collapse from 64 to 2. That is the whole argument in one drag: the estimator is not what fails first. Measurability fails first, and no better estimator recovers it. Now raise R: at that same noise, 1 → 4 → 8 seeds takes measurable units from 2 → 6 → 13, exactly the 1/sqrt(R) the floor formula predicts. Which is also why frontier-scale individual attribution is out of reach — the fix exists and nobody can afford it. One asymmetry worth noticing at the top of the noise range: TracIn degrades more slowly than influence, because averaging over checkpoints absorbs trajectory noise that a single-optimum Hessian estimate cannot.

09The floor — which claims are measurable at all

This is the section the rest of the page exists to set up. Attribution has a resolution limit, it is computable in advance, and it decides which of your numbers are measurements and which are decoration.

Setup. total effect of the corpus on the query metric Delta per-unit effects are heavy-tailed (empirically) tau_i = C · i^(−a) normalised so SUM_i tau_i = Delta , rank i = 1 … N, a ≈ 0.8–1.3 retention for a unit last seen at training fraction t rho(t) = exp( − kappa · [remaining LR mass from t to end] ) Detection threshold. With R seeds per arm and seed-noise sd sigma, the two-arm difference has sd sigma·sqrt(2/R), so at confidence z: tau_min = z · sigma · sqrt( 2 / R ) Two consequences. individually measurable ⇔ rho·tau_i ≥ tau_min ⇒ rank i ≤ k* a group of G random units clears the floor when G · rho · (Delta/N) ≥ tau_min ⇒ G_min = tau_min · N / (rho · Delta)
Attribution resolution limitrank vs detectable effect
per-unit effect after retention, rho·tau_i — falling mass of a random group of G units — rising detection floor, tau_min = z·sigma·sqrt(2/R), z = 2 per-unit effect before retention decay below the floor — not measurable both axes log; x doubles as unit rank and group size; y is effect on the query metric in nats
—Individually measurable units (rank ≤ k*)
—Measurable fraction of the corpus
—Min group size for a random group
—Detection floor, tau_min
—Retention factor rho at that point in training
—Seeds needed to halve the floor

Reading it: at defaults — a 2.5-billion-unit corpus, Zipf tail, 20 millinats of seed noise, four seeds per arm — the number of units whose individual effect clears the floor is in the single digits. Not thousands. That is not pessimism, it is the arithmetic, and it matches what influence methods actually surface at pretraining scale: near-duplicates and little else. The picture to read is the wedge: the falling line is what one unit is worth, the rising dashed line is what a random group of G units is worth, and the shaded band between them is everything the floor swallows. Attribution is available to the left of k* and to the right of G_min, and nowhere in between. At defaults G_min ≈ 10^7 — the granularity of a whole shard or publisher, and nothing finer. Drag Seeds per arm and watch the floor fall as 1/sqrt(R): one more decimal place of resolution costs 100× the compute. Drag last seen at across its full range and the curve lifts 20× — that is the anneal effect from §05, and it is why late-stage data is the part of a pretraining corpus you can actually answer questions about.

What follows from the floor

Three rules, and they are the operational content of this document.

  1. Report groups, not units, over pretraining data. Choose the grouping — source, shard, license class, language, publisher — so that each group's expected mass clears tau_min. Publishers and license classes are usually large enough. Individual documents are usually not, and a per-document ranked list over a pretraining corpus is a claim about a quantity smaller than the measurement error.
  2. Report sigma next to every number. A ranked attribution list without a noise floor is uninterpretable, and running the floor experiment once per model is cheap. If a system cannot say what its floor is, its rankings are not evidence.
  3. Move credit into the measurable regime where you can. Retrieval, tool execution, image-to-image inputs, adapters, and preference pairs are all exactly or nearly exactly attributable. Architecture choices — mixture-of-experts routing logs, direct preference optimisation instead of PPO, keeping the anneal set separate — shift mass out of the unmeasurable band by design. That is a cheaper path to auditability than any amount of retrospective influence computation.

10Verdict — what is knowable, at what cost

The full answer to "how do you keep track of the contribution of any and all training data units to the final output," in one table. Four epistemic statuses, because collapsing them into one is how attribution claims go wrong.

SignalStatusMethodCostWhere
Which units entered which stepexactstep ledger, roaring bitmap1–2 bit/unit/epochT10
Per-unit loss trajectory / memorisationexactloss ledger4 B/unit/epochT8
Which units could touch an embedding rowexacttoken-presence bitmapnegligibleT2
Which units could touch an expertexactMoE router log1 B/tokenT6
Optimizer preconditioner & clip factorexactlog at train time or lose it8 B/stepT9
Init-image share in image-to-imageexactclosed form in sigma_0freeD7
Runtime context contributionexactper-source NLL ablationk forward passes§06
Tool-execution contributiondeductivepinned env hash + rerun1 rerun§06
LoRA / adapter unit influenceexactbrute-force leave-one-outminutes, 1 GPUD8
SFT / DPO pair influenceestimatedEK-FAC or TracIn, LDS-validated1–10 GPU-h/query§05
Pretraining group influenceestimatedgroup ablation retrain, or TRAK1 retrain/group§09
Pretraining unit influencebelow floor—unaffordable§09
PPO end-to-end label influencetwo-hopcomposed inverse Hessiansnot done at scale§05
Distilled / few-step studentinheritedteacher ledger + stated assumption—D9
Quantised, pruned, or merged weightsdegradedre-estimate post hocfull re-run§05
Frozen text encoder's corpusunaddressedseparate influence pass, rarely run—D1
Tokenizer sample influenceprovenance onlymanifest hashfreeT1
exact / deductive a recorded or re-derivable fact about the run estimated / inherited / degraded a model of the run; needs a stated floor and a validation number unaddressed a real contribution with no method currently applied to it

The close

The counterfactual is a well-defined mathematical object. For any training unit and any output, the number exists — the difference between a world where that document was in the corpus and one where it was not. It has all the properties you would want: it is unique, additive over disjoint interventions, and indifferent to how the model is implemented.

Computing it requires running the training algorithm, and the training algorithm has no shortcut. You cannot evaluate the outcome of ten trillion tokens of stochastic gradient descent without performing ten trillion tokens of stochastic gradient descent, and the outcome differs from run to run by more than the quantity you are trying to measure. The object is real and the access is blocked, and the blockage is not temporary.

So the practical discipline is not better estimation. It is keeping the receipts as the computation goes — the step ledger, the loss ledger, the routing log, the clip factors, the environment hash — and then being precise about which claims those receipts support. Some are exact. Some are estimates with a stated floor. Some are inherited assumptions wearing a number's clothing, and those are the ones to refuse to print.

The tool-use case in §06 is the shape of the general fix, and it is worth ending on. When the model writes a program and something else runs it, that slice of the output becomes provable rather than estimable — but only because someone recorded what the program did. Irreducibility does not stop you from knowing. It only stops you from knowing in advance, which is a different constraint, and one you engineer around by writing things down while they happen.