def test_focal_loss_equals_ce_loss_multi_class(self) -> None: """ Focal loss with predictions for multiple classes matches ce loss. """ inputs = logit( torch.tensor( [[ [0.95, 0.55, 0.12, 0.05], [0.09, 0.95, 0.36, 0.11], [0.06, 0.12, 0.56, 0.07], [0.09, 0.15, 0.25, 0.45], ]], dtype=torch.float32, )) targets = torch.tensor( [[[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]], dtype=torch.float32, ) focal_loss = sigmoid_focal_loss(inputs, targets, gamma=0, alpha=-1, reduction="mean") ce_loss = F.binary_cross_entropy_with_logits(inputs, targets, reduction="mean") self.assertEqual(ce_loss, focal_loss)
def score_box_point_loss(target_scores, target_box_deltas, target_point_deltas, pred_logits, pred_box_deltas, pred_point_deltas, point_mask): # target_scores: [B, k] # target_box_deltas: [B, k, 4] # target_point_deltas: [B, k, p, 2] # point_mask: [B, k] score_loss = sigmoid_focal_loss(pred_logits, target_scores, reduction='sum') maxscore, _ = target_scores.max(-1) pos_mask = maxscore > 0.5 target_box_deltas_pos = target_box_deltas[pos_mask].reshape(-1, 2, 2) pred_box_deltas_pos = pred_box_deltas[pos_mask].reshape(-1, 2, 2) box_loss = euclidean_loss(pred_box_deltas_pos, target_box_deltas_pos, 'sum') point_mask = pos_mask & point_mask target_point = target_point_deltas[point_mask] # -1, p, 2 pred_point = pred_point_deltas[point_mask] # -1, p, 2 point_loss = euclidean_loss(pred_point, target_point, 'sum') npos = target_box_deltas_pos.shape[0] npoint = target_point.shape[0] return score_loss / npos, box_loss / npos, point_loss / npoint
def test_focal_loss_equals_ce_loss(self) -> None: """ No weighting of easy/hard (gamma = 0) or positive/negative (alpha = 0). """ inputs = logit( torch.tensor([[[0.95], [0.90], [0.98], [0.99]]], dtype=torch.float32)) targets = torch.tensor([[[1], [1], [1], [1]]], dtype=torch.float32) inputs_fl = inputs.clone().requires_grad_() targets_fl = targets.clone() inputs_ce = inputs.clone().requires_grad_() targets_ce = targets.clone() focal_loss = sigmoid_focal_loss(inputs_fl, targets_fl, gamma=0, alpha=-1, reduction="mean") ce_loss = F.binary_cross_entropy_with_logits(inputs_ce, targets_ce, reduction="mean") self.assertEqual(ce_loss, focal_loss.data) focal_loss.backward() ce_loss.backward() self.assertTrue( # pyre-ignore torch.allclose(inputs_fl.grad.data, inputs_ce.grad.data))
def test_positives_ignored_focal_loss(self) -> None: """ With alpha = 0 postive examples have focal loss of 0. """ inputs = logit( torch.tensor( [[[0.05], [0.12], [0.89], [0.79]]], dtype=torch.float32 ) ) targets = torch.tensor([[[1], [1], [0], [0]]], dtype=torch.float32) focal_loss = ( sigmoid_focal_loss(inputs, targets, gamma=2, alpha=0) .squeeze() .numpy() ) ce_loss = ( F.binary_cross_entropy_with_logits( inputs, targets, reduction="none" ) .squeeze() .numpy() ) targets = targets.squeeze().numpy() self.assertTrue(np.all(ce_loss[targets == 1] > 0)) self.assertTrue(np.all(focal_loss[targets == 1] == 0))
def run_focal_loss() -> None: fl = sigmoid_focal_loss(inputs, targets, gamma=0, alpha=alpha, reduction="mean") fl.backward() torch.cuda.synchronize()
def forward(self, input: Tensor, target: Tensor): """ Shape: - Input: : $(N, *)$ where $*$ means, any number of additional dimensions. - Target: : $(N, *)$, same shape as the input. - Output: scalar. If `reduction` is 'none', then $(N, *)$ , same shape as input. """ loss = sigmoid_focal_loss(input, target, self.gamma, self.alpha, self.reduction) return loss
def test_hard_ex_focal_loss_similar_to_ce_loss(self) -> None: """ With gamma = 2 loss of hard examples is unchanged. """ inputs = logit( torch.tensor([0.05, 0.12, 0.09, 0.17], dtype=torch.float32)) targets = torch.tensor([1, 1, 1, 1], dtype=torch.float32) focal_loss = sigmoid_focal_loss(inputs, targets, gamma=2, alpha=-1) ce_loss = F.binary_cross_entropy_with_logits(inputs, targets, reduction="none") loss_ratio = (ce_loss / focal_loss).squeeze() correct_ratio = 1.0 / ((1.0 - inputs.sigmoid())**2) self.assertTrue(np.allclose(loss_ratio, correct_ratio))
def test_easy_ex_focal_loss_weighted_less_than_ce_loss(self) -> None: """ With gamma = 2, alpha = 0.5 loss of easy examples is downweighted. """ inputs = logit( torch.tensor([[[0.95], [0.90], [0.6], [0.3]]], dtype=torch.float64)) targets = torch.tensor([[[1], [1], [1], [1]]], dtype=torch.float64) focal_loss = sigmoid_focal_loss(inputs, targets, gamma=2, alpha=0.5) ce_loss = F.binary_cross_entropy_with_logits(inputs, targets, reduction="none") loss_ratio = (ce_loss / focal_loss).squeeze() correct_ratio = 2.0 / ((1.0 - inputs.squeeze().sigmoid())**2) self.assertTrue(np.allclose(loss_ratio, correct_ratio))
def test_easy_ex_focal_loss_less_than_ce_loss(self) -> None: """ With gamma = 2 loss of easy examples is downweighted. """ N = 5 inputs = logit(torch.rand(N)) targets = torch.randint(0, 2, (N, )).float() focal_loss = sigmoid_focal_loss(inputs, targets, gamma=2, alpha=-1) ce_loss = F.binary_cross_entropy_with_logits(inputs, targets, reduction="none") loss_ratio = (ce_loss / focal_loss).squeeze() prob = torch.sigmoid(inputs) p_t = prob * targets + (1 - prob) * (1 - targets) correct_ratio = 1.0 / ((1.0 - p_t)**2) self.assertTrue(np.allclose(loss_ratio, correct_ratio))
def test_sum_focal_loss_equals_ce_loss(self) -> None: """ Sum of focal loss across all examples matches ce loss. """ inputs = logit( torch.tensor([[[0.05], [0.12], [0.89], [0.79]]], dtype=torch.float32)) targets = torch.tensor([[[1], [1], [0], [0]]], dtype=torch.float32) focal_loss = sigmoid_focal_loss(inputs, targets, gamma=0, alpha=-1, reduction="sum") ce_loss = F.binary_cross_entropy_with_logits(inputs, targets, reduction="sum") self.assertEqual(ce_loss, focal_loss)
def score_box_loss(target_scores, target_deltas, pred_logits, pred_deltas): # target_scores: [B, k] # target_deltas: [B, k, 4] # pred_scores: [B, k] # pred_deltas: [B, k, 4] score_loss = sigmoid_focal_loss(pred_logits, target_scores, reduction='sum') maxscore, _ = target_scores.max(-1) pos_mask = maxscore > 0.5 target_box_deltas_pos = target_deltas[pos_mask].reshape(-1, 2, 2) pred_box_deltas_pos = pred_deltas[pos_mask].reshape(-1, 2, 2) box_loss = euclidean_loss(pred_box_deltas_pos, target_box_deltas_pos, 'sum') npos = target_box_deltas_pos.shape[0] return score_loss / npos, box_loss / npos
def test_mean_focal_loss_equals_ce_loss(self) -> None: """ Mean value of focal loss across all examples matches ce loss. """ inputs = logit( torch.tensor( [[0.05, 0.9], [0.52, 0.45], [0.89, 0.8], [0.39, 0.5]], dtype=torch.float32, )) targets = torch.tensor([[1, 0], [1, 0], [1, 1], [0, 1]], dtype=torch.float32) focal_loss = sigmoid_focal_loss(inputs, targets, gamma=0, alpha=-1, reduction="mean") ce_loss = F.binary_cross_entropy_with_logits(inputs, targets, reduction="mean") self.assertEqual(ce_loss, focal_loss)
def focal_loss(self): from fvcore.nn import sigmoid_focal_loss self._log_accuracy() bg_class_ind = self.pred_class_logits.shape[1] - 1 fg_inds = (self.gt_classes >= 0) & (self.gt_classes < bg_class_ind) num_foreground = fg_inds.nonzero().numel() gt_classes_target = torch.zeros_like(self.pred_class_logits) gt_classes_target[fg_inds, self.gt_classes[fg_inds]] = 1 loss_cls = sigmoid_focal_loss( self.pred_class_logits[:, 0:bg_class_ind], gt_classes_target[:, 0:bg_class_ind], alpha=self.focal_loss_alpha, gamma=self.focal_loss_gamma, reduction="sum", ) / max(1, num_foreground) # logger.info("___________________________softmax cross entropy loss would have been: {}_________________________" # "__".format(F.cross_entropy(self.pred_class_logits, self.gt_classes, reduction="mean"))) # logger.info("classification loss divided by: {}".format(max(1, num_foreground))) return loss_cls
def losses(self, gt_clses, gt_belongs, gt_masks, pred_logits, pred_points, ins_feature): """ Args: For `gt_clses`, `gt_belongs`, `gt_masks` parameters, see :meth:`TensorMask.get_ground_truth`. For `pred_logits` and `pred_points`, see :meth:`TensorMaskHead.forward`. Returns: losses (dict[str: Tensor]): mapping from a named loss to a scalar tensor storing the loss. Used during training only. The potential dict keys are: "loss_cls", and "loss_mask". """ num_fg = gt_belongs.size(0) w, h = pred_points.size(2), pred_points.size(3) max_side = w * 0.5 if w > h else h * 0.5 # print(num_fg,torch.sum(gt_clses)) loss_normalizer = torch.tensor(max(1, num_fg), dtype=torch.float32, device=self.device) # classification loss_cls = (sigmoid_focal_loss( pred_logits, gt_clses, alpha=self.focal_loss_alpha, gamma=self.focal_loss_gamma, reduction="sum", ) / loss_normalizer) losses = { "loss_cls": loss_cls, } # mask prediction loss_mask = 0 if num_fg < 1: losses = { "loss_cls": loss_cls, "loss_mask": torch.sum(pred_points * 0) } return losses pred_points_valids = pred_points[gt_belongs[:, 0], :, gt_belongs[:, 1], gt_belongs[:, 2]] N, P = pred_points_valids.size() pred_location = pred_points_valids.detach().view(N, P // 2, 2) location = ( pred_location + gt_belongs[:, 1:].view(N, 1, 2) ) * self.classify_feature_strides[-1] / self.ins_feature_strides[0] location = location.float() pred_points_valids = pred_points_valids.view(N, P // 2, 2, 1) Q = gt_masks.size(1) gt_masks = gt_masks.view(N, 1, Q, 2) gt_masks = gt_masks.transpose(2, 3) pred_points_valids_contour = pred_points_valids batch_index = gt_belongs[:, 0] # print(pred_points_valids_contour.size(),gt_masks.size()) #chamfer distance: # loss_mask=chamfer_loss(pred_points_valids_contour,gt_masks)*self.mask_loss_weight #normlized chamfer distance loss_mask1 = normlize_chamfer_loss( pred_points_valids_contour, gt_masks, max_side=max_side) * self.mask_loss_weight dist = emd_l1_loss2(pred_points_valids_contour, gt_masks) loss_mask2 = torch.mean(dist) points_delta = self.ins_head(ins_feature, location, batch_index) contour_weight = torch.zeros_like(points_delta) + 0.1 contour_weight[:, :81 * 3, :] = 1 loss_delta = torch.mean( torch.abs(points_delta - dist) * contour_weight) losses["loss_mask"] = loss_mask1 + loss_mask2 losses["loss_delta"] = loss_delta return losses
for img_data, labels_data in tqdm(dataloader_train): labels_data[labels_data > 1] = 1 img_label = labels_data.numpy().astype(np.float) optimizer.zero_grad() if opt.gpu_id >= 0: img_data = img_data.cuda() labels_data = labels_data.cuda().float() classes = model(img_data) loss_dis = sigmoid_focal_loss(classes, labels_data.view(-1, 1), alpha=0.25, gamma=2, reduction="mean") loss_dis_data = loss_dis.item() loss_dis.backward() optimizer.step() classes = classes.view(1, -1).squeeze(0) classes = torch.sigmoid(classes) output_dis = classes.data.cpu().numpy() tol_label = np.concatenate((tol_label, img_label)) tol_pred = np.concatenate((tol_pred, output_dis)) loss_train += loss_dis_data
def losses( self, gt_clses, gt_belongs, gt_masks, gt_ins, pred_logits, pred_points, ins_feature ): """ Args: For `gt_clses`, `gt_belongs`, `gt_masks` parameters, see :meth:`TensorMask.get_ground_truth`. For `pred_logits` and `pred_points`, see :meth:`TensorMaskHead.forward`. Returns: losses (dict[str: Tensor]): mapping from a named loss to a scalar tensor storing the loss. Used during training only. The potential dict keys are: "loss_cls", and "loss_mask". """ num_fg=gt_belongs.size(0) w,h=pred_points.size(2),pred_points.size(3) max_side=w*0.5 if w>h else h*0.5 # print(num_fg,torch.sum(gt_clses)) loss_normalizer = torch.tensor(max(1, num_fg), dtype=torch.float32, device=self.device) # classification loss_cls = ( sigmoid_focal_loss( pred_logits, gt_clses, alpha=self.focal_loss_alpha, gamma=self.focal_loss_gamma, reduction="sum", ) / loss_normalizer ) losses = {"loss_cls": loss_cls, } # mask prediction loss_mask = 0 if num_fg<1: losses= {"loss_cls": loss_cls, "loss_mask":torch.sum(pred_points*0)} return losses pred_points_valids=pred_points[gt_belongs[:,0],:,gt_belongs[:,1],gt_belongs[:,2]] N,P=pred_points_valids.size() # pred_location=(pred_points_valids.view(N,P//2,2)+gt_belongs[:,1:].view(N,1,2))*4 pred_points_valids=pred_points_valids.view(N,P//2,2,1) Q=gt_masks.size(1) location=(gt_masks+gt_belongs[:,1:].view(N,1,2))*self.classify_feature_strides[-1]/self.ins_feature_strides[0] location=location.float() gt_masks=gt_masks.view(N,1,Q,2) gt_masks=gt_masks.transpose(2,3) pred_points_valids_contour=pred_points_valids batch_index=gt_belongs[:,0] pred_ins=self.ins_head(ins_feature,location,batch_index) # _,_,h,w=gt_ins.size() pred_ins=F.interpolate(pred_ins,scale_factor=self.ins_feature_strides[0],mode='bilinear').squeeze(1) loss_ins=F.binary_cross_entropy_with_logits(pred_ins,gt_ins)*self.ins_loss_weight losses['loss_ins'] = loss_ins # print(pred_points_valids_contour.size(),gt_masks.size()) #chamfer distance: # loss_mask=chamfer_loss(pred_points_valids_contour,gt_masks)*self.mask_loss_weight #normlized chamfer distance # loss_mask=normlize_chamfer_loss(pred_points_valids_contour,gt_masks,max_side=max_side)*self.mask_loss_weight loss_mask=emd_l1_loss(pred_points_valids_contour,gt_masks)*self.mask_loss_weight losses["loss_mask"] = loss_mask return losses
def losses( self, gt_clses, gt_belongs, gt_masks, pred_logits, pred_points, ): """ Args: For `gt_clses`, `gt_belongs`, `gt_masks` parameters, see :meth:`TensorMask.get_ground_truth`. For `pred_logits` and `pred_points`, see :meth:`TensorMaskHead.forward`. Returns: losses (dict[str: Tensor]): mapping from a named loss to a scalar tensor storing the loss. Used during training only. The potential dict keys are: "loss_cls", and "loss_mask". """ num_fg = gt_belongs.size(0) w, h = pred_points.size(2), pred_points.size(3) max_side = w * 0.5 if w > h else h * 0.5 # print(num_fg,torch.sum(gt_clses)) loss_normalizer = torch.tensor(max(1, num_fg), dtype=torch.float32, device=self.device) # classification loss_cls = (sigmoid_focal_loss( pred_logits, gt_clses, alpha=self.focal_loss_alpha, gamma=self.focal_loss_gamma, reduction="sum", ) / loss_normalizer) losses = { "loss_cls": loss_cls, } # mask prediction loss_mask = 0 if num_fg < 1: losses = { "loss_cls": loss_cls, "loss_mask": torch.sum(pred_points * 0) } return losses pred_points_valids = pred_points[gt_belongs[:, 0], :, gt_belongs[:, 1], gt_belongs[:, 2]] N, P = pred_points_valids.size() pred_points_valids = pred_points_valids.view(N, P // 2, 2, 1) Q = gt_masks.size(1) gt_masks = gt_masks.view(N, 1, Q, 2) gt_masks = gt_masks.transpose(2, 3) pred_points_valids_contour = pred_points_valids if self.circumscribed: num_points = P // 2 edge = np.sqrt(num_points) # top=[0+i for i in range(edge)] # left=[i*edge+0 for i in range(1,edge-1)] # right=[i*edge+edge-1 for i in range(1,edge-1)] # bottom=[(edge-1)*edge+i for i in range(edge)] # Index=top+left+right+bottom All = [i * edge + j for i in range(edge) for j in range(edge)] Inner = [ i * edge + j for i in range(1, edge - 1) for j in range(1, edge - 1) ] Out = list(set(All) - set(Inner)) Out = torch.tensor(Out, dtype=torch.int64, device=self.device) pred_points_valids_contour = pred_points_valids[:, Out, :, :] # Inner=torch.tensor(Inner,dtype=torch.int64,device=self.device) # pred_points_valids_inner=pred_points_valids[:,Inner,:,:] # print(pred_points_valids_contour.size(),gt_masks.size()) #chamfer distance: # loss_mask1=chamfer_loss(pred_points_valids_contour,gt_masks)*self.mask_loss_weight #normlized chamfer distance loss_mask1 = normlize_chamfer_loss( pred_points_valids_contour, gt_masks, max_side=max_side) * self.mask_loss_weight loss_mask2 = emd_l1_loss(pred_points_valids_contour, gt_masks) * self.mask_loss_weight losses["loss_mask"] = loss_mask1 + loss_mask2 return losses