kaiko-eva 0.0.0.dev8__py3-none-any.whl → 0.0.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of kaiko-eva might be problematic. Click here for more details.

Files changed (41) hide show
  1. eva/core/callbacks/__init__.py +2 -1
  2. eva/core/callbacks/config.py +143 -0
  3. eva/core/data/datasets/__init__.py +10 -2
  4. eva/core/data/datasets/embeddings/__init__.py +13 -0
  5. eva/core/data/datasets/{classification/embeddings.py → embeddings/base.py} +41 -43
  6. eva/core/data/datasets/embeddings/classification/__init__.py +10 -0
  7. eva/core/data/datasets/embeddings/classification/embeddings.py +66 -0
  8. eva/core/data/datasets/embeddings/classification/multi_embeddings.py +106 -0
  9. eva/core/data/transforms/__init__.py +3 -1
  10. eva/core/data/transforms/padding/__init__.py +5 -0
  11. eva/core/data/transforms/padding/pad_2d_tensor.py +38 -0
  12. eva/core/data/transforms/sampling/__init__.py +5 -0
  13. eva/core/data/transforms/sampling/sample_from_axis.py +40 -0
  14. eva/core/loggers/__init__.py +7 -0
  15. eva/core/loggers/dummy.py +38 -0
  16. eva/core/loggers/experimental_loggers.py +8 -0
  17. eva/core/loggers/log/__init__.py +5 -0
  18. eva/core/loggers/log/parameters.py +64 -0
  19. eva/core/loggers/log/utils.py +13 -0
  20. eva/core/models/modules/head.py +6 -11
  21. eva/core/models/modules/module.py +25 -1
  22. eva/core/trainers/_recorder.py +69 -7
  23. eva/core/trainers/functional.py +22 -5
  24. eva/core/trainers/trainer.py +20 -6
  25. eva/vision/__init__.py +1 -1
  26. eva/vision/data/datasets/__init__.py +1 -8
  27. eva/vision/data/datasets/_utils.py +3 -3
  28. eva/vision/data/datasets/classification/__init__.py +1 -8
  29. eva/vision/data/datasets/segmentation/base.py +20 -35
  30. eva/vision/data/datasets/segmentation/total_segmentator.py +88 -69
  31. eva/vision/models/.DS_Store +0 -0
  32. eva/vision/models/networks/.DS_Store +0 -0
  33. eva/vision/utils/convert.py +24 -0
  34. eva/vision/utils/io/nifti.py +10 -6
  35. {kaiko_eva-0.0.0.dev8.dist-info → kaiko_eva-0.0.2.dist-info}/METADATA +71 -27
  36. {kaiko_eva-0.0.0.dev8.dist-info → kaiko_eva-0.0.2.dist-info}/RECORD +39 -23
  37. {kaiko_eva-0.0.0.dev8.dist-info → kaiko_eva-0.0.2.dist-info}/WHEEL +1 -1
  38. eva/core/data/datasets/classification/__init__.py +0 -5
  39. eva/vision/data/datasets/classification/total_segmentator.py +0 -213
  40. {kaiko_eva-0.0.0.dev8.dist-info → kaiko_eva-0.0.2.dist-info}/entry_points.txt +0 -0
  41. {kaiko_eva-0.0.0.dev8.dist-info → kaiko_eva-0.0.2.dist-info}/licenses/LICENSE +0 -0
@@ -6,25 +6,26 @@ from glob import glob
6
6
  from typing import Callable, Dict, List, Literal, Tuple
7
7
 
8
8
  import numpy as np
9
+ from torchvision import tv_tensors
9
10
  from torchvision.datasets import utils
10
11
  from typing_extensions import override
11
12
 
12
13
  from eva.vision.data.datasets import _utils, _validators, structs
13
14
  from eva.vision.data.datasets.segmentation import base
14
- from eva.vision.utils import io
15
+ from eva.vision.utils import convert, io
15
16
 
16
17
 
17
18
  class TotalSegmentator2D(base.ImageSegmentation):
18
19
  """TotalSegmentator 2D segmentation dataset."""
19
20
 
20
- _train_index_ranges: List[Tuple[int, int]] = [(0, 83)]
21
- """Train range indices."""
21
+ _expected_dataset_lengths: Dict[str, int] = {
22
+ "train_small": 29892,
23
+ "val_small": 6480,
24
+ }
25
+ """Dataset version and split to the expected size."""
22
26
 
23
- _val_index_ranges: List[Tuple[int, int]] = [(83, 103)]
24
- """Validation range indices."""
25
-
26
- _n_slices_per_image: int = 20
27
- """The amount of slices to sample per 3D CT scan image."""
27
+ _sample_every_n_slices: int | None = None
28
+ """The amount of slices to sub-sample per 3D CT scan image."""
28
29
 
29
30
  _resources_full: List[structs.DownloadResource] = [
30
31
  structs.DownloadResource(
@@ -48,11 +49,10 @@ class TotalSegmentator2D(base.ImageSegmentation):
48
49
  self,
49
50
  root: str,
50
51
  split: Literal["train", "val"] | None,
51
- version: Literal["small", "full"] = "small",
52
+ version: Literal["small", "full"] | None = "small",
52
53
  download: bool = False,
53
- image_transforms: Callable | None = None,
54
- target_transforms: Callable | None = None,
55
- image_target_transforms: Callable | None = None,
54
+ as_uint8: bool = True,
55
+ transforms: Callable | None = None,
56
56
  ) -> None:
57
57
  """Initialize dataset.
58
58
 
@@ -60,33 +60,26 @@ class TotalSegmentator2D(base.ImageSegmentation):
60
60
  root: Path to the root directory of the dataset. The dataset will
61
61
  be downloaded and extracted here, if it does not already exist.
62
62
  split: Dataset split to use. If `None`, the entire dataset is used.
63
- version: The version of the dataset to initialize.
63
+ version: The version of the dataset to initialize. If `None`, it will
64
+ use the files located at root as is and wont perform any checks.
64
65
  download: Whether to download the data for the specified split.
65
66
  Note that the download will be executed only by additionally
66
67
  calling the :meth:`prepare_data` method and if the data does not
67
68
  exist yet on disk.
68
- image_transforms: A function/transform that takes in an image
69
- and returns a transformed version.
70
- target_transforms: A function/transform that takes in the target
71
- and transforms it.
72
- image_target_transforms: A function/transforms that takes in an
73
- image and a label and returns the transformed versions of both.
74
- This transform happens after the `image_transforms` and
75
- `target_transforms`.
69
+ as_uint8: Whether to convert and return the images as a 8-bit.
70
+ transforms: A function/transforms that takes in an image and a target
71
+ mask and returns the transformed versions of both.
76
72
  """
77
- super().__init__(
78
- image_transforms=image_transforms,
79
- target_transforms=target_transforms,
80
- image_target_transforms=image_target_transforms,
81
- )
73
+ super().__init__(transforms=transforms)
82
74
 
83
75
  self._root = root
84
76
  self._split = split
85
77
  self._version = version
86
78
  self._download = download
79
+ self._as_uint8 = as_uint8
87
80
 
88
81
  self._samples_dirs: List[str] = []
89
- self._indices: List[int] = []
82
+ self._indices: List[Tuple[int, int]] = []
90
83
 
91
84
  @functools.cached_property
92
85
  @override
@@ -107,7 +100,8 @@ class TotalSegmentator2D(base.ImageSegmentation):
107
100
 
108
101
  @override
109
102
  def filename(self, index: int) -> str:
110
- sample_dir = self._samples_dirs[self._indices[index]]
103
+ sample_idx, _ = self._indices[index]
104
+ sample_dir = self._samples_dirs[sample_idx]
111
105
  return os.path.join(sample_dir, "ct.nii.gz")
112
106
 
113
107
  @override
@@ -122,53 +116,58 @@ class TotalSegmentator2D(base.ImageSegmentation):
122
116
 
123
117
  @override
124
118
  def validate(self) -> None:
119
+ if self._version is None:
120
+ return
121
+
125
122
  _validators.check_dataset_integrity(
126
123
  self,
127
- length=1660 if self._split == "train" else 400,
124
+ length=self._expected_dataset_lengths.get(f"{self._split}_{self._version}", 0),
128
125
  n_classes=117,
129
126
  first_and_last_labels=("adrenal_gland_left", "vertebrae_T9"),
130
127
  )
131
128
 
132
129
  @override
133
130
  def __len__(self) -> int:
134
- return len(self._indices) * self._n_slices_per_image
131
+ return len(self._indices)
135
132
 
136
133
  @override
137
- def load_image(self, index: int) -> np.ndarray:
138
- image_path = self._get_image_path(index)
139
- slice_index = self._get_sample_slice_index(index)
134
+ def load_image(self, index: int) -> tv_tensors.Image:
135
+ sample_index, slice_index = self._indices[index]
136
+ image_path = self._get_image_path(sample_index)
140
137
  image_array = io.read_nifti_slice(image_path, slice_index)
141
- return image_array.repeat(3, axis=2)
138
+ if self._as_uint8:
139
+ image_array = convert.to_8bit(image_array)
140
+ image_rgb_array = image_array.repeat(3, axis=2)
141
+ return tv_tensors.Image(image_rgb_array.transpose(2, 0, 1))
142
142
 
143
143
  @override
144
- def load_mask(self, index: int) -> np.ndarray:
145
- masks_dir = self._get_masks_dir(index)
146
- slice_index = self._get_sample_slice_index(index)
144
+ def load_mask(self, index: int) -> tv_tensors.Mask:
145
+ sample_index, slice_index = self._indices[index]
146
+ masks_dir = self._get_masks_dir(sample_index)
147
147
  mask_paths = (os.path.join(masks_dir, label + ".nii.gz") for label in self.classes)
148
- masks = [io.read_nifti_slice(path, slice_index) for path in mask_paths]
149
- return np.concatenate(masks, axis=-1)
150
-
151
- def _get_masks_dir(self, index: int) -> str:
152
- """Returns the directory of the corresponding masks."""
153
- sample_dir = self._get_sample_dir(index)
154
- return os.path.join(self._root, sample_dir, "segmentations")
148
+ one_hot_encoded = np.concatenate(
149
+ [io.read_nifti_slice(path, slice_index) for path in mask_paths],
150
+ axis=2,
151
+ )
152
+ background_mask = one_hot_encoded.sum(axis=2, keepdims=True) == 0
153
+ one_hot_encoded_with_bg = np.concatenate([background_mask, one_hot_encoded], axis=2)
154
+ segmentation_label = np.argmax(one_hot_encoded_with_bg, axis=2)
155
+ return tv_tensors.Mask(segmentation_label)
155
156
 
156
- def _get_image_path(self, index: int) -> str:
157
+ def _get_image_path(self, sample_index: int) -> str:
157
158
  """Returns the corresponding image path."""
158
- sample_dir = self._get_sample_dir(index)
159
+ sample_dir = self._samples_dirs[sample_index]
159
160
  return os.path.join(self._root, sample_dir, "ct.nii.gz")
160
161
 
161
- def _get_sample_dir(self, index: int) -> str:
162
- """Returns the corresponding sample directory."""
163
- sample_index = self._indices[index // self._n_slices_per_image]
164
- return self._samples_dirs[sample_index]
162
+ def _get_masks_dir(self, sample_index: int) -> str:
163
+ """Returns the directory of the corresponding masks."""
164
+ sample_dir = self._samples_dirs[sample_index]
165
+ return os.path.join(self._root, sample_dir, "segmentations")
165
166
 
166
- def _get_sample_slice_index(self, index: int) -> int:
167
- """Returns the corresponding slice index."""
168
- image_path = self._get_image_path(index)
169
- total_slices = io.fetch_total_nifti_slices(image_path)
170
- slice_indices = np.linspace(0, total_slices - 1, num=self._n_slices_per_image, dtype=int)
171
- return slice_indices[index % self._n_slices_per_image]
167
+ def _get_number_of_slices_per_sample(self, sample_index: int) -> int:
168
+ """Returns the total amount of slices of a sample."""
169
+ image_path = self._get_image_path(sample_index)
170
+ return io.fetch_total_nifti_slices(image_path)
172
171
 
173
172
  def _fetch_samples_dirs(self) -> List[str]:
174
173
  """Returns the name of all the samples of all the splits of the dataset."""
@@ -179,31 +178,51 @@ class TotalSegmentator2D(base.ImageSegmentation):
179
178
  ]
180
179
  return sorted(sample_filenames)
181
180
 
182
- def _create_indices(self) -> List[int]:
183
- """Builds the dataset indices for the specified split."""
184
- split_index_ranges = {
185
- "train": self._train_index_ranges,
186
- "val": self._val_index_ranges,
187
- None: [(0, 103)],
188
- }
189
- index_ranges = split_index_ranges.get(self._split)
190
- if index_ranges is None:
191
- raise ValueError("Invalid data split. Use 'train', 'val' or `None`.")
181
+ def _get_split_indices(self) -> List[int]:
182
+ """Returns the samples indices that corresponding the dataset split and version."""
183
+ key = f"{self._split}_{self._version}"
184
+ match key:
185
+ case "train_small":
186
+ index_ranges = [(0, 83)]
187
+ case "val_small":
188
+ index_ranges = [(83, 102)]
189
+ case _:
190
+ index_ranges = [(0, len(self._samples_dirs))]
192
191
 
193
192
  return _utils.ranges_to_indices(index_ranges)
194
193
 
194
+ def _create_indices(self) -> List[Tuple[int, int]]:
195
+ """Builds the dataset indices for the specified split.
196
+
197
+ Returns:
198
+ A list of tuples, where the first value indicates the
199
+ sample index which the second its corresponding slice
200
+ index.
201
+ """
202
+ indices = [
203
+ (sample_idx, slide_idx)
204
+ for sample_idx in self._get_split_indices()
205
+ for slide_idx in range(self._get_number_of_slices_per_sample(sample_idx))
206
+ if slide_idx % (self._sample_every_n_slices or 1) == 0
207
+ ]
208
+ return indices
209
+
195
210
  def _download_dataset(self) -> None:
196
211
  """Downloads the dataset."""
197
212
  dataset_resources = {
198
213
  "small": self._resources_small,
199
214
  "full": self._resources_full,
200
- None: (0, 103),
201
215
  }
202
- resources = dataset_resources.get(self._version)
216
+ resources = dataset_resources.get(self._version or "")
203
217
  if resources is None:
204
- raise ValueError("Invalid data version. Use 'small' or 'full'.")
218
+ raise ValueError(
219
+ f"Can't download data version '{self._version}'. Use 'small' or 'full'."
220
+ )
205
221
 
206
222
  for resource in resources:
223
+ if os.path.isdir(self._root):
224
+ continue
225
+
207
226
  utils.download_and_extract_archive(
208
227
  resource.url,
209
228
  download_root=self._root,
Binary file
Binary file
@@ -0,0 +1,24 @@
1
+ """Image conversion related functionalities."""
2
+
3
+ from typing import Any
4
+
5
+ import numpy as np
6
+ import numpy.typing as npt
7
+
8
+
9
+ def to_8bit(image_array: npt.NDArray[Any]) -> npt.NDArray[np.uint8]:
10
+ """Casts an image of higher bit image (i.e. 16bit) to 8bit.
11
+
12
+ Args:
13
+ image_array: The image array to convert.
14
+
15
+ Returns:
16
+ The image as normalized as a 8-bit format.
17
+ """
18
+ if np.issubdtype(image_array.dtype, np.integer):
19
+ image_array = image_array.astype(np.float64)
20
+
21
+ image_scaled_array = image_array - image_array.min()
22
+ image_scaled_array /= image_scaled_array.max()
23
+ image_scaled_array *= 255
24
+ return image_scaled_array.astype(np.uint8)
@@ -8,16 +8,19 @@ import numpy.typing as npt
8
8
  from eva.vision.utils.io import _utils
9
9
 
10
10
 
11
- def read_nifti_slice(path: str, slice_index: int) -> npt.NDArray[Any]:
11
+ def read_nifti_slice(
12
+ path: str, slice_index: int, *, use_storage_dtype: bool = True
13
+ ) -> npt.NDArray[Any]:
12
14
  """Reads and loads a NIfTI image from a file path as `uint8`.
13
15
 
14
16
  Args:
15
17
  path: The path to the NIfTI file.
16
- slice_index: The image slice index to return. If `None`, it will
17
- return the full 3D image.
18
+ slice_index: The image slice index to return.
19
+ use_storage_dtype: Whether to cast the raw image
20
+ array to the inferred type.
18
21
 
19
22
  Returns:
20
- The image as a numpy array.
23
+ The image as a numpy array (height, width, channels).
21
24
 
22
25
  Raises:
23
26
  FileExistsError: If the path does not exist or it is unreachable.
@@ -25,10 +28,11 @@ def read_nifti_slice(path: str, slice_index: int) -> npt.NDArray[Any]:
25
28
  """
26
29
  _utils.check_file(path)
27
30
  image_data = nib.load(path) # type: ignore
28
- dtype = image_data.get_data_dtype() # type: ignore
29
31
  image_slice = image_data.slicer[:, :, slice_index : slice_index + 1] # type: ignore
30
32
  image_array = image_slice.get_fdata()
31
- return image_array.astype(dtype)
33
+ if use_storage_dtype:
34
+ image_array = image_array.astype(image_data.get_data_dtype()) # type: ignore
35
+ return image_array
32
36
 
33
37
 
34
38
  def fetch_total_nifti_slices(path: str) -> int:
@@ -1,10 +1,10 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: kaiko-eva
3
- Version: 0.0.0.dev8
3
+ Version: 0.0.2
4
4
  Summary: Evaluation Framework for oncology foundation models.
5
- Keywords: machine-learning evaluation-framework oncology foundation-models
6
- Author-Email: Ioannis Gatopoulos <ioannis@kaiko.ai>, Nicolas Känzig <nicolas@kaiko.ai>, Roman Moser <roman@kaiko.ai>
7
- Maintainer-Email: Ioannis Gatopoulos <ioannis@kaiko.ai>, Nicolas Känzig <nicolas@kaiko.ai>, Roman Moser <roman@kaiko.ai>
5
+ Keywords: machine-learning,evaluation-framework,oncology,foundation-models
6
+ Author-Email: Ioannis Gatopoulos <ioannis@kaiko.ai>, =?utf-8?q?Nicolas_K=C3=A4nzig?= <nicolas@kaiko.ai>, Roman Moser <roman@kaiko.ai>
7
+ Maintainer-Email: Ioannis Gatopoulos <ioannis@kaiko.ai>, =?utf-8?q?Nicolas_K=C3=A4nzig?= <nicolas@kaiko.ai>, Roman Moser <roman@kaiko.ai>
8
8
  License: Apache License
9
9
  Version 2.0, January 2004
10
10
  http://www.apache.org/licenses/
@@ -215,15 +215,17 @@ Project-URL: Homepage, https://kaiko-ai.github.io/eva/dev/
215
215
  Project-URL: Repository, https://github.com/kaiko-ai/eva
216
216
  Project-URL: Documentation, https://kaiko-ai.github.io/eva/dev/
217
217
  Requires-Python: >=3.10
218
- Requires-Dist: lightning>=2.2.1
219
- Requires-Dist: jsonargparse[omegaconf]>=4.27.4
218
+ Requires-Dist: torch==2.3.0
219
+ Requires-Dist: lightning>=2.2.2
220
+ Requires-Dist: jsonargparse[omegaconf]==4.28
220
221
  Requires-Dist: tensorboard>=2.16.2
221
222
  Requires-Dist: loguru>=0.7.2
222
223
  Requires-Dist: pandas>=2.2.0
223
224
  Requires-Dist: transformers>=4.38.2
224
225
  Requires-Dist: onnxruntime>=1.17.1
225
- Requires-Dist: onnx>=1.15.0
226
+ Requires-Dist: onnx>=1.16.0
226
227
  Requires-Dist: toolz>=0.12.1
228
+ Requires-Dist: rich>=13.7.1
227
229
  Requires-Dist: h5py>=3.10.0; extra == "vision"
228
230
  Requires-Dist: nibabel>=5.2.0; extra == "vision"
229
231
  Requires-Dist: opencv-python-headless>=4.9.0.80; extra == "vision"
@@ -240,15 +242,19 @@ Description-Content-Type: text/markdown
240
242
 
241
243
  <div align="center">
242
244
 
243
- <img src="https://github.com/kaiko-ai/eva/blob/main/docs/images/eva-logo.png?raw=true" width="400">
245
+ <br />
246
+
247
+ <img src="https://github.com/kaiko-ai/eva/blob/main/docs/images/eva-logo.png?raw=true" width="340">
244
248
 
249
+ <br />
245
250
  <br />
246
251
 
247
252
  _Oncology FM Evaluation Framework by kaiko.ai_
248
253
 
249
254
  [![PyPI](https://img.shields.io/pypi/v/kaiko-eva.svg?logo=python)](https://pypi.python.org/pypi/kaiko-eva)
250
- [![CI](https://github.com/kaiko-ai/eva/workflows/CI/badge.svg)](https://github.com/kaiko-ai/eva/actions?query=workflow%3ACI)
251
- [![license](https://img.shields.io/badge/License-Apache%202.0-blue.svg?labelColor=gray)](https://github.com/kaiko-ai/eva#license)
255
+ [![docs](https://img.shields.io/badge/📚_docs-latest-green)](https://kaiko-ai.github.io/eva/latest)
256
+ [![license](https://img.shields.io/badge/⚖️_License-Apache%202.0-blue.svg?labelColor=gray)](https://github.com/kaiko-ai/eva#license)<br>
257
+ [![paper](http://img.shields.io/badge/OpenReview-MIDL_2024-B31B1B.svg)](https://openreview.net/forum?id=FNBQOPj18N&noteId=FNBQOPj18N)
252
258
 
253
259
  <p align="center">
254
260
  <a href="https://github.com/kaiko-ai/eva#installation">Installation</a> •
@@ -264,7 +270,8 @@ _Oncology FM Evaluation Framework by kaiko.ai_
264
270
 
265
271
  <br />
266
272
 
267
- _`eva`_ is an evaluation framework for oncology foundation models (FMs) by [kaiko.ai](https://kaiko.ai/). Check out the [documentation](https://kaiko-ai.github.io/eva/) for more information.
273
+ _`eva`_ is an evaluation framework for oncology foundation models (FMs) by [kaiko.ai](https://kaiko.ai/).
274
+ Check out the [documentation](https://kaiko-ai.github.io/eva/) for more information.
268
275
 
269
276
  ### Highlights:
270
277
  - Easy and reliable benchmark of Oncology FMs
@@ -298,18 +305,33 @@ eva --version
298
305
 
299
306
  ## How To Use
300
307
 
301
- _eva_ can be used directly from the terminal as a CLI tool as follows:
308
+ _`eva`_ can be used directly from the terminal as a CLI tool as follows:
302
309
  ```sh
303
310
  eva {fit,predict,predict_fit} --config url/or/path/to/the/config.yaml
304
311
  ```
305
312
 
306
- For example, to perform a downstream evaluation of DINO ViT-S/16 on the BACH dataset with linear probing by first inferring the embeddings and performing 5 sequential fits, execute:
313
+ When used as a CLI tool, _`eva`_ supports configuration files (`.yaml`) as an argument to define its functionality.
314
+ Native supported configs can be found at the [configs](https://github.com/kaiko-ai/eva/tree/main/configs) directory
315
+ of the repo. Apart from cloning the repo, you can download the latest config folder as `.zip` from your browser from
316
+ [here](https://download-directory.github.io/?url=https://github.com/kaiko-ai/eva/tree/main/configs). Alternatively,
317
+ from a specific release the configs can be downloaded from the terminal as follows:
318
+ ```sh
319
+ curl -LO https://github.com/kaiko-ai/eva/releases/download/0.0.1/configs.zip | unzip configs
320
+ ```
321
+
322
+ For example, to perform a downstream evaluation of DINO ViT-S/16 on the BACH dataset with
323
+ linear probing by first inferring the embeddings and performing 5 sequential fits, execute:
307
324
  ```sh
325
+ # from a locally stored config file
326
+ eva predict_fit --config ./configs/vision/dino_vit/offline/bach.yaml
327
+
328
+ # from a remote stored config file
308
329
  eva predict_fit --config https://raw.githubusercontent.com/kaiko-ai/eva/main/configs/vision/dino_vit/offline/bach.yaml
309
330
  ```
310
331
 
311
332
  > [!NOTE]
312
- > All the datasets that support automatic download in the repo have by default the option to automatically download set to false. For automatic download you have to manually set download=true.
333
+ > All the datasets that support automatic download in the repo have by default the option to automatically download set to false.
334
+ > For automatic download you have to manually set download=true.
313
335
 
314
336
 
315
337
  To view all the possibles, execute:
@@ -317,11 +339,12 @@ To view all the possibles, execute:
317
339
  eva --help
318
340
  ```
319
341
 
320
- For more information, please refer to the [documentation](https://kaiko-ai.github.io/eva/dev/user-guide/tutorials/offline_vs_online/) and [tutorials](https://kaiko-ai.github.io/eva/dev/user-guide/advanced/replicate_evaluations/).
342
+ For more information, please refer to the [documentation](https://kaiko-ai.github.io/eva/dev/user-guide/tutorials/offline_vs_online/)
343
+ and [tutorials](https://kaiko-ai.github.io/eva/dev/user-guide/advanced/replicate_evaluations/).
321
344
 
322
345
  ## Benchmarks
323
346
 
324
- In this section you will find model benchmarks which were generated with _eva_.
347
+ In this section you will find model benchmarks which were generated with _`eva`_.
325
348
 
326
349
  ### Table I: WSI patch-level benchmark
327
350
 
@@ -334,13 +357,15 @@ In this section you will find model benchmarks which were generated with _eva_.
334
357
  | ViT-S/16 _(random)_ <sup>[1]</sup> | 0.410 | 0.617 | 0.501 | 0.753 | 0.728 |
335
358
  | ViT-S/16 _(ImageNet)_ <sup>[1]</sup> | 0.695 | 0.935 | 0.831 | 0.864 | 0.849 |
336
359
  | ViT-B/8 _(ImageNet)_ <sup>[1]</sup> | 0.710 | 0.939 | 0.814 | 0.870 | 0.856 |
360
+ | ViT-L/14 _(ImageNet)_ <sup>[1]</sup> | 0.707 | 0.916 | 0.832 | 0.873 | 0.888 |
337
361
  | DINO<sub>(p=16)</sub> <sup>[2]</sup> | 0.801 | 0.934 | 0.768 | 0.889 | 0.895 |
338
362
  | Phikon <sup>[3]</sup> | 0.725 | 0.935 | 0.777 | 0.912 | 0.915 |
339
- | ViT-S/16 _(kaiko.ai)_ <sup>[4]</sup> | 0.797 | 0.943 | 0.828 | 0.903 | 0.893 |
340
- | ViT-S/8 _(kaiko.ai)_ <sup>[4]</sup> | 0.834 | 0.946 | 0.832 | 0.897 | 0.887 |
341
- | ViT-B/16 _(kaiko.ai)_ <sup>[4]</sup> | 0.810 | 0.960 | 0.826 | 0.900 | 0.898 |
342
- | ViT-B/8 _(kaiko.ai)_ <sup>[4]</sup> | 0.865 | 0.956 | 0.809 | 0.913 | 0.921 |
343
- | ViT-L/14 _(kaiko.ai)_ <sup>[4]</sup> | 0.870 | 0.930 | 0.809 | 0.908 | 0.898 |
363
+ | UNI <sup>[4]</sup> | 0.814 | 0.950 | 0.837 | 0.936 | 0.938 |
364
+ | ViT-S/16 _(kaiko.ai)_ <sup>[5]</sup> | 0.797 | 0.943 | 0.828 | 0.903 | 0.893 |
365
+ | ViT-S/8 _(kaiko.ai)_ <sup>[5]</sup> | 0.834 | 0.946 | 0.832 | 0.897 | 0.887 |
366
+ | ViT-B/16 _(kaiko.ai)_ <sup>[5]</sup> | 0.810 | 0.960 | 0.826 | 0.900 | 0.898 |
367
+ | ViT-B/8 _(kaiko.ai)_ <sup>[5]</sup> | 0.865 | 0.956 | 0.809 | 0.913 | 0.921 |
368
+ | ViT-L/14 _(kaiko.ai)_ <sup>[5]</sup> | 0.870 | 0.930 | 0.809 | 0.908 | 0.898 |
344
369
 
345
370
  _Table I: Linear probing evaluation of FMs on patch-level downstream datasets.<br> We report averaged balanced accuracy
346
371
  over 5 runs, with an average standard deviation of ±0.003._
@@ -350,14 +375,16 @@ over 5 runs, with an average standard deviation of ±0.003._
350
375
  <br />
351
376
 
352
377
  _References_:
353
- 1. _"Emerging properties in self-supervised vision transformers”_
354
- 2. _"Benchmarking self-supervised learning on diverse pathology datasets”_
355
- 3. _"Scaling self-supervised learning for histopathology with masked image modeling”_
356
- 4. _"Towards Training Large-Scale Pathology Foundation Models: from TCGA to Hospital Scale”_
378
+ 1. _"Emerging properties in self-supervised vision transformers”_, [arXiv](https://arxiv.org/abs/2104.14294)
379
+ 2. _"Benchmarking self-supervised learning on diverse pathology datasets”_, [arXiv](https://arxiv.org/abs/2212.04690)
380
+ 3. _"Scaling self-supervised learning for histopathology with masked image modeling”_, [medRxiv](https://www.medrxiv.org/content/10.1101/2023.07.21.23292757v1)
381
+ 4. _"A General-Purpose Self-Supervised Model for Computational Pathology”_, [arXiv](https://arxiv.org/abs/2308.15474)
382
+ 5. _"Towards Training Large-Scale Pathology Foundation Models: from TCGA to Hospital Scale”_, [arXiv](https://arxiv.org/pdf/2404.15217)
357
383
 
358
384
  ## Contributing
359
385
 
360
- _eva_ is an open source project and welcomes contributions of all kinds. Please checkout the [developer](./docs/DEVELOPER_GUIDE.md) and [contributing guide](./docs/CONTRIBUTING.md) for help on how to do so.
386
+ _`eva`_ is an open source project and welcomes contributions of all kinds. Please checkout the [developer](./docs/DEVELOPER_GUIDE.md)
387
+ and [contributing guide](./docs/CONTRIBUTING.md) for help on how to do so.
361
388
 
362
389
  All contributors must follow the [code of conduct](./docs/CODE_OF_CONDUCT.md).
363
390
 
@@ -381,7 +408,24 @@ Our codebase is built using multiple opensource contributions
381
408
 
382
409
  </div>
383
410
 
384
- ---
411
+
412
+ ## Citation
413
+
414
+ If you find this repository useful, please consider giving a star ⭐ and adding the following citation:
415
+
416
+ ```
417
+ @inproceedings{
418
+ kaiko.ai2024eva,
419
+ title={eva: Evaluation framework for pathology foundation models},
420
+ author={kaiko.ai and Ioannis Gatopoulos and Nicolas K{\"a}nzig and Roman Moser and Sebastian Ot{\'a}lora},
421
+ booktitle={Medical Imaging with Deep Learning},
422
+ year={2024},
423
+ url={https://openreview.net/forum?id=FNBQOPj18N}
424
+ }
425
+ ```
426
+
427
+ <br />
428
+
385
429
  <div align="center">
386
430
  <img src="https://github.com/kaiko-ai/eva/blob/main/docs/images/kaiko-logo.png?raw=true" width="200">
387
431
  </div>