2020-06-25 13:22:28 +00:00
|
|
|
import os
|
2020-03-14 17:02:05 +00:00
|
|
|
from argparse import Namespace
|
2020-08-24 09:28:56 +00:00
|
|
|
from distutils.version import LooseVersion
|
2020-03-03 01:49:14 +00:00
|
|
|
|
|
|
|
import pytest
|
|
|
|
import torch
|
2020-06-25 13:22:28 +00:00
|
|
|
import yaml
|
2020-08-07 13:13:21 +00:00
|
|
|
from omegaconf import OmegaConf
|
2020-09-15 21:48:48 +00:00
|
|
|
from tensorboard.backend.event_processing.event_accumulator import EventAccumulator
|
2020-03-03 01:49:14 +00:00
|
|
|
|
2020-06-25 13:22:28 +00:00
|
|
|
from pytorch_lightning import Trainer
|
2020-03-19 13:14:29 +00:00
|
|
|
from pytorch_lightning.loggers import TensorBoardLogger
|
2020-06-25 13:22:28 +00:00
|
|
|
from tests.base import EvalModelTemplate
|
|
|
|
|
|
|
|
|
2020-08-07 13:13:21 +00:00
|
|
|
@pytest.mark.skipif(
|
2020-08-24 09:28:56 +00:00
|
|
|
LooseVersion(torch.__version__) < LooseVersion("1.5.0"),
|
2020-08-07 13:13:21 +00:00
|
|
|
reason="Minimal PT version is set to 1.5",
|
|
|
|
)
|
2020-06-25 13:22:28 +00:00
|
|
|
def test_tensorboard_hparams_reload(tmpdir):
|
|
|
|
model = EvalModelTemplate()
|
|
|
|
|
2020-08-07 13:13:21 +00:00
|
|
|
trainer = Trainer(max_epochs=1, default_root_dir=tmpdir)
|
2020-06-25 13:22:28 +00:00
|
|
|
trainer.fit(model)
|
|
|
|
|
|
|
|
folder_path = trainer.logger.log_dir
|
|
|
|
|
|
|
|
# make sure yaml is there
|
2020-08-07 13:13:21 +00:00
|
|
|
with open(os.path.join(folder_path, "hparams.yaml")) as file:
|
2020-06-25 13:22:28 +00:00
|
|
|
# The FullLoader parameter handles the conversion from YAML
|
|
|
|
# scalar values to Python the dictionary format
|
2020-07-09 11:15:41 +00:00
|
|
|
yaml_params = yaml.safe_load(file)
|
2020-08-07 13:13:21 +00:00
|
|
|
assert yaml_params["b1"] == 0.5
|
2020-06-25 13:22:28 +00:00
|
|
|
assert len(yaml_params.keys()) == 10
|
|
|
|
|
|
|
|
# verify artifacts
|
2020-08-07 13:13:21 +00:00
|
|
|
assert len(os.listdir(os.path.join(folder_path, "checkpoints"))) == 1
|
2020-09-15 21:48:48 +00:00
|
|
|
|
|
|
|
# verify tb logs
|
|
|
|
event_acc = EventAccumulator(folder_path)
|
|
|
|
event_acc.Reload()
|
|
|
|
|
|
|
|
data_pt_1_5 = b'\x12\x93\x01"\x0b\n\tdrop_prob"\x0c\n\nbatch_size"\r\n\x0bin_features"\x0f\n\rlearning_rate"' \
|
|
|
|
b'\x10\n\x0eoptimizer_name"\x0b\n\tdata_root"\x0e\n\x0cout_features"\x0c\n\nhidden_dim"' \
|
|
|
|
b'\x04\n\x02b1"\x04\n\x02b2*\r\n\x0b\x12\thp_metric'
|
|
|
|
data_pt_1_6 = b'\x12\xa7\x01"\r\n\tdrop_prob \x03"\x0e\n\nbatch_size \x03"\x0f\n\x0bin_features \x03"' \
|
|
|
|
b'\x11\n\rlearning_rate \x03"\x12\n\x0eoptimizer_name \x01"\r\n\tdata_root \x01"' \
|
|
|
|
b'\x10\n\x0cout_features \x03"\x0e\n\nhidden_dim \x03"\x06\n\x02b1 \x03"' \
|
|
|
|
b'\x06\n\x02b2 \x03*\r\n\x0b\x12\thp_metric'
|
|
|
|
|
|
|
|
hparams_data = data_pt_1_6 if LooseVersion(torch.__version__) >= LooseVersion("1.6.0") else data_pt_1_5
|
|
|
|
|
|
|
|
assert event_acc.summary_metadata['_hparams_/experiment'].plugin_data.plugin_name == 'hparams'
|
|
|
|
assert event_acc.summary_metadata['_hparams_/experiment'].plugin_data.content == hparams_data
|
2020-03-03 01:49:14 +00:00
|
|
|
|
|
|
|
|
|
|
|
def test_tensorboard_automatic_versioning(tmpdir):
|
|
|
|
"""Verify that automatic versioning works"""
|
|
|
|
|
2020-07-09 11:15:41 +00:00
|
|
|
root_dir = tmpdir / "tb_versioning"
|
|
|
|
root_dir.mkdir()
|
|
|
|
(root_dir / "version_0").mkdir()
|
|
|
|
(root_dir / "version_1").mkdir()
|
2020-03-03 01:49:14 +00:00
|
|
|
|
|
|
|
logger = TensorBoardLogger(save_dir=tmpdir, name="tb_versioning")
|
|
|
|
assert logger.version == 2
|
|
|
|
|
|
|
|
|
|
|
|
def test_tensorboard_manual_versioning(tmpdir):
|
|
|
|
"""Verify that manual versioning works"""
|
|
|
|
|
2020-07-09 11:15:41 +00:00
|
|
|
root_dir = tmpdir / "tb_versioning"
|
|
|
|
root_dir.mkdir()
|
|
|
|
(root_dir / "version_0").mkdir()
|
|
|
|
(root_dir / "version_1").mkdir()
|
|
|
|
(root_dir / "version_2").mkdir()
|
2020-03-03 01:49:14 +00:00
|
|
|
|
|
|
|
logger = TensorBoardLogger(save_dir=tmpdir, name="tb_versioning", version=1)
|
|
|
|
|
|
|
|
assert logger.version == 1
|
|
|
|
|
|
|
|
|
|
|
|
def test_tensorboard_named_version(tmpdir):
|
|
|
|
"""Verify that manual versioning works for string versions, e.g. '2020-02-05-162402' """
|
|
|
|
|
2020-07-09 11:15:41 +00:00
|
|
|
name = "tb_versioning"
|
|
|
|
(tmpdir / name).mkdir()
|
2020-03-03 01:49:14 +00:00
|
|
|
expected_version = "2020-02-05-162402"
|
|
|
|
|
2020-07-09 11:15:41 +00:00
|
|
|
logger = TensorBoardLogger(save_dir=tmpdir, name=name, version=expected_version)
|
2020-03-03 01:49:14 +00:00
|
|
|
logger.log_hyperparams({"a": 1, "b": 2}) # Force data to be written
|
|
|
|
|
|
|
|
assert logger.version == expected_version
|
2020-07-09 11:15:41 +00:00
|
|
|
assert os.listdir(tmpdir / name) == [expected_version]
|
|
|
|
assert os.listdir(tmpdir / name / expected_version)
|
2020-03-03 01:49:14 +00:00
|
|
|
|
|
|
|
|
2020-08-07 13:13:21 +00:00
|
|
|
@pytest.mark.parametrize("name", ["", None])
|
2020-04-15 00:32:33 +00:00
|
|
|
def test_tensorboard_no_name(tmpdir, name):
|
2020-03-03 01:49:14 +00:00
|
|
|
"""Verify that None or empty name works"""
|
2020-04-15 00:32:33 +00:00
|
|
|
logger = TensorBoardLogger(save_dir=tmpdir, name=name)
|
2020-07-09 11:15:41 +00:00
|
|
|
logger.log_hyperparams({"a": 1, "b": 2}) # Force data to be written
|
2020-03-03 01:49:14 +00:00
|
|
|
assert logger.root_dir == tmpdir
|
2020-08-07 13:13:21 +00:00
|
|
|
assert os.listdir(tmpdir / "version_0")
|
2020-03-03 01:49:14 +00:00
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.parametrize("step_idx", [10, None])
|
|
|
|
def test_tensorboard_log_metrics(tmpdir, step_idx):
|
|
|
|
logger = TensorBoardLogger(tmpdir)
|
|
|
|
metrics = {
|
|
|
|
"float": 0.3,
|
|
|
|
"int": 1,
|
|
|
|
"FloatTensor": torch.tensor(0.1),
|
2020-08-07 13:13:21 +00:00
|
|
|
"IntTensor": torch.tensor(1),
|
2020-03-03 01:49:14 +00:00
|
|
|
}
|
|
|
|
logger.log_metrics(metrics, step_idx)
|
|
|
|
|
|
|
|
|
|
|
|
def test_tensorboard_log_hyperparams(tmpdir):
|
|
|
|
logger = TensorBoardLogger(tmpdir)
|
|
|
|
hparams = {
|
|
|
|
"float": 0.3,
|
|
|
|
"int": 1,
|
|
|
|
"string": "abc",
|
2020-03-14 17:02:05 +00:00
|
|
|
"bool": True,
|
2020-08-07 13:13:21 +00:00
|
|
|
"dict": {"a": {"b": "c"}},
|
2020-03-14 17:02:05 +00:00
|
|
|
"list": [1, 2, 3],
|
2020-08-07 13:13:21 +00:00
|
|
|
"namespace": Namespace(foo=Namespace(bar="buzz")),
|
|
|
|
"layer": torch.nn.BatchNorm1d,
|
2020-03-03 01:49:14 +00:00
|
|
|
}
|
|
|
|
logger.log_hyperparams(hparams)
|
2020-04-27 07:52:31 +00:00
|
|
|
|
|
|
|
|
2020-04-27 07:53:59 +00:00
|
|
|
def test_tensorboard_log_hparams_and_metrics(tmpdir):
|
2020-08-24 06:57:04 +00:00
|
|
|
logger = TensorBoardLogger(tmpdir, default_hp_metric=False)
|
2020-04-27 07:50:01 +00:00
|
|
|
hparams = {
|
|
|
|
"float": 0.3,
|
|
|
|
"int": 1,
|
|
|
|
"string": "abc",
|
|
|
|
"bool": True,
|
2020-08-07 13:13:21 +00:00
|
|
|
"dict": {"a": {"b": "c"}},
|
2020-04-27 07:50:01 +00:00
|
|
|
"list": [1, 2, 3],
|
2020-08-07 13:13:21 +00:00
|
|
|
"namespace": Namespace(foo=Namespace(bar="buzz")),
|
|
|
|
"layer": torch.nn.BatchNorm1d,
|
2020-04-27 07:50:01 +00:00
|
|
|
}
|
2020-08-07 13:13:21 +00:00
|
|
|
metrics = {"abc": torch.tensor([0.54])}
|
|
|
|
logger.log_hyperparams(hparams, metrics)
|
|
|
|
|
|
|
|
|
|
|
|
def test_tensorboard_log_omegaconf_hparams_and_metrics(tmpdir):
|
2020-08-24 06:57:04 +00:00
|
|
|
logger = TensorBoardLogger(tmpdir, default_hp_metric=False)
|
2020-08-07 13:13:21 +00:00
|
|
|
hparams = {
|
|
|
|
"float": 0.3,
|
|
|
|
"int": 1,
|
|
|
|
"string": "abc",
|
|
|
|
"bool": True,
|
|
|
|
"dict": {"a": {"b": "c"}},
|
|
|
|
"list": [1, 2, 3],
|
|
|
|
# "namespace": Namespace(foo=Namespace(bar="buzz")),
|
|
|
|
# "layer": torch.nn.BatchNorm1d,
|
|
|
|
}
|
|
|
|
hparams = OmegaConf.create(hparams)
|
|
|
|
|
|
|
|
metrics = {"abc": torch.tensor([0.54])}
|
2020-04-27 07:50:01 +00:00
|
|
|
logger.log_hyperparams(hparams, metrics)
|
2020-08-19 23:08:46 +00:00
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.parametrize("example_input_array", [None, torch.rand(2, 28 * 28)])
|
|
|
|
def test_tensorboard_log_graph(tmpdir, example_input_array):
|
|
|
|
""" test that log graph works with both model.example_input_array and
|
|
|
|
if array is passed externaly
|
|
|
|
"""
|
|
|
|
model = EvalModelTemplate()
|
2020-08-22 10:35:09 +00:00
|
|
|
if example_input_array is not None:
|
2020-08-19 23:08:46 +00:00
|
|
|
model.example_input_array = None
|
2020-08-22 10:35:09 +00:00
|
|
|
logger = TensorBoardLogger(tmpdir, log_graph=True)
|
2020-08-19 23:08:46 +00:00
|
|
|
logger.log_graph(model, example_input_array)
|
|
|
|
|
|
|
|
|
|
|
|
def test_tensorboard_log_graph_warning_no_example_input_array(tmpdir):
|
|
|
|
""" test that log graph throws warning if model.example_input_array is None """
|
|
|
|
model = EvalModelTemplate()
|
|
|
|
model.example_input_array = None
|
2020-08-22 10:35:09 +00:00
|
|
|
logger = TensorBoardLogger(tmpdir, log_graph=True)
|
2020-08-19 23:08:46 +00:00
|
|
|
with pytest.warns(
|
|
|
|
UserWarning,
|
|
|
|
match='Could not log computational graph since the `model.example_input_array`'
|
|
|
|
' attribute is not set or `input_array` was not given'
|
|
|
|
):
|
|
|
|
logger.log_graph(model)
|