Built-in activation transforms (tl.transforms)¶
Every name on this page is experimental and may change in any
release; tl.transforms is a provisional home (it may be renamed because it
collides with the input-side transform= slot).
TorchLens ships a curated library of declared transforms for the common
activation reductions — pooling, flatten, cast, normalize, sparse random
projection (SRP), PCA-apply — so they are one word instead of a lambda:
seeded, provenance-recorded, axis-aware, composable. A built-in is a frozen
TransformSpec with a validated plan and a pure apply; the canonical
JSON of a chain is its resume identity, and every numerics-visible change
mismatches. Raw callables keep working, recorded as identification-only.
import torchlens as tl
from torchlens import transforms as T
paths = tl.extract_dataset(
model, stimuli,
layers=["layer3", "layer4", "avgpool"],
path="run/",
output_transform={
"layer3": T.chain(T.flatten(), T.srp(1024, seed=0)),
"layer4": T.srp(4096, seed=0),
T.DEFAULT: T.chain(T.flatten(), T.unit_norm(), T.cast("bfloat16")),
},
)
Six things worth knowing before a harvest¶
Every number below names the corpus that produced it, and yours will differ
(per the transform contract). Two corpora appear: the design panel's 128 varied
COCO test2017 images and this repo's vendored fixture (128 rows
augmented from 32 CC0/public-domain photographs,
tests/transforms_corpus/), both measured on real resnet50 IMAGENET1K_V2
features with the shipped very_sparse_fixed construction (v1).
- SRP's guarantee is Euclidean, and it delivers. L2-RDM Spearman 0.989-0.9995 on resnet50 layer4/avgpool over the 128 COCO images at k >= 1024 (the lowest measured k), and 0.947-0.991 across the panel's corpus ladder at k=1024. On the vendored fixture: 0.977-0.978 at k=1024, 0.995 at k=4096 (layer4). No k=256 L2 number exists for the COCO corpus; the fixture measures 0.927.
- If your analysis is a correlation-distance RDM — most RSA is — expect real loss. COCO corpus, resnet50 layer4: corr-RDM Spearman 0.28 at k=256, 0.49 at k=1024, 0.71 at k=4096, 0.84 at k=16384. Vendored fixture: 0.43-0.48 at k=256, 0.69-0.73 at k=1024, 0.87 at k=4096.
- The reason is your RDM's dynamic range, not k alone. The measured
spread BETWEEN corpora at the same site and k is larger than the spread
across k — run
T.srp_fidelity_probe(pilot_batch, candidate_num_components=(...))on one batch of your own stimuli before committing a harvest. The probe's report carries its scope fields, so pasted numbers stay honest. - Pool before you project; normalize AFTER you project. Pool-first measured 0.73 vs 0.49 at matched k (COCO corpus); the center+normalize-FIRST recipe was refuted in all 8 measured cells (worse by 0.06-0.22) and is deliberately not shipped as a preset.
- SRP is for strong compression. Near k ~ D/2, plain coordinate subsampling wins (by 0.18, COCO corpus); at strong compression (k <= D/8) SRP earns its keep — +0.09-0.14 on the COCO corpus and +0.36-0.40 on the vendored fixture (the self-baselining O1d gate).
- Seeds share matrices under the default policy. With
share="by_extent", five runs with the SAME seed (default or explicit) share ONE projection; independence needs DISTINCT seeds under either policy. The realized seed and its source ("explicit"/"library_default") are always recorded, so the manifest reconstructs every projection.
A retraction note, kept on purpose: the panel's first fidelity table
(r = 0.914-0.996) was measured on 128 augmentations of ONE photograph and
collapsed to 0.28-0.84 on diverse images. It was deleted, not supplemented,
and the corpus-admissibility gate (O14: n_distinct >= 16, effective rank
= 40, corr-distance CoV <= 0.12, asserted on the fixture ITSELF) exists so a future degenerate fixture fails loudly instead of silently republishing a flattering table.
The roster¶
| Name | What it does | Contract highlights |
|---|---|---|
srp(num_components, seed=None, density="auto", construction=..., mode=..., share=...) |
Sparse random projection | num_components REQUIRED; seed + seed_source recorded; two named constructions; binds one extent per (chain, site) and REFUSES drift |
pool_tokens(op, assume_no_padding=False) |
Token mean/max/first/last | Three-valued mask policy; first/last gather VALID indices, never physical endpoints |
cls_token(assume_no_padding=False) |
The CLS gather | DISTINCT semantic name gated on recorded SpecialTokenFacts — gpt2 REFUSES a fictional CLS |
pool_spatial(op="mean"), channel_mean() |
Spatial/channel reductions | Resolve DECLARED roles or refuse teaching both remedies; rank is never evidence |
reduce(op, axis), take_index(axis, index), flatten(start_axis=1), unit_norm(axis=-1), magnitude(), cast(dtype) |
Role-free kernels | Explicit axes; the stimulus axis is inviolable; float-only cast; fp32 accumulation for half inputs |
project(basis, center=None) |
Frozen linear projection | Digest-addressed: records carry the digest, never values; unstaged digests refuse with restaging remedies |
pca_apply(fitted) |
Apply a fitted PCA | Consumes tl.stats.PCA(...).fitted(...) / tl.stats.load_fitted(path); centered; fit scope recorded. Fit-during-write is REFUSED (early and late shards would get different coordinates) |
srp_dims_for(n, eps) |
Sizing helper | The DENSE-JL L2 heuristic, honestly labeled: a poor guide for corr-RDMs, and it says so |
srp_fidelity_probe(features, candidate_num_components) |
Fidelity probe | Measures L2-RDM and corr-RDM fidelity per candidate k on YOUR pilot batch; report carries scope |
Chains compose left to right (T.chain(a, b) or [a, b] — one object, one
record); per-site Mapping dispatch (with T.DEFAULT) resolves before
coercion; bare strings name only zero-parameter deterministic presets.
Custom transforms register versioned via register_transform and cannot
shadow builtins; artifact loading never imports or executes code.
The SRP contract, briefly¶
The default construction very_sparse_fixed places EXACTLY
m = round(density * D) distinct nonzeros per output column (deterministic
counter-hash rejection; realized nnz equals k * m; signs keyed to
positions; values +/- 1/sqrt(density * k)). It is NOT the i.i.d.
Bernoulli matrix of the JL literature — the fixed per-column count has
variance 0 where the i.i.d. hypothesis requires 44-316 — so it records
distance_claim="empirical_only" at every density: the oracle suite is its
entire warrant, and it is geometrically indistinguishable from the i.i.d.
construction on real activations. iid_bernoulli IS the
i.i.d. matrix (the O1f distribution-identity gate proves it) and ALSO
records empirical_only until the literature task verifies Achlioptas 2003
against the paper itself; its O(D*k) generation cost is disclosed at plan
time. Generation is block-independent and bit-identical under any column
chunking, the canonical matrix is digested before any layout conversion
(srp_verify_matrix re-derives and checks), and the realized per-site
facts (srp_realized_facts: extent, effective seed, construction, realized
density/nnz, digest, multiply path, share key, distance claim) are what the
extraction manifest embeds.
GPU/MPS: no on-device number is published anywhere in this page — the
machine-gated suite (tests/test_transforms_lib_gpu_gate.py, the C-XFORM
cluster row) must run green first. On CPU, the dense / sparse_csr /
dense_chunked multiply paths all run one row-local fold (elementwise
multiplies and adds in canonical slot order), so they are bit-identical and a
projected row never depends on which rows share its batch; byte
reduction on the
measured chains was 92-184x — while taking ZERO off the retained forward
peak (that win belongs to emission-time streaming; every built-in declares
stream_safe and aliasing facts for it, and nobody may cite this library
for a peak-memory claim).
Credits¶
scikit-learn (SparseRandomProjection and the
johnson_lindenstrauss_min_dim practice of shipping a sizing helper, kept
with its scope relabeled); Achlioptas 2003 and Li/Hastie/Church 2006
(constructions implemented and selected deliberately — cited as lineage,
never as a guarantee until the literature task verifies them);
Dasgupta/Kumar/Sarlos 2010 and Kane/Nelson 2014 (the fixed-sparsity
sparse-JL literature the open task must check); DeepJuice (on-device
reduction precedent); Net2Brain (reduction-before-PCA placement);
thingsvision (pooling/flatten vocabulary); sentence-transformers and
Hugging Face (masked-mean reference semantics); timm/torchvision (pooling
precedent); baukit (running statistics); COCO (the diverse-stimulus
corpus, credited WITH the licensing caveat that keeps it un-vendorable —
the repo's precision path pins URL + sha256 and downloads on demand).