We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
[feature request]Changing how the loss metric get the shape information
Recently I was working with multi-output models and I was using a pattern that I believe to
be common. The model returns a tuple, for instance `y_pred = (ypred_1, ypred_2)` with the correspondent y of the form `y = (y_1, y_2)` with the `loss_fn` accepting as arguments `ypred` and  `y` (`loss_fn(ypred, y)`).

However I have run into problems  when using the Loss metric due to the batch size used on the update function of this metric calling the attribute shape of the, presumed, torch.Tensor directly.

I have as suggestion to change the Loss metric class to accept an extra function, for instance `batch_size = lambda x: x.shape[0]` to recover the current behavior and to permit to access the shape attribute from a tuple/list.


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `ignite/metrics/loss.py`
Content:
```
1 from __future__ import division
2 
3 from ignite.exceptions import NotComputableError
4 from ignite.metrics.metric import Metric
5 
6 
7 class Loss(Metric):
8     """
9     Calculates the average loss according to the passed loss_fn.
10 
11     Args:
12         loss_fn (callable): a callable taking a prediction tensor, a target
13             tensor, optionally other arguments, and returns the average loss
14             over all observations in the batch.
15         output_transform (callable): a callable that is used to transform the
16             :class:`ignite.engine.Engine`'s `process_function`'s output into the
17             form expected by the metric.
18             This can be useful if, for example, you have a multi-output model and
19             you want to compute the metric with respect to one of the outputs.
20             The output is is expected to be a tuple (prediction, target) or
21             (prediction, target, kwargs) where kwargs is a dictionary of extra
22             keywords arguments.
23 
24     """
25 
26     def __init__(self, loss_fn, output_transform=lambda x: x):
27         super(Loss, self).__init__(output_transform)
28         self._loss_fn = loss_fn
29 
30     def reset(self):
31         self._sum = 0
32         self._num_examples = 0
33 
34     def update(self, output):
35         if len(output) == 2:
36             y_pred, y = output
37             kwargs = {}
38         else:
39             y_pred, y, kwargs = output
40         average_loss = self._loss_fn(y_pred, y, **kwargs)
41 
42         if len(average_loss.shape) != 0:
43             raise ValueError('loss_fn did not return the average loss')
44 
45         self._sum += average_loss.item() * y.shape[0]
46         self._num_examples += y.shape[0]
47 
48     def compute(self):
49         if self._num_examples == 0:
50             raise NotComputableError(
51                 'Loss must have at least one example before it can be computed')
52         return self._sum / self._num_examples
53
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/ignite/metrics/loss.py b/ignite/metrics/loss.py
--- a/ignite/metrics/loss.py
+++ b/ignite/metrics/loss.py
@@ -20,12 +20,16 @@
             The output is is expected to be a tuple (prediction, target) or
             (prediction, target, kwargs) where kwargs is a dictionary of extra
             keywords arguments.
+        batch_size (callable): a callable taking a target tensor that returns the
+            first dimension size (usually the batch size).
 
     """
 
-    def __init__(self, loss_fn, output_transform=lambda x: x):
+    def __init__(self, loss_fn, output_transform=lambda x: x,
+                 batch_size=lambda x: x.shape[0]):
         super(Loss, self).__init__(output_transform)
         self._loss_fn = loss_fn
+        self._batch_size = batch_size
 
     def reset(self):
         self._sum = 0
@@ -42,8 +46,9 @@
         if len(average_loss.shape) != 0:
             raise ValueError('loss_fn did not return the average loss')
 
-        self._sum += average_loss.item() * y.shape[0]
-        self._num_examples += y.shape[0]
+        N = self._batch_size(y)
+        self._sum += average_loss.item() * N
+        self._num_examples += N
 
     def compute(self):
         if self._num_examples == 0:

{"golden_diff": "diff --git a/ignite/metrics/loss.py b/ignite/metrics/loss.py\n--- a/ignite/metrics/loss.py\n+++ b/ignite/metrics/loss.py\n@@ -20,12 +20,16 @@\n             The output is is expected to be a tuple (prediction, target) or\n             (prediction, target, kwargs) where kwargs is a dictionary of extra\n             keywords arguments.\n+        batch_size (callable): a callable taking a target tensor that returns the\n+            first dimension size (usually the batch size).\n \n     \"\"\"\n \n-    def __init__(self, loss_fn, output_transform=lambda x: x):\n+    def __init__(self, loss_fn, output_transform=lambda x: x,\n+                 batch_size=lambda x: x.shape[0]):\n         super(Loss, self).__init__(output_transform)\n         self._loss_fn = loss_fn\n+        self._batch_size = batch_size\n \n     def reset(self):\n         self._sum = 0\n@@ -42,8 +46,9 @@\n         if len(average_loss.shape) != 0:\n             raise ValueError('loss_fn did not return the average loss')\n \n-        self._sum += average_loss.item() * y.shape[0]\n-        self._num_examples += y.shape[0]\n+        N = self._batch_size(y)\n+        self._sum += average_loss.item() * N\n+        self._num_examples += N\n \n     def compute(self):\n         if self._num_examples == 0:\n", "issue": "[feature request]Changing how the loss metric get the shape information\nRecently I was working with multi-output models and I was using a pattern that I believe to\r\nbe common. The model returns a tuple, for instance `y_pred = (ypred_1, ypred_2)` with the correspondent y of the form `y = (y_1, y_2)` with the `loss_fn` accepting as arguments `ypred` and  `y` (`loss_fn(ypred, y)`).\r\n\r\nHowever I have run into problems  when using the Loss metric due to the batch size used on the update function of this metric calling the attribute shape of the, presumed, torch.Tensor directly.\r\n\r\nI have as suggestion to change the Loss metric class to accept an extra function, for instance `batch_size = lambda x: x.shape[0]` to recover the current behavior and to permit to access the shape attribute from a tuple/list.\r\n\n", "before_files": [{"content": "from __future__ import division\n\nfrom ignite.exceptions import NotComputableError\nfrom ignite.metrics.metric import Metric\n\n\nclass Loss(Metric):\n    \"\"\"\n    Calculates the average loss according to the passed loss_fn.\n\n    Args:\n        loss_fn (callable): a callable taking a prediction tensor, a target\n            tensor, optionally other arguments, and returns the average loss\n            over all observations in the batch.\n        output_transform (callable): a callable that is used to transform the\n            :class:`ignite.engine.Engine`'s `process_function`'s output into the\n            form expected by the metric.\n            This can be useful if, for example, you have a multi-output model and\n            you want to compute the metric with respect to one of the outputs.\n            The output is is expected to be a tuple (prediction, target) or\n            (prediction, target, kwargs) where kwargs is a dictionary of extra\n            keywords arguments.\n\n    \"\"\"\n\n    def __init__(self, loss_fn, output_transform=lambda x: x):\n        super(Loss, self).__init__(output_transform)\n        self._loss_fn = loss_fn\n\n    def reset(self):\n        self._sum = 0\n        self._num_examples = 0\n\n    def update(self, output):\n        if len(output) == 2:\n            y_pred, y = output\n            kwargs = {}\n        else:\n            y_pred, y, kwargs = output\n        average_loss = self._loss_fn(y_pred, y, **kwargs)\n\n        if len(average_loss.shape) != 0:\n            raise ValueError('loss_fn did not return the average loss')\n\n        self._sum += average_loss.item() * y.shape[0]\n        self._num_examples += y.shape[0]\n\n    def compute(self):\n        if self._num_examples == 0:\n            raise NotComputableError(\n                'Loss must have at least one example before it can be computed')\n        return self._sum / self._num_examples\n", "path": "ignite/metrics/loss.py"}], "after_files": [{"content": "from __future__ import division\n\nfrom ignite.exceptions import NotComputableError\nfrom ignite.metrics.metric import Metric\n\n\nclass Loss(Metric):\n    \"\"\"\n    Calculates the average loss according to the passed loss_fn.\n\n    Args:\n        loss_fn (callable): a callable taking a prediction tensor, a target\n            tensor, optionally other arguments, and returns the average loss\n            over all observations in the batch.\n        output_transform (callable): a callable that is used to transform the\n            :class:`ignite.engine.Engine`'s `process_function`'s output into the\n            form expected by the metric.\n            This can be useful if, for example, you have a multi-output model and\n            you want to compute the metric with respect to one of the outputs.\n            The output is is expected to be a tuple (prediction, target) or\n            (prediction, target, kwargs) where kwargs is a dictionary of extra\n            keywords arguments.\n        batch_size (callable): a callable taking a target tensor that returns the\n            first dimension size (usually the batch size).\n\n    \"\"\"\n\n    def __init__(self, loss_fn, output_transform=lambda x: x,\n                 batch_size=lambda x: x.shape[0]):\n        super(Loss, self).__init__(output_transform)\n        self._loss_fn = loss_fn\n        self._batch_size = batch_size\n\n    def reset(self):\n        self._sum = 0\n        self._num_examples = 0\n\n    def update(self, output):\n        if len(output) == 2:\n            y_pred, y = output\n            kwargs = {}\n        else:\n            y_pred, y, kwargs = output\n        average_loss = self._loss_fn(y_pred, y, **kwargs)\n\n        if len(average_loss.shape) != 0:\n            raise ValueError('loss_fn did not return the average loss')\n\n        N = self._batch_size(y)\n        self._sum += average_loss.item() * N\n        self._num_examples += N\n\n    def compute(self):\n        if self._num_examples == 0:\n            raise NotComputableError(\n                'Loss must have at least one example before it can be computed')\n        return self._sum / self._num_examples\n", "path": "ignite/metrics/loss.py"}]}