torch-em 0.9.0__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.
- torch_em/__init__.py +11 -0
- torch_em/__version__.py +1 -0
- torch_em/classification/__init__.py +6 -0
- torch_em/classification/classification.py +126 -0
- torch_em/classification/classification_dataset.py +65 -0
- torch_em/classification/classification_logger.py +137 -0
- torch_em/classification/classification_trainer.py +42 -0
- torch_em/cli.py +413 -0
- torch_em/data/__init__.py +18 -0
- torch_em/data/concat_dataset.py +39 -0
- torch_em/data/dataset_wrapper.py +23 -0
- torch_em/data/datasets/__init__.py +13 -0
- torch_em/data/datasets/electron_microscopy/__init__.py +48 -0
- torch_em/data/datasets/electron_microscopy/aimseg.py +159 -0
- torch_em/data/datasets/electron_microscopy/asem.py +256 -0
- torch_em/data/datasets/electron_microscopy/astih.py +340 -0
- torch_em/data/datasets/electron_microscopy/axondeepseg.py +277 -0
- torch_em/data/datasets/electron_microscopy/axonem.py +150 -0
- torch_em/data/datasets/electron_microscopy/betaseg.py +166 -0
- torch_em/data/datasets/electron_microscopy/cellmap.py +550 -0
- torch_em/data/datasets/electron_microscopy/cem.py +382 -0
- torch_em/data/datasets/electron_microscopy/cremi.py +251 -0
- torch_em/data/datasets/electron_microscopy/deepict.py +178 -0
- torch_em/data/datasets/electron_microscopy/densecell.py +197 -0
- torch_em/data/datasets/electron_microscopy/emneuron.py +153 -0
- torch_em/data/datasets/electron_microscopy/emps.py +185 -0
- torch_em/data/datasets/electron_microscopy/fafb.py +241 -0
- torch_em/data/datasets/electron_microscopy/fafb_nuclei.py +116 -0
- torch_em/data/datasets/electron_microscopy/fib25.py +215 -0
- torch_em/data/datasets/electron_microscopy/hemibrain.py +243 -0
- torch_em/data/datasets/electron_microscopy/human_cortex_h01.py +242 -0
- torch_em/data/datasets/electron_microscopy/human_organoids.py +179 -0
- torch_em/data/datasets/electron_microscopy/hydra_vulgaris.py +299 -0
- torch_em/data/datasets/electron_microscopy/isbi2012.py +121 -0
- torch_em/data/datasets/electron_microscopy/kasthuri.py +173 -0
- torch_em/data/datasets/electron_microscopy/lucchi.py +174 -0
- torch_em/data/datasets/electron_microscopy/malecns.py +214 -0
- torch_em/data/datasets/electron_microscopy/manc.py +197 -0
- torch_em/data/datasets/electron_microscopy/microns.py +527 -0
- torch_em/data/datasets/electron_microscopy/microns_nuclei.py +119 -0
- torch_em/data/datasets/electron_microscopy/mitoem.py +274 -0
- torch_em/data/datasets/electron_microscopy/mitoemv2.py +309 -0
- torch_em/data/datasets/electron_microscopy/mousecc.py +180 -0
- torch_em/data/datasets/electron_microscopy/nisb.py +283 -0
- torch_em/data/datasets/electron_microscopy/nuc_mm.py +159 -0
- torch_em/data/datasets/electron_microscopy/platynereis.py +448 -0
- torch_em/data/datasets/electron_microscopy/probtem.py +179 -0
- torch_em/data/datasets/electron_microscopy/snemi.py +135 -0
- torch_em/data/datasets/electron_microscopy/sponge_em.py +137 -0
- torch_em/data/datasets/electron_microscopy/synapseweb_hippocampus.py +200 -0
- torch_em/data/datasets/electron_microscopy/uro_cell.py +226 -0
- torch_em/data/datasets/electron_microscopy/vnc.py +163 -0
- torch_em/data/datasets/electron_microscopy/waenet.py +293 -0
- torch_em/data/datasets/electron_microscopy/wildenberg.py +384 -0
- torch_em/data/datasets/electron_microscopy/zebrafinch.py +312 -0
- torch_em/data/datasets/histopathology/__init__.py +23 -0
- torch_em/data/datasets/histopathology/bcss.py +243 -0
- torch_em/data/datasets/histopathology/conic.py +234 -0
- torch_em/data/datasets/histopathology/consep.py +209 -0
- torch_em/data/datasets/histopathology/cpm.py +221 -0
- torch_em/data/datasets/histopathology/cryonuseg.py +186 -0
- torch_em/data/datasets/histopathology/cytodark0.py +211 -0
- torch_em/data/datasets/histopathology/glas.py +172 -0
- torch_em/data/datasets/histopathology/glysac.py +182 -0
- torch_em/data/datasets/histopathology/hest.py +735 -0
- torch_em/data/datasets/histopathology/janowczyk.py +231 -0
- torch_em/data/datasets/histopathology/lizard.py +194 -0
- torch_em/data/datasets/histopathology/lynsec.py +222 -0
- torch_em/data/datasets/histopathology/monusac.py +294 -0
- torch_em/data/datasets/histopathology/monuseg.py +248 -0
- torch_em/data/datasets/histopathology/nuclick.py +149 -0
- torch_em/data/datasets/histopathology/nuinsseg.py +128 -0
- torch_em/data/datasets/histopathology/orion_crc.py +526 -0
- torch_em/data/datasets/histopathology/pannuke.py +277 -0
- torch_em/data/datasets/histopathology/panoptils.py +195 -0
- torch_em/data/datasets/histopathology/pcns.py +325 -0
- torch_em/data/datasets/histopathology/puma.py +345 -0
- torch_em/data/datasets/histopathology/segpath.py +336 -0
- torch_em/data/datasets/histopathology/srsanet.py +167 -0
- torch_em/data/datasets/histopathology/tnbc.py +200 -0
- torch_em/data/datasets/light_microscopy/__init__.py +83 -0
- torch_em/data/datasets/light_microscopy/aisegcell.py +210 -0
- torch_em/data/datasets/light_microscopy/arvidsson.py +179 -0
- torch_em/data/datasets/light_microscopy/bac_mother.py +136 -0
- torch_em/data/datasets/light_microscopy/balf.py +268 -0
- torch_em/data/datasets/light_microscopy/bbbc030.py +214 -0
- torch_em/data/datasets/light_microscopy/bbbc034.py +155 -0
- torch_em/data/datasets/light_microscopy/bccd.py +206 -0
- torch_em/data/datasets/light_microscopy/bitdepth_nucseg.py +156 -0
- torch_em/data/datasets/light_microscopy/blastospim.py +139 -0
- torch_em/data/datasets/light_microscopy/bmgd.py +236 -0
- torch_em/data/datasets/light_microscopy/brain_organoids.py +131 -0
- torch_em/data/datasets/light_microscopy/brifiseg.py +166 -0
- torch_em/data/datasets/light_microscopy/cartocell.py +145 -0
- torch_em/data/datasets/light_microscopy/celegans_atlas.py +134 -0
- torch_em/data/datasets/light_microscopy/cellbindb.py +166 -0
- torch_em/data/datasets/light_microscopy/cellpose.py +176 -0
- torch_em/data/datasets/light_microscopy/cellseg_3d.py +126 -0
- torch_em/data/datasets/light_microscopy/cisd.py +278 -0
- torch_em/data/datasets/light_microscopy/covid_if.py +166 -0
- torch_em/data/datasets/light_microscopy/cshaper.py +231 -0
- torch_em/data/datasets/light_microscopy/ctc.py +226 -0
- torch_em/data/datasets/light_microscopy/cvz_fluo.py +172 -0
- torch_em/data/datasets/light_microscopy/deepbacs.py +213 -0
- torch_em/data/datasets/light_microscopy/deepseas.py +142 -0
- torch_em/data/datasets/light_microscopy/dic_hepg2.py +190 -0
- torch_em/data/datasets/light_microscopy/dsb.py +248 -0
- torch_em/data/datasets/light_microscopy/dynamicnuclearnet.py +170 -0
- torch_em/data/datasets/light_microscopy/e11bio.py +259 -0
- torch_em/data/datasets/light_microscopy/embedseg_data.py +147 -0
- torch_em/data/datasets/light_microscopy/enseg.py +234 -0
- torch_em/data/datasets/light_microscopy/evican.py +335 -0
- torch_em/data/datasets/light_microscopy/glioma_c6.py +234 -0
- torch_em/data/datasets/light_microscopy/gonuclear.py +245 -0
- torch_em/data/datasets/light_microscopy/hpa.py +453 -0
- torch_em/data/datasets/light_microscopy/idr0095.py +274 -0
- torch_em/data/datasets/light_microscopy/ifnuclei.py +107 -0
- torch_em/data/datasets/light_microscopy/liconn.py +271 -0
- torch_em/data/datasets/light_microscopy/livecell.py +305 -0
- torch_em/data/datasets/light_microscopy/lpc_nucseg.py +201 -0
- torch_em/data/datasets/light_microscopy/lsm_mouse_embryo.py +210 -0
- torch_em/data/datasets/light_microscopy/medussa.py +228 -0
- torch_em/data/datasets/light_microscopy/microbeseg.py +174 -0
- torch_em/data/datasets/light_microscopy/mndino.py +230 -0
- torch_em/data/datasets/light_microscopy/morphonet.py +247 -0
- torch_em/data/datasets/light_microscopy/morphoseg.py +216 -0
- torch_em/data/datasets/light_microscopy/mouse_embryo.py +153 -0
- torch_em/data/datasets/light_microscopy/mucic.py +484 -0
- torch_em/data/datasets/light_microscopy/neurips_cell_seg.py +361 -0
- torch_em/data/datasets/light_microscopy/neurosphere.py +176 -0
- torch_em/data/datasets/light_microscopy/nis3d.py +156 -0
- torch_em/data/datasets/light_microscopy/nisnet3d.py +191 -0
- torch_em/data/datasets/light_microscopy/nuc_morph.py +238 -0
- torch_em/data/datasets/light_microscopy/omnipose.py +171 -0
- torch_em/data/datasets/light_microscopy/oocyteseg.py +234 -0
- torch_em/data/datasets/light_microscopy/orgaextractor.py +160 -0
- torch_em/data/datasets/light_microscopy/organoid.py +270 -0
- torch_em/data/datasets/light_microscopy/organoidnet.py +148 -0
- torch_em/data/datasets/light_microscopy/orgasegment.py +144 -0
- torch_em/data/datasets/light_microscopy/orgline.py +282 -0
- torch_em/data/datasets/light_microscopy/parhyale_regen.py +140 -0
- torch_em/data/datasets/light_microscopy/phmamm.py +154 -0
- torch_em/data/datasets/light_microscopy/plantseg.py +244 -0
- torch_em/data/datasets/light_microscopy/pnas_arabidopsis.py +140 -0
- torch_em/data/datasets/light_microscopy/scaffold_a549.py +171 -0
- torch_em/data/datasets/light_microscopy/segpc.py +175 -0
- torch_em/data/datasets/light_microscopy/slimia.py +235 -0
- torch_em/data/datasets/light_microscopy/spheroids_hepg2.py +139 -0
- torch_em/data/datasets/light_microscopy/synthmt.py +191 -0
- torch_em/data/datasets/light_microscopy/tissuenet.py +192 -0
- torch_em/data/datasets/light_microscopy/toiam.py +111 -0
- torch_em/data/datasets/light_microscopy/u20s.py +159 -0
- torch_em/data/datasets/light_microscopy/usiigaci.py +124 -0
- torch_em/data/datasets/light_microscopy/vgg_hela.py +160 -0
- torch_em/data/datasets/light_microscopy/vibrio_cholerae.py +137 -0
- torch_em/data/datasets/light_microscopy/vicar.py +152 -0
- torch_em/data/datasets/light_microscopy/wing_disc.py +198 -0
- torch_em/data/datasets/light_microscopy/xpress.py +149 -0
- torch_em/data/datasets/light_microscopy/yeastcellseg.py +187 -0
- torch_em/data/datasets/light_microscopy/yeastms.py +196 -0
- torch_em/data/datasets/light_microscopy/yeastsam.py +131 -0
- torch_em/data/datasets/light_microscopy/yeaz.py +181 -0
- torch_em/data/datasets/medical/__init__.py +60 -0
- torch_em/data/datasets/medical/abus.py +165 -0
- torch_em/data/datasets/medical/acdc.py +162 -0
- torch_em/data/datasets/medical/acouslic_ai.py +125 -0
- torch_em/data/datasets/medical/amd_sd.py +190 -0
- torch_em/data/datasets/medical/amos.py +179 -0
- torch_em/data/datasets/medical/autopet.py +152 -0
- torch_em/data/datasets/medical/btcv.py +229 -0
- torch_em/data/datasets/medical/busi.py +142 -0
- torch_em/data/datasets/medical/camus.py +141 -0
- torch_em/data/datasets/medical/cbis_ddsm.py +195 -0
- torch_em/data/datasets/medical/chaos.py +236 -0
- torch_em/data/datasets/medical/cholecseg8k.py +203 -0
- torch_em/data/datasets/medical/covid19_seg.py +171 -0
- torch_em/data/datasets/medical/covid_qu_ex.py +190 -0
- torch_em/data/datasets/medical/ct_cadaiver.py +131 -0
- torch_em/data/datasets/medical/curvas.py +194 -0
- torch_em/data/datasets/medical/dca1.py +149 -0
- torch_em/data/datasets/medical/drive.py +176 -0
- torch_em/data/datasets/medical/dsad.py +154 -0
- torch_em/data/datasets/medical/duke_liver.py +208 -0
- torch_em/data/datasets/medical/feta24.py +144 -0
- torch_em/data/datasets/medical/fundus_avseg.py +180 -0
- torch_em/data/datasets/medical/han_seg.py +166 -0
- torch_em/data/datasets/medical/hil_toothseg.py +174 -0
- torch_em/data/datasets/medical/idrid.py +164 -0
- torch_em/data/datasets/medical/ircadb.py +201 -0
- torch_em/data/datasets/medical/isic.py +190 -0
- torch_em/data/datasets/medical/isles.py +141 -0
- torch_em/data/datasets/medical/jnuifm.py +122 -0
- torch_em/data/datasets/medical/jsrt.py +179 -0
- torch_em/data/datasets/medical/kits.py +303 -0
- torch_em/data/datasets/medical/kvasir.py +147 -0
- torch_em/data/datasets/medical/leg_3d_us.py +190 -0
- torch_em/data/datasets/medical/lgg_mri.py +183 -0
- torch_em/data/datasets/medical/m2caiseg.py +213 -0
- torch_em/data/datasets/medical/mbh_seg.py +125 -0
- torch_em/data/datasets/medical/mice_tumseg.py +157 -0
- torch_em/data/datasets/medical/micro_usp.py +141 -0
- torch_em/data/datasets/medical/montgomery.py +155 -0
- torch_em/data/datasets/medical/motum.py +161 -0
- torch_em/data/datasets/medical/msd.py +180 -0
- torch_em/data/datasets/medical/oasis.py +162 -0
- torch_em/data/datasets/medical/oimhs.py +205 -0
- torch_em/data/datasets/medical/osic_pulmofib.py +234 -0
- torch_em/data/datasets/medical/palm.py +174 -0
- torch_em/data/datasets/medical/panorama.py +193 -0
- torch_em/data/datasets/medical/papila.py +205 -0
- torch_em/data/datasets/medical/pengwin.py +168 -0
- torch_em/data/datasets/medical/piccolo.py +149 -0
- torch_em/data/datasets/medical/plethora.py +238 -0
- torch_em/data/datasets/medical/psfhs.py +148 -0
- torch_em/data/datasets/medical/ravir.py +133 -0
- torch_em/data/datasets/medical/sa_med2d.py +431 -0
- torch_em/data/datasets/medical/sega.py +201 -0
- torch_em/data/datasets/medical/segthy.py +204 -0
- torch_em/data/datasets/medical/siim_acr.py +179 -0
- torch_em/data/datasets/medical/spider.py +142 -0
- torch_em/data/datasets/medical/toothfairy.py +223 -0
- torch_em/data/datasets/medical/uwaterloo_skin.py +161 -0
- torch_em/data/datasets/medical/verse.py +142 -0
- torch_em/data/datasets/util.py +593 -0
- torch_em/data/image_collection_dataset.py +264 -0
- torch_em/data/pseudo_label_dataset.py +86 -0
- torch_em/data/raw_dataset.py +383 -0
- torch_em/data/raw_image_collection_dataset.py +187 -0
- torch_em/data/sampler.py +233 -0
- torch_em/data/segmentation_dataset.py +287 -0
- torch_em/data/tensor_dataset.py +90 -0
- torch_em/loss/__init__.py +15 -0
- torch_em/loss/affinity_side_loss.py +172 -0
- torch_em/loss/cldice.py +216 -0
- torch_em/loss/combined_loss.py +38 -0
- torch_em/loss/contrastive.py +169 -0
- torch_em/loss/contrastive_impl.py +272 -0
- torch_em/loss/dice.py +256 -0
- torch_em/loss/distance_based.py +69 -0
- torch_em/loss/spoco_loss.py +643 -0
- torch_em/loss/wrapper.py +183 -0
- torch_em/metric/__init__.py +8 -0
- torch_em/metric/cldice.py +63 -0
- torch_em/metric/instance_segmentation_metric.py +483 -0
- torch_em/model/__init__.py +8 -0
- torch_em/model/probabilistic_unet.py +475 -0
- torch_em/model/resnet3d.py +518 -0
- torch_em/model/unet.py +728 -0
- torch_em/model/unetr.py +1018 -0
- torch_em/model/vim.py +267 -0
- torch_em/model/vit.py +1017 -0
- torch_em/multi_gpu_training.py +190 -0
- torch_em/segmentation.py +574 -0
- torch_em/self_training/__init__.py +13 -0
- torch_em/self_training/augmentations.py +4 -0
- torch_em/self_training/fix_match.py +689 -0
- torch_em/self_training/logger.py +404 -0
- torch_em/self_training/loss.py +406 -0
- torch_em/self_training/mean_teacher.py +723 -0
- torch_em/self_training/probabilistic_unet_trainer.py +117 -0
- torch_em/self_training/pseudo_labeling.py +397 -0
- torch_em/self_training/uni_match_v2.py +435 -0
- torch_em/shallow2deep/__init__.py +8 -0
- torch_em/shallow2deep/prepare_shallow2deep.py +880 -0
- torch_em/shallow2deep/pseudolabel_training.py +162 -0
- torch_em/shallow2deep/shallow2deep_dataset.py +403 -0
- torch_em/shallow2deep/shallow2deep_eval.py +231 -0
- torch_em/shallow2deep/shallow2deep_model.py +205 -0
- torch_em/shallow2deep/transform.py +86 -0
- torch_em/trainer/__init__.py +3 -0
- torch_em/trainer/default_trainer.py +852 -0
- torch_em/trainer/flashoptim_trainer.py +111 -0
- torch_em/trainer/logger_base.py +18 -0
- torch_em/trainer/spoco_trainer.py +152 -0
- torch_em/trainer/tensorboard_logger.py +162 -0
- torch_em/trainer/wandb_logger.py +118 -0
- torch_em/transform/__init__.py +8 -0
- torch_em/transform/augmentation.py +302 -0
- torch_em/transform/defect.py +245 -0
- torch_em/transform/generic.py +235 -0
- torch_em/transform/invertible_augmentations.py +199 -0
- torch_em/transform/label.py +633 -0
- torch_em/transform/nnunet_raw.py +96 -0
- torch_em/transform/raw.py +389 -0
- torch_em/util/__init__.py +21 -0
- torch_em/util/debug.py +178 -0
- torch_em/util/grid_search.py +232 -0
- torch_em/util/image.py +115 -0
- torch_em/util/modelzoo.py +893 -0
- torch_em/util/prediction.py +320 -0
- torch_em/util/reporting.py +77 -0
- torch_em/util/segmentation.py +233 -0
- torch_em/util/submit_slurm.py +103 -0
- torch_em/util/test.py +59 -0
- torch_em/util/training.py +19 -0
- torch_em/util/util.py +486 -0
- torch_em/util/validation.py +265 -0
- torch_em-0.9.0.dist-info/METADATA +27 -0
- torch_em-0.9.0.dist-info/RECORD +303 -0
- torch_em-0.9.0.dist-info/WHEEL +5 -0
- torch_em-0.9.0.dist-info/entry_points.txt +7 -0
- torch_em-0.9.0.dist-info/licenses/LICENSE +21 -0
- torch_em-0.9.0.dist-info/top_level.txt +1 -0
torch_em/__init__.py
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"""
|
|
2
|
+
.. include:: ../doc/start_page.md
|
|
3
|
+
.. include:: ../doc/datasets_and_dataloaders.md
|
|
4
|
+
"""
|
|
5
|
+
from .segmentation import (
|
|
6
|
+
default_segmentation_dataset,
|
|
7
|
+
default_segmentation_loader,
|
|
8
|
+
default_segmentation_trainer,
|
|
9
|
+
get_data_loader,
|
|
10
|
+
)
|
|
11
|
+
from .__version__ import __version__
|
torch_em/__version__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.9.0"
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
"""Dataset, DataLoader and Trainer implementations for image classification tasks.
|
|
2
|
+
"""
|
|
3
|
+
from .classification import default_classification_trainer, default_classification_loader
|
|
4
|
+
|
|
5
|
+
from .classification_logger import ClassificationLogger
|
|
6
|
+
from .classification_trainer import ClassificationTrainer
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
from functools import partial
|
|
2
|
+
from typing import Optional, Sequence, Tuple, Union
|
|
3
|
+
|
|
4
|
+
import numpy as np
|
|
5
|
+
import sklearn.metrics as metrics
|
|
6
|
+
import torch
|
|
7
|
+
import torch_em
|
|
8
|
+
from numpy.typing import ArrayLike
|
|
9
|
+
|
|
10
|
+
from .classification_dataset import ClassificationDataset
|
|
11
|
+
from .classification_logger import ClassificationLogger
|
|
12
|
+
from .classification_trainer import ClassificationTrainer
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class ClassificationMetric:
|
|
16
|
+
"""Metric for classification training.
|
|
17
|
+
|
|
18
|
+
Args:
|
|
19
|
+
metric_name: The name of the metrics. The name will be looked up in `sklearn.metrics`,
|
|
20
|
+
so it must be a valid identifier in that python package.
|
|
21
|
+
metric_kwargs: Keyword arguments for the metric.
|
|
22
|
+
"""
|
|
23
|
+
def __init__(self, metric_name: str = "accuracy_score", **metric_kwargs):
|
|
24
|
+
if not hasattr(metrics, metric_name):
|
|
25
|
+
raise ValueError(f"Invalid metric_name {metric_name}.")
|
|
26
|
+
self.metric = getattr(metrics, metric_name)
|
|
27
|
+
self.metric_kwargs = metric_kwargs
|
|
28
|
+
|
|
29
|
+
def __call__(self, y_true: np.ndarray, y_pred: np.ndarray) -> float:
|
|
30
|
+
"""Evaluate model prediction against classification labels.
|
|
31
|
+
|
|
32
|
+
Args:
|
|
33
|
+
y_true: The classification labels.
|
|
34
|
+
y_pred: The model predictions.
|
|
35
|
+
|
|
36
|
+
Returns:
|
|
37
|
+
The metric value.
|
|
38
|
+
"""
|
|
39
|
+
metric_error = 1.0 - self.metric(y_true, y_pred, **self.metric_kwargs)
|
|
40
|
+
return metric_error
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def default_classification_loader(
|
|
44
|
+
data: Sequence[ArrayLike],
|
|
45
|
+
target: Sequence[ArrayLike],
|
|
46
|
+
batch_size: int,
|
|
47
|
+
normalization: Optional[callable] = None,
|
|
48
|
+
augmentation: Optional[callable] = None,
|
|
49
|
+
image_shape: Optional[Tuple[int, ...]] = None,
|
|
50
|
+
**loader_kwargs,
|
|
51
|
+
) -> torch.utils.data.DataLoader:
|
|
52
|
+
"""Get a data loader for classification training.
|
|
53
|
+
|
|
54
|
+
Args:
|
|
55
|
+
data: The input data for classification. Expects a sequence of array-like data.
|
|
56
|
+
The data can be two or three dimensional.
|
|
57
|
+
target: The target data for classification. Expects a sequence of the same length as `data`.
|
|
58
|
+
Each value in the sequence must be a scalar.
|
|
59
|
+
batch_size: The batch size for the data loader.
|
|
60
|
+
normalization: The normalization function. If None, data standardization will be used.
|
|
61
|
+
augmentation: The augmentation function. If None, the default augmentations will be used.
|
|
62
|
+
image_shape: The target shape of the data. If given, each sample will be resampled to this size.
|
|
63
|
+
loader_kwargs: Additional keyword arguments for `torch.utils.data.DataLoader`.
|
|
64
|
+
|
|
65
|
+
Returns:
|
|
66
|
+
The data loader.
|
|
67
|
+
"""
|
|
68
|
+
ndim = data[0].ndim - 1
|
|
69
|
+
if ndim not in (2, 3):
|
|
70
|
+
raise ValueError(f"Expect input data of dimensionality 2 or 3, got {ndim}")
|
|
71
|
+
|
|
72
|
+
if normalization is None:
|
|
73
|
+
axis = (1, 2) if ndim == 2 else (1, 2, 3)
|
|
74
|
+
normalization = partial(torch_em.transform.raw.standardize, axis=axis)
|
|
75
|
+
|
|
76
|
+
if augmentation is None:
|
|
77
|
+
augmentation = torch_em.transform.get_augmentations(ndim=ndim)
|
|
78
|
+
|
|
79
|
+
dataset = ClassificationDataset(data, target, normalization, augmentation, image_shape)
|
|
80
|
+
loader = torch_em.segmentation.get_data_loader(dataset, batch_size, **loader_kwargs)
|
|
81
|
+
return loader
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def default_classification_trainer(
|
|
85
|
+
name: str,
|
|
86
|
+
model: torch.nn.Module,
|
|
87
|
+
train_loader: torch.utils.data.DataLoader,
|
|
88
|
+
val_loader: torch.utils.data.DataLoader,
|
|
89
|
+
loss: Optional[Union[torch.nn.Module, callable]] = None,
|
|
90
|
+
metric: Optional[Union[torch.nn.Module, callable]] = None,
|
|
91
|
+
logger=ClassificationLogger,
|
|
92
|
+
trainer_class=ClassificationTrainer,
|
|
93
|
+
**kwargs,
|
|
94
|
+
):
|
|
95
|
+
"""Get a trainer for a classification task.
|
|
96
|
+
|
|
97
|
+
This will create an instance of `torch_em.classification.ClassificationTrainer`.
|
|
98
|
+
Check out its documentation string for details on how to configure and use the trainer.
|
|
99
|
+
|
|
100
|
+
Args:
|
|
101
|
+
name: The name for the checkpoint created by the trainer.
|
|
102
|
+
model: The classification model to train.
|
|
103
|
+
train_loader: The data loader for training.
|
|
104
|
+
val_loader: The data loader for validation.
|
|
105
|
+
loss: The loss function. If None, will use cross entropy.
|
|
106
|
+
metric: The metric function. If None, will use the accuracy error.
|
|
107
|
+
logger: The logger for keeping track of the training progress.
|
|
108
|
+
trainer_class: The trainer class.
|
|
109
|
+
kwargs: Keyword arguments for the trainer class.
|
|
110
|
+
|
|
111
|
+
Returns:
|
|
112
|
+
The classification trainer.
|
|
113
|
+
"""
|
|
114
|
+
# Set the default loss and metric (if no values where passed).
|
|
115
|
+
loss = torch.nn.CrossEntropyLoss() if loss is None else loss
|
|
116
|
+
metric = ClassificationMetric() if metric is None else metric
|
|
117
|
+
|
|
118
|
+
# Metric: Note that we use lower metric = better.
|
|
119
|
+
# So we record the accuracy error instead of the accuracy..
|
|
120
|
+
trainer = torch_em.default_segmentation_trainer(
|
|
121
|
+
name, model, train_loader, val_loader,
|
|
122
|
+
loss=loss, metric=metric,
|
|
123
|
+
logger=logger, trainer_class=trainer_class,
|
|
124
|
+
**kwargs,
|
|
125
|
+
)
|
|
126
|
+
return trainer
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
from typing import Sequence, Tuple
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
import torch
|
|
5
|
+
|
|
6
|
+
from numpy.typing import ArrayLike
|
|
7
|
+
from skimage.transform import resize
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class ClassificationDataset(torch.utils.data.Dataset):
|
|
11
|
+
"""Dataset for classification training.
|
|
12
|
+
|
|
13
|
+
Args:
|
|
14
|
+
data: The input data for classification. Expects a sequence of array-like data.
|
|
15
|
+
The data can be two or three dimensional.
|
|
16
|
+
target: The target data for classification. Expects a sequence of the same length as `data`.
|
|
17
|
+
Each value in the sequence must be a scalar.
|
|
18
|
+
normalization: The normalization function.
|
|
19
|
+
augmentation: The augmentation function.
|
|
20
|
+
image_shape: The target shape of the data. If given, each sample will be resampled to this size.
|
|
21
|
+
"""
|
|
22
|
+
def __init__(
|
|
23
|
+
self,
|
|
24
|
+
data: Sequence[ArrayLike],
|
|
25
|
+
target: Sequence[ArrayLike],
|
|
26
|
+
normalization: callable,
|
|
27
|
+
augmentation: callable,
|
|
28
|
+
image_shape: Tuple[int, ...],
|
|
29
|
+
):
|
|
30
|
+
if len(data) != len(target):
|
|
31
|
+
raise ValueError(f"Length of data and target don't agree: {len(data)} != {len(target)}")
|
|
32
|
+
self.data = data
|
|
33
|
+
self.target = target
|
|
34
|
+
self.normalization = normalization
|
|
35
|
+
self.augmentation = augmentation
|
|
36
|
+
self.image_shape = image_shape
|
|
37
|
+
|
|
38
|
+
def __len__(self):
|
|
39
|
+
return len(self.data)
|
|
40
|
+
|
|
41
|
+
def resize(self, x):
|
|
42
|
+
"""@private
|
|
43
|
+
"""
|
|
44
|
+
out = [resize(channel, self.image_shape, preserve_range=True)[None] for channel in x]
|
|
45
|
+
return np.concatenate(out, axis=0)
|
|
46
|
+
|
|
47
|
+
def __getitem__(self, index):
|
|
48
|
+
x, y = self.data[index], self.target[index]
|
|
49
|
+
|
|
50
|
+
# apply normalization
|
|
51
|
+
if self.normalization is not None:
|
|
52
|
+
x = self.normalization(x)
|
|
53
|
+
|
|
54
|
+
# resize to sample shape if it was given
|
|
55
|
+
if self.image_shape is not None:
|
|
56
|
+
x = self.resize(x)
|
|
57
|
+
|
|
58
|
+
# apply augmentations (if any)
|
|
59
|
+
if self.augmentation is not None:
|
|
60
|
+
_shape = x.shape
|
|
61
|
+
# adds unwanted batch axis
|
|
62
|
+
x = self.augmentation(x)[0][0]
|
|
63
|
+
assert x.shape == _shape
|
|
64
|
+
|
|
65
|
+
return x, y
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import os
|
|
2
|
+
|
|
3
|
+
import matplotlib.pyplot as plt
|
|
4
|
+
import numpy as np
|
|
5
|
+
|
|
6
|
+
from matplotlib.backends.backend_agg import FigureCanvasAgg
|
|
7
|
+
from sklearn.metrics import ConfusionMatrixDisplay
|
|
8
|
+
from torch.utils.tensorboard import SummaryWriter
|
|
9
|
+
from torch_em.trainer.logger_base import TorchEmLogger
|
|
10
|
+
from torch_em.transform.raw import normalize
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def confusion_matrix(y_true, y_pred, class_labels=None, title=None, save_path=None, **plot_kwargs):
|
|
14
|
+
"""@private
|
|
15
|
+
"""
|
|
16
|
+
fig, ax = plt.subplots(1)
|
|
17
|
+
|
|
18
|
+
if save_path is None:
|
|
19
|
+
canvas = FigureCanvasAgg(fig)
|
|
20
|
+
|
|
21
|
+
disp = ConfusionMatrixDisplay.from_predictions(
|
|
22
|
+
y_true, y_pred, normalize="true", display_labels=class_labels
|
|
23
|
+
)
|
|
24
|
+
disp.plot(ax=ax, **plot_kwargs)
|
|
25
|
+
|
|
26
|
+
if title is not None:
|
|
27
|
+
ax.set_title(title)
|
|
28
|
+
if save_path is not None:
|
|
29
|
+
plt.savefig(save_path)
|
|
30
|
+
return
|
|
31
|
+
|
|
32
|
+
canvas.draw()
|
|
33
|
+
image = np.asarray(canvas.buffer_rgba())[..., :3]
|
|
34
|
+
image = image.transpose((2, 0, 1))
|
|
35
|
+
plt.close()
|
|
36
|
+
return image
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def make_grid(images, target=None, prediction=None, images_per_row=8, **kwargs):
|
|
40
|
+
"""@private
|
|
41
|
+
"""
|
|
42
|
+
assert images.ndim in (4, 5)
|
|
43
|
+
assert images.shape[1] in (1, 3), f"{images.shape}"
|
|
44
|
+
|
|
45
|
+
if images.ndim == 5:
|
|
46
|
+
is_3d = True
|
|
47
|
+
z = images.shape[2] // 2
|
|
48
|
+
else:
|
|
49
|
+
is_3d = False
|
|
50
|
+
|
|
51
|
+
n_images = images.shape[0]
|
|
52
|
+
n_rows = n_images // images_per_row
|
|
53
|
+
if n_images % images_per_row != 0:
|
|
54
|
+
n_rows += 1
|
|
55
|
+
|
|
56
|
+
images = images.detach().cpu().numpy()
|
|
57
|
+
if target is not None:
|
|
58
|
+
target = target.detach().cpu().numpy()
|
|
59
|
+
if prediction is not None:
|
|
60
|
+
prediction = prediction.max(1)[1].detach().cpu().numpy()
|
|
61
|
+
|
|
62
|
+
fig, axes = plt.subplots(n_rows, images_per_row)
|
|
63
|
+
canvas = FigureCanvasAgg(fig)
|
|
64
|
+
for r in range(n_rows):
|
|
65
|
+
for c in range(images_per_row):
|
|
66
|
+
i = r * images_per_row + c
|
|
67
|
+
if i == len(images):
|
|
68
|
+
break
|
|
69
|
+
ax = axes[r, c] if n_rows > 1 else axes[r]
|
|
70
|
+
ax.set_axis_off()
|
|
71
|
+
im = images[i, :, z] if is_3d else images[i]
|
|
72
|
+
im = im.transpose((1, 2, 0))
|
|
73
|
+
im = normalize(im, axis=(0, 1))
|
|
74
|
+
if im.shape[-1] == 3: # rgb
|
|
75
|
+
ax.imshow(im)
|
|
76
|
+
else:
|
|
77
|
+
ax.imshow(im[..., 0], cmap="gray")
|
|
78
|
+
|
|
79
|
+
if target is None and prediction is None:
|
|
80
|
+
continue
|
|
81
|
+
|
|
82
|
+
# TODO get the class name, and if we have both target
|
|
83
|
+
# and prediction check whether they agree or not and do stuff
|
|
84
|
+
title = ""
|
|
85
|
+
if target is not None:
|
|
86
|
+
title += f"t: {target[i]} "
|
|
87
|
+
if prediction is not None:
|
|
88
|
+
title += f"p: {prediction[i]}"
|
|
89
|
+
ax.set_title(title, fontsize=8)
|
|
90
|
+
|
|
91
|
+
canvas.draw()
|
|
92
|
+
image = np.asarray(canvas.buffer_rgba())[..., :3]
|
|
93
|
+
image = image.transpose((2, 0, 1))
|
|
94
|
+
plt.close()
|
|
95
|
+
return image
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
class ClassificationLogger(TorchEmLogger):
|
|
99
|
+
"""Logger for classification trainer.
|
|
100
|
+
|
|
101
|
+
Args:
|
|
102
|
+
trainer: The trainer instance.
|
|
103
|
+
save_root: Root folder for saving the checkpoints and logs.
|
|
104
|
+
"""
|
|
105
|
+
def __init__(self, trainer, save_root: str, **unused_kwargs):
|
|
106
|
+
super().__init__(trainer, save_root)
|
|
107
|
+
self.log_dir = f"./logs/{trainer.name}" if save_root is None else\
|
|
108
|
+
os.path.join(save_root, "logs", trainer.name)
|
|
109
|
+
os.makedirs(self.log_dir, exist_ok=True)
|
|
110
|
+
|
|
111
|
+
self.tb = SummaryWriter(self.log_dir)
|
|
112
|
+
self.log_image_interval = trainer.log_image_interval
|
|
113
|
+
|
|
114
|
+
def add_image(self, x, y, pred, name, step):
|
|
115
|
+
"""@private
|
|
116
|
+
"""
|
|
117
|
+
scale_each = False
|
|
118
|
+
grid = make_grid(x, y, pred, padding=4, normalize=True, scale_each=scale_each)
|
|
119
|
+
self.tb.add_image(tag=f"{name}/images_and_predictions", img_tensor=grid, global_step=step)
|
|
120
|
+
|
|
121
|
+
def log_train(self, step, loss, lr, x, y, prediction, log_gradients=False):
|
|
122
|
+
"""@private
|
|
123
|
+
"""
|
|
124
|
+
self.tb.add_scalar(tag="train/loss", scalar_value=loss, global_step=step)
|
|
125
|
+
self.tb.add_scalar(tag="train/learning_rate", scalar_value=lr, global_step=step)
|
|
126
|
+
if step % self.log_image_interval == 0:
|
|
127
|
+
self.add_image(x, y, prediction, "train", step)
|
|
128
|
+
|
|
129
|
+
def log_validation(self, step, metric, loss, x, y, prediction, y_true=None, y_pred=None):
|
|
130
|
+
"""@private
|
|
131
|
+
"""
|
|
132
|
+
self.tb.add_scalar(tag="validation/loss", scalar_value=loss, global_step=step)
|
|
133
|
+
self.tb.add_scalar(tag="validation/metric", scalar_value=metric, global_step=step)
|
|
134
|
+
self.add_image(x, y, prediction, "validation", step)
|
|
135
|
+
if y_true is not None and y_pred is not None:
|
|
136
|
+
cm = confusion_matrix(y_true, y_pred)
|
|
137
|
+
self.tb.add_image(tag="validation/confusion_matrix", img_tensor=cm, global_step=step)
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import warnings
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
import torch
|
|
5
|
+
import torch_em
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class ClassificationTrainer(torch_em.trainer.DefaultTrainer):
|
|
9
|
+
"""Trainer for classification tasks.
|
|
10
|
+
|
|
11
|
+
This class inherits from `torch_em.trainer.DefaultTrainer` with minor changes for
|
|
12
|
+
classification instead of segmentation training. Check out the documentation of
|
|
13
|
+
the default trainer class for details on how to configure and use the trainer
|
|
14
|
+
"""
|
|
15
|
+
def _validate_impl(self, forward_context):
|
|
16
|
+
self.model.eval()
|
|
17
|
+
|
|
18
|
+
loss_val = 0.0
|
|
19
|
+
|
|
20
|
+
# we use the syntax from sklearn.metrics to compute metrics
|
|
21
|
+
# over all the preditions
|
|
22
|
+
y_true, y_pred = [], []
|
|
23
|
+
|
|
24
|
+
with torch.no_grad():
|
|
25
|
+
for x, y in self.val_loader:
|
|
26
|
+
x, y = x.to(self.device), y.to(self.device)
|
|
27
|
+
with forward_context():
|
|
28
|
+
pred, loss = self._forward_and_loss(x, y)
|
|
29
|
+
loss_val += loss.item()
|
|
30
|
+
y_true.append(y.detach().cpu().numpy())
|
|
31
|
+
y_pred.append(pred.max(1)[1].detach().cpu().numpy())
|
|
32
|
+
|
|
33
|
+
if torch.isnan(pred).any():
|
|
34
|
+
warnings.warn("Predictions are NaN")
|
|
35
|
+
loss_val /= len(self.val_loader)
|
|
36
|
+
|
|
37
|
+
y_true, y_pred = np.concatenate(y_true), np.concatenate(y_pred)
|
|
38
|
+
metric_val = self.metric(y_true, y_pred)
|
|
39
|
+
|
|
40
|
+
if self.logger is not None:
|
|
41
|
+
self.logger.log_validation(self._iteration, metric_val, loss_val, x, y, pred, y_true, y_pred)
|
|
42
|
+
return metric_val
|