| import math |
| import numpy as np |
| import torch |
| import torch.nn.functional as F |
| from torch import optim |
| from torch.optim import Optimizer |
|
|
| class AverageMeter(object): |
| """Computes and stores the average and current value""" |
| def __init__(self): |
| self.reset() |
|
|
| def reset(self): |
| self.val = 0 |
| self.avg = 0 |
| self.sum = 0 |
| self.count = 0 |
|
|
| def update(self, val, n=1): |
| self.val = val |
| self.sum += val * n |
| self.count += n |
| self.avg = self.sum / self.count |
|
|
| def xyxy2xywh(x): |
| y = torch.zeros(x.shape) if x.dtype is torch.float32 else np.zeros(x.shape) |
| y[:, 0] = (x[:, 0] + x[:, 2]) / 2 |
| y[:, 1] = (x[:, 1] + x[:, 3]) / 2 |
| y[:, 2] = x[:, 2] - x[:, 0] |
| y[:, 3] = x[:, 3] - x[:, 1] |
| return y |
|
|
|
|
| def xywh2xyxy(x): |
| y = torch.zeros(x.shape) if x.dtype is torch.float32 else np.zeros(x.shape) |
| y[:, 0] = (x[:, 0] - x[:, 2] / 2) |
| y[:, 1] = (x[:, 1] - x[:, 3] / 2) |
| y[:, 2] = (x[:, 0] + x[:, 2] / 2) |
| y[:, 3] = (x[:, 1] + x[:, 3] / 2) |
| return y |
| |
| def bbox_iou_numpy(box1, box2): |
| """Computes IoU between bounding boxes. |
| Parameters |
| ---------- |
| box1 : ndarray |
| (N, 4) shaped array with bboxes |
| box2 : ndarray |
| (M, 4) shaped array with bboxes |
| Returns |
| ------- |
| : ndarray |
| (N, M) shaped array with IoUs |
| """ |
| area = (box2[:, 2] - box2[:, 0]) * (box2[:, 3] - box2[:, 1]) |
|
|
| iw = np.minimum(np.expand_dims(box1[:, 2], axis=1), box2[:, 2]) - np.maximum( |
| np.expand_dims(box1[:, 0], 1), box2[:, 0] |
| ) |
| ih = np.minimum(np.expand_dims(box1[:, 3], axis=1), box2[:, 3]) - np.maximum( |
| np.expand_dims(box1[:, 1], 1), box2[:, 1] |
| ) |
|
|
| iw = np.maximum(iw, 0) |
| ih = np.maximum(ih, 0) |
|
|
| ua = np.expand_dims((box1[:, 2] - box1[:, 0]) * (box1[:, 3] - box1[:, 1]), axis=1) + area - iw * ih |
|
|
| ua = np.maximum(ua, np.finfo(float).eps) |
|
|
| intersection = iw * ih |
|
|
| return intersection / ua |
|
|
|
|
| def bbox_iou(box1, box2, x1y1x2y2=True): |
| """ |
| Returns the IoU of two bounding boxes |
| """ |
| if x1y1x2y2: |
| |
| b1_x1, b1_y1, b1_x2, b1_y2 = box1[:, 0], box1[:, 1], box1[:, 2], box1[:, 3] |
| b2_x1, b2_y1, b2_x2, b2_y2 = box2[:, 0], box2[:, 1], box2[:, 2], box2[:, 3] |
| else: |
| |
| b1_x1, b1_x2 = box1[:, 0] - box1[:, 2] / 2, box1[:, 0] + box1[:, 2] / 2 |
| b1_y1, b1_y2 = box1[:, 1] - box1[:, 3] / 2, box1[:, 1] + box1[:, 3] / 2 |
| b2_x1, b2_x2 = box2[:, 0] - box2[:, 2] / 2, box2[:, 0] + box2[:, 2] / 2 |
| b2_y1, b2_y2 = box2[:, 1] - box2[:, 3] / 2, box2[:, 1] + box2[:, 3] / 2 |
|
|
| |
| inter_rect_x1 = torch.max(b1_x1, b2_x1) |
| inter_rect_y1 = torch.max(b1_y1, b2_y1) |
| inter_rect_x2 = torch.min(b1_x2, b2_x2) |
| inter_rect_y2 = torch.min(b1_y2, b2_y2) |
| |
| inter_area = torch.clamp(inter_rect_x2 - inter_rect_x1, 0) * torch.clamp(inter_rect_y2 - inter_rect_y1, 0) |
| |
| b1_area = (b1_x2 - b1_x1) * (b1_y2 - b1_y1) |
| b2_area = (b2_x2 - b2_x1) * (b2_y2 - b2_y1) |
|
|
| |
| |
| return inter_area / (b1_area + b2_area - inter_area + 1e-16) |
|
|
| def multiclass_metrics(pred, gt): |
| """ |
| check precision and recall for predictions. |
| Output: overall = {precision, recall, f1} |
| """ |
| eps=1e-6 |
| overall = {'precision': -1, 'recall': -1, 'f1': -1} |
| NP, NR, NC = 0, 0, 0 |
| for ii in range(pred.shape[0]): |
| pred_ind = np.array(pred[ii]>0.5, dtype=int) |
| gt_ind = np.array(gt[ii]>0.5, dtype=int) |
| inter = pred_ind * gt_ind |
| |
| NC += np.sum(inter) |
| NP += np.sum(pred_ind) |
| NR += np.sum(gt_ind) |
| if NP > 0: |
| overall['precision'] = float(NC)/NP |
| if NR > 0: |
| overall['recall'] = float(NC)/NR |
| if NP > 0 and NR > 0: |
| overall['f1'] = 2*overall['precision']*overall['recall']/(overall['precision']+overall['recall']+eps) |
| return overall |
|
|
| def compute_ap(recall, precision): |
| """ Compute the average precision, given the recall and precision curves. |
| Code originally from https://github.com/rbgirshick/py-faster-rcnn. |
| # Arguments |
| recall: The recall curve (list). |
| precision: The precision curve (list). |
| # Returns |
| The average precision as computed in py-faster-rcnn. |
| """ |
| |
| |
| mrec = np.concatenate(([0.0], recall, [1.0])) |
| mpre = np.concatenate(([0.0], precision, [0.0])) |
|
|
| |
| for i in range(mpre.size - 1, 0, -1): |
| mpre[i - 1] = np.maximum(mpre[i - 1], mpre[i]) |
|
|
| |
| |
| i = np.where(mrec[1:] != mrec[:-1])[0] |
|
|
| |
| ap = np.sum((mrec[i + 1] - mrec[i]) * mpre[i + 1]) |
| return ap |
|
|
| def concat_coord(x): |
| ins_feat = x |
| batch_size, c, h, w = x.size() |
|
|
| float_h = float(h) |
| float_w = float(w) |
|
|
| y_range = torch.arange(0., float_h, dtype=torch.float32) |
| y_range = 2.0 * y_range / (float_h - 1.0) - 1.0 |
| x_range = torch.arange(0., float_w, dtype=torch.float32) |
| x_range = 2.0 * x_range / (float_w - 1.0) - 1.0 |
| x_range = x_range[None, :] |
| y_range = y_range[:, None] |
| x = x_range.repeat(h, 1) |
| y = y_range.repeat(1, w) |
|
|
| x = x[None, None, :, :] |
| y = y[None, None, :, :] |
| x = x.repeat(batch_size, 1, 1, 1) |
| y = y.repeat(batch_size, 1, 1, 1) |
| x = x.cuda() |
| y = y.cuda() |
|
|
| ins_feat_out = torch.cat((ins_feat, x, x, x, y, y, y), 1) |
|
|
| return ins_feat_out |
|
|
|
|
| def get_cosine_schedule_with_warmup(optimizer: Optimizer, num_warmup_steps: int, num_training_steps: int, |
| num_cycles: float = 0.5, last_epoch: int = -1): |
| """ |
| Implementation by Huggingface: |
| https://github.com/huggingface/transformers/blob/v4.16.2/src/transformers/optimization.py |
| |
| Create a schedule with a learning rate that decreases following the values |
| of the cosine function between the initial lr set in the optimizer to 0, |
| after a warmup period during which it increases linearly between 0 and the |
| initial lr set in the optimizer. |
| Args: |
| optimizer ([`~torch.optim.Optimizer`]): |
| The optimizer for which to schedule the learning rate. |
| num_warmup_steps (`int`): |
| The number of steps for the warmup phase. |
| num_training_steps (`int`): |
| The total number of training steps. |
| num_cycles (`float`, *optional*, defaults to 0.5): |
| The number of waves in the cosine schedule (the defaults is to just |
| decrease from the max value to 0 following a half-cosine). |
| last_epoch (`int`, *optional*, defaults to -1): |
| The index of the last epoch when resuming training. |
| Return: |
| `torch.optim.lr_scheduler.LambdaLR` with the appropriate schedule. |
| """ |
|
|
| def lr_lambda(current_step): |
| if current_step < num_warmup_steps: |
| return max(1e-6, float(current_step) / float(max(1, num_warmup_steps))) |
| progress = float(current_step - num_warmup_steps) / float(max(1, num_training_steps - num_warmup_steps)) |
| return max(0.0, 0.5 * (1.0 + math.cos(math.pi * float(num_cycles) * 2.0 * progress))) |
|
|
| return optim.lr_scheduler.LambdaLR(optimizer, lr_lambda, last_epoch) |
|
|
| def dice_loss(inputs, targets): |
| """ |
| Compute the DICE loss, similar to generalized IOU for masks |
| Args: |
| inputs: A float tensor of arbitrary shape. |
| The predictions for each example. |
| targets: A float tensor with the same shape as inputs. Stores the binary |
| classification label for each element in inputs |
| (0 for the negative class and 1 for the positive class). |
| """ |
|
|
| inputs = inputs.sigmoid() |
| inputs = inputs.flatten(1) |
| targets = targets.flatten(1) |
| numerator = 2 * (inputs * targets).sum(1) |
| denominator = inputs.sum(-1) + targets.sum(-1) |
| loss = 1 - (numerator + 1) / (denominator + 1) |
| return loss.mean() |
|
|
| def sigmoid_focal_loss(inputs, targets, alpha: float = -1, gamma: float = 0): |
| """ |
| Loss used in RetinaNet for dense detection: https://arxiv.org/abs/1708.02002. |
| Args: |
| inputs: A float tensor of arbitrary shape. |
| The predictions for each example. |
| targets: A float tensor with the same shape as inputs. Stores the binary |
| classification label for each element in inputs |
| (0 for the negative class and 1 for the positive class). |
| alpha: (optional) Weighting factor in range (0,1) to balance |
| positive vs negative examples. Default = -1 (no weighting). |
| gamma: Exponent of the modulating factor (1 - p_t) to |
| balance easy vs hard examples. |
| Returns: |
| Loss tensor |
| """ |
|
|
| prob = inputs.sigmoid() |
| ce_loss = F.binary_cross_entropy_with_logits(inputs, targets, reduction="none") |
| p_t = prob * targets + (1 - prob) * (1 - targets) |
| loss = ce_loss * ((1 - p_t) ** gamma) |
|
|
| if alpha >= 0: |
| alpha_t = alpha * targets + (1 - alpha) * (1 - targets) |
| loss = alpha_t * loss |
| return loss.mean() |