Training-free per-sample routing over pools of frozen pre-trained encoders.
Project description
normrouter
Training-free per-sample routing over pools of frozen pre-trained encoders.
Given a pool of (backbone, layer) candidates, normrouter scores each test sample with training-free embedding-geometry statistics and routes it to one candidate or to a weighted combination of all of them. The router is not trained and uses neither test labels nor per-candidate calibration.
Two scores are provided: Levy, the radial typicality of the embedding norm, and LSNG, a local spectral score that is comparable across embedding dimensions.
Installation
pip install normrouter
Route with a pool of encoders
from normrouter import NormRouter
router = NormRouter.from_backbones(["dinov2_vitl14", "mae_vit_large"])
router.fit(train_images, train_labels)
predictions = router.predict(test_images)
fit embeds the training samples once per encoder, then fits one scorer and
one linear probe per (encoder, layer) candidate. predict scores every
candidate for each test sample and combines the probes' predictions
according to the routing mode: "hard" picks the argmax candidate,
"soft" (the default) averages the full pool with softmax weights, and
"topk-uniform" averages the top_k best-scored candidates uniformly.
Scores are quantile-calibrated against each candidate's training scores by
default, which keeps them comparable across encoders with different
embedding scales; pass calibrate="none" for the raw statistics.
A custom pool, including finetuned checkpoints, goes in through
NormRouter(pool, ...).
Score your own embeddings
The functional API works on plain numpy arrays. train_embeddings are the
embeddings a candidate produced for the training set, test_embeddings the
ones to score.
from normrouter import compute_levy, compute_lsng, softmax_route
levy = compute_levy(test_embeddings, train_embeddings)
lsng = compute_lsng(test_embeddings, train_embeddings, seed=0)
Scores from several candidates turn into routing weights:
import numpy as np
scores = np.stack([lsng_a, lsng_b, lsng_c]) # (n_candidates, n_samples)
weights = softmax_route(scores, temperature=1.0)
Hard routing is the argmax over candidates; soft ensembling is the weights-weighted average of the candidates' predicted class probabilities.
Compose a candidate pool
from normrouter import EncoderEntry, Pool
base = EncoderEntry(key="dinov2_vitl14", base_key="dinov2_vitl14", num_layers=24)
expert = EncoderEntry(
key="dinov2_vitl14_galaxy10",
base_key="dinov2_vitl14",
num_layers=24,
checkpoint_path="checkpoints/dinov2_vitl14_galaxy10/backbone.pt",
)
pool = Pool.of(base, layers="last:4") + Pool.of(expert, layers="all")
pool = pool.subset(encoders=["dinov2*"])
pool.candidate_ids()
Pools refuse duplicate candidates and conflicting encoder definitions, so a checkpoint can never silently shadow another one under the same key.
Research code
The ssl_model_audit package and the scripts/ directory contain the full
experiment pipeline behind the package: embedding extraction with on-disk
caching, linear and kNN probes, the scorer registry, routing strategies, and
the figure and table generators. The main entry point is:
python scripts/run_experiment.py --dataset galaxy10 --backbones dinov2_vitl14 mae_vit_large
Supported backbones include DINOv2 (S/B/L/G), MAE, CLIP, BEiT, I-JEPA,
MoCo v3, SimSiam, SimCLR, SwAV, BYOL, Barlow Twins, VICReg, and a supervised
ResNet-50. The transformer backbones declare their layer counts and work
directly with NormRouter.from_backbones; the ResNet-50 ones (SimCLR, SwAV,
BYOL, Barlow Twins, VICReg, supervised) need a Pool with an explicit
num_layers. Finetuned experts are registered from local checkpoints. Text
encoders (Pythia) are supported through the same pipeline.
Development
pip install -e ".[dev]"
pytest -m "not slow and not network"
Tests marked slow or network download model weights and are excluded from
the default CI run.
License
Attribution-NonCommercial 4.0 International (CC BY-NC 4.0), see LICENSE.md.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file normrouter-0.1.0.tar.gz.
File metadata
- Download URL: normrouter-0.1.0.tar.gz
- Upload date:
- Size: 138.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2ffacfed2afeade3ec72973331db3175ea17c6bb32d743ada38a60b0612beb62
|
|
| MD5 |
27af2d1b084477145357dc566a2c94e5
|
|
| BLAKE2b-256 |
68c39048cb606e891ea813b5cdae438acfe55a7b761e8546dd5f32734fccac4d
|
Provenance
The following attestation bundles were made for normrouter-0.1.0.tar.gz:
Publisher:
publish.yml on FabianGroeger96/ssl-model-audit
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
normrouter-0.1.0.tar.gz -
Subject digest:
2ffacfed2afeade3ec72973331db3175ea17c6bb32d743ada38a60b0612beb62 - Sigstore transparency entry: 2168002182
- Sigstore integration time:
-
Permalink:
FabianGroeger96/ssl-model-audit@afc475d4beec8184328165b014f2b3e95b5c4e01 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/FabianGroeger96
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@afc475d4beec8184328165b014f2b3e95b5c4e01 -
Trigger Event:
release
-
Statement type:
File details
Details for the file normrouter-0.1.0-py3-none-any.whl.
File metadata
- Download URL: normrouter-0.1.0-py3-none-any.whl
- Upload date:
- Size: 115.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4ad2e961b776838730e79b5a8a005616b62fa408e370a60073206bdb27f2b037
|
|
| MD5 |
e3e23ae7b4c2da11a41eaf37a3398285
|
|
| BLAKE2b-256 |
1a64b4317003fe3dca80a4af4c6994dc0193442d7d416670324f5099f4eaae86
|
Provenance
The following attestation bundles were made for normrouter-0.1.0-py3-none-any.whl:
Publisher:
publish.yml on FabianGroeger96/ssl-model-audit
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
normrouter-0.1.0-py3-none-any.whl -
Subject digest:
4ad2e961b776838730e79b5a8a005616b62fa408e370a60073206bdb27f2b037 - Sigstore transparency entry: 2168002204
- Sigstore integration time:
-
Permalink:
FabianGroeger96/ssl-model-audit@afc475d4beec8184328165b014f2b3e95b5c4e01 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/FabianGroeger96
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@afc475d4beec8184328165b014f2b3e95b5c4e01 -
Trigger Event:
release
-
Statement type: