Gradian

How it works

The three lanes, per-example gradients in one backward pass, the influence math, and the design decisions that came out of measurements.

View .md

Three lanes run into one report.

  • Evaluate measures what changed, and refuses to attribute anything inside the noise band.
  • Attribute turns the surviving regression into a ranked, clustered verdict over your training data.
  • Audit checks the dataset, config and trainer logs mechanically, with no GPU and no attribution involved.

Per-example gradients in one backward pass#

Attribution needs a gradient per example, but a normal backward pass gives you the batch average.

Gradian builds the batch loss as a plain sum over examples, then captures each LoRA Linear layer's input and output gradient with hooks. One pass yields the exact per-example gradient. It is exact rather than approximate, it avoids vmap fragility over transformers forwards, and it produces the factored form EK-FAC would need later for free.

A batch-size-1 reference implementation and a vmap path both exist, and the test suite requires all three to agree exactly.

Engines behind one interface#

Engine What it is
datainf The default. Closed-form inverse-Hessian-vector product for LoRA
graddot Curvature-free gradient dot product, the baseline
tracin Curvature-free cross-check

Because the interface is uniform, running two engines and reporting their rank agreement is a config flag rather than a rewrite. Disagreement is reported as low confidence rather than silently averaged.

The math#

Per-example gradient, captured from hooked activations X and output gradients G:

g_j = einsum("bto,bti->boi", G, X)

Classical influence of training example j on query q, with H the Hessian at the optimum:

score_j = ⟨ ∇L(z_q),  H⁻¹ ∇L(z_j) ⟩

DataInf swaps the order of inversion and summation, then applies Sherman-Morrison, so no matrix is ever inverted. Per LoRA block l, with damping λ_l and n training examples:

r_l = (1/λ_l)·[ g_q − (1/n)·Σᵢ (gᵢ·g_q)/(λ_l + ‖gᵢ‖²)·gᵢ ]

score_j = Σ_l ⟨ r_l, g_j⁽ˡ⁾ ⟩

The contrastive query gradient, used whenever generations are available:

g_q = ∇ℓ(correct answer) − ∇ℓ(what the model actually said)

Optional Johnson-Lindenstrauss projection, which makes storage independent of model size at a measured cost in ranking fidelity:

g̃ = (1/√k)·R·g       R ∈ ℝ^{k×d},  R_ij ~ N(0, 1)

First-order prediction you can test by retraining:

predicted_loss_delta_if_removed = score_j / n

Sign convention, fixed once#

A positive score means the example reinforced the queried behavior. A negative score means it degraded it. This is documented in one place in the code and never re-derived.

The query gradient is contrastive by default#

This is the least obvious and most important design decision, and it came out of a measurement rather than a paper.

For a substitution failure, where the model now says the wrong thing, cross-entropy on the correct answer gets the sign backwards. The poisoned examples come out as the most helpful data in the corpus, because what a fine-tune mostly teaches is answer format, which genuinely lowers the loss on the right answer too. Subtracting the gradient of what the model actually said cancels that shared direction.

Query gradient Planted (poisoned) examples Median percentile
CE on the correct answer +5143 (looks helpful) 93.8
Contrastive: correct minus actual output -1717 (harmful) 5.5

Invariant to normalization, module coverage, engine and damping, across 16 configurations with the same wrong conclusion.

One consequence worth knowing: the contrastive query needs generated text to subtract. A loglik capability metric has none, so it degrades to plain cross-entropy and inherits the sign problem above. Use exact_match or contains for factual capabilities.

Two resolutions#

Whole-example influence answers "which examples hurt me". For long documents that is often too coarse.

A wrong sentence inside a 400-token entry is roughly 2 of 110 supervised tokens, so its harmful signal is diluted about 760 times relative to a short entry stating the same thing. Span-level drill-down keeps the tokens fixed and varies only the loss mask, scoring one gradient per sentence. It localizes the culprit sentence 92% of the time, provided the query is built from the single failing item rather than an average, which drops it to 33%.

A corollary worth internalizing: a wrong fact stated directly in a handful of examples does more damage than the same fact buried in three times as many long ones, and is far easier to find.

It says when the base model is the likelier culprit#

We never see the base model's training data, so its internals stay opaque. Gradian does run the base model, and that bounds the blame three ways:

  • Items the base model also fails. No capability was lost, so no training example explains them.
  • A low base score. There was little there to lose.
  • A thin base preference margin, meaning how strongly the base model favored the correct answer over what the tuned model now says. Below about 1 nat, the capability was held so weakly that almost any fine-tune would have tipped it.

These are confidence qualifiers on the data verdict, stated up front in the report summary. They are not a claim about the base model's training.

Everything is content addressed#

The index id hashes the checkpoint, dataset, gradient spec, seed, damping and library versions, so re-querying is free and a diagnosis is exactly reproducible. Every report prints the manifest it came from. If a number changed between two runs, a changed manifest field is the explanation.

Nothing is attributed inside the noise band#

A capability delta whose bootstrap confidence interval includes zero is reported and explicitly not diagnosed. This is by design, and it means plenty of honest runs end without a verdict.

Research this builds on#

Work Contribution used here
Koh and Liang, Understanding Black-box Predictions via Influence Functions (ICML 2017) The influence function formulation for training-data attribution
Kwon et al., DataInf (ICLR 2024) Closed-form inverse-Hessian-vector product for LoRA, the default engine
Pruthi et al., TracIn (NeurIPS 2020) Curvature-free gradient-dot baseline and cross-check
Grosse et al., Studying Large Language Model Generalization with Influence Functions (2023) EK-FAC approximation, scaling influence to LLMs, query design
Johnson and Lindenstrauss (1984) Random projection bound behind the optional gradient compression
Hu et al., LoRA (ICLR 2022) The adapter parameterization that makes per-example gradients tractable