We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
utils.convert_tensor considers `device = 0` to be no device
## 🐛 Bug description
In utils.convert_tensor, this line appears:
`return tensor.to(device=device, non_blocking=non_blocking) if device else tensor`

This means that for `device = 0` (as returned from `torch.cuda.current_device`) no conversion is applied, which can be very confusing. I might add a PR for that tomorrow, unless people tell me there's a reason to leave that line as it is.

For reproduction:
```python
import torch
from ignite.utils import convert_tensor

mytens = torch.zeros(2)
device = torch.cuda.current_device()
converted_tens = convert_tensor(mytens, device)
assert converted_tens.device == device
```

## Environment

 - PyTorch Version (e.g., 1.4): 1.4
 - Ignite Version (e.g., 0.3.0): 0.3
 - OS (e.g., Linux): Windows 10
 - How you installed Ignite (`conda`, `pip`, source): conda
 - Python version: 3.7.6



--- 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/utils.py`
Content:
```
1 import collections.abc as collections
2 import logging
3 from typing import Union, Optional, Callable, Any, Type, Tuple
4 
5 import torch
6 
7 __all__ = ["convert_tensor", "apply_to_tensor", "apply_to_type", "to_onehot", "setup_logger"]
8 
9 
10 def convert_tensor(
11     input_: Union[torch.Tensor, collections.Sequence, collections.Mapping, str, bytes],
12     device: Optional[Union[str, torch.device]] = None,
13     non_blocking: bool = False,
14 ) -> Union[torch.Tensor, collections.Sequence, collections.Mapping, str, bytes]:
15     """Move tensors to relevant device."""
16 
17     def _func(tensor: torch.Tensor) -> torch.Tensor:
18         return tensor.to(device=device, non_blocking=non_blocking) if device else tensor
19 
20     return apply_to_tensor(input_, _func)
21 
22 
23 def apply_to_tensor(
24     input_: Union[torch.Tensor, collections.Sequence, collections.Mapping, str, bytes], func: Callable
25 ) -> Union[torch.Tensor, collections.Sequence, collections.Mapping, str, bytes]:
26     """Apply a function on a tensor or mapping, or sequence of tensors.
27     """
28     return apply_to_type(input_, torch.Tensor, func)
29 
30 
31 def apply_to_type(
32     input_: Union[Any, collections.Sequence, collections.Mapping, str, bytes],
33     input_type: Union[Type, Tuple[Type[Any], Any]],
34     func: Callable,
35 ) -> Union[Any, collections.Sequence, collections.Mapping, str, bytes]:
36     """Apply a function on a object of `input_type` or mapping, or sequence of objects of `input_type`.
37     """
38     if isinstance(input_, input_type):
39         return func(input_)
40     elif isinstance(input_, (str, bytes)):
41         return input_
42     elif isinstance(input_, collections.Mapping):
43         return type(input_)({k: apply_to_type(sample, input_type, func) for k, sample in input_.items()})
44     elif isinstance(input_, tuple) and hasattr(input_, "_fields"):  # namedtuple
45         return type(input_)(*(apply_to_type(sample, input_type, func) for sample in input_))
46     elif isinstance(input_, collections.Sequence):
47         return type(input_)([apply_to_type(sample, input_type, func) for sample in input_])
48     else:
49         raise TypeError(("input must contain {}, dicts or lists; found {}".format(input_type, type(input_))))
50 
51 
52 def to_onehot(indices: torch.Tensor, num_classes: int) -> torch.Tensor:
53     """Convert a tensor of indices of any shape `(N, ...)` to a
54     tensor of one-hot indicators of shape `(N, num_classes, ...) and of type uint8. Output's device is equal to the
55     input's device`.
56     """
57     onehot = torch.zeros(indices.shape[0], num_classes, *indices.shape[1:], dtype=torch.uint8, device=indices.device)
58     return onehot.scatter_(1, indices.unsqueeze(1), 1)
59 
60 
61 def setup_logger(
62     name: str,
63     level: int = logging.INFO,
64     format: str = "%(asctime)s %(name)s %(levelname)s: %(message)s",
65     filepath: Optional[str] = None,
66     distributed_rank: int = 0,
67 ) -> logging.Logger:
68     """Setups logger: name, level, format etc.
69 
70     Args:
71         name (str): new name for the logger.
72         level (int): logging level, e.g. CRITICAL, ERROR, WARNING, INFO, DEBUG
73         format (str): logging format. By default, `%(asctime)s %(name)s %(levelname)s: %(message)s`
74         filepath (str, optional): Optional logging file path. If not None, logs are written to the file.
75         distributed_rank (int, optional): Optional, rank in distributed configuration to avoid logger setup for workers.
76 
77     Returns:
78         logging.Logger
79 
80     For example, to improve logs readability when training with a trainer and evaluator:
81 
82     .. code-block:: python
83 
84         from ignite.utils import setup_logger
85 
86         trainer = ...
87         evaluator = ...
88 
89         trainer.logger = setup_logger("trainer")
90         evaluator.logger = setup_logger("evaluator")
91 
92         trainer.run(data, max_epochs=10)
93 
94         # Logs will look like
95         # 2020-01-21 12:46:07,356 trainer INFO: Engine run starting with max_epochs=5.
96         # 2020-01-21 12:46:07,358 trainer INFO: Epoch[1] Complete. Time taken: 00:5:23
97         # 2020-01-21 12:46:07,358 evaluator INFO: Engine run starting with max_epochs=1.
98         # 2020-01-21 12:46:07,358 evaluator INFO: Epoch[1] Complete. Time taken: 00:01:02
99         # ...
100 
101     """
102     logger = logging.getLogger(name)
103 
104     if distributed_rank > 0:
105         return logger
106 
107     logger.setLevel(level)
108 
109     # Remove previous handlers
110     if logger.hasHandlers():
111         for h in list(logger.handlers):
112             logger.removeHandler(h)
113 
114     formatter = logging.Formatter(format)
115 
116     ch = logging.StreamHandler()
117     ch.setLevel(level)
118     ch.setFormatter(formatter)
119     logger.addHandler(ch)
120 
121     if filepath is not None:
122         fh = logging.FileHandler(filepath)
123         fh.setLevel(level)
124         fh.setFormatter(formatter)
125         logger.addHandler(fh)
126 
127     return logger
128
```
--- 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/utils.py b/ignite/utils.py
--- a/ignite/utils.py
+++ b/ignite/utils.py
@@ -15,7 +15,7 @@
     """Move tensors to relevant device."""
 
     def _func(tensor: torch.Tensor) -> torch.Tensor:
-        return tensor.to(device=device, non_blocking=non_blocking) if device else tensor
+        return tensor.to(device=device, non_blocking=non_blocking) if device is not None else tensor
 
     return apply_to_tensor(input_, _func)

{"golden_diff": "diff --git a/ignite/utils.py b/ignite/utils.py\n--- a/ignite/utils.py\n+++ b/ignite/utils.py\n@@ -15,7 +15,7 @@\n     \"\"\"Move tensors to relevant device.\"\"\"\n \n     def _func(tensor: torch.Tensor) -> torch.Tensor:\n-        return tensor.to(device=device, non_blocking=non_blocking) if device else tensor\n+        return tensor.to(device=device, non_blocking=non_blocking) if device is not None else tensor\n \n     return apply_to_tensor(input_, _func)\n", "issue": "utils.convert_tensor considers `device = 0` to be no device\n## \ud83d\udc1b Bug description\r\nIn utils.convert_tensor, this line appears:\r\n`return tensor.to(device=device, non_blocking=non_blocking) if device else tensor`\r\n\r\nThis means that for `device = 0` (as returned from `torch.cuda.current_device`) no conversion is applied, which can be very confusing. I might add a PR for that tomorrow, unless people tell me there's a reason to leave that line as it is.\r\n\r\nFor reproduction:\r\n```python\r\nimport torch\r\nfrom ignite.utils import convert_tensor\r\n\r\nmytens = torch.zeros(2)\r\ndevice = torch.cuda.current_device()\r\nconverted_tens = convert_tensor(mytens, device)\r\nassert converted_tens.device == device\r\n```\r\n\r\n## Environment\r\n\r\n - PyTorch Version (e.g., 1.4): 1.4\r\n - Ignite Version (e.g., 0.3.0): 0.3\r\n - OS (e.g., Linux): Windows 10\r\n - How you installed Ignite (`conda`, `pip`, source): conda\r\n - Python version: 3.7.6\r\n\r\n\n", "before_files": [{"content": "import collections.abc as collections\nimport logging\nfrom typing import Union, Optional, Callable, Any, Type, Tuple\n\nimport torch\n\n__all__ = [\"convert_tensor\", \"apply_to_tensor\", \"apply_to_type\", \"to_onehot\", \"setup_logger\"]\n\n\ndef convert_tensor(\n    input_: Union[torch.Tensor, collections.Sequence, collections.Mapping, str, bytes],\n    device: Optional[Union[str, torch.device]] = None,\n    non_blocking: bool = False,\n) -> Union[torch.Tensor, collections.Sequence, collections.Mapping, str, bytes]:\n    \"\"\"Move tensors to relevant device.\"\"\"\n\n    def _func(tensor: torch.Tensor) -> torch.Tensor:\n        return tensor.to(device=device, non_blocking=non_blocking) if device else tensor\n\n    return apply_to_tensor(input_, _func)\n\n\ndef apply_to_tensor(\n    input_: Union[torch.Tensor, collections.Sequence, collections.Mapping, str, bytes], func: Callable\n) -> Union[torch.Tensor, collections.Sequence, collections.Mapping, str, bytes]:\n    \"\"\"Apply a function on a tensor or mapping, or sequence of tensors.\n    \"\"\"\n    return apply_to_type(input_, torch.Tensor, func)\n\n\ndef apply_to_type(\n    input_: Union[Any, collections.Sequence, collections.Mapping, str, bytes],\n    input_type: Union[Type, Tuple[Type[Any], Any]],\n    func: Callable,\n) -> Union[Any, collections.Sequence, collections.Mapping, str, bytes]:\n    \"\"\"Apply a function on a object of `input_type` or mapping, or sequence of objects of `input_type`.\n    \"\"\"\n    if isinstance(input_, input_type):\n        return func(input_)\n    elif isinstance(input_, (str, bytes)):\n        return input_\n    elif isinstance(input_, collections.Mapping):\n        return type(input_)({k: apply_to_type(sample, input_type, func) for k, sample in input_.items()})\n    elif isinstance(input_, tuple) and hasattr(input_, \"_fields\"):  # namedtuple\n        return type(input_)(*(apply_to_type(sample, input_type, func) for sample in input_))\n    elif isinstance(input_, collections.Sequence):\n        return type(input_)([apply_to_type(sample, input_type, func) for sample in input_])\n    else:\n        raise TypeError((\"input must contain {}, dicts or lists; found {}\".format(input_type, type(input_))))\n\n\ndef to_onehot(indices: torch.Tensor, num_classes: int) -> torch.Tensor:\n    \"\"\"Convert a tensor of indices of any shape `(N, ...)` to a\n    tensor of one-hot indicators of shape `(N, num_classes, ...) and of type uint8. Output's device is equal to the\n    input's device`.\n    \"\"\"\n    onehot = torch.zeros(indices.shape[0], num_classes, *indices.shape[1:], dtype=torch.uint8, device=indices.device)\n    return onehot.scatter_(1, indices.unsqueeze(1), 1)\n\n\ndef setup_logger(\n    name: str,\n    level: int = logging.INFO,\n    format: str = \"%(asctime)s %(name)s %(levelname)s: %(message)s\",\n    filepath: Optional[str] = None,\n    distributed_rank: int = 0,\n) -> logging.Logger:\n    \"\"\"Setups logger: name, level, format etc.\n\n    Args:\n        name (str): new name for the logger.\n        level (int): logging level, e.g. CRITICAL, ERROR, WARNING, INFO, DEBUG\n        format (str): logging format. By default, `%(asctime)s %(name)s %(levelname)s: %(message)s`\n        filepath (str, optional): Optional logging file path. If not None, logs are written to the file.\n        distributed_rank (int, optional): Optional, rank in distributed configuration to avoid logger setup for workers.\n\n    Returns:\n        logging.Logger\n\n    For example, to improve logs readability when training with a trainer and evaluator:\n\n    .. code-block:: python\n\n        from ignite.utils import setup_logger\n\n        trainer = ...\n        evaluator = ...\n\n        trainer.logger = setup_logger(\"trainer\")\n        evaluator.logger = setup_logger(\"evaluator\")\n\n        trainer.run(data, max_epochs=10)\n\n        # Logs will look like\n        # 2020-01-21 12:46:07,356 trainer INFO: Engine run starting with max_epochs=5.\n        # 2020-01-21 12:46:07,358 trainer INFO: Epoch[1] Complete. Time taken: 00:5:23\n        # 2020-01-21 12:46:07,358 evaluator INFO: Engine run starting with max_epochs=1.\n        # 2020-01-21 12:46:07,358 evaluator INFO: Epoch[1] Complete. Time taken: 00:01:02\n        # ...\n\n    \"\"\"\n    logger = logging.getLogger(name)\n\n    if distributed_rank > 0:\n        return logger\n\n    logger.setLevel(level)\n\n    # Remove previous handlers\n    if logger.hasHandlers():\n        for h in list(logger.handlers):\n            logger.removeHandler(h)\n\n    formatter = logging.Formatter(format)\n\n    ch = logging.StreamHandler()\n    ch.setLevel(level)\n    ch.setFormatter(formatter)\n    logger.addHandler(ch)\n\n    if filepath is not None:\n        fh = logging.FileHandler(filepath)\n        fh.setLevel(level)\n        fh.setFormatter(formatter)\n        logger.addHandler(fh)\n\n    return logger\n", "path": "ignite/utils.py"}], "after_files": [{"content": "import collections.abc as collections\nimport logging\nfrom typing import Union, Optional, Callable, Any, Type, Tuple\n\nimport torch\n\n__all__ = [\"convert_tensor\", \"apply_to_tensor\", \"apply_to_type\", \"to_onehot\", \"setup_logger\"]\n\n\ndef convert_tensor(\n    input_: Union[torch.Tensor, collections.Sequence, collections.Mapping, str, bytes],\n    device: Optional[Union[str, torch.device]] = None,\n    non_blocking: bool = False,\n) -> Union[torch.Tensor, collections.Sequence, collections.Mapping, str, bytes]:\n    \"\"\"Move tensors to relevant device.\"\"\"\n\n    def _func(tensor: torch.Tensor) -> torch.Tensor:\n        return tensor.to(device=device, non_blocking=non_blocking) if device is not None else tensor\n\n    return apply_to_tensor(input_, _func)\n\n\ndef apply_to_tensor(\n    input_: Union[torch.Tensor, collections.Sequence, collections.Mapping, str, bytes], func: Callable\n) -> Union[torch.Tensor, collections.Sequence, collections.Mapping, str, bytes]:\n    \"\"\"Apply a function on a tensor or mapping, or sequence of tensors.\n    \"\"\"\n    return apply_to_type(input_, torch.Tensor, func)\n\n\ndef apply_to_type(\n    input_: Union[Any, collections.Sequence, collections.Mapping, str, bytes],\n    input_type: Union[Type, Tuple[Type[Any], Any]],\n    func: Callable,\n) -> Union[Any, collections.Sequence, collections.Mapping, str, bytes]:\n    \"\"\"Apply a function on a object of `input_type` or mapping, or sequence of objects of `input_type`.\n    \"\"\"\n    if isinstance(input_, input_type):\n        return func(input_)\n    elif isinstance(input_, (str, bytes)):\n        return input_\n    elif isinstance(input_, collections.Mapping):\n        return type(input_)({k: apply_to_type(sample, input_type, func) for k, sample in input_.items()})\n    elif isinstance(input_, tuple) and hasattr(input_, \"_fields\"):  # namedtuple\n        return type(input_)(*(apply_to_type(sample, input_type, func) for sample in input_))\n    elif isinstance(input_, collections.Sequence):\n        return type(input_)([apply_to_type(sample, input_type, func) for sample in input_])\n    else:\n        raise TypeError((\"input must contain {}, dicts or lists; found {}\".format(input_type, type(input_))))\n\n\ndef to_onehot(indices: torch.Tensor, num_classes: int) -> torch.Tensor:\n    \"\"\"Convert a tensor of indices of any shape `(N, ...)` to a\n    tensor of one-hot indicators of shape `(N, num_classes, ...) and of type uint8. Output's device is equal to the\n    input's device`.\n    \"\"\"\n    onehot = torch.zeros(indices.shape[0], num_classes, *indices.shape[1:], dtype=torch.uint8, device=indices.device)\n    return onehot.scatter_(1, indices.unsqueeze(1), 1)\n\n\ndef setup_logger(\n    name: str,\n    level: int = logging.INFO,\n    format: str = \"%(asctime)s %(name)s %(levelname)s: %(message)s\",\n    filepath: Optional[str] = None,\n    distributed_rank: int = 0,\n) -> logging.Logger:\n    \"\"\"Setups logger: name, level, format etc.\n\n    Args:\n        name (str): new name for the logger.\n        level (int): logging level, e.g. CRITICAL, ERROR, WARNING, INFO, DEBUG\n        format (str): logging format. By default, `%(asctime)s %(name)s %(levelname)s: %(message)s`\n        filepath (str, optional): Optional logging file path. If not None, logs are written to the file.\n        distributed_rank (int, optional): Optional, rank in distributed configuration to avoid logger setup for workers.\n\n    Returns:\n        logging.Logger\n\n    For example, to improve logs readability when training with a trainer and evaluator:\n\n    .. code-block:: python\n\n        from ignite.utils import setup_logger\n\n        trainer = ...\n        evaluator = ...\n\n        trainer.logger = setup_logger(\"trainer\")\n        evaluator.logger = setup_logger(\"evaluator\")\n\n        trainer.run(data, max_epochs=10)\n\n        # Logs will look like\n        # 2020-01-21 12:46:07,356 trainer INFO: Engine run starting with max_epochs=5.\n        # 2020-01-21 12:46:07,358 trainer INFO: Epoch[1] Complete. Time taken: 00:5:23\n        # 2020-01-21 12:46:07,358 evaluator INFO: Engine run starting with max_epochs=1.\n        # 2020-01-21 12:46:07,358 evaluator INFO: Epoch[1] Complete. Time taken: 00:01:02\n        # ...\n\n    \"\"\"\n    logger = logging.getLogger(name)\n\n    if distributed_rank > 0:\n        return logger\n\n    logger.setLevel(level)\n\n    # Remove previous handlers\n    if logger.hasHandlers():\n        for h in list(logger.handlers):\n            logger.removeHandler(h)\n\n    formatter = logging.Formatter(format)\n\n    ch = logging.StreamHandler()\n    ch.setLevel(level)\n    ch.setFormatter(formatter)\n    logger.addHandler(ch)\n\n    if filepath is not None:\n        fh = logging.FileHandler(filepath)\n        fh.setLevel(level)\n        fh.setFormatter(formatter)\n        logger.addHandler(fh)\n\n    return logger\n", "path": "ignite/utils.py"}]}