Arulkumar03/Fox_Sheep_Detector_Computer_Vision_model
0
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 