Repository: Project-MONAI/MONAI

Issue:
Error using spatial size
I do not understand this error. 
<img width="1291" alt="Screen Shot 2020-11-06 at 9 55 16 AM" src="https://user-images.githubusercontent.com/33342277/98325281-8785e300-2016-11eb-9e36-19fb8dcabd22.png">
    
I used the transforamtion as below:
val_transform = monai.transforms.Compose(
    [
        LoadPNGd(keys = ["image","label"]),
        AsChannelFirstd(keys = ["image","label"], channel_dim = -1), 
        Lambdad(keys=['label'], func=lambda x: x[0:1]*0.2125 + x[1:2]*0.7154 + x[2:3]*0.0721),
        Resized(["image","label"],spatial_size=(256,256)),
        ScaleIntensityd(keys = ["image","label"], minv=0.0, maxv=1.0),
        NormalizeIntensityd(keys=["image"], subtrahend=mean, divisor=std),
        ToTensord(keys = ["image","label"])
    ]
    )


Discussion:

Resolving patch:
diff --git a/monai/transforms/spatial/array.py b/monai/transforms/spatial/array.py
--- a/monai/transforms/spatial/array.py
+++ b/monai/transforms/spatial/array.py
@@ -343,7 +343,6 @@ def __call__(
             )
         spatial_size = fall_back_tuple(self.spatial_size, img.shape[1:])
         resized = torch.nn.functional.interpolate(  # type: ignore
-            recompute_scale_factor=True,
             input=torch.as_tensor(np.ascontiguousarray(img), dtype=torch.float).unsqueeze(0),
             size=spatial_size,
             mode=self.mode.value if mode is None else InterpolateMode(mode).value,


Test patch:
diff --git a/tests/test_integration_classification_2d.py b/tests/test_integration_classification_2d.py
--- a/tests/test_integration_classification_2d.py
+++ b/tests/test_integration_classification_2d.py
@@ -24,10 +24,12 @@
 from monai.networks.nets import densenet121
 from monai.transforms import AddChannel, Compose, LoadPNG, RandFlip, RandRotate, RandZoom, ScaleIntensity, ToTensor
 from monai.utils import set_determinism
+from tests.testing_data.integration_answers import test_integration_value
 from tests.utils import skip_if_quick
 
 TEST_DATA_URL = "https://www.dropbox.com/s/5wwskxctvcxiuea/MedNIST.tar.gz?dl=1"
 MD5_VALUE = "0bc7306e7427e00ad1c5526a6677552d"
+TASK = "integration_classification_2d"
 
 
 class MedNISTDataset(torch.utils.data.Dataset):
@@ -149,6 +151,7 @@ def run_inference_test(root_dir, test_x, test_y, device="cuda:0"):
     return tps
 
 
+@skip_if_quick
 class IntegrationClassification2D(unittest.TestCase):
     def setUp(self):
         set_determinism(seed=0)
@@ -205,7 +208,6 @@ def tearDown(self):
             warnings.warn("not found best_metric_model.pth, training skipped?")
             pass
 
-    @skip_if_quick
     def test_training(self):
         if not os.path.exists(os.path.join(self.data_dir, "MedNIST")):
             # skip test if no MedNIST dataset
@@ -221,21 +223,19 @@ def test_training(self):
 
             # check training properties
             print(f"integration_classification_2d {losses}")
-            np.testing.assert_allclose(
-                losses, [0.776872731128316, 0.16163671757005582, 0.07500977408449361, 0.04593902905797882], rtol=1e-2
-            )
+            self.assertTrue(test_integration_value(TASK, key="losses", data=losses, rtol=1e-2))
             repeated[i].extend(losses)
             print("best metric", best_metric)
-            np.testing.assert_allclose(best_metric, 0.9999186110333108, rtol=1e-4)
+            self.assertTrue(test_integration_value(TASK, key="best_metric", data=best_metric, rtol=1e-4))
             repeated[i].append(best_metric)
             np.testing.assert_allclose(best_metric_epoch, 4)
             model_file = os.path.join(self.data_dir, "best_metric_model.pth")
             self.assertTrue(os.path.exists(model_file))
 
             infer_metric = run_inference_test(self.data_dir, self.test_x, self.test_y, device=self.device)
-
+            print("infer metric", infer_metric)
             # check inference properties
-            np.testing.assert_allclose(np.asarray(infer_metric), [1029, 896, 980, 1033, 961, 1046], atol=1)
+            self.assertTrue(test_integration_value(TASK, key="infer_prop", data=np.asarray(infer_metric), rtol=1))
             repeated[i].extend(infer_metric)
 
         np.testing.assert_allclose(repeated[0], repeated[1])
diff --git a/tests/test_integration_segmentation_3d.py b/tests/test_integration_segmentation_3d.py
--- a/tests/test_integration_segmentation_3d.py
+++ b/tests/test_integration_segmentation_3d.py
@@ -26,7 +26,9 @@
 from monai.metrics import DiceMetric
 from monai.networks.nets import UNet
 from monai.transforms import (
+    Activations,
     AsChannelFirstd,
+    AsDiscrete,
     Compose,
     LoadNiftid,
     RandCropByPosNegLabeld,
@@ -37,8 +39,11 @@
 )
 from monai.utils import set_determinism
 from monai.visualize import plot_2d_or_3d_image
+from tests.testing_data.integration_answers import test_integration_value
 from tests.utils import skip_if_quick
 
+TASK = "integration_segmentation_3d"
+
 
 def run_training_test(root_dir, device="cuda:0", cachedataset=False):
     monai.config.print_config()
@@ -86,7 +91,8 @@ def run_training_test(root_dir, device="cuda:0", cachedataset=False):
     # create a validation data loader
     val_ds = monai.data.Dataset(data=val_files, transform=val_transforms)
     val_loader = monai.data.DataLoader(val_ds, batch_size=1, num_workers=4)
-    dice_metric = DiceMetric(include_background=True, to_onehot_y=False, sigmoid=True, reduction="mean")
+    val_post_tran = Compose([Activations(sigmoid=True), AsDiscrete(threshold_values=True)])
+    dice_metric = DiceMetric(include_background=True, reduction="mean")
 
     # create UNet, DiceLoss and Adam optimizer
     model = monai.networks.nets.UNet(
@@ -140,11 +146,10 @@ def run_training_test(root_dir, device="cuda:0", cachedataset=False):
                 for val_data in val_loader:
                     val_images, val_labels = val_data["img"].to(device), val_data["seg"].to(device)
                     sw_batch_size, roi_size = 4, (96, 96, 96)
-                    val_outputs = sliding_window_inference(val_images, roi_size, sw_batch_size, model)
-                    value = dice_metric(y_pred=val_outputs, y=val_labels)
-                    not_nans = dice_metric.not_nans.item()
-                    metric_count += not_nans
-                    metric_sum += value.item() * not_nans
+                    val_outputs = val_post_tran(sliding_window_inference(val_images, roi_size, sw_batch_size, model))
+                    value, not_nans = dice_metric(y_pred=val_outputs, y=val_labels)
+                    metric_count += not_nans.item()
+                    metric_sum += value.item() * not_nans.item()
                 metric = metric_sum / metric_count
                 metric_values.append(metric)
                 if metric > best_metric:
@@ -186,7 +191,8 @@ def run_inference_test(root_dir, device="cuda:0"):
     val_ds = monai.data.Dataset(data=val_files, transform=val_transforms)
     # sliding window inferene need to input 1 image in every iteration
     val_loader = monai.data.DataLoader(val_ds, batch_size=1, num_workers=4)
-    dice_metric = DiceMetric(include_background=True, to_onehot_y=False, sigmoid=True, reduction="mean")
+    val_post_tran = Compose([Activations(sigmoid=True), AsDiscrete(threshold_values=True)])
+    dice_metric = DiceMetric(include_background=True, reduction="mean")
 
     model = UNet(
         dimensions=3,
@@ -210,17 +216,16 @@ def run_inference_test(root_dir, device="cuda:0"):
             val_images, val_labels = val_data["img"].to(device), val_data["seg"].to(device)
             # define sliding window size and batch size for windows inference
             sw_batch_size, roi_size = 4, (96, 96, 96)
-            val_outputs = sliding_window_inference(val_images, roi_size, sw_batch_size, model)
-            value = dice_metric(y_pred=val_outputs, y=val_labels)
-            not_nans = dice_metric.not_nans.item()
-            metric_count += not_nans
-            metric_sum += value.item() * not_nans
-            val_outputs = (val_outputs.sigmoid() >= 0.5).float()
+            val_outputs = val_post_tran(sliding_window_inference(val_images, roi_size, sw_batch_size, model))
+            value, not_nans = dice_metric(y_pred=val_outputs, y=val_labels)
+            metric_count += not_nans.item()
+            metric_sum += value.item() * not_nans.item()
             saver.save_batch(val_outputs, val_data["img_meta_dict"])
         metric = metric_sum / metric_count
     return metric
 
 
+@skip_if_quick
 class IntegrationSegmentation3D(unittest.TestCase):
     def setUp(self):
         set_determinism(seed=0)
@@ -239,7 +244,6 @@ def tearDown(self):
         set_determinism(seed=None)
         shutil.rmtree(self.data_dir)
 
-    @skip_if_quick
     def test_training(self):
         repeated = []
         for i in range(3):
@@ -251,21 +255,11 @@ def test_training(self):
             )
 
             # check training properties
-            np.testing.assert_allclose(
-                losses,
-                [
-                    0.5367561787366867,
-                    0.4780906319618225,
-                    0.45814884305000303,
-                    0.4462165802717209,
-                    0.42342003881931306,
-                    0.42573192417621614,
-                ],
-                rtol=1e-3,
-            )
+            print("losses", losses)
+            self.assertTrue(test_integration_value(TASK, key="losses", data=losses, rtol=1e-3))
             repeated[i].extend(losses)
             print("best metric", best_metric)
-            np.testing.assert_allclose(best_metric, 0.9284994244575501, rtol=1e-2)
+            self.assertTrue(test_integration_value(TASK, key="best_metric", data=best_metric, rtol=1e-2))
             repeated[i].append(best_metric)
             self.assertTrue(len(glob(os.path.join(self.data_dir, "runs"))) > 0)
             model_file = os.path.join(self.data_dir, "best_metric_model.pth")
@@ -275,56 +269,14 @@ def test_training(self):
 
             # check inference properties
             print("infer metric", infer_metric)
-            np.testing.assert_allclose(infer_metric, 0.9297081649303436, rtol=1e-2)
+            self.assertTrue(test_integration_value(TASK, key="infer_metric", data=infer_metric, rtol=1e-2))
             repeated[i].append(infer_metric)
             output_files = sorted(glob(os.path.join(self.data_dir, "output", "img*", "*.nii.gz")))
-            sums = [
-                0.14236407539774545,
-                0.15230706175082387,
-                0.15166480364209348,
-                0.14007500611778745,
-                0.1884924621086837,
-                0.1695513862739126,
-                0.14712184860663788,
-                0.1681115604336759,
-                0.1572906638415615,
-                0.17937440398539023,
-                0.16272332511753773,
-                0.16802574064747222,
-                0.14526150586106137,
-                0.1159984862360706,
-                0.16195498979241085,
-                0.20102274293763406,
-                0.17589239729105313,
-                0.10252633855115888,
-                0.193636370778981,
-                0.20292485235029903,
-                0.19634198039845435,
-                0.20824971460474945,
-                0.1622303133196011,
-                0.13221847658694733,
-                0.14885074606109583,
-                0.14270806519560253,
-                0.2314349441350736,
-                0.1611329079999522,
-                0.1484014668347457,
-                0.10382948525640207,
-                0.11913278973359338,
-                0.13073357801141586,
-                0.11465035056069182,
-                0.15304480634774378,
-                0.16318785095442723,
-                0.19371516498982155,
-                0.22269088446692173,
-                0.18099178390758894,
-                0.19004000086784215,
-                0.0855747283711328,
-            ]
             print([np.mean(nib.load(output).get_fdata()) for output in output_files])
-            for (output, s) in zip(output_files, sums):
+            for output in output_files:
                 ave = np.mean(nib.load(output).get_fdata())
-                np.testing.assert_allclose(ave, s, atol=1e-2)
                 repeated[i].append(ave)
+            self.assertTrue(test_integration_value(TASK, key="output_sums", data=repeated[i][8:], rtol=1e-2))
         np.testing.assert_allclose(repeated[0], repeated[1])
         np.testing.assert_allclose(repeated[0], repeated[2])
 
diff --git a/tests/test_integration_sliding_window.py b/tests/test_integration_sliding_window.py
--- a/tests/test_integration_sliding_window.py
+++ b/tests/test_integration_sliding_window.py
@@ -26,7 +26,7 @@
 from monai.networks.nets import UNet
 from monai.transforms import AddChannel
 from monai.utils import set_determinism
-from tests.utils import make_nifti_image
+from tests.utils import make_nifti_image, skip_if_quick
 
 
 def run_test(batch_size, img_name, seg_name, output_dir, device="cuda:0"):
@@ -59,6 +59,7 @@ def _sliding_window_processor(_engine, batch):
     return saved_name
 
 
+@skip_if_quick
 class TestIntegrationSlidingWindow(unittest.TestCase):
     def setUp(self):
         set_determinism(seed=0)
diff --git a/tests/test_integration_unet_2d.py b/tests/test_integration_unet_2d.py
--- a/tests/test_integration_unet_2d.py
+++ b/tests/test_integration_unet_2d.py
@@ -19,6 +19,7 @@
 from monai.data import create_test_image_2d
 from monai.losses import DiceLoss
 from monai.networks.nets import BasicUNet, UNet
+from tests.utils import skip_if_quick
 
 
 def run_test(net_name="basicunet", batch_size=64, train_steps=100, device="cuda:0"):
@@ -49,6 +50,7 @@ def __len__(self):
     return loss
 
 
+@skip_if_quick
 class TestIntegrationUnet2D(unittest.TestCase):
     def test_unet_training(self):
         for n in ["basicunet", "unet"]:
diff --git a/tests/test_integration_workflows.py b/tests/test_integration_workflows.py
--- a/tests/test_integration_workflows.py
+++ b/tests/test_integration_workflows.py
@@ -50,8 +50,11 @@
     ToTensord,
 )
 from monai.utils import set_determinism
+from tests.testing_data.integration_answers import test_integration_value
 from tests.utils import skip_if_quick
 
+TASK = "integration_workflows"
+
 
 def run_training_test(root_dir, device="cuda:0", amp=False):
     images = sorted(glob(os.path.join(root_dir, "img*.nii.gz")))
@@ -229,6 +232,7 @@ def run_inference_test(root_dir, model_file, device="cuda:0", amp=False):
     return evaluator.state.best_metric
 
 
+@skip_if_quick
 class IntegrationWorkflows(unittest.TestCase):
     def setUp(self):
         set_determinism(seed=0)
@@ -249,7 +253,6 @@ def tearDown(self):
         set_determinism(seed=None)
         shutil.rmtree(self.data_dir)
 
-    @skip_if_quick
     def test_training(self):
         repeated = []
         test_rounds = 3 if monai.config.get_torch_version_tuple() >= (1, 6) else 2
@@ -260,9 +263,9 @@ def test_training(self):
             best_metric = run_training_test(self.data_dir, device=self.device, amp=(i == 2))
             print("best metric", best_metric)
             if i == 2:
-                np.testing.assert_allclose(best_metric, 0.9219996750354766, rtol=1e-2)
+                self.assertTrue(test_integration_value(TASK, key="best_metric_2", data=best_metric, rtol=1e-2))
             else:
-                np.testing.assert_allclose(best_metric, 0.921965891122818, rtol=1e-2)
+                self.assertTrue(test_integration_value(TASK, key="best_metric", data=best_metric, rtol=1e-2))
             repeated[i].append(best_metric)
 
             model_file = sorted(glob(os.path.join(self.data_dir, "net_key_metric*.pt")))[-1]
@@ -270,102 +273,19 @@ def test_training(self):
             print("infer metric", infer_metric)
             # check inference properties
             if i == 2:
-                np.testing.assert_allclose(infer_metric, 0.9217855930328369, rtol=1e-2)
+                self.assertTrue(test_integration_value(TASK, key="infer_metric_2", data=infer_metric, rtol=1e-2))
             else:
-                np.testing.assert_allclose(infer_metric, 0.9217526227235794, rtol=1e-2)
+                self.assertTrue(test_integration_value(TASK, key="infer_metric", data=infer_metric, rtol=1e-2))
             repeated[i].append(infer_metric)
 
             output_files = sorted(glob(os.path.join(self.data_dir, "img*", "*.nii.gz")))
-            if i == 2:
-                sums = [
-                    0.14183807373046875,
-                    0.15151405334472656,
-                    0.13811445236206055,
-                    0.1336650848388672,
-                    0.1842341423034668,
-                    0.16353750228881836,
-                    0.14104795455932617,
-                    0.16643333435058594,
-                    0.15668964385986328,
-                    0.1764383316040039,
-                    0.16112232208251953,
-                    0.1641840934753418,
-                    0.14401578903198242,
-                    0.11075973510742188,
-                    0.16075706481933594,
-                    0.19603967666625977,
-                    0.1743607521057129,
-                    0.05361223220825195,
-                    0.19009971618652344,
-                    0.19875097274780273,
-                    0.19498729705810547,
-                    0.2027440071105957,
-                    0.16035127639770508,
-                    0.13188838958740234,
-                    0.15143728256225586,
-                    0.1370086669921875,
-                    0.22630071640014648,
-                    0.16111421585083008,
-                    0.14713764190673828,
-                    0.10443782806396484,
-                    0.11977195739746094,
-                    0.13068008422851562,
-                    0.11225223541259766,
-                    0.15175437927246094,
-                    0.1594991683959961,
-                    0.1894702911376953,
-                    0.21605825424194336,
-                    0.17748403549194336,
-                    0.18474626541137695,
-                    0.03627157211303711,
-                ]
-            else:
-                sums = [
-                    0.14183568954467773,
-                    0.15139484405517578,
-                    0.13803958892822266,
-                    0.13356733322143555,
-                    0.18455982208251953,
-                    0.16363763809204102,
-                    0.14090299606323242,
-                    0.16649341583251953,
-                    0.15651702880859375,
-                    0.17655181884765625,
-                    0.1611647605895996,
-                    0.1644759178161621,
-                    0.14383649826049805,
-                    0.11055231094360352,
-                    0.16080236434936523,
-                    0.19629907608032227,
-                    0.17441368103027344,
-                    0.053577423095703125,
-                    0.19043731689453125,
-                    0.19904851913452148,
-                    0.19525957107543945,
-                    0.20304203033447266,
-                    0.16030073165893555,
-                    0.13170528411865234,
-                    0.15118885040283203,
-                    0.13686418533325195,
-                    0.22668886184692383,
-                    0.1611466407775879,
-                    0.1472468376159668,
-                    0.10427331924438477,
-                    0.11962461471557617,
-                    0.1305699348449707,
-                    0.11204767227172852,
-                    0.15171241760253906,
-                    0.1596231460571289,
-                    0.18976259231567383,
-                    0.21649408340454102,
-                    0.17761707305908203,
-                    0.1851673126220703,
-                    0.036365509033203125,
-                ]
-            for (output, s) in zip(output_files, sums):
+            for output in output_files:
                 ave = np.mean(nib.load(output).get_fdata())
-                np.testing.assert_allclose(ave, s, rtol=1e-2)
                 repeated[i].append(ave)
+            if i == 2:
+                self.assertTrue(test_integration_value(TASK, key="output_sums_2", data=repeated[i][2:], rtol=1e-2))
+            else:
+                self.assertTrue(test_integration_value(TASK, key="output_sums", data=repeated[i][2:], rtol=1e-2))
         np.testing.assert_allclose(repeated[0], repeated[1])
 
 
diff --git a/tests/test_integration_workflows_gan.py b/tests/test_integration_workflows_gan.py
--- a/tests/test_integration_workflows_gan.py
+++ b/tests/test_integration_workflows_gan.py
@@ -126,6 +126,7 @@ def generator_loss(gen_images):
     return trainer.state
 
 
+@skip_if_quick
 class IntegrationWorkflowsGAN(unittest.TestCase):
     def setUp(self):
         set_determinism(seed=0)
@@ -144,7 +145,6 @@ def tearDown(self):
         set_determinism(seed=None)
         shutil.rmtree(self.data_dir)
 
-    @skip_if_quick
     def test_training(self):
         torch.manual_seed(0)
 
diff --git a/tests/test_parallel_execution.py b/tests/test_parallel_execution.py
--- a/tests/test_parallel_execution.py
+++ b/tests/test_parallel_execution.py
@@ -15,7 +15,7 @@
 import torch
 
 from monai.engines import create_multigpu_supervised_trainer
-from tests.utils import expect_failure_if_no_gpu
+from tests.utils import skip_if_no_cuda
 
 
 def fake_loss(y_pred, y):
@@ -32,7 +32,7 @@ class TestParallelExecution(unittest.TestCase):
     Tests single GPU, multi GPU, and CPU execution with the Ignite supervised trainer.
     """
 
-    @expect_failure_if_no_gpu
+    @skip_if_no_cuda
     def test_single_gpu(self):
         device = torch.device("cuda:0")
         net = torch.nn.Conv2d(1, 1, 3, padding=1).to(device)
@@ -40,7 +40,7 @@ def test_single_gpu(self):
         trainer = create_multigpu_supervised_trainer(net, opt, fake_loss, [device])
         trainer.run(fake_data_stream(), 2, 2)
 
-    @expect_failure_if_no_gpu
+    @skip_if_no_cuda
     def test_multi_gpu(self):
         device = torch.device("cuda")
         net = torch.nn.Conv2d(1, 1, 3, padding=1).to(device)
@@ -58,3 +58,7 @@ def test_cpu(self):
         opt = torch.optim.Adam(net.parameters(), 1e-3)
         trainer = create_multigpu_supervised_trainer(net, opt, fake_loss, [])
         trainer.run(fake_data_stream(), 2, 2)
+
+
+if __name__ == "__main__":
+    unittest.main()
diff --git a/tests/testing_data/integration_answers.py b/tests/testing_data/integration_answers.py
new file mode 100644
--- /dev/null
+++ b/tests/testing_data/integration_answers.py
@@ -0,0 +1,327 @@
+# Copyright 2020 MONAI Consortium
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#     http://www.apache.org/licenses/LICENSE-2.0
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# 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.
+
+import numpy as np
+
+EXPECTED_ANSWERS = [
+    {  # test answers for PyTorch 1.6
+        "integration_classification_2d": {
+            "losses": [0.776835828070428, 0.1615355300011149, 0.07492854832938523, 0.04591309238865877],
+            "best_metric": 0.9999184380485994,
+            "infer_prop": [1029, 896, 980, 1033, 961, 1046],
+        },
+        "integration_segmentation_3d": {
+            "losses": [
+                0.5367561340332031,
+                0.478084459900856,
+                0.4581540793180466,
+                0.44623913466930387,
+                0.42341493666172025,
+                0.42569945752620697,
+            ],
+            "best_metric": 0.9295084029436111,
+            "infer_metric": 0.9296411260962486,
+            "output_sums": [
+                0.14302121377204619,
+                0.15321686701244813,
+                0.15267064069005093,
+                0.1408481434833016,
+                0.18862719991649474,
+                0.16992848513054068,
+                0.1479306037291329,
+                0.1691071594535633,
+                0.15804366588267224,
+                0.18019304183940157,
+                0.1635089455927468,
+                0.16851606024285842,
+                0.1454348651039073,
+                0.11584957890961554,
+                0.16255468027312903,
+                0.20118089432240313,
+                0.176187783307603,
+                0.1004243279488101,
+                0.19385348502657657,
+                0.2030768555124136,
+                0.196251372926592,
+                0.20823046240222043,
+                0.1631389353339986,
+                0.13299661219478043,
+                0.14917081129077908,
+                0.14383374638201593,
+                0.23050183928776746,
+                0.1614747942341212,
+                0.14913436515470202,
+                0.10443081170610946,
+                0.11978674347415241,
+                0.13126176432899028,
+                0.11570832453348577,
+                0.15306806147195887,
+                0.163673089782912,
+                0.19394971756732426,
+                0.22197501007172804,
+                0.1812147930033603,
+                0.19051659118682873,
+                0.0774867922747158,
+            ],
+        },
+        "integration_workflows": {
+            "best_metric": 0.9219646483659745,
+            "infer_metric": 0.921751058101654,
+            "output_sums": [
+                0.14183664321899414,
+                0.1513957977294922,
+                0.13804054260253906,
+                0.13356828689575195,
+                0.18456125259399414,
+                0.16363763809204102,
+                0.14090299606323242,
+                0.16649389266967773,
+                0.15651893615722656,
+                0.17655134201049805,
+                0.16116666793823242,
+                0.1644763946533203,
+                0.14383649826049805,
+                0.11055326461791992,
+                0.16080379486083984,
+                0.19629907608032227,
+                0.17441415786743164,
+                0.053577423095703125,
+                0.19043779373168945,
+                0.19904804229736328,
+                0.19526052474975586,
+                0.20304107666015625,
+                0.16030025482177734,
+                0.13170623779296875,
+                0.15118932723999023,
+                0.13686418533325195,
+                0.22668886184692383,
+                0.1611471176147461,
+                0.1472463607788086,
+                0.10427379608154297,
+                0.11962461471557617,
+                0.1305704116821289,
+                0.11204910278320312,
+                0.15171337127685547,
+                0.15962505340576172,
+                0.18976259231567383,
+                0.21649456024169922,
+                0.17761802673339844,
+                0.18516874313354492,
+                0.03636503219604492,
+            ],
+            "best_metric_2": 0.9219559609889985,
+            "infer_metric_2": 0.9217371672391892,
+            "output_sums_2": [
+                0.14187288284301758,
+                0.15140819549560547,
+                0.13802719116210938,
+                0.1335887908935547,
+                0.18454980850219727,
+                0.1636652946472168,
+                0.14091157913208008,
+                0.16653108596801758,
+                0.15651702880859375,
+                0.17658615112304688,
+                0.1611957550048828,
+                0.16448307037353516,
+                0.14385128021240234,
+                0.1105203628540039,
+                0.16085100173950195,
+                0.19626951217651367,
+                0.17442035675048828,
+                0.053586483001708984,
+                0.19042730331420898,
+                0.1990523338317871,
+                0.1952815055847168,
+                0.20303773880004883,
+                0.16034317016601562,
+                0.13172531127929688,
+                0.15118741989135742,
+                0.1368694305419922,
+                0.22667837142944336,
+                0.16119050979614258,
+                0.14726591110229492,
+                0.10426473617553711,
+                0.11961841583251953,
+                0.13054800033569336,
+                0.11203193664550781,
+                0.15172529220581055,
+                0.15963029861450195,
+                0.18975019454956055,
+                0.21646499633789062,
+                0.17763566970825195,
+                0.18517112731933594,
+                0.03638744354248047,
+            ],
+        },
+    },
+    {  # test answers for PyTorch 1.7
+        "integration_classification_2d": {
+            "losses": [0.777176220515731, 0.16019743723664315, 0.07480076164197011, 0.045643698364780966],
+            "best_metric": 0.9999418774120775,
+            "infer_prop": [1030, 897, 980, 1033, 960, 1048],
+        },
+        "integration_segmentation_3d": {
+            "losses": [
+                0.5427072256803512,
+                0.46434969305992124,
+                0.45358552038669586,
+                0.4363856494426727,
+                0.42080804109573366,
+                0.42058534920215607,
+            ],
+            "best_metric": 0.9292903542518616,
+            "infer_metric": 0.9306288316845894,
+            "output_sums": [
+                0.14192493409895743,
+                0.15182314591386872,
+                0.15143080738742032,
+                0.13972497034181824,
+                0.18790884439406313,
+                0.16933812661492562,
+                0.14664343345928132,
+                0.1678599094806423,
+                0.1568852615222309,
+                0.17882538307200632,
+                0.16226220644853354,
+                0.16756325103417588,
+                0.1449974856885373,
+                0.1160602083671129,
+                0.1614830941632057,
+                0.20060717335382267,
+                0.17543495742507476,
+                0.10308107883493946,
+                0.19289222718691168,
+                0.20225689438356148,
+                0.19587806881756237,
+                0.20773073456322155,
+                0.16193015294299506,
+                0.13181961683097554,
+                0.14850995284454005,
+                0.14238637655756,
+                0.2307113922277095,
+                0.1608335768948913,
+                0.1480752874532259,
+                0.1038477413165911,
+                0.11880665574424197,
+                0.13084873656303445,
+                0.1141965805147642,
+                0.1531586543003841,
+                0.16275008603701097,
+                0.19320476187766733,
+                0.2217811250932611,
+                0.18027048819200148,
+                0.18958803602663193,
+                0.08653716931250294,
+            ],
+        },
+        "integration_workflows": {
+            "best_metric": 0.9217087924480438,
+            "infer_metric": 0.9214379042387009,
+            "output_sums": [
+                0.14209461212158203,
+                0.15126705169677734,
+                0.13800382614135742,
+                0.1338181495666504,
+                0.1850571632385254,
+                0.16372442245483398,
+                0.14059066772460938,
+                0.16674423217773438,
+                0.15653657913208008,
+                0.17690563201904297,
+                0.16154909133911133,
+                0.16521310806274414,
+                0.14388608932495117,
+                0.1103353500366211,
+                0.1609959602355957,
+                0.1967010498046875,
+                0.1746964454650879,
+                0.05329275131225586,
+                0.19098854064941406,
+                0.19976520538330078,
+                0.19576644897460938,
+                0.20346736907958984,
+                0.1601848602294922,
+                0.1316051483154297,
+                0.1511220932006836,
+                0.13670969009399414,
+                0.2276287078857422,
+                0.1611800193786621,
+                0.14751672744750977,
+                0.10413789749145508,
+                0.11944007873535156,
+                0.1305546760559082,
+                0.11204719543457031,
+                0.15145111083984375,
+                0.16007614135742188,
+                0.1904129981994629,
+                0.21741962432861328,
+                0.17812013626098633,
+                0.18587207794189453,
+                0.03605222702026367,
+            ],
+            "best_metric_2": 0.9210659921169281,
+            "infer_metric_2": 0.9208109736442566,
+            "output_sums_2": [
+                0.14227628707885742,
+                0.1515035629272461,
+                0.13819408416748047,
+                0.13402271270751953,
+                0.18525266647338867,
+                0.16388607025146484,
+                0.14076614379882812,
+                0.16694307327270508,
+                0.15677356719970703,
+                0.1771831512451172,
+                0.16172313690185547,
+                0.1653728485107422,
+                0.14413118362426758,
+                0.11057281494140625,
+                0.16121912002563477,
+                0.19680166244506836,
+                0.1748638153076172,
+                0.053426265716552734,
+                0.19117307662963867,
+                0.19996356964111328,
+                0.1959366798400879,
+                0.20363712310791016,
+                0.16037797927856445,
+                0.13180780410766602,
+                0.1513657569885254,
+                0.13686084747314453,
+                0.2277364730834961,
+                0.16137409210205078,
+                0.1476879119873047,
+                0.10438394546508789,
+                0.11967992782592773,
+                0.13080739974975586,
+                0.11226606369018555,
+                0.15168476104736328,
+                0.1602616310119629,
+                0.190582275390625,
+                0.21756458282470703,
+                0.17825984954833984,
+                0.18604803085327148,
+                0.036206722259521484,
+            ],
+        },
+    },
+]
+
+
+def test_integration_value(test_name, key, data, rtol=1e-2):
+    for expected in EXPECTED_ANSWERS:
+        if test_name not in expected:
+            continue
+        value = expected[test_name][key]
+        if np.allclose(data, value, rtol=rtol):
+            return True
+    return False
diff --git a/tests/utils.py b/tests/utils.py
--- a/tests/utils.py
+++ b/tests/utils.py
@@ -27,6 +27,10 @@
 
 
 def skip_if_quick(obj):
+    """
+    Skip the unit tests if environment variable `quick_test_var=true`.
+    For example, the user can skip the relevant tests by setting ``export QUICKTEST=true``.
+    """
     is_quick = os.environ.get(quick_test_var, "").lower() == "true"
 
     return unittest.skipIf(is_quick, "Skipping slow tests")(obj)
@@ -44,6 +48,13 @@ def __call__(self, obj):
         return unittest.skipIf(self.module_missing, f"optional module not present: {self.module_name}")(obj)
 
 
+def skip_if_no_cuda(obj):
+    """
+    Skip the unit tests if torch.cuda.is_available is False
+    """
+    return unittest.skipIf(not torch.cuda.is_available(), "Skipping CUDA-based tests")(obj)
+
+
 def make_nifti_image(array, affine=None):
     """
     Create a temporary nifti image on the disk and return the image name.
@@ -103,13 +114,6 @@ def setUp(self):
         self.segn = torch.tensor(self.segn)
 
 
-def expect_failure_if_no_gpu(test):
-    if not torch.cuda.is_available():
-        return unittest.expectedFailure(test)
-    else:
-        return test
-
-
 def test_script_save(net, inputs):
     scripted = torch.jit.script(net)
     buffer = scripted.save_to_buffer()