Views
No views yet
transformers:1import torch
2from transformers import AutoModel
3
4
5device = "cuda" if torch.cuda.is_available() else "cpu"
6
7# Load HistAug (Virchow2 latent augmentation model)
8model_id = "sofieneb/histaug-virchow2"
9model = AutoModel.from_pretrained(model_id, trust_remote_code=True).to(device)
10
11# Example: patch embeddings from Virchow2
12num_patches = 50000
13embedding_dim = 2560
14patch_embeddings = torch.randn((num_patches, embedding_dim), device=device)
15
16# Sample augmentation parameters
17# mode="wsi_wise" applies the same transformation across the whole slide
18# mode="instance_wise" applies different transformations per patch
19aug_params = model.sample_aug_params(
20 batch_size=num_patches,
21 device=patch_embeddings.device,
22 mode="wsi_wise"
23)
24
25# Apply augmentation in latent space
26augmented_embeddings = model(patch_embeddings, aug_params)
27
28print(augmented_embeddings.shape) # (num_patches, embedding_dim)1{
2 "transforms": {
3 "parameters": {
4 "brightness": [-0.5, 0.5],
5 "contrast": [-0.5, 0.5],
6 "crop": 0.75,
7 "dilation": 0.75,
8 "erosion": 0.75,
9 "powerlaw": [-0.5, 0.5],
10 "gaussian_blur": 0.75,
11 "h_flip": 0.75,
12 "hed": [-0.5, 0.5],
13 "hue": [-0.5, 0.5],
14 "rotation": 0.75,
15 "saturation": [-0.5, 0.5],
16 "v_flip": 0.75
17 }
18 }
19}brightness, hue, hed, powerlaw, saturation) use an interval [min, max] from which parameters are sampled.h_flip, v_flip, dilation, erosion, rotation, gaussian_blur, crop) use a probability (e.g., 0.75) indicating how likely the transform is applied during sampling.You can access and modify this at runtime via:print(model.histaug.transforms_parameters)
model.histaug.transforms_parameters.pop the key; during sampling it will appear with parameter 0 (effectively disabled).mode="wsi_wise" (same parameters for all patches) or mode="instance_wise" (per-patch parameters).1## Controlling Transformations — pop vs. change params (continuous & discrete)
2
3import torch
4
5device = "cuda" if torch.cuda.is_available() else "cpu"
6num_to_sample = 5
7
8# start: sample once and inspect current config
9sample_1 = model.sample_aug_params(batch_size=num_to_sample, device=device, mode="wsi_wise")
10print("initial sample:\n", sample_1, "\n")
11
12print("initial transforms_parameters:\n", model.histaug.transforms_parameters, "\n")
13
14# pop examples
15# pop a continuous transform: remove "hue" (interval transform)
16model.histaug.transforms_parameters.pop("hue", None)
17
18# pop a discrete transform: remove "rotation" (probability-based)
19model.histaug.transforms_parameters.pop("rotation", None)
20
21sample_2 = model.sample_aug_params(batch_size=num_to_sample, device=device, mode="wsi_wise")
22print("after popping 'hue' (continuous) and 'rotation' (discrete):\n", sample_2, "\n")
23
24# change param examples
25# change a continuous transform interval: narrow 'brightness' from [-0.5, 0.5] to [-0.25, 0.25]
26model.histaug.transforms_parameters["brightness"] = [-0.25, 0.25]
27
28# change a discrete transform probability: lower 'h_flip' from 0.75 to 0.10
29model.histaug.transforms_parameters["h_flip"] = 0.10
30
31sample_3 = model.sample_aug_params(batch_size=num_to_sample, device=device, mode="wsi_wise")
32print("after changing 'brightness' interval and 'h_flip' probability:\n", sample_3, "\n")
331import torch
2
3# histaug: the loaded HistAug model (Virchow2 variant)
4# mil_model: your MIL aggregator (e.g., ABMIL/CLAM/TransMIL head)
5# criterion, optimizer, loader already defined
6
7device = "cuda" if torch.cuda.is_available() else "cpu"
8histaug = histaug.to(device).eval() # histaug generator is frozen during MIL training
9for p in histaug.parameters():
10 p.requires_grad_(False)
11
12def maybe_augment_bag(bag_features: torch.Tensor,
13 p_apply: float = 0.60,
14 mode: str = "wsi_wise") -> torch.Tensor:
15 """
16 bag_features: (num_patches, embed_dim) on device
17 p_apply: probability to apply augmentation
18 mode: "wsi_wise" (same params for all patches) or "instance_wise"
19 """
20 if torch.rand(()) >= p_apply:
21 return bag_features
22 with torch.no_grad():
23 aug_params = histaug.sample_aug_params(
24 batch_size=bag_features.size(0),
25 device=bag_features.device,
26 mode=mode # "wsi_wise" or "instance_wise"
27 )
28 bag_features = histaug(bag_features, aug_params)
29 return bag_features
30
31# --- single-bag training example ---
32for bag_features, label in loader: # bag_features: (num_patches, embed_dim)
33 bag_features = bag_features.to(device)
34
35 # apply augmentation with 60% probability (WSI-wise by default)
36 bag_features = maybe_augment_bag(bag_features, p_apply=0.60, mode="wsi_wise") # output : (num_patches, embed_dim)
37
38 logits = mil_model(bag_features) # forward through your MIL head
39 loss = criterion(logits, label.to(device))
40 loss.backward()
41 optimizer.step()
42 optimizer.zero_grad()1# On your compute job (no internet):
2export HF_HUB_OFFLINE=1
3export TRANSFORMERS_OFFLINE=11# On the front-end/login node (with internet):
2python -c "from transformers import AutoModel; AutoModel.from_pretrained('sofieneb/histaug-virchow2', trust_remote_code=True)"1from transformers import AutoModel
2model = AutoModel.from_pretrained(
3 "sofieneb/histaug-virchow2",
4 trust_remote_code=True,
5 local_files_only=True, # uses local cache only
6)hf download1# On the front-end/login node (with internet):
2hf download sofieneb/histaug-virchow2 --local-dir ./histaug-virchow21from transformers import AutoModel
2model = AutoModel.from_pretrained(
3 "./histaug-virchow2", # local path instead of hub ID
4 trust_remote_code=True,
5 local_files_only=True, # uses local files only
6)1@misc{boutaj2025controllablelatentspaceaugmentation,
2 title={Controllable Latent Space Augmentation for Digital Pathology},
3 author={Sofiène Boutaj and Marin Scalbert and Pierre Marza and Florent Couzinie-Devy and Maria Vakalopoulou and Stergios Christodoulidis},
4 year={2025},
5 eprint={2508.14588},
6 archivePrefix={arXiv},
7 primaryClass={cs.CV},
8 url={https://arxiv.org/abs/2508.14588},
9}