Model Zoo¶
Choose a tile, slide, or patient encoder below. List installed presets without loading weights:
from slide2vec import list_models
list_models() # all presets
list_models("tile") # tile-level only
list_models("slide") # slide-level only
list_models("patient") # patient-level only
Tile-level encoders¶
Spacing values are supported scales, in microns per pixel. If a preset
supports several scales and has no registered default, pass
PreprocessingConfig(tiling=hs2p.TilingConfig(...)) explicitly for slide
extraction.
Registry input_size is the default final model input size. Slide
extraction reads tiles at requested_tile_size_px (default: input_size),
applies only the encoder’s photometric preprocessing (dtype, scaling,
normalization), and encodes exactly that size. No encoder-side resize or
center crop follows the read. Lunit, mSTAR, GigaPath, GPFM and DINOv2 default
to 224px; DINOv3 to 256px. Slide and patient presets inherit the default of
their tile encoder.
This is slide2vec’s declared extraction policy, not a reproduction of each
model’s published sampling protocol. Earlier releases read Lunit and mSTAR at
248px and GigaPath at 256px, then center-cropped to 224px, leaving unencoded margins
between non-overlapping tiles. A 224px grid changes the tile count and
coverage; embeddings from the two policies are not equivalent. resume
refuses to reuse tile, hierarchical or slide embeddings whose metadata records
a different requested_tile_size_px (see
compatibility).
An off-default size requires allow_non_recommended_settings=True, an
encoder that supports variable input, and a multiple of the patch size. The
permission only allows the size; preprocessing stays geometry-preserving. For
DINOv2 at its native checkpoint resolution:
model = Model.from_preset("dinov2-vitb14", allow_non_recommended_settings=True)
model.embed_slides(
slides,
preprocessing=PreprocessingConfig(
tiling=TilingConfig(
requested_spacing_um=0.5,
requested_tile_size_px=518,
tolerance=0.05,
overlap=0.0,
),
),
)
This forwards exactly 518×518 pixels. Without the flag, an off-default request
raises; 525px raises even with the flag (not a multiple of 14). With
dinov3-vitb16, requested_tile_size_px=224 forwards 224×224 pixels
through its 16px patch grid.
Pre-cropped images (embed_images, embed_tiles) keep each encoder’s
shipped get_transform recipe: Lunit Resize 248 → CenterCrop 224,
GigaPath and DINOv2 Resize 256 → CenterCrop 224, DINOv3
Resize 256 → CenterCrop 256, GPFM and mSTAR direct 224 resize. Dense extraction is
unchanged.
Preset |
Model |
Output dim |
Spacing (µm/px) |
|---|---|---|---|
|
384 |
|
|
|
384 |
|
|
|
512 |
|
|
|
768 |
any ( |
|
|
768 |
any ( |
|
|
768 |
|
|
|
768 |
|
|
|
768 |
|
|
|
768 / 1536 |
|
|
|
1024 |
|
|
|
1024 |
|
|
|
1024 |
|
|
|
1024 |
|
|
|
1024 |
|
|
|
1024 |
|
|
|
1024 |
|
|
|
1024 / 2048 |
|
|
|
1280 / 2560 |
|
|
|
1280 / 2560 |
|
|
|
1536 |
|
|
|
1536 |
|
|
|
1536 |
|
|
|
1536 |
|
|
|
1536 / 3072 |
|
|
|
1536 / 3072 |
|
|
|
768 / 1536 |
|
|
|
384 / 768 |
|
|
|
1536 |
|
|
|
3072 |
|
|
|
4608 |
|
Slash-separated dimensions denote output variants. For example, Virchow2
uses cls_patch_mean (2560 dimensions) by default; select its 1280-dimensional
CLS vector with:
from slide2vec import Model
model = Model.from_preset("virchow2", output_variant="cls")
Natural-image baselines¶
dinov2-vitb14 and dinov3-vitb16 are natural-image DINO ViTs with no
intrinsic micron spacing: any requested spacing is accepted and 0.5 is the
tiling default. dinov3-vitb16 (patch 16, four register tokens, RoPE)
defaults to 256px tiles. Its patch_mean output (default) is the mean of the
spatial patch tokens after the final norm, excluding the CLS and register
tokens, which is the pooled output the timm checkpoint ships; cls returns
the CLS token. Both are 768-dimensional. Dense grids are H/16 × W/16
(14 × 14 at 224px, 16 × 16 at 256px) and attention maps apply the
backbone’s rotary embeddings. dinov3-vitb16 requires timm>=1.0.20,
which the base install provides. Weights are public under the DINOv3 license; see the
model card.
Slide-level encoders¶
Patient-level encoders¶
Patient-level encoders aggregate multiple slide embeddings for the same patient
into a single patient-level embedding. They require a patient_id column in
the input manifest (or patient_id keys in each slide dict
when using the Python API).
Preset |
Model |
Tile encoder |
Spacing (µm/px) |
Output dim |
|---|---|---|---|---|
|
|
|
768 |
Installation extras¶
The base install (pip install slide2vec) covers most presets. Extras add
the packages a few models need. They follow one rule:
Extras carry lower bounds only. An exact or upper pin is kept only when the model’s code breaks outside it.
A model whose stack conflicts with the shared one gets its own extra, and the conflict is declared in pyproject.toml.
slide2vec[fm] combines every model extra. The presets that need more
than the base install are:
Preset |
Extra |
|---|---|
|
|
|
|
|
|
|
base, or |
|
|
|
|
|
base plus an upstream package (below) |
Every other preset needs only the base install.
prism2 imports FlashAttention, so it needs the flash-attn extra as
well. Its text decoder calls a Phi-3 helper that transformers 4.53 removed;
slide2vec supplies that helper on newer transformers, so prism2 shares the
fm environment.
gigapath-slide needs an attention backend: the flash-attn extra on
GPUs with compute capability above 7, and the xformers
extra on older GPUs. PRISM uses xformers optionally, when
PERCEIVER_MEM_EFF_ATTN=true. The xformers extra is unpinned because
each xformers build pins its own torch.
The musk, conch, and gigapath-slide presets also require upstream
packages that are not on PyPI. The tile-only gigapath preset uses timm and
does not need the GigaPath package. Install the relevant package below:
pip install git+https://github.com/lilab-stanford/MUSK.git
pip install git+https://github.com/Mahmoodlab/CONCH.git
pip install git+https://github.com/prov-gigapath/prov-gigapath.git
From a checkout, uv sync --group models-git installs the same packages at
the commits recorded in uv.lock. uv sync --extra cu128 --extra fm
--group models-git reproduces the default Docker image.
Gated models require access approval on their linked Hugging Face page, plus
hf auth login or an HF_TOKEN environment variable. The base install
also includes hs2p’s sam2 dependencies for AtlasPatch tissue segmentation;
see Preprocessing to enable it.
Dense grids and attention maps¶
Tile encoders can also return a spatial patch-token grid (B, d, h, w)
instead of the pooled (B, D) vector, and most can return per-head
CLS-attention grids (B, K, h, w). See API Guide for the dense and
attention APIs.
Support varies by preset. Check it without loading weights:
from slide2vec.encoders import resolve_encoder_capabilities
capabilities = resolve_encoder_capabilities("uni2")
print(capabilities.level) # "tile"
print(capabilities.pooled) # True
print(capabilities.dense) # True
print(capabilities.dense_prenorm) # True (timm ViT presets only)
print(capabilities.attention) # True
print(capabilities.patch_size) # (14, 14)
Resolution reads the registry only: it does not instantiate the encoder,
download files, or access the network. Slide and patient reports include
tile_encoder and tile_encoder_output_variant so you can preflight the
fixed tile dependency the same way.
Bring your own encoder¶
Any encoder can be a preset. Subclass TorchTileEncoder for a
torch.nn.Module that maps (B, 3, H, W) to (B, D), or
TimmTileEncoder for a timm model (dense grids and attention
maps included), and register it with register_encoder(). Load
weights in the constructor, never at import time.
import torch
from slide2vec import TorchTileEncoder, register_encoder
@register_encoder(
"my-tile-model",
encode_dim=768,
input_size=224,
supports_variable_input_size=False,
supported_spacing_um=0.5,
precision="fp16",
source="/models/my-tile-model.ts",
)
class MyTileModel(TorchTileEncoder):
def __init__(self, *, output_variant: str | None = None):
model = torch.jit.load("/models/my-tile-model.ts", map_location="cpu")
super().__init__(
model,
encode_dim=768,
input_size=224,
mean=(0.485, 0.456, 0.406),
std=(0.229, 0.224, 0.225),
output_variant=output_variant,
)
mean and std are the model’s own normalization; check its model card
rather than assuming ImageNet values. For a timm checkpoint, copy
slide2vec/encoders/models/uni.py and change the model name, encode_dim,
patch_size and spacing. Then make the module importable in one of two ways.
From a source checkout¶
Save the file as slide2vec/encoders/models/my_tile_model.py. Every module
in that directory is imported on the first registry read (list_models(), a
preset lookup, loading a model), so the preset appears in list_models(),
works as model.name in YAML and on the CLI, and is rebuilt by every torchrun
worker. Files whose name starts with an underscore are skipped. A file that fails
to import makes that first read fail; the traceback names the file. Register at
module scope, but call list_models() or read preset metadata only inside
functions: a read while the directory is still importing is refused.
From an installed package¶
With pip install slide2vec or the Docker image, ship the module as its own
distribution with a zero-argument provider in the slide2vec.encoders
entry-point group. Installing it is enough: discovery is lazy, a provider that
raises is skipped and reported in the “Available: …” error, and workers
rediscover the preset by name.
my-slide2vec-encoders/
├── pyproject.toml
└── src/
└── my_slide2vec_encoders/
├── __init__.py
└── tile_model.py # the class above
[build-system]
requires = ["setuptools>=61"]
build-backend = "setuptools.build_meta"
[project]
name = "my-slide2vec-encoders"
version = "0.1.0"
dependencies = ["slide2vec>=7.1", "torch", "torchvision"]
[project.entry-points."slide2vec.encoders"]
my_org = "my_slide2vec_encoders:register_encoders"
[tool.setuptools.packages.find]
where = ["src"]
src/my_slide2vec_encoders/__init__.py defines the provider, which imports
the module so its decorator runs. The provider stays metadata-only: no weights,
no network.
def register_encoders() -> None:
from my_slide2vec_encoders import tile_model # noqa: F401
Install and use it:
pip install ./my-slide2vec-encoders
from slide2vec import Model, list_models
assert "my-tile-model" in list_models()
model = Model.from_preset("my-tile-model")
For distributed extraction, install the distribution, and make its weights and credentials reachable, in the same way on every worker and node.