From 98804dfea996efa73f8ef4390de2111b6d3bc28e Mon Sep 17 00:00:00 2001 From: sh0622-kim <66167254+sh0622-kim@users.noreply.github.com> Date: Sat, 14 Jan 2023 00:39:37 +0900 Subject: [PATCH 01/49] add vis mlflow backend --- mmengine/model/base_module.py | 15 +-- mmengine/visualization/__init__.py | 4 +- mmengine/visualization/vis_backend.py | 126 ++++++++++++++++++++++ tests/test_visualizer/test_vis_backend.py | 50 ++++++++- 4 files changed, 185 insertions(+), 10 deletions(-) diff --git a/mmengine/model/base_module.py b/mmengine/model/base_module.py index 1c2ad60b06..8268e9ffb3 100644 --- a/mmengine/model/base_module.py +++ b/mmengine/model/base_module.py @@ -164,14 +164,15 @@ def _dump_init_info(self): # dump the information to the logger file if there is a `FileHandler` for handler in logger.handlers: if isinstance(handler, FileHandler): - handler.stream.write( - 'Name of parameter - Initialization information\n') - for name, param in self.named_parameters(): + if(handler.stream != None): handler.stream.write( - f'\n{name} - {param.shape}: ' - f"\n{self._params_init_info[param]['init_info']} \n") - handler.stream.flush() - with_file_handler = True + 'Name of parameter - Initialization information\n') + for name, param in self.named_parameters(): + handler.stream.write( + f'\n{name} - {param.shape}: ' + f"\n{self._params_init_info[param]['init_info']} \n") + handler.stream.flush() + with_file_handler = True if not with_file_handler: for name, param in self.named_parameters(): print_log( diff --git a/mmengine/visualization/__init__.py b/mmengine/visualization/__init__.py index 6c8b0bb546..139070850f 100644 --- a/mmengine/visualization/__init__.py +++ b/mmengine/visualization/__init__.py @@ -1,9 +1,9 @@ # Copyright (c) OpenMMLab. All rights reserved. from .vis_backend import (BaseVisBackend, LocalVisBackend, - TensorboardVisBackend, WandbVisBackend) + TensorboardVisBackend, WandbVisBackend, MLFlowVisBackend) from .visualizer import Visualizer __all__ = [ 'Visualizer', 'BaseVisBackend', 'LocalVisBackend', 'WandbVisBackend', - 'TensorboardVisBackend' + 'TensorboardVisBackend', 'MLflowVisBackend' ] diff --git a/mmengine/visualization/vis_backend.py b/mmengine/visualization/vis_backend.py index 1dc9c3031a..3b7894db86 100644 --- a/mmengine/visualization/vis_backend.py +++ b/mmengine/visualization/vis_backend.py @@ -610,3 +610,129 @@ def close(self): """close an opened tensorboard object.""" if hasattr(self, '_tensorboard'): self._tensorboard.close() + +@VISBACKENDS.register_module() +class MLFlowVisBackend(BaseVisBackend): + """MLFlow visualization backend class. + + It can write images, config, scalars, etc. to a + mlflow file. + + Examples: + >>> from mmengine.visualization import MLFlowVisBackend + >>> import numpy as np + >>> vis_backend = MLFlowVisBackend(save_dir='temp_dir') + >>> img = np.random.randint(0, 256, size=(10, 10, 3)) + >>> vis_backend.add_image('img', img) + >>> vis_backend.add_scaler('mAP', 0.6) + >>> vis_backend.add_scalars({'loss': 0.1,'acc':0.8}) + >>> cfg = Config(dict(a=1, b=dict(b1=[0, 1]))) + >>> vis_backend.add_config(cfg) + + Args: + save_dir (str): The root directory to save the files + produced by the backend. + """ + + def __init__(self, + save_dir: str, + exp_name: Optional[str] = None, + tags: Optional[dict] = None, + params: Optional[dict] = None, + log_model: bool = True): + super().__init__(save_dir) + self._exp_name = exp_name + self._tags = tags + self._params = params + self._log_model = log_model + + def _init_env(self): + """Setup env for MLFlow.""" + if not os.path.exists(self._save_dir): + os.makedirs(self._save_dir, exist_ok=True) # type: ignore + + try: + import mlflow + except ImportError: + raise ImportError('Please install mlflow to use ' + 'MLFlowVisBackend.') # type: ignore + self._mlflow = mlflow + + if self._exp_name is not None: + self._mlflow.set_experiment(self._exp_name) + if self._tags is not None: + self._mlflow.set_tags(self._tags) + if self._params is not None: + self._mlflow.log_params(self._params) + + @property # type: ignore + @force_init_env + def experiment(self): + """Return MLFlow object.""" + return self._mlflow + + @force_init_env + def add_config(self, config: Config, **kwargs) -> None: + """Record the config to mlflow. + + Args: + config (Config): The Config object + """ + return super().add_config(config, **kwargs) + + @force_init_env + def add_image(self, + name: str, + image: np.ndarray, + step: int = 0, + **kwargs) -> None: + """Record the image to mlflow. + + Args: + name (str): The image identifier. + image (np.ndarray): The image to be saved. The format + should be RGB. + step (int): Global step value to record. Default to 0. + """ + self._mlflow.log_image(name, image, step) + + + @force_init_env + def add_scalar(self, + name: str, + value: Union[int, float, torch.Tensor, np.ndarray], + step: int = 0, + **kwargs) -> None: + """Record the scalar data to mlflow. + + Args: + name (str): The scalar identifier. + value (int, float, torch.Tensor, np.ndarray): Value to save. + step (int): Global step value to record. Default to 0. + """ + self._mlflow.log_metric(name, value, step) + + @force_init_env + def add_scalars(self, + scalar_dict: dict, + step: int = 0, + file_path: Optional[str] = None, + **kwargs) -> None: + """Record the scalar's data to mlflow. + + Args: + scalar_dict (dict): Key-value pair storing the tag and + corresponding values. + step (int): Global step value to record. Default to 0. + file_path (str, optional): Useless parameter. Just for + interface unification. Default to None. + """ + assert isinstance(scalar_dict, dict) + assert 'step' not in scalar_dict, 'Please set it directly ' \ + 'through the step parameter' + for key, value in scalar_dict.items(): + self.add_scalar(key, value, step) + + def close(self): + """close an opened mlflow object.""" + pass # mlflow will close automatically \ No newline at end of file diff --git a/tests/test_visualizer/test_vis_backend.py b/tests/test_visualizer/test_vis_backend.py index 78151f7a8d..4b53e5e6d0 100644 --- a/tests/test_visualizer/test_vis_backend.py +++ b/tests/test_visualizer/test_vis_backend.py @@ -12,7 +12,7 @@ from mmengine.fileio import load from mmengine.registry import VISBACKENDS from mmengine.visualization import (LocalVisBackend, TensorboardVisBackend, - WandbVisBackend) + WandbVisBackend, MLFlowVisBackend) class TestLocalVisBackend: @@ -241,3 +241,51 @@ def test_close(self): wandb_vis_backend._init_env() wandb_vis_backend.close() shutil.rmtree('temp_dir') + +class TestMLFlowVisBackend: + def test_init(self): + MLFlowVisBackend('temp_dir') + VISBACKENDS.build(dict(type='MLFlowVisBackend', save_dir='temp_dir')) + + def test_experiment(self): + mlflow_vis_backend = MLFlowVisBackend('temp_dir') + assert mlflow_vis_backend.experiment == mlflow_vis_backend._mlflow + shutil.rmtree('temp_dir') + + def test_add_config(self): + cfg = Config(dict(a=1, b=dict(b1=[0, 1]))) + mlflow_vis_backend = MLFlowVisBackend('temp_dir', log_code_name='code') + mlflow_vis_backend.add_config(cfg) + shutil.rmtree('temp_dir') + + def test_add_image(self): + image = np.random.randint(0, 256, size=(10, 10, 3)).astype(np.uint8) + mlflow_vis_backend = MLFlowVisBackend('temp_dir') + mlflow_vis_backend.add_image('img', image) + mlflow_vis_backend.add_image('img', image) + shutil.rmtree('temp_dir') + + def test_add_scalar(self): + mlflow_vis_backend = MLFlowVisBackend('temp_dir') + mlflow_vis_backend.add_scalar('map', 0.9) + # test append mode + mlflow_vis_backend.add_scalar('map', 0.9) + mlflow_vis_backend.add_scalar('map', 0.95) + shutil.rmtree('temp_dir') + + def test_add_scalars(self): + mlflow_vis_backend = MLFlowVisBackend('temp_dir') + input_dict = {'map': 0.7, 'acc': 0.9} + mlflow_vis_backend.add_scalars(input_dict) + # test append mode + mlflow_vis_backend.add_scalars({'map': 0.8, 'acc': 0.8}) + mlflow_vis_backend.add_scalars({'map': [0.8], 'acc': 0.8}) + shutil.rmtree('temp_dir') + + def test_close(self): + mlflow_vis_backend = MLFlowVisBackend('temp_dir') + mlflow_vis_backend._init_env() + mlflow_vis_backend.close() + shutil.rmtree('temp_dir') + + \ No newline at end of file From 8f6e932e26c441596fd36d73b1cd041e0d0bbcc9 Mon Sep 17 00:00:00 2001 From: sh0622-kim <66167254+sh0622-kim@users.noreply.github.com> Date: Sat, 14 Jan 2023 11:18:24 +0900 Subject: [PATCH 02/49] add mlflow config comment --- mmengine/visualization/vis_backend.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/mmengine/visualization/vis_backend.py b/mmengine/visualization/vis_backend.py index 3b7894db86..6a3f5630e9 100644 --- a/mmengine/visualization/vis_backend.py +++ b/mmengine/visualization/vis_backend.py @@ -632,6 +632,12 @@ class MLFlowVisBackend(BaseVisBackend): Args: save_dir (str): The root directory to save the files produced by the backend. + exp_name (str, optional): The experiment name. Default to None. + tags (dict, optional): The tags to be added to the experiment. + Default to None. + params (dict, optional): The params to be added to the experiment. + Default to None. + log_model (bool, optional): Whether to log the model. Default to True. """ def __init__(self, From 1db2ba6802540735d18be983302cfb00df1430aa Mon Sep 17 00:00:00 2001 From: sh0622-kim <66167254+sh0622-kim@users.noreply.github.com> Date: Sat, 14 Jan 2023 11:38:26 +0900 Subject: [PATCH 03/49] Fix lint errors --- mmengine/model/base_module.py | 2 +- mmengine/visualization/__init__.py | 3 ++- mmengine/visualization/vis_backend.py | 28 +++++++++++------------ tests/test_visualizer/test_vis_backend.py | 3 +-- 4 files changed, 18 insertions(+), 18 deletions(-) diff --git a/mmengine/model/base_module.py b/mmengine/model/base_module.py index 8268e9ffb3..03824ac018 100644 --- a/mmengine/model/base_module.py +++ b/mmengine/model/base_module.py @@ -164,7 +164,7 @@ def _dump_init_info(self): # dump the information to the logger file if there is a `FileHandler` for handler in logger.handlers: if isinstance(handler, FileHandler): - if(handler.stream != None): + if(handler.stream is not None): handler.stream.write( 'Name of parameter - Initialization information\n') for name, param in self.named_parameters(): diff --git a/mmengine/visualization/__init__.py b/mmengine/visualization/__init__.py index 139070850f..57890066ca 100644 --- a/mmengine/visualization/__init__.py +++ b/mmengine/visualization/__init__.py @@ -1,6 +1,7 @@ # Copyright (c) OpenMMLab. All rights reserved. from .vis_backend import (BaseVisBackend, LocalVisBackend, - TensorboardVisBackend, WandbVisBackend, MLFlowVisBackend) + TensorboardVisBackend, WandbVisBackend, + MLFlowVisBackend) from .visualizer import Visualizer __all__ = [ diff --git a/mmengine/visualization/vis_backend.py b/mmengine/visualization/vis_backend.py index 6a3f5630e9..4d29d9237c 100644 --- a/mmengine/visualization/vis_backend.py +++ b/mmengine/visualization/vis_backend.py @@ -611,6 +611,7 @@ def close(self): if hasattr(self, '_tensorboard'): self._tensorboard.close() + @VISBACKENDS.register_module() class MLFlowVisBackend(BaseVisBackend): """MLFlow visualization backend class. @@ -639,7 +640,6 @@ class MLFlowVisBackend(BaseVisBackend): Default to None. log_model (bool, optional): Whether to log the model. Default to True. """ - def __init__(self, save_dir: str, exp_name: Optional[str] = None, @@ -688,10 +688,10 @@ def add_config(self, config: Config, **kwargs) -> None: @force_init_env def add_image(self, - name: str, - image: np.ndarray, - step: int = 0, - **kwargs) -> None: + name: str, + image: np.ndarray, + step: int = 0, + **kwargs) -> None: """Record the image to mlflow. Args: @@ -705,10 +705,10 @@ def add_image(self, @force_init_env def add_scalar(self, - name: str, - value: Union[int, float, torch.Tensor, np.ndarray], - step: int = 0, - **kwargs) -> None: + name: str, + value: Union[int, float, torch.Tensor, np.ndarray], + step: int = 0, + **kwargs) -> None: """Record the scalar data to mlflow. Args: @@ -720,12 +720,12 @@ def add_scalar(self, @force_init_env def add_scalars(self, - scalar_dict: dict, - step: int = 0, - file_path: Optional[str] = None, - **kwargs) -> None: + scalar_dict: dict, + step: int = 0, + file_path: Optional[str] = None, + **kwargs) -> None: """Record the scalar's data to mlflow. - + Args: scalar_dict (dict): Key-value pair storing the tag and corresponding values. diff --git a/tests/test_visualizer/test_vis_backend.py b/tests/test_visualizer/test_vis_backend.py index 4b53e5e6d0..8481482a6b 100644 --- a/tests/test_visualizer/test_vis_backend.py +++ b/tests/test_visualizer/test_vis_backend.py @@ -242,6 +242,7 @@ def test_close(self): wandb_vis_backend.close() shutil.rmtree('temp_dir') + class TestMLFlowVisBackend: def test_init(self): MLFlowVisBackend('temp_dir') @@ -287,5 +288,3 @@ def test_close(self): mlflow_vis_backend._init_env() mlflow_vis_backend.close() shutil.rmtree('temp_dir') - - \ No newline at end of file From 5445adefbb9a55e5350d4c0fb9b73cce600d6186 Mon Sep 17 00:00:00 2001 From: sh0622-kim <66167254+sh0622-kim@users.noreply.github.com> Date: Sat, 14 Jan 2023 11:47:47 +0900 Subject: [PATCH 04/49] Fix lint errors --- mmengine/model/base_module.py | 17 +++---- mmengine/visualization/__init__.py | 2 +- mmengine/visualization/vis_backend.py | 65 +++++++++++++-------------- 3 files changed, 42 insertions(+), 42 deletions(-) diff --git a/mmengine/model/base_module.py b/mmengine/model/base_module.py index 03824ac018..423d4ad2de 100644 --- a/mmengine/model/base_module.py +++ b/mmengine/model/base_module.py @@ -164,15 +164,16 @@ def _dump_init_info(self): # dump the information to the logger file if there is a `FileHandler` for handler in logger.handlers: if isinstance(handler, FileHandler): - if(handler.stream is not None): + if (handler.stream is None): + continue + handler.stream.write( + 'Name of parameter - Initialization information\n') + for name, param in self.named_parameters(): handler.stream.write( - 'Name of parameter - Initialization information\n') - for name, param in self.named_parameters(): - handler.stream.write( - f'\n{name} - {param.shape}: ' - f"\n{self._params_init_info[param]['init_info']} \n") - handler.stream.flush() - with_file_handler = True + f'\n{name} - {param.shape}: ' + f"\n{self._params_init_info[param]['init_info']} \n") + handler.stream.flush() + with_file_handler = True if not with_file_handler: for name, param in self.named_parameters(): print_log( diff --git a/mmengine/visualization/__init__.py b/mmengine/visualization/__init__.py index 57890066ca..db7b4e971d 100644 --- a/mmengine/visualization/__init__.py +++ b/mmengine/visualization/__init__.py @@ -1,6 +1,6 @@ # Copyright (c) OpenMMLab. All rights reserved. from .vis_backend import (BaseVisBackend, LocalVisBackend, - TensorboardVisBackend, WandbVisBackend, + TensorboardVisBackend, WandbVisBackend, MLFlowVisBackend) from .visualizer import Visualizer diff --git a/mmengine/visualization/vis_backend.py b/mmengine/visualization/vis_backend.py index 4d29d9237c..d7fe7b600b 100644 --- a/mmengine/visualization/vis_backend.py +++ b/mmengine/visualization/vis_backend.py @@ -640,7 +640,7 @@ class MLFlowVisBackend(BaseVisBackend): Default to None. log_model (bool, optional): Whether to log the model. Default to True. """ - def __init__(self, + def __init__(self, save_dir: str, exp_name: Optional[str] = None, tags: Optional[dict] = None, @@ -692,16 +692,15 @@ def add_image(self, image: np.ndarray, step: int = 0, **kwargs) -> None: - """Record the image to mlflow. - - Args: - name (str): The image identifier. - image (np.ndarray): The image to be saved. The format - should be RGB. - step (int): Global step value to record. Default to 0. - """ - self._mlflow.log_image(name, image, step) + """Record the image to mlflow. + Args: + name (str): The image identifier. + image (np.ndarray): The image to be saved. The format + should be RGB. + step (int): Global step value to record. Default to 0. + """ + self._mlflow.log_image(name, image, step) @force_init_env def add_scalar(self, @@ -709,14 +708,14 @@ def add_scalar(self, value: Union[int, float, torch.Tensor, np.ndarray], step: int = 0, **kwargs) -> None: - """Record the scalar data to mlflow. - - Args: - name (str): The scalar identifier. - value (int, float, torch.Tensor, np.ndarray): Value to save. - step (int): Global step value to record. Default to 0. - """ - self._mlflow.log_metric(name, value, step) + """Record the scalar data to mlflow. + + Args: + name (str): The scalar identifier. + value (int, float, torch.Tensor, np.ndarray): Value to save. + step (int): Global step value to record. Default to 0. + """ + self._mlflow.log_metric(name, value, step) @force_init_env def add_scalars(self, @@ -724,21 +723,21 @@ def add_scalars(self, step: int = 0, file_path: Optional[str] = None, **kwargs) -> None: - """Record the scalar's data to mlflow. - - Args: - scalar_dict (dict): Key-value pair storing the tag and - corresponding values. - step (int): Global step value to record. Default to 0. - file_path (str, optional): Useless parameter. Just for - interface unification. Default to None. - """ - assert isinstance(scalar_dict, dict) - assert 'step' not in scalar_dict, 'Please set it directly ' \ - 'through the step parameter' - for key, value in scalar_dict.items(): - self.add_scalar(key, value, step) + """Record the scalar's data to mlflow. + + Args: + scalar_dict (dict): Key-value pair storing the tag and + corresponding values. + step (int): Global step value to record. Default to 0. + file_path (str, optional): Useless parameter. Just for + interface unification. Default to None. + """ + assert isinstance(scalar_dict, dict) + assert 'step' not in scalar_dict, 'Please set it directly ' \ + 'through the step parameter' + for key, value in scalar_dict.items(): + self.add_scalar(key, value, step) def close(self): """close an opened mlflow object.""" - pass # mlflow will close automatically \ No newline at end of file + pass # mlflow will close automatically From a02aeecf09f843c1ccd8e68224e34371bedbc276 Mon Sep 17 00:00:00 2001 From: sh0622-kim <66167254+sh0622-kim@users.noreply.github.com> Date: Sat, 14 Jan 2023 11:52:44 +0900 Subject: [PATCH 05/49] Fix lint errors --- mmengine/visualization/__init__.py | 2 +- mmengine/visualization/vis_backend.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/mmengine/visualization/__init__.py b/mmengine/visualization/__init__.py index db7b4e971d..fba0f0f1e4 100644 --- a/mmengine/visualization/__init__.py +++ b/mmengine/visualization/__init__.py @@ -6,5 +6,5 @@ __all__ = [ 'Visualizer', 'BaseVisBackend', 'LocalVisBackend', 'WandbVisBackend', - 'TensorboardVisBackend', 'MLflowVisBackend' + 'TensorboardVisBackend', 'MLFlowVisBackend' ] diff --git a/mmengine/visualization/vis_backend.py b/mmengine/visualization/vis_backend.py index d7fe7b600b..f71f474824 100644 --- a/mmengine/visualization/vis_backend.py +++ b/mmengine/visualization/vis_backend.py @@ -734,7 +734,7 @@ def add_scalars(self, """ assert isinstance(scalar_dict, dict) assert 'step' not in scalar_dict, 'Please set it directly ' \ - 'through the step parameter' + 'through the step parameter' for key, value in scalar_dict.items(): self.add_scalar(key, value, step) From 3e1f30db31e2461b4450d256b762bdce5cfcf7ad Mon Sep 17 00:00:00 2001 From: sh0622-kim <66167254+sh0622-kim@users.noreply.github.com> Date: Sat, 14 Jan 2023 11:59:39 +0900 Subject: [PATCH 06/49] Fix isot errors. --- mmengine/visualization/__init__.py | 8 ++++---- tests/test_visualizer/test_vis_backend.py | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/mmengine/visualization/__init__.py b/mmengine/visualization/__init__.py index fba0f0f1e4..973c1e5abb 100644 --- a/mmengine/visualization/__init__.py +++ b/mmengine/visualization/__init__.py @@ -1,10 +1,10 @@ # Copyright (c) OpenMMLab. All rights reserved. from .vis_backend import (BaseVisBackend, LocalVisBackend, - TensorboardVisBackend, WandbVisBackend, - MLFlowVisBackend) + MLFlowVisBackend, TensorboardVisBackend, + WandbVisBackend) from .visualizer import Visualizer __all__ = [ - 'Visualizer', 'BaseVisBackend', 'LocalVisBackend', 'WandbVisBackend', - 'TensorboardVisBackend', 'MLFlowVisBackend' + 'Visualizer', 'BaseVisBackend', 'LocalVisBackend', + 'WandbVisBackend', 'TensorboardVisBackend', 'MLFlowVisBackend' ] diff --git a/tests/test_visualizer/test_vis_backend.py b/tests/test_visualizer/test_vis_backend.py index 8481482a6b..b071a6a095 100644 --- a/tests/test_visualizer/test_vis_backend.py +++ b/tests/test_visualizer/test_vis_backend.py @@ -11,8 +11,8 @@ from mmengine import Config from mmengine.fileio import load from mmengine.registry import VISBACKENDS -from mmengine.visualization import (LocalVisBackend, TensorboardVisBackend, - WandbVisBackend, MLFlowVisBackend) +from mmengine.visualization import (LocalVisBackend, MLFlowVisBackend, + TensorboardVisBackend, WandbVisBackend) class TestLocalVisBackend: From b72259e8e4b77f38b6325bfa8bf429d038c342cd Mon Sep 17 00:00:00 2001 From: sh0622-kim <66167254+sh0622-kim@users.noreply.github.com> Date: Sat, 14 Jan 2023 12:02:24 +0900 Subject: [PATCH 07/49] Fix isot errors. --- mmengine/visualization/__init__.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/mmengine/visualization/__init__.py b/mmengine/visualization/__init__.py index 973c1e5abb..f50c09247c 100644 --- a/mmengine/visualization/__init__.py +++ b/mmengine/visualization/__init__.py @@ -1,7 +1,6 @@ # Copyright (c) OpenMMLab. All rights reserved. -from .vis_backend import (BaseVisBackend, LocalVisBackend, - MLFlowVisBackend, TensorboardVisBackend, - WandbVisBackend) +from .vis_backend import (BaseVisBackend, LocalVisBackend, MLFlowVisBackend, + TensorboardVisBackend, WandbVisBackend) from .visualizer import Visualizer __all__ = [ From 561e8ec9f3a27aa0f2a3c15525e977363c5bc82c Mon Sep 17 00:00:00 2001 From: sh0622-kim <66167254+sh0622-kim@users.noreply.github.com> Date: Sat, 14 Jan 2023 12:09:11 +0900 Subject: [PATCH 08/49] Fix yapf errors --- mmengine/visualization/__init__.py | 4 ++-- mmengine/visualization/vis_backend.py | 3 ++- tests/test_visualizer/test_vis_backend.py | 1 + 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/mmengine/visualization/__init__.py b/mmengine/visualization/__init__.py index f50c09247c..b66838377c 100644 --- a/mmengine/visualization/__init__.py +++ b/mmengine/visualization/__init__.py @@ -4,6 +4,6 @@ from .visualizer import Visualizer __all__ = [ - 'Visualizer', 'BaseVisBackend', 'LocalVisBackend', - 'WandbVisBackend', 'TensorboardVisBackend', 'MLFlowVisBackend' + 'Visualizer', 'BaseVisBackend', 'LocalVisBackend', 'WandbVisBackend', + 'TensorboardVisBackend', 'MLFlowVisBackend' ] diff --git a/mmengine/visualization/vis_backend.py b/mmengine/visualization/vis_backend.py index f71f474824..2d2d0d1229 100644 --- a/mmengine/visualization/vis_backend.py +++ b/mmengine/visualization/vis_backend.py @@ -640,6 +640,7 @@ class MLFlowVisBackend(BaseVisBackend): Default to None. log_model (bool, optional): Whether to log the model. Default to True. """ + def __init__(self, save_dir: str, exp_name: Optional[str] = None, @@ -740,4 +741,4 @@ def add_scalars(self, def close(self): """close an opened mlflow object.""" - pass # mlflow will close automatically + pass # mlflow will close automatically diff --git a/tests/test_visualizer/test_vis_backend.py b/tests/test_visualizer/test_vis_backend.py index b071a6a095..70cb9d2a9d 100644 --- a/tests/test_visualizer/test_vis_backend.py +++ b/tests/test_visualizer/test_vis_backend.py @@ -244,6 +244,7 @@ def test_close(self): class TestMLFlowVisBackend: + def test_init(self): MLFlowVisBackend('temp_dir') VISBACKENDS.build(dict(type='MLFlowVisBackend', save_dir='temp_dir')) From ac69e941ea4b2ab9d425edee43dc570e49d03e2f Mon Sep 17 00:00:00 2001 From: sh0622-kim <66167254+sh0622-kim@users.noreply.github.com> Date: Sat, 14 Jan 2023 12:19:15 +0900 Subject: [PATCH 09/49] fix test code --- tests/test_visualizer/test_vis_backend.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_visualizer/test_vis_backend.py b/tests/test_visualizer/test_vis_backend.py index 70cb9d2a9d..3e340c6eba 100644 --- a/tests/test_visualizer/test_vis_backend.py +++ b/tests/test_visualizer/test_vis_backend.py @@ -244,6 +244,7 @@ def test_close(self): class TestMLFlowVisBackend: + sys.modules['mlflow'] = MagicMock() def test_init(self): MLFlowVisBackend('temp_dir') From 9506a49955469d2b4cb2b8faff6ee9d282e501f6 Mon Sep 17 00:00:00 2001 From: sh0622-kim <66167254+sh0622-kim@users.noreply.github.com> Date: Sat, 14 Jan 2023 12:26:23 +0900 Subject: [PATCH 10/49] Fix test code --- tests/test_visualizer/test_vis_backend.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_visualizer/test_vis_backend.py b/tests/test_visualizer/test_vis_backend.py index 3e340c6eba..f1f1998586 100644 --- a/tests/test_visualizer/test_vis_backend.py +++ b/tests/test_visualizer/test_vis_backend.py @@ -257,7 +257,7 @@ def test_experiment(self): def test_add_config(self): cfg = Config(dict(a=1, b=dict(b1=[0, 1]))) - mlflow_vis_backend = MLFlowVisBackend('temp_dir', log_code_name='code') + mlflow_vis_backend = MLFlowVisBackend('temp_dir') mlflow_vis_backend.add_config(cfg) shutil.rmtree('temp_dir') From 8f33bce04559e471cefd20cf5c5b3a648bf9fdea Mon Sep 17 00:00:00 2001 From: sh0622-kim <66167254+sh0622-kim@users.noreply.github.com> Date: Mon, 16 Jan 2023 19:49:24 +0900 Subject: [PATCH 11/49] Update mmengine/visualization/vis_backend.py Co-authored-by: Mashiro <57566630+HAOCHENYE@users.noreply.github.com> --- mmengine/visualization/vis_backend.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/mmengine/visualization/vis_backend.py b/mmengine/visualization/vis_backend.py index 2d2d0d1229..13ac8fa1a5 100644 --- a/mmengine/visualization/vis_backend.py +++ b/mmengine/visualization/vis_backend.py @@ -661,8 +661,7 @@ def _init_env(self): try: import mlflow except ImportError: - raise ImportError('Please install mlflow to use ' - 'MLFlowVisBackend.') # type: ignore + raise ImportError('Please run "pip install mlflow" to install mlflow') # type: ignore self._mlflow = mlflow if self._exp_name is not None: From ac4489c1ef5283b22745f12b5d5ad85d45eeca39 Mon Sep 17 00:00:00 2001 From: sh0622-kim <66167254+sh0622-kim@users.noreply.github.com> Date: Sat, 21 Jan 2023 00:17:16 +0900 Subject: [PATCH 12/49] Add MLFlow detail information --- mmengine/hooks/logger_hook.py | 3 ++ mmengine/visualization/vis_backend.py | 58 +++++++++++++++++++++++---- 2 files changed, 54 insertions(+), 7 deletions(-) diff --git a/mmengine/hooks/logger_hook.py b/mmengine/hooks/logger_hook.py index c14a8e7a44..7bdd5a3ef5 100644 --- a/mmengine/hooks/logger_hook.py +++ b/mmengine/hooks/logger_hook.py @@ -291,6 +291,9 @@ def after_run(self, runner) -> None: runner (Runner): The runner of the training/testing/validation process. """ + # close the visualizer + runner.visualizer.close() + # copy or upload logs to self.out_dir if self.out_dir is None: return diff --git a/mmengine/visualization/vis_backend.py b/mmengine/visualization/vis_backend.py index 13ac8fa1a5..2892af5046 100644 --- a/mmengine/visualization/vis_backend.py +++ b/mmengine/visualization/vis_backend.py @@ -3,6 +3,7 @@ import functools import os import os.path as osp +import sys import warnings from abc import ABCMeta, abstractmethod from typing import Any, Callable, Optional, Sequence, Union @@ -644,14 +645,18 @@ class MLFlowVisBackend(BaseVisBackend): def __init__(self, save_dir: str, exp_name: Optional[str] = None, + run_name: Optional[str] = None, tags: Optional[dict] = None, params: Optional[dict] = None, + tracking_uri: Optional[str] = None, log_model: bool = True): super().__init__(save_dir) self._exp_name = exp_name + self._run_name = run_name self._tags = tags self._params = params self._log_model = log_model + self._tracking_uri = tracking_uri def _init_env(self): """Setup env for MLFlow.""" @@ -661,11 +666,23 @@ def _init_env(self): try: import mlflow except ImportError: - raise ImportError('Please run "pip install mlflow" to install mlflow') # type: ignore + raise ImportError( + 'Please run "pip install mlflow" to install mlflow' + ) # type: ignore self._mlflow = mlflow - if self._exp_name is not None: - self._mlflow.set_experiment(self._exp_name) + if self._tracking_uri is not None: + self._mlflow.set_tracking_uri(self._tracking_uri) + else: + self._mlflow.set_tracking_uri(f'file://{self._save_dir}') + + if self._mlflow.get_experiment_by_name(self._exp_name) is None: + self._mlflow.create_experiment(self._exp_name) + + self._mlflow.set_experiment(self._exp_name) + + if self._run_name is not None: + self._mlflow.set_tag('mlflow.runName', self._run_name) if self._tags is not None: self._mlflow.set_tags(self._tags) if self._params is not None: @@ -684,7 +701,8 @@ def add_config(self, config: Config, **kwargs) -> None: Args: config (Config): The Config object """ - return super().add_config(config, **kwargs) + self.cfg = config + self._mlflow.log_params(self._flatten(self.cfg)) @force_init_env def add_image(self, @@ -738,6 +756,32 @@ def add_scalars(self, for key, value in scalar_dict.items(): self.add_scalar(key, value, step) - def close(self): - """close an opened mlflow object.""" - pass # mlflow will close automatically + def close(self) -> None: + """Close the mlflow.""" + work_dir = self.cfg.work_dir + + for file in os.listdir(work_dir): + if file.endswith('.pth'): + self._mlflow.log_artifact(os.path.join(work_dir, file)) + elif file == 'last_checkpoint': + self._mlflow.log_artifact(os.path.join(work_dir, file)) + elif file.startswith(os.path.basename(os.path.normpath(work_dir))): + self._mlflow.log_artifact(os.path.join(work_dir, file)) + + if hasattr(self, '_mlflow'): + self._mlflow.end_run() + + def _flatten(self, d, parent_key='', sep='.') -> dict: + if sys.version_info.major == 3 and sys.version_info.minor >= 10: + from collections.abc import MutableMapping + else: + from collections import MutableMapping + + items = [] + for k, v in d.items(): + new_key = parent_key + sep + k if parent_key else k + if isinstance(v, MutableMapping): + items.extend(self._flatten(v, new_key, sep=sep).items()) + else: + items.append((new_key, v)) + return dict(items) From 76dbe6f5072d2930ff345429a2bf1a9e93ef5bf5 Mon Sep 17 00:00:00 2001 From: sh0622-kim <66167254+sh0622-kim@users.noreply.github.com> Date: Sat, 21 Jan 2023 00:21:31 +0900 Subject: [PATCH 13/49] Add MLFlowBackend --- docs/en/api/visualization.rst | 1 + docs/zh_cn/api/visualization.rst | 1 + 2 files changed, 2 insertions(+) diff --git a/docs/en/api/visualization.rst b/docs/en/api/visualization.rst index 5265dc6edb..910e4d9737 100644 --- a/docs/en/api/visualization.rst +++ b/docs/en/api/visualization.rst @@ -31,5 +31,6 @@ visualization Backend BaseVisBackend LocalVisBackend + MLFlowVisBackend TensorboardVisBackend WandbVisBackend diff --git a/docs/zh_cn/api/visualization.rst b/docs/zh_cn/api/visualization.rst index 5265dc6edb..910e4d9737 100644 --- a/docs/zh_cn/api/visualization.rst +++ b/docs/zh_cn/api/visualization.rst @@ -31,5 +31,6 @@ visualization Backend BaseVisBackend LocalVisBackend + MLFlowVisBackend TensorboardVisBackend WandbVisBackend From c8de76fe9b5d65630dfed7572a0c39b70e1f495a Mon Sep 17 00:00:00 2001 From: sh0622-kim <66167254+sh0622-kim@users.noreply.github.com> Date: Sat, 21 Jan 2023 00:28:44 +0900 Subject: [PATCH 14/49] Fix mypy error --- mmengine/visualization/vis_backend.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mmengine/visualization/vis_backend.py b/mmengine/visualization/vis_backend.py index 2892af5046..0b7ebdc838 100644 --- a/mmengine/visualization/vis_backend.py +++ b/mmengine/visualization/vis_backend.py @@ -777,7 +777,7 @@ def _flatten(self, d, parent_key='', sep='.') -> dict: else: from collections import MutableMapping - items = [] + items:list[str] = [] for k, v in d.items(): new_key = parent_key + sep + k if parent_key else k if isinstance(v, MutableMapping): From 88d271c738dad1b6062459c89f964c1f28af0e96 Mon Sep 17 00:00:00 2001 From: sh0622-kim <66167254+sh0622-kim@users.noreply.github.com> Date: Sat, 21 Jan 2023 00:52:29 +0900 Subject: [PATCH 15/49] Fix mypy error --- mmengine/visualization/vis_backend.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mmengine/visualization/vis_backend.py b/mmengine/visualization/vis_backend.py index 0b7ebdc838..552ff73440 100644 --- a/mmengine/visualization/vis_backend.py +++ b/mmengine/visualization/vis_backend.py @@ -777,7 +777,7 @@ def _flatten(self, d, parent_key='', sep='.') -> dict: else: from collections import MutableMapping - items:list[str] = [] + items:Any = [] for k, v in d.items(): new_key = parent_key + sep + k if parent_key else k if isinstance(v, MutableMapping): From ed6c147b89626c7066eace5b08395fbda25b4ef0 Mon Sep 17 00:00:00 2001 From: sh0622-kim <66167254+sh0622-kim@users.noreply.github.com> Date: Sat, 21 Jan 2023 00:56:09 +0900 Subject: [PATCH 16/49] Fix flake8 error --- mmengine/visualization/vis_backend.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mmengine/visualization/vis_backend.py b/mmengine/visualization/vis_backend.py index 552ff73440..0301bd35d9 100644 --- a/mmengine/visualization/vis_backend.py +++ b/mmengine/visualization/vis_backend.py @@ -777,7 +777,7 @@ def _flatten(self, d, parent_key='', sep='.') -> dict: else: from collections import MutableMapping - items:Any = [] + items: Any = [] for k, v in d.items(): new_key = parent_key + sep + k if parent_key else k if isinstance(v, MutableMapping): From a7eb0b90ca27055a9cce4bdfaf7d85ff56060397 Mon Sep 17 00:00:00 2001 From: sh0622-kim <66167254+sh0622-kim@users.noreply.github.com> Date: Sat, 21 Jan 2023 01:01:11 +0900 Subject: [PATCH 17/49] Fix pyupgrade error --- mmengine/visualization/vis_backend.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/mmengine/visualization/vis_backend.py b/mmengine/visualization/vis_backend.py index 0301bd35d9..e7e4739e5d 100644 --- a/mmengine/visualization/vis_backend.py +++ b/mmengine/visualization/vis_backend.py @@ -772,10 +772,7 @@ def close(self) -> None: self._mlflow.end_run() def _flatten(self, d, parent_key='', sep='.') -> dict: - if sys.version_info.major == 3 and sys.version_info.minor >= 10: - from collections.abc import MutableMapping - else: - from collections import MutableMapping + from collections.abc import MutableMapping items: Any = [] for k, v in d.items(): From b6ac947b33f8f0aa70b218a002cd4d96eb587524 Mon Sep 17 00:00:00 2001 From: sh0622-kim <66167254+sh0622-kim@users.noreply.github.com> Date: Sat, 21 Jan 2023 01:03:13 +0900 Subject: [PATCH 18/49] Fix flake8 error --- mmengine/visualization/vis_backend.py | 1 - 1 file changed, 1 deletion(-) diff --git a/mmengine/visualization/vis_backend.py b/mmengine/visualization/vis_backend.py index e7e4739e5d..4a6981b25b 100644 --- a/mmengine/visualization/vis_backend.py +++ b/mmengine/visualization/vis_backend.py @@ -3,7 +3,6 @@ import functools import os import os.path as osp -import sys import warnings from abc import ABCMeta, abstractmethod from typing import Any, Callable, Optional, Sequence, Union From 9f6ca43bb17de20e3f8b7cf2912ce9a9c831846a Mon Sep 17 00:00:00 2001 From: sh0622-kim <66167254+sh0622-kim@users.noreply.github.com> Date: Sat, 21 Jan 2023 01:18:15 +0900 Subject: [PATCH 19/49] Fix test error --- tests/test_visualizer/test_vis_backend.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/test_visualizer/test_vis_backend.py b/tests/test_visualizer/test_vis_backend.py index f1f1998586..ea0b7961ca 100644 --- a/tests/test_visualizer/test_vis_backend.py +++ b/tests/test_visualizer/test_vis_backend.py @@ -286,7 +286,9 @@ def test_add_scalars(self): shutil.rmtree('temp_dir') def test_close(self): + cfg = Config(dict(work_dir='temp_dir')) mlflow_vis_backend = MLFlowVisBackend('temp_dir') mlflow_vis_backend._init_env() + mlflow_vis_backend.add_config(cfg) mlflow_vis_backend.close() shutil.rmtree('temp_dir') From 771f47a895b2e2707d0f8dedeb09f664e4c295b5 Mon Sep 17 00:00:00 2001 From: sh0622-kim <66167254+sh0622-kim@users.noreply.github.com> Date: Sat, 21 Jan 2023 01:39:11 +0900 Subject: [PATCH 20/49] add comment --- mmengine/visualization/vis_backend.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mmengine/visualization/vis_backend.py b/mmengine/visualization/vis_backend.py index 4a6981b25b..fb4473fe29 100644 --- a/mmengine/visualization/vis_backend.py +++ b/mmengine/visualization/vis_backend.py @@ -634,10 +634,12 @@ class MLFlowVisBackend(BaseVisBackend): save_dir (str): The root directory to save the files produced by the backend. exp_name (str, optional): The experiment name. Default to None. + run_name (str, optional): The run name. Default to None. tags (dict, optional): The tags to be added to the experiment. Default to None. params (dict, optional): The params to be added to the experiment. Default to None. + tracking_uri (str, optional): The tracking uri. Default to None. log_model (bool, optional): Whether to log the model. Default to True. """ From 635b0cbf5f52eb2b4892adbd8c11b8fb3da427f1 Mon Sep 17 00:00:00 2001 From: sh0622-kim <66167254+sh0622-kim@users.noreply.github.com> Date: Thu, 26 Jan 2023 20:22:31 +0900 Subject: [PATCH 21/49] Update mmengine/visualization/vis_backend.py Co-authored-by: Zaida Zhou <58739961+zhouzaida@users.noreply.github.com> --- mmengine/visualization/vis_backend.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mmengine/visualization/vis_backend.py b/mmengine/visualization/vis_backend.py index fb4473fe29..f5aa1a9758 100644 --- a/mmengine/visualization/vis_backend.py +++ b/mmengine/visualization/vis_backend.py @@ -765,9 +765,9 @@ def close(self) -> None: if file.endswith('.pth'): self._mlflow.log_artifact(os.path.join(work_dir, file)) elif file == 'last_checkpoint': - self._mlflow.log_artifact(os.path.join(work_dir, file)) - elif file.startswith(os.path.basename(os.path.normpath(work_dir))): - self._mlflow.log_artifact(os.path.join(work_dir, file)) + self._mlflow.log_artifact(osp.join(work_dir, file)) + elif file.startswith(osp.basename(osp.normpath(work_dir))): + self._mlflow.log_artifact(osp.join(work_dir, file)) if hasattr(self, '_mlflow'): self._mlflow.end_run() From 4556475e71e1e9120109cd89f627bb440e93fe33 Mon Sep 17 00:00:00 2001 From: sh0622-kim <66167254+sh0622-kim@users.noreply.github.com> Date: Thu, 26 Jan 2023 21:51:11 +0900 Subject: [PATCH 22/49] Add log artifact parameter Fix exception related to image log --- mmengine/visualization/vis_backend.py | 32 +++++++++++++-------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/mmengine/visualization/vis_backend.py b/mmengine/visualization/vis_backend.py index f5aa1a9758..b1dc077fd5 100644 --- a/mmengine/visualization/vis_backend.py +++ b/mmengine/visualization/vis_backend.py @@ -5,6 +5,7 @@ import os.path as osp import warnings from abc import ABCMeta, abstractmethod +from collections.abc import MutableMapping from typing import Any, Callable, Optional, Sequence, Union import cv2 @@ -640,7 +641,7 @@ class MLFlowVisBackend(BaseVisBackend): params (dict, optional): The params to be added to the experiment. Default to None. tracking_uri (str, optional): The tracking uri. Default to None. - log_model (bool, optional): Whether to log the model. Default to True. + log_artifact (bool, optional): Whether to log the artifact. Default to False. """ def __init__(self, @@ -650,14 +651,14 @@ def __init__(self, tags: Optional[dict] = None, params: Optional[dict] = None, tracking_uri: Optional[str] = None, - log_model: bool = True): + log_artifact: bool = False): super().__init__(save_dir) self._exp_name = exp_name self._run_name = run_name self._tags = tags self._params = params - self._log_model = log_model self._tracking_uri = tracking_uri + self._log_artifact = log_artifact def _init_env(self): """Setup env for MLFlow.""" @@ -719,7 +720,7 @@ def add_image(self, should be RGB. step (int): Global step value to record. Default to 0. """ - self._mlflow.log_image(name, image, step) + self._mlflow.log_image(image, name) @force_init_env def add_scalar(self, @@ -754,27 +755,26 @@ def add_scalars(self, assert isinstance(scalar_dict, dict) assert 'step' not in scalar_dict, 'Please set it directly ' \ 'through the step parameter' - for key, value in scalar_dict.items(): - self.add_scalar(key, value, step) + self._mlflow.log_metrics(scalar_dict, step) def close(self) -> None: """Close the mlflow.""" - work_dir = self.cfg.work_dir + if self._log_artifact: + work_dir = self.cfg.work_dir - for file in os.listdir(work_dir): - if file.endswith('.pth'): - self._mlflow.log_artifact(os.path.join(work_dir, file)) - elif file == 'last_checkpoint': - self._mlflow.log_artifact(osp.join(work_dir, file)) - elif file.startswith(osp.basename(osp.normpath(work_dir))): - self._mlflow.log_artifact(osp.join(work_dir, file)) + for file in os.listdir(work_dir): + if file.endswith('.pth'): + self._mlflow.log_artifact(os.path.join(work_dir, file)) + elif file == 'last_checkpoint': + self._mlflow.log_artifact(osp.join(work_dir, file)) + elif file.startswith(osp.basename(osp.normpath(work_dir))): + self._mlflow.log_artifact(osp.join(work_dir, file)) if hasattr(self, '_mlflow'): self._mlflow.end_run() def _flatten(self, d, parent_key='', sep='.') -> dict: - from collections.abc import MutableMapping - + """Flatten the dict.""" items: Any = [] for k, v in d.items(): new_key = parent_key + sep + k if parent_key else k From 5e7f3c5bceef8fd8294bc944474e42338519c442 Mon Sep 17 00:00:00 2001 From: sh0622-kim <66167254+sh0622-kim@users.noreply.github.com> Date: Thu, 26 Jan 2023 21:54:04 +0900 Subject: [PATCH 23/49] Fix flake8 error --- mmengine/visualization/vis_backend.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mmengine/visualization/vis_backend.py b/mmengine/visualization/vis_backend.py index b1dc077fd5..57a8291894 100644 --- a/mmengine/visualization/vis_backend.py +++ b/mmengine/visualization/vis_backend.py @@ -641,7 +641,8 @@ class MLFlowVisBackend(BaseVisBackend): params (dict, optional): The params to be added to the experiment. Default to None. tracking_uri (str, optional): The tracking uri. Default to None. - log_artifact (bool, optional): Whether to log the artifact. Default to False. + log_artifact (bool, optional): Whether to log the artifact. + Default to False. """ def __init__(self, From c4db6fe347a7338655cd90c73aaa5a0e5e920c11 Mon Sep 17 00:00:00 2001 From: sh0622-kim <66167254+sh0622-kim@users.noreply.github.com> Date: Fri, 27 Jan 2023 22:07:32 +0900 Subject: [PATCH 24/49] array -> dict --- mmengine/visualization/vis_backend.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/mmengine/visualization/vis_backend.py b/mmengine/visualization/vis_backend.py index 57a8291894..7c1216d428 100644 --- a/mmengine/visualization/vis_backend.py +++ b/mmengine/visualization/vis_backend.py @@ -776,11 +776,11 @@ def close(self) -> None: def _flatten(self, d, parent_key='', sep='.') -> dict: """Flatten the dict.""" - items: Any = [] + items = dict() for k, v in d.items(): new_key = parent_key + sep + k if parent_key else k if isinstance(v, MutableMapping): - items.extend(self._flatten(v, new_key, sep=sep).items()) + items.update(self._flatten(v, new_key, sep=sep)) else: - items.append((new_key, v)) - return dict(items) + items[new_key] = v + return items From 460e389af9fecaae7214503ebb204b0a68d970dd Mon Sep 17 00:00:00 2001 From: sh0622-kim <66167254+sh0622-kim@users.noreply.github.com> Date: Sat, 28 Jan 2023 20:47:45 +0900 Subject: [PATCH 25/49] config.py is saved unconditionally. It also handles dictionaries inside list. --- mmengine/visualization/vis_backend.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/mmengine/visualization/vis_backend.py b/mmengine/visualization/vis_backend.py index 7c1216d428..f0fba2b4ce 100644 --- a/mmengine/visualization/vis_backend.py +++ b/mmengine/visualization/vis_backend.py @@ -706,6 +706,7 @@ def add_config(self, config: Config, **kwargs) -> None: """ self.cfg = config self._mlflow.log_params(self._flatten(self.cfg)) + self._mlflow.log_text(self.cfg.pretty_text, 'config.py') @force_init_env def add_image(self, @@ -781,6 +782,13 @@ def _flatten(self, d, parent_key='', sep='.') -> dict: new_key = parent_key + sep + k if parent_key else k if isinstance(v, MutableMapping): items.update(self._flatten(v, new_key, sep=sep)) + elif isinstance(v, list): + if any(isinstance(x, dict) for x in v): + for i, x in enumerate(v): + items.update( + self._flatten(x, new_key + sep + str(i), sep=sep)) + else: + items[new_key] = v else: items[new_key] = v return items From ebce20d7eda0850c71d86fa18b77438c071b5a8d Mon Sep 17 00:00:00 2001 From: sh0622-kim <66167254+sh0622-kim@users.noreply.github.com> Date: Sun, 29 Jan 2023 12:58:54 +0900 Subject: [PATCH 26/49] Save all files in work_dir. --- mmengine/visualization/vis_backend.py | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/mmengine/visualization/vis_backend.py b/mmengine/visualization/vis_backend.py index f0fba2b4ce..3902da900e 100644 --- a/mmengine/visualization/vis_backend.py +++ b/mmengine/visualization/vis_backend.py @@ -762,15 +762,7 @@ def add_scalars(self, def close(self) -> None: """Close the mlflow.""" if self._log_artifact: - work_dir = self.cfg.work_dir - - for file in os.listdir(work_dir): - if file.endswith('.pth'): - self._mlflow.log_artifact(os.path.join(work_dir, file)) - elif file == 'last_checkpoint': - self._mlflow.log_artifact(osp.join(work_dir, file)) - elif file.startswith(osp.basename(osp.normpath(work_dir))): - self._mlflow.log_artifact(osp.join(work_dir, file)) + self._mlflow.log_artifacts(self.cfg.work_dir) if hasattr(self, '_mlflow'): self._mlflow.end_run() From f1e6d9f3c5ca240ac3bba75fb0b3a1c40eee5fe8 Mon Sep 17 00:00:00 2001 From: sung-hwa kim <66167254+sh0622-kim@users.noreply.github.com> Date: Sun, 29 Jan 2023 21:36:26 +0900 Subject: [PATCH 27/49] Update mmengine/visualization/vis_backend.py Co-authored-by: Zaida Zhou <58739961+zhouzaida@users.noreply.github.com> --- mmengine/visualization/vis_backend.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mmengine/visualization/vis_backend.py b/mmengine/visualization/vis_backend.py index 3902da900e..f5d8326063 100644 --- a/mmengine/visualization/vis_backend.py +++ b/mmengine/visualization/vis_backend.py @@ -622,11 +622,12 @@ class MLFlowVisBackend(BaseVisBackend): Examples: >>> from mmengine.visualization import MLFlowVisBackend + >>> from mmengine import Config >>> import numpy as np >>> vis_backend = MLFlowVisBackend(save_dir='temp_dir') >>> img = np.random.randint(0, 256, size=(10, 10, 3)) >>> vis_backend.add_image('img', img) - >>> vis_backend.add_scaler('mAP', 0.6) + >>> vis_backend.add_scalar('mAP', 0.6) >>> vis_backend.add_scalars({'loss': 0.1,'acc':0.8}) >>> cfg = Config(dict(a=1, b=dict(b1=[0, 1]))) >>> vis_backend.add_config(cfg) From ba1fd8ab44f9f7a8505314533c688b8ede49d9a2 Mon Sep 17 00:00:00 2001 From: sh0622-kim <66167254+sh0622-kim@users.noreply.github.com> Date: Sun, 29 Jan 2023 21:39:59 +0900 Subject: [PATCH 28/49] change to correct name (MLFlow -> MLflow) --- docs/en/api/visualization.rst | 2 +- docs/zh_cn/api/visualization.rst | 2 +- mmengine/visualization/__init__.py | 4 ++-- mmengine/visualization/vis_backend.py | 12 ++++++------ tests/test_visualizer/test_vis_backend.py | 20 ++++++++++---------- 5 files changed, 20 insertions(+), 20 deletions(-) diff --git a/docs/en/api/visualization.rst b/docs/en/api/visualization.rst index 910e4d9737..10362a9beb 100644 --- a/docs/en/api/visualization.rst +++ b/docs/en/api/visualization.rst @@ -31,6 +31,6 @@ visualization Backend BaseVisBackend LocalVisBackend - MLFlowVisBackend + MLflowVisBackend TensorboardVisBackend WandbVisBackend diff --git a/docs/zh_cn/api/visualization.rst b/docs/zh_cn/api/visualization.rst index 910e4d9737..10362a9beb 100644 --- a/docs/zh_cn/api/visualization.rst +++ b/docs/zh_cn/api/visualization.rst @@ -31,6 +31,6 @@ visualization Backend BaseVisBackend LocalVisBackend - MLFlowVisBackend + MLflowVisBackend TensorboardVisBackend WandbVisBackend diff --git a/mmengine/visualization/__init__.py b/mmengine/visualization/__init__.py index b66838377c..866fbedda9 100644 --- a/mmengine/visualization/__init__.py +++ b/mmengine/visualization/__init__.py @@ -1,9 +1,9 @@ # Copyright (c) OpenMMLab. All rights reserved. -from .vis_backend import (BaseVisBackend, LocalVisBackend, MLFlowVisBackend, +from .vis_backend import (BaseVisBackend, LocalVisBackend, MLflowVisBackend, TensorboardVisBackend, WandbVisBackend) from .visualizer import Visualizer __all__ = [ 'Visualizer', 'BaseVisBackend', 'LocalVisBackend', 'WandbVisBackend', - 'TensorboardVisBackend', 'MLFlowVisBackend' + 'TensorboardVisBackend', 'MLflowVisBackend' ] diff --git a/mmengine/visualization/vis_backend.py b/mmengine/visualization/vis_backend.py index f5d8326063..739d9d1b39 100644 --- a/mmengine/visualization/vis_backend.py +++ b/mmengine/visualization/vis_backend.py @@ -614,17 +614,17 @@ def close(self): @VISBACKENDS.register_module() -class MLFlowVisBackend(BaseVisBackend): - """MLFlow visualization backend class. +class MLflowVisBackend(BaseVisBackend): + """MLflow visualization backend class. It can write images, config, scalars, etc. to a mlflow file. Examples: - >>> from mmengine.visualization import MLFlowVisBackend + >>> from mmengine.visualization import MLflowVisBackend >>> from mmengine import Config >>> import numpy as np - >>> vis_backend = MLFlowVisBackend(save_dir='temp_dir') + >>> vis_backend = MLflowVisBackend(save_dir='temp_dir') >>> img = np.random.randint(0, 256, size=(10, 10, 3)) >>> vis_backend.add_image('img', img) >>> vis_backend.add_scalar('mAP', 0.6) @@ -663,7 +663,7 @@ def __init__(self, self._log_artifact = log_artifact def _init_env(self): - """Setup env for MLFlow.""" + """Setup env for MLflow.""" if not os.path.exists(self._save_dir): os.makedirs(self._save_dir, exist_ok=True) # type: ignore @@ -695,7 +695,7 @@ def _init_env(self): @property # type: ignore @force_init_env def experiment(self): - """Return MLFlow object.""" + """Return MLflow object.""" return self._mlflow @force_init_env diff --git a/tests/test_visualizer/test_vis_backend.py b/tests/test_visualizer/test_vis_backend.py index ea0b7961ca..6dcf18c349 100644 --- a/tests/test_visualizer/test_vis_backend.py +++ b/tests/test_visualizer/test_vis_backend.py @@ -11,7 +11,7 @@ from mmengine import Config from mmengine.fileio import load from mmengine.registry import VISBACKENDS -from mmengine.visualization import (LocalVisBackend, MLFlowVisBackend, +from mmengine.visualization import (LocalVisBackend, MLflowVisBackend, TensorboardVisBackend, WandbVisBackend) @@ -243,33 +243,33 @@ def test_close(self): shutil.rmtree('temp_dir') -class TestMLFlowVisBackend: +class TestMLflowVisBackend: sys.modules['mlflow'] = MagicMock() def test_init(self): - MLFlowVisBackend('temp_dir') - VISBACKENDS.build(dict(type='MLFlowVisBackend', save_dir='temp_dir')) + MLflowVisBackend('temp_dir') + VISBACKENDS.build(dict(type='MLflowVisBackend', save_dir='temp_dir')) def test_experiment(self): - mlflow_vis_backend = MLFlowVisBackend('temp_dir') + mlflow_vis_backend = MLflowVisBackend('temp_dir') assert mlflow_vis_backend.experiment == mlflow_vis_backend._mlflow shutil.rmtree('temp_dir') def test_add_config(self): cfg = Config(dict(a=1, b=dict(b1=[0, 1]))) - mlflow_vis_backend = MLFlowVisBackend('temp_dir') + mlflow_vis_backend = MLflowVisBackend('temp_dir') mlflow_vis_backend.add_config(cfg) shutil.rmtree('temp_dir') def test_add_image(self): image = np.random.randint(0, 256, size=(10, 10, 3)).astype(np.uint8) - mlflow_vis_backend = MLFlowVisBackend('temp_dir') + mlflow_vis_backend = MLflowVisBackend('temp_dir') mlflow_vis_backend.add_image('img', image) mlflow_vis_backend.add_image('img', image) shutil.rmtree('temp_dir') def test_add_scalar(self): - mlflow_vis_backend = MLFlowVisBackend('temp_dir') + mlflow_vis_backend = MLflowVisBackend('temp_dir') mlflow_vis_backend.add_scalar('map', 0.9) # test append mode mlflow_vis_backend.add_scalar('map', 0.9) @@ -277,7 +277,7 @@ def test_add_scalar(self): shutil.rmtree('temp_dir') def test_add_scalars(self): - mlflow_vis_backend = MLFlowVisBackend('temp_dir') + mlflow_vis_backend = MLflowVisBackend('temp_dir') input_dict = {'map': 0.7, 'acc': 0.9} mlflow_vis_backend.add_scalars(input_dict) # test append mode @@ -287,7 +287,7 @@ def test_add_scalars(self): def test_close(self): cfg = Config(dict(work_dir='temp_dir')) - mlflow_vis_backend = MLFlowVisBackend('temp_dir') + mlflow_vis_backend = MLflowVisBackend('temp_dir') mlflow_vis_backend._init_env() mlflow_vis_backend.add_config(cfg) mlflow_vis_backend.close() From 8c39b631872613ee0bf151ff0a416c9d8f1270e8 Mon Sep 17 00:00:00 2001 From: sh0622-kim <66167254+sh0622-kim@users.noreply.github.com> Date: Sun, 29 Jan 2023 21:45:22 +0900 Subject: [PATCH 29/49] Add tests package --- requirements/tests.txt | 3 ++- tests/test_visualizer/test_vis_backend.py | 1 - 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements/tests.txt b/requirements/tests.txt index debf7eb171..f698f99e18 100644 --- a/requirements/tests.txt +++ b/requirements/tests.txt @@ -1,4 +1,5 @@ coverage lmdb +mlflow parameterized -pytest +pytest \ No newline at end of file diff --git a/tests/test_visualizer/test_vis_backend.py b/tests/test_visualizer/test_vis_backend.py index 6dcf18c349..a21da8a12e 100644 --- a/tests/test_visualizer/test_vis_backend.py +++ b/tests/test_visualizer/test_vis_backend.py @@ -244,7 +244,6 @@ def test_close(self): class TestMLflowVisBackend: - sys.modules['mlflow'] = MagicMock() def test_init(self): MLflowVisBackend('temp_dir') From 7287554d66bf9d6155b64518e363e73ae582e819 Mon Sep 17 00:00:00 2001 From: sh0622-kim <66167254+sh0622-kim@users.noreply.github.com> Date: Sun, 29 Jan 2023 21:48:23 +0900 Subject: [PATCH 30/49] fix ut error --- requirements/tests.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements/tests.txt b/requirements/tests.txt index f698f99e18..b5eb93fbc8 100644 --- a/requirements/tests.txt +++ b/requirements/tests.txt @@ -2,4 +2,4 @@ coverage lmdb mlflow parameterized -pytest \ No newline at end of file +pytest From 6a38cff8a0c2c1df97a0231025f2a1f28eb48e62 Mon Sep 17 00:00:00 2001 From: sh0622-kim <66167254+sh0622-kim@users.noreply.github.com> Date: Sun, 29 Jan 2023 22:51:31 +0900 Subject: [PATCH 31/49] Change the default experiment name Add log_image extension --- mmengine/visualization/vis_backend.py | 9 +++++---- tests/test_visualizer/test_vis_backend.py | 2 -- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/mmengine/visualization/vis_backend.py b/mmengine/visualization/vis_backend.py index 739d9d1b39..80214a3579 100644 --- a/mmengine/visualization/vis_backend.py +++ b/mmengine/visualization/vis_backend.py @@ -635,7 +635,7 @@ class MLflowVisBackend(BaseVisBackend): Args: save_dir (str): The root directory to save the files produced by the backend. - exp_name (str, optional): The experiment name. Default to None. + exp_name (str, optional): The experiment name. Default to 'Default'. run_name (str, optional): The run name. Default to None. tags (dict, optional): The tags to be added to the experiment. Default to None. @@ -648,7 +648,7 @@ class MLflowVisBackend(BaseVisBackend): def __init__(self, save_dir: str, - exp_name: Optional[str] = None, + exp_name: Optional[str] = 'Default', run_name: Optional[str] = None, tags: Optional[dict] = None, params: Optional[dict] = None, @@ -678,7 +678,8 @@ def _init_env(self): if self._tracking_uri is not None: self._mlflow.set_tracking_uri(self._tracking_uri) else: - self._mlflow.set_tracking_uri(f'file://{self._save_dir}') + self._mlflow.set_tracking_uri( + f'file://{os.path.abspath(self._save_dir)}') if self._mlflow.get_experiment_by_name(self._exp_name) is None: self._mlflow.create_experiment(self._exp_name) @@ -723,7 +724,7 @@ def add_image(self, should be RGB. step (int): Global step value to record. Default to 0. """ - self._mlflow.log_image(image, name) + self._mlflow.log_image(image, f'{name}.png') @force_init_env def add_scalar(self, diff --git a/tests/test_visualizer/test_vis_backend.py b/tests/test_visualizer/test_vis_backend.py index a21da8a12e..c29fb9a38b 100644 --- a/tests/test_visualizer/test_vis_backend.py +++ b/tests/test_visualizer/test_vis_backend.py @@ -264,7 +264,6 @@ def test_add_image(self): image = np.random.randint(0, 256, size=(10, 10, 3)).astype(np.uint8) mlflow_vis_backend = MLflowVisBackend('temp_dir') mlflow_vis_backend.add_image('img', image) - mlflow_vis_backend.add_image('img', image) shutil.rmtree('temp_dir') def test_add_scalar(self): @@ -281,7 +280,6 @@ def test_add_scalars(self): mlflow_vis_backend.add_scalars(input_dict) # test append mode mlflow_vis_backend.add_scalars({'map': 0.8, 'acc': 0.8}) - mlflow_vis_backend.add_scalars({'map': [0.8], 'acc': 0.8}) shutil.rmtree('temp_dir') def test_close(self): From bf43063b1c7232f0faed341b0a6fb4585ac7c7c8 Mon Sep 17 00:00:00 2001 From: sh0622-kim <66167254+sh0622-kim@users.noreply.github.com> Date: Sun, 29 Jan 2023 23:05:57 +0900 Subject: [PATCH 32/49] fix ut error --- tests/test_visualizer/test_vis_backend.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/tests/test_visualizer/test_vis_backend.py b/tests/test_visualizer/test_vis_backend.py index c29fb9a38b..5fc0d127a9 100644 --- a/tests/test_visualizer/test_vis_backend.py +++ b/tests/test_visualizer/test_vis_backend.py @@ -252,19 +252,16 @@ def test_init(self): def test_experiment(self): mlflow_vis_backend = MLflowVisBackend('temp_dir') assert mlflow_vis_backend.experiment == mlflow_vis_backend._mlflow - shutil.rmtree('temp_dir') def test_add_config(self): cfg = Config(dict(a=1, b=dict(b1=[0, 1]))) mlflow_vis_backend = MLflowVisBackend('temp_dir') mlflow_vis_backend.add_config(cfg) - shutil.rmtree('temp_dir') def test_add_image(self): image = np.random.randint(0, 256, size=(10, 10, 3)).astype(np.uint8) mlflow_vis_backend = MLflowVisBackend('temp_dir') mlflow_vis_backend.add_image('img', image) - shutil.rmtree('temp_dir') def test_add_scalar(self): mlflow_vis_backend = MLflowVisBackend('temp_dir') @@ -272,7 +269,6 @@ def test_add_scalar(self): # test append mode mlflow_vis_backend.add_scalar('map', 0.9) mlflow_vis_backend.add_scalar('map', 0.95) - shutil.rmtree('temp_dir') def test_add_scalars(self): mlflow_vis_backend = MLflowVisBackend('temp_dir') @@ -280,7 +276,6 @@ def test_add_scalars(self): mlflow_vis_backend.add_scalars(input_dict) # test append mode mlflow_vis_backend.add_scalars({'map': 0.8, 'acc': 0.8}) - shutil.rmtree('temp_dir') def test_close(self): cfg = Config(dict(work_dir='temp_dir')) From 298d95e91c2729f5f9c6a8bb1a7200085088fdfc Mon Sep 17 00:00:00 2001 From: sung-hwa kim <66167254+sh0622-kim@users.noreply.github.com> Date: Sat, 11 Feb 2023 20:11:25 +0900 Subject: [PATCH 33/49] Update mmengine/visualization/vis_backend.py Co-authored-by: Zaida Zhou <58739961+zhouzaida@users.noreply.github.com> --- mmengine/visualization/vis_backend.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mmengine/visualization/vis_backend.py b/mmengine/visualization/vis_backend.py index 80214a3579..8c43b9ee44 100644 --- a/mmengine/visualization/vis_backend.py +++ b/mmengine/visualization/vis_backend.py @@ -626,7 +626,7 @@ class MLflowVisBackend(BaseVisBackend): >>> import numpy as np >>> vis_backend = MLflowVisBackend(save_dir='temp_dir') >>> img = np.random.randint(0, 256, size=(10, 10, 3)) - >>> vis_backend.add_image('img', img) + >>> vis_backend.add_image('img.png', img) >>> vis_backend.add_scalar('mAP', 0.6) >>> vis_backend.add_scalars({'loss': 0.1,'acc':0.8}) >>> cfg = Config(dict(a=1, b=dict(b1=[0, 1]))) From c8711e7a7862ef39f551fc0e21378ebf396cbceb Mon Sep 17 00:00:00 2001 From: sung-hwa kim <66167254+sh0622-kim@users.noreply.github.com> Date: Sat, 11 Feb 2023 20:11:38 +0900 Subject: [PATCH 34/49] Update mmengine/visualization/vis_backend.py Co-authored-by: Zaida Zhou <58739961+zhouzaida@users.noreply.github.com> --- mmengine/visualization/vis_backend.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mmengine/visualization/vis_backend.py b/mmengine/visualization/vis_backend.py index 8c43b9ee44..f284ca89d4 100644 --- a/mmengine/visualization/vis_backend.py +++ b/mmengine/visualization/vis_backend.py @@ -724,7 +724,7 @@ def add_image(self, should be RGB. step (int): Global step value to record. Default to 0. """ - self._mlflow.log_image(image, f'{name}.png') + self._mlflow.log_image(image, name) @force_init_env def add_scalar(self, From 4f604ad857c374ab87d2afae881d19579f17b890 Mon Sep 17 00:00:00 2001 From: sung-hwa kim <66167254+sh0622-kim@users.noreply.github.com> Date: Sat, 11 Feb 2023 20:11:53 +0900 Subject: [PATCH 35/49] Update mmengine/visualization/vis_backend.py Co-authored-by: Zaida Zhou <58739961+zhouzaida@users.noreply.github.com> --- mmengine/visualization/vis_backend.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mmengine/visualization/vis_backend.py b/mmengine/visualization/vis_backend.py index f284ca89d4..29a68c0d58 100644 --- a/mmengine/visualization/vis_backend.py +++ b/mmengine/visualization/vis_backend.py @@ -735,9 +735,9 @@ def add_scalar(self, """Record the scalar data to mlflow. Args: - name (str): The scalar identifier. - value (int, float, torch.Tensor, np.ndarray): Value to save. - step (int): Global step value to record. Default to 0. + name (str): The scalar identifier. + value (int, float, torch.Tensor, np.ndarray): Value to save. + step (int): Global step value to record. Default to 0. """ self._mlflow.log_metric(name, value, step) From da2d7792fb63cf007047531d445c03d06f822209 Mon Sep 17 00:00:00 2001 From: sung-hwa kim <66167254+sh0622-kim@users.noreply.github.com> Date: Sat, 11 Feb 2023 20:12:01 +0900 Subject: [PATCH 36/49] Update mmengine/visualization/vis_backend.py Co-authored-by: Zaida Zhou <58739961+zhouzaida@users.noreply.github.com> --- mmengine/visualization/vis_backend.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/mmengine/visualization/vis_backend.py b/mmengine/visualization/vis_backend.py index 29a68c0d58..13d31c914a 100644 --- a/mmengine/visualization/vis_backend.py +++ b/mmengine/visualization/vis_backend.py @@ -750,11 +750,11 @@ def add_scalars(self, """Record the scalar's data to mlflow. Args: - scalar_dict (dict): Key-value pair storing the tag and - corresponding values. - step (int): Global step value to record. Default to 0. - file_path (str, optional): Useless parameter. Just for - interface unification. Default to None. + scalar_dict (dict): Key-value pair storing the tag and + corresponding values. + step (int): Global step value to record. Default to 0. + file_path (str, optional): Useless parameter. Just for + interface unification. Default to None. """ assert isinstance(scalar_dict, dict) assert 'step' not in scalar_dict, 'Please set it directly ' \ From 05b945c27eaed52d048df4ae33bb9d896cb40c51 Mon Sep 17 00:00:00 2001 From: sh0622-kim <66167254+sh0622-kim@users.noreply.github.com> Date: Sat, 11 Feb 2023 20:23:20 +0900 Subject: [PATCH 37/49] Fix NRE --- mmengine/visualization/vis_backend.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/mmengine/visualization/vis_backend.py b/mmengine/visualization/vis_backend.py index 13d31c914a..9f48e44692 100644 --- a/mmengine/visualization/vis_backend.py +++ b/mmengine/visualization/vis_backend.py @@ -635,7 +635,7 @@ class MLflowVisBackend(BaseVisBackend): Args: save_dir (str): The root directory to save the files produced by the backend. - exp_name (str, optional): The experiment name. Default to 'Default'. + exp_name (str, optional): The experiment name. Default to None. run_name (str, optional): The run name. Default to None. tags (dict, optional): The tags to be added to the experiment. Default to None. @@ -648,7 +648,7 @@ class MLflowVisBackend(BaseVisBackend): def __init__(self, save_dir: str, - exp_name: Optional[str] = 'Default', + exp_name: Optional[str] = None, run_name: Optional[str] = None, tags: Optional[dict] = None, params: Optional[dict] = None, @@ -681,6 +681,8 @@ def _init_env(self): self._mlflow.set_tracking_uri( f'file://{os.path.abspath(self._save_dir)}') + self._exp_name = self._exp_name or 'Default' + if self._mlflow.get_experiment_by_name(self._exp_name) is None: self._mlflow.create_experiment(self._exp_name) From 1df103f1b20efe4185e9770856b514694dd454ac Mon Sep 17 00:00:00 2001 From: sh0622-kim <66167254+sh0622-kim@users.noreply.github.com> Date: Sat, 11 Feb 2023 20:26:35 +0900 Subject: [PATCH 38/49] update test --- tests/test_visualizer/test_vis_backend.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_visualizer/test_vis_backend.py b/tests/test_visualizer/test_vis_backend.py index 5fc0d127a9..6eea7484c0 100644 --- a/tests/test_visualizer/test_vis_backend.py +++ b/tests/test_visualizer/test_vis_backend.py @@ -261,7 +261,7 @@ def test_add_config(self): def test_add_image(self): image = np.random.randint(0, 256, size=(10, 10, 3)).astype(np.uint8) mlflow_vis_backend = MLflowVisBackend('temp_dir') - mlflow_vis_backend.add_image('img', image) + mlflow_vis_backend.add_image('img.png', image) def test_add_scalar(self): mlflow_vis_backend = MLflowVisBackend('temp_dir') From d2abe4df4168a6f4fced4487db16bde19cc69d47 Mon Sep 17 00:00:00 2001 From: sh0622-kim <66167254+sh0622-kim@users.noreply.github.com> Date: Mon, 13 Feb 2023 18:30:45 +0900 Subject: [PATCH 39/49] fix literal --- mmengine/visualization/vis_backend.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mmengine/visualization/vis_backend.py b/mmengine/visualization/vis_backend.py index 9f48e44692..473f4b702a 100644 --- a/mmengine/visualization/vis_backend.py +++ b/mmengine/visualization/vis_backend.py @@ -679,7 +679,7 @@ def _init_env(self): self._mlflow.set_tracking_uri(self._tracking_uri) else: self._mlflow.set_tracking_uri( - f'file://{os.path.abspath(self._save_dir)}') + f'file:\\{os.path.abspath(self._save_dir)}') self._exp_name = self._exp_name or 'Default' From 6b9d3c576b7e16a40c33e648f3c505a8e09901c2 Mon Sep 17 00:00:00 2001 From: sh0622-kim <66167254+sh0622-kim@users.noreply.github.com> Date: Sun, 19 Feb 2023 14:32:48 +0900 Subject: [PATCH 40/49] fix literal --- mmengine/visualization/vis_backend.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/mmengine/visualization/vis_backend.py b/mmengine/visualization/vis_backend.py index 473f4b702a..3c351dac3d 100644 --- a/mmengine/visualization/vis_backend.py +++ b/mmengine/visualization/vis_backend.py @@ -678,8 +678,11 @@ def _init_env(self): if self._tracking_uri is not None: self._mlflow.set_tracking_uri(self._tracking_uri) else: - self._mlflow.set_tracking_uri( - f'file:\\{os.path.abspath(self._save_dir)}') + if os.name == 'nt': + file_url = f'file:\\{os.path.abspath(self._save_dir)}' + else: + file_url = f'file://{os.path.abspath(self._save_dir)}' + self._mlflow.set_tracking_uri(file_url) self._exp_name = self._exp_name or 'Default' From 54463f3c0da3c1e944737630fd333f64394ae54a Mon Sep 17 00:00:00 2001 From: sh0622-kim <66167254+sh0622-kim@users.noreply.github.com> Date: Mon, 6 Mar 2023 21:44:37 +0900 Subject: [PATCH 41/49] add artifact_suffix --- mmengine/visualization/vis_backend.py | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/mmengine/visualization/vis_backend.py b/mmengine/visualization/vis_backend.py index 17aa205460..ad715f82bf 100644 --- a/mmengine/visualization/vis_backend.py +++ b/mmengine/visualization/vis_backend.py @@ -14,8 +14,10 @@ from mmengine.config import Config from mmengine.fileio import dump +from mmengine.hooks.logger_hook import SUFFIX_TYPE from mmengine.logging import MMLogger from mmengine.registry import VISBACKENDS +from mmengine.utils import scandir from mmengine.utils.dl_utils import TORCH_VERSION @@ -645,6 +647,8 @@ class MLflowVisBackend(BaseVisBackend): tracking_uri (str, optional): The tracking uri. Default to None. log_artifact (bool, optional): Whether to log the artifact. Default to False. + artifact_suffix (Tuple[str] or str, optional): The artifact suffix. + Default to ('.json', '.log', '.py', 'yaml', ''). """ def __init__(self, @@ -654,7 +658,9 @@ def __init__(self, tags: Optional[dict] = None, params: Optional[dict] = None, tracking_uri: Optional[str] = None, - log_artifact: bool = False): + log_artifact: bool = False, + artifact_suffix: SUFFIX_TYPE = ('.json', '.log', '.py', + 'yaml', '')): super().__init__(save_dir) self._exp_name = exp_name self._run_name = run_name @@ -662,6 +668,7 @@ def __init__(self, self._params = params self._tracking_uri = tracking_uri self._log_artifact = log_artifact + self._artifact_suffix = artifact_suffix def _init_env(self): """Setup env for MLflow.""" @@ -769,8 +776,12 @@ def add_scalars(self, def close(self) -> None: """Close the mlflow.""" - if self._log_artifact: - self._mlflow.log_artifacts(self.cfg.work_dir) + for filename in scandir(self.cfg.work_dir, self._artifact_suffix, + True): + file_path = osp.join(self.cfg.work_dir, filename) + relative_path = os.path.relpath(file_path, self.cfg.work_dir) + dir_path = os.path.dirname(relative_path) + self._mlflow.log_artifact(file_path, dir_path) if hasattr(self, '_mlflow'): self._mlflow.end_run() From a3b6a96e8ecb4387cf54b00f74134dd59c1227a5 Mon Sep 17 00:00:00 2001 From: sh0622-kim <66167254+sh0622-kim@users.noreply.github.com> Date: Mon, 6 Mar 2023 22:08:07 +0900 Subject: [PATCH 42/49] Fixed a bug that occurred when saving local --- mmengine/visualization/vis_backend.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/mmengine/visualization/vis_backend.py b/mmengine/visualization/vis_backend.py index ad715f82bf..612d18de48 100644 --- a/mmengine/visualization/vis_backend.py +++ b/mmengine/visualization/vis_backend.py @@ -776,11 +776,15 @@ def add_scalars(self, def close(self) -> None: """Close the mlflow.""" + file_paths = dict() for filename in scandir(self.cfg.work_dir, self._artifact_suffix, True): file_path = osp.join(self.cfg.work_dir, filename) relative_path = os.path.relpath(file_path, self.cfg.work_dir) dir_path = os.path.dirname(relative_path) + file_paths[file_path] = dir_path + + for file_path, dir_path in file_paths.items(): self._mlflow.log_artifact(file_path, dir_path) if hasattr(self, '_mlflow'): From 9a9851c3f62b2e2c52ff6719660e90e45f87971f Mon Sep 17 00:00:00 2001 From: sung-hwa kim <66167254+sh0622-kim@users.noreply.github.com> Date: Tue, 7 Mar 2023 09:14:36 +0900 Subject: [PATCH 43/49] Update mmengine/visualization/vis_backend.py --- mmengine/visualization/vis_backend.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mmengine/visualization/vis_backend.py b/mmengine/visualization/vis_backend.py index 612d18de48..a116ecf1b0 100644 --- a/mmengine/visualization/vis_backend.py +++ b/mmengine/visualization/vis_backend.py @@ -648,7 +648,7 @@ class MLflowVisBackend(BaseVisBackend): log_artifact (bool, optional): Whether to log the artifact. Default to False. artifact_suffix (Tuple[str] or str, optional): The artifact suffix. - Default to ('.json', '.log', '.py', 'yaml', ''). + Default to ('.json', '.log', '.py', 'yaml'). """ def __init__(self, From ffff25223c54c1623047b196fe3bb36517778865 Mon Sep 17 00:00:00 2001 From: sung-hwa kim <66167254+sh0622-kim@users.noreply.github.com> Date: Tue, 7 Mar 2023 09:17:25 +0900 Subject: [PATCH 44/49] Update mmengine/visualization/vis_backend.py --- mmengine/visualization/vis_backend.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mmengine/visualization/vis_backend.py b/mmengine/visualization/vis_backend.py index a116ecf1b0..68439951ca 100644 --- a/mmengine/visualization/vis_backend.py +++ b/mmengine/visualization/vis_backend.py @@ -660,7 +660,7 @@ def __init__(self, tracking_uri: Optional[str] = None, log_artifact: bool = False, artifact_suffix: SUFFIX_TYPE = ('.json', '.log', '.py', - 'yaml', '')): + 'yaml')): super().__init__(save_dir) self._exp_name = exp_name self._run_name = run_name From 7d86094fa4039aee89ebe65a4469e5d1037e2ffc Mon Sep 17 00:00:00 2001 From: sung-hwa kim <66167254+sh0622-kim@users.noreply.github.com> Date: Tue, 7 Mar 2023 15:35:05 +0900 Subject: [PATCH 45/49] Update mmengine/visualization/vis_backend.py Co-authored-by: Zaida Zhou <58739961+zhouzaida@users.noreply.github.com> --- mmengine/visualization/vis_backend.py | 1 - 1 file changed, 1 deletion(-) diff --git a/mmengine/visualization/vis_backend.py b/mmengine/visualization/vis_backend.py index 68439951ca..c875e5915c 100644 --- a/mmengine/visualization/vis_backend.py +++ b/mmengine/visualization/vis_backend.py @@ -667,7 +667,6 @@ def __init__(self, self._tags = tags self._params = params self._tracking_uri = tracking_uri - self._log_artifact = log_artifact self._artifact_suffix = artifact_suffix def _init_env(self): From c847379a18dd5c757a5759be3ca25a4ea861800c Mon Sep 17 00:00:00 2001 From: sung-hwa kim <66167254+sh0622-kim@users.noreply.github.com> Date: Tue, 7 Mar 2023 15:35:17 +0900 Subject: [PATCH 46/49] Update mmengine/visualization/vis_backend.py Co-authored-by: Zaida Zhou <58739961+zhouzaida@users.noreply.github.com> --- mmengine/visualization/vis_backend.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/mmengine/visualization/vis_backend.py b/mmengine/visualization/vis_backend.py index c875e5915c..68ce631c68 100644 --- a/mmengine/visualization/vis_backend.py +++ b/mmengine/visualization/vis_backend.py @@ -645,8 +645,6 @@ class MLflowVisBackend(BaseVisBackend): params (dict, optional): The params to be added to the experiment. Default to None. tracking_uri (str, optional): The tracking uri. Default to None. - log_artifact (bool, optional): Whether to log the artifact. - Default to False. artifact_suffix (Tuple[str] or str, optional): The artifact suffix. Default to ('.json', '.log', '.py', 'yaml'). """ From 427960a42e5705ba612d0c9eade35abf366cf416 Mon Sep 17 00:00:00 2001 From: sung-hwa kim <66167254+sh0622-kim@users.noreply.github.com> Date: Tue, 7 Mar 2023 15:35:28 +0900 Subject: [PATCH 47/49] Update mmengine/visualization/vis_backend.py Co-authored-by: Zaida Zhou <58739961+zhouzaida@users.noreply.github.com> --- mmengine/visualization/vis_backend.py | 1 - 1 file changed, 1 deletion(-) diff --git a/mmengine/visualization/vis_backend.py b/mmengine/visualization/vis_backend.py index 68ce631c68..e7bd17cf76 100644 --- a/mmengine/visualization/vis_backend.py +++ b/mmengine/visualization/vis_backend.py @@ -656,7 +656,6 @@ def __init__(self, tags: Optional[dict] = None, params: Optional[dict] = None, tracking_uri: Optional[str] = None, - log_artifact: bool = False, artifact_suffix: SUFFIX_TYPE = ('.json', '.log', '.py', 'yaml')): super().__init__(save_dir) From 02bfab9ecb07f393bb625c75b31bf95c28775dc0 Mon Sep 17 00:00:00 2001 From: sh0622-kim <66167254+sh0622-kim@users.noreply.github.com> Date: Sat, 18 Mar 2023 19:04:08 +0900 Subject: [PATCH 48/49] Fix for file handler closing --- mmengine/model/base_module.py | 2 -- mmengine/visualization/vis_backend.py | 5 +++++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/mmengine/model/base_module.py b/mmengine/model/base_module.py index 69f7343044..e742d15886 100644 --- a/mmengine/model/base_module.py +++ b/mmengine/model/base_module.py @@ -166,8 +166,6 @@ def _dump_init_info(self): # dump the information to the logger file if there is a `FileHandler` for handler in logger.handlers: if isinstance(handler, FileHandler): - if (handler.stream is None): - continue handler.stream.write( 'Name of parameter - Initialization information\n') for name, param in self.named_parameters(): diff --git a/mmengine/visualization/vis_backend.py b/mmengine/visualization/vis_backend.py index 612d18de48..57fecaa6c5 100644 --- a/mmengine/visualization/vis_backend.py +++ b/mmengine/visualization/vis_backend.py @@ -683,6 +683,11 @@ def _init_env(self): ) # type: ignore self._mlflow = mlflow + logger = MMLogger.get_current_instance() + for handler in logger.handlers: + if handler.stream is None or handler.stream.closed: + handler.stream = open(handler.baseFilename, 'a') + if self._tracking_uri is not None: self._mlflow.set_tracking_uri(self._tracking_uri) else: From 8eca59639ff34fa610404219dc7442eb4d213b26 Mon Sep 17 00:00:00 2001 From: sh0622-kim <66167254+sh0622-kim@users.noreply.github.com> Date: Mon, 20 Mar 2023 22:28:05 +0900 Subject: [PATCH 49/49] Add warning log --- mmengine/visualization/vis_backend.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/mmengine/visualization/vis_backend.py b/mmengine/visualization/vis_backend.py index 569188e685..e8daeeab95 100644 --- a/mmengine/visualization/vis_backend.py +++ b/mmengine/visualization/vis_backend.py @@ -681,12 +681,18 @@ def _init_env(self): ) # type: ignore self._mlflow = mlflow + # when mlflow is imported, a default logger is created. + # at this time, the default logger's stream is None + # so the stream is reopened only when the stream is None + # or the stream is closed logger = MMLogger.get_current_instance() for handler in logger.handlers: if handler.stream is None or handler.stream.closed: handler.stream = open(handler.baseFilename, 'a') if self._tracking_uri is not None: + logger.warning( + 'Please make sure that the mlflow server is running.') self._mlflow.set_tracking_uri(self._tracking_uri) else: if os.name == 'nt':