|
import math |
|
from typing import Optional |
|
|
|
import torch |
|
|
|
|
|
class WarmupCosineLambda: |
|
def __init__(self, warmup_steps: int, cycle_steps: int, decay_scale: float, exponential_warmup: bool = False): |
|
self.warmup_steps = warmup_steps |
|
self.cycle_steps = cycle_steps |
|
self.decay_scale = decay_scale |
|
self.exponential_warmup = exponential_warmup |
|
|
|
def __call__(self, epoch: int): |
|
if epoch < self.warmup_steps: |
|
if self.exponential_warmup: |
|
return self.decay_scale * pow(self.decay_scale, -epoch / self.warmup_steps) |
|
ratio = epoch / self.warmup_steps |
|
else: |
|
ratio = (1 + math.cos(math.pi * (epoch - self.warmup_steps) / self.cycle_steps)) / 2 |
|
return self.decay_scale + (1 - self.decay_scale) * ratio |
|
|
|
|
|
def topk_average_precision(output: torch.Tensor, y: torch.Tensor, k: int): |
|
score_array = torch.tensor([1.0 / i for i in range(1, k + 1)], device=output.device) |
|
topk = output.topk(k)[1] |
|
match_mat = topk == y[:, None].expand(topk.shape) |
|
return (match_mat * score_array).sum(dim=1) |
|
|
|
|
|
def calc_map5(output: torch.Tensor, y: torch.Tensor, threshold: Optional[float]): |
|
if threshold is not None: |
|
output = torch.cat([output, torch.full((output.shape[0], 1), threshold, device=output.device)], dim=1) |
|
return topk_average_precision(output, y, 5).mean().detach() |
|
|
|
|
|
def map_dict(output: torch.Tensor, y: torch.Tensor, prefix: str): |
|
d = {f"{prefix}/acc": topk_average_precision(output, y, 1).mean().detach()} |
|
for threshold in [None, 0.3, 0.4, 0.5, 0.6, 0.7]: |
|
d[f"{prefix}/map{threshold}"] = calc_map5(output, y, threshold) |
|
return d |
|
|