class_docstr: Anchor-free head used in `FSAF <https://arxiv.org/abs/1903.00621>`_.
  
  The head contains two subnetworks. The first classifies anchor boxes and
  the second regresses deltas for the anchors (num_anchors is 1 for anchor-
  free methods)
  
  Args:
      *args: Same as its base class in :class:`RetinaHead`
      score_threshold (float, optional): The score_threshold to calculate
          positive recall. If given, prediction scores lower than this value
          is counted as incorrect prediction. Default to None.
      **kwargs: Same as its base class in :class:`RetinaHead`
  
  Example:
      >>> import torch
      >>> self = FSAFHead(11, 7)
      >>> x = torch.rand(1, 7, 32, 32)
      >>> cls_score, bbox_pred = self.forward_single(x)
      >>> # Each anchor predicts a score for each class except background
      >>> cls_per_anchor = cls_score.shape[1] / self.num_anchors
      >>> box_per_anchor = bbox_pred.shape[1] / self.num_anchors
      >>> assert cls_per_anchor == self.num_classes
      >>> assert box_per_anchor == 4
class_docstr_tok: ["Anchor-free","head","used","in","`","FSAF","<","https",":","\/\/arxiv.org\/abs\/1903.00621",">","`","_",".","The","head","contains","two","subnetworks",".","The","first","classifies","anchor","boxes","and","the","second","regresses","deltas","for","the","anchors","(","num_anchors","is","1","for","anchor-","free","methods",")","Args",":","*","args",":","Same","as","its","base","class","in",":","class",":","`","RetinaHead","`","score_threshold","(","float",",","optional",")",":","The","score_threshold","to","calculate","positive","recall",".","If","given",",","prediction","scores","lower","than","this","value","is","counted","as","incorrect","prediction",".","Default","to","None",".","*","*","kwargs",":","Same","as","its","base","class","in",":","class",":","`","RetinaHead","`","Example",":",">",">",">","import","torch",">",">",">","self","=","FSAFHead","(","11",",","7",")",">",">",">","x","=","torch.rand","(","1",",","7",",","32",",","32",")",">",">",">","cls_score",",","bbox_pred","=","self.forward_single","(","x",")",">",">",">","#","Each","anchor","predicts","a","score","for","each","class","except","background",">",">",">","cls_per_anchor","=","cls_score.shape","[","1","]","\/","self.num_anchors",">",">",">","box_per_anchor","=","bbox_pred.shape","[","1","]","\/","self.num_anchors",">",">",">","assert","cls_per_anchor","==","self.num_classes",">",">",">","assert","box_per_anchor","==","4"]
docstr: Compute loss of the head.
  
  Args:
      cls_scores (list[Tensor]): Box scores for each scale level
          Has shape (N, num_points * num_classes, H, W).
      bbox_preds (list[Tensor]): Box energies / deltas for each scale
          level with shape (N, num_points * 4, H, W).
      gt_bboxes (list[Tensor]): each item are the truth boxes for each
          image in [tl_x, tl_y, br_x, br_y] format.
      gt_labels (list[Tensor]): class indices corresponding to each box
      img_metas (list[dict]): Meta information of each image, e.g.,
          image size, scaling factor, etc.
      gt_bboxes_ignore (None | list[Tensor]): specify which bounding
          boxes can be ignored when computing the loss.
  
  Returns:
      dict[str, Tensor]: A dictionary of loss components.
docstr_tok: ["Compute","loss","of","the","head",".","Args",":","cls_scores","(","list","[","Tensor","]",")",":","Box","scores","for","each","scale","level","Has","shape","(","N",",","num_points","*","num_classes",",","H",",","W",")",".","bbox_preds","(","list","[","Tensor","]",")",":","Box","energies","\/","deltas","for","each","scale","level","with","shape","(","N",",","num_points","*","4",",","H",",","W",")",".","gt_bboxes","(","list","[","Tensor","]",")",":","each","item","are","the","truth","boxes","for","each","image","in","[","tl_x",",","tl_y",",","br_x",",","br_y","]","format",".","gt_labels","(","list","[","Tensor","]",")",":","class","indices","corresponding","to","each","box","img_metas","(","list","[","dict","]",")",":","Meta","information","of","each","image",",","e.g.",",","image","size",",","scaling","factor",",","etc",".","gt_bboxes_ignore","(","None","|","list","[","Tensor","]",")",":","specify","which","bounding","boxes","can","be","ignored","when","computing","the","loss",".","Returns",":","dict","[","str",",","Tensor","]",":","A","dictionary","of","loss","components","."]
code: def loss(
      self,
      cls_scores,
      bbox_preds,
      gt_bboxes,
      gt_labels,
      img_metas,
      gt_bboxes_ignore=None,
  ):
      """Compute loss of the head.
  
      Args:
          cls_scores (list[Tensor]): Box scores for each scale level
              Has shape (N, num_points * num_classes, H, W).
          bbox_preds (list[Tensor]): Box energies / deltas for each scale
              level with shape (N, num_points * 4, H, W).
          gt_bboxes (list[Tensor]): each item are the truth boxes for each
              image in [tl_x, tl_y, br_x, br_y] format.
          gt_labels (list[Tensor]): class indices corresponding to each box
          img_metas (list[dict]): Meta information of each image, e.g.,
              image size, scaling factor, etc.
          gt_bboxes_ignore (None | list[Tensor]): specify which bounding
              boxes can be ignored when computing the loss.
  
      Returns:
          dict[str, Tensor]: A dictionary of loss components.
      """
      for i in range(len(bbox_preds)):  # loop over fpn level
          # avoid 0 area of the predicted bbox
          bbox_preds[i] = bbox_preds[i].clamp(min=1e-4)
      # TODO: It may directly use the base-class loss function.
      featmap_sizes = [featmap.size()[-2:] for featmap in cls_scores]
      assert len(featmap_sizes) == self.anchor_generator.num_levels
      batch_size = len(gt_bboxes)
      device = cls_scores[0].device
      anchor_list, valid_flag_list = self.get_anchors(
          featmap_sizes, img_metas, device=device
      )
      label_channels = (
          self.cls_out_channels if self.use_sigmoid_cls else 1
      )
      cls_reg_targets = self.get_targets(
          anchor_list,
          valid_flag_list,
          gt_bboxes,
          img_metas,
          gt_bboxes_ignore_list=gt_bboxes_ignore,
          gt_labels_list=gt_labels,
          label_channels=label_channels,
      )
      if cls_reg_targets is None:
          return None
      (
          labels_list,
          label_weights_list,
          bbox_targets_list,
          bbox_weights_list,
          num_total_pos,
          num_total_neg,
          pos_assigned_gt_inds_list,
      ) = cls_reg_targets
  
      num_gts = np.array(list(map(len, gt_labels)))
      num_total_samples = (
          num_total_pos + num_total_neg
          if self.sampling
          else num_total_pos
      )
      # anchor number of multi levels
      num_level_anchors = [
          anchors.size(0) for anchors in anchor_list[0]
      ]
      # concat all level anchors and flags to a single tensor
      concat_anchor_list = []
      for i in range(len(anchor_list)):
          concat_anchor_list.append(torch.cat(anchor_list[i]))
      all_anchor_list = images_to_levels(
          concat_anchor_list, num_level_anchors
      )
      losses_cls, losses_bbox = multi_apply(
          self.loss_single,
          cls_scores,
          bbox_preds,
          all_anchor_list,
          labels_list,
          label_weights_list,
          bbox_targets_list,
          bbox_weights_list,
          num_total_samples=num_total_samples,
      )
  
      # `pos_assigned_gt_inds_list` (length: fpn_levels) stores the assigned
      # gt index of each anchor bbox in each fpn level.
      cum_num_gts = list(np.cumsum(num_gts))  # length of batch_size
      for i, assign in enumerate(pos_assigned_gt_inds_list):
          # loop over fpn levels
          for j in range(1, batch_size):
              # loop over batch size
              # Convert gt indices in each img to those in the batch
              assign[j][assign[j] >= 0] += int(cum_num_gts[j - 1])
          pos_assigned_gt_inds_list[i] = assign.flatten()
          labels_list[i] = labels_list[i].flatten()
      num_gts = sum(
          map(len, gt_labels)
      )  # total number of gt in the batch
      # The unique label index of each gt in the batch
      label_sequence = torch.arange(num_gts, device=device)
      # Collect the average loss of each gt in each level
      with torch.no_grad():
          (loss_levels,) = multi_apply(
              self.collect_loss_level_single,
              losses_cls,
              losses_bbox,
              pos_assigned_gt_inds_list,
              labels_seq=label_sequence,
          )
          # Shape: (fpn_levels, num_gts). Loss of each gt at each fpn level
          loss_levels = torch.stack(loss_levels, dim=0)
          # Locate the best fpn level for loss back-propagation
          if loss_levels.numel() == 0:  # zero gt
              argmin = loss_levels.new_empty(
                  (num_gts,), dtype=torch.long
              )
          else:
              _, argmin = loss_levels.min(dim=0)
  
      # Reweight the loss of each (anchor, label) pair, so that only those
      #  at the best gt level are back-propagated.
      losses_cls, losses_bbox, pos_inds = multi_apply(
          self.reweight_loss_single,
          losses_cls,
          losses_bbox,
          pos_assigned_gt_inds_list,
          labels_list,
          list(range(len(losses_cls))),
          min_levels=argmin,
      )
      num_pos = torch.cat(pos_inds, 0).sum().float()
      pos_recall = self.calculate_pos_recall(
          cls_scores, labels_list, pos_inds
      )
  
      if num_pos == 0:  # No gt
          avg_factor = num_pos + float(num_total_neg)
      else:
          avg_factor = num_pos
      for i in range(len(losses_cls)):
          losses_cls[i] /= avg_factor
          losses_bbox[i] /= avg_factor
      return dict(
          loss_cls=losses_cls,
          loss_bbox=losses_bbox,
          num_pos=num_pos / batch_size,
          pos_recall=pos_recall,
      )
  
file_path: insightface/detection/scrfd/mmdet/models/dense_heads/fsaf_head.py
imports: import numpy
  import torch
  from mmcv.cnn import normal_init
  from mmcv.runner import force_fp32
  from mmdet.core import anchor_inside_flags, images_to_levels, multi_apply, unmap
  from ..builder import HEADS
  from ..losses.accuracy import accuracy
  from ..losses.utils import weight_reduce_loss
  from .retina_head import RetinaHead
label_desc: Use image node_id 5 for calling the FSAFHead obj's underlying member method code with example usage: obj.loss(cls_scores, bbox_preds, gt_bboxes, gt_labels, img_metas, gt_bboxes_ignore) and returns: dict, None