From 7ee19b6761e5183ea1cf723ab93940c858001550 Mon Sep 17 00:00:00 2001 From: Holger Roth Date: Wed, 31 Aug 2022 12:52:57 -0400 Subject: [PATCH 01/10] improve client algo docstring formatting Signed-off-by: Holger Roth --- monai/fl/client/client_algo.py | 65 ++++++++++++++++++++++++---------- 1 file changed, 46 insertions(+), 19 deletions(-) diff --git a/monai/fl/client/client_algo.py b/monai/fl/client/client_algo.py index 4cab0a4474..dc7a41c418 100644 --- a/monai/fl/client/client_algo.py +++ b/monai/fl/client/client_algo.py @@ -20,43 +20,69 @@ class ClientAlgo(abc.ABC): To define a new algo script, subclass this class and implement the following abstract methods: - - #Algo.train() - - #Algo.get_weights() - - #Algo.evaluate() + - self.train() + - self.get_weights() + - self.evaluate() - initialize() and finalize() can be optionally be implemented to help with lifecycle management of the object. + initialize(), abort(), and finalize() can be optionally be implemented to help with lifecycle management + of the class object. """ def initialize(self, extra=None): - """call to initialize the ClientAlgo class""" + """ + Call to initialize the ClientAlgo class + + Args: + extra: optional extra information, e.g. dict of `ExtraItems.CLIENT_NAME` and/or `ExtraItems.APP_ROOT` + """ pass def finalize(self, extra=None): - """call to finalize the ClientAlgo class""" + """ + Call to finalize the ClientAlgo class + + Args: + extra: optional extra information + """ pass def abort(self, extra=None): - """call to abort the ClientAlgo training or evaluation""" + """ + Call to abort the ClientAlgo training or evaluation + + Args: + extra: optional extra information + """ + pass @abc.abstractmethod def train(self, data: ExchangeObject, extra=None) -> None: """ - objective: train network and produce new network from train data. - # Arguments - data: ExchangeObject containing current network weights to base training on. + Train network and produce new network from train data. + + Args: + data: ExchangeObject containing current network weights to base training on. + extra: optional extra information + + Returns: + None """ raise NotImplementedError @abc.abstractmethod def get_weights(self, extra=None) -> ExchangeObject: """ - objective: get current local weights or weight differences + Get current local weights or weight differences + + Args: + extra: optional extra information - # Returns - ExchangeObject: current local weights or weight differences. + Returns: + ExchangeObject: current local weights or weight differences. - # Example returns ExchangeObject, e.g.:: + Example: + Returns ExchangeObject, e.g.:: ExchangeObject( weights = self.trainer.network.state_dict(), @@ -69,11 +95,12 @@ def get_weights(self, extra=None) -> ExchangeObject: @abc.abstractmethod def evaluate(self, data: ExchangeObject, extra=None) -> ExchangeObject: """ - objective: get evaluation metrics on test data. - # Arguments - data: ExchangeObject with network weights to use for evaluation + Get evaluation metrics on test data. + + Args: + data: ExchangeObject with network weights to use for evaluation - # Returns - metrics: ExchangeObject with evaluation metrics. + Returns: + metrics: ExchangeObject with evaluation metrics. """ raise NotImplementedError From d48d1e85414e51f0825eeccc54dce95d75561fd0 Mon Sep 17 00:00:00 2001 From: Holger Roth Date: Thu, 1 Sep 2022 19:38:22 -0400 Subject: [PATCH 02/10] use ignite's new interrupt api Signed-off-by: Holger Roth --- monai/fl/client/monai_algo.py | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/monai/fl/client/monai_algo.py b/monai/fl/client/monai_algo.py index 45b935c886..57ef939d0f 100644 --- a/monai/fl/client/monai_algo.py +++ b/monai/fl/client/monai_algo.py @@ -407,17 +407,13 @@ def abort(self, extra=None): Args: extra: Dict with additional information that can be provided by FL system. """ - self.logger.info(f"Aborting {self.client_name} during {self.phase} phase.") if isinstance(self.trainer, monai.engines.Trainer): self.logger.info(f"Aborting {self.client_name} trainer...") - self.trainer.terminate() - self.trainer.state.dataloader_iter = self.trainer._dataloader_iter # type: ignore - if self.trainer.state.iteration % self.trainer.state.epoch_length == 0: - self.trainer._fire_event(Events.EPOCH_COMPLETED) + self.trainer.interrupt() if isinstance(self.evaluator, monai.engines.Trainer): self.logger.info(f"Aborting {self.client_name} evaluator...") - self.evaluator.terminate() + self.evaluator.interrupt() def finalize(self, extra=None): """ @@ -425,10 +421,6 @@ def finalize(self, extra=None): Args: extra: Dict with additional information that can be provided by FL system. """ - - # TODO: finalize feature could be built into the MONAI Trainer class - if extra is None: - extra = {} self.logger.info(f"Terminating {self.client_name} during {self.phase} phase.") if isinstance(self.trainer, monai.engines.Trainer): self.logger.info(f"Terminating {self.client_name} trainer...") From 939decbc25fd2e6b0ec1deb28a8c19f25ad8b3df Mon Sep 17 00:00:00 2001 From: Holger Roth Date: Fri, 2 Sep 2022 11:39:12 -0400 Subject: [PATCH 03/10] remove ignite import Signed-off-by: Holger Roth --- monai/fl/client/monai_algo.py | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/monai/fl/client/monai_algo.py b/monai/fl/client/monai_algo.py index 57ef939d0f..1d39cf0d0b 100644 --- a/monai/fl/client/monai_algo.py +++ b/monai/fl/client/monai_algo.py @@ -12,14 +12,13 @@ import logging import os import sys -from typing import TYPE_CHECKING, Optional +from typing import Optional import torch import monai from monai.bundle import ConfigParser from monai.bundle.config_item import ConfigComponent, ConfigItem -from monai.config import IgniteInfo from monai.fl.client.client_algo import ClientAlgo from monai.fl.utils.constants import ( BundleKeys, @@ -33,12 +32,6 @@ ) from monai.fl.utils.exchange_object import ExchangeObject from monai.networks.utils import copy_model_state, get_state_dict -from monai.utils import min_version, optional_import - -if TYPE_CHECKING: - from ignite.engine import Events -else: - Events, _ = optional_import("ignite.engine", IgniteInfo.OPT_IMPORT_VERSION, min_version, "Events") logging.basicConfig(stream=sys.stdout, level=logging.INFO, format="%(asctime)s - %(message)s") From bee31e304d70f7b79c8f0e16d501c047d9a29f35 Mon Sep 17 00:00:00 2001 From: Holger Roth Date: Fri, 2 Sep 2022 11:47:00 -0400 Subject: [PATCH 04/10] add typing Signed-off-by: Holger Roth --- monai/fl/client/client_algo.py | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/monai/fl/client/client_algo.py b/monai/fl/client/client_algo.py index dc7a41c418..763bacdb26 100644 --- a/monai/fl/client/client_algo.py +++ b/monai/fl/client/client_algo.py @@ -10,6 +10,7 @@ # limitations under the License. import abc +from typing import Optional from monai.fl.utils.exchange_object import ExchangeObject @@ -28,7 +29,7 @@ class ClientAlgo(abc.ABC): of the class object. """ - def initialize(self, extra=None): + def initialize(self, extra: Optional[dict] = None): """ Call to initialize the ClientAlgo class @@ -37,7 +38,7 @@ def initialize(self, extra=None): """ pass - def finalize(self, extra=None): + def finalize(self, extra: Optional[dict] = None): """ Call to finalize the ClientAlgo class @@ -46,7 +47,7 @@ def finalize(self, extra=None): """ pass - def abort(self, extra=None): + def abort(self, extra: Optional[dict] = None): """ Call to abort the ClientAlgo training or evaluation @@ -57,7 +58,7 @@ def abort(self, extra=None): pass @abc.abstractmethod - def train(self, data: ExchangeObject, extra=None) -> None: + def train(self, data: ExchangeObject, extra: Optional[dict] = None) -> None: """ Train network and produce new network from train data. @@ -68,10 +69,10 @@ def train(self, data: ExchangeObject, extra=None) -> None: Returns: None """ - raise NotImplementedError + raise NotImplementedError(f"Subclass {self.__class__.__name__} must implement this method.") @abc.abstractmethod - def get_weights(self, extra=None) -> ExchangeObject: + def get_weights(self, extra: Optional[dict] = None) -> ExchangeObject: """ Get current local weights or weight differences @@ -90,17 +91,18 @@ def get_weights(self, extra=None) -> ExchangeObject: weight_type = WeightType.WEIGHTS ) """ - raise NotImplementedError + raise NotImplementedError(f"Subclass {self.__class__.__name__} must implement this method.") @abc.abstractmethod - def evaluate(self, data: ExchangeObject, extra=None) -> ExchangeObject: + def evaluate(self, data: ExchangeObject, extra: Optional[dict] = None) -> ExchangeObject: """ Get evaluation metrics on test data. Args: data: ExchangeObject with network weights to use for evaluation + extra: optional extra information Returns: metrics: ExchangeObject with evaluation metrics. """ - raise NotImplementedError + raise NotImplementedError(f"Subclass {self.__class__.__name__} must implement this method.") From 41dcb61ce52c77809b5f0fb9a2834872103d5226 Mon Sep 17 00:00:00 2001 From: Nic Ma Date: Mon, 5 Sep 2022 23:23:30 +0800 Subject: [PATCH 05/10] [DLMED] update ignite support Signed-off-by: Nic Ma --- docs/requirements.txt | 2 +- monai/fl/client/monai_algo.py | 2 ++ requirements-dev.txt | 2 +- setup.cfg | 4 ++-- tests/testing_data/config_fl_evaluate.json | 2 +- tests/testing_data/config_fl_train.json | 2 +- 6 files changed, 8 insertions(+), 6 deletions(-) diff --git a/docs/requirements.txt b/docs/requirements.txt index d3a6ed9576..597f7bc5dd 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,6 +1,6 @@ -f https://download.pytorch.org/whl/cpu/torch-1.6.0%2Bcpu-cp37-cp37m-linux_x86_64.whl torch>=1.6 -pytorch-ignite==0.4.9 +pytorch-ignite==0.4.10 numpy>=1.17 itk>=5.2 nibabel diff --git a/monai/fl/client/monai_algo.py b/monai/fl/client/monai_algo.py index 1d39cf0d0b..36f9366d02 100644 --- a/monai/fl/client/monai_algo.py +++ b/monai/fl/client/monai_algo.py @@ -32,6 +32,7 @@ ) from monai.fl.utils.exchange_object import ExchangeObject from monai.networks.utils import copy_model_state, get_state_dict +from monai.utils import min_version, require_pkg logging.basicConfig(stream=sys.stdout, level=logging.INFO, format="%(asctime)s - %(message)s") @@ -86,6 +87,7 @@ def disable_ckpt_loaders(parser): h["_disabled_"] = True +@require_pkg(pkg_name="ignite", version="0.4.10", version_checker=min_version) class MonaiAlgo(ClientAlgo): """ Implementation of ``ClientAlgo`` to allow federated learning with MONAI bundle configurations. diff --git a/requirements-dev.txt b/requirements-dev.txt index 2568bf9c1e..6620c96384 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,6 +1,6 @@ # Full requirements for developments -r requirements-min.txt -pytorch-ignite==0.4.9 +pytorch-ignite==0.4.10 gdown>=4.4.0 scipy itk>=5.2 diff --git a/setup.cfg b/setup.cfg index 09219bfc32..31464f9d87 100644 --- a/setup.cfg +++ b/setup.cfg @@ -34,7 +34,7 @@ all = pillow tensorboard gdown>=4.4.0 - pytorch-ignite==0.4.9 + pytorch-ignite==0.4.10 torchvision itk>=5.2 tqdm>=4.47.0 @@ -67,7 +67,7 @@ tensorboard = gdown = gdown>=4.4.0 ignite = - pytorch-ignite==0.4.9 + pytorch-ignite==0.4.10 torchvision = torchvision itk = diff --git a/tests/testing_data/config_fl_evaluate.json b/tests/testing_data/config_fl_evaluate.json index 84ab7988f6..3bdeb3ed77 100644 --- a/tests/testing_data/config_fl_evaluate.json +++ b/tests/testing_data/config_fl_evaluate.json @@ -84,4 +84,4 @@ "key_val_metric": "@validate#key_metric" } } -} +} \ No newline at end of file diff --git a/tests/testing_data/config_fl_train.json b/tests/testing_data/config_fl_train.json index 5954d2cfbc..f4ac85d28f 100644 --- a/tests/testing_data/config_fl_train.json +++ b/tests/testing_data/config_fl_train.json @@ -122,4 +122,4 @@ "train_handlers": "@train#handlers" } } -} +} \ No newline at end of file From 877f131fd6f07e57af9238c25aee8a29c7c16516 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 5 Sep 2022 15:24:34 +0000 Subject: [PATCH 06/10] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- tests/testing_data/config_fl_evaluate.json | 2 +- tests/testing_data/config_fl_train.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/testing_data/config_fl_evaluate.json b/tests/testing_data/config_fl_evaluate.json index 3bdeb3ed77..84ab7988f6 100644 --- a/tests/testing_data/config_fl_evaluate.json +++ b/tests/testing_data/config_fl_evaluate.json @@ -84,4 +84,4 @@ "key_val_metric": "@validate#key_metric" } } -} \ No newline at end of file +} diff --git a/tests/testing_data/config_fl_train.json b/tests/testing_data/config_fl_train.json index f4ac85d28f..5954d2cfbc 100644 --- a/tests/testing_data/config_fl_train.json +++ b/tests/testing_data/config_fl_train.json @@ -122,4 +122,4 @@ "train_handlers": "@train#handlers" } } -} \ No newline at end of file +} From 420a502a25d72f352121de6c956d71516e6e620c Mon Sep 17 00:00:00 2001 From: monai-bot Date: Mon, 5 Sep 2022 15:32:36 +0000 Subject: [PATCH 07/10] [MONAI] code formatting Signed-off-by: monai-bot --- monai/apps/auto3dseg/ensemble_builder.py | 1 - monai/apps/reconstruction/networks/nets/utils.py | 5 ----- monai/auto3dseg/algo_gen.py | 1 - monai/utils/misc.py | 1 - 4 files changed, 8 deletions(-) diff --git a/monai/apps/auto3dseg/ensemble_builder.py b/monai/apps/auto3dseg/ensemble_builder.py index 384cb629a3..d63ad398e1 100644 --- a/monai/apps/auto3dseg/ensemble_builder.py +++ b/monai/apps/auto3dseg/ensemble_builder.py @@ -214,7 +214,6 @@ def __init__(self, n_fold: int = 5): self.n_fold = n_fold def collect_algos(self): - """ Rank the algos by finding the best model in each cross-validation fold """ diff --git a/monai/apps/reconstruction/networks/nets/utils.py b/monai/apps/reconstruction/networks/nets/utils.py index 15f06f638f..b97cdab786 100644 --- a/monai/apps/reconstruction/networks/nets/utils.py +++ b/monai/apps/reconstruction/networks/nets/utils.py @@ -23,7 +23,6 @@ def reshape_complex_to_channel_dim(x: Tensor) -> Tensor: - """ Swaps the complex dimension with the channel dimension so that the network treats real/imaginary parts as two separate channels. @@ -50,7 +49,6 @@ def reshape_complex_to_channel_dim(x: Tensor) -> Tensor: def reshape_channel_complex_to_last_dim(x: Tensor) -> Tensor: - """ Swaps the complex dimension with the channel dimension so that the network output has 2 as its last dimension @@ -128,7 +126,6 @@ def reshape_batch_channel_to_channel_dim(x: Tensor, batch_size: int) -> Tensor: def complex_normalize(x: Tensor) -> Tuple[Tensor, Tensor, Tensor]: - """ Performs layer mean-std normalization for complex data. Normalization is done for each batch member along each part (part refers to real and imaginary parts), separately. @@ -171,7 +168,6 @@ def complex_normalize(x: Tensor) -> Tuple[Tensor, Tensor, Tensor]: def divisible_pad_t( x: Tensor, k: int = 16 ) -> Tuple[Tensor, Tuple[Tuple[int, int], Tuple[int, int], Tuple[int, int], int, int, int]]: - """ Pad input to feed into the network (torch script compatible) @@ -234,7 +230,6 @@ def divisible_pad_t( def inverse_divisible_pad_t( x: Tensor, pad_sizes: Tuple[Tuple[int, int], Tuple[int, int], Tuple[int, int], int, int, int] ) -> Tensor: - """ De-pad network output to match its original shape diff --git a/monai/auto3dseg/algo_gen.py b/monai/auto3dseg/algo_gen.py index 2f30987dd8..b84fea4a22 100644 --- a/monai/auto3dseg/algo_gen.py +++ b/monai/auto3dseg/algo_gen.py @@ -19,7 +19,6 @@ class Algo: """ def set_data_stats(self, *args, **kwargs): - """Provide dataset (and summaries) so that the model creation can depend on the input datasets.""" pass diff --git a/monai/utils/misc.py b/monai/utils/misc.py index 5b56dfc7bb..ae62f26635 100644 --- a/monai/utils/misc.py +++ b/monai/utils/misc.py @@ -535,7 +535,6 @@ def label_union(x: List) -> List: def prob2class(x, sigmoid: bool = False, threshold: float = 0.5, **kwargs): - """ Compute the lab from the probability of predicted feature maps From c9323147fabb9ccc331eb918b3d1bca925fbb969 Mon Sep 17 00:00:00 2001 From: Nic Ma Date: Tue, 6 Sep 2022 15:09:24 +0800 Subject: [PATCH 08/10] [DLMED] fix docs issue Signed-off-by: Nic Ma --- monai/fl/client/client_algo.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/monai/fl/client/client_algo.py b/monai/fl/client/client_algo.py index 763bacdb26..881c400824 100644 --- a/monai/fl/client/client_algo.py +++ b/monai/fl/client/client_algo.py @@ -82,14 +82,14 @@ def get_weights(self, extra: Optional[dict] = None) -> ExchangeObject: Returns: ExchangeObject: current local weights or weight differences. - Example: - Returns ExchangeObject, e.g.:: + ExchangeObject example:: ExchangeObject( weights = self.trainer.network.state_dict(), optim = None, # could be self.optimizer.state_dict() weight_type = WeightType.WEIGHTS ) + """ raise NotImplementedError(f"Subclass {self.__class__.__name__} must implement this method.") From 683cc58b6e8d41260387281967c1faf59d7bd531 Mon Sep 17 00:00:00 2001 From: Nic Ma Date: Tue, 6 Sep 2022 15:15:38 +0800 Subject: [PATCH 09/10] [DLMED] update according to comments Signed-off-by: Nic Ma --- monai/fl/client/client_algo.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/monai/fl/client/client_algo.py b/monai/fl/client/client_algo.py index 881c400824..5ffdaa7fd9 100644 --- a/monai/fl/client/client_algo.py +++ b/monai/fl/client/client_algo.py @@ -57,7 +57,6 @@ def abort(self, extra: Optional[dict] = None): pass - @abc.abstractmethod def train(self, data: ExchangeObject, extra: Optional[dict] = None) -> None: """ Train network and produce new network from train data. @@ -71,7 +70,6 @@ def train(self, data: ExchangeObject, extra: Optional[dict] = None) -> None: """ raise NotImplementedError(f"Subclass {self.__class__.__name__} must implement this method.") - @abc.abstractmethod def get_weights(self, extra: Optional[dict] = None) -> ExchangeObject: """ Get current local weights or weight differences @@ -93,7 +91,6 @@ def get_weights(self, extra: Optional[dict] = None) -> ExchangeObject: """ raise NotImplementedError(f"Subclass {self.__class__.__name__} must implement this method.") - @abc.abstractmethod def evaluate(self, data: ExchangeObject, extra: Optional[dict] = None) -> ExchangeObject: """ Get evaluation metrics on test data. From 7ef6d0b7be47e6b516815cf84e130300555f2f07 Mon Sep 17 00:00:00 2001 From: Nic Ma Date: Tue, 6 Sep 2022 15:25:41 +0800 Subject: [PATCH 10/10] [DLMED] remove abc Signed-off-by: Nic Ma --- monai/fl/client/client_algo.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/monai/fl/client/client_algo.py b/monai/fl/client/client_algo.py index 5ffdaa7fd9..6afd78c437 100644 --- a/monai/fl/client/client_algo.py +++ b/monai/fl/client/client_algo.py @@ -9,13 +9,12 @@ # See the License for the specific language governing permissions and # limitations under the License. -import abc from typing import Optional from monai.fl.utils.exchange_object import ExchangeObject -class ClientAlgo(abc.ABC): +class ClientAlgo: """ objective: provide an abstract base class for defining algo to run on any platform. To define a new algo script, subclass this class and implement the