Team Ai
Apppublic

Arulkumar03/Fox_Sheep_Detector_Computer_Vision_model

sourceHugging Facemitupdated 3y agoView on Hugging Face
0likes
events.py552 linesDownload Raw Back to utils
1# Copyright (c) Facebook, Inc. and its affiliates.2import datetime3import json4import logging5import os6import time7from collections import defaultdict8from contextlib import contextmanager9from functools import cached_property10from typing import Optional11import torch12from fvcore.common.history_buffer import HistoryBuffer13 14from detectron2.utils.file_io import PathManager15 16__all__ = [17    "get_event_storage",18    "has_event_storage",19    "JSONWriter",20    "TensorboardXWriter",21    "CommonMetricPrinter",22    "EventStorage",23]24 25_CURRENT_STORAGE_STACK = []26 27 28def get_event_storage():29    """30    Returns:31        The :class:`EventStorage` object that's currently being used.32        Throws an error if no :class:`EventStorage` is currently enabled.33    """34    assert len(35        _CURRENT_STORAGE_STACK36    ), "get_event_storage() has to be called inside a 'with EventStorage(...)' context!"37    return _CURRENT_STORAGE_STACK[-1]38 39 40def has_event_storage():41    """42    Returns:43        Check if there are EventStorage() context existed.44    """45    return len(_CURRENT_STORAGE_STACK) > 046 47 48class EventWriter:49    """50    Base class for writers that obtain events from :class:`EventStorage` and process them.51    """52 53    def write(self):54        raise NotImplementedError55 56    def close(self):57        pass58 59 60class JSONWriter(EventWriter):61    """62    Write scalars to a json file.63 64    It saves scalars as one json per line (instead of a big json) for easy parsing.65 66    Examples parsing such a json file:67    ::68        $ cat metrics.json | jq -s '.[0:2]'69        [70          {71            "data_time": 0.008433341979980469,72            "iteration": 19,73            "loss": 1.9228371381759644,74            "loss_box_reg": 0.050025828182697296,75            "loss_classifier": 0.5316952466964722,76            "loss_mask": 0.7236229181289673,77            "loss_rpn_box": 0.0856662318110466,78            "loss_rpn_cls": 0.48198649287223816,79            "lr": 0.007173333333333333,80            "time": 0.2540185451507568481          },82          {83            "data_time": 0.007216215133666992,84            "iteration": 39,85            "loss": 1.282649278640747,86            "loss_box_reg": 0.06222952902317047,87            "loss_classifier": 0.30682939291000366,88            "loss_mask": 0.6970193982124329,89            "loss_rpn_box": 0.038663312792778015,90            "loss_rpn_cls": 0.1471673548221588,91            "lr": 0.007706666666666667,92            "time": 0.249007701873779393          }94        ]95 96        $ cat metrics.json | jq '.loss_mask'97        0.712623178958892898        0.68942368030548199        0.6776131987571716100        ...101 102    """103 104    def __init__(self, json_file, window_size=20):105        """106        Args:107            json_file (str): path to the json file. New data will be appended if the file exists.108            window_size (int): the window size of median smoothing for the scalars whose109                `smoothing_hint` are True.110        """111        self._file_handle = PathManager.open(json_file, "a")112        self._window_size = window_size113        self._last_write = -1114 115    def write(self):116        storage = get_event_storage()117        to_save = defaultdict(dict)118 119        for k, (v, iter) in storage.latest_with_smoothing_hint(self._window_size).items():120            # keep scalars that have not been written121            if iter <= self._last_write:122                continue123            to_save[iter][k] = v124        if len(to_save):125            all_iters = sorted(to_save.keys())126            self._last_write = max(all_iters)127 128        for itr, scalars_per_iter in to_save.items():129            scalars_per_iter["iteration"] = itr130            self._file_handle.write(json.dumps(scalars_per_iter, sort_keys=True) + "\n")131        self._file_handle.flush()132        try:133            os.fsync(self._file_handle.fileno())134        except AttributeError:135            pass136 137    def close(self):138        self._file_handle.close()139 140 141class TensorboardXWriter(EventWriter):142    """143    Write all scalars to a tensorboard file.144    """145 146    def __init__(self, log_dir: str, window_size: int = 20, **kwargs):147        """148        Args:149            log_dir (str): the directory to save the output events150            window_size (int): the scalars will be median-smoothed by this window size151 152            kwargs: other arguments passed to `torch.utils.tensorboard.SummaryWriter(...)`153        """154        self._window_size = window_size155        self._writer_args = {"log_dir": log_dir, **kwargs}156        self._last_write = -1157 158    @cached_property159    def _writer(self):160        from torch.utils.tensorboard import SummaryWriter161 162        return SummaryWriter(**self._writer_args)163 164    def write(self):165        storage = get_event_storage()166        new_last_write = self._last_write167        for k, (v, iter) in storage.latest_with_smoothing_hint(self._window_size).items():168            if iter > self._last_write:169                self._writer.add_scalar(k, v, iter)170                new_last_write = max(new_last_write, iter)171        self._last_write = new_last_write172 173        # storage.put_{image,histogram} is only meant to be used by174        # tensorboard writer. So we access its internal fields directly from here.175        if len(storage._vis_data) >= 1:176            for img_name, img, step_num in storage._vis_data:177                self._writer.add_image(img_name, img, step_num)178            # Storage stores all image data and rely on this writer to clear them.179            # As a result it assumes only one writer will use its image data.180            # An alternative design is to let storage store limited recent181            # data (e.g. only the most recent image) that all writers can access.182            # In that case a writer may not see all image data if its period is long.183            storage.clear_images()184 185        if len(storage._histograms) >= 1:186            for params in storage._histograms:187                self._writer.add_histogram_raw(**params)188            storage.clear_histograms()189 190    def close(self):191        if "_writer" in self.__dict__:192            self._writer.close()193 194 195class CommonMetricPrinter(EventWriter):196    """197    Print **common** metrics to the terminal, including198    iteration time, ETA, memory, all losses, and the learning rate.199    It also applies smoothing using a window of 20 elements.200 201    It's meant to print common metrics in common ways.202    To print something in more customized ways, please implement a similar printer by yourself.203    """204 205    def __init__(self, max_iter: Optional[int] = None, window_size: int = 20):206        """207        Args:208            max_iter: the maximum number of iterations to train.209                Used to compute ETA. If not given, ETA will not be printed.210            window_size (int): the losses will be median-smoothed by this window size211        """212        self.logger = logging.getLogger("detectron2.utils.events")213        self._max_iter = max_iter214        self._window_size = window_size215        self._last_write = None  # (step, time) of last call to write(). Used to compute ETA216 217    def _get_eta(self, storage) -> Optional[str]:218        if self._max_iter is None:219            return ""220        iteration = storage.iter221        try:222            eta_seconds = storage.history("time").median(1000) * (self._max_iter - iteration - 1)223            storage.put_scalar("eta_seconds", eta_seconds, smoothing_hint=False)224            return str(datetime.timedelta(seconds=int(eta_seconds)))225        except KeyError:226            # estimate eta on our own - more noisy227            eta_string = None228            if self._last_write is not None:229                estimate_iter_time = (time.perf_counter() - self._last_write[1]) / (230                    iteration - self._last_write[0]231                )232                eta_seconds = estimate_iter_time * (self._max_iter - iteration - 1)233                eta_string = str(datetime.timedelta(seconds=int(eta_seconds)))234            self._last_write = (iteration, time.perf_counter())235            return eta_string236 237    def write(self):238        storage = get_event_storage()239        iteration = storage.iter240        if iteration == self._max_iter:241            # This hook only reports training progress (loss, ETA, etc) but not other data,242            # therefore do not write anything after training succeeds, even if this method243            # is called.244            return245 246        try:247            avg_data_time = storage.history("data_time").avg(248                storage.count_samples("data_time", self._window_size)249            )250            last_data_time = storage.history("data_time").latest()251        except KeyError:252            # they may not exist in the first few iterations (due to warmup)253            # or when SimpleTrainer is not used254            avg_data_time = None255            last_data_time = None256        try:257            avg_iter_time = storage.history("time").global_avg()258            last_iter_time = storage.history("time").latest()259        except KeyError:260            avg_iter_time = None261            last_iter_time = None262        try:263            lr = "{:.5g}".format(storage.history("lr").latest())264        except KeyError:265            lr = "N/A"266 267        eta_string = self._get_eta(storage)268 269        if torch.cuda.is_available():270            max_mem_mb = torch.cuda.max_memory_allocated() / 1024.0 / 1024.0271        else:272            max_mem_mb = None273 274        # NOTE: max_mem is parsed by grep in "dev/parse_results.sh"275        self.logger.info(276            str.format(277                " {eta}iter: {iter}  {losses}  {non_losses}  {avg_time}{last_time}"278                + "{avg_data_time}{last_data_time} lr: {lr}  {memory}",279                eta=f"eta: {eta_string}  " if eta_string else "",280                iter=iteration,281                losses="  ".join(282                    [283                        "{}: {:.4g}".format(284                            k, v.median(storage.count_samples(k, self._window_size))285                        )286                        for k, v in storage.histories().items()287                        if "loss" in k288                    ]289                ),290                non_losses="  ".join(291                    [292                        "{}: {:.4g}".format(293                            k, v.median(storage.count_samples(k, self._window_size))294                        )295                        for k, v in storage.histories().items()296                        if "[metric]" in k297                    ]298                ),299                avg_time="time: {:.4f}  ".format(avg_iter_time)300                if avg_iter_time is not None301                else "",302                last_time="last_time: {:.4f}  ".format(last_iter_time)303                if last_iter_time is not None304                else "",305                avg_data_time="data_time: {:.4f}  ".format(avg_data_time)306                if avg_data_time is not None307                else "",308                last_data_time="last_data_time: {:.4f}  ".format(last_data_time)309                if last_data_time is not None310                else "",311                lr=lr,312                memory="max_mem: {:.0f}M".format(max_mem_mb) if max_mem_mb is not None else "",313            )314        )315 316 317class EventStorage:318    """319    The user-facing class that provides metric storage functionalities.320 321    In the future we may add support for storing / logging other types of data if needed.322    """323 324    def __init__(self, start_iter=0):325        """326        Args:327            start_iter (int): the iteration number to start with328        """329        self._history = defaultdict(HistoryBuffer)330        self._smoothing_hints = {}331        self._latest_scalars = {}332        self._iter = start_iter333        self._current_prefix = ""334        self._vis_data = []335        self._histograms = []336 337    def put_image(self, img_name, img_tensor):338        """339        Add an `img_tensor` associated with `img_name`, to be shown on340        tensorboard.341 342        Args:343            img_name (str): The name of the image to put into tensorboard.344            img_tensor (torch.Tensor or numpy.array): An `uint8` or `float`345                Tensor of shape `[channel, height, width]` where `channel` is346                3. The image format should be RGB. The elements in img_tensor347                can either have values in [0, 1] (float32) or [0, 255] (uint8).348                The `img_tensor` will be visualized in tensorboard.349        """350        self._vis_data.append((img_name, img_tensor, self._iter))351 352    def put_scalar(self, name, value, smoothing_hint=True, cur_iter=None):353        """354        Add a scalar `value` to the `HistoryBuffer` associated with `name`.355 356        Args:357            smoothing_hint (bool): a 'hint' on whether this scalar is noisy and should be358                smoothed when logged. The hint will be accessible through359                :meth:`EventStorage.smoothing_hints`.  A writer may ignore the hint360                and apply custom smoothing rule.361 362                It defaults to True because most scalars we save need to be smoothed to363                provide any useful signal.364            cur_iter (int): an iteration number to set explicitly instead of current iteration365        """366        name = self._current_prefix + name367        cur_iter = self._iter if cur_iter is None else cur_iter368        history = self._history[name]369        value = float(value)370        history.update(value, cur_iter)371        self._latest_scalars[name] = (value, cur_iter)372 373        existing_hint = self._smoothing_hints.get(name)374 375        if existing_hint is not None:376            assert (377                existing_hint == smoothing_hint378            ), "Scalar {} was put with a different smoothing_hint!".format(name)379        else:380            self._smoothing_hints[name] = smoothing_hint381 382    def put_scalars(self, *, smoothing_hint=True, cur_iter=None, **kwargs):383        """384        Put multiple scalars from keyword arguments.385 386        Examples:387 388            storage.put_scalars(loss=my_loss, accuracy=my_accuracy, smoothing_hint=True)389        """390        for k, v in kwargs.items():391            self.put_scalar(k, v, smoothing_hint=smoothing_hint, cur_iter=cur_iter)392 393    def put_histogram(self, hist_name, hist_tensor, bins=1000):394        """395        Create a histogram from a tensor.396 397        Args:398            hist_name (str): The name of the histogram to put into tensorboard.399            hist_tensor (torch.Tensor): A Tensor of arbitrary shape to be converted400                into a histogram.401            bins (int): Number of histogram bins.402        """403        ht_min, ht_max = hist_tensor.min().item(), hist_tensor.max().item()404 405        # Create a histogram with PyTorch406        hist_counts = torch.histc(hist_tensor, bins=bins)407        hist_edges = torch.linspace(start=ht_min, end=ht_max, steps=bins + 1, dtype=torch.float32)408 409        # Parameter for the add_histogram_raw function of SummaryWriter410        hist_params = dict(411            tag=hist_name,412            min=ht_min,413            max=ht_max,414            num=len(hist_tensor),415            sum=float(hist_tensor.sum()),416            sum_squares=float(torch.sum(hist_tensor**2)),417            bucket_limits=hist_edges[1:].tolist(),418            bucket_counts=hist_counts.tolist(),419            global_step=self._iter,420        )421        self._histograms.append(hist_params)422 423    def history(self, name):424        """425        Returns:426            HistoryBuffer: the scalar history for name427        """428        ret = self._history.get(name, None)429        if ret is None:430            raise KeyError("No history metric available for {}!".format(name))431        return ret432 433    def histories(self):434        """435        Returns:436            dict[name -> HistoryBuffer]: the HistoryBuffer for all scalars437        """438        return self._history439 440    def latest(self):441        """442        Returns:443            dict[str -> (float, int)]: mapping from the name of each scalar to the most444                recent value and the iteration number its added.445        """446        return self._latest_scalars447 448    def latest_with_smoothing_hint(self, window_size=20):449        """450        Similar to :meth:`latest`, but the returned values451        are either the un-smoothed original latest value,452        or a median of the given window_size,453        depend on whether the smoothing_hint is True.454 455        This provides a default behavior that other writers can use.456 457        Note: All scalars saved in the past `window_size` iterations are used for smoothing.458        This is different from the `window_size` definition in HistoryBuffer.459        Use :meth:`get_history_window_size` to get the `window_size` used in HistoryBuffer.460        """461        result = {}462        for k, (v, itr) in self._latest_scalars.items():463            result[k] = (464                self._history[k].median(self.count_samples(k, window_size))465                if self._smoothing_hints[k]466                else v,467                itr,468            )469        return result470 471    def count_samples(self, name, window_size=20):472        """473        Return the number of samples logged in the past `window_size` iterations.474        """475        samples = 0476        data = self._history[name].values()477        for _, iter_ in reversed(data):478            if iter_ > data[-1][1] - window_size:479                samples += 1480            else:481                break482        return samples483 484    def smoothing_hints(self):485        """486        Returns:487            dict[name -> bool]: the user-provided hint on whether the scalar488                is noisy and needs smoothing.489        """490        return self._smoothing_hints491 492    def step(self):493        """494        User should either: (1) Call this function to increment storage.iter when needed. Or495        (2) Set `storage.iter` to the correct iteration number before each iteration.496 497        The storage will then be able to associate the new data with an iteration number.498        """499        self._iter += 1500 501    @property502    def iter(self):503        """504        Returns:505            int: The current iteration number. When used together with a trainer,506                this is ensured to be the same as trainer.iter.507        """508        return self._iter509 510    @iter.setter511    def iter(self, val):512        self._iter = int(val)513 514    @property515    def iteration(self):516        # for backward compatibility517        return self._iter518 519    def __enter__(self):520        _CURRENT_STORAGE_STACK.append(self)521        return self522 523    def __exit__(self, exc_type, exc_val, exc_tb):524        assert _CURRENT_STORAGE_STACK[-1] == self525        _CURRENT_STORAGE_STACK.pop()526 527    @contextmanager528    def name_scope(self, name):529        """530        Yields:531            A context within which all the events added to this storage532            will be prefixed by the name scope.533        """534        old_prefix = self._current_prefix535        self._current_prefix = name.rstrip("/") + "/"536        yield537        self._current_prefix = old_prefix538 539    def clear_images(self):540        """541        Delete all the stored images for visualization. This should be called542        after images are written to tensorboard.543        """544        self._vis_data = []545 546    def clear_histograms(self):547        """548        Delete all the stored histograms for visualization.549        This should be called after histograms are written to tensorboard.550        """551        self._histograms = []552