Repository: Project-MONAI/MONAI

Issue:
AutoRunner bug with AlgoEnsembleBestByFold before setting the num_fold
**Describe the bug**
AutoRunner will fail if the users first set their ensemble method to `AlgoEnsembleBestByFold` and then change the `num_fold` by `set_num_fold`

**To Reproduce**
```python
runner = AutoRunner()
runner.set_training_params(train_param)  # make it 2 epochs in train_param
runner.set_ensemble_method("AlgoEnsembleBestByFold")
runner.set_num_fold(1)
runner.run()
```

```
Traceback (most recent call last):
  File "/workspace/monai/monai-in-dev/tests/test_integration_autorunner.py", line 108, in test_autorunner_ensemble
    runner.run()
  File "/workspace/monai/monai-in-dev/monai/apps/auto3dseg/auto_runner.py", line 633, in run
    preds = ensembler(pred_param=self.pred_params)
  File "/workspace/monai/monai-in-dev/monai/apps/auto3dseg/ensemble_builder.py", line 156, in __call__
    infer_instance = algo[AlgoEnsembleKeys.ALGO]
TypeError: 'NoneType' object is not subscriptable
```
**Expected behavior**
The ensemble method can update itself after users `set_num_fold`
flake8 `zip()` without an explicit `strict=` parameter
```
6.0.0 (flake8-bugbear: 22.12.6, flake8-comprehensions: 3.10.1,
flake8-executable: 2.1.2, mccabe: 0.7.0, pep8-naming: 0.13.2, pycodestyle:
2.10.0, pyflakes: 3.0.1) CPython 3.8.14 on Linux
/home/runner/work/MONAI/MONAI/monai/apps/deepgrow/transforms.py:861:58: B905 `zip()` without an explicit `strict=` parameter.
/home/runner/work/MONAI/MONAI/monai/apps/detection/metrics/coco.py:470:51: B905 `zip()` without an explicit `strict=` parameter.
/home/runner/work/MONAI/MONAI/monai/apps/detection/metrics/matching.py:156:65: B905 `zip()` without an explicit `strict=` parameter.
/home/runner/work/MONAI/MONAI/monai/apps/detection/networks/retinanet_detector.py:651:29: B905 `zip()` without an explicit `strict=` parameter.
/home/runner/work/MONAI/MONAI/monai/apps/detection/networks/retinanet_detector.py:724:53: B905 `zip()` without an explicit `strict=` parameter.
/home/runner/work/MONAI/MONAI/monai/apps/detection/networks/retinanet_detector.py:787:80: B905 `zip()` without an explicit `strict=` parameter.
/home/runner/work/MONAI/MONAI/monai/apps/detection/networks/retinanet_detector.py:825:103: B905 `zip()` without an explicit `strict=` parameter.
/home/runner/work/MONAI/MONAI/monai/apps/detection/transforms/array.py:224:61: B905 `zip()` without an explicit `strict=` parameter.
/home/runner/work/MONAI/MONAI/monai/apps/detection/transforms/array.py:225:57: B905 `zip()` without an explicit `strict=` parameter.
```


Discussion:

Resolving patch:
diff --git a/monai/apps/auto3dseg/auto_runner.py b/monai/apps/auto3dseg/auto_runner.py
--- a/monai/apps/auto3dseg/auto_runner.py
+++ b/monai/apps/auto3dseg/auto_runner.py
@@ -265,14 +265,16 @@ def __init__(
         self.ensemble = ensemble  # last step, no need to check
 
         # intermediate variables
-        self.set_num_fold(num_fold=5)
+        self.num_fold = 5
+        self.ensemble_method_name = "AlgoEnsembleBestN"
         self.set_training_params()
         self.set_prediction_params()
         self.set_analyze_params()
 
         self.save_image = self.set_image_save_transform(kwargs)
         self.ensemble_method: AlgoEnsemble
-        self.set_ensemble_method()
+        self.set_ensemble_method(self.ensemble_method_name)
+        self.set_num_fold(num_fold=self.num_fold)
 
         # hpo
         if hpo_backend.lower() != "nni":
@@ -334,10 +336,16 @@ def set_num_fold(self, num_fold: int = 5):
 
         Args:
             num_fold: a positive integer to define the number of folds.
+
+        Notes:
+            If the ensemble method is ``AlgoEnsembleBestByFold``, this function automatically updates the ``n_fold``
+            parameter in the ``ensemble_method`` to avoid inconsistency between the training and the ensemble.
         """
         if num_fold <= 0:
             raise ValueError(f"num_fold is expected to be an integer greater than zero. Now it gets {num_fold}")
         self.num_fold = num_fold
+        if self.ensemble_method_name == "AlgoEnsembleBestByFold":
+            self.ensemble_method.n_fold = self.num_fold  # type: ignore
 
     def set_training_params(self, params: Optional[Dict[str, Any]] = None):
         """


Test patch:
diff --git a/tests/test_integration_autorunner.py b/tests/test_integration_autorunner.py
--- a/tests/test_integration_autorunner.py
+++ b/tests/test_integration_autorunner.py
@@ -107,6 +107,16 @@ def test_autorunner(self) -> None:
         with skip_if_downloading_fails():
             runner.run()
 
+    @skip_if_no_cuda
+    def test_autorunner_ensemble(self) -> None:
+        work_dir = os.path.join(self.test_path, "work_dir")
+        runner = AutoRunner(work_dir=work_dir, input=self.data_src_cfg)
+        runner.set_training_params(train_param)  # 2 epochs
+        runner.set_ensemble_method("AlgoEnsembleBestByFold")
+        runner.set_num_fold(1)
+        with skip_if_downloading_fails():
+            runner.run()
+
     @skip_if_no_cuda
     @unittest.skipIf(not has_nni, "nni required")
     def test_autorunner_hpo(self) -> None: