prompt: 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()
  
  ```
  
golden_diff: 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:
  
verification_info: {"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"}]}