torchlens.neuro -- the treaty desk¶
torchlens owns everything that touches the network; rsatoolbox and Brain-Score
own everything that touches the brain or the hypothesis. torchlens.neuro
hands representations across that seam with provenance that cannot lie, and
teaches where everything else lives. Every name on this page is
experimental and may change in any release.
Install: pip install "torchlens[neuro]" (rsatoolbox and xarray, every supported
Python). The Brain-Score seam is a separate extra:
pip install "torchlens[brainscore]" (brainscore-vision, Python >= 3.11).
Keep the two in separate environments: brainscore-vision 2.3 pins
scikit-learn<1.6 while rsatoolbox 0.3 needs scikit-learn>=1.6, so
torchlens[neuro,brainscore] resolves but import rsatoolbox fails
(measured 2026-09-02; an upstream pin, not a torchlens choice). In that
environment neuro.rdms and neuro.datasets are not advertised, and touching
them raises MissingDependencyError with code neuro_dependency_conflict,
naming both installed versions, the pinning package and the fix (separate
environments); the diagnosis reads installed metadata and never imports
rsatoolbox. __all__/dir() advertise only the names whose dependency is
present and importable; accessing an unadvertised name teaches the exact
install command or the conflict.
The map (mirrors help(torchlens.neuro))¶
- Extract (needs no extra):
tl.trace(model, x, save=...),tl.extract_dataset(..., path=..., stimulus_ids=...),tl.load(path). - Geometry (needs no extra):
tl.repgeom.rdm/classical_mds/scree/effective_dimensionality/procrustes_align,tl.stats.cka. Nothing is re-exported into neuro. - Handoff (
neuroextra):neuro.datasets(...),neuro.rdms(...). - Score (
brainscoreextra):neuro.activations_extractor(...),neuro.get_activations_fn(...)-- live-gate verified (below). - Compare (needs no extra):
neuro.compare(source, target, analyses=("rsa", "ridge", "cka"))-- descriptive RSA, cross-validated ridge and linear CKA on one extraction (below). - Refusals: noise ceilings, crossnobis/mahalanobis, searchlight,
inferential bootstrap/permutation/model fitting (descriptive intervals on
TorchLens's own scores are
tl.repgeom.compare_rdms_interval/cka_interval), encoding-model inference, CCA/SVCCA/PWCCA, non-metric MDS, model zoos, brain data. Each attempted name raises a teaching refusal naming the owner, the missing ingredient, and the torchlens spelling that feeds it.
neuro.datasets¶
import torchlens as tl
log = tl.trace(model, images, save=tl.in_module("layer1"))
site_datasets = tl.neuro.datasets(log, stimulus_ids=ids) # per-site rsatoolbox Datasets
site_datasets.ledger # per-site computed/skipped report
Returns an insertion-ordered mapping of canonical site key to
rsatoolbox.data.Dataset. source is a Trace, a LoadedExtraction, or an
extraction-directory path. sites=None sweeps every STIMULUS-INDEXED saved
site through the core eligibility gate (buffer overwrites are skipped with
one summarized disclosure -- never fabricated into stimulus-space results);
an explicit lookup refuses ineligible sites actionably, and a bare
multi-pass layer lookup names the pass-qualified alternatives.
Descriptors are the product -- rsatoolbox promotes Dataset descriptors into
RDM descriptors automatically (measured at 0.1.5 and 0.3.2), so everything
written here survives into figures: site, requested lookup, structural
site_key, layer label, pool (ALWAYS present; "flatten" when nothing
was applied -- the readout is scientific provenance, never sugar), source
kind, per-stimulus shape, dtype and any handoff cast (float16/bfloat16
widen to float32, never float64), torchlens and rsatoolbox versions,
model identity, and an intervention marker on edited traces.
Observation rows always carry tl_presentation_index: rsatoolbox's
calc_rdm(descriptor=...) returns rows in SORTED descriptor order, and this
always-written original-row index is measured (both versions) to be permuted
along with the data, so presentation order is always recoverable. Stimulus
identity follows the authority rule: extraction-artifact ids are
authoritative (an explicit stimulus_ids= acts as validation, never silent
replacement); explicit ids on a bare Trace validate length; absent both, a
positional index is DISCLOSED as synthetic. obs= columns (conditions,
runs, repeats) are length-checked before rsatoolbox receives them. Channel
rows use a neutral feature_index (never "neuroid") plus factual unravelled
unit coordinates.
neuro.rdms¶
rdms = tl.neuro.rdms(log) # source mode: computed, correlation default
rdms = tl.neuro.rdms(log, metric="euclidean") # any tl.repgeom metric
external = tl.neuro.rdms({"V4": matrix}, dissimilarity_measure="crossnobis") # matrix mode
ONE name, two type-distinct modes, one shared condensed converter
(square[np.triu_indices(n, k=1)], measured to match rsatoolbox's own
convention at both versions -- no scipy dependency):
- Source mode (the taught path): computes through the canonical
tl.repgeomarithmetic and records the metric ACTUALLY used (measure_source="computed") -- when the function computes, the label cannot lie. Defaults tometric="correlation"(field canon for the RSA audience, and the one metric measured to match rsatoolbox exactly); this deliberately diverges fromtl.repgeom.rdm's euclidean default, and both docstrings cross-reference it. A pinned anti-divergence test holds site selection and exception classes identical to callingtl.repgeom.rdm_evolutiondirectly. - Matrix mode (the escape hatch): converts already-computed square
matrices only;
dissimilarity_measure=is REQUIRED (a bare matrix has forgotten its metric) andmeasure_source="declared". Validates square shape, symmetry, zero diagonal, finite values, shared pattern counts, and descriptor lengths BEFORE rsatoolbox sees anything. It never invents pattern identity: supplypattern_descriptors=, or rows carry a positional index disclosed as positional.
The modes reject mixed arguments. rdm_descriptors additionally carry
measure_convention -- the formula in words -- because rsatoolbox's
"euclidean" equals torchlens's euclidean SQUARED and divided by
num_features (a Pearson-visible discrepancy produced by nothing but a
convention), and rsatoolbox has NO cosine RDM method (ours is real and
tested; their calc_rdm(method="cosine") raises NotImplementedError, and
a pinned test fails loudly if that ever changes). Source mode also records
similarity_repository_id, the RDM variant's identifier in the
similarity-repository catalogue (for example rdm/*/correlation; absent for
a metric the catalogue does not list). A chunked= execution
seam is reserved for streaming-scale RDMs; launch is eager and any value
refuses typed.
neuro.rdms implements no comparison, fitting, ceiling, or inference --
that vocabulary belongs to rsatoolbox, and the refusal rows point there.
The Brain-Score adapters (live-gate verified)¶
neuro.activations_extractor(model, preprocessing, ...) builds a Brain-Score
activations model over TorchLens capture, shaped like Brain-Score's own
PytorchWrapper (its _extractor is a real brainscore_vision
ActivationsExtractorHelper), so it goes straight into a model plugin's
ModelCommitment(activations_model=...) or is called with stimuli and
layers directly; neuro.get_activations_fn(model, ...) is the underlying per-batch callable
(standalone-testable; benchmark layer lists may address functional ops, not
just modules). Exposed in neuro because the end-to-end live gate PASSED
(2026-08-29): a real resnet18 IMAGENET1K_V1 checkpoint through a real
ActivationsExtractorHelper and StimulusSet against a live
brainscore-vision 2.3.22 install on Python 3.12 -- values, presentation
order, layer coordinates, logits, dotted module paths, a functional-op
mapping, a short final batch, and CPU behavior matched direct torchlens
extraction, and default sites ran on a PARTIALLY saved trace. CUDA is not
claimed (no covered runner). End to end through a plugin (2026-10-14):
a Brain-Score model plugin registering ModelCommitment(activations_model=
neuro.activations_extractor(resnet18, ...)) was scored by
brainscore_vision.score on one neural benchmark
(MajajHong2015public.IT-pls) and one behavioral benchmark
(Geirhos2021colour-top1, which reads the "logits" layer) against
brainscore-vision 2.4.1 / brainscore-core 2.5.0 on Python 3.11, CPU; on 24
stimuli every committed layer and the logits matched Brain-Score's
hook-based PytorchWrapper exactly. Brainscore-vision 2.4.1 pins xarray
2022.3.0, whose backend discovery fails on Python 3.12, so score on Python
3.11. The offline per_layer recipe keeps its
bridge-only spelling: a generic score-a-callable loop must not borrow a
benchmark's authority.
neuro.compare: RSA, ridge and CKA on one extraction¶
import torchlens as tl
trace = tl.trace(model, images, save=...)
result = tl.neuro.compare(trace, brain, analyses=("rsa", "ridge", "cka"))
print(result.table()) # one row per site, one column per analysis
print(result.disagreement().table()) # where the analyses rank sites differently
result = tl.neuro.compare(trace, brain, analyses=("rsa", "ridge", "cka"), num_resamples=1000)
neuro.compare needs no extra (it is numpy-only). It reads each site's
activations once from what the source already holds -- a Trace with saved
activations, a LoadedExtraction or extraction directory, or a
{site: [num_stimuli, ...]} mapping -- and never re-runs the model. brain
is any [num_stimuli, ...] target for the same stimuli in the same order
(recordings, or another model's features). Every score is a descriptive
similarity on these stimuli:
"rsa":tl.repgeom.compare_rdms(rdm(site, rdm_metric), rdm(target, rdm_metric), rsa_method); defaultsrdm_metric="correlation",rsa_method="spearman"."ridge": a cross-validated ridge score. The stimuli are split intonum_foldsfolds (default 5) by a permutation seeded withseed. On each training fold, features and target are centred by the training means and one penalty for all target columns is chosen fromridge_alphas(absolute, on unscaled features; default 1e-2 to 1e8) by the exact leave-one-out squared error of the training fold. The held-out fold is predicted, and the score is the mean over target columns of the Pearson r between out-of-fold predictions and the target (constant columns are left out of the mean).result.ridge_alphas_chosen[site]lists each fold's penalty."cka": linear CKA with the biased Gretton HSIC, the varianttl.stats.ckacomputes.
With num_resamples=, each score gets a stimulus-bootstrap ScoreInterval
(the ridge interval resamples the evaluation of the out-of-fold predictions
and does not refit). result.similarity_repository_ids names the catalogued
variant of each analysis (ridge has none). Inference, voxel maps, variance
partitioning and stacked fits stay with rsatoolbox, himalaya, scikit-learn
and Net2Brain; this call makes no claim of matching any of them.
The refused analyses have recipe pages: TorchLens to netrep
(Procrustes, CCA and the shape-metric family) and
TorchLens to himalaya (banded ridge over a multi-site
feature bank).
Uncertainty on TorchLens's own scores¶
tl.repgeom.compare_rdms_interval(rdm_a, rdm_b, method, num_resamples=1000,
seed=0, confidence=0.95) and tl.repgeom.cka_interval(a, b, ...) return a
ScoreInterval: the unchanged point score (exactly compare_rdms; linear CKA
as tl.stats.cka computes it) with a percentile bootstrap interval over
stimuli beside it, plus the settings that made it (num_resamples, num_valid_resamples,
seed, confidence, basis). RDM resamples drop the entries that pair a
stimulus with a copy of itself; CKA resamples reindex the stimulus Gram
matrices, so each resample costs O(n^2) whatever the feature count. The same
seed gives the same interval, and identical inputs give a zero-width
interval at 1.0.
These intervals are descriptive: they say how much TorchLens's score moves
when the stimulus sample changes. They are not hypothesis tests, they do not
model subjects or measurement noise, and they make no claim of parity with
rsatoolbox's bootstrap. Inferential RSA (corrected bootstraps, model
comparison, noise ceilings) stays with rsatoolbox.inference; asking
tl.neuro for bootstrap still refuses and points here for the descriptive
interval. The stimulus bootstrap is known to be conservative for RDM
comparisons (it tends to cover more often than its nominal rate).
Raw beside ceiling-normalised scores¶
TorchLens computes no ceilings. Where the Brain-Score bridge reports a
ceiling-normalised score, the raw score sits beside it under its own name:
tl.bridge.brain_score.per_layer(...) returns, per layer, a ScoreParts
record (raw, ceiling_normalised, ceiling, error, note), and
tl.bridge.brain_score.score_parts(score) reads one Brain-Score Score the
same way. A result that carries a ceiling attribute is ceiling-normalised:
its value goes to ceiling_normalised and its "raw" attribute to raw. A
result without one (a behavioral engineering score, or a plain number from
an offline callable) is raw, and ceiling_normalised is None. Nothing is
recomputed; Brain-Score's normalisation is its own.
Where metrics disagree¶
tl.repgeom.metric_disagreement(scores) takes scores[metric][site] (floats
or ScoreIntervals from one extraction) and returns a MetricDisagreement:
per-metric ranks (1 is the highest score), the pairs of sites that two
metrics order oppositely, table() for a plain-text report and to_dict()
for data. When the scores carry intervals, each flagged pair says whether the
opposite orders are separated by the intervals or lie within their overlap.
report = tl.repgeom.metric_disagreement(
{"rsa": {"layer1": 0.31, "layer2": 0.52}, "cka": {"layer1": 0.74, "layer2": 0.69}}
)
print(report.table())
The CKA method contract (published)¶
| Function | Estimator | Precision | Inputs | Degenerate case |
|---|---|---|---|---|
tl.stats.cka(a, b) |
Linear CKA, BIASED estimator (Kornblith et al. 2019 gram-matrix form; no unbiased-HSIC correction) | CPU float64 |
matched [n_obs x num_features] pairs (rows are observations; mismatched row counts refuse; NaN/Inf input refuses cka_input_nonfinite, as tl.repgeom.rdm does) |
zero-variance input returns NaN with a cka_zero_variance warning |
Reference values are pinned in tests/test_neuro_pkg_geometry.py; an
estimator change fails the literals. RBF/debiased estimators are
approved-with-conditions and deliberately DECOUPLED from the neuro launch;
no device= flag ships without a designed dtype/memory contract, and no
svcca kernel value is reserved (SVCCA is a different statistic -- future
CCA-family functions land as siblings over the same matched pairs).
Version matrix¶
The provenance claim rests on a foreign package's behaviour, so the
convention/ordering/averaging suite (tests/test_neuro_pkg_matrix.py and
friends) is pinned at BOTH rsatoolbox versions: 0.1.5 (the declared floor)
and current 0.3.x. Verified rows include descriptor promotion, condensed
ordering, the euclidean convention RELATION, correlation equality, the
sorted-output recovery via tl_presentation_index, and mean-then-compute
condition averaging.
Credit¶
rsatoolbox (the Dataset/RDMs descriptor model, condensed convention,
comparison and inference vocabulary this page points at); Brain-Score (the
ActivationsExtractorHelper contract); thingsvision (the Gaussian RDM
bandwidth convention behind tl.repgeom.rdm(metric="gaussian") and the
rank-scaled RDM display behind tl.repgeom.rank_transform_rdm); Nili et
al. 2014 (the percentile-ranked RDM display convention); netrep and
himalaya (the shape-metric and encoding-model homes the refusals name).