FasterRCNN源码解析(十)——FastRCNN损失计算或者预测结果后处理

FasterRCNN源码解析(十)——FastRCNN损失计算以及预测结果后处理

文章目录

  • FasterRCNN源码解析(十)——FastRCNN损失计算以及预测结果后处理
  • 一. fastrcnn_loss
  • 二. postprocess_detections
  • 三. self.transform.postprocess
  • 总结


一. fastrcnn_loss

参数:
class_logits (Tensor): 预测类别概率信息,shape=[num_anchors, num_classes]
box_regression (Tensor): 预测边目标界框回归信息
labels (list[BoxList]): 真实类别信息
regression_targets (Tensor): 真实目标边界框信息

返回值:
classification_loss (Tensor)
box_loss (Tensor)
主要步骤有:

  1. 把所有照片的labels以及regression_targets拼接到一起
  2. 计算类别损失信息
  3. 返回标签类别大于0的索引(只有正样本才有意义)
  4. 返回标签类别大于0位置的类别信息
  5. 计算边界框损失信息
def fastrcnn_loss(class_logits, box_regression, labels, regression_targets):
    # type: (Tensor, Tensor, List[Tensor], List[Tensor]) -> Tuple[Tensor, Tensor]
    """
    Computes the loss for Faster R-CNN.

    Arguments:
        class_logits (Tensor): 预测类别概率信息,shape=[num_anchors, num_classes]
        box_regression (Tensor): 预测边目标界框回归信息
        labels (list[BoxList]): 真实类别信息
        regression_targets (Tensor): 真实目标边界框信息

    Returns:
        classification_loss (Tensor)
        box_loss (Tensor)
    """

    labels = torch.cat(labels, dim=0)
    regression_targets = torch.cat(regression_targets, dim=0)

    # 计算类别损失信息
    classification_loss = F.cross_entropy(class_logits, labels)

    # get indices that correspond to the regression targets for
    # the corresponding ground truth labels, to be used with
    # advanced indexing
    # 返回标签类别大于0的索引
    # sampled_pos_inds_subset = torch.nonzero(torch.gt(labels, 0)).squeeze(1)
    sampled_pos_inds_subset = torch.where(torch.gt(labels, 0))[0]

    # 返回标签类别大于0位置的类别信息
    labels_pos = labels[sampled_pos_inds_subset]

    # shape=[num_proposal, num_classes]
    N, num_classes = class_logits.shape
    box_regression = box_regression.reshape(N, -1, 4)

    # 计算边界框损失信息
    box_loss = det_utils.smooth_l1_loss(
        # 获取指定索引proposal的指定类别box信息
        box_regression[sampled_pos_inds_subset, labels_pos],
        regression_targets[sampled_pos_inds_subset],
        beta=1 / 9,
        size_average=False,
    ) / labels.numel()

    return classification_loss, box_loss


  1. 数据传输过程

主要步骤有:

  1. 把所有照片的labels以及regression_targets拼接到一起
  2. 计算类别损失信息
  3. 返回标签类别大于0的索引(只有正样本才有意义)
  4. 返回标签类别大于0位置的类别信息
  5. 计算边界框损失信息
    FasterRCNN源码解析(十)——FastRCNN损失计算或者预测结果后处理_第1张图片
    FasterRCNN源码解析(十)——FastRCNN损失计算或者预测结果后处理_第2张图片

二. postprocess_detections

在非训练模式下即在预测情况下才会对我们的结果进行后处理(不会对我们的样本进行划分的,直接使用proposal进行预测,rpn只提供1000个proposal)
对网络的预测数据进行后处理,包括
(1)根据proposal以及预测的回归参数计算出最终bbox坐标
(2)对预测类别结果进行softmax处理
(3)裁剪预测的boxes信息,将越界的坐标调整到图片边界上
(4)移除所有背景信息
(5)移除低概率目标
(6)移除小尺寸目标
(7)执行nms处理,并按scores进行排序
(8)根据scores排序返回前topk个目标
Args:
class_logits: 网络预测类别概率信息
box_regression: 网络预测的边界框回归参数
proposals: rpn输出的proposal
image_shapes: 打包成batch前每张图像的宽高

步骤:

  1. 获取每张图像的预测boxes的数量,其shape为在这里插入图片描述

  2. 根据proposal以及预测的回归参数计算出最终bbox坐标,其shape为
    在这里插入图片描述

  3. 对预测类别结果进行softmax处理,其shape为
    在这里插入图片描述

  4. 根据每张图像的预测bbox数量分割结果

  5. 对每张图片进行遍历
    1. 裁剪预测的boxes信息,将越界的坐标调整到图片边界上
    2. 移除索引为0的所有信息(0代表背景)
    3. 移除低概率目标,self.scores_thresh=0.05
    4. 移除小目标
    5. 执行nms处理,执行后的结果会按照scores从大到小进行排序返回
    6. 获取scores排在前topk个预测目标

  6. 最后返回所有图片的all_boxes, all_scores, all_labels信息

    def postprocess_detections(self,
                               class_logits,    # type: Tensor
                               box_regression,  # type: Tensor
                               proposals,       # type: List[Tensor]
                               image_shapes     # type: List[Tuple[int, int]]
                               ):
        # type: (...) -> Tuple[List[Tensor], List[Tensor], List[Tensor]]
        """
        对网络的预测数据进行后处理,包括
        (1)根据proposal以及预测的回归参数计算出最终bbox坐标
        (2)对预测类别结果进行softmax处理
        (3)裁剪预测的boxes信息,将越界的坐标调整到图片边界上
        (4)移除所有背景信息
        (5)移除低概率目标
        (6)移除小尺寸目标
        (7)执行nms处理,并按scores进行排序
        (8)根据scores排序返回前topk个目标
        Args:
            class_logits: 网络预测类别概率信息
            box_regression: 网络预测的边界框回归参数
            proposals: rpn输出的proposal
            image_shapes: 打包成batch前每张图像的宽高

        Returns:

        """
        device = class_logits.device
        # 预测目标类别数
        num_classes = class_logits.shape[-1]

        # 获取每张图像的预测bbox数量
        boxes_per_image = [boxes_in_image.shape[0] for boxes_in_image in proposals]
        # 根据proposal以及预测的回归参数计算出最终bbox坐标
        pred_boxes = self.box_coder.decode(box_regression, proposals)

        # 对预测类别结果进行softmax处理
        pred_scores = F.softmax(class_logits, -1)

        # split boxes and scores per image
        # 根据每张图像的预测bbox数量分割结果
        pred_boxes_list = pred_boxes.split(boxes_per_image, 0)
        pred_scores_list = pred_scores.split(boxes_per_image, 0)

        all_boxes = []
        all_scores = []
        all_labels = []
        # 遍历每张图像预测信息
        for boxes, scores, image_shape in zip(pred_boxes_list, pred_scores_list, image_shapes):
            # 裁剪预测的boxes信息,将越界的坐标调整到图片边界上
            boxes = box_ops.clip_boxes_to_image(boxes, image_shape)

            # create labels for each prediction
            labels = torch.arange(num_classes, device=device)
            labels = labels.view(1, -1).expand_as(scores)

            # remove prediction with the background label
            # 移除索引为0的所有信息(0代表背景)
            boxes = boxes[:, 1:]
            scores = scores[:, 1:]
            labels = labels[:, 1:]

            # batch everything, by making every class prediction be a separate instance
            boxes = boxes.reshape(-1, 4)
            scores = scores.reshape(-1)
            labels = labels.reshape(-1)

            # remove low scoring boxes
            # 移除低概率目标,self.scores_thresh=0.05
            # gt: Computes input > other element-wise.
            # inds = torch.nonzero(torch.gt(scores, self.score_thresh)).squeeze(1)
            inds = torch.where(torch.gt(scores, self.score_thresh))[0]
            boxes, scores, labels = boxes[inds], scores[inds], labels[inds]

            # remove empty boxes
            # 移除小目标
            keep = box_ops.remove_small_boxes(boxes, min_size=1e-2)
            boxes, scores, labels = boxes[keep], scores[keep], labels[keep]

            # non-maximun suppression, independently done per class
            # 执行nms处理,执行后的结果会按照scores从大到小进行排序返回
            keep = box_ops.batched_nms(boxes, scores, labels, self.nms_thresh)

            # keep only topk scoring predictions
            # 获取scores排在前topk个预测目标
            keep = keep[:self.detection_per_img]
            boxes, scores, labels = boxes[keep], scores[keep], labels[keep]

            all_boxes.append(boxes)
            all_scores.append(scores)
            all_labels.append(labels)

        return all_boxes, all_scores, all_labels

三. self.transform.postprocess

为了将我们预测的信息映射到原图像上,详见FasterRCNN源码解析(四)——GeneralizedRCNNTransform部分

总结

撸了一遍代码,对基本的框架有了一定的掌握,以及样本、标签是怎样变换的,也明白了anchor是如何生成的。但是也存在一些问题,比如 无法深刻理解其中的的一些原理,前学后忘,只能对代码有个简单的认识,自己目前无法动手修改。希望在今后的学习过程中,能够慢慢解决这些问题。

你可能感兴趣的:(计算机视觉,pytorch,深度学习)