Switch to unified view

a b/docs-source/source/features.rst
1
.. _features:
2
3
Generating Features
4
===================
5
6
Converting images into feature vectors is a common step for many machine learning tasks, including :ref:`feature space analysis <activations>` and :ref:`multiple-instance learning (MIL) <mil>`. Slideflow provides a simple API for generating features from image tiles and includes several pretrained feature extractors. You can see a list of all available feature extractors with :func:`slideflow.list_extractors`.
7
8
Generating Features
9
*******************
10
11
The first step in generating features from a dataset of images is creating a feature extractor. Many types of feature extractors can be used, including imagenet-pretrained models, models finetuned in Slideflow, histology-specific pretrained feature extractors (ie. "foundation models"), or fine-tuned SSL models.  In all cases, feature extractors are built with :func:`slideflow.build_feature_extractor`, and features are generated for a :ref:`Dataset <datasets_and_val>` using :meth:`slideflow.Dataset.generate_feature_bags`, as described :ref:`below <bags>`.
12
13
.. code-block:: python
14
15
    # Build a feature extractor
16
    ctranspath = sf.build_feature_extractor('ctranspath')
17
18
    # Generate features for a dataset
19
    dataset.generate_feature_bags(ctranspath, outdir='/path/to/features')
20
21
22
Pretrained Extractors
23
*********************
24
25
Slideflow includes several pathology-specific feature extractors, also referred to as foundation models, pretrained on large-scale histology datasets.
26
27
.. list-table:: **Pretrained feature extractors.** Note: "histossl" was renamed to "phikon" in Slideflow 3.0.
28
    :header-rows: 1
29
    :widths: 14 10 8 8 8 14 28 10
30
31
    * - Model
32
      - Type
33
      - WSIs
34
      - Input size
35
      - Dim
36
      - Source
37
      - Package
38
      - Link
39
    * - **Virchow**
40
      - DINOv2
41
      - 1.5M
42
      - 224
43
      - 2560
44
      - Paige
45
      - ``slideflow``
46
      - `Paper <http://arxiv.org/pdf/2309.07778v5>`__
47
    * - **CTransPath**
48
      - SRCL
49
      - 32K
50
      - 224
51
      - 768
52
      - Tencent AI Lab
53
      - ``slideflow-gpl``
54
      - `Paper <https://www.sciencedirect.com/science/article/abs/pii/S1361841522002043>`__
55
    * - **RetCCL**
56
      - CCL
57
      - 32K
58
      - 256
59
      - 2048
60
      - Tencent AI Lab
61
      - ``slideflow-gpl``
62
      - `Paper <https://www.sciencedirect.com/science/article/abs/pii/S1361841522002730>`__
63
    * - **Phikon**
64
      - iBOT
65
      - 6.1K
66
      - 224
67
      - 768
68
      - Owkin
69
      - ``slideflow-noncommercial``
70
      - `Paper <https://www.medrxiv.org/content/10.1101/2023.07.21.23292757v2.full.pdf>`__
71
    * - **PLIP**
72
      - CLIP
73
      - N/A
74
      - 224
75
      - 512
76
      - Zhao Lab
77
      - ``slideflow-noncommercial``
78
      - `Paper <https://www.nature.com/articles/s41591-023-02504-3>`__
79
    * - **UNI**
80
      - DINOv2
81
      - 100K
82
      - 224
83
      - 1024
84
      - Mahmood Lab
85
      - ``slideflow-noncommercial``
86
      - `Paper <https://www.nature.com/articles/s41591-024-02857-3>`__
87
    * - **GigaPath**
88
      - DINOv2
89
      - 170K
90
      - 256
91
      - 1536
92
      - Microsoft
93
      - ``slideflow-noncommercial``
94
      - `Paper <https://aka.ms/gigapath>`__
95
96
97
In order to respect the original licensing agreements, pretrained models are distributed in separate packages. The core ``slideflow`` package provides access to models under the **Apache-2.0** license, while models under **GPL-3.0** are available in the ``slideflow-gpl`` package. Models restricted to non-commercial use are available under the **CC BY-NC 4.0** license through the ``slideflow-noncommercial`` package.
98
99
Loading weights
100
---------------
101
102
Pretrained feature extractors will automatically download their weights from Hugging Face upon creation. Some models, such as PLIP, GigaPath, UNI, and Phikon, require approval for access. Request approval on Hugging Face and ensure your local machine has been `authenticated <https://huggingface.co/docs/huggingface_hub/en/quick-start#authentication>`_.
103
104
All pretrained models can also be loaded using local weights. Use the ``weights`` argument when creating a feature extractor.
105
106
.. code-block:: python
107
108
    # Load UNI with local weights
109
    uni = sf.build_feature_extractor('uni', weights='../pytorch_model.bin')
110
111
Image preprocessing
112
-------------------
113
114
Each feature extractor includes a default image preprocessing pipeline that matches the original implementation. However, preprocessing can also be manually adjusted using various keyword arguments when creating a feature extractor.
115
116
- **resize**: ``int`` or ``bool``. If an ``int``, resizes images to this size. If ``True``, resizes images to the input size of the feature extractor. Default is ``False``.
117
- **center_crop**: ``int`` or ``bool``. If an ``int``, crops images to this size. If ``True``, crops images to the input size of the feature extractor. Center-cropping happens after resizing, if both are used. Default is ``False``.
118
- **interpolation**: ``str``. Interpolation method for resizing images. Default is ``bilinear`` for most models, but is ``bicubic`` for GigaPath and Virchow.
119
- **antialias**: ``bool``. Whether to apply antialiasing to resized images. Default is ``False`` (matching the default behavior of torchvision < 0.17).
120
- **norm_mean**: ``list``. Mean values for image normalization. Default is ``[0.485, 0.456, 0.406]`` for all models except PLIP.
121
- **norm_std**: ``list``. Standard deviation values for image normalization. Default is ``[0.229, 0.224, 0.225]`` for all models except PLIP.
122
123
124
Example:
125
126
.. code-block:: python
127
128
    # Load a feature extractor with custom preprocessing
129
    extractor = sf.build_feature_extractor(
130
        'ctranspath',
131
        resize=224,
132
        interpolation='bicubic',
133
        antialias=True
134
    )
135
136
Default values for these processing arguments are determined by the feature extractor. One notable exception to the standard preprocessing algorithm is GigaPath, for which images are resized first (default to 256x256) and then center cropped (default to 224x224), which mirrors the official implementation.
137
138
For transparency, you can see the current preprocessing pipeline with ``extractor.transform``:
139
140
.. code-block:: python
141
142
    >>> import slideflow as sf
143
    >>> ctranspath = sf.build_feature_extractor(
144
    ...   'ctranspath',
145
    ...   resize=256,
146
    ...   interpolation='bicubic',
147
    ...   center_crop=224
148
    ... )
149
    >>> ctranspath.transform
150
    Compose(
151
        CenterCrop(size=(224, 224))
152
        Resize(size=256, interpolation=bicubic, max_size=None, antialias=False)
153
        Lambda()
154
        Normalize(mean=(0.485, 0.456, 0.406), std=(0.229, 0.224, 0.225))
155
    )
156
157
158
GigaPath
159
--------
160
161
GigaPath is a DINOv2-based model from Microsoft/Providence trained on 170k whole-slide images and is bundled with ``slideflow-noncommercial``. The GigaPath model includes additional dependencies which are not broadly compatible with all OS distributions, and are thus not installed by default. To install the GigaPath dependencies:
162
163
.. code-block:: bash
164
165
    pip install slideflow-noncommercial[gigapath] git+ssh://git@github.com/prov-gigapath/prov-gigapath
166
167
168
GigaPath has two stages: a tile encoder and slide-level encoder. The tile encoder (``"gigapath.tile"``) works the same as all other feature extractors in Slideflow. You can build this encoder directly:
169
170
.. code-block:: python
171
172
    # Build the tile encoder
173
    gigapath_tile = sf.build_feature_extractor("gigapath.tile")
174
175
    # Use the tile encoder
176
    project.generate_feature_bags(gigapath_tile, ...)
177
178
179
or you can build the combined tile+slide model, and then use ``gigapath.tile``:
180
181
.. code-block:: python
182
183
    # Build the tile encoder
184
    gigapath = sf.build_feature_extractor("gigapath")
185
186
    # Use the tile encoder
187
    project.generate_feature_bags(gigapath.tile, ...)
188
189
As there are two stages to GigaPath, there are also separate model weights. As with other pretrained feature extractors, the weights will be auto-downloaded from Hugging Face upon first use if you are logged into Hugging Face and have been granted access to the repository. If you have manually downloaded the weights, these can be used with the following:
190
191
.. code-block:: python
192
193
    # Example of how to supply tile + slide weights
194
    # For the full GigaPath model
195
    gigapath = sf.build_feature_extractor(
196
        'gigapath',
197
        tile_encoder_weights='../pytorch_model.bin',
198
        slide_encoder_weights='../slide_encoder.pth'
199
    )
200
201
    # Or, just supply the tile weights
202
    gigapath_tile = sf.build_feature_extractor(
203
        'gigapath.tile',
204
        weights='pytorch_model.bin'
205
    )
206
207
208
Once feature bags have been generated and saved with the GigaPath tile encoder, you can then generate slide-level embeddings with ``gigapath.slide``:
209
210
.. code-block:: python
211
212
    # Load GigaPath
213
    gigapath = sf.build_feature_extractor('gigapath')
214
215
    # Generate tile-level features
216
    project.generate_feature_bags(gigapath.tile, ..., outdir='/gigapath_bags')
217
218
    # Generate slide-level embeddings
219
    gigapath.slide.generate_and_save('/gigapath_bags', outdir='/gigapath_embeddings')
220
221
In addition to running the tile and slide encoder steps separately, you can also run the combined pipeline all at once on a whole-slide image, generating a final slide-level embedding.
222
223
.. code-block:: python
224
225
    # Load GigaPath
226
    gigapath = sf.build_feature_extractor('gigapath')
227
228
    # Load slide
229
    wsi = sf.WSI('slide.svs', tile_px=256, tile_um=128)
230
231
    # Generate slide embedding
232
    embedding = gigapath(wsi)
233
234
235
ImageNet Features
236
*****************
237
238
To calculate features from an ImageNet-pretrained network, first build an imagenet feature extractor with :func:`slideflow.build_feature_extractor`. The first argument should be the name of an architecture followed by ``_imagenet``, and the expected tile size should be passed to the keyword argument ``tile_px``. You can optionally specify the layer from which to generate features with the ``layers`` argument; if not provided, it will default to calculating features from post-convolutional layer activations. For example, to build a ResNet50 feature extractor for images at 299 x 299 pixels:
239
240
.. code-block:: python
241
242
    resnet50 = sf.build_feature_extractor(
243
        'resnet50_imagenet',
244
        tile_px=299
245
    )
246
247
This will calculate features using activations from the post-convolutional layer. You can also concatenate activations from multiple neural network layers and apply pooling for layers with 2D output shapes.
248
249
.. code-block:: python
250
251
    resnet50 = sf.build_feature_extractor(
252
        'resnet50_imagenet',
253
        layers=['conv1_relu', 'conv3_block1_2_relu'],
254
        pooling='avg',
255
        tile_px=299
256
    )
257
258
If a model architecture is available in both the Tensorflow and PyTorch backends, Slideflow will default to using the active backend. You can manually set the feature extractor backend using ``backend``.
259
260
.. code-block:: python
261
262
    # Create a PyTorch feature extractor
263
    extractor = sf.build_feature_extractor(
264
        'resnet50_imagenet',
265
        layers=['layer2.0.conv1', 'layer3.1.conv2'],
266
        pooling='avg',
267
        tile_px=299,
268
        backend='torch'
269
    )
270
271
You can view all available feature extractors with :func:`slideflow.model.list_extractors`.
272
273
Layer Activations
274
*****************
275
276
You can also calculate features from any model trained in Slideflow. The first argument to ``build_feature_extractor()`` should be the path of the trained model.  You can optionally specify the layer at which to calculate activations using the ``layers`` keyword argument. If not specified, activations are calculated at the post-convolutional layer.
277
278
.. code-block:: python
279
280
    # Calculate features from trained model.
281
    features = build_feature_extractor(
282
        '/path/to/model',
283
        layers='sepconv3_bn'
284
    )
285
286
Self-Supervised Learning
287
************************
288
289
Finally, you can also generate features from a trained :ref:`self-supervised learning <simclr_ssl>` model (either `SimCLR <https://github.com/jamesdolezal/simclr>`_ or `DinoV2 <https://github.com/jamesdolezal/dinov2>`_).
290
291
For SimCLR models, use ``'simclr'`` as the first argument to ``build_feature_extractor()``, and pass the path to a saved model (or saved checkpoint file) via the keyword argument ``ckpt``.
292
293
.. code-block:: python
294
295
    simclr = sf.build_feature_extractor(
296
        'simclr',
297
        ckpt='/path/to/simclr.ckpt'
298
    )
299
300
For DinoV2 models, use ``'dinov2'`` as the first argument, and pass the model configuration YAML file to ``cfg`` and the teacher checkpoint weights to ``weights``.
301
302
.. code-block:: python
303
304
    dinov2 = sf.build_feature_extractor(
305
        'dinov2',
306
        weights='/path/to/teacher_checkpoint.pth',
307
        cfg='/path/to/config.yaml'
308
    )
309
310
311
312
Custom Extractors
313
*****************
314
315
Slideflow also provides an API for integrating your own custom, pretrained feature extractor. See :ref:`custom_extractors` for additional information.
316
317
.. _bags:
318
319
Exporting Features
320
******************
321
322
Feature bags
323
------------
324
325
Once you have prepared a feature extractor, features can be generated for a dataset and exported to disk for later use. Pass a feature extractor to the first argument of :meth:`slideflow.Project.generate_feature_bags`, with a :class:`slideflow.Dataset` as the second argument.
326
327
.. code-block:: python
328
329
    # Load a project and dataset.
330
    P = sf.Project(...)
331
    dataset = P.dataset(tile_px=299, tile_um=302)
332
333
    # Create a feature extractor.
334
    ctranspath = sf.build_feature_extractor('ctranspath', resize=True)
335
336
    # Calculate & export feature bags.
337
    P.generate_feature_bags(ctranspath, dataset)
338
339
.. note::
340
341
    If you are generating features from a SimCLR model trained with stain normalization,
342
    you should specify the stain normalizer using the ``normalizer`` argument to :meth:`slideflow.Project.generate_feature_bags` or :class:`slideflow.DatasetFeatures`.
343
344
Features are calculated for slides in batches, keeping memory usage low. By default, features are saved to disk in a directory named ``pt_files`` within the project directory, but you can override the destination directory using the ``outdir`` argument.
345
346
Alternatively, you can calculate features for a dataset using :class:`slideflow.DatasetFeatures` and the ``.to_torch()`` method.  This will calculate features for your entire dataset at once, which may require a large amount of memory. The first argument should be the feature extractor, and the second argument should be a :class:`slideflow.Dataset`.
347
348
.. code-block:: python
349
350
    # Calculate features for the entire dataset.
351
    features = sf.DatasetFeatures(ctranspath, dataset)
352
353
    # Export feature bags.
354
    features.to_torch('/path/to/bag_directory/')
355
356
357
.. warning::
358
359
    Using :class:`slideflow.DatasetFeatures` directly may result in a large amount of memory usage, particularly for sizable datasets. When generating feature bags for training MIL models, it is recommended to use :meth:`slideflow.Project.generate_feature_bags` instead.
360
361
Feature "bags" are PyTorch tensors of features for all images in a slide, saved to disk as ``.pt`` files. These bags are used to train MIL models. Bags can be manually loaded and inspected using :func:`torch.load`.
362
363
.. code-block:: python
364
365
    >>> import torch
366
    >>> bag = torch.load('/path/to/bag.pt')
367
    >>> bag.shape
368
    torch.Size([2310, 768])
369
    >>> bag.dtype
370
    torch.float32
371
372
When image features are exported for a dataset, the feature extractor configuration is saved to ``bags_config.json`` in the same directory as the exported features. This configuration file can be used to rebuild the feature extractor. An example file is shown below.
373
374
.. code-block:: json
375
376
    {
377
     "extractor": {
378
      "class": "slideflow.model.extractors.ctranspath.CTransPathFeatures",
379
      "kwargs": {
380
       "center_crop": true
381
      }
382
     },
383
     "normalizer": {
384
      "method": "macenko",
385
      "fit": {
386
       "stain_matrix_target": [
387
        [
388
         0.5062568187713623,
389
         0.22186939418315887
390
        ],
391
        [
392
         0.7532230615615845,
393
         0.8652154803276062
394
        ],
395
        [
396
         0.4069173336029053,
397
         0.42241501808166504
398
        ]
399
       ],
400
       "target_concentrations": [
401
        1.7656903266906738,
402
        1.2797492742538452
403
       ]
404
      }
405
     },
406
     "num_features": 2048,
407
     "tile_px": 299,
408
     "tile_um": 302
409
    }
410
411
The feature extractor can be manually rebuilt using :func:`slideflow.model.rebuild_extractor()`:
412
413
.. code-block:: python
414
415
    from slideflow.model import rebuild_extractor
416
417
    # Recreate the feature extractor
418
    # and stain normalizer, if applicable
419
    extractor, normalizer = rebuild_extractor('/path/to/bags_config.json')
420
421
422
From a TFRecord
423
---------------
424
425
In addition to generating and exporting feature bags for a dataset, features can also be generated from a single TFRecord file. This may be useful for debugging or testing purposes.
426
427
.. code-block:: python
428
429
    import slideflow as sf
430
431
    # Create a feature extractor
432
    ctranspath = sf.build_feature_extractor('ctranspath')
433
434
    # Bags is a tensor of shape (n_tiles, n_features)
435
    # Coords is a tensor of shape (n_tiles, 2), containing x/y tile coordinates.
436
    bags, coords = ctranspath('file.tfrecords')
437
438
439
From a whole-slide image
440
------------------------
441
442
Feature extractors can also create features from a whole-slide image. This is useful for single-slide analysis, MIL inference, and other tasks where features are needed for the entire slide. Features are returned as a 3D tensor, with shape ``(width, height, n_features)``, reflecting the spatial arrangement of features for tiles across the image.
443
444
.. code-block:: python
445
446
    # Load a feature extractor.
447
    ctranspath = sf.build_feature_extractor('ctranspath')
448
449
    # Load a whole-slide image.
450
    wsi = sf.WSI('slide.svs', tile_px=256, tile_um=128)
451
452
    # Generate features for the whole slide.
453
    # Shape: (width, height, n_features)
454
    features = ctranspath(wsi)
455
456
457
Mixed precision
458
---------------
459
460
All feature extractors will use mixed precision by default. This can be disabled by setting the ``mixed_precision`` argument to ``False`` when creating the feature extractor.
461
462
.. code-block:: python
463
464
    # Load a feature extractor without mixed precision
465
    extractor = sf.build_feature_extractor('ctranspath', mixed_precision=False)
466
467
468
License & Citation
469
------------------
470
471
Licensing and citation information for the pretrained feature extractors is accessible with the ``.license`` and ``.citation`` attributes.
472
473
.. code-block:: python
474
475
    >>> ctranspath.license
476
    'GNU General Public License v3.0'
477
    >>> print(ctranspath.citation)
478
479
    @{wang2022,
480
      title={Transformer-based Unsupervised Contrastive Learning for Histopathological Image Classification},
481
      author={Wang, Xiyue and Yang, Sen and Zhang, Jun and Wang, Minghui and Zhang, Jing  and Yang, Wei and Huang, Junzhou  and Han, Xiao},
482
      journal={Medical Image Analysis},
483
      year={2022},
484
      publisher={Elsevier}
485
    }