Repository: Project-MONAI/MONAI

Issue:
allows for flexible model "mode" in the evaluators
**Is your feature request related to a problem? Please describe.**
provide an option for all the evaluators to be able to switch between "train_mode" and "eval_mode" of the model.
(this is useful for GradCAM inference integration)


**Additional context**
https://github.com/Project-MONAI/MONAI/blob/70388fa298384f698dfb3ba790e3bedb1ffcb07b/monai/engines/evaluator.py#L200



Discussion:

Resolving patch:
diff --git a/monai/engines/evaluator.py b/monai/engines/evaluator.py
--- a/monai/engines/evaluator.py
+++ b/monai/engines/evaluator.py
@@ -17,9 +17,9 @@
 from monai.engines.utils import IterationEvents, default_prepare_batch
 from monai.engines.workflow import Workflow
 from monai.inferers import Inferer, SimpleInferer
-from monai.networks.utils import eval_mode
+from monai.networks.utils import eval_mode, train_mode
 from monai.transforms import Transform
-from monai.utils import ensure_tuple, exact_version, optional_import
+from monai.utils import ForwardMode, ensure_tuple, exact_version, optional_import
 from monai.utils.enums import CommonKeys as Keys
 
 if TYPE_CHECKING:
@@ -54,6 +54,8 @@ class Evaluator(Workflow):
         val_handlers: every handler is a set of Ignite Event-Handlers, must have `attach` function, like:
             CheckpointHandler, StatsHandler, SegmentationSaver, etc.
         amp: whether to enable auto-mixed-precision evaluation, default is False.
+        mode: model forward mode during evaluation, should be 'eval' or 'train',
+            which maps to `model.eval()` or `model.train()`, default to 'eval'.
 
     """
 
@@ -70,6 +72,7 @@ def __init__(
         additional_metrics: Optional[Dict[str, Metric]] = None,
         val_handlers: Optional[Sequence] = None,
         amp: bool = False,
+        mode: Union[ForwardMode, str] = ForwardMode.EVAL,
     ) -> None:
         super().__init__(
             device=device,
@@ -85,6 +88,13 @@ def __init__(
             handlers=val_handlers,
             amp=amp,
         )
+        mode = ForwardMode(mode)
+        if mode == ForwardMode.EVAL:
+            self.mode = eval_mode
+        elif mode == ForwardMode.TRAIN:
+            self.mode = train_mode
+        else:
+            raise ValueError(f"unsupported mode: {mode}, should be 'eval' or 'train'.")
 
     def run(self, global_epoch: int = 1) -> None:
         """
@@ -128,6 +138,8 @@ class SupervisedEvaluator(Evaluator):
         val_handlers: every handler is a set of Ignite Event-Handlers, must have `attach` function, like:
             CheckpointHandler, StatsHandler, SegmentationSaver, etc.
         amp: whether to enable auto-mixed-precision evaluation, default is False.
+        mode: model forward mode during evaluation, should be 'eval' or 'train',
+            which maps to `model.eval()` or `model.train()`, default to 'eval'.
 
     """
 
@@ -146,6 +158,7 @@ def __init__(
         additional_metrics: Optional[Dict[str, Metric]] = None,
         val_handlers: Optional[Sequence] = None,
         amp: bool = False,
+        mode: Union[ForwardMode, str] = ForwardMode.EVAL,
     ) -> None:
         super().__init__(
             device=device,
@@ -159,6 +172,7 @@ def __init__(
             additional_metrics=additional_metrics,
             val_handlers=val_handlers,
             amp=amp,
+            mode=mode,
         )
 
         self.network = network
@@ -197,7 +211,7 @@ def _iteration(self, engine: Engine, batchdata: Dict[str, torch.Tensor]) -> Dict
         # put iteration outputs into engine.state
         engine.state.output = output = {Keys.IMAGE: inputs, Keys.LABEL: targets}
         # execute forward computation
-        with eval_mode(self.network):
+        with self.mode(self.network):
             if self.amp:
                 with torch.cuda.amp.autocast():
                     output[Keys.PRED] = self.inferer(inputs, self.network, *args, **kwargs)
@@ -235,6 +249,8 @@ class EnsembleEvaluator(Evaluator):
         val_handlers: every handler is a set of Ignite Event-Handlers, must have `attach` function, like:
             CheckpointHandler, StatsHandler, SegmentationSaver, etc.
         amp: whether to enable auto-mixed-precision evaluation, default is False.
+        mode: model forward mode during evaluation, should be 'eval' or 'train',
+            which maps to `model.eval()` or `model.train()`, default to 'eval'.
 
     """
 
@@ -254,6 +270,7 @@ def __init__(
         additional_metrics: Optional[Dict[str, Metric]] = None,
         val_handlers: Optional[Sequence] = None,
         amp: bool = False,
+        mode: Union[ForwardMode, str] = ForwardMode.EVAL,
     ) -> None:
         super().__init__(
             device=device,
@@ -267,6 +284,7 @@ def __init__(
             additional_metrics=additional_metrics,
             val_handlers=val_handlers,
             amp=amp,
+            mode=mode,
         )
 
         self.networks = ensure_tuple(networks)
@@ -309,7 +327,7 @@ def _iteration(self, engine: Engine, batchdata: Dict[str, torch.Tensor]) -> Dict
         # put iteration outputs into engine.state
         engine.state.output = output = {Keys.IMAGE: inputs, Keys.LABEL: targets}
         for idx, network in enumerate(self.networks):
-            with eval_mode(network):
+            with self.mode(network):
                 if self.amp:
                     with torch.cuda.amp.autocast():
                         output.update({self.pred_keys[idx]: self.inferer(inputs, network, *args, **kwargs)})
diff --git a/monai/utils/__init__.py b/monai/utils/__init__.py
--- a/monai/utils/__init__.py
+++ b/monai/utils/__init__.py
@@ -18,6 +18,7 @@
     BlendMode,
     ChannelMatching,
     CommonKeys,
+    ForwardMode,
     GridSampleMode,
     GridSamplePadMode,
     InterpolateMode,
diff --git a/monai/utils/enums.py b/monai/utils/enums.py
--- a/monai/utils/enums.py
+++ b/monai/utils/enums.py
@@ -218,6 +218,15 @@ class Method(Enum):
     END = "end"
 
 
+class ForwardMode(Enum):
+    """
+    See also: :py:class:`monai.transforms.engines.evaluator.Evaluator`
+    """
+
+    TRAIN = "train"
+    EVAL = "eval"
+
+
 class InverseKeys:
     """Extra meta data keys used for inverse transforms."""
 


Test patch: