Repository: Project-MONAI/MONAI

Issue:
Documentation of nnUNet in monai.apps has several mistakes
**Describe the bug**

- Examples are not correct in https://github.com/Project-MONAI/MONAI/blob/dev/monai/apps/nnunet/__main__.py
- Some commands are using `nnUNetRunner` which is not implemented
- `num_gpus` is not effective




Discussion:

Resolving patch:
diff --git a/monai/apps/nnunet/__main__.py b/monai/apps/nnunet/__main__.py
--- a/monai/apps/nnunet/__main__.py
+++ b/monai/apps/nnunet/__main__.py
@@ -8,81 +8,6 @@
 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 # See the License for the specific language governing permissions and
 # limitations under the License.
-"""
-Examples:
-    - User can use the one-liner to start the nnU-Net workflow
-
-    .. code-block:: bash
-
-        python -m monai.apps.nnunet nnUNetV2Runner run --input "./input.yaml"
-
-    - convert dataset
-
-    .. code-block:: bash
-
-        python -m monai.apps.nnunet nnUNetRunner convert_dataset --input "./input_new.yaml"
-
-    - convert msd datasets
-
-    .. code-block:: bash
-
-        python -m monai.apps.nnunet nnUNetRunner convert_msd_dataset \\
-            --input "./input.yaml" --data_dir "Task05_Prostate"
-
-    - experiment planning and data pre-processing
-
-    .. code-block:: bash
-
-        python -m monai.apps.nnunet nnUNetRunner plan_and_process --input "./input.yaml"
-
-    - single-gpu training for all 20 models
-
-    .. code-block:: bash
-
-        python -m monai.apps.nnunet nnUNetV2Runner train --input "./input.yaml"
-
-    - single-gpu training for a single model
-
-    .. code-block:: bash
-
-        python -m monai.apps.nnunet nnUNetV2Runner train_single_model --input "./input.yaml" \\
-            --config "3d_fullres" \\
-            --fold 0 \\
-            --trainer_class_name "nnUNetTrainer_5epochs" \\
-            --export_validation_probabilities true
-
-    - multi-gpu training for all 20 models
-
-    .. code-block:: bash
-
-        export CUDA_VISIBLE_DEVICES=0,1  # optional
-        python -m monai.apps.nnunet nnUNetV2Runner train --input "./input.yaml" --num_gpus 2
-
-    - multi-gpu training for a single model
-
-    .. code-block:: bash
-
-        export CUDA_VISIBLE_DEVICES=0,1 # optional
-        python -m monai.apps.nnunet nnUNetV2Runner train_single_model --input "./input.yaml" \\
-            --config "3d_fullres" \\
-            --fold 0 \\
-            --trainer_class_name "nnUNetTrainer_5epochs" \\
-            --export_validation_probabilities true \\
-            --num_gpus 2
-
-    - find best configuration
-
-    .. code-block:: bash
-
-        python -m monai.apps.nnunet nnUNetRunner find_best_configuration --input "./input.yaml"
-
-    - predict, ensemble, and post-process
-
-    .. code-block:: bash
-
-        python -m monai.apps.nnunet nnUNetRunner predict_ensemble_postprocessing --input "./input.yaml"
-
-"""
 
 from __future__ import annotations
 
diff --git a/monai/apps/nnunet/nnunetv2_runner.py b/monai/apps/nnunet/nnunetv2_runner.py
--- a/monai/apps/nnunet/nnunetv2_runner.py
+++ b/monai/apps/nnunet/nnunetv2_runner.py
@@ -34,25 +34,114 @@
 
 
 class nnUNetV2Runner:  # noqa: N801
-    def __init__(self, input_config: Any, work_dir: str = "work_dir") -> None:
-        """
-        An interface for handling `nnU-Net` V2.
-        The users can run the nnU-Net V2 with default settings in one line of code.
-        They can also run parts of the process of nnU-Net V2 instead of the complete pipeline.
+    """
+    ``nnUNetV2Runner`` provides an interface in MONAI to use `nnU-Net` V2 library to analyze, train, and evaluate
+    neural networks for medical image segmentation tasks.
 
-        The output of the interface is a directory that contains:
+    ``nnUNetV2Runner`` can be used in two ways:
 
-            - converted dataset met requirement of nnU-Net V2
-            - data analysis results
-            - checkpoints from the trained U-Net models
-            - validation accuracy in each fold of cross-validation
-            - the predictions on the testing datasets from the final algorithm ensemble and potential post-processing
+    #. with one line of code to execute the complete pipeline.
+    #. with a series of commands to run each modules in the pipeline.
 
-        Args:
-            input_config: the configuration dictionary or the file path to the configuration in form of YAML.
-                The configuration should contain ``datalist``, ``dataroot``, ``modality``.
-            work_dir: working directory to save the intermediate and final results.
-        """
+    The output of the interface is a directory that contains:
+
+    #. converted dataset met the requirement of nnU-Net V2
+    #. data analysis results
+    #. checkpoints from the trained U-Net models
+    #. validation accuracy in each fold of cross-validation
+    #. the predictions on the testing datasets from the final algorithm ensemble and potential post-processing
+
+    Args:
+        input_config: the configuration dictionary or the file path to the configuration in the form of YAML.
+            The keys required in the configuration are:
+            - ``"datalist"``: File path to the datalist for the train/testing splits
+            - ``"dataroot"``: File path to the dataset
+            - ``"modality"``: Imaging modality, e.g. "CT", ["T2", "ADC"]
+            Currently, the configuration supports these optional keys:
+            - ``"nnunet_raw"``: File path that will be written to env variable for nnU-Net
+            - ``"nnunet_preprocessed"``: File path that will be written to env variable for nnU-Net
+            - ``"nnunet_results"``: File path that will be written to env variable for nnU-Net
+            - ``"nnUNet_trained_models"``
+            - ``"dataset_name_or_id"``: Name or Integer ID of the dataset
+            If an optional key is not specified, then the pipeline will use the default values.
+        work_dir: working directory to save the intermediate and final results.
+
+    Examples:
+        - Use the one-liner to start the nnU-Net workflow
+
+        .. code-block:: bash
+
+            python -m monai.apps.nnunet nnUNetV2Runner run --input_config ./input.yaml
+
+        - Use `convert_dataset` to prepare the data to meet nnU-Net requirements, generate dataset JSON file,
+            and copy the dataset to a location specified by ``nnunet_raw`` in the input config file
+
+        .. code-block:: bash
+
+            python -m monai.apps.nnunet nnUNetV2Runner convert_dataset --input_config="./input.yaml"
+
+        - `convert_msd_dataset` is an alternative option to prepare the data if the dataset is MSD.
+
+        .. code-block:: bash
+
+            python -m monai.apps.nnunet nnUNetV2Runner convert_msd_dataset \\
+                --input_config "./input.yaml" --data_dir "/path/to/Task09_Spleen"
+
+        - experiment planning and data pre-processing
+
+        .. code-block:: bash
+
+            python -m monai.apps.nnunet nnUNetV2Runner plan_and_process --input_config "./input.yaml"
+
+        - training all 20 models using all GPUs available.
+            "CUDA_VISIBLE_DEVICES" environment variable is not supported.
+
+        .. code-block:: bash
+
+            python -m monai.apps.nnunet nnUNetV2Runner train --input_config "./input.yaml"
+
+        - training a single model on a single GPU for 5 epochs. Here ``config`` is used to specify the configuration.
+
+        .. code-block:: bash
+
+            python -m monai.apps.nnunet nnUNetV2Runner train_single_model --input_config "./input.yaml" \\
+                --config "3d_fullres" \\
+                --fold 0 \\
+                --gpu_id 0 \\
+                --trainer_class_name "nnUNetTrainer_5epochs" \\
+                --export_validation_probabilities True
+
+        - training for all 20 models (4 configurations by 5 folds) on 2 GPUs
+
+        .. code-block:: bash
+
+            python -m monai.apps.nnunet nnUNetV2Runner train --input_config "./input.yaml" --device_ids "(0,1)"
+
+        - 5-fold training for a single model on 2 GPUs. Here ``configs`` is used to specify the configurations.
+
+        .. code-block:: bash
+
+            python -m monai.apps.nnunet nnUNetV2Runner train --input_config "./input.yaml" \\
+                --configs "3d_fullres" \\
+                --trainer_class_name "nnUNetTrainer_5epochs" \\
+                --export_validation_probabilities True \\
+                --device_ids "(0,1)"
+
+        - find the best configuration
+
+        .. code-block:: bash
+
+            python -m monai.apps.nnunet nnUNetV2Runner find_best_configuration --input_config "./input.yaml"
+
+        - predict, ensemble, and post-process
+
+        .. code-block:: bash
+
+            python -m monai.apps.nnunet nnUNetV2Runner predict_ensemble_postprocessing --input_config "./input.yaml"
+
+    """
+
+    def __init__(self, input_config: Any, work_dir: str = "work_dir") -> None:
         self.input_info: dict = {}
         self.input_config_or_dict = input_config
         self.work_dir = work_dir
@@ -95,7 +184,11 @@ def __init__(self, input_config: Any, work_dir: str = "work_dir") -> None:
 
             self.dataset_name = maybe_convert_to_dataset_name(int(self.dataset_name_or_id))
         except BaseException:
-            logger.warning("Dataset ID does not exist! Check input '.yaml' if this is unexpected.")
+            logger.warning(
+                f"Dataset with name/ID: {self.dataset_name_or_id} cannot be found in the record. "
+                "Please ignore the message above if you are running the pipeline from a fresh start. "
+                "But if the dataset is expected to be found, please check your input_config."
+            )
 
         from nnunetv2.configuration import default_num_processes
 
@@ -105,11 +198,12 @@ def __init__(self, input_config: Any, work_dir: str = "work_dir") -> None:
         self.best_configuration: dict = {}
 
     def convert_dataset(self):
+        """Convert and make a copy the dataset to meet the requirements of nnU-Net workflow."""
         try:
             raw_data_foldername_prefix = str(int(self.dataset_name_or_id) + 1000)
             raw_data_foldername_prefix = "Dataset" + raw_data_foldername_prefix[-3:]
 
-            # check if dataset is created
+            # check if the dataset is created
             subdirs = glob.glob(f"{self.nnunet_raw}/*")
             dataset_ids = [_item.split(os.sep)[-1] for _item in subdirs]
             dataset_ids = [_item.split("_")[0] for _item in dataset_ids]
@@ -136,7 +230,7 @@ def convert_dataset(self):
                 os.makedirs(os.path.join(raw_data_foldername, "imagesTr"))
                 os.makedirs(os.path.join(raw_data_foldername, "labelsTr"))
             else:
-                logger.warning("Input '.json' data list is incorrect.")
+                logger.error("The datalist file has incorrect format: the `training` key is not found.")
                 return
 
             test_key = None
@@ -168,16 +262,18 @@ def convert_dataset(self):
                 output_datafolder=raw_data_foldername,
             )
         except BaseException:
-            logger.warning("Input '.yaml' is incorrect.")
+            logger.warning("Input config may be incorrect. Detail info: error/exception message is:\n {err}")
             return
 
     def convert_msd_dataset(self, data_dir: str, overwrite_id: str | None = None, n_proc: int = -1) -> None:
         """
+        Convert and make a copy the MSD dataset to meet requirements of nnU-Net workflow.
+
         Args:
             data_dir: downloaded and extracted MSD dataset folder. CANNOT be nnUNetv1 dataset!
                 Example: "/workspace/downloads/Task05_Prostate".
             overwrite_id: Overwrite the dataset id. If not set then use the id of the MSD task (inferred from
-                folder name). Only use this if you already have an equivalently numbered dataset!
+                the folder name). Only use this if you already have an equivalently numbered dataset!
             n_proc: Number of processes used.
         """
         from nnunetv2.dataset_conversion.convert_MSD_dataset import convert_msd_dataset
@@ -194,22 +290,24 @@ def extract_fingerprints(
         verbose: bool = False,
     ) -> None:
         """
+        Extracts the dataset fingerprint used for experiment planning.
+
         Args:
             fpe: [OPTIONAL] Name of the Dataset Fingerprint Extractor class that should be used. Default is
-                'DatasetFingerprintExtractor'
+                "DatasetFingerprintExtractor".
             npfp: [OPTIONAL] Number of processes used for fingerprint extraction.
             verify_dataset_integrity: [RECOMMENDED] set this flag to check the dataset integrity. This is
                 useful and should be done once for each dataset!
             clean: [OPTIONAL] Set this flag to overwrite existing fingerprints. If this flag is not set and a
                 fingerprint already exists, the fingerprint extractor will not run.
             verbose: set this to print a lot of stuff. Useful for debugging. Will disable progress bar!
-                Recommended for cluster environments
+                Recommended for cluster environments.
         """
         from nnunetv2.experiment_planning.plan_and_preprocess_api import extract_fingerprints
 
         npfp = self.default_num_processes if npfp < 0 else npfp
 
-        logger.warning("Fingerprint extraction...")
+        logger.info("Fingerprint extraction...")
         extract_fingerprints([int(self.dataset_name_or_id)], fpe, npfp, verify_dataset_integrity, clean, verbose)
 
     def plan_experiments(
@@ -221,31 +319,33 @@ def plan_experiments(
         overwrite_plans_name: str = "nnUNetPlans",
     ) -> None:
         """
+        Generate a configuration file that specifies the details of the experiment.
+
         Args:
-            pl: [OPTIONAL] Name of the Experiment Planner class that should be used. Default is 'ExperimentPlanner'.
-                Note: There is no longer a distinction between 2d and 3d planner. It's an all in one solution now.
-            gpu_memory_target:[OPTIONAL] DANGER ZONE! Sets a custom GPU memory target. Default: 8 [GB].
-                Changing this will affect patch and batch size and will definitely affect your models performance!
+            pl: [OPTIONAL] Name of the Experiment Planner class that should be used. Default is "ExperimentPlanner".
+                Note: There is no longer a distinction between 2d and 3d planner. It's an all-in-one solution now.
+            gpu_memory_target: [OPTIONAL] DANGER ZONE! Sets a custom GPU memory target. Default: 8 [GB].
+                Changing this will affect patch and batch size and will definitely affect your models' performance!
                 Only use this if you really know what you are doing and NEVER use this without running the
                 default nnU-Net first (as a baseline).
-            preprocessor_name:[OPTIONAL] DANGER ZONE! Sets a custom preprocessor class. This class must be located in
-                nnunetv2.preprocessing. Default: 'DefaultPreprocessor'. Changing this may affect your models performance!
-                Only use this if you really know what you are doing and NEVER use this without running the
+            preprocessor_name: [OPTIONAL] DANGER ZONE! Sets a custom preprocessor class. This class must be located in
+                nnunetv2.preprocessing. Default: "DefaultPreprocessor". Changing this may affect your models'
+                performance! Only use this if you really know what you are doing and NEVER use this without running the
                 default nnU-Net first (as a baseline).
-            overwrite_target_spacing':[OPTIONAL] DANGER ZONE! Sets a custom target spacing for the 3d_fullres
+            overwrite_target_spacing: [OPTIONAL] DANGER ZONE! Sets a custom target spacing for the 3d_fullres
                 and 3d_cascade_fullres configurations. Default: None [no changes]. Changing this will affect
-                image size and potentially patch and batch size. This will definitely affect your models performance!
+                image size and potentially patch and batch size. This will definitely affect your models' performance!
                 Only use this if you really know what you are doing and NEVER use this without running the
                 default nnU-Net first (as a baseline). Changing the target spacing for the other configurations
                 is currently not implemented. New target spacing must be a list of three numbers!
-            overwrite_plans_name':[OPTIONAL] DANGER ZONE! If you used -gpu_memory_target, -preprocessor_name or
+            overwrite_plans_name: [OPTIONAL] DANGER ZONE! If you used -gpu_memory_target, -preprocessor_name or
                 -overwrite_target_spacing it is best practice to use -overwrite_plans_name to generate
                 a differently named plans file such that the nnunet default plans are not overwritten.
-                You will then need to specify your custom plan
+                You will then need to specify your custom plan.
         """
         from nnunetv2.experiment_planning.plan_and_preprocess_api import plan_experiments
 
-        logger.warning("Experiment planning...")
+        logger.info("Experiment planning...")
         plan_experiments(
             [int(self.dataset_name_or_id)],
             pl,
@@ -263,29 +363,31 @@ def preprocess(
         verbose: bool = False,
     ) -> None:
         """
+        Apply a set of preprocessing operations to the input data before the training.
+
         Args:
             overwrite_plans_name: [OPTIONAL] You can use this to specify a custom plans file that you may have
                 generated.
             c: [OPTIONAL] Configurations for which the preprocessing should be run. Default: 2d 3f_fullres
                 3d_lowres. 3d_cascade_fullres does not need to be specified because it uses the data
-                from 3f_fullres. Configurations that do not exist for some dataset will be skipped)
+                from 3f_fullres. Configurations that do not exist for some datasets will be skipped).
             n_proc: [OPTIONAL] Use this to define how many processes are to be used. If this is just one number then
                 this number of processes is used for all configurations specified with -c. If it's a
                 list of numbers this list must have as many elements as there are configurations. We
-                then iterate over zip(configs, num_processes) to determine then umber of processes
-                used for each configuration. More processes is always faster (up to the number of
-                threads your PC can support, so 8 for a 4 core CPU with hyperthreading. If you don't
+                then iterate over zip(configs, num_processes) to determine the number of processes
+                used for each configuration. More processes are always faster (up to the number of
+                threads your PC can support, so 8 for a 4-core CPU with hyperthreading. If you don't
                 know what that is then don't touch it, or at least don't increase it!). DANGER: More
                 often than not the number of processes that can be used is limited by the amount of
                 RAM available. Image resampling takes up a lot of RAM. MONITOR RAM USAGE AND
-                DECREASE -n_proc IF YOUR RAM FILLS UP TOO MUCH!. Default: 8 4 8 (=8 processes for 2d, 4
-                for 3d_fullres and 8 for 3d_lowres if -c is at its default)
-            verbose:Set this to print a lot of stuff. Useful for debugging. Will disable progress bar!
-                Recommended for cluster environments
+                DECREASE -n_proc IF YOUR RAM FILLS UP TOO MUCH! Default: 8 4 8 (=8 processes for 2d, 4
+                for 3d_fullres and 8 for 3d_lowres if -c is at its default).
+            verbose: Set this to print a lot of stuff. Useful for debugging. Will disable the progress bar!
+                Recommended for cluster environments.
         """
         from nnunetv2.experiment_planning.plan_and_preprocess_api import preprocess
 
-        logger.warning("Preprocessing...")
+        logger.info("Preprocessing...")
         preprocess(
             [int(self.dataset_name_or_id)],
             overwrite_plans_name,
@@ -311,10 +413,12 @@ def plan_and_process(
         verbose: bool = False,
     ) -> None:
         """
+        Performs experiment planning and preprocessing before the training.
+
         Args:
             fpe: [OPTIONAL] Name of the Dataset Fingerprint Extractor class that should be used. Default is
-                'DatasetFingerprintExtractor'
-            npfp: [OPTIONAL] Number of processes used for fingerprint extraction. Default: 8
+                "DatasetFingerprintExtractor".
+            npfp: [OPTIONAL] Number of processes used for fingerprint extraction. Default: 8.
             verify_dataset_integrity: [RECOMMENDED] set this flag to check the dataset integrity.
                 This is useful and should be done once for each dataset!
             no_pp: [OPTIONAL] Set this to only run fingerprint extraction and experiment planning (no
@@ -322,16 +426,15 @@ def plan_and_process(
             clean:[OPTIONAL] Set this flag to overwrite existing fingerprints. If this flag is not set and a
                 fingerprint already exists, the fingerprint extractor will not run. REQUIRED IF YOU
                 CHANGE THE DATASET FINGERPRINT EXTRACTOR OR MAKE CHANGES TO THE DATASET!
-            pl: [OPTIONAL] Name of the Experiment Planner class that should be used. Default is
-                ExperimentPlanner'. Note: There is no longer a distinction between 2d and 3d planner.
-                It's an all in one solution now.
+            pl: [OPTIONAL] Name of the Experiment Planner class that should be used. Default is "ExperimentPlanner".
+                Note: There is no longer a distinction between 2d and 3d planner. It's an all-in-one solution now.
             gpu_memory_target: [OPTIONAL] DANGER ZONE! Sets a custom GPU memory target. Default: 8 [GB].
                 Changing this will affect patch and batch size and will
-                definitely affect your models performance! Only use this if you really know what you
+                definitely affect your models' performance! Only use this if you really know what you
                 are doing and NEVER use this without running the default nnU-Net first (as a baseline).
             preprocessor_name: [OPTIONAL] DANGER ZONE! Sets a custom preprocessor class. This class must be located in
-                nnunetv2.preprocessing. Default: 'DefaultPreprocessor'. Changing this may affect your
-                models performance! Only use this if you really know what you
+                nnunetv2.preprocessing. Default: "DefaultPreprocessor". Changing this may affect your
+                models' performance! Only use this if you really know what you
                 are doing and NEVER use this without running the default nnU-Net first (as a baseline).
             overwrite_target_spacing: [OPTIONAL] DANGER ZONE! Sets a custom target spacing for the 3d_fullres and
                 3d_cascade_fullres configurations. Default: None [no changes]. Changing this will affect image size and
@@ -339,27 +442,27 @@ def plan_and_process(
                 Only use this if you really know what you are doing and NEVER use this without running the
                 default nnU-Net first (as a baseline). Changing the target spacing for the other
                 configurations is currently not implemented. New target spacing must be a list of three numbers!
-            overwrite_plans_name: [OPTIONAL] uSE A CUSTOM PLANS IDENTIFIER. If you used -gpu_memory_target,
+            overwrite_plans_name: [OPTIONAL] USE A CUSTOM PLANS IDENTIFIER. If you used -gpu_memory_target,
                 -preprocessor_name or -overwrite_target_spacing it is best practice to use -overwrite_plans_name to
                 generate a differently named plans file such that the nnunet default plans are not
                 overwritten. You will then need to specify your custom plans file with -p whenever
-                running other nnunet commands (training, inference etc)
+                running other nnunet commands (training, inference, etc)
             c: [OPTIONAL] Configurations for which the preprocessing should be run. Default: 2d 3f_fullres
                 3d_lowres. 3d_cascade_fullres does not need to be specified because it uses the data
-                from 3f_fullres. Configurations that do not exist for some dataset will be skipped.
+                from 3f_fullres. Configurations that do not exist for some datasets will be skipped.
             n_proc: [OPTIONAL] Use this to define how many processes are to be used. If this is just one number then
                 this number of processes is used for all configurations specified with -c. If it's a
                 list of numbers this list must have as many elements as there are configurations. We
-                then iterate over zip(configs, num_processes) to determine then umber of processes
-                used for each configuration. More processes is always faster (up to the number of
-                threads your PC can support, so 8 for a 4 core CPU with hyperthreading. If you don't
+                then iterate over zip(configs, num_processes) to determine the number of processes
+                used for each configuration. More processes are always faster (up to the number of
+                threads your PC can support, so 8 for a 4-core CPU with hyperthreading. If you don't
                 know what that is then don't touch it, or at least don't increase it!). DANGER: More
                 often than not the number of processes that can be used is limited by the amount of
                 RAM available. Image resampling takes up a lot of RAM. MONITOR RAM USAGE AND
-                DECREASE -n_proc IF YOUR RAM FILLS UP TOO MUCH!. Default: 8 4 8 (=8 processes for 2d, 4
-                for 3d_fullres and 8 for 3d_lowres if -c is at its default)
+                DECREASE -n_proc IF YOUR RAM FILLS UP TOO MUCH! Default: 8 4 8 (=8 processes for 2d, 4
+                for 3d_fullres and 8 for 3d_lowres if -c is at its default).
             verbose: Set this to print a lot of stuff. Useful for debugging. Will disable progress bar!
-                (Recommended for cluster environments')
+                (Recommended for cluster environments).
         """
         self.extract_fingerprints(fpe, npfp, verify_dataset_integrity, clean, verbose)
         self.plan_experiments(pl, gpu_memory_target, preprocessor_name, overwrite_target_spacing, overwrite_plans_name)
@@ -369,22 +472,31 @@ def plan_and_process(
 
     def train_single_model(self, config: Any, fold: int, gpu_id: int = 0, **kwargs: Any) -> None:
         """
+        Run the training on a single GPU with one specified configuration provided.
+        Note: this will override the environment variable `CUDA_VISIBLE_DEVICES`.
+
         Args:
-            config: configuration that should be trained.
+            config: configuration that should be trained. Examples: "2d", "3d_fullres", "3d_lowres".
             fold: fold of the 5-fold cross-validation. Should be an int between 0 and 4.
-            trainer_class_name: name of the custom trainer class. default: 'nnUNetTrainer'.
-            plans_identifier: custom plans identifier. default: 'nnUNetPlans'.
-            pretrained_weights: path to nnU-Net checkpoint file to be used as pretrained model. Will only be used
-                when actually training. Beta. Use with caution. default: False.
-            num_gpus: number of GPUs to use for training. default: 1.
-            use_compressed_data: true to use compressed data for training. Reading compressed data is much more CPU and
-                (potentially) RAM intensive and should only be used if you know what you are doing default: False.
-            export_validation_probabilities: true to save softmax predictions from final validation as npz files
-                (in addition to predicted segmentations). Needed for finding the best ensemble. default: False.
-            continue_training: continue training from latest checkpoint. default: False.
-            only_run_validation: true to run the validation only. Requires training to have finished. default: False.
-            disable_checkpointing: true to disable checkpointing. Ideal for testing things out and you don't want to
-                flood your hard drive with checkpoints. default: False.
+            gpu_id: an integer to select the device to use. Default: 0.
+        from nnunetv2.run.run_training import run_training
+            kwargs: this optional parameter allows you to specify additional arguments in
+                ``nnunetv2.run.run_training.run_training``. Currently supported args are
+                    - trainer_class_name: name of the custom trainer class. Default: "nnUNetTrainer".
+                    - plans_identifier: custom plans identifier. Default: "nnUNetPlans".
+                    - pretrained_weights: path to nnU-Net checkpoint file to be used as pretrained model. Will only be
+                        used when actually training. Beta. Use with caution. Default: False.
+                    - use_compressed_data: True to use compressed data for training. Reading compressed data is much
+                        more CPU and (potentially) RAM intensive and should only be used if you know what you are
+                        doing. Default: False.
+                    - export_validation_probabilities: True to save softmax predictions from final validation as npz
+                        files (in addition to predicted segmentations). Needed for finding the best ensemble.
+                        Default: False.
+                    - continue_training: continue training from latest checkpoint. Default: False.
+                    - only_run_validation: True to run the validation only. Requires training to have finished.
+                        Default: False.
+                    - disable_checkpointing: True to disable checkpointing. Ideal for testing things out and you
+                        don't want to flood your hard drive with checkpoints. Default: False.
         """
         os.environ["CUDA_VISIBLE_DEVICES"] = f"{gpu_id}"
 
@@ -399,10 +511,16 @@ def train(
         **kwargs: Any,
     ) -> None:
         """
+        Run the training for all the models specified by the configurations.
+        Note: to set the number of GPUs to use, use ``devices_ids`` instead of the `CUDA_VISIBLE_DEVICES`
+        environment variable.
+
         Args:
             configs: configurations that should be trained.
-                default: ("2d", "3d_fullres", "3d_lowres", "3d_cascade_fullres").
-            device_ids: ids of GPUs to use for training. default: None (all available GPUs).
+                Default: ("2d", "3d_fullres", "3d_lowres", "3d_cascade_fullres").
+            device_ids: a tuple/list of GPU device IDs to use for the training. Default: None (all available GPUs).
+            kwargs: this optional parameter allows you to specify additional arguments defined in the
+                ``train_single_model`` method.
         """
 
         if device_ids is None:
@@ -410,7 +528,7 @@ def train(
             output = result.stdout.decode("utf-8")
             num_gpus = len(output.strip().split("\n"))
             device_ids = tuple(range(num_gpus))
-        logger.warning(f"number of gpus is {len(device_ids)}, device ids are {device_ids}")
+        logger.info(f"number of GPUs is {len(device_ids)}, device ids are {device_ids}")
         if len(device_ids) > 1:
             self.train_parallel(configs=ensure_tuple(configs), device_ids=device_ids, **kwargs)
         else:
@@ -424,6 +542,16 @@ def train_parallel_cmd(
         device_ids: tuple | None = None,
         **kwargs: Any,
     ) -> list:
+        """
+        Create the line command for subprocess call for parallel training.
+
+        Args:
+            configs: configurations that should be trained.
+                Default: ("2d", "3d_fullres", "3d_lowres", "3d_cascade_fullres").
+            device_ids: a tuple of GPU device IDs to use for the training. Default: None (all available GPUs).
+            kwargs: this optional parameter allows you to specify additional arguments defined in the
+                ``train_single_model`` method.
+        """
         # unpack compressed files
         folder_names = []
         for root, _, files in os.walk(os.path.join(self.nnunet_preprocessed, self.dataset_name)):
@@ -433,7 +561,7 @@ def train_parallel_cmd(
         from nnunetv2.training.dataloading.utils import unpack_dataset
 
         for folder_name in folder_names:
-            logger.warning(f"[info] unpacking '{folder_name}'...")
+            logger.info(f"unpacking '{folder_name}'...")
             unpack_dataset(
                 folder=folder_name,
                 unpack_segmentation=True,
@@ -457,8 +585,8 @@ def train_parallel_cmd(
                         the_device = device_ids[_index % n_devices]  # type: ignore
                         cmd = (
                             "python -m monai.apps.nnunet nnUNetV2Runner train_single_model "
-                            + f"--input_config '{self.input_config_or_dict}' --config '{_config}' "
-                            + f"--fold {_i} --gpu_id {the_device}"
+                            + f"--input_config '{self.input_config_or_dict}' --work_dir '{self.work_dir}' "
+                            + f"--config '{_config}' --fold {_i} --gpu_id {the_device}"
                         )
                         for _key, _value in kwargs.items():
                             cmd += f" --{_key} {_value}"
@@ -473,20 +601,26 @@ def train_parallel(
         **kwargs: Any,
     ) -> None:
         """
+        Create the line command for subprocess call for parallel training.
+        Note: to set the number of GPUs to use, use ``devices_ids`` instead of the `CUDA_VISIBLE_DEVICES`
+        environment variable.
+
         Args:
             configs: configurations that should be trained.
                 default: ("2d", "3d_fullres", "3d_lowres", "3d_cascade_fullres").
-            device_ids: device ids to use for training. default: None.
+            device_ids: a tuple of GPU device IDs to use for the training. Default: None (all available GPUs).
+            kwargs: this optional parameter allows you to specify additional arguments defined in the
+                ``train_single_model`` method.
         """
         all_cmds = self.train_parallel_cmd(configs=configs, device_ids=device_ids, **kwargs)
         for s, cmds in enumerate(all_cmds):
             for gpu_id, gpu_cmd in cmds.items():
                 if not gpu_cmd:
                     continue
-                logger.warning(
-                    f"\n[info] training - stage {s + 1}:\n"
-                    f"[info] for gpu {gpu_id}, commands: {gpu_cmd}\n"
-                    f"[info] log '.txt' inside '{os.path.join(self.nnunet_results, self.dataset_name)}'"
+                logger.info(
+                    f"\ntraining - stage {s + 1}:\n"
+                    f"for gpu {gpu_id}, commands: {gpu_cmd}\n"
+                    f"log '.txt' inside '{os.path.join(self.nnunet_results, self.dataset_name)}'"
                 )
         for stage in all_cmds:
             processes = []
@@ -501,9 +635,13 @@ def train_parallel(
 
     def validate_single_model(self, config: str, fold: int, **kwargs: Any) -> None:
         """
+        Perform validation on single model.
+
         Args:
             config: configuration that should be trained.
             fold: fold of the 5-fold cross-validation. Should be an int between 0 and 4.
+            kwargs: this optional parameter allows you to specify additional arguments defined in the
+                ``train_single_model`` method.
         """
         self.train_single_model(config=config, fold=fold, only_run_validation=True, **kwargs)
 
@@ -511,9 +649,13 @@ def validate(
         self, configs: tuple = (M.N_3D_FULLRES, M.N_2D, M.N_3D_LOWRES, M.N_3D_CASCADE_FULLRES), **kwargs: Any
     ) -> None:
         """
+        Perform validation in all models defined by the configurations over 5 folds.
+
         Args:
             configs: configurations that should be trained.
                 default: ("2d", "3d_fullres", "3d_lowres", "3d_cascade_fullres").
+            kwargs: this optional parameter allows you to specify additional arguments defined in the
+                ``train_single_model`` method.
         """
         for cfg in ensure_tuple(configs):
             for _fold in range(self.num_folds):
@@ -531,16 +673,18 @@ def find_best_configuration(
         strict: bool = False,
     ) -> None:
         """
+        Find the best model configurations.
+
         Args:
-            plans: list of plan identifiers. Default: nnUNetPlans
-            configs: list of configurations. Default: ['2d', '3d_fullres', '3d_lowres', '3d_cascade_fullres']
-            trainers: list of trainers. Default: nnUNetTrainer
-            num_processes: number of processes to use for ensembling, postprocessing etc
-            folds: folds to use. Default: 0 1 2 3 4
-            allow_ensembling: Set this flag to enable ensembling
+            plans: list of plan identifiers. Default: nnUNetPlans.
+            configs: list of configurations. Default: ["2d", "3d_fullres", "3d_lowres", "3d_cascade_fullres"].
+            trainers: list of trainers. Default: nnUNetTrainer.
+            allow_ensembling: Set this flag to enable ensembling.
+            num_processes: number of processes to use for ensembling, postprocessing, etc.
             overwrite: If set we will overwrite already ensembled files etc. May speed up consecutive
-                runs of this command (why would oyu want to do that?) at the risk of not updating
-                outdated results.
+                runs of this command (not recommended) at the risk of not updating outdated results.
+            folds: folds to use. Default: (0, 1, 2, 3, 4).
+            strict: a switch that triggers RunTimeError if the logging folder cannot be found. Default: False.
         """
         from nnunetv2.evaluation.find_best_configuration import (
             dumb_trainer_config_plans_to_trained_models_dict,
@@ -616,7 +760,7 @@ def predict(
             num_processes_preprocessing: out-of-RAM issues.
             num_processes_segmentation_export: Number of processes used for segmentation export.
                 More is not always better. Beware of out-of-RAM issues.
-            gpu_id: which gpu to use for prediction.
+            gpu_id: which GPU to use for prediction.
         """
         os.environ["CUDA_VISIBLE_DEVICES"] = f"{gpu_id}"
 
@@ -658,11 +802,15 @@ def predict_ensemble_postprocessing(
         **kwargs: Any,
     ) -> None:
         """
+        Run prediction, ensemble, and/or postprocessing optionally.
+
         Args:
             folds: which folds to use
             run_ensemble: whether to run ensembling.
             run_predict: whether to predict using trained checkpoints
             run_postprocessing: whether to conduct post-processing
+            kwargs: this optional parameter allows you to specify additional arguments defined in the
+                ``predict`` method.
         """
         from nnunetv2.ensembling.ensemble import ensemble_folders
         from nnunetv2.postprocessing.remove_connected_components import apply_postprocessing_to_folder
@@ -730,12 +878,14 @@ def run(
         run_predict_ensemble_postprocessing: bool = True,
     ) -> None:
         """
+        Run the nnU-Net pipeline.
+
         Args:
-            run_convert_dataset: whether to convert datasets, defaults to False.
-            run_plan_and_process: whether to preprocess and analyze the dataset, defaults to False.
-            run_train: whether to train models, defaults to False.
-            run_find_best_configuration: whether to find the best model (ensemble) configurations, defaults to False.
-            run_predict_ensemble_postprocessing: whether to make predictions on test datasets, defaults to False.
+            run_convert_dataset: whether to convert datasets, defaults to True.
+            run_plan_and_process: whether to preprocess and analyze the dataset, defaults to True.
+            run_train: whether to train models, defaults to True.
+            run_find_best_configuration: whether to find the best model (ensemble) configurations, defaults to True.
+            run_predict_ensemble_postprocessing: whether to make predictions on test datasets, defaults to True.
         """
         if run_convert_dataset:
             self.convert_dataset()
diff --git a/monai/apps/nnunet/utils.py b/monai/apps/nnunet/utils.py
--- a/monai/apps/nnunet/utils.py
+++ b/monai/apps/nnunet/utils.py
@@ -47,7 +47,7 @@ def analyze_data(datalist_json: dict, data_dir: str) -> tuple[int, int]:
         os.path.join(data_dir, datalist_json["training"][0]["image"])
     )
     num_input_channels = img.size()[0] if img.dim() == 4 else 1
-    logger.warning(f"[info] num_input_channels: {num_input_channels}")
+    logger.info(f"num_input_channels: {num_input_channels}")
 
     num_foreground_classes = 0
     for _i in range(len(datalist_json["training"])):
@@ -55,7 +55,7 @@ def analyze_data(datalist_json: dict, data_dir: str) -> tuple[int, int]:
             os.path.join(data_dir, datalist_json["training"][_i]["label"])
         )
         num_foreground_classes = max(num_foreground_classes, int(seg.max()))
-    logger.warning(f"[info] num_foreground_classes: {num_foreground_classes}")
+    logger.info(f"num_foreground_classes: {num_foreground_classes}")
 
     return num_input_channels, num_foreground_classes
 
@@ -82,7 +82,7 @@ def create_new_data_copy(
         if _key is None:
             continue
 
-        logger.warning(f"[info] converting data section: {_key}...")
+        logger.info(f"converting data section: {_key}...")
         for _k in tqdm(range(len(datalist_json[_key]))) if has_tqdm else range(len(datalist_json[_key])):
             orig_img_name = (
                 datalist_json[_key][_k]["image"]


Test patch: