diff --git a/config/augmentations_test/linear-roads-bin-seg-test.yaml b/config/augmentations_test/linear-roads-bin-seg-test.yaml new file mode 100644 index 00000000..40165824 --- /dev/null +++ b/config/augmentations_test/linear-roads-bin-seg-test.yaml @@ -0,0 +1,9 @@ +defaults: + - /augmentations/preprocessing: + - standard_images + - /augmentations/combined: + - none + - /augmentations/position: + - none + - /augmentations/color: + - /augmentations/postprocessing: diff --git a/config/augmentations_train/linear-roads-bin-seg-test.yaml b/config/augmentations_train/linear-roads-bin-seg-test.yaml new file mode 100644 index 00000000..19c5a391 --- /dev/null +++ b/config/augmentations_train/linear-roads-bin-seg-test.yaml @@ -0,0 +1,15 @@ +defaults: + - /augmentations/preprocessing: + - standard_images + - /augmentations/combined: + - none + - /augmentations/position: + - flip + - rotate90 + - transpose + - grid_distortion + - /augmentations/color: + - random_gamma + - blur + - /augmentations/postprocessing: + - to_tensor \ No newline at end of file diff --git a/config/augmentations_val/linear-roads-bin-seg-test.yaml b/config/augmentations_val/linear-roads-bin-seg-test.yaml new file mode 100644 index 00000000..40165824 --- /dev/null +++ b/config/augmentations_val/linear-roads-bin-seg-test.yaml @@ -0,0 +1,9 @@ +defaults: + - /augmentations/preprocessing: + - standard_images + - /augmentations/combined: + - none + - /augmentations/position: + - none + - /augmentations/color: + - /augmentations/postprocessing: diff --git a/config/datasets/regression/table_regression.yaml b/config/datasets/regression/table_regression.yaml index b04549c4..93844d4e 100644 --- a/config/datasets/regression/table_regression.yaml +++ b/config/datasets/regression/table_regression.yaml @@ -1,3 +1,4 @@ +_target_: innofw.core.datamodules.pandas_datamodules.RegressionPandasDataModule target_col: Index train: source: tests/data/tabular/bmi/bmi_prep.csv diff --git a/config/datasets/semantic-segmentation/segmentation_arable.yaml b/config/datasets/semantic-segmentation/segmentation_arable.yaml index e9252633..ff148fbb 100755 --- a/config/datasets/semantic-segmentation/segmentation_arable.yaml +++ b/config/datasets/semantic-segmentation/segmentation_arable.yaml @@ -1,3 +1,4 @@ +_target_: innofw.core.datamodules.lightning_datamodules.segmentation_dir.DirSegmentationLightningDataModule task: - image-segmentation diff --git a/config/datasets/semantic-segmentation/segmentation_arable_full.yaml b/config/datasets/semantic-segmentation/segmentation_arable_full.yaml index ee95a0b3..8d9ea3b0 100755 --- a/config/datasets/semantic-segmentation/segmentation_arable_full.yaml +++ b/config/datasets/semantic-segmentation/segmentation_arable_full.yaml @@ -1,3 +1,4 @@ +_target_: innofw.core.datamodules.lightning_datamodules.segmentation_dir.DicomDirSegmentationLightningDataModule task: - image-segmentation diff --git a/config/datasets/semantic-segmentation/segmentation_landslides.yaml b/config/datasets/semantic-segmentation/segmentation_landslides.yaml index 25266c08..c02cdc5a 100755 --- a/config/datasets/semantic-segmentation/segmentation_landslides.yaml +++ b/config/datasets/semantic-segmentation/segmentation_landslides.yaml @@ -1,3 +1,4 @@ +_target_: innofw.core.datamodules.lightning_datamodules.HDF5LightningDataModule task: - image-segmentation diff --git a/config/datasets/token_classification_drugprot.yaml b/config/datasets/token_classification_drugprot.yaml index 374a1c2f..96188426 100644 --- a/config/datasets/token_classification_drugprot.yaml +++ b/config/datasets/token_classification_drugprot.yaml @@ -1,4 +1,4 @@ -task: +task: - text-ner name: Drugprot diff --git a/config/datasets/water_erosion_segmentation.yaml b/config/datasets/water_erosion_segmentation.yaml index 09f93049..84fbbfeb 100644 --- a/config/datasets/water_erosion_segmentation.yaml +++ b/config/datasets/water_erosion_segmentation.yaml @@ -1,3 +1,4 @@ +_target_: innofw.core.datamodules.lightning_datamodules.semantic_segmentation.tiff.SegmentationDM task: - image-segmentation diff --git a/config/experiments/classification/DS_190423_8dca23dc_ucmerced.yaml b/config/experiments/classification/DS_190423_8dca23dc_ucmerced.yaml index 9e7670e7..fc65bdda 100644 --- a/config/experiments/classification/DS_190423_8dca23dc_ucmerced.yaml +++ b/config/experiments/classification/DS_190423_8dca23dc_ucmerced.yaml @@ -22,7 +22,7 @@ models: project: "ucmerced_classification" task: "image-classification" random_seed: 42 -original_work_dir: ${hydra:runtime.cwd} +# original_work_dir: ${hydra:runtime.cwd} weights_freq: 1 batch_size: 256 epochs: 30 diff --git a/config/experiments/classification/NP_010423_concatenated_classification.yaml b/config/experiments/classification/NP_010423_concatenated_classification.yaml index ad114e4d..46fb36cc 100644 --- a/config/experiments/classification/NP_010423_concatenated_classification.yaml +++ b/config/experiments/classification/NP_010423_concatenated_classification.yaml @@ -1,8 +1,9 @@ # @package _global_ defaults: - - /datasets@_dataset_dict.classification_mnist1: classification/classification_mnist - - /datasets@_dataset_dict.classification_mnist2: classification/classification_mnist - - override /models: resnet.yaml + # - /datasets@_dataset_dict.classification_mnist1: classification/classification_mnist + # - /datasets@_dataset_dict.classification_mnist2: classification/classification_mnist + - override /datasets: classification/classification_mnist + - override /models: classification/resnet.yaml - override /augmentations_train: none #classification.yaml - override /augmentations_val: none - override /augmentations_test: none @@ -12,7 +13,7 @@ defaults: project: "mnist_classification" task: "image-classification" random_seed: 42 -original_work_dir: ${hydra:runtime.cwd} +# original_work_dir: ${hydra:runtime.cwd} weights_freq: 1 batch_size: 8 stop_param: 1 diff --git a/config/experiments/classification/SK_010822_jvslrmvk_resnet.yaml b/config/experiments/classification/SK_010822_jvslrmvk_resnet.yaml index 7c7050c1..53020af5 100644 --- a/config/experiments/classification/SK_010822_jvslrmvk_resnet.yaml +++ b/config/experiments/classification/SK_010822_jvslrmvk_resnet.yaml @@ -12,7 +12,7 @@ defaults: project: "mnist_classification" task: "image-classification" random_seed: 42 -original_work_dir: ${hydra:runtime.cwd} +# original_work_dir: ${hydra:runtime.cwd} weights_freq: 1 batch_size: 8 stop_param: 1 diff --git a/config/experiments/detection/KA_031122_invalid_experiment.yaml b/config/experiments/detection/KA_031122_invalid_experiment.yaml index 569c985f..9201fbfa 100644 --- a/config/experiments/detection/KA_031122_invalid_experiment.yaml +++ b/config/experiments/detection/KA_031122_invalid_experiment.yaml @@ -1,6 +1,6 @@ # @package _global_ defaults: - - override /models: linear_regression + - override /models: regression/linear_regression - override /datasets: osl_faces diff --git a/config/experiments/detection/KA_120722_8adfcdaa_yolov5.yaml b/config/experiments/detection/KA_120722_8adfcdaa_yolov5.yaml index ee8120f0..c883b532 100644 --- a/config/experiments/detection/KA_120722_8adfcdaa_yolov5.yaml +++ b/config/experiments/detection/KA_120722_8adfcdaa_yolov5.yaml @@ -1,9 +1,10 @@ # @package _global_ batch_size: 32 defaults: +- override /models: detection/yolov5 - override /datasets: detection_lep - override /optimizers: sgd -- override /models: detection/yolov5x.yaml + epochs: 500 project: lep random_seed: 42 diff --git a/config/experiments/detection/KA_120722_8adfcdaa_yolov5_non_pretrained.yaml b/config/experiments/detection/KA_120722_8adfcdaa_yolov5_non_pretrained.yaml index 937745f1..cfa98114 100644 --- a/config/experiments/detection/KA_120722_8adfcdaa_yolov5_non_pretrained.yaml +++ b/config/experiments/detection/KA_120722_8adfcdaa_yolov5_non_pretrained.yaml @@ -2,11 +2,12 @@ batch_size: 2 ckpt_path: /home/qazybek/pmi_tatneft/innofw/dui/logs/something/20240122-082640/weights/best.pt defaults: -- override /models: detection/yolov5 -- override /datasets: detection_lep.yaml -- override /initializations: random -epochs: 5 -project: lep + - override /models: detection/yolov5 + - override /datasets: detection_lep + - override /initializations: random + +project: "lep" +task: "image-detection" + random_seed: 42 -task: image-detection weights_freq: 1 diff --git a/config/experiments/detection/KA_120722_8adfcdaa_yolov5_pretrained.yaml b/config/experiments/detection/KA_120722_8adfcdaa_yolov5_pretrained.yaml index 34464660..f5c0dca8 100644 --- a/config/experiments/detection/KA_120722_8adfcdaa_yolov5_pretrained.yaml +++ b/config/experiments/detection/KA_120722_8adfcdaa_yolov5_pretrained.yaml @@ -1,7 +1,7 @@ # @package _global_ defaults: - override /models: detection/yolov5 - - override /datasets: detection/lep + - override /datasets: detection_lep project: "lep" diff --git a/config/experiments/detection/KG_190124_wpiig1_ttpla.yaml b/config/experiments/detection/KG_190124_wpiig1_ttpla.yaml index 50763967..4e212bca 100644 --- a/config/experiments/detection/KG_190124_wpiig1_ttpla.yaml +++ b/config/experiments/detection/KG_190124_wpiig1_ttpla.yaml @@ -1,7 +1,7 @@ # @package _global_ defaults: - override /models: detection/yolov5 - - override /datasets: detection/detection_ttpla + - override /datasets: detection/ttpla - override /optimizers: sgd project: "ttpla" diff --git a/config/experiments/detection/NP_210523_yolov8_insects.yaml b/config/experiments/detection/NP_210523_yolov8_insects.yaml index c187e3d4..8b3abf62 100644 --- a/config/experiments/detection/NP_210523_yolov8_insects.yaml +++ b/config/experiments/detection/NP_210523_yolov8_insects.yaml @@ -1,6 +1,7 @@ # @package _global_ defaults: - - override /models: ultralytics + # - override /models: ultralytics + - override /models: detection/yolov8 - override /datasets: detection/detection_ar_tax_or - override /loggers: wandb - override /optimizers: sgd diff --git a/config/experiments/regression/GR_070922_jLqZn8Wh_catboost_regressor_mcl1.yaml b/config/experiments/regression/GR_070922_jLqZn8Wh_catboost_regressor_mcl1.yaml index 6fc91e68..524097e7 100644 --- a/config/experiments/regression/GR_070922_jLqZn8Wh_catboost_regressor_mcl1.yaml +++ b/config/experiments/regression/GR_070922_jLqZn8Wh_catboost_regressor_mcl1.yaml @@ -7,4 +7,4 @@ defaults: project: "mcl1" task: "qsar-regression" random_seed: 42 -original_work_dir: ${hydra:runtime.cwd} +# original_work_dir: ${hydra:runtime.cwd} diff --git a/config/experiments/regression/KA_081122_df82ds8_defaults.yaml b/config/experiments/regression/KA_081122_df82ds8_defaults.yaml index b5596ad8..67f2ad58 100644 --- a/config/experiments/regression/KA_081122_df82ds8_defaults.yaml +++ b/config/experiments/regression/KA_081122_df82ds8_defaults.yaml @@ -1,6 +1,7 @@ # @package _global_ defaults: - - override /datasets: regression_house_prices + - override /datasets: regression/regression_house_prices + - override /models: regression/linear_regression project: "check_defaults" task: "table-regression" diff --git a/config/experiments/regression/KA_280722_ba099979_catboost_regressor.yaml b/config/experiments/regression/KA_280722_ba099979_catboost_regressor.yaml index 6383698b..64e6e1a0 100644 --- a/config/experiments/regression/KA_280722_ba099979_catboost_regressor.yaml +++ b/config/experiments/regression/KA_280722_ba099979_catboost_regressor.yaml @@ -1,7 +1,7 @@ # @package _global_ defaults: - override /models: regression/catboost_regression - - override /datasets: regression_house_prices + - override /datasets: regression/regression_house_prices # - override /callbacks: regression diff --git a/config/experiments/regression/SK_010822_infer_kdsr_linear_regression.yaml b/config/experiments/regression/SK_010822_infer_kdsr_linear_regression.yaml index 82f699b9..956bbd27 100644 --- a/config/experiments/regression/SK_010822_infer_kdsr_linear_regression.yaml +++ b/config/experiments/regression/SK_010822_infer_kdsr_linear_regression.yaml @@ -1,7 +1,7 @@ # @package _global_ defaults: - override /models: regression/linear_regression - - override /datasets: regression_house_prices + - override /datasets: regression/regression_house_prices project: "house_prices" ckpt_path: diff --git a/config/experiments/semantic-segmentation/AS_151123_oks.yaml b/config/experiments/semantic-segmentation/AS_151123_oks.yaml index c2be8a49..6be4439d 100644 --- a/config/experiments/semantic-segmentation/AS_151123_oks.yaml +++ b/config/experiments/semantic-segmentation/AS_151123_oks.yaml @@ -1,8 +1,6 @@ +# @package _global_ accelerator: gpu batch_size: 20 -datasets: -- batch_size: 20 -- num_workers: 42 defaults: - override /models: semantic-segmentation/deeplabv3plus - override /datasets: semantic-segmentation/oks_151123 @@ -13,11 +11,11 @@ defaults: devices: 1 epochs: 1 models: -- in_channels: 3 -- classes: 1 -- encoder_name: resnet101 -- encoder_depth: 5 -- decoder_channels: 256 + in_channels: 3 + classes: 1 + encoder_name: resnet101 + encoder_depth: 5 + decoder_channels: 256 num_workers: 42 project: oks random_seed: 42 diff --git a/config/experiments/semantic-segmentation/DA_230922_sdgh32lk_deeplab.yaml b/config/experiments/semantic-segmentation/DA_230922_sdgh32lk_deeplab.yaml index 385ad2d1..33c5c509 100644 --- a/config/experiments/semantic-segmentation/DA_230922_sdgh32lk_deeplab.yaml +++ b/config/experiments/semantic-segmentation/DA_230922_sdgh32lk_deeplab.yaml @@ -18,6 +18,6 @@ weights_freq: 1 batch_size: 16 epochs: 50 accelerator: gpu -gpus: [2] +gpus: 2 #[2] models: in_channels: 4 diff --git a/config/experiments/semantic-segmentation/DN_051923stroke.yaml b/config/experiments/semantic-segmentation/DN_051923stroke.yaml index 7e0bf8e1..1fad8277 100644 --- a/config/experiments/semantic-segmentation/DN_051923stroke.yaml +++ b/config/experiments/semantic-segmentation/DN_051923stroke.yaml @@ -16,7 +16,7 @@ accelerator: gpu random_seed: 42 epochs: 50 batch_size: 64 -original_work_dir: ${hydra:runtime.cwd} +# original_work_dir: ${hydra:runtime.cwd} weights_freq: 1 override hydra/job_logging: stdout metrics: diff --git a/config/experiments/semantic-segmentation/KA_250123_439erfks_basic_water_erosion_segmentation.yaml b/config/experiments/semantic-segmentation/KA_250123_439erfks_basic_water_erosion_segmentation.yaml index f94180d1..577f3671 100644 --- a/config/experiments/semantic-segmentation/KA_250123_439erfks_basic_water_erosion_segmentation.yaml +++ b/config/experiments/semantic-segmentation/KA_250123_439erfks_basic_water_erosion_segmentation.yaml @@ -1,13 +1,13 @@ # @package _global_ defaults: - override /models: semantic-segmentation/deeplabv3plus.yaml - - override /datasets: segmentation_arable_full.yaml + - override /datasets: semantic-segmentation/segmentation_arable_full.yaml - override /optimizers: adam.yaml - override /losses: segmentation_losses # triple_segmentation_loss - - override /augmentations: segmentation_satellite_imagery + - override /augmentations_train: none #segmentation_satellite_imagery # - override /initializations: he - override /callbacks: segmentation_2 - - override /wandb: qb + - override /wandb: default optimizers: implementations: @@ -24,8 +24,7 @@ models: accelerator: gpu devices: 1 -gpus: - - 1 # use second gpu +gpus: 1 # use second gpu wandb: diff --git a/config/experiments/semantic-segmentation/SK_180822_qwefnew31_yolov5_lungs_seg.yaml b/config/experiments/semantic-segmentation/SK_180822_qwefnew31_yolov5_lungs_seg.yaml index 76dfa8f5..a22b1bea 100644 --- a/config/experiments/semantic-segmentation/SK_180822_qwefnew31_yolov5_lungs_seg.yaml +++ b/config/experiments/semantic-segmentation/SK_180822_qwefnew31_yolov5_lungs_seg.yaml @@ -1,6 +1,6 @@ # @package _global_ defaults: - - override /models: semantic-segmentation/yolov5_segmentation + - override /models: detection/yolov5_segmentation - override /datasets: detection/detection_lungs diff --git a/config/experiments/semantic-segmentation/landslides-kanopus/baseline.yaml b/config/experiments/semantic-segmentation/landslides-kanopus/baseline.yaml index 3de66539..14a54818 100644 --- a/config/experiments/semantic-segmentation/landslides-kanopus/baseline.yaml +++ b/config/experiments/semantic-segmentation/landslides-kanopus/baseline.yaml @@ -5,7 +5,7 @@ defaults: - override /datasets: semantic-segmentation/landslides-kanopus/270223-hdf5 - override /losses: semantic-segmentation/triple_loss_old_pipe - override /optimizers: adam - - override /schedulers: custom_old_pipeline + - override /schedulers: cosine_annealing - override /augmentations_train: landslides-kanopus - override /augmentations_val: landslides-kanopus # - override /metrics: basic diff --git a/config/experiments/semantic-segmentation/water-erosion-kanopus/baseline.yaml b/config/experiments/semantic-segmentation/water-erosion-kanopus/baseline.yaml index 2449babc..a9b12674 100644 --- a/config/experiments/semantic-segmentation/water-erosion-kanopus/baseline.yaml +++ b/config/experiments/semantic-segmentation/water-erosion-kanopus/baseline.yaml @@ -1,11 +1,11 @@ # @package _global_ defaults: - _self_ - - override /models: semantic-segmentationdeeplabv3plus + - override /models: semantic-segmentation/deeplabv3plus - override /datasets: semantic-segmentation/water-erosion-kanopus/220223-hdf5 - override /losses: semantic-segmentation/triple_loss_old_pipe - override /optimizers: adam - - override /schedulers: custom_old_pipeline + - override /schedulers: cosine_annealing - override /augmentations_train: water-erosion-bin-seg - override /augmentations_val: water-erosion-bin-seg # - override /metrics: basic diff --git a/config/schedulers/custom.yaml b/config/schedulers/custom.yaml deleted file mode 100755 index 68c925dc..00000000 --- a/config/schedulers/custom.yaml +++ /dev/null @@ -1,15 +0,0 @@ -task: - - all - -implementations: - torch: - Custom: - function: - _target_: torch.optim.lr_scheduler.LambdaLR - lr_lambda: - _target_: innofw.schedulers.lr_cyclic - decay: 0.01 - decay_step: 1.2 - cyclic_decay: 0.9 - cyclic_len: 50 - last_epoch: -1 diff --git a/config/schedulers/custom_old_pipeline.yaml b/config/schedulers/custom_old_pipeline.yaml deleted file mode 100644 index 26937df5..00000000 --- a/config/schedulers/custom_old_pipeline.yaml +++ /dev/null @@ -1,15 +0,0 @@ -task: - - all - -implementations: - torch: - Custom: - function: - _target_: torch.optim.lr_scheduler.LambdaLR - lr_lambda: - _target_: innofw.schedulers.old_pipeline.lr_cyclic - decay: 0.01 - decay_step: 1.2 - cyclic_decay: 0.9 - cyclic_len: 50 - last_epoch: -1 diff --git a/config/ui.yaml b/config/ui.yaml index 285f0304..3a9259b1 100644 --- a/config/ui.yaml +++ b/config/ui.yaml @@ -31,4 +31,4 @@ accelerator: gpu #devices: 1 gpus: 1 -original_work_dir: ${hydra:runtime.cwd} +# original_work_dir: ${hydra:runtime.cwd} diff --git a/examples/infer_contrasting_ct_dicom_brain.sh b/examples/infer_contrasting_ct_dicom_brain.sh index 831ee7e6..b654d37e 100644 --- a/examples/infer_contrasting_ct_dicom_brain.sh +++ b/examples/infer_contrasting_ct_dicom_brain.sh @@ -1 +1 @@ -python3 /innofw/utils/data_utils/preprocessing/CT_hemorrhage_contrast.py \ No newline at end of file +python3 ./innofw/utils/data_utils/preprocessing/CT_hemorrhage_contrast.py \ No newline at end of file diff --git a/innofw/core/active_learning/datamodule.py b/innofw/core/active_learning/datamodule.py index dedaef09..6198ca98 100644 --- a/innofw/core/active_learning/datamodule.py +++ b/innofw/core/active_learning/datamodule.py @@ -3,6 +3,8 @@ import numpy as np import pandas as pd +from abc import abstractmethod + class DataContainer(TypedDict): @@ -34,14 +36,14 @@ class DataModuleI(Protocol): setup(): returns Nothing. """ - - def test_dataloader(self) -> DataContainer: + @abstractmethod + def test_dataloader(self) -> DataContainer: # pragma: no cover ... - - def train_dataloader(self) -> DataContainer: + @abstractmethod + def train_dataloader(self) -> DataContainer: # pragma: no cover ... - - def setup(self) -> None: + @abstractmethod + def setup(self) -> None: # pragma: no cover ... diff --git a/innofw/core/active_learning/learners/base.py b/innofw/core/active_learning/learners/base.py index 5f891743..e682d97a 100644 --- a/innofw/core/active_learning/learners/base.py +++ b/innofw/core/active_learning/learners/base.py @@ -71,13 +71,13 @@ def run(self, *, ckpt_path): self.active_datamodule.update_indices(most_uncertain) @abstractmethod - def eval_model(self, X, y): - pass + def eval_model(self, X, y): #pragma: no cover + ... @abstractmethod - def predict_model(self, X): - pass + def predict_model(self, X): #pragma: no cover + ... @abstractmethod - def obtain_most_uncertain(self): - pass + def obtain_most_uncertain(self): #pragma: no cover + ... diff --git a/innofw/core/augmentations/base.py b/innofw/core/augmentations/base.py index f8c94a50..6d26bfb6 100755 --- a/innofw/core/augmentations/base.py +++ b/innofw/core/augmentations/base.py @@ -32,10 +32,10 @@ def __init__(self, transforms): self.transforms = transforms @abstractmethod - def forward(self, x): + def forward(self, x): # pragma: no cover pass @staticmethod @abstractmethod - def is_suitable_input(transform) -> bool: + def is_suitable_input(transform) -> bool: # pragma: no cover pass diff --git a/innofw/core/augmentations/multiplicative_noise.py b/innofw/core/augmentations/multiplicative_noise.py deleted file mode 100644 index 63968f96..00000000 --- a/innofw/core/augmentations/multiplicative_noise.py +++ /dev/null @@ -1,32 +0,0 @@ -import albumentations as albu -import numpy as np -from albumentations.augmentations import functional as F - - -class MultiplicativeNoiseSelective(albu.MultiplicativeNoise): - """Multiply image to random number or array of numbers. - Args: - multiplier (float or tuple of floats): If single float image will be multiplied to this number. - If tuple of float multiplier will be in range `[multiplier[0], multiplier[1])`. Default: (0.9, 1.1). - per_channel (bool): If `False`, same values for all channels will be used. - If `True` use sample values for each channels. Default False. - elementwise (bool): If `False` multiply multiply all pixels in an image with a random value sampled once. - If `True` Multiply image pixels with values that are pixelwise randomly sampled. Defaule: False. - Targets: - image - Image types: - Any - """ - - def apply(self, img, multiplier=np.array([1]), **kwargs): - c = img.shape[0] - if np.random.random() < 0.5: - img[: int(c / 2), ...] = F.multiply( - img[: int(c / 2), ...], multiplier - ) - return img - else: - img[int(c / 2) :, ...] = F.multiply( - img[int(c / 2) :, ...], multiplier - ) - return img diff --git a/innofw/core/callbacks/lightning_callbacks/log_predictions_wandb.py b/innofw/core/callbacks/lightning_callbacks/log_predictions_wandb.py index 4c5a4bb7..0de22358 100644 --- a/innofw/core/callbacks/lightning_callbacks/log_predictions_wandb.py +++ b/innofw/core/callbacks/lightning_callbacks/log_predictions_wandb.py @@ -1,198 +1,198 @@ -import logging - -import numpy as np -import pytorch_lightning as pl -import torch -from pytorch_lightning import Callback -from segmentation.constants import SegDataKeys -from segmentation.constants import SegOutKeys - -import wandb - -# -# - - -class SegmentationLogPredictions(Callback): - def __init__(self, logging_interval: int = 5, num_predictions: int = 4): - self.logging_interval = logging_interval - self.num_predictions = num_predictions - - self.different_images = True - - self.train_containers = { - SegDataKeys.image: [], - SegDataKeys.label: [], - SegOutKeys.predictions: [], - } - self.val_containers = { - SegDataKeys.image: [], - SegDataKeys.label: [], - SegOutKeys.predictions: [], - } - self.class_labels = {0: "background", 1: "arable"} - - def setup(self, trainer, pl_module, stage=None, *args, **kwargs): - # 1. init - # a. get number of batches in the set - tdl = trainer.datamodule.train_dataloader() - vdl = trainer.datamodule.val_dataloader() - - self.train_batch_size: int = len(iter(vdl).next()[SegDataKeys.image]) - self.val_batch_size: int = len(iter(vdl).next()[SegDataKeys.image]) - - train_ds_size = len(tdl.dataset) - val_ds_size = len(vdl.dataset) - - self.train_num_predictions = ( - self.num_predictions - if train_ds_size > self.num_predictions - else train_ds_size - ) - self.val_num_predictions = ( - self.num_predictions - if val_ds_size > self.num_predictions - else val_ds_size - ) - - self.train_indices = sorted( - np.random.choice(train_ds_size, self.train_num_predictions) - ) - self.val_indices = sorted( - np.random.choice(val_ds_size, self.val_num_predictions) - ) - - logging.info(f"train indices {self.train_indices}") - logging.info(f"val indices {self.val_indices}") - - def on_stage_batch_end( - self, - indices, - batch_size, - batch, - outputs, - batch_idx, - container, - trainer, - ): - if trainer.current_epoch % self.logging_interval != 0: - return - - for i in indices: - if i >= (batch_idx + 1) * batch_size: - break - - if i >= batch_idx * batch_size: - offset = i - batch_idx * batch_size - prediction = outputs[SegOutKeys.predictions][offset] - image = batch[SegDataKeys.image][offset] - label = batch[SegDataKeys.label][offset] - - container[SegDataKeys.image].append(image) - container[SegDataKeys.label].append(label) - container[SegOutKeys.predictions].append(prediction) - - def on_validation_batch_end( - self, - trainer, - pl_module, - outputs, - batch, - batch_idx, - dataloader_idx=None, - ): - self.on_stage_batch_end( - self.val_indices, - self.val_batch_size, - batch, - outputs, - batch_idx, - self.val_containers, - trainer, - ) - - def on_train_batch_end( - self, - trainer, - pl_module, - outputs, - batch, - batch_idx, - dataloader_idx=None, - ): - self.on_stage_batch_end( - self.train_indices, - self.train_batch_size, - batch, - outputs, - batch_idx, - self.train_containers, - trainer, - ) - - def on_train_epoch_start( - self, trainer: "pl.Trainer", pl_module: "pl.LightningModule" - ) -> None: - self.train_containers = { - SegDataKeys.image: [], - SegDataKeys.label: [], - SegOutKeys.predictions: [], - } - - def on_validation_epoch_start( - self, trainer: "pl.Trainer", pl_module: "pl.LightningModule" - ) -> None: - self.val_containers = { - SegDataKeys.image: [], - SegDataKeys.label: [], - SegOutKeys.predictions: [], - } - - def on_train_epoch_end( - self, trainer: "pl.Trainer", pl_module: "pl.LightningModule" - ) -> None: - self.log_predictions(trainer, "train", self.train_containers) - - def on_validation_epoch_end( - self, trainer: "pl.Trainer", pl_module: "pl.LightningModule" - ) -> None: - self.log_predictions(trainer, "val", self.val_containers) - - def log_predictions(self, trainer, stage, container): - try: - predictions = container[SegOutKeys.predictions] - images = container[SegDataKeys.image] - labels = container[SegDataKeys.label] - - if any(map(lambda x: len(x) == 0, [predictions, images, labels])): - return - - for prediction, label, image in zip( - predictions, labels, images, range(self.num_predictions) - ): - prep_prediction = ( - torch.sigmoid(prediction[0, ...]).detach().cpu().numpy() - > 0.5 - ).astype(np.uint8) - prep_mask = label.cpu().numpy().astype(np.uint8) - - img_pred_mask = wandb.Image( - image, - masks={ - "predictions": { - "mask_data": prep_prediction, - "class_labels": self.class_labels, - }, - "ground_truth": { - "mask_data": prep_mask, - "class_labels": self.class_labels, - }, - }, # .float() - ) - # logger log an image - trainer.logger.experiment.log( - {f"{stage}_images": img_pred_mask} - ) - - except Exception as e: - logging.info(e) +# import logging + +# import numpy as np +# import pytorch_lightning as pl +# import torch +# from pytorch_lightning import Callback +# from segmentation.constants import SegDataKeys +# from segmentation.constants import SegOutKeys + +# import wandb + +# # +# # + + +# class SegmentationLogPredictions(Callback): +# def __init__(self, logging_interval: int = 5, num_predictions: int = 4): +# self.logging_interval = logging_interval +# self.num_predictions = num_predictions + +# self.different_images = True + +# self.train_containers = { +# SegDataKeys.image: [], +# SegDataKeys.label: [], +# SegOutKeys.predictions: [], +# } +# self.val_containers = { +# SegDataKeys.image: [], +# SegDataKeys.label: [], +# SegOutKeys.predictions: [], +# } +# self.class_labels = {0: "background", 1: "arable"} + +# def setup(self, trainer, pl_module, stage=None, *args, **kwargs): +# # 1. init +# # a. get number of batches in the set +# tdl = trainer.datamodule.train_dataloader() +# vdl = trainer.datamodule.val_dataloader() + +# self.train_batch_size: int = len(iter(vdl).next()[SegDataKeys.image]) +# self.val_batch_size: int = len(iter(vdl).next()[SegDataKeys.image]) + +# train_ds_size = len(tdl.dataset) +# val_ds_size = len(vdl.dataset) + +# self.train_num_predictions = ( +# self.num_predictions +# if train_ds_size > self.num_predictions +# else train_ds_size +# ) +# self.val_num_predictions = ( +# self.num_predictions +# if val_ds_size > self.num_predictions +# else val_ds_size +# ) + +# self.train_indices = sorted( +# np.random.choice(train_ds_size, self.train_num_predictions) +# ) +# self.val_indices = sorted( +# np.random.choice(val_ds_size, self.val_num_predictions) +# ) + +# logging.info(f"train indices {self.train_indices}") +# logging.info(f"val indices {self.val_indices}") + +# def on_stage_batch_end( +# self, +# indices, +# batch_size, +# batch, +# outputs, +# batch_idx, +# container, +# trainer, +# ): +# if trainer.current_epoch % self.logging_interval != 0: +# return + +# for i in indices: +# if i >= (batch_idx + 1) * batch_size: +# break + +# if i >= batch_idx * batch_size: +# offset = i - batch_idx * batch_size +# prediction = outputs[SegOutKeys.predictions][offset] +# image = batch[SegDataKeys.image][offset] +# label = batch[SegDataKeys.label][offset] + +# container[SegDataKeys.image].append(image) +# container[SegDataKeys.label].append(label) +# container[SegOutKeys.predictions].append(prediction) + +# def on_validation_batch_end( +# self, +# trainer, +# pl_module, +# outputs, +# batch, +# batch_idx, +# dataloader_idx=None, +# ): +# self.on_stage_batch_end( +# self.val_indices, +# self.val_batch_size, +# batch, +# outputs, +# batch_idx, +# self.val_containers, +# trainer, +# ) + +# def on_train_batch_end( +# self, +# trainer, +# pl_module, +# outputs, +# batch, +# batch_idx, +# dataloader_idx=None, +# ): +# self.on_stage_batch_end( +# self.train_indices, +# self.train_batch_size, +# batch, +# outputs, +# batch_idx, +# self.train_containers, +# trainer, +# ) + +# def on_train_epoch_start( +# self, trainer: "pl.Trainer", pl_module: "pl.LightningModule" +# ) -> None: +# self.train_containers = { +# SegDataKeys.image: [], +# SegDataKeys.label: [], +# SegOutKeys.predictions: [], +# } + +# def on_validation_epoch_start( +# self, trainer: "pl.Trainer", pl_module: "pl.LightningModule" +# ) -> None: +# self.val_containers = { +# SegDataKeys.image: [], +# SegDataKeys.label: [], +# SegOutKeys.predictions: [], +# } + +# def on_train_epoch_end( +# self, trainer: "pl.Trainer", pl_module: "pl.LightningModule" +# ) -> None: +# self.log_predictions(trainer, "train", self.train_containers) + +# def on_validation_epoch_end( +# self, trainer: "pl.Trainer", pl_module: "pl.LightningModule" +# ) -> None: +# self.log_predictions(trainer, "val", self.val_containers) + +# def log_predictions(self, trainer, stage, container): +# try: +# predictions = container[SegOutKeys.predictions] +# images = container[SegDataKeys.image] +# labels = container[SegDataKeys.label] + +# if any(map(lambda x: len(x) == 0, [predictions, images, labels])): +# return + +# for prediction, label, image in zip( +# predictions, labels, images, range(self.num_predictions) +# ): +# prep_prediction = ( +# torch.sigmoid(prediction[0, ...]).detach().cpu().numpy() +# > 0.5 +# ).astype(np.uint8) +# prep_mask = label.cpu().numpy().astype(np.uint8) + +# img_pred_mask = wandb.Image( +# image, +# masks={ +# "predictions": { +# "mask_data": prep_prediction, +# "class_labels": self.class_labels, +# }, +# "ground_truth": { +# "mask_data": prep_mask, +# "class_labels": self.class_labels, +# }, +# }, # .float() +# ) +# # logger log an image +# trainer.logger.experiment.log( +# {f"{stage}_images": img_pred_mask} +# ) + +# except Exception as e: +# logging.info(e) diff --git a/innofw/core/callbacks/lightning_callbacks/log_segmentation_metrics.py b/innofw/core/callbacks/lightning_callbacks/log_segmentation_metrics.py index 54f6c503..f51da758 100755 --- a/innofw/core/callbacks/lightning_callbacks/log_segmentation_metrics.py +++ b/innofw/core/callbacks/lightning_callbacks/log_segmentation_metrics.py @@ -1,90 +1,90 @@ -from typing import Any -from typing import Optional - -import hydra -import segmentation_models_pytorch as smp -import torch -from pytorch_lightning.callbacks import Callback - - -class LoggingSMPMetricsCallback(Callback): - def __init__(self, metrics, log_every_n_steps=50, *args, **kwargs): - self.metrics = metrics - self.train_scores = {} - self.val_scores = {} - self.test_scores = {} - self.log_every_n_steps = log_every_n_steps - - def on_stage_batch_end(self, outputs, batch, dict_to_store): - masks = batch["labels"].long().squeeze() - logits = outputs["logits"].squeeze() - threshold = 0.4 - tp, fp, fn, tn = smp.metrics.get_stats( - logits, masks, mode="binary", threshold=threshold - ) - - def compute_func(func): - res = hydra.utils.instantiate( - func, tp, fp, fn, tn, reduction="micro" - ).item() - res = round(res, 3) - return res - - for name, func in self.metrics.items(): - score = compute_func(func) - - if name not in dict_to_store: - dict_to_store[name] = [score] - else: - dict_to_store[name].append(score) - - def on_stage_epoch_end( - self, trainer, pl_module: "pl.LightningModule", dict_with_scores - ): - for name, scores in dict_with_scores.items(): - tensorboard = pl_module.logger.experiment - mean_score = torch.mean(torch.tensor(scores)) - # for logger in trainer.loggers: - # logger.add_scalar(f"metrics/train/{name}", mean_score, - # step=trainer.fit_loop.epoch_loop.batch_idx) - tensorboard.add_scalar( - f"metrics/train/{name}", mean_score, trainer.current_epoch - ) - - def on_train_batch_end( - self, - trainer: "pl.Trainer", - pl_module: "pl.LightningModule", - outputs, # : STEP_OUTPUT - batch: Any, - batch_idx: int, - unused: Optional[int] = 0, - ) -> None: - if (batch_idx + 1) % self.log_every_n_steps == 0: - return - - self.on_stage_batch_end(outputs, batch, self.train_scores) - - def on_validation_batch_end( - self, - trainer: "pl.Trainer", - pl_module: "pl.LightningModule", - outputs, - batch: Any, - batch_idx: int, - dataloader_idx: int, - ) -> None: - if (batch_idx + 1) % self.log_every_n_steps == 0: - return - - self.on_stage_batch_end(outputs, batch, self.val_scores) - - def on_train_epoch_end( - self, trainer: "pl.Trainer", pl_module: "pl.LightningModule" - ) -> None: - self.on_stage_epoch_end(trainer, pl_module, self.train_scores) - - def on_validation_epoch_end( - self, trainer: "pl.Trainer", pl_module: "pl.LightningModule" - ) -> None: - self.on_stage_epoch_end(trainer, pl_module, self.val_scores) +# from typing import Any +# from typing import Optional +# +# import hydra +# import segmentation_models_pytorch as smp +# import torch +# from pytorch_lightning.callbacks import Callback +# +# +# class LoggingSMPMetricsCallback(Callback): +# def __init__(self, metrics, log_every_n_steps=50, *args, **kwargs): +# self.metrics = metrics +# self.train_scores = {} +# self.val_scores = {} +# self.test_scores = {} +# self.log_every_n_steps = log_every_n_steps +# +# def on_stage_batch_end(self, outputs, batch, dict_to_store): +# masks = batch["labels"].long().squeeze() +# logits = outputs["logits"].squeeze() +# threshold = 0.4 +# tp, fp, fn, tn = smp.metrics.get_stats( +# logits, masks, mode="binary", threshold=threshold +# ) +# +# def compute_func(func): +# res = hydra.utils.instantiate( +# func, tp, fp, fn, tn, reduction="micro" +# ).item() +# res = round(res, 3) +# return res +# +# for name, func in self.metrics.items(): +# score = compute_func(func) +# +# if name not in dict_to_store: +# dict_to_store[name] = [score] +# else: +# dict_to_store[name].append(score) +# +# def on_stage_epoch_end( +# self, trainer, pl_module: "pl.LightningModule", dict_with_scores +# ): +# for name, scores in dict_with_scores.items(): +# tensorboard = pl_module.logger.experiment +# mean_score = torch.mean(torch.tensor(scores)) +# # for logger in trainer.loggers: +# # logger.add_scalar(f"metrics/train/{name}", mean_score, +# # step=trainer.fit_loop.epoch_loop.batch_idx) +# tensorboard.add_scalar( +# f"metrics/train/{name}", mean_score, trainer.current_epoch +# ) +# +# def on_train_batch_end( +# self, +# trainer: "pl.Trainer", +# pl_module: "pl.LightningModule", +# outputs, # : STEP_OUTPUT +# batch: Any, +# batch_idx: int, +# unused: Optional[int] = 0, +# ) -> None: +# if (batch_idx + 1) % self.log_every_n_steps == 0: +# return +# +# self.on_stage_batch_end(outputs, batch, self.train_scores) +# +# def on_validation_batch_end( +# self, +# trainer: "pl.Trainer", +# pl_module: "pl.LightningModule", +# outputs, +# batch: Any, +# batch_idx: int, +# dataloader_idx: int, +# ) -> None: +# if (batch_idx + 1) % self.log_every_n_steps == 0: +# return +# +# self.on_stage_batch_end(outputs, batch, self.val_scores) +# +# def on_train_epoch_end( +# self, trainer: "pl.Trainer", pl_module: "pl.LightningModule" +# ) -> None: +# self.on_stage_epoch_end(trainer, pl_module, self.train_scores) +# +# def on_validation_epoch_end( +# self, trainer: "pl.Trainer", pl_module: "pl.LightningModule" +# ) -> None: +# self.on_stage_epoch_end(trainer, pl_module, self.val_scores) diff --git a/innofw/core/datamodules/lightning_datamodules/__init__.py b/innofw/core/datamodules/lightning_datamodules/__init__.py index 14a42218..5d7130a7 100755 --- a/innofw/core/datamodules/lightning_datamodules/__init__.py +++ b/innofw/core/datamodules/lightning_datamodules/__init__.py @@ -1,3 +1,4 @@ from .image_folder_dm import ImageLightningDataModule from .qsar_dm import QsarSelfiesDataModule from .semantic_segmentation.hdf5 import HDF5LightningDataModule +from .drugprot import DrugprotDataModule \ No newline at end of file diff --git a/innofw/core/datamodules/lightning_datamodules/semantic_segmentation/hdf5.py b/innofw/core/datamodules/lightning_datamodules/semantic_segmentation/hdf5.py index 31bfedec..a885759f 100755 --- a/innofw/core/datamodules/lightning_datamodules/semantic_segmentation/hdf5.py +++ b/innofw/core/datamodules/lightning_datamodules/semantic_segmentation/hdf5.py @@ -200,6 +200,8 @@ def val_dataloader(self): ) # test_dataloader? + def test_dataloader(self): + pass def setup_infer(self): if isinstance(self.predict_dataset, HDF5Dataset): diff --git a/innofw/core/datasets/segmentation_tif.py b/innofw/core/datasets/segmentation_tif.py index 8c00306e..95a78317 100644 --- a/innofw/core/datasets/segmentation_tif.py +++ b/innofw/core/datasets/segmentation_tif.py @@ -6,7 +6,7 @@ import rasterio as rio from pydantic import FilePath from pydantic import validate_arguments -from segmentation.constants import SegDataKeys +from innofw.constants import SegDataKeys from torch.utils.data import Dataset # diff --git a/innofw/core/models/torch/architectures/classification/__init__.py b/innofw/core/models/torch/architectures/classification/__init__.py index 3f17109f..7b288ce4 100755 --- a/innofw/core/models/torch/architectures/classification/__init__.py +++ b/innofw/core/models/torch/architectures/classification/__init__.py @@ -1 +1,2 @@ from .resnet import Resnet18, Resnet34 +from .mobilenet import MobileNetV2 diff --git a/innofw/core/models/torch/architectures/classification/mobilenet.py b/innofw/core/models/torch/architectures/classification/mobilenet.py index 226bf074..244ccbdb 100644 --- a/innofw/core/models/torch/architectures/classification/mobilenet.py +++ b/innofw/core/models/torch/architectures/classification/mobilenet.py @@ -32,7 +32,7 @@ def __init__( super().__init__() self.num_classes = num_classes - self.model = models.MobileNetV2(pretrained) + self.model = models.mobilenet_v2(pretrained) self.model.fc = nn.Linear(512, num_classes) def forward(self, x): diff --git a/innofw/core/models/torch/lightning_modules/classification.py b/innofw/core/models/torch/lightning_modules/classification.py index 53b531ec..0ddeb1ad 100755 --- a/innofw/core/models/torch/lightning_modules/classification.py +++ b/innofw/core/models/torch/lightning_modules/classification.py @@ -79,9 +79,13 @@ def training_step(self, batch, batch_idx): def validation_step(self, batch, batch_idx): return self.stage_step("val", batch) + + def test_step(self, batch, batch_idx): + return self.stage_step("test", batch) def predict_step(self, batch, batch_idx, **kwargs): - outputs = self.forward(batch.float()) + input_tensor, _ = batch + outputs = self.forward(input_tensor.float()) outputs = torch.argmax(outputs, 1) return outputs @@ -91,9 +95,9 @@ def stage_step(self, stage, batch, do_logging=False, *args, **kwargs): image, label = batch predictions = self.forward(image.float()) - + if stage in ["train", "val"]: - loss = self.losses(predictions, label) + loss = self.calc_losses(predictions, label) self.log(f"{stage}_loss", loss, on_step=False, on_epoch=True) output["loss"] = loss if stage != "predict": @@ -103,6 +107,19 @@ def stage_step(self, stage, batch, do_logging=False, *args, **kwargs): self.log_metrics(stage, metrics) return output + def calc_losses(self, label, pred) -> torch.FloatTensor: + """Function to compute losses""" + total_loss = 0.0 + if isinstance(self.losses, list): + for loss_name, weight, loss in self.losses: + # for loss_name in loss_dict: + ls_mask = loss(label, pred) + total_loss += weight * ls_mask + else: + total_loss = self.losses(label, pred) + + return total_loss + def log_metrics(self, stage, metrics_res): for key, value in metrics_res.items(): self.log(key, value, sync_dist=True, on_step=False, on_epoch=True) diff --git a/innofw/core/schedulers/old_pipeline.py b/innofw/core/schedulers/old_pipeline.py deleted file mode 100644 index 26f4d23b..00000000 --- a/innofw/core/schedulers/old_pipeline.py +++ /dev/null @@ -1,55 +0,0 @@ -import functools -import math - - -# todo: refactor -def lr_cyclic(decay=0.2, decay_step=10, cyclic_decay=0.9, cyclic_len=25): - def lr_step(epoch, decay, decay_step, cyclic_decay, cyclic_len): - cyclic_n = epoch // cyclic_len - epoch -= cyclic_len * cyclic_n - # cyclic_decay=0.99 - # decay=0.5 - return (decay * (cyclic_decay**cyclic_n)) ** (epoch // decay_step) - - def lr_exp(epoch, decay, decay_step, cyclic_decay, cyclic_len): - cyclic_n = epoch // cyclic_len - epoch -= cyclic_len * cyclic_n - return math.exp(-decay * epoch) * (cyclic_decay**cyclic_n) - - def lr_cos(epoch, decay, decay_step, cyclic_decay, cyclic_len): - """Returns alpha * cos((epoch - a) / (b - a) * pi) + beta - a, b - left and right bounds of i-th cycle, i=0,1,... - So lr = lr0 * lr_cos - """ - n = 0 - k = 1 - cyclic_sum = cyclic_len - while epoch >= cyclic_sum: - k *= decay_step - n += 1 - cyclic_sum += k * cyclic_len - b = cyclic_sum - a = b - k * cyclic_len - alpha = 0.5 * (1 - decay) - beta = 0.5 * (1 + decay) - - return (alpha * math.cos((epoch - a) / (b - a) * math.pi) + beta) * ( - cyclic_decay**n - ) - - def lr_poly(epoch, decay, decay_step, cyclic_decay, cyclic_len): - cyclic_n = epoch // cyclic_len - epoch -= cyclic_len * cyclic_n - return (1 - epoch / (1.048 * cyclic_len)) ** 0.9 * ( - cyclic_decay**cyclic_n - ) - - return functools.partial( - # lr_exp, - # lr_poly, - lr_cos, - decay=decay, - decay_step=decay_step, - cyclic_decay=cyclic_decay, - cyclic_len=cyclic_len, - ) diff --git a/innofw/exceptions.py b/innofw/exceptions.py index 4defe363..8e678f6a 100644 --- a/innofw/exceptions.py +++ b/innofw/exceptions.py @@ -1,2 +1,2 @@ class NonUniqueException(Exception): - pass + pass \ No newline at end of file diff --git a/innofw/onvif_util/camera_info.py b/innofw/onvif_util/camera_info.py index 68855391..a800baa1 100644 --- a/innofw/onvif_util/camera_info.py +++ b/innofw/onvif_util/camera_info.py @@ -6,18 +6,16 @@ def get_camera_info(ip, port, user: Optional[str], password: Optional[str]): # Создаём объект с указанием хоста, порта, пользователя, пароля и пути до wsdl - user = str(user) - password = str(password) - print(ip, port, "U: " + user, "P: " + password) + user, password = str(user), str(password) + + # print(ip, port, "U: " + user, "P: " + password) mycam = ONVIFCamera(ip, port, user, password) # запрашиваем и выводим информацию об устройстве - resp = mycam.devicemgmt.GetDeviceInformation() - print(str(resp)) + resp_, resp = mycam.devicemgmt.GetDeviceInformation(), mycam.devicemgmt.GetNetworkInterfaces() # запрашиваем и выводим информацию о сетевых интерфейсах - resp = mycam.devicemgmt.GetNetworkInterfaces() - print(str(resp)) + print(f'{ip}, {port}, "U:{user}, "P: {password} \n{str(resp_)}, {str(resp)}') # запрашиваем адрес медиа потока media_service = mycam.create_media_service() diff --git a/innofw/onvif_util/mover.py b/innofw/onvif_util/mover.py index f4767e92..797338c3 100644 --- a/innofw/onvif_util/mover.py +++ b/innofw/onvif_util/mover.py @@ -48,7 +48,7 @@ def __init__(self, ip, user, password): self.__cam_password = str(password) mycam = ONVIFCamera( - self.__cam_ip, 80, self.__cam_user, self.__cam_password + self.__cam_ip, 80, self.__cam_user, self.__cam_password, wsdl_dir='./innofw/onvif_util/wsdl' ) logging.info("Create media service object") media = mycam.create_media_service() @@ -280,29 +280,6 @@ def go_to_preset(self, preset_position: str): return None -def pan_right(ptz_cam): - ptz_cam.relative_move(0.5, 0.0, 0.0) ## ( Pan, Tilt, Zoom ) - - -def pan_left(ptz_cam): - ptz_cam.relative_move(-0.5, 0.0, 0.0) ## ( Pan, Tilt, Zoom ) - - -def zoom_in(ptz_cam): - ptz_cam.relative_move(0.0, 0.0, 0.5) ## ( Pan, Tilt, Zoom ) - - -def zoom_out(ptz_cam): - ptz_cam.relative_move(0.0, 0.0, -0.5) ## ( Pan, Tilt, Zoom ) - - -def tilt_up(ptz_cam): - ptz_cam.relative_move(0.0, 0.5, 0.0) ## ( Pan, Tilt, Zoom ) - - -def tilt_down(ptz_cam): - ptz_cam.relative_move(0.0, -0.5, 0.0) ## ( Pan, Tilt, Zoom ) - def move(ip, user: Optional[str], password: Optional[str], move_type): """ @@ -322,17 +299,17 @@ def move(ip, user: Optional[str], password: Optional[str], move_type): ptz_cam = CameraControl(ip, user, password) if move_type == "zoom_in": - zoom_in(ptz_cam) + ptz_cam.relative_move(0.0, 0.0, 0.5) elif move_type == "zoom_out": - zoom_out(ptz_cam) + ptz_cam.relative_move(0.0, 0.0, -0.5) elif move_type == "pan_left": - pan_left(ptz_cam) + ptz_cam.relative_move(-0.5, 0.0, 0.0) elif move_type == "pan_right": - pan_right(ptz_cam) + ptz_cam.relative_move(0.5, 0.0, 0.0) elif move_type == "tilt_up": - tilt_up(ptz_cam) + ptz_cam.relative_move(0.0, 0.5, 0.0) elif move_type == "tilt_down": - tilt_down(ptz_cam) + ptz_cam.relative_move(0.0, -0.5, 0.0) if __name__ == "__main__": diff --git a/innofw/schema/model_checkpoint.py b/innofw/schema/model_checkpoint.py index 80a07455..f5479cba 100644 --- a/innofw/schema/model_checkpoint.py +++ b/innofw/schema/model_checkpoint.py @@ -1,12 +1,7 @@ -# from typing import Any - from innoframework.schema.model_metadata import ModelMetadata from pydantic import BaseModel -# - - class ModelCheckpoint(BaseModel): model: Any metadata: ModelMetadata diff --git a/innofw/utils/data_utils/preprocessing/CT_hemorrhage_contrast.py b/innofw/utils/data_utils/preprocessing/CT_hemorrhage_contrast.py index 8620684d..b76907f7 100644 --- a/innofw/utils/data_utils/preprocessing/CT_hemorrhage_contrast.py +++ b/innofw/utils/data_utils/preprocessing/CT_hemorrhage_contrast.py @@ -1,3 +1,5 @@ +import sys +import pydicom from pydicom import dcmread from pydicom.pixel_data_handlers.util import apply_voi_lut import numpy as np @@ -26,7 +28,7 @@ def window_image(img, window_center, window_width, intercept, slope): img = img * slope + intercept img_min = window_center - window_width // 2 img_max = window_center + window_width // 2 - img = numpy.clip(img, min_val, max_val) + img = np.clip(img, img_min, img_max) return img def resize(img, new_w, new_h): @@ -54,4 +56,4 @@ def prepare_image(img_dicom): windowed = np.array(windowed) cv2.imwrite(sys.argv[2]+"/"+id.split("/")[-1], windowed) except Exception as err: - logging.error(err) + l.error(err) diff --git a/innofw/utils/weights_initializations.py b/innofw/utils/weights_initializations.py index c2430318..be7a1288 100755 --- a/innofw/utils/weights_initializations.py +++ b/innofw/utils/weights_initializations.py @@ -1,9 +1,6 @@ -# -# import hydra import torch.nn as nn - class WeightInitializer: """ Class used for working with data from s3 storage diff --git a/tests/data/images/detection/lep/data.yaml b/tests/data/images/detection/lep/data.yaml index 1b8a990f..453492b9 100644 --- a/tests/data/images/detection/lep/data.yaml +++ b/tests/data/images/detection/lep/data.yaml @@ -1,5 +1,5 @@ -train: /mnt/nvmestorage/qb/repos/exp/pvl/innofw/tests/data/images/detection/lep/innofw_split_data/images/train -val: /mnt/nvmestorage/qb/repos/exp/pvl/innofw/tests/data/images/detection/lep/innofw_split_data/images/val -test: /mnt/nvmestorage/qb/repos/exp/pvl/innofw/tests/data/images/detection/lep/images/test +train: /home/ailab_user/lada/innofw_test/innofw/tests/data/images/detection/lep/innofw_split_data/images/train +val: /home/ailab_user/lada/innofw_test/innofw/tests/data/images/detection/lep/innofw_split_data/images/val +test: /home/ailab_user/lada/innofw_test/innofw/tests/data/images/detection/lep/images/test nc: 4 names: ['lep_1', 'lep_2', 'lep_3', 'lep_4'] diff --git a/tests/data/images/segmentation/arable/test/test.hdf5 b/tests/data/images/segmentation/arable/test/test.hdf5 index 7e2a1e9d..6fb4145d 100644 Binary files a/tests/data/images/segmentation/arable/test/test.hdf5 and b/tests/data/images/segmentation/arable/test/test.hdf5 differ diff --git a/tests/data/images/segmentation/arable/train/train.hdf5 b/tests/data/images/segmentation/arable/train/train.hdf5 index 7e2a1e9d..6fb4145d 100644 Binary files a/tests/data/images/segmentation/arable/train/train.hdf5 and b/tests/data/images/segmentation/arable/train/train.hdf5 differ diff --git a/tests/data/images/segmentation/dicom/test/image/200.dcm b/tests/data/images/segmentation/dicom/test/image/200.dcm new file mode 100644 index 00000000..9201d7d1 Binary files /dev/null and b/tests/data/images/segmentation/dicom/test/image/200.dcm differ diff --git a/tests/data/images/segmentation/dicom/test/label/200.png b/tests/data/images/segmentation/dicom/test/label/200.png new file mode 100644 index 00000000..e3e90122 Binary files /dev/null and b/tests/data/images/segmentation/dicom/test/label/200.png differ diff --git a/tests/data/images/segmentation/dicom/train/image/201.dcm b/tests/data/images/segmentation/dicom/train/image/201.dcm new file mode 100644 index 00000000..49d4a781 Binary files /dev/null and b/tests/data/images/segmentation/dicom/train/image/201.dcm differ diff --git a/tests/data/images/segmentation/dicom/train/image/202.dcm b/tests/data/images/segmentation/dicom/train/image/202.dcm new file mode 100644 index 00000000..09e4fd4c Binary files /dev/null and b/tests/data/images/segmentation/dicom/train/image/202.dcm differ diff --git a/tests/data/images/segmentation/dicom/train/label/201.png b/tests/data/images/segmentation/dicom/train/label/201.png new file mode 100644 index 00000000..66420129 Binary files /dev/null and b/tests/data/images/segmentation/dicom/train/label/201.png differ diff --git a/tests/data/images/segmentation/dicom/train/label/202.png b/tests/data/images/segmentation/dicom/train/label/202.png new file mode 100644 index 00000000..04f49311 Binary files /dev/null and b/tests/data/images/segmentation/dicom/train/label/202.png differ diff --git a/tests/data/images/segmentation/forest/test/1/100.tif b/tests/data/images/segmentation/forest/test/1/100.tif new file mode 100644 index 00000000..a2d8166a Binary files /dev/null and b/tests/data/images/segmentation/forest/test/1/100.tif differ diff --git a/tests/data/images/segmentation/forest/test/1/B02.tif b/tests/data/images/segmentation/forest/test/1/B02.tif new file mode 100644 index 00000000..b246f1e4 Binary files /dev/null and b/tests/data/images/segmentation/forest/test/1/B02.tif differ diff --git a/tests/data/images/segmentation/forest/test/1/B03.tif b/tests/data/images/segmentation/forest/test/1/B03.tif new file mode 100644 index 00000000..46241f8b Binary files /dev/null and b/tests/data/images/segmentation/forest/test/1/B03.tif differ diff --git a/tests/data/images/segmentation/forest/test/1/B04.tif b/tests/data/images/segmentation/forest/test/1/B04.tif new file mode 100644 index 00000000..6d8384c5 Binary files /dev/null and b/tests/data/images/segmentation/forest/test/1/B04.tif differ diff --git a/tests/data/images/segmentation/forest/train/1/100.tif b/tests/data/images/segmentation/forest/train/1/100.tif new file mode 100644 index 00000000..a2d8166a Binary files /dev/null and b/tests/data/images/segmentation/forest/train/1/100.tif differ diff --git a/tests/data/images/segmentation/forest/train/1/B02.tif b/tests/data/images/segmentation/forest/train/1/B02.tif new file mode 100644 index 00000000..b246f1e4 Binary files /dev/null and b/tests/data/images/segmentation/forest/train/1/B02.tif differ diff --git a/tests/data/images/segmentation/forest/train/1/B03.tif b/tests/data/images/segmentation/forest/train/1/B03.tif new file mode 100644 index 00000000..46241f8b Binary files /dev/null and b/tests/data/images/segmentation/forest/train/1/B03.tif differ diff --git a/tests/data/images/segmentation/forest/train/1/B04.tif b/tests/data/images/segmentation/forest/train/1/B04.tif new file mode 100644 index 00000000..6d8384c5 Binary files /dev/null and b/tests/data/images/segmentation/forest/train/1/B04.tif differ diff --git a/tests/fixtures/config/datasets.py b/tests/fixtures/config/datasets.py index f0e829ed..e136b97a 100644 --- a/tests/fixtures/config/datasets.py +++ b/tests/fixtures/config/datasets.py @@ -142,7 +142,32 @@ "channels_num": 4, } ) - +forest_segmentation_cfg_w_target = DictConfig( + { + "task": ["image-segmentation"], + "name": "arable", + "description": "something", + "markup_info": "something", + "date_time": "07.02.2024", + "_target_": "innofw.core.datamodules.lightning_datamodules.semantic_segmentation.tiff.SegmentationDM", + "train": { + "source": str( + get_test_folder_path() / "data/images/segmentation/forest/train" + ) + }, + "val": { + "source": str( + get_test_folder_path() / "data/images/segmentation/forest/test" + ) + }, + "test": { + "source": str( + get_test_folder_path() / "data/images/segmentation/forest/test" + ) + }, + "channels_num": 3, + } +) faces_datamodule_cfg_w_target = DictConfig( { "task": ["image-classification"], @@ -308,3 +333,57 @@ "target_col": "gap", } ) + +anomaly_detection_timeseries_datamodule_cfg_w_target = DictConfig( + { + "task": ["anomaly-detection-timeseries"], + "name": "ecg", + "description": " The original dataset for ECG5000 is a 20-hour long ECG downloaded from Physionet", + "markup_info": "Информация о разметке", + "date_time": "03.08.2022", + "_target_": "innofw.core.datamodules.lightning_datamodules.anomaly_detection_timeseries_dm.TimeSeriesLightningDataModule", + "train": { + "source": "https://api.blackhole.ai.innopolis.university/public-datasets/ECG/train.zip", + "target": "./data/ECG/train", + }, + "test": { + "source": "https://api.blackhole.ai.innopolis.university/public-datasets/ECG/test.zip", + "target": "./data/ECG/test", + }, + "infer": { + "source": "https://api.blackhole.ai.innopolis.university/public-datasets/ECG/test.zip", + "target": "./data/ECG/infer", + }, + "val_size": 0.2, + } + +) + +stroke_segmentation_datamodule_cfg_w_target = DictConfig( + { + "task": ["semantic_segmentation"], + "name": "stroke_segmentation", + "description": "Created by AT&T Laboratories Cambridge", + "markup_info": "Информация о разметке", + "date_time": "19.07.2022", + "_target_": "innofw.core.datamodules.lightning_datamodules.semantic_segmentation.stroke_dm.StrokeSegmentationDatamodule", + "train": { + "source": str( + get_test_folder_path() /"data/images/segmentation/dicom/train"), + }, + "test": { + "source": str( + get_test_folder_path() /"data/images/segmentation/dicom/test"), + }, + "infer": { + "source": str( + get_test_folder_path() /"data/images/segmentation/dicom/test"), + }, + "channels_num": 3, + "val_size": 0.2, + "batch_size": 8, + "num_workers": 1, + "random_seed": 42, + } + +) \ No newline at end of file diff --git a/tests/fixtures/config/losses.py b/tests/fixtures/config/losses.py index 1dab878f..92970eec 100644 --- a/tests/fixtures/config/losses.py +++ b/tests/fixtures/config/losses.py @@ -19,3 +19,88 @@ }, }, ) + +soft_ce_loss_w_target = DictConfig( + { + "name": "Classification", + "description": "something", + "task": ["image-classification"], + "implementations": { + "torch": { + "SoftCrossEntropyLoss": { + "weight": 0.5, + "object": { + "_target_": "pytorch_toolbelt.losses.SoftCrossEntropyLoss", + }, + }, + } + }, + }, +) + +vae_loss_w_target = DictConfig( + { + "name": "ELBO", + "description": "something", + "task": ["text-vae", "text-vae-forward", "text-vae-reverse"], + "implementations": { + "torch":{ + "mse":{ + "weight": 1.0, + "object":{ + "_target_": "torch.nn.MSELoss"} + }, + "target_loss": { + "weight": 1.0, + "object": { + "_target_": "torch.nn.MSELoss" + } + }, + "kld": { + "weight": 0.1, + "object": { + "_target_": "innofw.core.losses.kld.KLD" + } + } + } + } + } +) + +token_class_loss_w_target = DictConfig( + { + "name": "Token Classification", + "description": "something", + "task": ["text-ner"], + "implementations": { + "torch": { + "FocalLoss":{ + "weight": 1, + "object": { + "_target_": "innofw.core.losses.focal_loss.FocalLoss", + "gamma": 2 + } + } + } + } + } +) + +focal_loss_w_target = DictConfig( + { + "name": "Detection", + "description": "something", + "task": ["image-detection"], + "implementations": { + "torch": { + "BinaryFocalLoss": { + "weight": 1, + "object": { + "_target_": "pytorch_toolbelt.losses.BinaryFocalLoss", + # "mode": "binary", + }, + }, + } + }, + }, +) diff --git a/tests/fixtures/config/models.py b/tests/fixtures/config/models.py index b3035bc6..543a5e3d 100644 --- a/tests/fixtures/config/models.py +++ b/tests/fixtures/config/models.py @@ -24,6 +24,14 @@ } ) +resnet_binary_cfg_w_target = DictConfig( + { + "name": "resnet18", + "description": "model from torchvision", + "num_classes": 2, + } +) + # case: xgboost regressor xgbregressor_cfg_w_target = DictConfig( {"name": "xgboost_regressor", "description": "something", "_target_": ""} @@ -75,6 +83,14 @@ } ) +baselearner_cfg_w_target = DictConfig( + { + "name": "base_active_learner", + "description": "Base regression model", + "_target_": "sklearn.linear_model.LinearRegression", + } +) + catboost_with_uncertainty_cfg_w_target = DictConfig( { "name": "catboost + data uncertainty", @@ -84,3 +100,43 @@ "posterior_sampling": "true", } ) + +# case: vae for seq2seq modeling + +text_vae_cfg_w_target = DictConfig( + { + "name": "chem-vae", + "description": "vae for seq2seq modeling", + "_target_": "innofw.core.models.torch.architectures.autoencoders.vae.VAE", + "encoder": { + "_target_": "innofw.core.models.torch.architectures.autoencoders.vae.Encoder", + "in_dim": 609, # len(alphabet) * max(len_mols) + "hidden_dim": 128, + "enc_out_dim": 128, + }, + "decoder": { + "_target_": "innofw.core.models.torch.architectures.autoencoders.vae.GRUDecoder", + "latent_dimension": 128, + "gru_stack_size": 3, + "gru_neurons_num": 128, + "out_dimension": 29, # len(alphabet) + } + + } +) + +biobert_cfg_w_target = DictConfig( + { + "name": "biobert-ner", + "description": "bert for token classification biobert-base-cased-v1.2", + "_target_": "innofw.core.models.torch.architectures.token_classification.biobert_ner.BiobertNer", + "model":{ + "_target_": "transformers.BertForTokenClassification.from_pretrained", + "pretrained_model_name_or_path": "dmis-lab/biobert-base-cased-v1.2" + }, + "tokenizer":{ + "_target_": "transformers.BertTokenizerFast.from_pretrained", + "pretrained_model_name_or_path": "dmis-lab/biobert-base-cased-v1.2" + } + } +) \ No newline at end of file diff --git a/tests/fixtures/config/schedulers.py b/tests/fixtures/config/schedulers.py new file mode 100644 index 00000000..46b23011 --- /dev/null +++ b/tests/fixtures/config/schedulers.py @@ -0,0 +1,11 @@ +from omegaconf import DictConfig + + +linear_w_target = DictConfig( + { + '_target_': 'torch.optim.lr_scheduler.LinearLR', + 'start_factor': 0.33, + 'end_factor': 1.0, + 'total_iters': 150, + }, +) diff --git a/tests/integration/callbacks/test_callbacks.py b/tests/integration/callbacks/test_callbacks.py new file mode 100644 index 00000000..6871239a --- /dev/null +++ b/tests/integration/callbacks/test_callbacks.py @@ -0,0 +1,6 @@ +from innofw.core.callbacks.lightning_callbacks.detection.log_predictions import LogPredictionsDetectionCallback + + +def test_LogPredictionsDetectionCallback(): + cb = LogPredictionsDetectionCallback() + assert cb is not None \ No newline at end of file diff --git a/tests/integration/callbacks/test_xgboost_callbacks.py b/tests/integration/callbacks/test_xgboost_callbacks.py new file mode 100644 index 00000000..44e2f7a5 --- /dev/null +++ b/tests/integration/callbacks/test_xgboost_callbacks.py @@ -0,0 +1,5 @@ +from innofw.core.callbacks.xgboost_callbacks.log_trainig_steps import XGBoostTrainingTensorBoardCallback + +def test_xgboost_callback(): + cb = XGBoostTrainingTensorBoardCallback('./logs/test/test1/') + assert cb is not None \ No newline at end of file diff --git a/tests/integration/models/test_models_via_mocking.py b/tests/integration/models/test_models_via_mocking.py index 126776ae..98799a79 100755 --- a/tests/integration/models/test_models_via_mocking.py +++ b/tests/integration/models/test_models_via_mocking.py @@ -9,6 +9,7 @@ from innofw.core.models.sklearn_adapter import SklearnAdapter from innofw.core.models.torch.architectures.segmentation.unet import UNet +from innofw.core.models.torch.architectures.classification import MobileNetV2 from innofw.core.models.xgboost_adapter import XGBoostAdapter @@ -63,3 +64,8 @@ def test_unet(): x = torch.from_numpy(np.random.random((1, 3, 512, 512))) model = UNet() assert model.forward(x.float()) is not None + +def test_mobilenet(): + x = torch.from_numpy(np.random.random((1, 3, 512, 512))) + model = MobileNetV2(num_classes=2, pretrained=False) + assert model.forward(x.float()) is not None diff --git a/tests/integration/models/torch/lighting_modules/conftest.py b/tests/integration/models/torch/lighting_modules/conftest.py index 2aaeaae9..4f9eea76 100644 --- a/tests/integration/models/torch/lighting_modules/conftest.py +++ b/tests/integration/models/torch/lighting_modules/conftest.py @@ -3,6 +3,7 @@ import pytest import torch +import random from omegaconf import DictConfig from pytorch_lightning import LightningDataModule from pytorch_lightning import LightningModule @@ -16,15 +17,19 @@ from innofw.core.models.torch.lightning_modules.segmentation import ( SemanticSegmentationLightningModule, ) +from innofw.core.models.torch.lightning_modules.classification import ( + ClassificationLightningModule, +) from innofw.utils.framework import get_losses from innofw.utils.framework import get_model from tests.fixtures.config import losses as fixt_losses from tests.fixtures.config import models as fixt_models from tests.fixtures.config import optimizers as fixt_optimizers from tests.fixtures.config import trainers as fixt_trainers +from tests.fixtures.config import schedulers as fixt_schedulers - -class DummyDataset(Dataset): +# Segmentation +class SegDummyDataset(Dataset): def __init__(self, num_samples): self.num_samples = num_samples @@ -35,16 +40,27 @@ def __getitem__(self, index): def __len__(self): return self.num_samples + +class ClassDummyDataset(Dataset): + def __init__(self, num_samples): + self.num_samples = num_samples + def __getitem__(self, index): + x = torch.rand(3, 224, 224) + y = torch.randint(0, 2, (2,)) #torch.tensor(random.randint(0, 2), dtype=torch.int8) # + return x, y + + def __len__(self): + return self.num_samples -class DummyDataModule(LightningDataModule): +class SegDummyDataModule(LightningDataModule): def __init__(self, num_samples: int, batch_size: int = 4): super().__init__() self.num_samples = num_samples self.batch_size = batch_size def setup(self, stage=None): - self.dataset = DummyDataset(self.num_samples) + self.dataset = SegDummyDataset(self.num_samples) def train_dataloader(self): return DataLoader(self.dataset, batch_size=self.batch_size) @@ -54,7 +70,24 @@ def val_dataloader(self): def test_dataloader(self): return DataLoader(self.dataset, batch_size=self.batch_size) + +class ClassDummyDataModule(LightningDataModule): + def __init__(self, num_samples: int, batch_size: int = 4): + super().__init__() + self.num_samples = num_samples + self.batch_size = batch_size + def setup(self, stage=None): + self.dataset = ClassDummyDataset(self.num_samples) + + def train_dataloader(self): + return DataLoader(self.dataset, batch_size=self.batch_size) + + def val_dataloader(self): + return DataLoader(self.dataset, batch_size=self.batch_size) + + def test_dataloader(self): + return DataLoader(self.dataset, batch_size=self.batch_size) @pytest.fixture(scope="module") def segmentation_module() -> LightningModule: @@ -97,8 +130,14 @@ def trainer_with_temporary_directory(): @pytest.fixture(scope="module") -def dummy_data_module(): - data_module = DummyDataModule(num_samples=100, batch_size=4) +def seg_dummy_data_module(): + data_module = SegDummyDataModule(num_samples=100, batch_size=4) + data_module.setup() # Call the setup method to define the dataset attribute + return data_module + +@pytest.fixture(scope="module") +def class_dummy_data_module(): + data_module = ClassDummyDataModule(num_samples=100, batch_size=4) data_module.setup() # Call the setup method to define the dataset attribute return data_module @@ -127,12 +166,68 @@ def segmentation_module_function_scope() -> LightningModule: def fitted_segmentation_module( segmentation_module: LightningModule, trainer_with_temporary_directory, - dummy_data_module: DummyDataModule, + seg_dummy_data_module: SegDummyDataModule, ): trainer, _ = trainer_with_temporary_directory - dataloader = dummy_data_module.train_dataloader() + dataloader = seg_dummy_data_module.train_dataloader() trainer.fit(segmentation_module, train_dataloaders=dataloader) segmentation_module.trainer = ( trainer # Add the trainer to the fitted module for future use ) return segmentation_module + +@pytest.fixture(scope="module") +def classification_module() -> LightningModule: + cfg = DictConfig( + { + "models": fixt_models.resnet_binary_cfg_w_target, + "trainer": fixt_trainers.trainer_cfg_w_cpu_devices, + "losses": fixt_losses.soft_ce_loss_w_target, + } + ) + model = get_model(cfg.models, cfg.trainer) + losses = get_losses(cfg, "image-classification", Frameworks.torch) + optimizer_cfg = DictConfig(fixt_optimizers.adam_optim_w_target) + scheduler_cfg = DictConfig(fixt_schedulers.linear_w_target) + + module = ClassificationLightningModule( + model=model, losses=losses, optimizer_cfg=optimizer_cfg, scheduler_cfg=scheduler_cfg + ) + + return module + + +@pytest.fixture(scope="function") +def classification_module_function_scope() -> LightningModule: + cfg = DictConfig( + { + "models": fixt_models.resnet_binary_cfg_w_target, + "trainer": fixt_trainers.trainer_cfg_w_cpu_devices, + "losses": fixt_losses.soft_ce_loss_w_target, + } + ) + model = get_model(cfg.models, cfg.trainer) + losses = get_losses(cfg, "image-classification", Frameworks.torch) + optimizer_cfg = DictConfig(fixt_optimizers.adam_optim_w_target) + scheduler_cfg = DictConfig(fixt_schedulers.linear_w_target) + + module = ClassificationLightningModule( + model=model, losses=losses, optimizer_cfg=optimizer_cfg, scheduler_cfg=scheduler_cfg + ) + + return module + + +@pytest.fixture(scope="module") +def fitted_classification_module( + classification_module: LightningModule, + trainer_with_temporary_directory, + class_dummy_data_module: ClassDummyDataModule, +): + trainer, _ = trainer_with_temporary_directory + dataloader = class_dummy_data_module.train_dataloader() + trainer.fit(classification_module, train_dataloaders=dataloader) + classification_module.trainer = ( + trainer # Add the trainer to the fitted module for future use + ) + return classification_module \ No newline at end of file diff --git a/tests/integration/models/torch/lighting_modules/test_biobert_ner.py b/tests/integration/models/torch/lighting_modules/test_biobert_ner.py new file mode 100644 index 00000000..b5c96a50 --- /dev/null +++ b/tests/integration/models/torch/lighting_modules/test_biobert_ner.py @@ -0,0 +1,252 @@ +import os + +import pytest +import torch +from omegaconf import DictConfig +from pytorch_lightning import LightningDataModule +from pytorch_lightning import LightningModule +from torch.utils.data import DataLoader +from torch.utils.data import Dataset + +from innofw.constants import Frameworks +from innofw.core.models.torch.lightning_modules.biobert_ner_model import ( + BiobertNERModel +) +from innofw.utils.framework import get_datamodule +from innofw.utils.framework import get_losses +from innofw.utils.framework import get_model +from tests.fixtures.config import datasets as fixt_datasets +from tests.fixtures.config import losses as fixt_losses +from tests.fixtures.config import models as fixt_models +from tests.fixtures.config import optimizers as fixt_optimizers +from tests.fixtures.config import schedulers as fixt_schedulers +from tests.fixtures.config import trainers as fixt_trainers + + +class DummyDataset(Dataset): + def __init__(self, num_samples): + self.num_samples = num_samples + + def __getitem__(self, index): + x = torch.rand(3, 224, 224) + y = torch.randint(0, 2, (1, 224, 224)) + return x, y + + def __len__(self): + return self.num_samples + + +class DummyDataModule(LightningDataModule): + def __init__(self, num_samples: int, batch_size: int = 4): + super().__init__() + self.num_samples = num_samples + self.batch_size = batch_size + + def setup(self, stage=None): + self.dataset = DummyDataset(self.num_samples) + + def train_dataloader(self): + return DataLoader(self.dataset, batch_size=self.batch_size) + + def val_dataloader(self): + return DataLoader(self.dataset, batch_size=self.batch_size) + + def test_dataloader(self): + return DataLoader(self.dataset, batch_size=self.batch_size) + + +@pytest.mark.skip(reason="too resource-intensive") +@pytest.fixture(scope="module") +def dummy_data_module(): + cfg = DictConfig(fixt_datasets.drugprot_datamodule_cfg_w_target) + data_module = get_datamodule(cfg, framework=Frameworks.torch, task="text-ner") + # data_module = DummyDataModule(num_samples=100, batch_size=4) + data_module.setup() # Call the setup method to define the dataset attribute + return data_module + + +@pytest.mark.skip(reason="too resource-intensive") +@pytest.fixture(scope="module") +def biobert_module() -> LightningModule: + cfg = DictConfig( + { + "models": fixt_models.biobert_cfg_w_target, + "trainer": fixt_trainers.trainer_cfg_w_cpu_devices, + "losses": fixt_losses.token_class_loss_w_target, + } + ) + model = get_model(cfg.models, cfg.trainer) + losses = get_losses(cfg, "text-ner", Frameworks.torch) + optimizer_cfg = DictConfig(fixt_optimizers.adam_optim_w_target) + scheduler_cfg = DictConfig(fixt_schedulers.linear_w_target) + + module = BiobertNERModel( + model=model, losses=losses, optimizer_cfg=optimizer_cfg, scheduler_cfg=scheduler_cfg + ) + + return module + + +@pytest.mark.skip(reason="too resource-intensive") +@pytest.fixture(scope="function") +def biobert_module_function_scope() -> LightningModule: + cfg = DictConfig( + { + "models": fixt_models.biobert_cfg_w_target, + "trainer": fixt_trainers.trainer_cfg_w_cpu_devices, + "losses": fixt_losses.token_class_loss_w_target, + } + ) + model = get_model(cfg.models, cfg.trainer) + losses = get_losses(cfg, "text-ner", Frameworks.torch) + optimizer_cfg = DictConfig(fixt_optimizers.adam_optim_w_target) + scheduler_cfg = DictConfig(fixt_schedulers.linear_w_target) + + module = BiobertNERModel( + model=model, losses=losses, optimizer_cfg=optimizer_cfg, scheduler_cfg=scheduler_cfg + ) + + return module + + +@pytest.mark.skip(reason="too resource-intensive") +@pytest.fixture(scope="module") +def fitted_biobert_module( + biobert_module: LightningModule, + trainer_with_temporary_directory, + dummy_data_module: DummyDataModule, +): + trainer, _ = trainer_with_temporary_directory + dataloader = dummy_data_module.train_dataloader() + trainer.fit(biobert_module, train_dataloaders=dataloader) + biobert_module.trainer = ( + trainer # Add the trainer to the fitted module for future use + ) + return biobert_module + + +@pytest.mark.skip(reason="too resource-intensive") +@pytest.mark.skipif( + not torch.cuda.is_available(), reason="No GPU is found on this machine" +) +def test_training_with_gpu( + biobert_module_function_scope, + trainer_with_temporary_directory, + dummy_data_module, +): + trainer, checkpoint_dir = trainer_with_temporary_directory + dataloader = dummy_data_module.train_dataloader() + trainer.fit(biobert_module_function_scope, train_dataloaders=dataloader) + + +@pytest.mark.skip(reason="too resource-intensive") +def test_training_without_checkpoint( + biobert_module_function_scope, + trainer_with_temporary_directory, + dummy_data_module, +): + trainer, checkpoint_dir = trainer_with_temporary_directory + dataloader = dummy_data_module.train_dataloader() + trainer.fit(biobert_module_function_scope, train_dataloaders=dataloader) + + +@pytest.mark.skip(reason="too resource-intensive") +def test_training_with_checkpoint( + fitted_biobert_module, + trainer_with_temporary_directory, + dummy_data_module, +): + trainer, checkpoint_dir = trainer_with_temporary_directory + dataloader = dummy_data_module.train_dataloader() + + # First training phase is already done in the fitted_biobert_module fixture + last_checkpoint_path = ( + fitted_biobert_module.trainer.checkpoint_callback.best_model_path + ) + + # Continue training using the fitted_biobert_module + trainer.fit( + fitted_biobert_module, + ckpt_path=last_checkpoint_path, + train_dataloaders=dataloader, + ) + + +@pytest.mark.skip(reason="too resource-intensive") +def test_testing_without_checkpoint( + biobert_module_function_scope, + dummy_data_module, + trainer_with_temporary_directory, +): + trainer, checkpoint_dir = trainer_with_temporary_directory + dataloader = dummy_data_module.train_dataloader() + + # Test the loaded model + trainer.test( + biobert_module_function_scope, + dataloaders=dataloader, + ) + + +@pytest.mark.skip(reason="too resource-intensive") +def test_testing_with_checkpoint( + biobert_module_function_scope, + fitted_biobert_module, + dummy_data_module, + trainer_with_temporary_directory, +): + fitted_model_trainer = fitted_biobert_module.trainer + fitted_model_dataloader = dummy_data_module.test_dataloader() + + # Get the checkpoint directory and list all checkpoint files + checkpoint_dir = fitted_model_trainer.checkpoint_callback.dirpath + checkpoints = sorted(os.listdir(checkpoint_dir)) + first_checkpoint_path = os.path.join(checkpoint_dir, checkpoints[0]) + last_checkpoint_path = os.path.join(checkpoint_dir, checkpoints[-1]) + + # Test with the first checkpoint + first_checkpoint_test_results = fitted_model_trainer.test( + fitted_biobert_module, + dataloaders=fitted_model_dataloader, + ckpt_path=first_checkpoint_path, + ) + + # Test with the last checkpoint + last_checkpoint_test_results = fitted_model_trainer.test( + fitted_biobert_module, + dataloaders=fitted_model_dataloader, + ckpt_path=last_checkpoint_path, + ) + + for key in last_checkpoint_test_results[0].keys(): + assert ( + last_checkpoint_test_results[0][key] + > 0 # first_checkpoint_test_results[0][key] + ) + + +@pytest.mark.skip(reason="too resource-intensive") +def test_predicting_without_checkpoint( + biobert_module_function_scope, + dummy_data_module, + trainer_with_temporary_directory, +): + trainer, checkpoint_dir = trainer_with_temporary_directory + fitted_model_dataloader = dummy_data_module.test_dataloader() + trainer.predict( + biobert_module_function_scope, dataloaders=fitted_model_dataloader + ) + + +@pytest.mark.skip(reason="too resource-intensive") +def test_predicting_with_checkpoint(fitted_biobert_module, dummy_data_module): + trainer = fitted_biobert_module.trainer + dataloader = dummy_data_module.train_dataloader() + last_checkpoint_path = trainer.checkpoint_callback.best_model_path + + # Create a DataLoader for the prediction data + trainer.predict( + fitted_biobert_module, + ckpt_path=last_checkpoint_path, + dataloaders=dataloader, + ) diff --git a/tests/integration/models/torch/lighting_modules/test_chemistry_vae.py b/tests/integration/models/torch/lighting_modules/test_chemistry_vae.py new file mode 100644 index 00000000..a55e697d --- /dev/null +++ b/tests/integration/models/torch/lighting_modules/test_chemistry_vae.py @@ -0,0 +1,250 @@ +import os + +import pytest +import torch + +from omegaconf import DictConfig +from pytorch_lightning import LightningDataModule +from pytorch_lightning import LightningModule +from pytorch_lightning import Trainer +from pytorch_lightning.callbacks import ModelCheckpoint +from torch.utils.data import DataLoader +from torch.utils.data import Dataset + +from innofw.constants import Frameworks +from innofw.constants import SegDataKeys +from innofw.core.models.torch.lightning_modules.chemistry_vae import ( + ChemistryVAELightningModule, + ChemistryVAEForwardLightningModule, + ChemistryVAEReverseLightningModule +) + +from innofw.utils.framework import get_losses +from innofw.utils.framework import get_model +from innofw.utils.framework import get_datamodule +from tests.fixtures.config import losses as fixt_losses +from tests.fixtures.config import models as fixt_models +from tests.fixtures.config import optimizers as fixt_optimizers +from tests.fixtures.config import trainers as fixt_trainers +from tests.fixtures.config import schedulers as fixt_schedulers +from tests.fixtures.config import datasets as fixt_datasets + + +class DummyDataset(Dataset): + def __init__(self, num_samples): + self.num_samples = num_samples + + def __getitem__(self, index): + x = torch.rand(3, 32, 32) + y = torch.randint(0, 2, (1, 32, 32)) + return x, y + + def __len__(self): + return self.num_samples + +class DummyDataModule(LightningDataModule): + def __init__(self, num_samples: int, batch_size: int = 2): + super().__init__() + self.num_samples = num_samples + self.batch_size = batch_size + + def setup(self, stage=None): + self.dataset = DummyDataset(self.num_samples) + + def train_dataloader(self): + return DataLoader(self.dataset, batch_size=self.batch_size) + + def val_dataloader(self): + return DataLoader(self.dataset, batch_size=self.batch_size) + + def test_dataloader(self): + return DataLoader(self.dataset, batch_size=self.batch_size) + +@pytest.fixture(scope="module") +def dummy_data_module(): + cfg = DictConfig(fixt_datasets.qsar_datamodule_cfg_w_target) + data_module = get_datamodule(cfg, framework=Frameworks.torch, task="text-vae") + # data_module = DummyDataModule(num_samples=100, batch_size=4) + data_module.setup() # Call the setup method to define the dataset attribute + return data_module + +@pytest.fixture(scope="module") +def vae_module() -> LightningModule: + cfg = DictConfig( + { + "models": fixt_models.text_vae_cfg_w_target, + "trainer": fixt_trainers.trainer_cfg_w_cpu_devices, + "losses": fixt_losses.vae_loss_w_target, + } + ) + model = get_model(cfg.models, cfg.trainer) + losses = get_losses(cfg, "text-vae", Frameworks.torch) + optimizer_cfg = DictConfig(fixt_optimizers.adam_optim_w_target) + scheduler_cfg = DictConfig(fixt_schedulers.linear_w_target) + + module = ChemistryVAELightningModule( + model=model, losses=losses, optimizer_cfg=optimizer_cfg, scheduler_cfg=scheduler_cfg + ) + + return module + +@pytest.fixture(scope="module") +def vae_forward_module() -> LightningModule: + cfg = DictConfig( + { + "models": fixt_models.text_vae_cfg_w_target, + "trainer": fixt_trainers.trainer_cfg_w_cpu_devices, + "losses": fixt_losses.vae_loss_w_target, + } + ) + model = get_model(cfg.models, cfg.trainer) + losses = get_losses(cfg, "text-vae", Frameworks.torch) + optimizer_cfg = DictConfig(fixt_optimizers.adam_optim_w_target) + scheduler_cfg = DictConfig(fixt_schedulers.linear_w_target) + + module = ChemistryVAEForwardLightningModule( + model=model, losses=losses, optimizer_cfg=optimizer_cfg, scheduler_cfg=scheduler_cfg + ) + + return module + +@pytest.fixture(scope="module") +def vae_reverse_module() -> LightningModule: + cfg = DictConfig( + { + "models": fixt_models.text_vae_cfg_w_target, + "trainer": fixt_trainers.trainer_cfg_w_cpu_devices, + "losses": fixt_losses.vae_loss_w_target, + } + ) + model = get_model(cfg.models, cfg.trainer) + losses = get_losses(cfg, "text-vae", Frameworks.torch) + optimizer_cfg = DictConfig(fixt_optimizers.adam_optim_w_target) + scheduler_cfg = DictConfig(fixt_schedulers.linear_w_target) + + module = ChemistryVAEReverseLightningModule( + model=model, losses=losses, optimizer_cfg=optimizer_cfg, scheduler_cfg=scheduler_cfg + ) + + return module + +@pytest.fixture(scope="function") +def vae_module_function_scope() -> LightningModule: + cfg = DictConfig( + { + "models": fixt_models.text_vae_cfg_w_target, + "trainer": fixt_trainers.trainer_cfg_w_cpu_devices, + "losses": fixt_losses.vae_loss_w_target, + } + ) + model = get_model(cfg.models, cfg.trainer) + losses = get_losses(cfg, "text-vae", Frameworks.torch) + optimizer_cfg = DictConfig(fixt_optimizers.adam_optim_w_target) + scheduler_cfg = DictConfig(fixt_schedulers.linear_w_target) + + module = ChemistryVAELightningModule( + model=model, losses=losses, optimizer_cfg=optimizer_cfg, scheduler_cfg=scheduler_cfg + ) + + return module + +@pytest.fixture(scope="module") +def fitted_vae_module( + vae_forward_module: LightningModule, + trainer_with_temporary_directory, + dummy_data_module: DummyDataModule, +): + trainer, _ = trainer_with_temporary_directory + dataloader = dummy_data_module.train_dataloader() + trainer.fit(vae_forward_module, train_dataloaders=dataloader) + vae_forward_module.trainer = ( + trainer # Add the trainer to the fitted module for future use + ) + return vae_forward_module + +@pytest.mark.skipif( + not torch.cuda.is_available(), reason="No GPU is found on this machine" +) +def test_training_with_gpu( + vae_module_function_scope, + trainer_with_temporary_directory, + dummy_data_module, +): + trainer, checkpoint_dir = trainer_with_temporary_directory + dataloader = dummy_data_module.train_dataloader() + trainer.fit(vae_module_function_scope, train_dataloaders=dataloader) + + +def test_training_without_checkpoint( + vae_module_function_scope, + trainer_with_temporary_directory, + dummy_data_module, +): + trainer, checkpoint_dir = trainer_with_temporary_directory + dataloader = dummy_data_module.train_dataloader() + trainer.fit(vae_module_function_scope, train_dataloaders=dataloader) + + +def test_training_with_checkpoint( + fitted_vae_module, + trainer_with_temporary_directory, + dummy_data_module, +): + trainer, checkpoint_dir = trainer_with_temporary_directory + dataloader = dummy_data_module.train_dataloader() + + # First training phase is already done in the fitted_vae_module fixture + last_checkpoint_path = ( + fitted_vae_module.trainer.checkpoint_callback.best_model_path + ) + + # Continue training using the fitted_vae_module + trainer.fit( + fitted_vae_module, + ckpt_path=last_checkpoint_path, + train_dataloaders=dataloader, + ) + +def test_training_forward_with_gpu( + vae_forward_module, + trainer_with_temporary_directory, + dummy_data_module, +): + trainer, checkpoint_dir = trainer_with_temporary_directory + dataloader = dummy_data_module.train_dataloader() + trainer.fit(vae_forward_module, train_dataloaders=dataloader) + +def test_predicting_reverse_without_checkpoint( + vae_reverse_module, + dummy_data_module, + trainer_with_temporary_directory, +): + trainer, checkpoint_dir = trainer_with_temporary_directory + fitted_model_dataloader = dummy_data_module.test_dataloader() + trainer.predict( + vae_reverse_module, dataloaders=fitted_model_dataloader + ) + +def test_predicting_without_checkpoint( + vae_module_function_scope, + dummy_data_module, + trainer_with_temporary_directory, +): + trainer, checkpoint_dir = trainer_with_temporary_directory + fitted_model_dataloader = dummy_data_module.test_dataloader() + trainer.predict( + vae_module_function_scope, dataloaders=fitted_model_dataloader + ) + + +def test_predicting_with_checkpoint(fitted_vae_module, dummy_data_module): + trainer = fitted_vae_module.trainer + dataloader = dummy_data_module.train_dataloader() + last_checkpoint_path = trainer.checkpoint_callback.best_model_path + + # Create a DataLoader for the prediction data + trainer.predict( + fitted_vae_module, + ckpt_path=last_checkpoint_path, + dataloaders=dataloader, + ) diff --git a/tests/integration/models/torch/lighting_modules/test_classification.py b/tests/integration/models/torch/lighting_modules/test_classification.py new file mode 100644 index 00000000..c5370978 --- /dev/null +++ b/tests/integration/models/torch/lighting_modules/test_classification.py @@ -0,0 +1,124 @@ +import os + +import pytest +import torch + + +@pytest.mark.skipif( + not torch.cuda.is_available(), reason="No GPU is found on this machine" +) +def test_training_with_gpu( + classification_module_function_scope, + trainer_with_temporary_directory, + class_dummy_data_module, +): + trainer, checkpoint_dir = trainer_with_temporary_directory + dataloader = class_dummy_data_module.train_dataloader() + trainer.fit(classification_module_function_scope, train_dataloaders=dataloader) + + +def test_training_without_checkpoint( + classification_module_function_scope, + trainer_with_temporary_directory, + class_dummy_data_module, +): + trainer, checkpoint_dir = trainer_with_temporary_directory + dataloader = class_dummy_data_module.train_dataloader() + trainer.fit(classification_module_function_scope, train_dataloaders=dataloader) + + +def test_training_with_checkpoint( + fitted_classification_module, + trainer_with_temporary_directory, + class_dummy_data_module, +): + trainer, checkpoint_dir = trainer_with_temporary_directory + dataloader = class_dummy_data_module.train_dataloader() + + # First training phase is already done in the fitted_classification_module fixture + last_checkpoint_path = ( + fitted_classification_module.trainer.checkpoint_callback.best_model_path + ) + + # Continue training using the fitted_classification_module + trainer.fit( + fitted_classification_module, + ckpt_path=last_checkpoint_path, + train_dataloaders=dataloader, + ) + + +def test_testing_without_checkpoint( + classification_module_function_scope, + class_dummy_data_module, + trainer_with_temporary_directory, +): + trainer, checkpoint_dir = trainer_with_temporary_directory + dataloader = class_dummy_data_module.train_dataloader() + + # Test the loaded model + trainer.test( + classification_module_function_scope, + dataloaders=dataloader, + ) + + +def test_testing_with_checkpoint( + classification_module_function_scope, + fitted_classification_module, + class_dummy_data_module, + trainer_with_temporary_directory, +): + fitted_model_trainer = fitted_classification_module.trainer + fitted_model_dataloader = class_dummy_data_module.test_dataloader() + + # Get the checkpoint directory and list all checkpoint files + checkpoint_dir = fitted_model_trainer.checkpoint_callback.dirpath + checkpoints = sorted(os.listdir(checkpoint_dir)) + first_checkpoint_path = os.path.join(checkpoint_dir, checkpoints[0]) + last_checkpoint_path = os.path.join(checkpoint_dir, checkpoints[-1]) + + # Test with the first checkpoint + first_checkpoint_test_results = fitted_model_trainer.test( + fitted_classification_module, + dataloaders=fitted_model_dataloader, + ckpt_path=first_checkpoint_path, + ) + + # Test with the last checkpoint + last_checkpoint_test_results = fitted_model_trainer.test( + fitted_classification_module, + dataloaders=fitted_model_dataloader, + ckpt_path=last_checkpoint_path, + ) + + for key in last_checkpoint_test_results[0].keys(): + assert ( + last_checkpoint_test_results[0][key] + > 0 # first_checkpoint_test_results[0][key] + ) + + +def test_predicting_without_checkpoint( + classification_module_function_scope, + class_dummy_data_module, + trainer_with_temporary_directory, +): + trainer, checkpoint_dir = trainer_with_temporary_directory + fitted_model_dataloader = class_dummy_data_module.test_dataloader() + trainer.predict( + classification_module_function_scope, dataloaders=fitted_model_dataloader + ) + + +def test_predicting_with_checkpoint(fitted_classification_module, class_dummy_data_module): + trainer = fitted_classification_module.trainer + dataloader = class_dummy_data_module.train_dataloader() + last_checkpoint_path = trainer.checkpoint_callback.best_model_path + + # Create a DataLoader for the prediction data + trainer.predict( + fitted_classification_module, + ckpt_path=last_checkpoint_path, + dataloaders=dataloader, + ) diff --git a/tests/integration/models/torch/lighting_modules/test_detection.py b/tests/integration/models/torch/lighting_modules/test_detection.py new file mode 100644 index 00000000..bd2c058a --- /dev/null +++ b/tests/integration/models/torch/lighting_modules/test_detection.py @@ -0,0 +1,232 @@ +import os + +import pytest +import torch + +from omegaconf import DictConfig +from pytorch_lightning import LightningDataModule +from pytorch_lightning import LightningModule +from pytorch_lightning import Trainer +from pytorch_lightning.callbacks import ModelCheckpoint +from torch.utils.data import DataLoader +from torch.utils.data import Dataset + +from innofw.constants import Frameworks +from innofw.constants import SegDataKeys +from innofw.core.models.torch.lightning_modules.detection import ( + DetectionLightningModule, + _evaluate_iou +) + +from innofw.utils.framework import get_losses +from innofw.utils.framework import get_model +from innofw.utils.framework import get_datamodule +from tests.fixtures.config import losses as fixt_losses +from tests.fixtures.config import models as fixt_models +from tests.fixtures.config import optimizers as fixt_optimizers +from tests.fixtures.config import trainers as fixt_trainers +from tests.fixtures.config import schedulers as fixt_schedulers +from tests.fixtures.config import datasets as fixt_datasets + + +class DummyDataset(Dataset): + def __init__(self, num_samples): + self.num_samples = num_samples + + def __getitem__(self, index): + x = torch.rand(3, 224, 224) + y = torch.randint(0, 2, (1, 224, 224)) + return x, y + + def __len__(self): + return self.num_samples + +class DummyDataModule(LightningDataModule): + def __init__(self, num_samples: int, batch_size: int = 4): + super().__init__() + self.num_samples = num_samples + self.batch_size = batch_size + + def setup(self, stage=None): + self.dataset = DummyDataset(self.num_samples) + + def train_dataloader(self): + return DataLoader(self.dataset, batch_size=self.batch_size) + + def val_dataloader(self): + return DataLoader(self.dataset, batch_size=self.batch_size) + + def test_dataloader(self): + return DataLoader(self.dataset, batch_size=self.batch_size) + +@pytest.fixture(scope="module") +def dummy_data_module(): + cfg = DictConfig(fixt_datasets.wheat_datamodule_cfg_w_target) + data_module = get_datamodule(cfg, framework=Frameworks.torch, task="image-detection") + # data_module = DummyDataModule(num_samples=100, batch_size=4) + data_module.setup() # Call the setup method to define the dataset attribute + return data_module + +# @pytest.mark.skip(reason="some bug") +@pytest.fixture(scope="module") +def detection_module() -> LightningModule: + cfg = DictConfig( + { + "models": fixt_models.faster_rcnn_cfg_w_target, + "trainer": fixt_trainers.trainer_cfg_w_cpu_devices, + "losses": fixt_losses.focal_loss_w_target, + } + ) + model = get_model(cfg.models, cfg.trainer) + losses = get_losses(cfg, "image-detection", Frameworks.torch) + optimizer_cfg = DictConfig(fixt_optimizers.adam_optim_w_target) + scheduler_cfg = DictConfig(fixt_schedulers.linear_w_target) + + module = DetectionLightningModule( + model=model, losses=losses, optimizer_cfg=optimizer_cfg, scheduler_cfg=scheduler_cfg + ) + + return module + + +# @pytest.mark.skip(reason="some bug") +@pytest.fixture(scope="function") +def detection_module_function_scope() -> LightningModule: + cfg = DictConfig( + { + "models": fixt_models.faster_rcnn_cfg_w_target, + "trainer": fixt_trainers.trainer_cfg_w_cpu_devices, + "losses": fixt_losses.focal_loss_w_target, + } + ) + model = get_model(cfg.models, cfg.trainer) + losses = get_losses(cfg, "image-detection", Frameworks.torch) + optimizer_cfg = DictConfig(fixt_optimizers.adam_optim_w_target) + scheduler_cfg = DictConfig(fixt_schedulers.linear_w_target) + + module = DetectionLightningModule( + model=model, losses=losses, optimizer_cfg=optimizer_cfg, scheduler_cfg=scheduler_cfg + ) + + return module + +# @pytest.mark.skip(reason="some bug") +@pytest.fixture(scope="module") +def fitted_detection_module( + detection_module: LightningModule, + trainer_with_temporary_directory, + dummy_data_module: DummyDataModule, +): + trainer, _ = trainer_with_temporary_directory + dataloader = dummy_data_module.train_dataloader() + trainer.fit(detection_module, train_dataloaders=dataloader) + detection_module.trainer = ( + trainer # Add the trainer to the fitted module for future use + ) + return detection_module + +# @pytest.mark.skip(reason="some bug") +@pytest.mark.skipif( + not torch.cuda.is_available(), reason="No GPU is found on this machine" +) +def test_training_with_gpu( + detection_module_function_scope, + trainer_with_temporary_directory, + dummy_data_module, +): + trainer, checkpoint_dir = trainer_with_temporary_directory + dataloader = dummy_data_module.train_dataloader() + trainer.fit(detection_module_function_scope, train_dataloaders=dataloader) + +# @pytest.mark.skip(reason="some bug") +def test_training_without_checkpoint( + detection_module_function_scope, + trainer_with_temporary_directory, + dummy_data_module, +): + trainer, checkpoint_dir = trainer_with_temporary_directory + dataloader = dummy_data_module.train_dataloader() + trainer.fit(detection_module_function_scope, train_dataloaders=dataloader) + +# @pytest.mark.skip(reason="some bug") +def test_training_with_checkpoint( + fitted_detection_module, + trainer_with_temporary_directory, + dummy_data_module, +): + trainer, checkpoint_dir = trainer_with_temporary_directory + dataloader = dummy_data_module.train_dataloader() + + # First training phase is already done in the fitted_detection_module fixture + last_checkpoint_path = ( + fitted_detection_module.trainer.checkpoint_callback.best_model_path + ) + + # Continue training using the fitted_detection_module + trainer.fit( + fitted_detection_module, + ckpt_path=last_checkpoint_path, + train_dataloaders=dataloader, + ) +@pytest.mark.skip(reason="some bug") +def test_testing_without_checkpoint( + detection_module_function_scope, + dummy_data_module, + trainer_with_temporary_directory, +): + trainer, checkpoint_dir = trainer_with_temporary_directory + dataloader = dummy_data_module.train_dataloader() + + # Test the loaded model + trainer.test( + detection_module_function_scope, + dataloaders=dataloader, + ) + +@pytest.mark.skip(reason="some bug") +def test_testing_with_checkpoint( + detection_module_function_scope, + fitted_detection_module, + dummy_data_module, + trainer_with_temporary_directory, +): + fitted_model_trainer = fitted_detection_module.trainer + fitted_model_dataloader = dummy_data_module.test_dataloader() + + # Get the checkpoint directory and list all checkpoint files + checkpoint_dir = fitted_model_trainer.checkpoint_callback.dirpath + checkpoints = sorted(os.listdir(checkpoint_dir)) + first_checkpoint_path = os.path.join(checkpoint_dir, checkpoints[0]) + last_checkpoint_path = os.path.join(checkpoint_dir, checkpoints[-1]) + + # Test with the first checkpoint + first_checkpoint_test_results = fitted_model_trainer.test( + fitted_detection_module, + dataloaders=fitted_model_dataloader, + ckpt_path=first_checkpoint_path, + ) + + # Test with the last checkpoint + last_checkpoint_test_results = fitted_model_trainer.test( + fitted_detection_module, + dataloaders=fitted_model_dataloader, + ckpt_path=last_checkpoint_path, + ) + + for key in last_checkpoint_test_results[0].keys(): + assert ( + last_checkpoint_test_results[0][key] + > 0 # first_checkpoint_test_results[0][key] + ) + + +def test__evaluate_iou(): + + target = {"boxes": torch.tensor([[10, 10, 10 ,10]])} + pred_none = {"boxes": torch.tensor([])} + iou = _evaluate_iou(target, pred_none) + assert isinstance(iou, torch.Tensor) + + pred_is = {"boxes": torch.tensor([[15, 15, 10, 10]])} + iou_ = _evaluate_iou(target, pred_is) + assert isinstance(iou_, torch.Tensor) \ No newline at end of file diff --git a/tests/integration/models/torch/lighting_modules/test_segmentation.py b/tests/integration/models/torch/lighting_modules/test_segmentation.py index 273dddc0..7f280695 100644 --- a/tests/integration/models/torch/lighting_modules/test_segmentation.py +++ b/tests/integration/models/torch/lighting_modules/test_segmentation.py @@ -10,30 +10,30 @@ def test_training_with_gpu( segmentation_module_function_scope, trainer_with_temporary_directory, - dummy_data_module, + seg_dummy_data_module, ): trainer, checkpoint_dir = trainer_with_temporary_directory - dataloader = dummy_data_module.train_dataloader() + dataloader = seg_dummy_data_module.train_dataloader() trainer.fit(segmentation_module_function_scope, train_dataloaders=dataloader) def test_training_without_checkpoint( segmentation_module_function_scope, trainer_with_temporary_directory, - dummy_data_module, + seg_dummy_data_module, ): trainer, checkpoint_dir = trainer_with_temporary_directory - dataloader = dummy_data_module.train_dataloader() + dataloader = seg_dummy_data_module.train_dataloader() trainer.fit(segmentation_module_function_scope, train_dataloaders=dataloader) def test_training_with_checkpoint( fitted_segmentation_module, trainer_with_temporary_directory, - dummy_data_module, + seg_dummy_data_module, ): trainer, checkpoint_dir = trainer_with_temporary_directory - dataloader = dummy_data_module.train_dataloader() + dataloader = seg_dummy_data_module.train_dataloader() # First training phase is already done in the fitted_segmentation_module fixture last_checkpoint_path = ( @@ -50,11 +50,11 @@ def test_training_with_checkpoint( def test_testing_without_checkpoint( segmentation_module_function_scope, - dummy_data_module, + seg_dummy_data_module, trainer_with_temporary_directory, ): trainer, checkpoint_dir = trainer_with_temporary_directory - dataloader = dummy_data_module.train_dataloader() + dataloader = seg_dummy_data_module.train_dataloader() # Test the loaded model trainer.test( @@ -66,11 +66,11 @@ def test_testing_without_checkpoint( def test_testing_with_checkpoint( segmentation_module_function_scope, fitted_segmentation_module, - dummy_data_module, + seg_dummy_data_module, trainer_with_temporary_directory, ): fitted_model_trainer = fitted_segmentation_module.trainer - fitted_model_dataloader = dummy_data_module.test_dataloader() + fitted_model_dataloader = seg_dummy_data_module.test_dataloader() # Get the checkpoint directory and list all checkpoint files checkpoint_dir = fitted_model_trainer.checkpoint_callback.dirpath @@ -101,19 +101,19 @@ def test_testing_with_checkpoint( def test_predicting_without_checkpoint( segmentation_module_function_scope, - dummy_data_module, + seg_dummy_data_module, trainer_with_temporary_directory, ): trainer, checkpoint_dir = trainer_with_temporary_directory - fitted_model_dataloader = dummy_data_module.test_dataloader() + fitted_model_dataloader = seg_dummy_data_module.test_dataloader() trainer.predict( segmentation_module_function_scope, dataloaders=fitted_model_dataloader ) -def test_predicting_with_checkpoint(fitted_segmentation_module, dummy_data_module): +def test_predicting_with_checkpoint(fitted_segmentation_module, seg_dummy_data_module): trainer = fitted_segmentation_module.trainer - dataloader = dummy_data_module.train_dataloader() + dataloader = seg_dummy_data_module.train_dataloader() last_checkpoint_path = trainer.checkpoint_callback.best_model_path # Create a DataLoader for the prediction data diff --git a/tests/integration/optimizers/test_adam.py b/tests/integration/optimizers/test_adam.py index 09091939..407b95bb 100755 --- a/tests/integration/optimizers/test_adam.py +++ b/tests/integration/optimizers/test_adam.py @@ -17,7 +17,8 @@ def test_optimizer_creation(): cfg = DictConfig( { "optimizers": { - "_target_": "torch.optim.Adam", + # "_target_": "torch.optim.Adam", + "_target_": "innofw.core.optimizers.custom_optimizers.optimizers.ADAM", "lr": 1e-5, } } @@ -32,7 +33,9 @@ def test_optimizer_creation(): def test_optimizer_creation_wrong_framework(): cfg = DictConfig( - {"optimizers": {"_target_": "torch.optim.Adam", "lr": 1e-5}} + # {"optimizers": {"_target_": "torch.optim.Adam", "lr": 1e-5}} + {"optimizers": {"_target_": "innofw.core.optimizers.custom_optimizers.optimizers.ADAM", + "lr": 1e-5}} ) task = "image-segmentation" framework = Frameworks.sklearn diff --git a/tests/integration/optimizers/test_lion.py b/tests/integration/optimizers/test_lion.py index 4ac7a690..778b41fb 100644 --- a/tests/integration/optimizers/test_lion.py +++ b/tests/integration/optimizers/test_lion.py @@ -1,6 +1,7 @@ # other import hydra import pytest +import torch import torch.optim from omegaconf import DictConfig from segmentation_models_pytorch import Unet @@ -45,3 +46,40 @@ def test_optimizer_creation_wrong_framework(): optim = get_optimizer( cfg, "optimizers", task, framework, params=model.parameters() ) +from innofw.core.optimizers.custom_optimizers.optimizers import LION + +@pytest.fixture +def simple_model(): + # Define a simple model with one parameter + torch.manual_seed(42) + return torch.tensor([1.0], requires_grad=True) + +def test_step_function(simple_model): + # Define optimizer parameters + lr = 0.1 + b1 = 0.9 + b2 = 0.99 + wd = 0.0 + + # Create an instance of the _Lion optimizer + optimizer = LION._Lion([simple_model], lr=lr, b1=b1, b2=b2, wd=wd) + + # Define a simple loss function + loss_fn = torch.nn.MSELoss() + + # Perform a single optimization step + def closure(): + output = simple_model * 2 # Some simple model + loss = loss_fn(output, torch.tensor([5.0])) + loss.backward() + return loss + + loss_before = closure() + optimizer.step(closure) + loss_after = closure() + + # Check if the loss has decreased after optimization + assert loss_after < loss_before + + # Check if the model parameter has been updated + assert not torch.all(torch.eq(simple_model, torch.tensor([1.0]))) diff --git a/tests/integration/optimizers/test_sgd.py b/tests/integration/optimizers/test_sgd.py index 2c4d169c..f1fe9846 100755 --- a/tests/integration/optimizers/test_sgd.py +++ b/tests/integration/optimizers/test_sgd.py @@ -17,7 +17,7 @@ def test_optimizer_creation(): cfg = DictConfig( { "optimizers": { - "_target_": "torch.optim.SGD", + "_target_": "innofw.core.optimizers.custom_optimizers.optimizers.SGD", "lr": 1e-5, } } @@ -37,7 +37,7 @@ def test_optimizer_creation_wrong_framework(): cfg = DictConfig( { "optimizers": { - "_target_": "torch.optim.SGD", + "_target_": "innofw.core.optimizers.custom_optimizers.optimizers.SGD", "lr": 1e-5, } } diff --git a/tests/integration/schema/test_configs.py b/tests/integration/schema/test_configs.py index 4942257b..402eaab9 100644 --- a/tests/integration/schema/test_configs.py +++ b/tests/integration/schema/test_configs.py @@ -1,6 +1,8 @@ # import logging - +import os +import shutil +from pathlib import Path import pytest import yaml from omegaconf import DictConfig @@ -37,35 +39,39 @@ def test_models(model_config_file): get_model(model_config, base_trainer_on_cpu_cfg) -@pytest.mark.skip(reason="some problems with dataset downloading") +# @pytest.mark.skip(reason="some problems with dataset downloading") @pytest.mark.parametrize(["dataset_config_file"], datasets_config_files) def test_datasets(dataset_config_file, tmp_path): - with open(dataset_config_file, "r") as f: - dataset_config = DictConfig(yaml.safe_load(f)) - - for stage in ["train", "test", "infer"]: - try: - dataset_config[stage]["target"] = tmp_path / stage - except: - pass - - logging.info(dataset_config) - - dm = None - - for framework in Frameworks: - try: - dm = get_datamodule(dataset_config, framework) - break - except Exception as e: - logging.exception(e) - - assert dm is not None - - # tmp_path.rmdir() - - -@pytest.mark.skip(reason="some problems with dataset downloading") + if os.path.isfile(dataset_config_file): + with open(dataset_config_file, "r") as f: + dataset_config = DictConfig(yaml.safe_load(f)) + + for stage in ["train", "test", "infer"]: + try: + dataset_config[stage]["target"] = tmp_path / stage + except: + pass + + logging.info(dataset_config) + + dm = None + task = dataset_config['task'][0] + for framework in Frameworks: + try: + dm = get_datamodule(dataset_config, framework, task) + break + except Exception as e: + logging.exception(e) + + assert dm is not None + else: + pass + for dir_name in ['data', 'logs']: + if os.path.exists(get_project_root() / dir_name) and os.path.isdir(get_project_root() / dir_name): + shutil.rmtree(get_project_root() / dir_name) + + +# @pytest.mark.skip(reason="some problems with dataset downloading") @pytest.mark.parametrize(["experiment_config_file"], experiment_config_files) def test_experiments(experiment_config_file): from hydra import compose, initialize @@ -73,9 +79,12 @@ def test_experiments(experiment_config_file): GlobalHydra.instance().clear() initialize(config_path="../../../config", job_name="test_app") + + experiment_file = f"{str(os.path.splitext(experiment_config_file)[0]).split('/experiments/')[-1]}" + cfg = compose( config_name="train", - overrides=[f"experiments={experiment_config_file.stem}"], + overrides=[f"experiments={experiment_file}"], # experiment_config_file.stem return_hydra_config=True, ) get_experiment(cfg) diff --git a/tests/unit/active_learning/test_base_learner.py b/tests/unit/active_learning/test_base_learner.py new file mode 100644 index 00000000..b9b84558 --- /dev/null +++ b/tests/unit/active_learning/test_base_learner.py @@ -0,0 +1,62 @@ +import pytest + +from innofw.constants import Frameworks +from innofw.core.models.sklearn_adapter import SklearnAdapter +from innofw.core.active_learning.learners import BaseActiveLearner +from innofw.utils.framework import get_datamodule +from innofw.utils.framework import get_model +from tests.fixtures.config.datasets import qm9_datamodule_cfg_w_target +from tests.fixtures.config.models import baselearner_cfg_w_target +from tests.fixtures.config.trainers import base_trainer_on_cpu_cfg +from tests.utils import get_test_folder_path + +class ConcreteActiveLearner(BaseActiveLearner): + def eval_model(self, X, y): + # Implementation of eval_model method + pass + + def predict_model(self, X): + # Implementation of predict_model method + pass + + def obtain_most_uncertain(self, y): + # Implementation of obtain_most_uncertain method + pass + +# @pytest.mark.skip(reason="some bug") +def test_base_active_learner_creation(): + model = get_model( + baselearner_cfg_w_target, base_trainer_on_cpu_cfg + ) + task = "qsar-regression" + datamodule = get_datamodule( + qm9_datamodule_cfg_w_target, Frameworks.sklearn, task=task + ) + + sut = ConcreteActiveLearner( + model=model, + datamodule=datamodule, + ) + + assert sut is not None + +# @pytest.mark.skip(reason="some bug") +def test_base_active_learner_run(): + model = get_model( + baselearner_cfg_w_target, base_trainer_on_cpu_cfg + ) + model = SklearnAdapter(model=model, metrics=None, log_dir="./logs/test/test1/") + + task = "qsar-regression" + datamodule = get_datamodule( + qm9_datamodule_cfg_w_target, Frameworks.sklearn, task=task + ) + + sut = ConcreteActiveLearner( + model=model, + datamodule=datamodule, + ) + + sut.run(ckpt_path=str(get_test_folder_path() /'weights/regression_house_prices/lin_reg.pickle')) + + assert sut is not None diff --git a/tests/unit/active_learning/test_catboost_learner.py b/tests/unit/active_learning/test_catboost_learner.py index 31fb44a0..4f535140 100644 --- a/tests/unit/active_learning/test_catboost_learner.py +++ b/tests/unit/active_learning/test_catboost_learner.py @@ -1,19 +1,25 @@ from innofw.constants import Frameworks +from innofw.core import InnoModel +from innofw.core.models.catboost_adapter import CatBoostAdapter from innofw.core.active_learning.learners import CatBoostActiveLearner from innofw.utils.framework import get_datamodule from innofw.utils.framework import get_model -from tests.fixtures.config.datasets import qm9_datamodule_cfg_w_target +from innofw.utils.getters import get_trainer_cfg, get_log_dir, get_a_learner +from tests.fixtures.config.datasets import house_prices_datamodule_cfg_w_target from tests.fixtures.config.models import catboost_with_uncertainty_cfg_w_target from tests.fixtures.config.trainers import base_trainer_on_cpu_cfg +from hydra.core.hydra_config import HydraConfig +from omegaconf import OmegaConf, DictConfig +import hydra def test_catboost_active_learner_creation(): model = get_model( catboost_with_uncertainty_cfg_w_target, base_trainer_on_cpu_cfg ) - task = "qsar-regression" + task = "table-regression" datamodule = get_datamodule( - qm9_datamodule_cfg_w_target, Frameworks.catboost, task=task + house_prices_datamodule_cfg_w_target, Frameworks.catboost, task=task ) sut = CatBoostActiveLearner( @@ -22,3 +28,70 @@ def test_catboost_active_learner_creation(): ) assert sut is not None + + +# def test_base_learner_run(): +# model = get_model( +# catboost_with_uncertainty_cfg_w_target, base_trainer_on_cpu_cfg +# ) +# #GR_230822_ASdw31ga_catboost_industry_data +# # /workspace/innofw/config/experiments/regression/GR_230822_ASdw31ga_catboost_industry_data.yaml +# # /workspace/innofw/config/train.yaml + +# model = InnoModel(model, log_dir="./logs/test/test1/") +# task = "table-regression" +# datamodule = get_datamodule( +# house_prices_datamodule_cfg_w_target, Frameworks.catboost, task=task +# ) + +# sut = CatBoostActiveLearner( +# model=model, +# datamodule=datamodule, +# ) + +# sut.run(ckpt_path='/workspace/innofw/tests/weights/catboost_industry_data/model.pickle') + +# assert sut is not None + +# from innofw.utils.loggers import setup_clear_ml, setup_wandb +# def test_base_learner_run_empty_pool(): +# # if not config.get("experiment_name"): +# # hydra_cfg = HydraConfig.get() +# # experiment_name = OmegaConf.to_container(hydra_cfg.runtime.choices)[ +# # "experiments" +# # ] +# # config.experiment_name = experiment_name + +# config = OmegaConf.load('/workspace/innofw/config/experiments/regression/GR_230822_ASdw31ga_catboost_industry_data.yaml') +# print(config) +# setup_clear_ml(config) +# setup_wandb(config) +# model = get_model( +# catboost_with_uncertainty_cfg_w_target, base_trainer_on_cpu_cfg +# ) + + +# # wrap the model +# model_params = { +# "model": model, +# "log_dir": "./logs/test/test1/", +# } +# model = InnoModel(**model_params) + + +# task = "table-regression" +# datamodule = get_datamodule( +# house_prices_datamodule_cfg_w_target, Frameworks.catboost, task=task +# ) + +# datamodule.pool_idxs = [] +# a_learner = get_a_learner(config, model, datamodule) + +# # sut = CatBoostActiveLearner( +# # model=model, +# # datamodule=datamodule, +# # ) + +# ret = a_learner.run(datamodule, ckpt_path='/workspace/innofw/tests/weights/catboost_industry_data/model.pickle') + +# assert ret is None \ No newline at end of file diff --git a/tests/unit/active_learning/test_datamodule.py b/tests/unit/active_learning/test_datamodule.py index edfa80d8..287c7ab9 100644 --- a/tests/unit/active_learning/test_datamodule.py +++ b/tests/unit/active_learning/test_datamodule.py @@ -1,5 +1,5 @@ from innofw.constants import Frameworks -from innofw.core.active_learning.datamodule import ActiveDataModule +from innofw.core.active_learning.datamodule import ActiveDataModule, get_active_datamodule from innofw.utils.framework import get_datamodule from tests.fixtures.config.datasets import qm9_datamodule_cfg_w_target @@ -10,5 +10,15 @@ def test_active_datamodule_creation(): qm9_datamodule_cfg_w_target, Frameworks.catboost, task=task ) sut = ActiveDataModule(datamodule=datamodule) + assert sut is not None + sut.setup() + assert len(sut.train_dataloader()) == 2 + +def test_get_active_datamodule(): + task = "qsar-regression" + datamodule = get_datamodule( + qm9_datamodule_cfg_w_target, Frameworks.catboost, task=task + ) + sut = get_active_datamodule assert sut is not None diff --git a/tests/unit/augmentations/custom/test_ndvi.py b/tests/unit/augmentations/custom/test_ndvi.py index 19c042f0..cc39ab96 100644 --- a/tests/unit/augmentations/custom/test_ndvi.py +++ b/tests/unit/augmentations/custom/test_ndvi.py @@ -8,5 +8,8 @@ def test_standardize_ndvi(): img = np.random.randint(-1, 1, (2, 3, 224, 224)) prep_img = preprocessing.apply(img) + init_args = preprocessing.get_transform_init_args_names() + + assert init_args is () assert prep_img.min() >= 0 and prep_img.max() <= 1 diff --git a/tests/unit/augmentations/custom/test_preprocessing.py b/tests/unit/augmentations/custom/test_preprocessing.py new file mode 100644 index 00000000..87954624 --- /dev/null +++ b/tests/unit/augmentations/custom/test_preprocessing.py @@ -0,0 +1,22 @@ +from innofw.core.augmentations.preprocessing import DivideBy255, ToFloatWClip +import numpy as np + +def test_divide_by_255(): + transf = DivideBy255() + + image = np.ones((64, 64, 3)) * 255 + res_image = transf.apply(image) + init_args = transf.get_transform_init_args_names() + + assert init_args is () + assert np.min(res_image)==1 and np.max(res_image)==1 + +def test_to_float_w_clip(): + transf = ToFloatWClip(max_value=100) + + image = np.ones((64, 64, 3)) * 100 + res_image = transf.apply(image) + init_args = transf.get_transform_init_args_names() + + assert init_args is () + assert np.min(res_image)==1 and np.max(res_image)==1 \ No newline at end of file diff --git a/tests/unit/augmentations/providers/albumentations/test_classification.py b/tests/unit/augmentations/providers/albumentations/test_classification.py deleted file mode 100644 index 72014d46..00000000 --- a/tests/unit/augmentations/providers/albumentations/test_classification.py +++ /dev/null @@ -1,40 +0,0 @@ -import albumentations as A -import numpy as np - -from innofw.core.augmentations import Augmentation -from innofw.utils.config import read_cfg -from innofw.utils.framework import get_augmentations - - -def test_albumentations(): - albumenatations_transform = A.Compose( - [ - A.RandomCrop( - width=256, - height=256, - ), - A.HorizontalFlip(p=0.5), - A.RandomBrightnessContrast(p=0.2), - ] - ) - aug = Augmentation(albumenatations_transform) - assert aug is not None - - -def test_stages(): - cfg = read_cfg( - overrides=[ - "augmentations_train=linear-roads-bin-seg", - "experiments=regression/KA_130722_9f7134db_linear_regression", - ] - ) - aug = get_augmentations(cfg["augmentations_train"]["augmentations"]) - img = np.random.randint(0, 255, (3, 64, 64)) - - aug_img = Augmentation(aug)(img) - assert aug_img.min() >= 0 and aug_img.max() <= 1 - - mask = np.random.randint(0, 2, (3, 64, 64)) - aug_img, aug_mask = Augmentation(aug)(img, mask) - assert aug_img.min() >= 0 and aug_img.max() <= 1 - assert all(np.unique(aug_mask) == np.unique(mask)) # should not be any division diff --git a/tests/unit/augmentations/providers/albumentations/test_segmentation.py b/tests/unit/augmentations/providers/albumentations/test_segmentation.py new file mode 100644 index 00000000..7cd40ad1 --- /dev/null +++ b/tests/unit/augmentations/providers/albumentations/test_segmentation.py @@ -0,0 +1,66 @@ +import albumentations as A +import numpy as np +import pytest +from hydra.core.global_hydra import GlobalHydra + +from innofw.core.augmentations import Augmentation +from innofw.core.augmentations import get_augs_adapter, register_augmentations_adapter +from innofw.utils.config import read_cfg, read_cfg_2_dict +from innofw.utils.framework import get_augmentations + + +def test_albumentations(): + albumenatations_transform = A.Compose( + [ + A.RandomCrop( + width=256, + height=256, + ), + A.HorizontalFlip(p=0.5), + A.RandomBrightnessContrast(p=0.2), + ] + ) + aug = Augmentation(albumenatations_transform) + assert aug is not None + + +# @pytest.mark.skip(reason="some problems with config") +def test_stages(): + GlobalHydra.instance().clear() + cfg = read_cfg( + + overrides=[ + "augmentations_train=linear-roads-bin-seg", + "experiments=semantic-segmentation/linear-roads-bin-seg/KA_160223_39ek249_linear_roads", #regression/KA_130722_9f7134db_linear_regression + ]) + + aug = get_augmentations(cfg.get("augmentations_train")) + img = np.random.randint(0, 255, (3, 64, 64)) + + aug_img = Augmentation(aug)(img) + assert aug_img.min() >= 0 and aug_img.max() <= 1 + + mask = np.random.randint(0, 2, (3, 64, 64)) + aug_img, aug_mask = Augmentation(aug)(img, mask) + assert aug_img.min() >= 0 and aug_img.max() <= 1 + assert all(np.unique(aug_mask) == np.unique(mask)) # should not be any division + + #test wrong channel order handling + img = np.random.randint(0,255, (64, 64, 3)) + mask = np.random.randint(0, 2, (64, 64, 3)) + aug_img = Augmentation(aug)(img, mask) + assert aug_img[0].shape[0] == 3 and len(aug_img[0].shape) == 3 + +def test_torch_tensor_postproceessing(): + GlobalHydra.instance().clear() + cfg = read_cfg( + + overrides=[ + "augmentations_train=linear-roads-bin-seg-test", + "experiments=semantic-segmentation/linear-roads-bin-seg/KA_160223_39ek249_linear_roads", #regression/KA_130722_9f7134db_linear_regression + ]) + + aug = get_augmentations(cfg.get("augmentations_train")) + img = np.random.randint(0, 255, (64, 64, 3)) + aug_img = Augmentation(aug)(img) + assert aug_img.shape[0] == 3 and len(aug_img.shape) == 3 diff --git a/tests/unit/augmentations/providers/torchvision/test_classification.py b/tests/unit/augmentations/providers/torchvision/test_classification.py index bac71535..47389064 100644 --- a/tests/unit/augmentations/providers/torchvision/test_classification.py +++ b/tests/unit/augmentations/providers/torchvision/test_classification.py @@ -1,6 +1,7 @@ import torchvision.transforms as T from innofw.core.augmentations import Augmentation +from innofw.core.augmentations.torchvision_adapter import TorchvisionAdapter def test_torchvision(): @@ -14,3 +15,4 @@ def test_torchvision(): aug = Augmentation(torchvision_transform) assert aug is not None + assert repr(aug).startswith("Torchvision: Compose(") \ No newline at end of file diff --git a/tests/unit/clear_ml/test_clear_ml.py b/tests/unit/clear_ml/test_clear_ml.py index dba599d2..135a5e9a 100755 --- a/tests/unit/clear_ml/test_clear_ml.py +++ b/tests/unit/clear_ml/test_clear_ml.py @@ -1,3 +1,6 @@ +import os +import shutil + import pytest from omegaconf import OmegaConf @@ -5,6 +8,7 @@ from dataclasses import dataclass from innofw.utils.loggers import setup_clear_ml +from innofw.utils import get_project_root @dataclass @@ -74,4 +78,8 @@ def test_clear_ml_agent_execution(mocker): cfg = OmegaConf.create(cfg) cfg["clear_ml"]["queue"] = "no_queue" task = setup_clear_ml(cfg) + + for dir_name in ['data', 'logs']: + if os.path.exists(get_project_root() / dir_name) and os.path.isdir(get_project_root() / dir_name): + shutil.rmtree(get_project_root() / dir_name) assert task is not None diff --git a/tests/unit/datamodules/lightning_datamodules/test_hdf5.py b/tests/unit/datamodules/lightning_datamodules/test_hdf5.py new file mode 100644 index 00000000..d3e09610 --- /dev/null +++ b/tests/unit/datamodules/lightning_datamodules/test_hdf5.py @@ -0,0 +1,50 @@ +import pytest + +from innofw.constants import Frameworks +from innofw.constants import Stages +from innofw.core.datamodules.lightning_datamodules.semantic_segmentation.hdf5 import ( + HDF5LightningDataModule, +) +from innofw.utils.framework import get_datamodule +from tests.fixtures.config.datasets import arable_segmentation_cfg_w_target + +# local modules + + +def test_smoke(): + # create a datamodule + fw = Frameworks.torch + task = "image-segmentation" + dm: HDF5LightningDataModule = get_datamodule( + arable_segmentation_cfg_w_target, fw, task=task + ) + assert dm is not None + + # initialize train and test datasets + dm.setup #(Stages.train) + assert dm.channels_num is not None + assert dm.val_size is not None + assert dm.random_seed is not None + assert dm.w_sampler is not None + assert dm.train is not None + assert dm.test is not None + + + +@pytest.mark.parametrize("stage", [Stages.train]) #, Stages.test]) +def test_train_datamodule(stage): + # create datamodule + fw = Frameworks.torch + task = "image-segmentation" + dm: HDF5LightningDataModule = get_datamodule( + arable_segmentation_cfg_w_target, fw, task=task + ) + assert dm is not None + + # initialize train and test datasets + dm.setup(stage) + + + # get dataloader by stage + dl = dm.get_stage_dataloader(stage) + assert dl is not None diff --git a/tests/unit/datamodules/lightning_datamodules/test_stroke.py b/tests/unit/datamodules/lightning_datamodules/test_stroke.py new file mode 100644 index 00000000..ce4dc902 --- /dev/null +++ b/tests/unit/datamodules/lightning_datamodules/test_stroke.py @@ -0,0 +1,48 @@ +import pytest + +from innofw.constants import Frameworks +from innofw.constants import Stages +from innofw.core.datamodules.lightning_datamodules.semantic_segmentation.stroke_dm import ( + StrokeSegmentationDatamodule, +) +from innofw.utils.framework import get_datamodule +from tests.fixtures.config.datasets import stroke_segmentation_datamodule_cfg_w_target + +# local modules + +# @pytest.mark.skip +def test_smoke(): + # create a qsar datamodule + fw = Frameworks.torch + task = "image-segmentation" + dm: StrokeSegmentationDatamodule = get_datamodule( + stroke_segmentation_datamodule_cfg_w_target, fw, task=task + ) + assert dm is not None + + # initialize train and test datasets + dm.setup() + + assert dm.channels_num is not None + assert dm.val_size is not None + assert dm.random_seed is not None + assert dm.train is not None + assert dm.test is not None + +# @pytest.mark.skip +@pytest.mark.parametrize("stage", [Stages.train, Stages.test]) +def test_train_datamodule(stage): + # create a qsar datamodule + fw = Frameworks.torch + task = "image-segmentation" + dm: StrokeSegmentationDatamodule = get_datamodule( + stroke_segmentation_datamodule_cfg_w_target, fw, task=task + ) + assert dm is not None + + # initialize train and test datasets + dm.setup(stage) + + # get dataloader by stage + dl = dm.get_stage_dataloader(stage) + assert dl is not None diff --git a/tests/unit/datamodules/lightning_datamodules/test_timeseries.py b/tests/unit/datamodules/lightning_datamodules/test_timeseries.py new file mode 100644 index 00000000..21e7096e --- /dev/null +++ b/tests/unit/datamodules/lightning_datamodules/test_timeseries.py @@ -0,0 +1,48 @@ +import pytest + +from innofw.constants import Frameworks +from innofw.constants import Stages +from innofw.core.datamodules.lightning_datamodules.anomaly_detection_timeseries_dm import ( + TimeSeriesLightningDataModule, +) +from innofw.utils.framework import get_datamodule +from tests.fixtures.config.datasets import anomaly_detection_timeseries_datamodule_cfg_w_target + +# local modules + + +def test_smoke(): + # create a datamodule + fw = Frameworks.torch + task = "anomaly-detection-timeseries" + dm: TimeSeriesLightningDataModule = get_datamodule( + anomaly_detection_timeseries_datamodule_cfg_w_target, fw, task=task + ) + assert dm is not None + + # initialize train and test datasets + dm.setup #(Stages.train) + + assert dm.train is not None + assert dm.test is not None + + + +@pytest.mark.parametrize("stage", [Stages.train, Stages.test]) +def test_train_datamodule(stage): + # create datamodule + fw = Frameworks.torch + task = "anomaly-detection-timeseries" + dm: TimeSeriesLightningDataModule = get_datamodule( + anomaly_detection_timeseries_datamodule_cfg_w_target, fw, task=task + ) + assert dm is not None + + # initialize train and test datasets + dm.setup(stage) + # assert sut.train_ds is not None + # assert sut.val_ds is not None + + # get dataloader by stage + dl = dm.get_stage_dataloader(stage) + assert dl is not None diff --git a/tests/unit/dataset/semantic_segmentation/test_hdf5_old.py b/tests/unit/dataset/semantic_segmentation/test_hdf5_old.py new file mode 100644 index 00000000..ecd129da --- /dev/null +++ b/tests/unit/dataset/semantic_segmentation/test_hdf5_old.py @@ -0,0 +1,149 @@ +import numpy as np +import pytest +import torch +from typing import List +from hydra.errors import InstantiationException +from hydra.utils import instantiate +from numpy import ndarray +from omegaconf import DictConfig + +from innofw.constants import SegDataKeys +from innofw.constants import Frameworks +from innofw.utils.framework import get_augmentations, get_obj +from innofw.core.datasets.segmentation_hdf5_old_pipe import ( + Dataset, + DatasetUnion, + TiledDataset, + _to_tensor, + _augment_and_preproc, + _get_preprocessing_fn, + _get_class_weights +) +from tests.fixtures.config.augmentations import ( + resize_augmentation_albu as resize_augmentation, +) +from tests.utils import get_test_folder_path +from tests.fixtures.config.datasets import arable_segmentation_cfg_w_target + +datasets = [str(get_test_folder_path() / "data/images/segmentation/arable/test/test.hdf5"), + str(get_test_folder_path() / "data/images/segmentation/arable/train/train.hdf5")] + +@pytest.mark.parametrize( + ["path_to_hdf5", "with_mosaic", 'augmentations', "in_channels"], + [ + [str(get_test_folder_path() / "data/images/segmentation/arable/train/train.hdf5"), + False, + resize_augmentation, + 4], + [str(get_test_folder_path() / "data/images/segmentation/arable/test/test.hdf5"), + True, + None, + 4], + [datasets, + True, + None, + 4], + ] + +) +def test_read(path_to_hdf5, with_mosaic, augmentations, in_channels): + framework = Frameworks.torch + task = "image-segmentation" + # augmentations = get_augmentations(resize_augmentation) + aug = get_obj(resize_augmentation, "augmentations", task, framework) + if isinstance(path_to_hdf5, List): + ds = DatasetUnion( + [ + Dataset(path_to_hdf5=f, + in_channels=in_channels, + augmentations=aug, + ) + for f in path_to_hdf5 + ]) + else: + ds = Dataset(path_to_hdf5=path_to_hdf5, + in_channels=in_channels, + augmentations=aug) + # ds: Dataset = instantiate(cfg, _convert_="partial") + assert ds is not None + # assert ds.len > 0 + + # ds.setup() + + + item = ds[0] + assert item is not None + + # for item in ds: + + assert isinstance(item[SegDataKeys.image], ndarray) or isinstance( + item[SegDataKeys.image], torch.Tensor + ) + assert item[SegDataKeys.image].shape[0] == in_channels + assert isinstance(item[SegDataKeys.label], ndarray) or isinstance( + item[SegDataKeys.label], torch.Tensor + ) + + + assert item[SegDataKeys.label].max() <= 1 + # min value is 0 + assert item[SegDataKeys.label].min() == 0 + + if with_mosaic: + assert item[SegDataKeys.image].shape[1] == item[SegDataKeys.label].shape[1] + assert item[SegDataKeys.image].shape[2] == item[SegDataKeys.label].shape[2] + else: + assert item[SegDataKeys.image].shape[1] == item[SegDataKeys.label].shape[1] + assert item[SegDataKeys.image].shape[2] == item[SegDataKeys.label].shape[2] + + +# @pytest.mark.parametrize( +# ["ds_cfg", "aug_cfg"], +# [[arable_segmentation_cfg_w_target.copy(), resize_augmentation.copy()]], +# ) +# def test_ds_w_transform(ds_cfg, aug_cfg): +# ds_cfg["transform"] = aug_cfg +# test_read(ds_cfg, False, None, in_channels=4) + + +# @pytest.fixture +# def input_data(): +# x = np.random.rand(3,512,512) +# return x + +# @pytest.fixture +# def input_mask(): +# x = np.random.rand(1,256,256) +# return x + +def test__to_tensor(): + x = np.random.rand(256,256,3) + + out_data = _to_tensor(x) + assert out_data.shape[0] == 3 + +# @pytest.mark.parametrize( +# ["aug"], +# [[resize_augmentation.copy()]], +# ) +def test__augment_and_preproc(): + input_data = np.random.rand(256,256,3) + input_mask = np.random.rand(256,256,1) + framework = Frameworks.torch + task = "image-segmentation" + # augmentations = get_augmentations(resize_augmentation) + augmentations = get_obj(resize_augmentation, "augmentations", task, framework) + + x, y = _augment_and_preproc(input_data, input_mask, augmentations, None) + assert isinstance(x, ndarray) or isinstance(x, torch.Tensor) + assert isinstance(y, ndarray) or isinstance(y, torch.Tensor) + + assert x.shape[1] == 244 + assert x.shape[1] == y.shape[1] + +def test__get_class_weights(): + mask = np.random.randint(0, 2, (256,256,1)) + weights = _get_class_weights(mask) + assert weights is not None + + diff --git a/tests/unit/dataset/semantic_segmentation/test_tiff.py b/tests/unit/dataset/semantic_segmentation/test_tiff.py index 6d6dfa81..e0145c46 100644 --- a/tests/unit/dataset/semantic_segmentation/test_tiff.py +++ b/tests/unit/dataset/semantic_segmentation/test_tiff.py @@ -7,7 +7,7 @@ from innofw.constants import SegDataKeys from innofw.core.datasets.semantic_segmentation.tiff_dataset import ( - SegmentationDataset, + SegmentationDataset, get_metadata ) from tests.fixtures.config.augmentations import ( bare_aug_torchvision as resize_augmentation, @@ -56,6 +56,9 @@ def test_read(cfg, w_mask, size, n_channels): with pytest.raises(KeyError): assert item[SegDataKeys.label] +def test_get_metadata(): + meta = get_metadata('tests/data/images/segmentation/forest/train/1/B02.tif') + assert meta is not None @pytest.mark.parametrize( ["ds_cfg", "aug_cfg"], diff --git a/tests/unit/dataset/semantic_segmentation/test_tiled_dataset.py b/tests/unit/dataset/semantic_segmentation/test_tiled_dataset.py new file mode 100644 index 00000000..9c847a14 --- /dev/null +++ b/tests/unit/dataset/semantic_segmentation/test_tiled_dataset.py @@ -0,0 +1,74 @@ +import numpy as np +import pytest +import torch +from hydra.errors import InstantiationException +from hydra.utils import instantiate +from numpy import ndarray +from omegaconf import DictConfig + +from innofw.constants import SegDataKeys +from innofw.constants import Frameworks +from innofw.utils.framework import get_augmentations, get_obj +from innofw.core.datasets.segmentation_hdf5_old_pipe import ( + TiledDataset, + +) +from tests.fixtures.config.augmentations import ( + resize_augmentation_albu as resize_augmentation, +) +from tests.utils import get_test_folder_path +from tests.fixtures.config.datasets import arable_segmentation_cfg_w_target + + +@pytest.mark.parametrize( + ["tif_folders", 'crop_size', "crop_step", 'augmentations'], + [ + [str(get_test_folder_path() / "data/images/segmentation/forest/train/1"), + (128, 128), + (128, 128), + resize_augmentation], + [str(get_test_folder_path() / "data/images/segmentation/forest/test/1"), + (128, 128), + (64, 64), + None],] +) +def test_read(tif_folders, crop_size, crop_step, augmentations): + framework = Frameworks.torch + task = "image-segmentation" + # augmentations = get_augmentations(resize_augmentation) + aug = get_obj(resize_augmentation, "augmentations", task, framework) + ds = TiledDataset(tif_folders=[tif_folders], + crop_size=crop_size, + crop_step=crop_step, + augmentations=aug) + # ds: Dataset = instantiate(cfg, _convert_="partial") + assert ds is not None + # assert ds.len > 0 + + # ds.setup() + + + item = ds[0] + assert item is not None + + # for item in ds: + + assert isinstance(item['image'], ndarray) or isinstance( + item['image'], torch.Tensor + ) + assert item['image'].shape[0] == 3 + assert isinstance(item['mask'], ndarray) or isinstance( + item['mask'], torch.Tensor + ) + + + assert item['mask'].max() <= 1 + # min value is 0 + assert item['mask'].min() == 0 + + assert item['image'].shape[1] == item['mask'].shape[1] + assert item['image'].shape[2] == item['mask'].shape[2] + + + + diff --git a/tests/unit/dataset/semantic_segmentation/test_wrcd_old.py b/tests/unit/dataset/semantic_segmentation/test_wrcd_old.py new file mode 100644 index 00000000..c0909483 --- /dev/null +++ b/tests/unit/dataset/semantic_segmentation/test_wrcd_old.py @@ -0,0 +1,79 @@ +import numpy as np +import pytest +import torch +from hydra.errors import InstantiationException +from hydra.utils import instantiate +from numpy import ndarray +from omegaconf import DictConfig + +from innofw.constants import SegDataKeys +from innofw.constants import Frameworks +from innofw.utils.framework import get_augmentations, get_obj +from innofw.core.datasets.segmentation_hdf5_old_pipe import ( + WeightedRandomCropDataset, + TiledDataset, + _to_tensor, + _augment_and_preproc, + _get_preprocessing_fn, + _get_class_weights +) +from tests.fixtures.config.augmentations import ( + resize_augmentation_albu as resize_augmentation, +) +from tests.utils import get_test_folder_path +from tests.fixtures.config.datasets import arable_segmentation_cfg_w_target + + +@pytest.mark.parametrize( + ["tif_folders", "is_train", 'augmentations'], + [ + [str(get_test_folder_path() / "data/images/segmentation/forest/train/1"), + False, + resize_augmentation], + [str(get_test_folder_path() / "data/images/segmentation/forest/test/1"), + True, + None],] +) +def test_read(tif_folders, is_train, augmentations): + framework = Frameworks.torch + task = "image-segmentation" + # augmentations = get_augmentations(resize_augmentation) + aug = get_obj(resize_augmentation, "augmentations", task, framework) + ds = WeightedRandomCropDataset(tif_folders=[tif_folders], + is_train=is_train, + augmentations=aug) + # ds: Dataset = instantiate(cfg, _convert_="partial") + assert ds is not None + # assert ds.len > 0 + + # ds.setup() + + + item = ds[0] + assert item is not None + + # for item in ds: + + assert isinstance(item['image'], ndarray) or isinstance( + item['image'], torch.Tensor + ) + assert item['image'].shape[0] == 3 + assert isinstance(item['mask'], ndarray) or isinstance( + item['mask'], torch.Tensor + ) + + + assert item['mask'].max() <= 1 + # min value is 0 + assert item['mask'].min() == 0 + + if is_train: + assert item['image'].shape[1] == item['mask'].shape[1] + assert item['image'].shape[2] == item['mask'].shape[2] + else: + assert item['image'].shape[1] == item['mask'].shape[1] + assert item['image'].shape[2] == item['mask'].shape[2] + + + + diff --git a/tests/unit/dataset/test_segmentation_tif.py b/tests/unit/dataset/test_segmentation_tif.py new file mode 100644 index 00000000..639b8c56 --- /dev/null +++ b/tests/unit/dataset/test_segmentation_tif.py @@ -0,0 +1,96 @@ +import numpy as np +import pytest +import torch +from hydra.errors import InstantiationException +from hydra.utils import instantiate +from numpy import ndarray + +from innofw.constants import SegDataKeys +from innofw.core.datasets.segmentation import ( + SegmentationDataset +) +from innofw.core.datasets.segmentation_tif import get_metadata +from tests.fixtures.config.augmentations import ( + bare_aug_torchvision as resize_augmentation, +) +from tests.fixtures.config.datasets_2 import roads_tiff_dataset_w_masks + + +@pytest.mark.parametrize( + ["cfg", "w_mask", "size", "n_channels"], + [ + [roads_tiff_dataset_w_masks.copy(), True, 2048, 3], + [roads_tiff_dataset_w_masks.copy(), False, 2048, 3], + ], +) +def test_read(cfg, w_mask, size, n_channels): + if not w_mask: + cfg["masks"] = None + + ds: SegmentationDataset = instantiate(cfg, _convert_="partial") + assert ds is not None + assert len(ds) > 0 + + item = ds[0] + assert item is not None + + for item in ds: + assert isinstance(item[SegDataKeys.image], ndarray) or isinstance( + item[SegDataKeys.image], torch.Tensor + ) + assert item[SegDataKeys.image].shape == (n_channels, size, size) + + if w_mask: + assert isinstance(item[SegDataKeys.image], ndarray) or isinstance( + item[SegDataKeys.image], torch.Tensor + ) + assert item[SegDataKeys.label].shape == (1, size, size) + + # as it is a binary segmentation data + # it should have at most two distinct values + assert len(np.unique(item[SegDataKeys.label])) <= 2 + # max value is 1 + assert item[SegDataKeys.label].max() <= 1 + # min value is 0 + assert item[SegDataKeys.label].min() == 0 + else: + with pytest.raises(KeyError): + assert item[SegDataKeys.label] + +def test_get_metadata(): + meta = get_metadata('tests/data/images/segmentation/forest/train/1/B02.tif') + assert meta is not None + +@pytest.mark.parametrize( + ["ds_cfg", "aug_cfg"], + [[roads_tiff_dataset_w_masks.copy(), resize_augmentation.copy()]], +) +def test_ds_w_transform(ds_cfg, aug_cfg): + ds_cfg["transform"] = aug_cfg + test_read(ds_cfg, False, 244, 3) # resize should decrease the size + + +@pytest.mark.parametrize(["ds_cfg"], [[roads_tiff_dataset_w_masks.copy()]]) +def test_channels(ds_cfg): + # data should contain 4 channels + # but 3 needed + ds_cfg["channels"] = 2 + test_read(ds_cfg, False, 2048, 2) + + +@pytest.mark.parametrize(["cfg"], [[roads_tiff_dataset_w_masks.copy()]]) +def test_ds_w_caching(cfg): + cfg["w_caching"] = True + # with masks + test_read(cfg, True, 2048, 3) + # without masks + test_read(cfg, False, 2048, 3) + + +@pytest.mark.parametrize(["cfg"], [[roads_tiff_dataset_w_masks.copy()]]) +def test_wrong_img_mask_number(cfg): + cfg["images"] = cfg["images"][:1] + + with pytest.raises(InstantiationException): + ds: SegmentationDataset = instantiate(cfg, _convert_="partial") + diff --git a/tests/unit/losses/providers/torch/test_segmentation.py b/tests/unit/losses/providers/torch/test_segmentation.py index 0966124d..f996a1b0 100644 --- a/tests/unit/losses/providers/torch/test_segmentation.py +++ b/tests/unit/losses/providers/torch/test_segmentation.py @@ -7,8 +7,13 @@ import pytest import torch -from innofw.core.losses.dice_loss_old import DiceLoss +from innofw.core.losses.dice_loss_old import DiceLoss, IoUBatch +from innofw.core.losses.dice_loss_old import (_threshold, + _reduce,) + from innofw.core.losses.surface_loss_old import SurfaceLoss +from innofw.core.losses.focal_loss_old import FocalLoss + def test_dice_loss(): @@ -23,20 +28,54 @@ def test_dice_loss(): torch.tensor([0.9, 0.4, 0.4]), torch.tensor([1, 0, 1]) ) == pytest.approx(torch.tensor([0.7843]), 1e-3) +def test_focal_loss(): + focal_loss = FocalLoss( + gamma=2, smooth=0.1, eps=1e-5 + ) + + assert focal_loss( + torch.tensor([0, 0.4, 0.4]), torch.tensor([0, 0, 1]) + ) == pytest.approx(torch.tensor(0.081), abs=1e-3) + assert focal_loss( + torch.tensor([0.9, 0.4, 0.4]), torch.tensor([1, 0, 1]) + ) == pytest.approx(torch.tensor(0.055), abs=1e-3) def test_surface_loss(): surface_loss = SurfaceLoss(scheduler=lambda m, i: min(0.3, m + i * 0.025)) - surface_loss(torch.tensor([0, 0.4, 0.4]), torch.tensor([0, 0, 1])) + assert surface_loss(torch.tensor([0, 0.4, 0.4]), torch.tensor([0, 0, 1])) is not None # assert surface_loss(torch.tensor([0, 0.4, 0.4]), torch.tensor([0, 0, 1])) == pytest.approx(torch.tensor([0.8521]), 1e-3) # assert surface_loss(torch.tensor([0.9, 0.4, 0.4]), torch.tensor([1, 0, 1])) == pytest.approx(torch.tensor([0.7843]), 1e-3) -# @pytest.mark.parametrize(["loss"], [[BCELoss()]]) -# def test_torch_loss(loss): -# loss = Loss(loss) +def test_iou_batch(): + for reduction in [None, "sum", "mean"]: + iou_loss = IoUBatch( + eps=1e-7, + threshold=0.5, + per_image=False, + reduction=reduction, + ) + + assert iou_loss( + torch.tensor([0, 0.4, 0.4]), torch.tensor([0, 0, 1]) + ) < 1 #pytest.approx(torch.tensor([1]), 1e-7) + assert iou_loss( + torch.tensor([0.9, 0.4, 0.8]), torch.tensor([1, 0, 1]) + ) == 1 #pytest.approx(torch.tensor([1]), 1e-3) + + +def test__threshold(): + + x = torch.rand(2, 3) + out = _threshold(x, threshold=0.5) + assert torch.max(out) == 1 + assert torch.min(out) == 0 -# assert loss +def test__reduce(): + x = torch.Tensor([0, 1]) + out_mean = _reduce(x, reduction='mean') + out_sum = _reduce(x, reduction='sum') + out_none = _reduce(x, reduction=None) + assert out_mean == 0.5 + assert out_sum == 1 -# pred = torch.tensor([0.0, 1.0, 0.0]) -# target = torch.tensor([1.0, 1.0, 0.0]) -# loss(pred, target) diff --git a/tests/unit/onvif/test_camera_info.py b/tests/unit/onvif/test_camera_info.py new file mode 100644 index 00000000..a20cdaeb --- /dev/null +++ b/tests/unit/onvif/test_camera_info.py @@ -0,0 +1,53 @@ +import pytest + + +from innofw.onvif_util.camera_info import get_camera_info +import unittest +from unittest.mock import patch, Mock + +onvif = pytest.importorskip("onvif") +@pytest.mark.skip(reason="does not work") +class TestGetCameraInfo(unittest.TestCase): + + @patch('onvif.ONVIFCamera', autospec=True) + def test_get_camera_info(self, mock_onvif_camera): + # Mocking ONVIFCamera to avoid actual network requests + mock_camera_instance = Mock() + mock_onvif_camera.return_value = mock_camera_instance + + # Mocking the update_xaddrs method + mock_camera_instance.update_xaddrs.return_value = None + + # Mocking the return values of GetDeviceInformation and GetNetworkInterfaces + mock_device_info = Mock() + mock_network_interfaces = Mock() + mock_camera_instance.devicemgmt.GetDeviceInformation.return_value = mock_device_info + mock_camera_instance.devicemgmt.GetNetworkInterfaces.return_value = mock_network_interfaces + mock_camera_instance.devicemgmt.GetCapabilities.return_value = [None] + + # Mocking the return value of GetStreamUri + mock_media_service = Mock() + mock_profiles = [Mock()] + mock_media_service.GetProfiles.return_value = mock_profiles + mock_token = "mock_token" + mock_media_service.create_type.return_value = Mock() + mock_media_service.create_type.return_value.ProfileToken = mock_token + mock_camera_instance.create_media_service.return_value = mock_media_service + mock_media_service.GetStreamUri.return_value = "mock_stream_uri" + + # Call your function with the mocked ONVIFCamera + ip = 'your_ip' + port = 80 + user = 'your_user' + password = 'your_password' + get_camera_info(ip, port, user, password) + + # Assertions + mock_onvif_camera.assert_called_once_with(ip, port, user, password) + mock_camera_instance.devicemgmt.GetDeviceInformation.assert_called_once() + mock_camera_instance.devicemgmt.GetNetworkInterfaces.assert_called_once() + mock_media_service.GetProfiles.assert_called_once() + mock_media_service.create_type.assert_called_once_with("GetStreamUri") + mock_media_service.GetStreamUri.assert_called_once() + + diff --git a/tests/unit/onvif/test_mover.py b/tests/unit/onvif/test_mover.py new file mode 100644 index 00000000..485f7ec1 --- /dev/null +++ b/tests/unit/onvif/test_mover.py @@ -0,0 +1,81 @@ +import pytest + + +from innofw.onvif_util.mover import CameraControl +import unittest +from unittest.mock import patch, Mock + +onvif = pytest.importorskip("onvif") + +@pytest.mark.skip(reason="does not work") +class TestCameraControl(unittest.TestCase): + @patch('onvif.ONVIFCamera') + def test_absolute_move(self, mock_onvif_camera): + # Mock the ONVIFCamera class to include wsdl_dir attribute + mock_onvif_camera_instance = mock_onvif_camera.return_value + mock_onvif_camera_instance.create_media_service.return_value = Mock() + mock_onvif_camera_instance.create_ptz_service.return_value = Mock() + mock_onvif_camera_instance.GetProfiles.return_value = [Mock()] + mock_onvif_camera_instance.AbsoluteMove.return_value = Mock() + mock_onvif_camera_instance.ContinuousMove.return_value = Mock() + mock_onvif_camera_instance.RelativeMove.return_value = Mock() + mock_onvif_camera_instance.Stop.return_value = Mock() + + # Mocking ONVIFCamera to avoid actual network requests + mock_camera_instance = Mock() + mock_onvif_camera.return_value = mock_camera_instance + + # Mocking the update_xaddrs method + mock_camera_instance.update_xaddrs.return_value = None + + # Mocking the return values of GetDeviceInformation and GetNetworkInterfaces + mock_device_info = Mock() + mock_network_interfaces = Mock() + mock_camera_instance.devicemgmt.GetDeviceInformation.return_value = mock_device_info + mock_camera_instance.devicemgmt.GetNetworkInterfaces.return_value = mock_network_interfaces + + # Mocking the return value of GetStreamUri + mock_media_service = Mock() + mock_token = "mock_token" + mock_media_service.create_type.return_value = Mock() + mock_media_service.create_type.return_value.ProfileToken = mock_token + + mock_media_service.GetStreamUri.return_value = "mock_stream_uri" + + # Set wsdl_dir attribute for the mock instance + mock_onvif_camera_instance.wsdl_dir = '/path/to/wsdl' + + # Create an instance of CameraControl + ip = 'your_ip' + user = 'your_user' + password = 'your_password' + camera_control = CameraControl(ip, user, password) + + # Call absolute_move method + pan = 0.5 + tilt = 0.3 + zoom = 1.0 + resp = camera_control.absolute_move(pan, tilt, zoom) + + # Assertions + self.assertIsNotNone(resp) + mock_onvif_camera.assert_called_once_with(ip, 80, user, password) + mock_onvif_camera_instance.create_media_service.assert_called_once() + mock_onvif_camera_instance.create_ptz_service.assert_called_once() + mock_onvif_camera_instance.GetProfiles.assert_called_once() + mock_onvif_camera_instance.AbsoluteMove.assert_called_once() + + move = camera_control.continuous_move(pan, tilt, zoom) + self.assertIsNotNone(move) + mock_onvif_camera_instance.GetProfiles.assert_called_once() + mock_onvif_camera_instance.ContinuousMove.assert_called_once() + + stop = camera_control.stop_move() + self.assertIsNotNone(stop) + mock_onvif_camera_instance.Stop.assert_called_once() + + +if __name__ == '__main__': + unittest.main() + + diff --git a/tests/unit/onvif/test_stream.py b/tests/unit/onvif/test_stream.py new file mode 100644 index 00000000..7e394831 --- /dev/null +++ b/tests/unit/onvif/test_stream.py @@ -0,0 +1,34 @@ +import pytest + +from omegaconf import OmegaConf +from typing import Optional +from innofw.onvif_util.stream import show_stream +import unittest +from unittest.mock import patch, Mock + +cv2 = pytest.importorskip("cv2") + + +class TestShowStream(unittest.TestCase): + + @patch('cv2.VideoCapture') + @patch('cv2.imshow') + @patch('cv2.waitKey') + def test_show_stream(self, mock_wait_key, mock_imshow, mock_video_capture): + # Mocking the VideoCapture instance + mock_capture_instance = Mock() + mock_video_capture.return_value = mock_capture_instance + + # Mocking the return value of read method + mock_capture_instance.read.side_effect = [(True, Mock()), (False, None)] + + # Call your function with the mocked methods + uri = 'your_video_uri' + show_stream(uri) + + # Assertions + mock_video_capture.assert_called_once_with(uri, cv2.CAP_FFMPEG) + mock_imshow.assert_called() + mock_wait_key.assert_called_with(1) + + diff --git a/tests/unit/test_utils/test_data_utils/test_preprocessing/test_dicom_handler.py b/tests/unit/test_utils/test_data_utils/test_preprocessing/test_dicom_handler.py index 340b2b68..db14273d 100644 --- a/tests/unit/test_utils/test_data_utils/test_preprocessing/test_dicom_handler.py +++ b/tests/unit/test_utils/test_data_utils/test_preprocessing/test_dicom_handler.py @@ -4,10 +4,12 @@ from innofw.utils.data_utils.preprocessing.dicom_handler import dicom_to_img from innofw.utils.data_utils.preprocessing.dicom_handler import img_to_dicom +from innofw.utils.data_utils.preprocessing.dicom_handler import raster_to_dicom from tests.utils import get_test_folder_path def test_dicom_handler(): + dicom_path = os.path.join( get_test_folder_path(), "data/images/other/dicoms/test.dcm" ) @@ -15,3 +17,8 @@ def test_dicom_handler(): assert isinstance(img, np.ndarray) dicom = img_to_dicom(img) assert dicom + +def test_raster_to_dicom(): + rgb_img = np.random.rand(124, 124, 3) + ds = raster_to_dicom(rgb_img.astype(np.uint8), dicom=None) + assert ds \ No newline at end of file diff --git a/tests/unit/test_utils/test_data_utils/test_preprocessing/test_hemorrhage_contrast.py b/tests/unit/test_utils/test_data_utils/test_preprocessing/test_hemorrhage_contrast.py new file mode 100644 index 00000000..ca725f19 --- /dev/null +++ b/tests/unit/test_utils/test_data_utils/test_preprocessing/test_hemorrhage_contrast.py @@ -0,0 +1,99 @@ +import os + +import numpy as np +from pydicom import dcmread + +from innofw.utils.data_utils.preprocessing.CT_hemorrhage_contrast import (resize, + normalize_minmax, + get_metadata_from_dicom, + get_first_of_dicom_field_as_int, + prepare_image, + window_image +) +from innofw.utils.data_utils.preprocessing.dicom_handler import dicom_to_img +from tests.utils import get_test_folder_path + + +def test_dicom_resize(): + dicom_path = os.path.join( + get_test_folder_path(), "data/images/other/dicoms/test.dcm" + ) + img = dicom_to_img(dicom_path) + resize_img = resize(img, 256, 256) + + assert np.array(resize_img).shape[0] == 256 + assert np.array(resize_img).shape[1] == 256 + +def test_dicom_norm(): + dicom_path = os.path.join( + get_test_folder_path(), "data/images/other/dicoms/test.dcm" + ) + img = dicom_to_img(dicom_path) + + norm_img = normalize_minmax(img) + + assert np.max(np.array(norm_img)) <= 255 + assert np.min(np.array(norm_img)) >= 0 + +def test_dicom_metadata(): + dicom_path = os.path.join( + get_test_folder_path(), "data/images/other/dicoms/test.dcm" + ) + img = dcmread(dicom_path) + + # img_id, prep_img = prepare_image(img) + metadata = get_metadata_from_dicom(img) + window_center = metadata['window_center'] + window_width = metadata['window_width'] + slope = metadata['slope'] + assert isinstance(window_center, int) + assert window_width == 200 + assert isinstance(slope, int) + +def test_metadata_int(): + # dicom_path = os.path.join( + # get_test_folder_path(), "data/images/other/dicoms/test.dcm" + # ) + metadata = { + "window_center": 50.0, + "window_width": float(200), + "intercept": -(2049/2), + "slope": 1.0, + } + new_metadata = {k: get_first_of_dicom_field_as_int(v) for k, v in metadata.items()} + + for param in new_metadata.keys(): + assert isinstance(new_metadata[param], int) + + +def test_prepare_image(): + import PIL + dicom_path = os.path.join( + get_test_folder_path(), "data/images/other/dicoms/test.dcm" + ) + img = dcmread(dicom_path) + + img_id, prep_img = prepare_image(img) + # array_img = np.array(prep_img) + assert isinstance(prep_img, PIL.Image.Image) + assert isinstance(img_id, str) + + +def test_window_image(): + dicom_path = os.path.join( + get_test_folder_path(), "data/images/other/dicoms/test.dcm" + ) + img = dcmread(dicom_path) + + window_center = 50 + window_width = 200 + intercept = 0 + slope = 1 + + out_img = window_image(img.pixel_array, window_center, window_width, intercept, slope) + assert out_img is not None + + + + +