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/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/fl/client/client_algo.py b/monai/fl/client/client_algo.py index 4cab0a4474..6afd78c437 100644 --- a/monai/fl/client/client_algo.py +++ b/monai/fl/client/client_algo.py @@ -9,71 +9,96 @@ # 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 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""" + def initialize(self, extra: Optional[dict] = None): + """ + 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""" + def finalize(self, extra: Optional[dict] = None): + """ + 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""" + def abort(self, extra: Optional[dict] = None): + """ + Call to abort the ClientAlgo training or evaluation + + Args: + extra: optional extra information + """ + pass - @abc.abstractmethod - def train(self, data: ExchangeObject, extra=None) -> None: + def train(self, data: ExchangeObject, extra: Optional[dict] = 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 + 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: """ - objective: get current local weights or weight differences + Get current local weights or weight differences - # Returns - ExchangeObject: current local weights or weight differences. + Args: + extra: optional extra information - # Example returns ExchangeObject, e.g.:: + Returns: + ExchangeObject: current local weights or weight differences. + + ExchangeObject example:: ExchangeObject( weights = self.trainer.network.state_dict(), optim = None, # could be self.optimizer.state_dict() 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: """ - 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 + extra: optional extra information - # Returns - metrics: ExchangeObject with evaluation metrics. + Returns: + metrics: ExchangeObject with evaluation metrics. """ - raise NotImplementedError + raise NotImplementedError(f"Subclass {self.__class__.__name__} must implement this method.") diff --git a/monai/fl/client/monai_algo.py b/monai/fl/client/monai_algo.py index 7f99b9ead4..36f9366d02 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,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, optional_import - -if TYPE_CHECKING: - from ignite.engine import Events -else: - Events, _ = optional_import("ignite.engine", IgniteInfo.OPT_IMPORT_VERSION, min_version, "Events") +from monai.utils import min_version, require_pkg logging.basicConfig(stream=sys.stdout, level=logging.INFO, format="%(asctime)s - %(message)s") @@ -93,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. @@ -407,18 +402,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 - - 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): """ @@ -426,10 +416,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...") 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 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 =