| | import pandas as pd |
| | import os |
| | import torch |
| | from datetime import datetime |
| | import numpy as np |
| | import random |
| | from time import time |
| | from torch.optim.lr_scheduler import _LRScheduler |
| |
|
| |
|
| | class AverageMeter(object): |
| | """ |
| | A AverageMeter to meter avg of number-like data. |
| | """ |
| |
|
| | def __init__(self, name, keys, writer=None): |
| | self.name = name |
| | self._data = pd.DataFrame( |
| | index=keys, columns=["last_value", "total", "counts", "average"] |
| | ) |
| | self.writer = writer |
| | self.reset() |
| |
|
| | def reset(self): |
| | for col in self._data.columns: |
| | self._data[col].values[:] = 0 |
| |
|
| | def update(self, key, value, n=1): |
| | if self.writer is not None: |
| | tag = "{}/{}".format(self.name, key) |
| | self.writer.add_scalar(tag, value) |
| | |
| | |
| | |
| | |
| | self._data.loc[key, 'last_value'] = value |
| | self._data.loc[key, 'total'] += value * n |
| | self._data.loc[key, 'counts'] += n |
| | self._data.loc[key, 'average'] = self._data.loc[key, 'total'] / self._data.loc[key, 'counts'] |
| |
|
| |
|
| | def avg(self, key): |
| | return self._data.average[key] |
| |
|
| | def result(self): |
| | return dict(self._data.average) |
| |
|
| | def last(self, key): |
| | return self._data.last_value[key] |
| |
|
| | def total(self, key): |
| | return self._data.total[key] |
| | |
| |
|
| |
|
| | def init_seed(seed=0, deterministic=False): |
| | """ |
| | |
| | :param seed: |
| | :param deterministic: |
| | :return: |
| | """ |
| | os.environ["PYTHONHASHSEED"] = str(seed) |
| | random.seed(seed) |
| | np.random.seed(seed) |
| | torch.manual_seed(seed) |
| | torch.cuda.manual_seed(seed) |
| | torch.cuda.manual_seed_all(seed) |
| |
|
| | if deterministic: |
| | torch.backends.cudnn.benchmark = False |
| | torch.backends.cudnn.deterministic = True |
| | else: |
| | torch.backends.cudnn.benchmark = True |
| | torch.backends.cudnn.deterministic = False |
| |
|
| | def get_instance(module, name, config, **kwargs): |
| | """ |
| | A reflection function to get backbone/classifier/. |
| | |
| | Args: |
| | module ([type]): Package Name. |
| | name (str): Top level value in config dict. (backbone, classifier, etc.) |
| | config (dict): The parsed config dict. |
| | |
| | Returns: |
| | Corresponding instance. |
| | """ |
| | if config[name]["kwargs"] is not None: |
| | kwargs.update(config[name]["kwargs"]) |
| | |
| | return getattr(module, config[name]["name"])(**kwargs) |
| |
|
| | |
| | class GradualWarmupScheduler(_LRScheduler): |
| | """Gradually warm-up(increasing) learning rate in optimizer. |
| | Proposed in 'Accurate, Large Minibatch SGD: Training ImageNet in 1 Hour'. |
| | Args: |
| | optimizer (Optimizer): Wrapped optimizer. |
| | multiplier: target learning rate = base lr * multiplier if multiplier > 1.0. if multiplier = 1.0, lr starts from 0 and ends up with the base_lr. |
| | total_epoch: target learning rate is reached at total_epoch, gradually |
| | after_scheduler: after target_epoch, use this scheduler(eg. ReduceLROnPlateau) |
| | """ |
| |
|
| | def __init__(self, optimizer, config): |
| | |
| | |
| | self.optimizer = optimizer |
| | self.total_epoch = config["epoch"] |
| | self.warmup = config["warmup"] |
| | self.after_scheduler = self.get_after_scheduler(config) |
| | self.finish_warmup = False |
| | super(GradualWarmupScheduler, self).__init__(optimizer) |
| |
|
| | def get_after_scheduler(self, config): |
| | scheduler_name = config["lr_scheduler"]["name"] |
| | scheduler_dict = config["lr_scheduler"]["kwargs"] |
| |
|
| | if self.warmup != 0: |
| | if scheduler_name == "CosineAnnealingLR": |
| | scheduler_dict["T_max"] -= self.warmup - 1 |
| | elif scheduler_name == "MultiStepLR": |
| | scheduler_dict["milestones"] = [ |
| | step - self.warmup + 1 for step in scheduler_dict["milestones"] |
| | ] |
| |
|
| | if scheduler_name == "LambdaLR": |
| | return torch.optim.lr_scheduler.LambdaLR( |
| | self.optimizer, |
| | lr_lambda=eval(config["lr_scheduler"]["kwargs"]["lr_lambda"]), |
| | last_epoch=-1, |
| | ) |
| |
|
| | return getattr(torch.optim.lr_scheduler, scheduler_name)( |
| | optimizer=self.optimizer, **scheduler_dict |
| | ) |
| |
|
| | def get_lr(self): |
| | if self.last_epoch >= self.warmup - 1: |
| | self.finish_warmup = True |
| | return self.after_scheduler.get_last_lr() |
| |
|
| | return [ |
| | base_lr * float(self.last_epoch + 1) / self.warmup |
| | for base_lr in self.base_lrs |
| | ] |
| |
|
| | def step_ReduceLROnPlateau(self, metrics, epoch=None): |
| | if epoch is None: |
| | epoch = self.last_epoch + 1 |
| | self.last_epoch = ( |
| | epoch if epoch != 0 else 1 |
| | ) |
| | if self.last_epoch <= self.total_epoch: |
| | warmup_lr = [ |
| | base_lr |
| | * ((self.multiplier - 1.0) * self.last_epoch / self.total_epoch + 1.0) |
| | for base_lr in self.base_lrs |
| | ] |
| | for param_group, lr in zip(self.optimizer.param_groups, warmup_lr): |
| | param_group["lr"] = lr |
| | else: |
| | if epoch is None: |
| | self.after_scheduler.step(metrics, None) |
| | else: |
| | self.after_scheduler.step(metrics, epoch - self.total_epoch) |
| |
|
| | def step(self, epoch=None, metrics=None): |
| | if type(self.after_scheduler) != torch.optim.lr_scheduler.ReduceLROnPlateau: |
| | if self.finish_warmup and self.after_scheduler: |
| | if epoch is None: |
| | self.after_scheduler.step(None) |
| | else: |
| | self.after_scheduler.step(epoch - self.warmup) |
| | self._last_lr = self.after_scheduler.get_last_lr() |
| | else: |
| | return super(GradualWarmupScheduler, self).step(epoch) |
| | else: |
| | self.step_ReduceLROnPlateau(metrics, epoch) |
| |
|
| |
|
| | def count_parameters(model): |
| | return sum(p.numel() for p in model.parameters() if p.requires_grad) |
| |
|
| | def count_all_parameters(model): |
| |
|
| | return sum(p.numel() for p in model.parameters()) |
| |
|
| | def fmt_date_str(date=None, fmt="%y-%m-%d-%H-%M-%S"): |
| | """Format date to string. |
| | |
| | Args: |
| | datetime (datetime, optional): get current time if None. Defaults to None. |
| | |
| | Returns: |
| | str: formatted date string |
| | """ |
| | if date is None: |
| | date = datetime.now() |
| | return date.strftime(fmt) |
| |
|
| | def compute_bwt(acc_table, curr_acc, task_idx): |
| | ''' |
| | After training T tasks, $BWT = \frac{\sum_{i=3}^T\sum_{j=1}^{i-2}R_{i,j}-R{j,j}}{T(T-1)/2}$ |
| | Equivalent to Positive BwT of Continuum |
| | https://arxiv.org/pdf/1810.13166 |
| | ''' |
| |
|
| | if task_idx > 1: |
| | |
| | bwt = 0. |
| | for i in range(2, task_idx): |
| | for j in range(i - 1): |
| | bwt += acc_table[i, j] - acc_table[j, j] |
| |
|
| | for j in range(task_idx - 1): |
| | bwt += curr_acc[j] - acc_table[j, j] |
| |
|
| | return (bwt * 2) / (task_idx * (task_idx+1)) |
| |
|
| | return 0. |
| |
|
| |
|
| | def compute_frgt(acc_table, curr_acc, task_idx): |
| | ''' |
| | After training T tasks, $Frgt = \frac{\sum_{j=1}^{T-2}R_{T-1,j}-R_{j,j}}{T-1}$ |
| | Equivalent to Forgetting of Continuum |
| | ''' |
| |
|
| | if task_idx > 1: |
| | return sum(np.diag(acc_table)[:task_idx - 1] - curr_acc[:task_idx+1][:-2]) / task_idx |
| | return 0. |
| |
|
| |
|
| | def compute_fps(model, config): |
| | model.eval() |
| |
|
| | |
| |
|
| | data = { |
| | 'image' : torch.rand((2, 3, config['image_size'], config['image_size'])).cuda(), |
| | 'label' : torch.zeros((2)) |
| | } |
| |
|
| | t_all = [] |
| |
|
| | for i in range(100): |
| | t1 = time() |
| | if config['setting'] == 'task-aware': |
| | model.inference(data, task_id = random.randint(0, config['task_num'] - 1)) |
| | elif config['setting'] == 'task-agnostic': |
| | model.inference(data) |
| | t2 = time() |
| | t_all.append(t2 - t1) |
| |
|
| | return {'avg_fps' : 1 / np.mean(t_all), |
| | 'best_fps' : 1 / min(t_all)} |