We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Tensorboard logger error: lightning_logs directory not exists in multi-node DDP on nodes with rank != 0
## 🐛 Bug

In multi-node DDP train mode on all nodes except rank 0 errors appears at the start of the training caused by accessing lightning_logs directory in tensorboard logger which is not exist at the moment.

### To Reproduce

Steps to reproduce the behavior:

1. setup multi-node cluster (without SLURM)
2. set environment variables on each node:
```
export MASTER_ADDR=<rank 0 node IP>
export MASTER_PORT=23456
export RANK=<node id>
export SLURM_NODEID=<node id>
export WORLD_SIZE=<world-size>
```
3. install dependencies:
```
pip install torch torchvision hydra-core pytorch-lightning
```
4. copy app.y and conf.yaml to each node
5. run script on each node
```
python app.py
```
6. see the error:
```
Exception:

-- Process 0 terminated with the following error:
Traceback (most recent call last):
  File "/home/ubuntu/anaconda3/envs/nightly_pt/lib/python3.6/site-packages/torch/multiprocessing/spawn.py", line 19, in _wrap
    fn(i, *args)
  File "/home/ubuntu/anaconda3/envs/nightly_pt/lib/python3.6/site-packages/pytorch_lightning/trainer/distrib_data_parallel.py", line 342, in ddp_train
    self.run_pretrain_routine(model)
  File "/home/ubuntu/anaconda3/envs/nightly_pt/lib/python3.6/site-packages/pytorch_lightning/trainer/trainer.py", line 777, in run_pretrain_routine
    self.configure_checkpoint_callback()
  File "/home/ubuntu/anaconda3/envs/nightly_pt/lib/python3.6/site-packages/pytorch_lightning/trainer/callback_config.py", line 45, in configure_checkpoint_callback
    f'version_{self.logger.version}',
  File "/home/ubuntu/anaconda3/envs/nightly_pt/lib/python3.6/site-packages/pytorch_lightning/loggers/tensorboard.py", line 161, in version
    self._version = self._get_next_version()
  File "/home/ubuntu/anaconda3/envs/nightly_pt/lib/python3.6/site-packages/pytorch_lightning/loggers/tensorboard.py", line 167, in _get_next_version
    for d in os.listdir(root_dir):
FileNotFoundError: [Errno 2] No such file or directory: '/home/ubuntu/pytorch-lightning-intro-guide/outputs/2020-04-04/15-53-26/lightning_logs'
```

#### Code sample

app.py:
```
import pathlib

import hydra
import pytorch_lightning as pl
import torch
from omegaconf import OmegaConf
from torch.nn import functional as F
from torch.optim import Adam
from torch.utils.data import DataLoader, random_split
from torchvision import datasets, transforms


class LitMNIST(pl.LightningModule):
    def __init__(self):
        super().__init__()
        self.layer_1 = torch.nn.Linear(28 * 28, 128)
        self.layer_2 = torch.nn.Linear(128, 256)
        self.layer_3 = torch.nn.Linear(256, 10)

        self.train_dataset = None
        self.val_dataset = None
        self.test_dataset = None

    def forward(self, x):
        batch_size, channels, width, height = x.size()
        x = x.view(batch_size, -1)
        x = self.layer_1(x)
        x = F.relu(x)
        x = self.layer_2(x)
        x = F.relu(x)
        x = self.layer_3(x)
        x = F.log_softmax(x, dim=1)
        return x

    def prepare_data(self):
        # transform
        transform = transforms.Compose(
            [transforms.ToTensor(), transforms.Normalize((0.1307,), (0.3081,))])

        # download
        data_dir = pathlib.Path.home() / 'data'
        mnist_train = datasets.MNIST(data_dir, train=True,
                                     download=True, transform=transform)
        mnist_test = datasets.MNIST(data_dir, train=False,
                                    download=True, transform=transform)

        # train/val split
        mnist_train, mnist_val = random_split(mnist_train, [55000, 5000])

        # assign to use in dataloaders
        self.train_dataset = mnist_train
        self.val_dataset = mnist_val
        self.test_dataset = mnist_test

    def train_dataloader(self):
        return DataLoader(self.train_dataset, batch_size=64)

    def val_dataloader(self):
        return DataLoader(self.val_dataset, batch_size=64)

    def test_dataloader(self):
        return DataLoader(self.test_dataset, batch_size=64)

    def configure_optimizers(self):
        return Adam(self.parameters(), lr=1e-3)

    def training_step(self, batch, batch_idx):
        x, y = batch
        logits = self(x)
        loss = F.nll_loss(logits, y)

        # add logging
        logs = {'loss': loss}
        return {'loss': loss, 'log': logs}

    def validation_step(self, batch, batch_idx):
        x, y = batch
        logits = self(x)
        loss = F.nll_loss(logits, y)
        return {'val_loss': loss}

    def validation_epoch_end(self, outputs):
        avg_loss = torch.stack(  # pylint: disable=no-member
            [x['val_loss'] for x in outputs]).mean()
        tensorboard_logs = {'val_loss': avg_loss}
        return {'avg_val_loss': avg_loss, 'log': tensorboard_logs}

    def test_step(self, batch, batch_idx):
        x, y = batch
        logits = self(x)
        loss = F.nll_loss(logits, y)
        return {'val_loss': loss}

    def test_epoch_end(self, outputs):
        avg_loss = torch.stack(  # pylint: disable=no-member
            [x['val_loss'] for x in outputs]).mean()
        tensorboard_logs = {'val_loss': avg_loss}
        return {'avg_val_loss': avg_loss, 'log': tensorboard_logs}

    def init_ddp_connection(self, proc_rank: int, world_size: int) -> None:
        torch.distributed.init_process_group(
            'nccl', rank=proc_rank, world_size=world_size)


@hydra.main(config_path='conf.yaml')
def main(conf: OmegaConf):
    model = LitMNIST()

    trainer = pl.Trainer(gpus=conf.gpus,
                         num_nodes=conf.num_nodes,
                         distributed_backend=conf.distributed_backend,
                         max_epochs=3)
    trainer.fit(model)


if __name__ == '__main__':
    main()  # pylint: disable=no-value-for-parameter
```

conf.yaml:
```
gpus: 1
num_nodes: 2
distributed_backend: ddp
```

### Expected behavior

Train should go without error

### Environment
```
cuda:
	GPU:
		Tesla K80
		Tesla K80
		Tesla K80
		Tesla K80
		Tesla K80
		Tesla K80
		Tesla K80
		Tesla K80
	available:           True
	version:             10.1
packages:
	numpy:               1.18.1
	pyTorch_debug:       False
	pyTorch_version:     1.4.0
	pytorch-lightning:   0.7.1
	tensorboard:         2.2.0
	tqdm:                4.45.0
system:
	OS:                  Linux
	architecture:
		64bit

	processor:           x86_64
	python:              3.6.10
	version:             #113-Ubuntu SMP Wed Jan 29 14:54:54 UTC 2020
```
### Additional context

<!-- Add any other context about the problem here. -->


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `pytorch_lightning/loggers/tensorboard.py`
Content:
```
1 import csv
2 import os
3 from argparse import Namespace
4 from typing import Optional, Dict, Union, Any
5 from warnings import warn
6 
7 import torch
8 from pkg_resources import parse_version
9 from torch.utils.tensorboard import SummaryWriter
10 
11 from pytorch_lightning.loggers.base import LightningLoggerBase, rank_zero_only
12 
13 
14 class TensorBoardLogger(LightningLoggerBase):
15     r"""
16 
17     Log to local file system in TensorBoard format
18 
19     Implemented using :class:`torch.utils.tensorboard.SummaryWriter`. Logs are saved to
20     ``os.path.join(save_dir, name, version)``
21 
22     Example:
23         .. code-block:: python
24 
25             logger = TensorBoardLogger("tb_logs", name="my_model")
26             trainer = Trainer(logger=logger)
27             trainer.train(model)
28 
29     Args:
30         save_dir: Save directory
31         name: Experiment name. Defaults to "default".  If it is the empty string then no per-experiment
32             subdirectory is used.
33         version: Experiment version. If version is not specified the logger inspects the save
34             directory for existing versions, then automatically assigns the next available version.
35             If it is a string then it is used as the run-specific subdirectory name,
36             otherwise version_${version} is used.
37         \**kwargs: Other arguments are passed directly to the :class:`SummaryWriter` constructor.
38 
39     """
40     NAME_CSV_TAGS = 'meta_tags.csv'
41 
42     def __init__(
43             self, save_dir: str, name: Optional[str] = "default",
44             version: Optional[Union[int, str]] = None, **kwargs
45     ):
46         super().__init__()
47         self.save_dir = save_dir
48         self._name = name
49         self._version = version
50 
51         self._experiment = None
52         self.tags = {}
53         self.kwargs = kwargs
54 
55     @property
56     def root_dir(self) -> str:
57         """
58         Parent directory for all tensorboard checkpoint subdirectories.
59         If the experiment name parameter is None or the empty string, no experiment subdirectory is used
60         and checkpoint will be saved in save_dir/version_dir
61         """
62         if self.name is None or len(self.name) == 0:
63             return self.save_dir
64         else:
65             return os.path.join(self.save_dir, self.name)
66 
67     @property
68     def log_dir(self) -> str:
69         """
70         The directory for this run's tensorboard checkpoint.  By default, it is named 'version_${self.version}'
71         but it can be overridden by passing a string value for the constructor's version parameter
72         instead of None or an int
73         """
74         # create a pseudo standard path ala test-tube
75         version = self.version if isinstance(self.version, str) else f"version_{self.version}"
76         log_dir = os.path.join(self.root_dir, version)
77         return log_dir
78 
79     @property
80     def experiment(self) -> SummaryWriter:
81         r"""
82 
83          Actual tensorboard object. To use tensorboard features do the following.
84 
85          Example::
86 
87              self.logger.experiment.some_tensorboard_function()
88 
89          """
90         if self._experiment is not None:
91             return self._experiment
92 
93         os.makedirs(self.root_dir, exist_ok=True)
94         self._experiment = SummaryWriter(log_dir=self.log_dir, **self.kwargs)
95         return self._experiment
96 
97     @rank_zero_only
98     def log_hyperparams(self, params: Union[Dict[str, Any], Namespace]) -> None:
99         params = self._convert_params(params)
100         params = self._flatten_dict(params)
101         sanitized_params = self._sanitize_params(params)
102 
103         if parse_version(torch.__version__) < parse_version("1.3.0"):
104             warn(
105                 f"Hyperparameter logging is not available for Torch version {torch.__version__}."
106                 " Skipping log_hyperparams. Upgrade to Torch 1.3.0 or above to enable"
107                 " hyperparameter logging."
108             )
109         else:
110             from torch.utils.tensorboard.summary import hparams
111             exp, ssi, sei = hparams(sanitized_params, {})
112             writer = self.experiment._get_file_writer()
113             writer.add_summary(exp)
114             writer.add_summary(ssi)
115             writer.add_summary(sei)
116 
117         # some alternative should be added
118         self.tags.update(sanitized_params)
119 
120     @rank_zero_only
121     def log_metrics(self, metrics: Dict[str, float], step: Optional[int] = None) -> None:
122         for k, v in metrics.items():
123             if isinstance(v, torch.Tensor):
124                 v = v.item()
125             self.experiment.add_scalar(k, v, step)
126 
127     @rank_zero_only
128     def save(self) -> None:
129         try:
130             self.experiment.flush()
131         except AttributeError:
132             # you are using PT version (<v1.2) which does not have implemented flush
133             self.experiment._get_file_writer().flush()
134 
135         dir_path = self.log_dir
136         if not os.path.isdir(dir_path):
137             dir_path = self.save_dir
138 
139         # prepare the file path
140         meta_tags_path = os.path.join(dir_path, self.NAME_CSV_TAGS)
141 
142         # save the metatags file
143         with open(meta_tags_path, 'w', newline='') as csvfile:
144             fieldnames = ['key', 'value']
145             writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
146             writer.writerow({'key': 'key', 'value': 'value'})
147             for k, v in self.tags.items():
148                 writer.writerow({'key': k, 'value': v})
149 
150     @rank_zero_only
151     def finalize(self, status: str) -> None:
152         self.save()
153 
154     @property
155     def name(self) -> str:
156         return self._name
157 
158     @property
159     def version(self) -> int:
160         if self._version is None:
161             self._version = self._get_next_version()
162         return self._version
163 
164     def _get_next_version(self):
165         root_dir = os.path.join(self.save_dir, self.name)
166         existing_versions = []
167         for d in os.listdir(root_dir):
168             if os.path.isdir(os.path.join(root_dir, d)) and d.startswith("version_"):
169                 existing_versions.append(int(d.split("_")[1]))
170 
171         if len(existing_versions) == 0:
172             return 0
173 
174         return max(existing_versions) + 1
175
```
--- 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/pytorch_lightning/loggers/tensorboard.py b/pytorch_lightning/loggers/tensorboard.py
--- a/pytorch_lightning/loggers/tensorboard.py
+++ b/pytorch_lightning/loggers/tensorboard.py
@@ -9,6 +9,7 @@
 from torch.utils.tensorboard import SummaryWriter
 
 from pytorch_lightning.loggers.base import LightningLoggerBase, rank_zero_only
+from pytorch_lightning import _logger as log
 
 
 class TensorBoardLogger(LightningLoggerBase):
@@ -163,6 +164,11 @@
 
     def _get_next_version(self):
         root_dir = os.path.join(self.save_dir, self.name)
+
+        if not os.path.isdir(root_dir):
+            log.warning('Missing logger folder: %s', root_dir)
+            return 0
+
         existing_versions = []
         for d in os.listdir(root_dir):
             if os.path.isdir(os.path.join(root_dir, d)) and d.startswith("version_"):

{"golden_diff": "diff --git a/pytorch_lightning/loggers/tensorboard.py b/pytorch_lightning/loggers/tensorboard.py\n--- a/pytorch_lightning/loggers/tensorboard.py\n+++ b/pytorch_lightning/loggers/tensorboard.py\n@@ -9,6 +9,7 @@\n from torch.utils.tensorboard import SummaryWriter\n \n from pytorch_lightning.loggers.base import LightningLoggerBase, rank_zero_only\n+from pytorch_lightning import _logger as log\n \n \n class TensorBoardLogger(LightningLoggerBase):\n@@ -163,6 +164,11 @@\n \n     def _get_next_version(self):\n         root_dir = os.path.join(self.save_dir, self.name)\n+\n+        if not os.path.isdir(root_dir):\n+            log.warning('Missing logger folder: %s', root_dir)\n+            return 0\n+\n         existing_versions = []\n         for d in os.listdir(root_dir):\n             if os.path.isdir(os.path.join(root_dir, d)) and d.startswith(\"version_\"):\n", "issue": "Tensorboard logger error: lightning_logs directory not exists in multi-node DDP on nodes with rank != 0\n## \ud83d\udc1b Bug\r\n\r\nIn multi-node DDP train mode on all nodes except rank 0 errors appears at the start of the training caused by accessing lightning_logs directory in tensorboard logger which is not exist at the moment.\r\n\r\n### To Reproduce\r\n\r\nSteps to reproduce the behavior:\r\n\r\n1. setup multi-node cluster (without SLURM)\r\n2. set environment variables on each node:\r\n```\r\nexport MASTER_ADDR=<rank 0 node IP>\r\nexport MASTER_PORT=23456\r\nexport RANK=<node id>\r\nexport SLURM_NODEID=<node id>\r\nexport WORLD_SIZE=<world-size>\r\n```\r\n3. install dependencies:\r\n```\r\npip install torch torchvision hydra-core pytorch-lightning\r\n```\r\n4. copy app.y and conf.yaml to each node\r\n5. run script on each node\r\n```\r\npython app.py\r\n```\r\n6. see the error:\r\n```\r\nException:\r\n\r\n-- Process 0 terminated with the following error:\r\nTraceback (most recent call last):\r\n  File \"/home/ubuntu/anaconda3/envs/nightly_pt/lib/python3.6/site-packages/torch/multiprocessing/spawn.py\", line 19, in _wrap\r\n    fn(i, *args)\r\n  File \"/home/ubuntu/anaconda3/envs/nightly_pt/lib/python3.6/site-packages/pytorch_lightning/trainer/distrib_data_parallel.py\", line 342, in ddp_train\r\n    self.run_pretrain_routine(model)\r\n  File \"/home/ubuntu/anaconda3/envs/nightly_pt/lib/python3.6/site-packages/pytorch_lightning/trainer/trainer.py\", line 777, in run_pretrain_routine\r\n    self.configure_checkpoint_callback()\r\n  File \"/home/ubuntu/anaconda3/envs/nightly_pt/lib/python3.6/site-packages/pytorch_lightning/trainer/callback_config.py\", line 45, in configure_checkpoint_callback\r\n    f'version_{self.logger.version}',\r\n  File \"/home/ubuntu/anaconda3/envs/nightly_pt/lib/python3.6/site-packages/pytorch_lightning/loggers/tensorboard.py\", line 161, in version\r\n    self._version = self._get_next_version()\r\n  File \"/home/ubuntu/anaconda3/envs/nightly_pt/lib/python3.6/site-packages/pytorch_lightning/loggers/tensorboard.py\", line 167, in _get_next_version\r\n    for d in os.listdir(root_dir):\r\nFileNotFoundError: [Errno 2] No such file or directory: '/home/ubuntu/pytorch-lightning-intro-guide/outputs/2020-04-04/15-53-26/lightning_logs'\r\n```\r\n\r\n#### Code sample\r\n\r\napp.py:\r\n```\r\nimport pathlib\r\n\r\nimport hydra\r\nimport pytorch_lightning as pl\r\nimport torch\r\nfrom omegaconf import OmegaConf\r\nfrom torch.nn import functional as F\r\nfrom torch.optim import Adam\r\nfrom torch.utils.data import DataLoader, random_split\r\nfrom torchvision import datasets, transforms\r\n\r\n\r\nclass LitMNIST(pl.LightningModule):\r\n    def __init__(self):\r\n        super().__init__()\r\n        self.layer_1 = torch.nn.Linear(28 * 28, 128)\r\n        self.layer_2 = torch.nn.Linear(128, 256)\r\n        self.layer_3 = torch.nn.Linear(256, 10)\r\n\r\n        self.train_dataset = None\r\n        self.val_dataset = None\r\n        self.test_dataset = None\r\n\r\n    def forward(self, x):\r\n        batch_size, channels, width, height = x.size()\r\n        x = x.view(batch_size, -1)\r\n        x = self.layer_1(x)\r\n        x = F.relu(x)\r\n        x = self.layer_2(x)\r\n        x = F.relu(x)\r\n        x = self.layer_3(x)\r\n        x = F.log_softmax(x, dim=1)\r\n        return x\r\n\r\n    def prepare_data(self):\r\n        # transform\r\n        transform = transforms.Compose(\r\n            [transforms.ToTensor(), transforms.Normalize((0.1307,), (0.3081,))])\r\n\r\n        # download\r\n        data_dir = pathlib.Path.home() / 'data'\r\n        mnist_train = datasets.MNIST(data_dir, train=True,\r\n                                     download=True, transform=transform)\r\n        mnist_test = datasets.MNIST(data_dir, train=False,\r\n                                    download=True, transform=transform)\r\n\r\n        # train/val split\r\n        mnist_train, mnist_val = random_split(mnist_train, [55000, 5000])\r\n\r\n        # assign to use in dataloaders\r\n        self.train_dataset = mnist_train\r\n        self.val_dataset = mnist_val\r\n        self.test_dataset = mnist_test\r\n\r\n    def train_dataloader(self):\r\n        return DataLoader(self.train_dataset, batch_size=64)\r\n\r\n    def val_dataloader(self):\r\n        return DataLoader(self.val_dataset, batch_size=64)\r\n\r\n    def test_dataloader(self):\r\n        return DataLoader(self.test_dataset, batch_size=64)\r\n\r\n    def configure_optimizers(self):\r\n        return Adam(self.parameters(), lr=1e-3)\r\n\r\n    def training_step(self, batch, batch_idx):\r\n        x, y = batch\r\n        logits = self(x)\r\n        loss = F.nll_loss(logits, y)\r\n\r\n        # add logging\r\n        logs = {'loss': loss}\r\n        return {'loss': loss, 'log': logs}\r\n\r\n    def validation_step(self, batch, batch_idx):\r\n        x, y = batch\r\n        logits = self(x)\r\n        loss = F.nll_loss(logits, y)\r\n        return {'val_loss': loss}\r\n\r\n    def validation_epoch_end(self, outputs):\r\n        avg_loss = torch.stack(  # pylint: disable=no-member\r\n            [x['val_loss'] for x in outputs]).mean()\r\n        tensorboard_logs = {'val_loss': avg_loss}\r\n        return {'avg_val_loss': avg_loss, 'log': tensorboard_logs}\r\n\r\n    def test_step(self, batch, batch_idx):\r\n        x, y = batch\r\n        logits = self(x)\r\n        loss = F.nll_loss(logits, y)\r\n        return {'val_loss': loss}\r\n\r\n    def test_epoch_end(self, outputs):\r\n        avg_loss = torch.stack(  # pylint: disable=no-member\r\n            [x['val_loss'] for x in outputs]).mean()\r\n        tensorboard_logs = {'val_loss': avg_loss}\r\n        return {'avg_val_loss': avg_loss, 'log': tensorboard_logs}\r\n\r\n    def init_ddp_connection(self, proc_rank: int, world_size: int) -> None:\r\n        torch.distributed.init_process_group(\r\n            'nccl', rank=proc_rank, world_size=world_size)\r\n\r\n\r\n@hydra.main(config_path='conf.yaml')\r\ndef main(conf: OmegaConf):\r\n    model = LitMNIST()\r\n\r\n    trainer = pl.Trainer(gpus=conf.gpus,\r\n                         num_nodes=conf.num_nodes,\r\n                         distributed_backend=conf.distributed_backend,\r\n                         max_epochs=3)\r\n    trainer.fit(model)\r\n\r\n\r\nif __name__ == '__main__':\r\n    main()  # pylint: disable=no-value-for-parameter\r\n```\r\n\r\nconf.yaml:\r\n```\r\ngpus: 1\r\nnum_nodes: 2\r\ndistributed_backend: ddp\r\n```\r\n\r\n### Expected behavior\r\n\r\nTrain should go without error\r\n\r\n### Environment\r\n```\r\ncuda:\r\n\tGPU:\r\n\t\tTesla K80\r\n\t\tTesla K80\r\n\t\tTesla K80\r\n\t\tTesla K80\r\n\t\tTesla K80\r\n\t\tTesla K80\r\n\t\tTesla K80\r\n\t\tTesla K80\r\n\tavailable:           True\r\n\tversion:             10.1\r\npackages:\r\n\tnumpy:               1.18.1\r\n\tpyTorch_debug:       False\r\n\tpyTorch_version:     1.4.0\r\n\tpytorch-lightning:   0.7.1\r\n\ttensorboard:         2.2.0\r\n\ttqdm:                4.45.0\r\nsystem:\r\n\tOS:                  Linux\r\n\tarchitecture:\r\n\t\t64bit\r\n\r\n\tprocessor:           x86_64\r\n\tpython:              3.6.10\r\n\tversion:             #113-Ubuntu SMP Wed Jan 29 14:54:54 UTC 2020\r\n```\r\n### Additional context\r\n\r\n<!-- Add any other context about the problem here. -->\r\n\n", "before_files": [{"content": "import csv\nimport os\nfrom argparse import Namespace\nfrom typing import Optional, Dict, Union, Any\nfrom warnings import warn\n\nimport torch\nfrom pkg_resources import parse_version\nfrom torch.utils.tensorboard import SummaryWriter\n\nfrom pytorch_lightning.loggers.base import LightningLoggerBase, rank_zero_only\n\n\nclass TensorBoardLogger(LightningLoggerBase):\n    r\"\"\"\n\n    Log to local file system in TensorBoard format\n\n    Implemented using :class:`torch.utils.tensorboard.SummaryWriter`. Logs are saved to\n    ``os.path.join(save_dir, name, version)``\n\n    Example:\n        .. code-block:: python\n\n            logger = TensorBoardLogger(\"tb_logs\", name=\"my_model\")\n            trainer = Trainer(logger=logger)\n            trainer.train(model)\n\n    Args:\n        save_dir: Save directory\n        name: Experiment name. Defaults to \"default\".  If it is the empty string then no per-experiment\n            subdirectory is used.\n        version: Experiment version. If version is not specified the logger inspects the save\n            directory for existing versions, then automatically assigns the next available version.\n            If it is a string then it is used as the run-specific subdirectory name,\n            otherwise version_${version} is used.\n        \\**kwargs: Other arguments are passed directly to the :class:`SummaryWriter` constructor.\n\n    \"\"\"\n    NAME_CSV_TAGS = 'meta_tags.csv'\n\n    def __init__(\n            self, save_dir: str, name: Optional[str] = \"default\",\n            version: Optional[Union[int, str]] = None, **kwargs\n    ):\n        super().__init__()\n        self.save_dir = save_dir\n        self._name = name\n        self._version = version\n\n        self._experiment = None\n        self.tags = {}\n        self.kwargs = kwargs\n\n    @property\n    def root_dir(self) -> str:\n        \"\"\"\n        Parent directory for all tensorboard checkpoint subdirectories.\n        If the experiment name parameter is None or the empty string, no experiment subdirectory is used\n        and checkpoint will be saved in save_dir/version_dir\n        \"\"\"\n        if self.name is None or len(self.name) == 0:\n            return self.save_dir\n        else:\n            return os.path.join(self.save_dir, self.name)\n\n    @property\n    def log_dir(self) -> str:\n        \"\"\"\n        The directory for this run's tensorboard checkpoint.  By default, it is named 'version_${self.version}'\n        but it can be overridden by passing a string value for the constructor's version parameter\n        instead of None or an int\n        \"\"\"\n        # create a pseudo standard path ala test-tube\n        version = self.version if isinstance(self.version, str) else f\"version_{self.version}\"\n        log_dir = os.path.join(self.root_dir, version)\n        return log_dir\n\n    @property\n    def experiment(self) -> SummaryWriter:\n        r\"\"\"\n\n         Actual tensorboard object. To use tensorboard features do the following.\n\n         Example::\n\n             self.logger.experiment.some_tensorboard_function()\n\n         \"\"\"\n        if self._experiment is not None:\n            return self._experiment\n\n        os.makedirs(self.root_dir, exist_ok=True)\n        self._experiment = SummaryWriter(log_dir=self.log_dir, **self.kwargs)\n        return self._experiment\n\n    @rank_zero_only\n    def log_hyperparams(self, params: Union[Dict[str, Any], Namespace]) -> None:\n        params = self._convert_params(params)\n        params = self._flatten_dict(params)\n        sanitized_params = self._sanitize_params(params)\n\n        if parse_version(torch.__version__) < parse_version(\"1.3.0\"):\n            warn(\n                f\"Hyperparameter logging is not available for Torch version {torch.__version__}.\"\n                \" Skipping log_hyperparams. Upgrade to Torch 1.3.0 or above to enable\"\n                \" hyperparameter logging.\"\n            )\n        else:\n            from torch.utils.tensorboard.summary import hparams\n            exp, ssi, sei = hparams(sanitized_params, {})\n            writer = self.experiment._get_file_writer()\n            writer.add_summary(exp)\n            writer.add_summary(ssi)\n            writer.add_summary(sei)\n\n        # some alternative should be added\n        self.tags.update(sanitized_params)\n\n    @rank_zero_only\n    def log_metrics(self, metrics: Dict[str, float], step: Optional[int] = None) -> None:\n        for k, v in metrics.items():\n            if isinstance(v, torch.Tensor):\n                v = v.item()\n            self.experiment.add_scalar(k, v, step)\n\n    @rank_zero_only\n    def save(self) -> None:\n        try:\n            self.experiment.flush()\n        except AttributeError:\n            # you are using PT version (<v1.2) which does not have implemented flush\n            self.experiment._get_file_writer().flush()\n\n        dir_path = self.log_dir\n        if not os.path.isdir(dir_path):\n            dir_path = self.save_dir\n\n        # prepare the file path\n        meta_tags_path = os.path.join(dir_path, self.NAME_CSV_TAGS)\n\n        # save the metatags file\n        with open(meta_tags_path, 'w', newline='') as csvfile:\n            fieldnames = ['key', 'value']\n            writer = csv.DictWriter(csvfile, fieldnames=fieldnames)\n            writer.writerow({'key': 'key', 'value': 'value'})\n            for k, v in self.tags.items():\n                writer.writerow({'key': k, 'value': v})\n\n    @rank_zero_only\n    def finalize(self, status: str) -> None:\n        self.save()\n\n    @property\n    def name(self) -> str:\n        return self._name\n\n    @property\n    def version(self) -> int:\n        if self._version is None:\n            self._version = self._get_next_version()\n        return self._version\n\n    def _get_next_version(self):\n        root_dir = os.path.join(self.save_dir, self.name)\n        existing_versions = []\n        for d in os.listdir(root_dir):\n            if os.path.isdir(os.path.join(root_dir, d)) and d.startswith(\"version_\"):\n                existing_versions.append(int(d.split(\"_\")[1]))\n\n        if len(existing_versions) == 0:\n            return 0\n\n        return max(existing_versions) + 1\n", "path": "pytorch_lightning/loggers/tensorboard.py"}], "after_files": [{"content": "import csv\nimport os\nfrom argparse import Namespace\nfrom typing import Optional, Dict, Union, Any\nfrom warnings import warn\n\nimport torch\nfrom pkg_resources import parse_version\nfrom torch.utils.tensorboard import SummaryWriter\n\nfrom pytorch_lightning.loggers.base import LightningLoggerBase, rank_zero_only\nfrom pytorch_lightning import _logger as log\n\n\nclass TensorBoardLogger(LightningLoggerBase):\n    r\"\"\"\n\n    Log to local file system in TensorBoard format\n\n    Implemented using :class:`torch.utils.tensorboard.SummaryWriter`. Logs are saved to\n    ``os.path.join(save_dir, name, version)``\n\n    Example:\n        .. code-block:: python\n\n            logger = TensorBoardLogger(\"tb_logs\", name=\"my_model\")\n            trainer = Trainer(logger=logger)\n            trainer.train(model)\n\n    Args:\n        save_dir: Save directory\n        name: Experiment name. Defaults to \"default\".  If it is the empty string then no per-experiment\n            subdirectory is used.\n        version: Experiment version. If version is not specified the logger inspects the save\n            directory for existing versions, then automatically assigns the next available version.\n            If it is a string then it is used as the run-specific subdirectory name,\n            otherwise version_${version} is used.\n        \\**kwargs: Other arguments are passed directly to the :class:`SummaryWriter` constructor.\n\n    \"\"\"\n    NAME_CSV_TAGS = 'meta_tags.csv'\n\n    def __init__(\n            self, save_dir: str, name: Optional[str] = \"default\",\n            version: Optional[Union[int, str]] = None, **kwargs\n    ):\n        super().__init__()\n        self.save_dir = save_dir\n        self._name = name\n        self._version = version\n\n        self._experiment = None\n        self.tags = {}\n        self.kwargs = kwargs\n\n    @property\n    def root_dir(self) -> str:\n        \"\"\"\n        Parent directory for all tensorboard checkpoint subdirectories.\n        If the experiment name parameter is None or the empty string, no experiment subdirectory is used\n        and checkpoint will be saved in save_dir/version_dir\n        \"\"\"\n        if self.name is None or len(self.name) == 0:\n            return self.save_dir\n        else:\n            return os.path.join(self.save_dir, self.name)\n\n    @property\n    def log_dir(self) -> str:\n        \"\"\"\n        The directory for this run's tensorboard checkpoint.  By default, it is named 'version_${self.version}'\n        but it can be overridden by passing a string value for the constructor's version parameter\n        instead of None or an int\n        \"\"\"\n        # create a pseudo standard path ala test-tube\n        version = self.version if isinstance(self.version, str) else f\"version_{self.version}\"\n        log_dir = os.path.join(self.root_dir, version)\n        return log_dir\n\n    @property\n    def experiment(self) -> SummaryWriter:\n        r\"\"\"\n\n         Actual tensorboard object. To use tensorboard features do the following.\n\n         Example::\n\n             self.logger.experiment.some_tensorboard_function()\n\n         \"\"\"\n        if self._experiment is not None:\n            return self._experiment\n\n        os.makedirs(self.root_dir, exist_ok=True)\n        self._experiment = SummaryWriter(log_dir=self.log_dir, **self.kwargs)\n        return self._experiment\n\n    @rank_zero_only\n    def log_hyperparams(self, params: Union[Dict[str, Any], Namespace]) -> None:\n        params = self._convert_params(params)\n        params = self._flatten_dict(params)\n        sanitized_params = self._sanitize_params(params)\n\n        if parse_version(torch.__version__) < parse_version(\"1.3.0\"):\n            warn(\n                f\"Hyperparameter logging is not available for Torch version {torch.__version__}.\"\n                \" Skipping log_hyperparams. Upgrade to Torch 1.3.0 or above to enable\"\n                \" hyperparameter logging.\"\n            )\n        else:\n            from torch.utils.tensorboard.summary import hparams\n            exp, ssi, sei = hparams(sanitized_params, {})\n            writer = self.experiment._get_file_writer()\n            writer.add_summary(exp)\n            writer.add_summary(ssi)\n            writer.add_summary(sei)\n\n        # some alternative should be added\n        self.tags.update(sanitized_params)\n\n    @rank_zero_only\n    def log_metrics(self, metrics: Dict[str, float], step: Optional[int] = None) -> None:\n        for k, v in metrics.items():\n            if isinstance(v, torch.Tensor):\n                v = v.item()\n            self.experiment.add_scalar(k, v, step)\n\n    @rank_zero_only\n    def save(self) -> None:\n        try:\n            self.experiment.flush()\n        except AttributeError:\n            # you are using PT version (<v1.2) which does not have implemented flush\n            self.experiment._get_file_writer().flush()\n\n        dir_path = self.log_dir\n        if not os.path.isdir(dir_path):\n            dir_path = self.save_dir\n\n        # prepare the file path\n        meta_tags_path = os.path.join(dir_path, self.NAME_CSV_TAGS)\n\n        # save the metatags file\n        with open(meta_tags_path, 'w', newline='') as csvfile:\n            fieldnames = ['key', 'value']\n            writer = csv.DictWriter(csvfile, fieldnames=fieldnames)\n            writer.writerow({'key': 'key', 'value': 'value'})\n            for k, v in self.tags.items():\n                writer.writerow({'key': k, 'value': v})\n\n    @rank_zero_only\n    def finalize(self, status: str) -> None:\n        self.save()\n\n    @property\n    def name(self) -> str:\n        return self._name\n\n    @property\n    def version(self) -> int:\n        if self._version is None:\n            self._version = self._get_next_version()\n        return self._version\n\n    def _get_next_version(self):\n        root_dir = os.path.join(self.save_dir, self.name)\n\n        if not os.path.isdir(root_dir):\n            log.warning('Missing logger folder: %s', root_dir)\n            return 0\n\n        existing_versions = []\n        for d in os.listdir(root_dir):\n            if os.path.isdir(os.path.join(root_dir, d)) and d.startswith(\"version_\"):\n                existing_versions.append(int(d.split(\"_\")[1]))\n\n        if len(existing_versions) == 0:\n            return 0\n\n        return max(existing_versions) + 1\n", "path": "pytorch_lightning/loggers/tensorboard.py"}]}