Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place. Commercial Alternative to JupyterHub.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place. Commercial Alternative to JupyterHub.
Path: blob/master/utils/loss.py
Views: 475
# YOLOv5 🚀 by Ultralytics, GPL-3.0 license1"""2Loss functions3"""45import torch6import torch.nn as nn78from utils.metrics import bbox_iou9from utils.torch_utils import is_parallel101112def smooth_BCE(eps=0.1): # https://github.com/ultralytics/yolov3/issues/238#issuecomment-59802844113# return positive, negative label smoothing BCE targets14return 1.0 - 0.5 * eps, 0.5 * eps151617class BCEBlurWithLogitsLoss(nn.Module):18# BCEwithLogitLoss() with reduced missing label effects.19def __init__(self, alpha=0.05):20super().__init__()21self.loss_fcn = nn.BCEWithLogitsLoss(reduction='none') # must be nn.BCEWithLogitsLoss()22self.alpha = alpha2324def forward(self, pred, true):25loss = self.loss_fcn(pred, true)26pred = torch.sigmoid(pred) # prob from logits27dx = pred - true # reduce only missing label effects28# dx = (pred - true).abs() # reduce missing label and false label effects29alpha_factor = 1 - torch.exp((dx - 1) / (self.alpha + 1e-4))30loss *= alpha_factor31return loss.mean()323334class FocalLoss(nn.Module):35# Wraps focal loss around existing loss_fcn(), i.e. criteria = FocalLoss(nn.BCEWithLogitsLoss(), gamma=1.5)36def __init__(self, loss_fcn, gamma=1.5, alpha=0.25):37super().__init__()38self.loss_fcn = loss_fcn # must be nn.BCEWithLogitsLoss()39self.gamma = gamma40self.alpha = alpha41self.reduction = loss_fcn.reduction42self.loss_fcn.reduction = 'none' # required to apply FL to each element4344def forward(self, pred, true):45loss = self.loss_fcn(pred, true)46# p_t = torch.exp(-loss)47# loss *= self.alpha * (1.000001 - p_t) ** self.gamma # non-zero power for gradient stability4849# TF implementation https://github.com/tensorflow/addons/blob/v0.7.1/tensorflow_addons/losses/focal_loss.py50pred_prob = torch.sigmoid(pred) # prob from logits51p_t = true * pred_prob + (1 - true) * (1 - pred_prob)52alpha_factor = true * self.alpha + (1 - true) * (1 - self.alpha)53modulating_factor = (1.0 - p_t) ** self.gamma54loss *= alpha_factor * modulating_factor5556if self.reduction == 'mean':57return loss.mean()58elif self.reduction == 'sum':59return loss.sum()60else: # 'none'61return loss626364class QFocalLoss(nn.Module):65# Wraps Quality focal loss around existing loss_fcn(), i.e. criteria = FocalLoss(nn.BCEWithLogitsLoss(), gamma=1.5)66def __init__(self, loss_fcn, gamma=1.5, alpha=0.25):67super().__init__()68self.loss_fcn = loss_fcn # must be nn.BCEWithLogitsLoss()69self.gamma = gamma70self.alpha = alpha71self.reduction = loss_fcn.reduction72self.loss_fcn.reduction = 'none' # required to apply FL to each element7374def forward(self, pred, true):75loss = self.loss_fcn(pred, true)7677pred_prob = torch.sigmoid(pred) # prob from logits78alpha_factor = true * self.alpha + (1 - true) * (1 - self.alpha)79modulating_factor = torch.abs(true - pred_prob) ** self.gamma80loss *= alpha_factor * modulating_factor8182if self.reduction == 'mean':83return loss.mean()84elif self.reduction == 'sum':85return loss.sum()86else: # 'none'87return loss888990class ComputeLoss:91# Compute losses92def __init__(self, model, autobalance=False):93self.sort_obj_iou = False94device = next(model.parameters()).device # get model device95h = model.hyp # hyperparameters9697# Define criteria98BCEcls = nn.BCEWithLogitsLoss(pos_weight=torch.tensor([h['cls_pw']], device=device))99BCEtheta = nn.BCEWithLogitsLoss(pos_weight=torch.tensor([h['theta_pw']], device=device))100BCEobj = nn.BCEWithLogitsLoss(pos_weight=torch.tensor([h['obj_pw']], device=device))101102# Class label smoothing https://arxiv.org/pdf/1902.04103.pdf eqn 3103self.cp, self.cn = smooth_BCE(eps=h.get('label_smoothing', 0.0)) # positive, negative BCE targets104105# Focal loss106g = h['fl_gamma'] # focal loss gamma107if g > 0:108BCEcls, BCEobj = FocalLoss(BCEcls, g), FocalLoss(BCEobj, g)109BCEtheta = FocalLoss(BCEtheta, g)110111det = model.module.model[-1] if is_parallel(model) else model.model[-1] # Detect() module112self.stride = det.stride # tensor([8., 16., 32., ...])113self.balance = {3: [4.0, 1.0, 0.4]}.get(det.nl, [4.0, 1.0, 0.25, 0.06, 0.02]) # P3-P7114# self.ssi = list(det.stride).index(16) if autobalance else 0 # stride 16 index115self.ssi = list(self.stride).index(16) if autobalance else 0 # stride 16 index116self.BCEcls, self.BCEobj, self.gr, self.hyp, self.autobalance = BCEcls, BCEobj, 1.0, h, autobalance117self.BCEtheta = BCEtheta118for k in 'na', 'nc', 'nl', 'anchors':119setattr(self, k, getattr(det, k))120121def __call__(self, p, targets): # predictions, targets, model122"""123Args:124p (list[P3_out,...]): torch.Size(b, self.na, h_i, w_i, self.no), self.na means the number of anchors scales125targets (tensor): (n_gt_all_batch, [img_index clsid cx cy l s theta gaussian_θ_labels])126127Return:128total_loss * bs (tensor): [1]129torch.cat((lbox, lobj, lcls, ltheta)).detach(): [4]130"""131device = targets.device132lcls, lbox, lobj = torch.zeros(1, device=device), torch.zeros(1, device=device), torch.zeros(1, device=device)133ltheta = torch.zeros(1, device=device)134# tcls, tbox, indices, anchors = self.build_targets(p, targets) # targets135tcls, tbox, indices, anchors, tgaussian_theta = self.build_targets(p, targets) # targets136137# Losses138for i, pi in enumerate(p): # layer index, layer predictions139b, a, gj, gi = indices[i] # image, anchor, gridy, gridx140tobj = torch.zeros_like(pi[..., 0], device=device) # target obj141142n = b.shape[0] # number of targets143if n:144ps = pi[b, a, gj, gi] # prediction subset corresponding to targets, (n_targets, self.no)145146# Regression147pxy = ps[:, :2].sigmoid() * 2 - 0.5148pwh = (ps[:, 2:4].sigmoid() * 2) ** 2 * anchors[i] # featuremap pixel149pbox = torch.cat((pxy, pwh), 1) # predicted box150iou = bbox_iou(pbox.T, tbox[i], x1y1x2y2=False, CIoU=True) # iou(prediction, target)151lbox += (1.0 - iou).mean() # iou loss152153# Objectness154score_iou = iou.detach().clamp(0).type(tobj.dtype)155if self.sort_obj_iou:156sort_id = torch.argsort(score_iou)157b, a, gj, gi, score_iou = b[sort_id], a[sort_id], gj[sort_id], gi[sort_id], score_iou[sort_id]158tobj[b, a, gj, gi] = (1.0 - self.gr) + self.gr * score_iou # iou ratio159160# Classification161class_index = 5 + self.nc162if self.nc > 1: # cls loss (only if multiple classes)163# t = torch.full_like(ps[:, 5:], self.cn, device=device) # targets164t = torch.full_like(ps[:, 5:class_index], self.cn, device=device) # targets165t[range(n), tcls[i]] = self.cp166# lcls += self.BCEcls(ps[:, 5:], t) # BCE167lcls += self.BCEcls(ps[:, 5:class_index], t) # BCE168169# theta Classification by Circular Smooth Label170t_theta = tgaussian_theta[i].type(ps.dtype) # target theta_gaussian_labels171ltheta += self.BCEtheta(ps[:, class_index:], t_theta)172173# Append targets to text file174# with open('targets.txt', 'a') as file:175# [file.write('%11.5g ' * 4 % tuple(x) + '\n') for x in torch.cat((txy[i], twh[i]), 1)]176177obji = self.BCEobj(pi[..., 4], tobj)178lobj += obji * self.balance[i] # obj loss179if self.autobalance:180self.balance[i] = self.balance[i] * 0.9999 + 0.0001 / obji.detach().item()181182if self.autobalance:183self.balance = [x / self.balance[self.ssi] for x in self.balance]184lbox *= self.hyp['box']185lobj *= self.hyp['obj']186lcls *= self.hyp['cls']187ltheta *= self.hyp['theta']188bs = tobj.shape[0] # batch size189190# return (lbox + lobj + lcls) * bs, torch.cat((lbox, lobj, lcls)).detach()191return (lbox + lobj + lcls + ltheta) * bs, torch.cat((lbox, lobj, lcls, ltheta)).detach()192193def build_targets(self, p, targets):194"""195Args:196p (list[P3_out,...]): torch.Size(b, self.na, h_i, w_i, self.no), self.na means the number of anchors scales197targets (tensor): (n_gt_all_batch, [img_index clsid cx cy l s theta gaussian_θ_labels]) pixel198199Return:non-normalized data200tcls (list[P3_out,...]): len=self.na, tensor.size(n_filter2)201tbox (list[P3_out,...]): len=self.na, tensor.size(n_filter2, 4) featuremap pixel202indices (list[P3_out,...]): len=self.na, tensor.size(4, n_filter2) [b, a, gj, gi]203anch (list[P3_out,...]): len=self.na, tensor.size(n_filter2, 2)204tgaussian_theta (list[P3_out,...]): len=self.na, tensor.size(n_filter2, hyp['cls_theta'])205# ttheta (list[P3_out,...]): len=self.na, tensor.size(n_filter2)206"""207# Build targets for compute_loss()208na, nt = self.na, targets.shape[0] # number of anchors, targets209tcls, tbox, indices, anch = [], [], [], []210# ttheta, tgaussian_theta = [], []211tgaussian_theta = []212# gain = torch.ones(7, device=targets.device) # normalized to gridspace gain213feature_wh = torch.ones(2, device=targets.device) # feature_wh214ai = torch.arange(na, device=targets.device).float().view(na, 1).repeat(1, nt) # same as .repeat_interleave(nt)215# targets (tensor): (n_gt_all_batch, c) -> (na, n_gt_all_batch, c) -> (na, n_gt_all_batch, c+1)216# targets (tensor): (na, n_gt_all_batch, [img_index, clsid, cx, cy, l, s, theta, gaussian_θ_labels, anchor_index]])217targets = torch.cat((targets.repeat(na, 1, 1), ai[:, :, None]), 2) # append anchor indices218219g = 0.5 # bias220off = torch.tensor([[0, 0], # tensor: (5, 2)221[1, 0], [0, 1], [-1, 0], [0, -1], # j,k,l,m222# [1, 1], [1, -1], [-1, 1], [-1, -1], # jk,jm,lk,lm223], device=targets.device).float() * g # offsets224225for i in range(self.nl):226anchors = self.anchors[i]227# gain[2:6] = torch.tensor(p[i].shape)[[3, 2, 3, 2]] # xyxy gain=[1, 1, w, h, w, h, 1, 1]228feature_wh[0:2] = torch.tensor(p[i].shape)[[3, 2]] # xyxy gain=[w_f, h_f]229230# Match targets to anchors231# t = targets * gain # xywh featuremap pixel232t = targets.clone() # (na, n_gt_all_batch, c+1)233t[:, :, 2:6] /= self.stride[i] # xyls featuremap pixel234if nt:235# Matches236r = t[:, :, 4:6] / anchors[:, None] # edge_ls ratio, torch.size(na, n_gt_all_batch, 2)237j = torch.max(r, 1 / r).max(2)[0] < self.hyp['anchor_t'] # compare, torch.size(na, n_gt_all_batch)238# j = wh_iou(anchors, t[:, 4:6]) > model.hyp['iou_t'] # iou(3,n)=wh_iou(anchors(3,2), gwh(n,2))239t = t[j] # filter; Tensor.size(n_filter1, c+1)240241# Offsets242gxy = t[:, 2:4] # grid xy; (n_filter1, 2)243# gxi = gain[[2, 3]] - gxy # inverse244gxi = feature_wh[[0, 1]] - gxy # inverse245j, k = ((gxy % 1 < g) & (gxy > 1)).T246l, m = ((gxi % 1 < g) & (gxi > 1)).T247j = torch.stack((torch.ones_like(j), j, k, l, m)) # (5, n_filter1)248t = t.repeat((5, 1, 1))[j] # (n_filter1, c+1) -> (5, n_filter1, c+1) -> (n_filter2, c+1)249offsets = (torch.zeros_like(gxy)[None] + off[:, None])[j] # (5, n_filter1, 2) -> (n_filter2, 2)250else:251t = targets[0] # (n_gt_all_batch, c+1)252offsets = 0253254# Define, t (tensor): (n_filter2, [img_index, clsid, cx, cy, l, s, theta, gaussian_θ_labels, anchor_index])255b, c = t[:, :2].long().T # image, class; (n_filter2)256gxy = t[:, 2:4] # grid xy257gwh = t[:, 4:6] # grid wh258# theta = t[:, 6]259gaussian_theta_labels = t[:, 7:-1]260gij = (gxy - offsets).long()261gi, gj = gij.T # grid xy indices262263# Append264a = t[:, -1].long() # anchor indices 取整265# indices.append((b, a, gj.clamp_(0, gain[3] - 1), gi.clamp_(0, gain[2] - 1))) # image, anchor, grid indices266indices.append((b, a, gj.clamp_(0, feature_wh[1] - 1), gi.clamp_(0, feature_wh[0] - 1))) # image, anchor, grid indices267tbox.append(torch.cat((gxy - gij, gwh), 1)) # box268anch.append(anchors[a]) # anchors269tcls.append(c) # class270# ttheta.append(theta) # theta, θ∈[-pi/2, pi/2)271tgaussian_theta.append(gaussian_theta_labels)272273# return tcls, tbox, indices, anch274return tcls, tbox, indices, anch, tgaussian_theta #, ttheta275276277