Repository: Project-MONAI/MONAI

Issue:
'DiceCELoss' object is not callable
Hello.
It seems that there is a bug in calling a DiceCELoss object in the training loop. 
The following code will fail with this error: "TypeError: 'DiceCELoss' object is not callable"

```
loss_function = monai.losses.DiceCELoss() # before the training
# ....
loss = loss_function(outputs, labels) # in the training loop
```
I have installed MONAI from git and the same code works if I simply call DiceLoss instead of DiceCELoss.
'DiceCELoss' object is not callable
Hello.
It seems that there is a bug in calling a DiceCELoss object in the training loop. 
The following code will fail with this error: "TypeError: 'DiceCELoss' object is not callable"

```
loss_function = monai.losses.DiceCELoss() # before the training
# ....
loss = loss_function(outputs, labels) # in the training loop
```
I have installed MONAI from git and the same code works if I simply call DiceLoss instead of DiceCELoss.


Discussion:

Resolving patch:
diff --git a/monai/handlers/tensorboard_handlers.py b/monai/handlers/tensorboard_handlers.py
--- a/monai/handlers/tensorboard_handlers.py
+++ b/monai/handlers/tensorboard_handlers.py
@@ -29,7 +29,7 @@
 DEFAULT_TAG = "Loss"
 
 
-class TensorBoardStatsHandler(object):
+class TensorBoardStatsHandler:
     """
     TensorBoardStatsHandler defines a set of Ignite Event-handlers for all the TensorBoard logics.
     It's can be used for any Ignite Engine(trainer, validator and evaluator).
@@ -172,7 +172,7 @@ def _default_iteration_writer(self, engine: Engine, writer: SummaryWriter) -> No
         writer.flush()
 
 
-class TensorBoardImageHandler(object):
+class TensorBoardImageHandler:
     """
     TensorBoardImageHandler is an Ignite Event handler that can visualize images, labels and outputs as 2D/3D images.
     2D output (shape in Batch, channel, H, W) will be shown as simple image using the first element in the batch,
diff --git a/monai/losses/dice.py b/monai/losses/dice.py
--- a/monai/losses/dice.py
+++ b/monai/losses/dice.py
@@ -593,7 +593,7 @@ def _compute_alpha_generalized_true_positives(self, flat_target: torch.Tensor) -
         return alpha
 
 
-class DiceCELoss:
+class DiceCELoss(_Loss):
     """
     Compute both Dice loss and Cross Entropy Loss, and return the sum of these two losses.
     Input logits `input` (BNHW[D] where N is number of classes) is compared with ground truth `target` (BNHW[D]).
diff --git a/monai/utils/decorators.py b/monai/utils/decorators.py
--- a/monai/utils/decorators.py
+++ b/monai/utils/decorators.py
@@ -27,7 +27,7 @@ def __iter__(self):
         return self.create_gen()
 
 
-class MethodReplacer(object):
+class MethodReplacer:
     """
     Base class for method decorators which can be used to replace methods pass to replace_method() with wrapped versions.
     """


Test patch:
diff --git a/tests/test_dice_ce_loss.py b/tests/test_dice_ce_loss.py
--- a/tests/test_dice_ce_loss.py
+++ b/tests/test_dice_ce_loss.py
@@ -56,13 +56,13 @@
 class TestDiceCELoss(unittest.TestCase):
     @parameterized.expand(TEST_CASES)
     def test_result(self, input_param, input_data, expected_val):
-        result = DiceCELoss(**input_param).forward(**input_data)
+        result = DiceCELoss(**input_param)(**input_data)
         np.testing.assert_allclose(result.detach().cpu().numpy(), expected_val, atol=1e-4, rtol=1e-4)
 
     def test_ill_shape(self):
         loss = DiceCELoss()
         with self.assertRaisesRegex(ValueError, ""):
-            loss.forward(torch.ones((1, 2, 3)), torch.ones((1, 1, 2, 3)))
+            loss(torch.ones((1, 2, 3)), torch.ones((1, 1, 2, 3)))
 
 
 if __name__ == "__main__":
diff --git a/tests/test_normalize_intensity.py b/tests/test_normalize_intensity.py
--- a/tests/test_normalize_intensity.py
+++ b/tests/test_normalize_intensity.py
@@ -61,7 +61,7 @@ def test_default(self):
         normalized = normalizer(self.imt)
         self.assertTrue(normalized.dtype == np.float32)
         expected = (self.imt - np.mean(self.imt)) / np.std(self.imt)
-        np.testing.assert_allclose(normalized, expected, rtol=1e-6)
+        np.testing.assert_allclose(normalized, expected, rtol=1e-5)
 
     @parameterized.expand(TEST_CASES)
     def test_nonzero(self, input_param, input_data, expected_data):
diff --git a/tests/utils.py b/tests/utils.py
--- a/tests/utils.py
+++ b/tests/utils.py
@@ -57,7 +57,7 @@ def skip_if_quick(obj):
     return unittest.skipIf(is_quick, "Skipping slow tests")(obj)
 
 
-class SkipIfNoModule(object):
+class SkipIfNoModule:
     """Decorator to be used if test should be skipped
     when optional module is not present."""
 
@@ -69,7 +69,7 @@ def __call__(self, obj):
         return unittest.skipIf(self.module_missing, f"optional module not present: {self.module_name}")(obj)
 
 
-class SkipIfModule(object):
+class SkipIfModule:
     """Decorator to be used if test should be skipped
     when optional module is present."""
 
@@ -102,7 +102,7 @@ def skip_if_windows(obj):
     return unittest.skipIf(sys.platform == "win32", "Skipping tests on Windows")(obj)
 
 
-class SkipIfBeforePyTorchVersion(object):
+class SkipIfBeforePyTorchVersion:
     """Decorator to be used if test should be skipped
     with PyTorch versions older than that given."""
 
@@ -119,7 +119,7 @@ def __call__(self, obj):
         )(obj)
 
 
-class SkipIfAtLeastPyTorchVersion(object):
+class SkipIfAtLeastPyTorchVersion:
     """Decorator to be used if test should be skipped
     with PyTorch versions newer than that given."""