Source code for mighty.monitor.viz

"""
Visdom server
-------------

.. autosummary::
    :toctree: toctree/monitor

    VisdomMighty
"""

import os
import sys
import time
from collections import defaultdict

import numpy as np
import visdom

from mighty.monitor.batch_timer import timer
from mighty.utils.constants import VISDOM_LOGS_DIR


__all__ = [
    "VisdomMighty"
]


[docs] class VisdomMighty(visdom.Visdom): """ A Visdom server that updates measures in online fashion. Parameters ---------- env : str, optional Environment name. Default: "main" offline : bool, optional Online (False) or offline (True) mode. Default: False """ def __init__(self, env="main", offline=False): port = int(os.environ.get('VISDOM_PORT', 8097)) server = os.environ.get('VISDOM_SERVER', 'http://localhost') base_url = os.environ.get('VISDOM_BASE_URL', '/') env = env.replace('_', '-') # visdom things log_to_filename = None if offline: VISDOM_LOGS_DIR.mkdir(exist_ok=True) log_to_filename = VISDOM_LOGS_DIR / f"{env}.log" try: super().__init__(env=env, server=server, port=port, username=os.environ.get('VISDOM_USER', None), password=os.environ.get('VISDOM_PASSWORD', None), log_to_filename=log_to_filename, offline=offline, base_url=base_url, raise_exceptions=True) except ConnectionError as error: tb = sys.exc_info()[2] raise ConnectionError("Start Visdom server with " "'python -m visdom.server' command." ).with_traceback(tb) self.timer = timer self.legends = defaultdict(list) self.with_markers = False if offline: print(f"Visdom logs are saved in {log_to_filename}") else: url = f"{self.server}:{self.port}{self.base_url}" print(f"Monitor is opened at {url}. " f"Choose environment '{self.env}'.") # self._register_comments_window() def _register_comments_window(self): txt_init = "Enter comments:" win = 'comments' def type_callback(event): if event['event_type'] == 'KeyPress': curr_txt = event['pane_data']['content'] if event['key'] == 'Enter': curr_txt += '<br>' elif event['key'] == 'Backspace': curr_txt = curr_txt[:-1] elif event['key'] == 'Delete': curr_txt = txt_init elif len(event['key']) == 1: curr_txt += event['key'] self.text(curr_txt, win='comments') self.text(txt_init, win=win) self.register_event_handler(type_callback, win)
[docs] def line_update(self, y, opts, name=None): """ Appends `y` axis value to the plot. The `x` axis value will be extracted from the global timer. Parameters ---------- y : float or list of float or torch.Tensor The Y axis value. opts : dict Visdom plot `opts`. name : str or None, optional The label name of this plot. Used when a plot has a legend. Default: None """ y = np.array([y]) n_lines = y.shape[-1] if n_lines == 0: return if y.ndim > 1 and n_lines == 1: # visdom expects 1d array for a single line plot y = y[0] x = np.full_like(y, self.timer.epoch_progress(), dtype=np.float32) # hack to make window names consistent if the user forgets to specify # the title win = opts.get('title', str(opts)) if self.with_markers: opts['markers'] = True opts['markersize'] = 7 self.line(Y=y, X=x, win=win, opts=opts, update='append', name=name) if name is not None: self.update_window_opts(win=win, opts=dict(legend=[], title=win))
[docs] def log(self, text, timestamp=True): """ Log the text. Parameters ---------- text : str Text timestamp : bool, optional Prepend date timestamp (True) or not. Default: True """ if timestamp: text = f"{time.strftime('%Y-%b-%d %H:%M')} {text}" self.text(text, win='log', opts=dict(title='log'), append=self.win_exists(win='log'))