Repository: Project-MONAI/MONAI

Issue:
Sliding Window Inference giving error on 0.4.0
Command used for Sliding Window Inference (Error on Monai 0.4.0 but its working fine on 0.3.0)

### Code Snippet:
---------------------------------------------------------------------------

```
for val_data in self.val_loader:
    val_step_start_time = time.time()
    val_images, val_labels = val_data["image"].to(self.device), val_data["label"].to(self.device)
    roi_size = (128, 128, 128)
    sw_batch_size = 6
    if amp:
        with torch.cuda.amp.autocast():
            val_outputs = sliding_window_inference(val_images, roi_size, sw_batch_size, self.network, sw_device=self.device, device=self.device)
    else:
    val_outputs = sliding_window_inference(val_images, roi_size, sw_batch_size, self.network)  
```
Getting same error with and without amp


Network Used (DynUNet as proposed in tutorial):

```
self.device = torch.device("cuda:0")
sizes, spacings = [128, 128, 128], [1.83723155, 1.83723155, 2.42438879]
n_class, in_channels = 2, 1
best_dice, best_epoch = (n_class - 1) * [0], (n_class - 1) * [0]
strides, kernels = [], []
while True:
    spacing_ratio = [sp / min(spacings) for sp in spacings]
    stride = [2 if ratio <= 2 and size >= 8 else 1 for (ratio, size) in zip(spacing_ratio, sizes)]
    kernel = [3 if ratio <= 2 else 1 for ratio in spacing_ratio]
    if all(s == 1 for s in stride):
        break
    sizes = [i / j for i, j in zip(sizes, stride)]
    spacings = [i * j for i, j in zip(spacings, stride)]
    kernels.append(kernel)
    strides.append(stride)
strides.insert(0, len(spacings) * [1])
kernels.append(len(spacings) * [3])
self.network = DynUNet(
    spatial_dims=3,
    in_channels=in_channels,
    out_channels=n_class,
    kernel_size=kernels,
    strides=strides,
    upsample_kernel_size=strides[1:],
    norm_name="instance",
    deep_supervision=True,
    deep_supr_num=2,
    res_block=False,
).to(self.device)
```

### Error: 
---------------------------------------------------------------------------

AttributeError                            Traceback (most recent call last)
/data/archit/Liver/Experiments/monai/main.py in <module>
     25
     26 if __name__ == "__main__":
---> 27     main()

/data/archit/Liver/Experiments/monai/main.py in main()
     19         if args.continue_training == True:
     20             trainer.load_best_checkpoint()
---> 21         trainer.trainProcess(amp=True)
     22
     23

/data/archit/Liver/Experiments/monai/TeraReconAI/train/segmentationTrainer.py in trainProcess(self, amp)
     71         self.initialize_network()
     72         amp_start = time.time()
---> 73         super()._trainProcess(amp)
     74         amp_total_time = time.time() - amp_start
     75         print(f"Total training time with AMP: {amp_total_time:.4f}")

/data/archit/Liver/Experiments/monai/TeraReconAI/train/trainer.py in _trainProcess(self, amp)
    168                         # else:
    169                         self.network = self.network.to(self.device)
--> 170                         val_outputs = sliding_window_inference(val_images, roi_size, sw_batch_size, self.network, sw_device=self.device, device=self.device)
    171
    172

/data/archit/Software/anaconda3/envs/monai/lib/python3.8/site-packages/monai/inferers/utils.py in sliding_window_inference(inputs, roi_size, sw_batch_size, predictor, overlap, mode, sigma_scale, padding_mode, cval, sw_device, device, *args, **kwargs)
    127         ]
    128         window_data = torch.cat([inputs[win_slice] for win_slice in unravel_slice]).to(sw_device)
--> 129         seg_prob = predictor(window_data, *args, **kwargs).to(device)  # batched patch segmentation
    130
    131         if not _initialized:  # init. buffer at the first iteration

AttributeError: 'list' object has no attribute 'to'



Discussion:
Hi @architraj29 ,

Thanks for your interest here.
Your issue is similar to: https://github.com/Project-MONAI/MONAI/issues/1263
I will submit a PR to make it more clear.

Thanks.


Resolving patch:
diff --git a/monai/networks/nets/dynunet.py b/monai/networks/nets/dynunet.py
--- a/monai/networks/nets/dynunet.py
+++ b/monai/networks/nets/dynunet.py
@@ -79,10 +79,6 @@ class DynUNet(nn.Module):
         upsample_kernel_size: convolution kernel size for transposed convolution layers.
         norm_name: [``"batch"``, ``"instance"``, ``"group"``]
             feature normalization type and arguments.
-        deep_supervision: whether to add deep supervision head before output. Defaults to ``True``.
-            If added, in training mode, the network will output not only the last feature maps
-            (after being converted via output block), but also the previous feature maps that come
-            from the intermediate up sample layers.
         deep_supr_num: number of feature maps that will output during deep supervision head. The
             value should be less than the number of up sample layers. Defaults to 1.
         res_block: whether to use residual connection based convolution blocks during the network.
@@ -98,7 +94,6 @@ def __init__(
         strides: Sequence[Union[Sequence[int], int]],
         upsample_kernel_size: Sequence[Union[Sequence[int], int]],
         norm_name: str = "instance",
-        deep_supervision: bool = True,
         deep_supr_num: int = 1,
         res_block: bool = False,
     ):
@@ -110,7 +105,6 @@ def __init__(
         self.strides = strides
         self.upsample_kernel_size = upsample_kernel_size
         self.norm_name = norm_name
-        self.deep_supervision = deep_supervision
         self.conv_block = UnetResBlock if res_block else UnetBasicBlock
         self.filters = [min(2 ** (5 + i), 320 if spatial_dims == 3 else 512) for i in range(len(strides))]
         self.input_block = self.get_input_block()
@@ -143,8 +137,6 @@ def create_skips(index, downsamples, upsamples, superheads, bottleneck):
                 return bottleneck
             elif index == 0:  # don't associate a supervision head with self.input_block
                 current_head, rest_heads = nn.Identity(), superheads
-            elif not self.deep_supervision:  # bypass supervision heads by passing nn.Identity in place of a real one
-                current_head, rest_heads = nn.Identity(), superheads[1:]
             else:
                 current_head, rest_heads = superheads[0], superheads[1:]
 
@@ -183,12 +175,14 @@ def check_deep_supr_num(self):
 
     def forward(self, x):
         out = self.skip_layers(x)
-        out = self.output_block(out)
+        return self.output_block(out)
 
-        if self.training and self.deep_supervision:
-            return [out] + self.heads[1 : self.deep_supr_num + 1]
+    def get_feature_maps(self):
+        """
+        Return the feature maps.
 
-        return [out]
+        """
+        return self.heads[1 : self.deep_supr_num + 1]
 
     def get_input_block(self):
         return self.conv_block(


Test patch:
diff --git a/tests/test_dynunet.py b/tests/test_dynunet.py
--- a/tests/test_dynunet.py
+++ b/tests/test_dynunet.py
@@ -43,7 +43,6 @@
                         "strides": strides,
                         "upsample_kernel_size": strides[1:],
                         "norm_name": "batch",
-                        "deep_supervision": False,
                         "res_block": res_block,
                     },
                     (1, in_channels, in_size, in_size),
@@ -66,7 +65,6 @@
                 "strides": ((1, 2, 1), 2, 2, 1),
                 "upsample_kernel_size": (2, 2, 1),
                 "norm_name": "instance",
-                "deep_supervision": False,
                 "res_block": res_block,
             },
             (1, in_channels, in_size, in_size, in_size),
@@ -88,7 +86,6 @@
                         "strides": strides,
                         "upsample_kernel_size": strides[1:],
                         "norm_name": "group",
-                        "deep_supervision": True,
                         "deep_supr_num": deep_supr_num,
                         "res_block": res_block,
                     },
@@ -110,7 +107,7 @@ def test_shape(self, input_param, input_shape, expected_shape):
         net = DynUNet(**input_param).to(device)
         with eval_mode(net):
             result = net(torch.randn(input_shape).to(device))
-            self.assertEqual(result[0].shape, expected_shape)
+            self.assertEqual(result.shape, expected_shape)
 
     def test_script(self):
         input_param, input_shape, _ = TEST_CASE_DYNUNET_2D[0]
@@ -124,7 +121,7 @@ class TestDynUNetDeepSupervision(unittest.TestCase):
     def test_shape(self, input_param, input_shape, expected_shape):
         net = DynUNet(**input_param).to(device)
         with torch.no_grad():
-            results = net(torch.randn(input_shape).to(device))
+            results = [net(torch.randn(input_shape).to(device))] + net.get_feature_maps()
             self.assertEqual(len(results), len(expected_shape))
             for idx in range(len(results)):
                 result, sub_expected_shape = results[idx], expected_shape[idx]