Per-example model capacity versus fidelity for PyTorch models.
How many bits of a trained network's capacity does each training example cost, and how well
is that example fit? capfid answers this for every training example, and for any set of
examples, by measuring how much a linearized Laplace posterior over the weights changes when
the example is unlearned.
Manuscript: How Many Bits Does a Training Example Cost? Per-Example Capacity versus
Fidelity via Linearized Laplace Unlearning. PDF (preprint;
arXiv link to follow). Every figure and table can be regenerated with
examples/paper/reproduce_paper.py. Appendix A is a
worked example that derives the three capacities from scratch in Bayesian linear regression,
where they reduce to the leverage of the ridge hat matrix and the PRESS residual.
For a network trained to a MAP estimate θ* of Σᵢ ℓ(yᵢ, f(xᵢ; θ)) + (α/2)‖θ‖², capfid builds
the linearized Laplace (generalized Gauss–Newton) posterior q_D = N(θ*, Σ) and asks what
happens when an example i is removed:
| key | quantity | meaning |
|---|---|---|
dof |
tr Kᵢ | effective number of parameters the example determines (sums to MacKay's γ) |
volume_bits |
h(θ|D∖i) − h(θ|D) | weight-space volume the example pins down; label-agnostic capacity |
information_bits |
KL(q_D ‖ q_D∖i) | information the weights actually store about (xᵢ, yᵢ) |
fidelity_bits |
log₂ p(yᵢ | xᵢ, θ*) | how well the trained model fits the example |
heldout_fidelity_bits |
log₂ p(yᵢ | xᵢ, θ*₋ᵢ) | fit after unlearning the example (one Newton step) |
memorization_bits |
fidelity − held-out | how much the fit depends on having seen the example |
All of these reduce exactly to C × C matrices at each example (C = number of outputs), so
every example is scored after one Cholesky factorization. Under exact Bayesian updating,
information_bits equals the example's generalization gap, and in linear regression
volume_bits is the classical leverage formula −½ log₂(1 − hᵢᵢ). See the manuscript for
derivations.
pip install git+https://github.com/maomlab/capfid.git
# with plotting and the example dependencies
pip install "capfid[examples] @ git+https://github.com/maomlab/capfid.git"
# development
git clone https://github.com/maomlab/capfid.git && cd capfid
pip install -e ".[dev]"
pytestRequires Python ≥ 3.9 and PyTorch ≥ 2.0. A CPU-only PyTorch build is sufficient.
import capfid
# model: trained on Σ_i loss_i + (alpha/2)||θ||² (see "Using it on your own model")
la = capfid.LinearizedLaplace(model, X, y, capfid.Categorical(), prior_precision=alpha)
res = la.pointwise() # dict of per-example numpy arrays
# pandas.DataFrame(res).sort_values("information_bits", ascending=False).head()
la.set_capacity(indices) # joint capacity of a subset, incl. its redundancy ratio
la.totals() # effective params, model information, MDL code length
from capfid.plotting import capacity_fidelity_plot
capacity_fidelity_plot(res, highlight=suspected_label_errors)examples/quickstart.py runs end to end in about 10 seconds on a CPU
(two moons with 10% flipped labels). On that example the flipped labels cost a median of
0.47 bits each versus 0.01 bits for clean ones.
- Match the objective.
prior_precisionis α in a summed loss plus (α/2)‖θ‖². If you trained on a mean loss with coupled weight decay λ (SGD or Adamweight_decay), usecapfid.prior_precision_from_weight_decay(λ, n_train), i.e. α = N·λ. AdamW's decoupled decay is only approximately equivalent. - Start near a stationary point. Unlearning uses one Newton step, which assumes the
gradient is about zero. A few L-BFGS iterations help:
capfid.train_map(model, X, y, lik, alpha, adam_steps=0, lbfgs_iters=100). - Examples must be processed independently. The model is put in eval mode; avoid batch norm in training mode.
- Size.
LinearizedLaplaceneeds about 8P² bytes and O(P³) time, which is practical up to roughly P = 2 × 10⁴ parameters. For wide networks with few training examples, useKernelLaplace: it computes the identical posterior in the N·C-dimensional output space through the empirical neural tangent kernel, in O((NC)³) time and about 24(NC)² bytes, independent of P (an MLP with millions of parameters on several thousand examples takes seconds to minutes). For networks whose parameters are all innn.Linearlayers, the kernel is assembled without forming Jacobians (capfid.ntk_gram). Otherwise, restrict the posterior to a subset, for example the last layer:LinearizedLaplace(..., param_filter=lambda name: name.startswith("head")). - Likelihoods.
Categorical()takes class indices;Bernoulli()takes 0/1 targets for sigmoid outputs;Gaussian(sigma)takes targets shaped like the outputs. For others, subclasscapfid.Likelihoodand implementlog_lik,grad_hessandhess. - Networks not trained to a MAP estimate (dropout, early stopping, or a prior precision
chosen after training). Pass
prior_mean=toKernelLaplaceto center the posterior on the MAP of the linearized network (a Gaussian process with the network's tangent kernel) instead of on θ*. Unlearning is then exact for a Gaussian likelihood, andtotals().log_evidence_natsis the Gaussian-process marginal likelihood. Because a trained network's kernel has been fit to the same labels, that marginal likelihood is optimistic; choosing α (and σ) by the predictive likelihood of held-out data (la.predict(X_valid, y_valid)) is safer. Withoutprior_mean, one-step unlearning from a non-stationary θ* can produce very large spurious memorization scores. - New inputs.
la.predict(X_new, y_new)returns posterior predictions and each input's prospective volume: the capacity it would occupy if added to the training data, which needs no label.
- Typical: low capacity, high fidelity, held-out ≈ fidelity.
- Memorized: high capacity, high fidelity, low held-out fidelity (for example, fitted label noise).
- Ignored: modest capacity, low fidelity (examples the model does not explain).
- Atypical but correct: elevated capacity, high fidelity (long-tail examples).
- The posterior is local to one basin and ignores the implicit regularization of stochastic training; capacities are defined relative to the prior precision α.
- One-step unlearning is accurate for typical examples but overestimates the effect of removing heavily memorized ones (by several-fold in the manuscript's transformer experiment). Use capacity as a ranking and held-out fidelity as qualitative there; joint information for large sets inherits this.
- For confident classifiers at weak priors, posterior-averaged fidelities
(
expected_fidelity_bits) are pessimistic. Plug-in fidelity and the capacity measures are unaffected.
examples/tdc_casestudy/ trains dropout MLPs on three
Therapeutics Data Commons tasks (AMES mutagenicity, AstraZeneca lipophilicity, AqSolDB
solubility) with ECFP4, APDP and CheMeleon features, then uses KernelLaplace to find the
compounds each model spends its capacity on, depicts them, and places them in UMAP embeddings
of each dataset. See its README for the findings and how to rerun it (about 3 hours on
10 CPU cores).
Its results are also browsable as a self-contained page,
examples/tdc_casestudy/dashboard.html (4 MB, no
server needed — download it and open it): colour the UMAPs by any per-compound quantity, click
a compound to see its structure, every model's scores and its nearest neighbours' labels, plot
any two quantities against each other, and inspect the groups of compounds a featurization
cannot tell apart.
pip install -e ".[examples]"
python examples/paper/reproduce_paper.py --out paper/figures # ~12 min, one CPU core
python examples/paper/reproduce_paper.py --quick --out /tmp/figures # ~1 min smoke testThe LaTeX source is in paper/.
pytest runs 31 tests, including exact checks against brute-force leave-one-out refits in
Bayesian linear regression (information, held-out likelihood, the Bayes identity, the leverage
formula, and the Newton step), structural checks on a nonlinear classifier (leverage
eigenvalues in [0, 1), Σ dof = γ, joint volume ≥ sum of single volumes, code-length
identities), agreement of KernelLaplace with LinearizedLaplace for every quantity, and
exact Gaussian-process leave-one-out and marginal likelihood for the refit posterior.
If you use capfid, please cite the manuscript (see also CITATION.cff):
@misc{capfid2026,
title = {How Many Bits Does a Training Example Cost? Per-Example Capacity versus
Fidelity via Linearized Laplace Unlearning},
author = {O'Meara, Matthew J.},
year = {2026},
note = {Preprint. Code: \url{https://github.com/maomlab/capfid}}
}MIT. See LICENSE.
