Repository: pytorch/ignite

Issue:
Remove `activation` argument from AveragePrecision and ROC_AUC
I propose to remove `activation` argument from `AveragePrecision` and `ROC_AUC`:
https://github.com/pytorch/ignite/blob/862ab0073e461a32bf5b53fa015f88c143ae9079/ignite/contrib/metrics/roc_auc.py#L23-L28
to be coherent with `Accuracy` et friends and the usage of `output_transform` to apply the binarization/activation :
https://github.com/pytorch/ignite/blob/862ab0073e461a32bf5b53fa015f88c143ae9079/ignite/metrics/accuracy.py#L81-L93



Discussion:
anmolsjoshi: @vfdev-5 please review

Patch:
diff --git a/docs/source/contrib/metrics.rst b/docs/source/contrib/metrics.rst
index 1b274568..74396e6c 100644
--- a/docs/source/contrib/metrics.rst
+++ b/docs/source/contrib/metrics.rst
@@ -7,21 +7,62 @@ Contribution module of metrics
 
 .. automodule:: ignite.contrib.metrics
    :members:
-   :undoc-members:
    :imported-members:
 
 
 Regression metrics
 ------------------
 
-Module `ignite.contrib.metrics.regression` provides implementations of
+Module :mod:`ignite.contrib.metrics.regression` provides implementations of
 metrics useful for regression tasks. Definitions of metrics are based on `Botchkarev 2018`_, page 30 "Appendix 2. Metrics mathematical definitions".
 
 .. _`Botchkarev 2018`:
         https://arxiv.org/ftp/arxiv/papers/1809/1809.03006.pdf
 
+Complete list of metrics:
 
-.. automodule:: ignite.contrib.metrics.regression
-   :members:
-   :undoc-members:
-   :imported-members:
+    - :class:`~ignite.contrib.metrics.regression.CanberraMetric`
+    - :class:`~ignite.contrib.metrics.regression.FractionalAbsoluteError`
+    - :class:`~ignite.contrib.metrics.regression.FractionalBias`
+    - :class:`~ignite.contrib.metrics.regression.GeometricMeanAbsoluteError`
+    - :class:`~ignite.contrib.metrics.regression.GeometricMeanRelativeAbsoluteError`
+    - :class:`~ignite.contrib.metrics.regression.ManhattanDistance`
+    - :class:`~ignite.contrib.metrics.regression.MaximumAbsoluteError`
+    - :class:`~ignite.contrib.metrics.regression.MeanAbsoluteRelativeError`
+    - :class:`~ignite.contrib.metrics.regression.MeanError`
+    - :class:`~ignite.contrib.metrics.regression.MeanNormalizedBias`
+    - :class:`~ignite.contrib.metrics.regression.MedianAbsoluteError`
+    - :class:`~ignite.contrib.metrics.regression.MedianAbsolutePercentageError`
+    - :class:`~ignite.contrib.metrics.regression.MedianRelativeAbsoluteError`
+    - :class:`~ignite.contrib.metrics.regression.WaveHedgesDistance`
+
+
+.. currentmodule:: ignite.contrib.metrics.regression
+
+.. autoclass:: CanberraMetric
+
+.. autoclass:: FractionalAbsoluteError
+
+.. autoclass:: FractionalBias
+
+.. autoclass:: GeometricMeanAbsoluteError
+
+.. autoclass:: GeometricMeanRelativeAbsoluteError
+
+.. autoclass:: ManhattanDistance
+
+.. autoclass:: MaximumAbsoluteError
+
+.. autoclass:: MeanAbsoluteRelativeError
+
+.. autoclass:: MeanError
+
+.. autoclass:: MeanNormalizedBias
+
+.. autoclass:: MedianAbsoluteError
+
+.. autoclass:: MedianAbsolutePercentageError
+
+.. autoclass:: MedianRelativeAbsoluteError
+
+.. autoclass:: WaveHedgesDistance
diff --git a/ignite/contrib/metrics/average_precision.py b/ignite/contrib/metrics/average_precision.py
index 03148dda..08c568e8 100644
--- a/ignite/contrib/metrics/average_precision.py
+++ b/ignite/contrib/metrics/average_precision.py
@@ -1,16 +1,13 @@
-from functools import partial
 from ignite.metrics import EpochMetric
 
 
-def average_precision_compute_fn(y_preds, y_targets, activation=None):
+def average_precision_compute_fn(y_preds, y_targets):
     try:
         from sklearn.metrics import average_precision_score
     except ImportError:
         raise RuntimeError("This contrib module requires sklearn to be installed.")
 
     y_true = y_targets.numpy()
-    if activation is not None:
-        y_preds = activation(y_preds)
     y_pred = y_preds.numpy()
     return average_precision_score(y_true, y_pred)
 
@@ -21,14 +18,23 @@ class AveragePrecision(EpochMetric):
     sklearn.metrics.average_precision_score.html#sklearn.metrics.average_precision_score>`_ .
 
     Args:
-        activation (callable, optional): optional function to apply on prediction tensors,
-            e.g. `activation=torch.sigmoid` to transform logits.
         output_transform (callable, optional): a callable that is used to transform the
             :class:`~ignite.engine.Engine`'s `process_function`'s output into the
             form expected by the metric. This can be useful if, for example, you have a multi-output model and
             you want to compute the metric with respect to one of the outputs.
 
+    AveragePrecision expects y to be comprised of 0's and 1's. y_pred must either be probability estimates or
+    confidence values. To apply an activation to y_pred, use output_transform as shown below:
+
+    .. code-block:: python
+
+        def activated_output_transform(output):
+            y_pred, y = output
+            y_pred = torch.softmax(y_pred)
+            return y_pred, y
+
+        avg_precision = AveragePrecision(activated_output_transform)
+
     """
     def __init__(self, activation=None, output_transform=lambda x: x):
-        super(AveragePrecision, self).__init__(partial(average_precision_compute_fn, activation=activation),
-                                               output_transform=output_transform)
+        super(AveragePrecision, self).__init__(average_precision_compute_fn, output_transform=output_transform)
diff --git a/ignite/contrib/metrics/regression/__init__.py b/ignite/contrib/metrics/regression/__init__.py
index 1843b096..d486f7ca 100644
--- a/ignite/contrib/metrics/regression/__init__.py
+++ b/ignite/contrib/metrics/regression/__init__.py
@@ -8,3 +8,7 @@ from ignite.contrib.metrics.regression.canberra_metric import CanberraMetric
 from ignite.contrib.metrics.regression.fractional_absolute_error import FractionalAbsoluteError
 from ignite.contrib.metrics.regression.wave_hedges_distance import WaveHedgesDistance
 from ignite.contrib.metrics.regression.geometric_mean_absolute_error import GeometricMeanAbsoluteError
+from ignite.contrib.metrics.regression.median_absolute_error import MedianAbsoluteError
+from ignite.contrib.metrics.regression.median_relative_absolute_error import MedianRelativeAbsoluteError
+from ignite.contrib.metrics.regression.median_absolute_percentage_error import MedianAbsolutePercentageError
+from ignite.contrib.metrics.regression.geometric_mean_relative_absolute_error import GeometricMeanRelativeAbsoluteError
diff --git a/ignite/contrib/metrics/regression/_base.py b/ignite/contrib/metrics/regression/_base.py
index a3172ed8..aa178e31 100644
--- a/ignite/contrib/metrics/regression/_base.py
+++ b/ignite/contrib/metrics/regression/_base.py
@@ -1,11 +1,16 @@
 from abc import abstractmethod
-from ignite.metrics import Metric
+
+import warnings
+
+import torch
+
+from ignite.metrics import Metric, EpochMetric
 
 
 class _BaseRegression(Metric):
     # Base class for all regression metrics
     # `update` method check the shapes and call internal overloaded
-    # method `_update`
+    # method `_update`.
 
     def update(self, output):
         y_pred, y = output
@@ -32,3 +37,32 @@ class _BaseRegression(Metric):
     @abstractmethod
     def _update(self, output):
         pass
+
+
+class _BaseRegressionEpoch(_BaseRegression, EpochMetric):
+    # Base class for all median-based regression metrics
+    # `update` method check the shapes and call internal overloaded method `_update`.
+    # Class internally stores complete history of predictions and targets of type float32.
+
+    def __init__(self, compute_fn, output_transform=lambda x: x):
+        EpochMetric.__init__(self, compute_fn=compute_fn, output_transform=output_transform)
+
+    def reset(self):
+        self._predictions = torch.tensor([], dtype=torch.float32)
+        self._targets = torch.tensor([], dtype=torch.float32)
+
+    def _update(self, output):
+        y_pred, y = output
+        y_pred = y_pred.type_as(self._predictions)
+        y = y.type_as(self._targets)
+
+        self._predictions = torch.cat([self._predictions, y_pred], dim=0)
+        self._targets = torch.cat([self._targets, y], dim=0)
+
+        # Check once the signature and execution of compute_fn
+        if self._predictions.shape == y_pred.shape:
+            try:
+                self.compute_fn(self._predictions, self._targets)
+            except Exception as e:
+                warnings.warn("Probably, there can be a problem with `compute_fn`:\n {}".format(e),
+                              RuntimeWarning)
diff --git a/ignite/contrib/metrics/regression/canberra_metric.py b/ignite/contrib/metrics/regression/canberra_metric.py
index fdd79921..13731b96 100644
--- a/ignite/contrib/metrics/regression/canberra_metric.py
+++ b/ignite/contrib/metrics/regression/canberra_metric.py
@@ -1,5 +1,7 @@
 from __future__ import division
+
 import torch
+
 from ignite.contrib.metrics.regression._base import _BaseRegression
 
 
@@ -7,7 +9,7 @@ class CanberraMetric(_BaseRegression):
     r"""
     Calculates the Canberra Metric.
 
-    :math:`\text{CM} = \sum _j^n\frac{|A_j - P_j|}{A_j + P_j}`
+    :math:`\text{CM} = \sum_{j=1}^n\frac{|A_j - P_j|}{A_j + P_j}`
 
     where, :math:`A_j` is the ground truth and :math:`P_j` is the predicted value.
 
diff --git a/ignite/contrib/metrics/regression/fractional_absolute_error.py b/ignite/contrib/metrics/regression/fractional_absolute_error.py
index 7f44137f..761ddf56 100644
--- a/ignite/contrib/metrics/regression/fractional_absolute_error.py
+++ b/ignite/contrib/metrics/regression/fractional_absolute_error.py
@@ -1,5 +1,7 @@
 from __future__ import division
+
 import torch
+
 from ignite.exceptions import NotComputableError
 from ignite.contrib.metrics.regression._base import _BaseRegression
 
@@ -8,7 +10,7 @@ class FractionalAbsoluteError(_BaseRegression):
     r"""
     Calculates the Fractional Absolute Error.
 
-    :math:`\text{FAE} = \frac{1}{n}\sum _j^n\frac{2 * |A_j - P_j|}{|A_j| + |P_j|}`
+    :math:`\text{FAE} = \frac{1}{n}\sum_{j=1}^n\frac{2 |A_j - P_j|}{|A_j| + |P_j|}`
 
     where, :math:`A_j` is the ground truth and :math:`P_j` is the predicted value.
 
diff --git a/ignite/contrib/metrics/regression/fractional_bias.py b/ignite/contrib/metrics/regression/fractional_bias.py
index 3523cd4a..57aa0df5 100644
--- a/ignite/contrib/metrics/regression/fractional_bias.py
+++ b/ignite/contrib/metrics/regression/fractional_bias.py
@@ -10,7 +10,7 @@ class FractionalBias(_BaseRegression):
     r"""
     Calculates the Fractional Bias:
 
-    :math:`\text{FB} = \frac{1}{n}\sum_{j=1}^n\frac{2 * (A_j - P_j)}{A_j + P_j}`,
+    :math:`\text{FB} = \frac{1}{n}\sum_{j=1}^n\frac{2 (A_j - P_j)}{A_j + P_j}`,
 
     where :math:`A_j` is the ground truth and :math:`P_j` is the predicted value.
 
diff --git a/ignite/contrib/metrics/regression/geometric_mean_absolute_error.py b/ignite/contrib/metrics/regression/geometric_mean_absolute_error.py
index c9f2f48a..29a6d79b 100644
--- a/ignite/contrib/metrics/regression/geometric_mean_absolute_error.py
+++ b/ignite/contrib/metrics/regression/geometric_mean_absolute_error.py
@@ -1,5 +1,7 @@
 from __future__ import division
+
 import torch
+
 from ignite.exceptions import NotComputableError
 from ignite.contrib.metrics.regression._base import _BaseRegression
 
@@ -8,7 +10,7 @@ class GeometricMeanAbsoluteError(_BaseRegression):
     r"""
     Calculates the Geometric Mean Absolute Error.
 
-    :math:`\text{GMAE} = exp(\frac{1}{n}\sum_{j=1}^n\ln(|A_j - P_j|)`)
+    :math:`\text{GMAE} = \exp(\frac{1}{n}\sum_{j=1}^n\ln(|A_j - P_j|)`)
 
     where, :math:`A_j` is the ground truth and :math:`P_j` is the predicted value.
 
diff --git a/ignite/contrib/metrics/regression/geometric_mean_relative_absolute_error.py b/ignite/contrib/metrics/regression/geometric_mean_relative_absolute_error.py
new file mode 100644
index 00000000..93940dd2
--- /dev/null
+++ b/ignite/contrib/metrics/regression/geometric_mean_relative_absolute_error.py
@@ -0,0 +1,43 @@
+from __future__ import division
+
+import torch
+
+from ignite.contrib.metrics.regression._base import _BaseRegression
+
+
+class GeometricMeanRelativeAbsoluteError(_BaseRegression):
+    r"""
+    Calculates the Geometric Mean Relative Absolute Error:
+
+    :math:`\text{GMRAE} = \exp(\frac{1}{n}\sum_{j=1}^n \ln\frac{|A_j - P_j|}{|A_j - \bar{A}|})`
+
+    where :math:`A_j` is the ground truth and :math:`P_j` is the predicted value.
+
+    More details can be found in `Botchkarev 2018`__.
+
+    - `update` must receive output of the form `(y_pred, y)`.
+    - `y` and `y_pred` must be of same shape `(N, )` or `(N, 1)`.
+
+
+    __ https://arxiv.org/abs/1809.03006
+
+    """
+    def reset(self):
+        self._sum_y = 0.0
+        self._num_examples = 0
+        self._sum_of_errors = 0.0
+
+    def _update(self, output):
+        y_pred, y = output
+        self._sum_y += y.sum()
+        self._num_examples += y.shape[0]
+        y_mean = self._sum_y / self._num_examples
+        numerator = torch.abs(y.view_as(y_pred) - y_pred)
+        denominator = torch.abs(y.view_as(y_pred) - y_mean)
+        self._sum_of_errors += torch.log(numerator / denominator).sum()
+
+    def compute(self):
+        if self._num_examples == 0:
+            raise NotComputableError('GeometricMeanRelativeAbsoluteError must have at least '
+                                     'one example before it can be computed.')
+        return torch.exp(torch.mean(self._sum_of_errors / self._num_examples)).item()
diff --git a/ignite/contrib/metrics/regression/median_absolute_error.py b/ignite/contrib/metrics/regression/median_absolute_error.py
new file mode 100644
index 00000000..f35f0b3d
--- /dev/null
+++ b/ignite/contrib/metrics/regression/median_absolute_error.py
@@ -0,0 +1,34 @@
+import torch
+
+from ignite.contrib.metrics.regression._base import _BaseRegressionEpoch
+
+
+def median_absolute_error_compute_fn(y_pred, y):
+    e = torch.abs(y.view_as(y_pred) - y_pred)
+    return torch.median(e).item()
+
+
+class MedianAbsoluteError(_BaseRegressionEpoch):
+    r"""
+    Calculates the Median Absolute Error:
+
+    :math:`\text{MdAE} = \text{MD}_{j=1,n} \left( |A_j - P_j| \right)`,
+
+    where :math:`A_j` is the ground truth and :math:`P_j` is the predicted value.
+
+    More details can be found in `Botchkarev 2018`__.
+
+    - `update` must receive output of the form `(y_pred, y)`.
+    - `y` and `y_pred` must be of same shape `(N, )` or `(N, 1)` and of type `float32`.
+
+    .. warning::
+
+        Current implementation stores all input data (output and target) in as tensors before computing a metric.
+        This can potentially lead to a memory error if the input data is larger than available RAM.
+
+
+    __ https://arxiv.org/abs/1809.03006
+
+    """
+    def __init__(self, output_transform=lambda x: x):
+        super(MedianAbsoluteError, self).__init__(median_absolute_error_compute_fn, output_transform)
diff --git a/ignite/contrib/metrics/regression/median_absolute_percentage_error.py b/ignite/contrib/metrics/regression/median_absolute_percentage_error.py
new file mode 100644
index 00000000..d3b9741c
--- /dev/null
+++ b/ignite/contrib/metrics/regression/median_absolute_percentage_error.py
@@ -0,0 +1,37 @@
+from __future__ import division
+
+import torch
+
+from ignite.contrib.metrics.regression._base import _BaseRegressionEpoch
+
+
+def median_absolute_percentage_error_compute_fn(y_pred, y):
+    e = torch.abs(y.view_as(y_pred) - y_pred) / torch.abs(y.view_as(y_pred))
+    return 100.0 * torch.median(e).item()
+
+
+class MedianAbsolutePercentageError(_BaseRegressionEpoch):
+    r"""
+    Calculates the Median Absolute Percentage Error:
+
+    :math:`\text{MdAPE} = 100 \cdot \text{MD}_{j=1,n} \left( \frac{|A_j - P_j|}{|A_j|} \right)`,
+
+    where :math:`A_j` is the ground truth and :math:`P_j` is the predicted value.
+
+    More details can be found in `Botchkarev 2018`__.
+
+    - `update` must receive output of the form `(y_pred, y)`.
+    - `y` and `y_pred` must be of same shape `(N, )` or `(N, 1)` and of type `float32`.
+
+    .. warning::
+
+        Current implementation stores all input data (output and target) in as tensors before computing a metric.
+        This can potentially lead to a memory error if the input data is larger than available RAM.
+
+
+    __ https://arxiv.org/abs/1809.03006
+
+    """
+    def __init__(self, output_transform=lambda x: x):
+        super(MedianAbsolutePercentageError, self).__init__(median_absolute_percentage_error_compute_fn,
+                                                            output_transform)
diff --git a/ignite/contrib/metrics/regression/median_relative_absolute_error.py b/ignite/contrib/metrics/regression/median_relative_absolute_error.py
new file mode 100644
index 00000000..32a18d53
--- /dev/null
+++ b/ignite/contrib/metrics/regression/median_relative_absolute_error.py
@@ -0,0 +1,37 @@
+from __future__ import division
+
+import torch
+
+from ignite.contrib.metrics.regression._base import _BaseRegressionEpoch
+
+
+def median_relative_absolute_error_compute_fn(y_pred, y):
+    e = torch.abs(y.view_as(y_pred) - y_pred) / torch.abs(y.view_as(y_pred) - torch.mean(y))
+    return torch.median(e).item()
+
+
+class MedianRelativeAbsoluteError(_BaseRegressionEpoch):
+    r"""
+    Calculates the Median Relative Absolute Error:
+
+    :math:`\text{MdRAE} = \text{MD}_{j=1,n} \left( \frac{|A_j - P_j|}{|A_j - \bar{A}|} \right)`,
+
+    where :math:`A_j` is the ground truth and :math:`P_j` is the predicted value.
+
+    More details can be found in `Botchkarev 2018`__.
+
+    - `update` must receive output of the form `(y_pred, y)`.
+    - `y` and `y_pred` must be of same shape `(N, )` or `(N, 1)` and of type `float32`.
+
+    .. warning::
+
+        Current implementation stores all input data (output and target) in as tensors before computing a metric.
+        This can potentially lead to a memory error if the input data is larger than available RAM.
+
+
+    __ https://arxiv.org/abs/1809.03006
+
+    """
+    def __init__(self, output_transform=lambda x: x):
+        super(MedianRelativeAbsoluteError, self).__init__(median_relative_absolute_error_compute_fn,
+                                                          output_transform)
diff --git a/ignite/contrib/metrics/regression/wave_hedges_distance.py b/ignite/contrib/metrics/regression/wave_hedges_distance.py
index 564660e1..d7cda8d9 100644
--- a/ignite/contrib/metrics/regression/wave_hedges_distance.py
+++ b/ignite/contrib/metrics/regression/wave_hedges_distance.py
@@ -1,5 +1,7 @@
 from __future__ import division
+
 import torch
+
 from ignite.contrib.metrics.regression._base import _BaseRegression
 
 
@@ -7,7 +9,7 @@ class WaveHedgesDistance(_BaseRegression):
     r"""
     Calculates the Wave Hedges Distance.
 
-    :math:`\text{WHD} = \sum _j^n\frac{|A_j - P_j|}{max(A_j, P_j)}`,
+    :math:`\text{WHD} = \sum_{j=1}^n\frac{|A_j - P_j|}{max(A_j, P_j)}`,
     where, :math:`A_j` is the ground truth and :math:`P_j` is the predicted value.
 
     More details can be found in `Botchkarev 2018`__.
diff --git a/ignite/contrib/metrics/roc_auc.py b/ignite/contrib/metrics/roc_auc.py
index ba2acc37..25e39cdd 100644
--- a/ignite/contrib/metrics/roc_auc.py
+++ b/ignite/contrib/metrics/roc_auc.py
@@ -1,16 +1,13 @@
-from functools import partial
 from ignite.metrics import EpochMetric
 
 
-def roc_auc_compute_fn(y_preds, y_targets, activation=None):
+def roc_auc_compute_fn(y_preds, y_targets):
     try:
         from sklearn.metrics import roc_auc_score
     except ImportError:
         raise RuntimeError("This contrib module requires sklearn to be installed.")
 
     y_true = y_targets.numpy()
-    if activation is not None:
-        y_preds = activation(y_preds)
     y_pred = y_preds.numpy()
     return roc_auc_score(y_true, y_pred)
 
@@ -22,14 +19,23 @@ class ROC_AUC(EpochMetric):
     sklearn.metrics.roc_auc_score.html#sklearn.metrics.roc_auc_score>`_ .
 
     Args:
-        activation (callable, optional): optional function to apply on prediction tensors,
-            e.g. `activation=torch.sigmoid` to transform logits.
         output_transform (callable, optional): a callable that is used to transform the
             :class:`~ignite.engine.Engine`'s `process_function`'s output into the
             form expected by the metric. This can be useful if, for example, you have a multi-output model and
             you want to compute the metric with respect to one of the outputs.
 
+    ROC_AUC expects y to be comprised of 0's and 1's. y_pred must either be probability estimates or confidence
+    values. To apply an activation to y_pred, use output_transform as shown below:
+
+    .. code-block:: python
+
+        def activated_output_transform(output):
+            y_pred, y = output
+            y_pred = torch.sigmoid(y_pred)
+            return y_pred, y
+
+        roc_auc = ROC_AUC(activated_output_transform)
+
     """
-    def __init__(self, activation=None, output_transform=lambda x: x):
-        super(ROC_AUC, self).__init__(partial(roc_auc_compute_fn, activation=activation),
-                                      output_transform=output_transform)
+    def __init__(self, output_transform=lambda x: x):
+        super(ROC_AUC, self).__init__(roc_auc_compute_fn, output_transform=output_transform)


Test patch:
diff --git a/tests/ignite/contrib/metrics/regression/test__base.py b/tests/ignite/contrib/metrics/regression/test__base.py
new file mode 100644
index 00000000..5a65fbca
--- /dev/null
+++ b/tests/ignite/contrib/metrics/regression/test__base.py
@@ -0,0 +1,103 @@
+import torch
+import numpy as np
+import pytest
+from ignite.contrib.metrics.regression._base import _BaseRegression, _BaseRegressionEpoch
+
+
+def test_base_regression_shapes():
+
+    class L1(_BaseRegression):
+        def reset(self):
+            self._sum_of_errors = 0.0
+
+        def _update(self, output):
+            y_pred, y = output
+            errors = torch.abs(y.view_as(y_pred) - y_pred)
+            self._sum_of_errors += torch.sum(errors).item()
+
+        def compute(self):
+            return self._sum_of_errors
+
+    m = L1()
+
+    with pytest.raises(ValueError):
+        m.update((torch.rand(4, 1, 2),
+                  torch.rand(4, 1)))
+
+    with pytest.raises(ValueError):
+        m.update((torch.rand(4, 1),
+                  torch.rand(4, 1, 2)))
+
+    with pytest.raises(ValueError):
+        m.update((torch.rand(4, 1, 2),
+                  torch.rand(4,)))
+
+    with pytest.raises(ValueError):
+        m.update((torch.rand(4,),
+                  torch.rand(4, 1, 2)))
+
+    with pytest.raises(ValueError):
+        m.update((torch.rand(4, 3),
+                  torch.rand(4, 1)))
+
+    with pytest.raises(ValueError):
+        m.update((torch.rand(4, 1),
+                  torch.rand(4, 3)))
+
+    with pytest.raises(ValueError):
+        m.update((torch.rand(4, 7),
+                  torch.rand(4,)))
+
+    with pytest.raises(ValueError):
+        m.update((torch.rand(4,),
+                  torch.rand(4, 7)))
+
+
+def test_base_regression_epoch_shapes():
+
+    def compute_fn(y_pred, y):
+        return 0.0
+
+    class ZeroEpoch(_BaseRegressionEpoch):
+        def __init__(self, output_transform=lambda x: x):
+            super(ZeroEpoch, self).__init__(compute_fn, output_transform)
+
+    m = ZeroEpoch()
+
+    with pytest.raises(ValueError):
+        m.update((torch.rand(4, 1, 2),
+                  torch.rand(4, 1)))
+
+    with pytest.raises(ValueError):
+        m.update((torch.rand(4, 1),
+                  torch.rand(4, 1, 2)))
+
+    with pytest.raises(ValueError):
+        m.update((torch.rand(4, 1, 2),
+                  torch.rand(4,)))
+
+    with pytest.raises(ValueError):
+        m.update((torch.rand(4,),
+                  torch.rand(4, 1, 2)))
+
+    with pytest.raises(ValueError):
+        m.update((torch.rand(4, 3),
+                  torch.rand(4, 1)))
+
+    with pytest.raises(ValueError):
+        m.update((torch.rand(4, 1),
+                  torch.rand(4, 3)))
+
+    with pytest.raises(ValueError):
+        m.update((torch.rand(4, 7),
+                  torch.rand(4,)))
+
+    with pytest.raises(ValueError):
+        m.update((torch.rand(4,),
+                  torch.rand(4, 7)))
+
+
+def test_base_regression_compute_fn():
+    # Wrong compute function
+    with pytest.raises(TypeError):
+        _BaseRegressionEpoch(12345)
diff --git a/tests/ignite/contrib/metrics/regression/test_geometric_mean_relative_absolute_error.py b/tests/ignite/contrib/metrics/regression/test_geometric_mean_relative_absolute_error.py
new file mode 100644
index 00000000..032cfd64
--- /dev/null
+++ b/tests/ignite/contrib/metrics/regression/test_geometric_mean_relative_absolute_error.py
@@ -0,0 +1,119 @@
+import torch
+import numpy as np
+import pytest
+from ignite.engine import Engine
+from ignite.contrib.metrics.regression import GeometricMeanRelativeAbsoluteError
+
+
+def test_wrong_input_shapes():
+    m = GeometricMeanRelativeAbsoluteError()
+
+    with pytest.raises(ValueError):
+        m.update((torch.rand(4, 1, 2),
+                  torch.rand(4, 1)))
+
+    with pytest.raises(ValueError):
+        m.update((torch.rand(4, 1),
+                  torch.rand(4, 1, 2)))
+
+    with pytest.raises(ValueError):
+        m.update((torch.rand(4, 1, 2),
+                  torch.rand(4,)))
+
+    with pytest.raises(ValueError):
+        m.update((torch.rand(4,),
+                  torch.rand(4, 1, 2)))
+
+
+def test_geometric_mean_relative_absolute_error():
+    size = 51
+    np_y_pred = np.random.rand(size,)
+    np_y = np.random.rand(size,)
+    np_gmrae = np.exp(np.log(np.abs(np_y - np_y_pred) / np.abs(np_y - np_y.mean())).mean())
+
+    m = GeometricMeanRelativeAbsoluteError()
+    y_pred = torch.from_numpy(np_y_pred)
+    y = torch.from_numpy(np_y)
+
+    m.reset()
+    m.update((y_pred, y))
+
+    assert np_gmrae == pytest.approx(m.compute())
+
+
+def test_geometric_mean_relative_absolute_error_2():
+
+    np.random.seed(1)
+    size = 105
+    np_y_pred = np.random.rand(size, 1)
+    np_y = np.random.rand(size, 1)
+    np.random.shuffle(np_y)
+
+    np_y_sum = 0
+    num_examples = 0
+    num_sum_of_errors = 0
+    np_gmrae = 0
+
+    m = GeometricMeanRelativeAbsoluteError()
+    y_pred = torch.from_numpy(np_y_pred)
+    y = torch.from_numpy(np_y)
+
+    m.reset()
+    n_iters = 15
+    batch_size = size // n_iters
+    for i in range(n_iters + 1):
+        idx = i * batch_size
+        np_y_i = np_y[idx: idx + batch_size]
+        np_y_pred_i = np_y_pred[idx: idx + batch_size]
+
+        np_y_sum += np_y_i.sum()
+        num_examples += np_y_i.shape[0]
+        np_mean = np_y_sum / num_examples
+
+        np_gmrae += np.log(np.abs(np_y_i - np_y_pred_i) / np.abs(np_y_i - np_mean)).sum()
+        m.update((y_pred[idx: idx + batch_size], y[idx: idx + batch_size]))
+
+    assert np.exp(np_gmrae / num_examples) == pytest.approx(m.compute())
+
+
+def test_integration_geometric_mean_relative_absolute_error_with_output_transform():
+
+    np.random.seed(1)
+    size = 105
+    np_y_pred = np.random.rand(size, 1)
+    np_y = np.random.rand(size, 1)
+    np.random.shuffle(np_y)
+
+    np_y_sum = 0
+    num_examples = 0
+    num_sum_of_errors = 0
+    np_gmrae = 0
+
+    n_iters = 15
+    batch_size = size // n_iters
+    for i in range(n_iters + 1):
+        idx = i * batch_size
+        np_y_i = np_y[idx: idx + batch_size]
+        np_y_pred_i = np_y_pred[idx: idx + batch_size]
+
+        np_y_sum += np_y_i.sum()
+        num_examples += np_y_i.shape[0]
+        np_mean = np_y_sum / num_examples
+
+        np_gmrae += np.log(np.abs(np_y_i - np_y_pred_i) / np.abs(np_y_i - np_mean)).sum()
+
+    def update_fn(engine, batch):
+        idx = (engine.state.iteration - 1) * batch_size
+        y_true_batch = np_y[idx:idx + batch_size]
+        y_pred_batch = np_y_pred[idx:idx + batch_size]
+        return idx, torch.from_numpy(y_pred_batch), torch.from_numpy(y_true_batch)
+
+    engine = Engine(update_fn)
+
+    m = GeometricMeanRelativeAbsoluteError(output_transform=lambda x: (x[1], x[2]))
+    m.attach(engine, 'geometric_mean_relative_absolute_error')
+
+    data = list(range(size // batch_size))
+    gmrae = engine.run(data, max_epochs=1).metrics['geometric_mean_relative_absolute_error']
+
+    assert np.exp(np_gmrae / num_examples) == pytest.approx(m.compute())
diff --git a/tests/ignite/contrib/metrics/regression/test_median_absolute_error.py b/tests/ignite/contrib/metrics/regression/test_median_absolute_error.py
new file mode 100644
index 00000000..1712adf3
--- /dev/null
+++ b/tests/ignite/contrib/metrics/regression/test_median_absolute_error.py
@@ -0,0 +1,98 @@
+import torch
+import numpy as np
+import pytest
+from ignite.engine import Engine
+from ignite.contrib.metrics.regression import MedianAbsoluteError
+
+
+def test_wrong_input_shapes():
+    m = MedianAbsoluteError()
+
+    with pytest.raises(ValueError):
+        m.update((torch.rand(4, 1, 2),
+                  torch.rand(4, 1)))
+
+    with pytest.raises(ValueError):
+        m.update((torch.rand(4, 1),
+                  torch.rand(4, 1, 2)))
+
+    with pytest.raises(ValueError):
+        m.update((torch.rand(4, 1, 2),
+                  torch.rand(4,)))
+
+    with pytest.raises(ValueError):
+        m.update((torch.rand(4,),
+                  torch.rand(4, 1, 2)))
+
+
+def test_median_absolute_error():
+
+    # See https://github.com/torch/torch7/pull/182
+    # For even number of elements, PyTorch returns middle element
+    # NumPy returns average of middle elements
+    # Size of dataset will be odd for these tests
+
+    size = 51
+    np_y_pred = np.random.rand(size,)
+    np_y = np.random.rand(size,)
+    np_median_absolute_error = np.median(np.abs(np_y - np_y_pred))
+
+    m = MedianAbsoluteError()
+    y_pred = torch.from_numpy(np_y_pred)
+    y = torch.from_numpy(np_y)
+
+    m.reset()
+    m.update((y_pred, y))
+
+    assert np_median_absolute_error == pytest.approx(m.compute())
+
+
+def test_median_absolute_error_2():
+
+    np.random.seed(1)
+    size = 105
+    np_y_pred = np.random.rand(size, 1)
+    np_y = np.random.rand(size, 1)
+    np.random.shuffle(np_y)
+    np_median_absolute_error = np.median(np.abs(np_y - np_y_pred))
+
+    m = MedianAbsoluteError()
+    y_pred = torch.from_numpy(np_y_pred)
+    y = torch.from_numpy(np_y)
+
+    m.reset()
+    batch_size = 16
+    n_iters = size // batch_size + 1
+    for i in range(n_iters):
+        idx = i * batch_size
+        m.update((y_pred[idx: idx + batch_size], y[idx: idx + batch_size]))
+
+    assert np_median_absolute_error == pytest.approx(m.compute())
+
+
+def test_integration_median_absolute_error_with_output_transform():
+
+    np.random.seed(1)
+    size = 105
+    np_y_pred = np.random.rand(size, 1)
+    np_y = np.random.rand(size, 1)
+    np.random.shuffle(np_y)
+    np_median_absolute_error = np.median(np.abs(np_y - np_y_pred))
+
+    batch_size = 15
+
+    def update_fn(engine, batch):
+        idx = (engine.state.iteration - 1) * batch_size
+        y_true_batch = np_y[idx:idx + batch_size]
+        y_pred_batch = np_y_pred[idx:idx + batch_size]
+        return idx, torch.from_numpy(y_pred_batch), torch.from_numpy(y_true_batch)
+
+    engine = Engine(update_fn)
+
+    m = MedianAbsoluteError(output_transform=lambda x: (x[1], x[2]))
+    m.attach(engine, 'median_absolute_error')
+
+    data = list(range(size // batch_size))
+    median_absolute_error = engine.run(data, max_epochs=1).metrics['median_absolute_error']
+
+    assert np_median_absolute_error == pytest.approx(median_absolute_error)
diff --git a/tests/ignite/contrib/metrics/regression/test_median_absolute_percentage_error.py b/tests/ignite/contrib/metrics/regression/test_median_absolute_percentage_error.py
new file mode 100644
index 00000000..ab40726d
--- /dev/null
+++ b/tests/ignite/contrib/metrics/regression/test_median_absolute_percentage_error.py
@@ -0,0 +1,98 @@
+import torch
+import numpy as np
+import pytest
+from ignite.engine import Engine
+from ignite.contrib.metrics.regression import MedianAbsolutePercentageError
+
+
+def test_wrong_input_shapes():
+    m = MedianAbsolutePercentageError()
+
+    with pytest.raises(ValueError):
+        m.update((torch.rand(4, 1, 2),
+                  torch.rand(4, 1)))
+
+    with pytest.raises(ValueError):
+        m.update((torch.rand(4, 1),
+                  torch.rand(4, 1, 2)))
+
+    with pytest.raises(ValueError):
+        m.update((torch.rand(4, 1, 2),
+                  torch.rand(4,)))
+
+    with pytest.raises(ValueError):
+        m.update((torch.rand(4,),
+                  torch.rand(4, 1, 2)))
+
+
+def test_median_absolute_percentage_error():
+
+    # See https://github.com/torch/torch7/pull/182
+    # For even number of elements, PyTorch returns middle element
+    # NumPy returns average of middle elements
+    # Size of dataset will be odd for these tests
+
+    size = 51
+    np_y_pred = np.random.rand(size,)
+    np_y = np.random.rand(size,)
+    np_median_absolute_percentage_error = 100.0 * np.median(np.abs(np_y - np_y_pred) / np.abs(np_y))
+
+    m = MedianAbsolutePercentageError()
+    y_pred = torch.from_numpy(np_y_pred)
+    y = torch.from_numpy(np_y)
+
+    m.reset()
+    m.update((y_pred, y))
+
+    assert np_median_absolute_percentage_error == pytest.approx(m.compute())
+
+
+def test_median_absolute_percentage_error_2():
+
+    np.random.seed(1)
+    size = 105
+    np_y_pred = np.random.rand(size, 1)
+    np_y = np.random.rand(size, 1)
+    np.random.shuffle(np_y)
+    np_median_absolute_percentage_error = 100.0 * np.median(np.abs(np_y - np_y_pred) / np.abs(np_y))
+
+    m = MedianAbsolutePercentageError()
+    y_pred = torch.from_numpy(np_y_pred)
+    y = torch.from_numpy(np_y)
+
+    m.reset()
+    batch_size = 16
+    n_iters = size // batch_size + 1
+    for i in range(n_iters):
+        idx = i * batch_size
+        m.update((y_pred[idx: idx + batch_size], y[idx: idx + batch_size]))
+
+    assert np_median_absolute_percentage_error == pytest.approx(m.compute())
+
+
+def test_integration_median_absolute_percentage_error_with_output_transform():
+
+    np.random.seed(1)
+    size = 105
+    np_y_pred = np.random.rand(size, 1)
+    np_y = np.random.rand(size, 1)
+    np.random.shuffle(np_y)
+    np_median_absolute_percentage_error = 100.0 * np.median(np.abs(np_y - np_y_pred) / np.abs(np_y))
+
+    batch_size = 15
+
+    def update_fn(engine, batch):
+        idx = (engine.state.iteration - 1) * batch_size
+        y_true_batch = np_y[idx:idx + batch_size]
+        y_pred_batch = np_y_pred[idx:idx + batch_size]
+        return idx, torch.from_numpy(y_pred_batch), torch.from_numpy(y_true_batch)
+
+    engine = Engine(update_fn)
+
+    m = MedianAbsolutePercentageError(output_transform=lambda x: (x[1], x[2]))
+    m.attach(engine, 'median_absolute_percentage_error')
+
+    data = list(range(size // batch_size))
+    median_absolute_percentage_error = engine.run(data, max_epochs=1).metrics['median_absolute_percentage_error']
+
+    assert np_median_absolute_percentage_error == pytest.approx(median_absolute_percentage_error)
diff --git a/tests/ignite/contrib/metrics/regression/test_median_relative_absolute_error.py b/tests/ignite/contrib/metrics/regression/test_median_relative_absolute_error.py
new file mode 100644
index 00000000..05399934
--- /dev/null
+++ b/tests/ignite/contrib/metrics/regression/test_median_relative_absolute_error.py
@@ -0,0 +1,98 @@
+import torch
+import numpy as np
+import pytest
+from ignite.engine import Engine
+from ignite.contrib.metrics.regression import MedianRelativeAbsoluteError
+
+
+def test_wrong_input_shapes():
+    m = MedianRelativeAbsoluteError()
+
+    with pytest.raises(ValueError):
+        m.update((torch.rand(4, 1, 2),
+                  torch.rand(4, 1)))
+
+    with pytest.raises(ValueError):
+        m.update((torch.rand(4, 1),
+                  torch.rand(4, 1, 2)))
+
+    with pytest.raises(ValueError):
+        m.update((torch.rand(4, 1, 2),
+                  torch.rand(4,)))
+
+    with pytest.raises(ValueError):
+        m.update((torch.rand(4,),
+                  torch.rand(4, 1, 2)))
+
+
+def test_median_relative_absolute_error():
+
+    # See https://github.com/torch/torch7/pull/182
+    # For even number of elements, PyTorch returns middle element
+    # NumPy returns average of middle elements
+    # Size of dataset will be odd for these tests
+
+    size = 51
+    np_y_pred = np.random.rand(size,)
+    np_y = np.random.rand(size,)
+    np_median_absolute_relative_error = np.median(np.abs(np_y - np_y_pred) / np.abs(np_y - np_y.mean()))
+
+    m = MedianRelativeAbsoluteError()
+    y_pred = torch.from_numpy(np_y_pred)
+    y = torch.from_numpy(np_y)
+
+    m.reset()
+    m.update((y_pred, y))
+
+    assert np_median_absolute_relative_error == pytest.approx(m.compute())
+
+
+def test_median_relative_absolute_error_2():
+
+    np.random.seed(1)
+    size = 105
+    np_y_pred = np.random.rand(size, 1)
+    np_y = np.random.rand(size, 1)
+    np.random.shuffle(np_y)
+    np_median_absolute_relative_error = np.median(np.abs(np_y - np_y_pred) / np.abs(np_y - np_y.mean()))
+
+    m = MedianRelativeAbsoluteError()
+    y_pred = torch.from_numpy(np_y_pred)
+    y = torch.from_numpy(np_y)
+
+    m.reset()
+    batch_size = 16
+    n_iters = size // batch_size + 1
+    for i in range(n_iters + 1):
+        idx = i * batch_size
+        m.update((y_pred[idx: idx + batch_size], y[idx: idx + batch_size]))
+
+    assert np_median_absolute_relative_error == pytest.approx(m.compute())
+
+
+def test_integration_median_relative_absolute_error_with_output_transform():
+
+    np.random.seed(1)
+    size = 105
+    np_y_pred = np.random.rand(size, 1)
+    np_y = np.random.rand(size, 1)
+    np.random.shuffle(np_y)
+    np_median_absolute_relative_error = np.median(np.abs(np_y - np_y_pred) / np.abs(np_y - np_y.mean()))
+
+    batch_size = 15
+
+    def update_fn(engine, batch):
+        idx = (engine.state.iteration - 1) * batch_size
+        y_true_batch = np_y[idx:idx + batch_size]
+        y_pred_batch = np_y_pred[idx:idx + batch_size]
+        return idx, torch.from_numpy(y_pred_batch), torch.from_numpy(y_true_batch)
+
+    engine = Engine(update_fn)
+
+    m = MedianRelativeAbsoluteError(output_transform=lambda x: (x[1], x[2]))
+    m.attach(engine, 'median_absolute_relative_error')
+
+    data = list(range(size // batch_size))
+    median_absolute_relative_error = engine.run(data, max_epochs=1).metrics['median_absolute_relative_error']
+
+    assert np_median_absolute_relative_error == pytest.approx(median_absolute_relative_error)
diff --git a/tests/ignite/contrib/metrics/test_average_precision.py b/tests/ignite/contrib/metrics/test_average_precision.py
index 71ca4af2..0e8034be 100644
--- a/tests/ignite/contrib/metrics/test_average_precision.py
+++ b/tests/ignite/contrib/metrics/test_average_precision.py
@@ -51,35 +51,47 @@ def test_ap_score_2():
     assert ap == np_ap
 
 
-def test_ap_score_with_activation():
+def test_integration_ap_score_with_output_transform():
 
+    np.random.seed(1)
     size = 100
-    np_y_pred = np.random.rand(size, 5)
-    np_y_pred_softmax = torch.softmax(torch.from_numpy(np_y_pred), dim=1).numpy()
-    np_y = np.random.randint(0, 2, size=(size, 5), dtype=np.long)
-    np_ap = average_precision_score(np_y, np_y_pred_softmax)
+    np_y_pred = np.random.rand(size, 1)
+    np_y = np.zeros((size,), dtype=np.long)
+    np_y[size // 2:] = 1
+    np.random.shuffle(np_y)
 
-    ap_metric = AveragePrecision(activation=torch.nn.Softmax(dim=1))
-    y_pred = torch.from_numpy(np_y_pred)
-    y = torch.from_numpy(np_y)
+    np_ap = average_precision_score(np_y, np_y_pred)
 
-    ap_metric.reset()
-    ap_metric.update((y_pred, y))
-    ap = ap_metric.compute()
+    batch_size = 10
+
+    def update_fn(engine, batch):
+        idx = (engine.state.iteration - 1) * batch_size
+        y_true_batch = np_y[idx:idx + batch_size]
+        y_pred_batch = np_y_pred[idx:idx + batch_size]
+        return idx, torch.from_numpy(y_pred_batch), torch.from_numpy(y_true_batch)
+
+    engine = Engine(update_fn)
+
+    ap_metric = AveragePrecision(output_transform=lambda x: (x[1], x[2]))
+    ap_metric.attach(engine, 'ap')
+
+    data = list(range(size // batch_size))
+    ap = engine.run(data, max_epochs=1).metrics['ap']
 
     assert ap == np_ap
 
 
-def test_integration_ap_score_with_output_transform():
+def test_integration_ap_score_with_activated_output_transform():
 
     np.random.seed(1)
     size = 100
     np_y_pred = np.random.rand(size, 1)
+    np_y_pred_softmax = torch.softmax(torch.from_numpy(np_y_pred), dim=1).numpy()
     np_y = np.zeros((size,), dtype=np.long)
     np_y[size // 2:] = 1
     np.random.shuffle(np_y)
 
-    np_ap = average_precision_score(np_y, np_y_pred)
+    np_ap = average_precision_score(np_y, np_y_pred_softmax)
 
     batch_size = 10
 
@@ -91,8 +103,8 @@ def test_integration_ap_score_with_output_transform():
 
     engine = Engine(update_fn)
 
-    roc_auc_metric = AveragePrecision(output_transform=lambda x: (x[1], x[2]))
-    roc_auc_metric.attach(engine, 'ap')
+    ap_metric = AveragePrecision(output_transform=lambda x: (torch.softmax(x[1], dim=1), x[2]))
+    ap_metric.attach(engine, 'ap')
 
     data = list(range(size // batch_size))
     ap = engine.run(data, max_epochs=1).metrics['ap']
diff --git a/tests/ignite/contrib/metrics/test_roc_auc.py b/tests/ignite/contrib/metrics/test_roc_auc.py
index 253f5fac..ba42a679 100644
--- a/tests/ignite/contrib/metrics/test_roc_auc.py
+++ b/tests/ignite/contrib/metrics/test_roc_auc.py
@@ -52,36 +52,47 @@ def test_roc_auc_score_2():
     assert roc_auc == np_roc_auc
 
 
-def test_roc_auc_score_with_activation():
+def test_integration_roc_auc_score_with_output_transform():
 
+    np.random.seed(1)
     size = 100
     np_y_pred = np.random.rand(size, 1)
-    np_y_pred_sigmoid = torch.sigmoid(torch.from_numpy(np_y_pred)).numpy()
     np_y = np.zeros((size,), dtype=np.long)
     np_y[size // 2:] = 1
-    np_roc_auc = roc_auc_score(np_y, np_y_pred_sigmoid)
+    np.random.shuffle(np_y)
 
-    roc_auc_metric = ROC_AUC(activation=torch.sigmoid)
-    y_pred = torch.from_numpy(np_y_pred)
-    y = torch.from_numpy(np_y)
+    np_roc_auc = roc_auc_score(np_y, np_y_pred)
 
-    roc_auc_metric.reset()
-    roc_auc_metric.update((y_pred, y))
-    roc_auc = roc_auc_metric.compute()
+    batch_size = 10
+
+    def update_fn(engine, batch):
+        idx = (engine.state.iteration - 1) * batch_size
+        y_true_batch = np_y[idx:idx + batch_size]
+        y_pred_batch = np_y_pred[idx:idx + batch_size]
+        return idx, torch.from_numpy(y_pred_batch), torch.from_numpy(y_true_batch)
+
+    engine = Engine(update_fn)
+
+    roc_auc_metric = ROC_AUC(output_transform=lambda x: (x[1], x[2]))
+    roc_auc_metric.attach(engine, 'roc_auc')
+
+    data = list(range(size // batch_size))
+    roc_auc = engine.run(data, max_epochs=1).metrics['roc_auc']
 
     assert roc_auc == np_roc_auc
 
 
-def test_integration_roc_auc_score_with_output_transform():
+def test_integration_roc_auc_score_with_activated_output_transform():
 
     np.random.seed(1)
     size = 100
     np_y_pred = np.random.rand(size, 1)
+    np_y_pred_sigmoid = torch.sigmoid(torch.from_numpy(np_y_pred)).numpy()
     np_y = np.zeros((size,), dtype=np.long)
     np_y[size // 2:] = 1
     np.random.shuffle(np_y)
 
-    np_roc_auc = roc_auc_score(np_y, np_y_pred)
+    np_roc_auc = roc_auc_score(np_y, np_y_pred_sigmoid)
 
     batch_size = 10
 
@@ -93,7 +104,7 @@ def test_integration_roc_auc_score_with_output_transform():
 
     engine = Engine(update_fn)
 
-    roc_auc_metric = ROC_AUC(output_transform=lambda x: (x[1], x[2]))
+    roc_auc_metric = ROC_AUC(output_transform=lambda x: (torch.sigmoid(x[1]), x[2]))
     roc_auc_metric.attach(engine, 'roc_auc')
 
     data = list(range(size // batch_size))