Faster RCNN原理及Pytorch代码解读——RPN(四):损失函数

前两篇博客已经知道了RPN输出的预测值和真值,有了这些我们就可以计算RPN的损失了。
RPN的损失函数主要包含分类与回归两部分,具体公式如下:
L ( P i , t i ) = 1 N c l s ∑ i = 1 L c l s ( P i , P i ∗ ) + λ 1 N r e g ∑ i = 1 P i ∗ L r e g ( t i , t i ∗ ) L({P_{i}, t_i}) =\frac{1}{N_{cls}} \sum_{i=1} L_{cls}(P_i, P^*_i)+ \lambda \frac{1}{N_{reg}} \sum_{i=1} P^*_iL_{reg}(t_i, t^*_i) L(Pi,ti)=Ncls1i=1Lcls(Pi,Pi)+λNreg1i=1PiLreg(ti,ti)
∑ i = 1 L c l s ( P i , P i ∗ ) \sum_{i=1} L_{cls}(P_i, P^*_i) i=1Lcls(Pi,Pi)代表了256个筛选出的Anchors的分类损失, P i P_i Pi为每一个Anchor的类别真值, P i ∗ P^*_i Pi为每一个Anchor的预测类别。由于RPN的作用是选择出Proposal, 并不要求细分出是哪一类前景,因此在这一阶段是二分类,使用的是交叉熵损失。值得注意的是,在F.cross_entropy()函数中集成了Softmax的操作,因此应该传入得分,而非经过Softmax之后的预测值。

P i ∗ L r e g ( t i , t i ∗ ) P^*_i L_{reg}(t_i, t^*_i) PiLreg(ti,ti)代表了回归损失,其中bbox_inside_weights实际上起到了 P i ∗ P^*_i Pi进行筛选的作用, bbox_outside_weights起到了 λ 1 N r e g \lambda \frac{1}{N_{reg}} λNreg1来平衡两部分损失的作用。 回归损失使用了smoothL1函数, 具体公式如下:
L r e g ( t i , t i ∗ ) = ∑ i ∈ x , y , w , h s m o o t h L 1 ( t i − t i ∗ ) L_{reg}(t_i, t^*_i)=\sum_{i \in x,y,w,h} smooth_{L_1}(t_i-t^*_i) Lreg(ti,ti)=ix,y,w,hsmoothL1(titi)
s m o o t h L 1 ( x ) = { 0.5 x 2 , if |x| < 1 ∣ x ∣ − 0.5 , otherwise smooth_{L_1}(x)=\begin{cases} 0.5x^2, & \text {if |x| < 1} \\ |x|-0.5, & \text{otherwise} \end{cases} smoothL1(x)={0.5x2,x0.5,if |x| < 1otherwise
从第二个式子中可以看到, s m o o t h L 1 smooth_{L1} smoothL1函数结合了1阶与2阶损失函数,原因在于,当预测偏移量与真值差距较大时,使用2阶函数时导数太大,模型容易发散而不容易收敛,因此在大于1时采用了导数较小的1阶损失函数。

下面看下代码是如何实现的,这一部分的代码在网络结构那一篇已经出现过了,现在再来重新看一遍。

		# rpn_data是上一篇的RPN训练标签的生成的结果,返回的数据有4个,分别是
		# labels、bbox_targets、bbox_inside_weights、bbox_outside_weights
		# 维度分别是(batch, 1, 333, 50)、(batch, 36, 37, 50)、(batch, 36, 37, 50)、(batch, 36, 37, 50)
		rpn_data = self.RPN_anchor_target((rpn_cls_score.data, gt_boxes, im_info, num_boxes))

        # 计算分类损失
        # rpn_cls_score_reshape是原始rpn网络的分类输出(还没有经过softmax),其shape为(batch, 18, 37, 50)
        # 修改后的形状为(batch, 16650, 2)
        rpn_cls_score = rpn_cls_score_reshape.permute(0, 2, 3, 1).contiguous().view(batch_size, -1, 2)
        # rpn_label修改后的形状为(batch, 16650)
        rpn_label = rpn_data[0].view(batch_size, -1)

		# 得到rpn_label中非-1位置的索引,注意这个索引是将rpn_label拉成一维得到的索引
        rpn_keep = rpn_label.view(-1).ne(-1).nonzero().view(-1)
        # 将rpn_keep对应位置的rpn_cls_score的得分和rpn_labe的标签挑选出来
        rpn_cls_score = torch.index_select(rpn_cls_score.view(-1,2), 0, rpn_keep)
        rpn_label = torch.index_select(rpn_label.view(-1), 0, rpn_keep.data)
        rpn_label = rpn_label.long()
        # 先对scores进行筛选得到256个样本的得分,随后进行交叉熵求解
        self.rpn_loss_cls = F.cross_entropy(rpn_cls_score, rpn_label)
        fg_cnt = torch.sum(rpn_label.data.ne(0))

        rpn_bbox_targets, rpn_bbox_inside_weights, rpn_bbox_outside_weights = rpn_data[1:]

        # 利用smoothl1损失函数进行loss计算
        self.rpn_loss_box = _smooth_l1_loss(rpn_bbox_pred, rpn_bbox_targets, rpn_bbox_inside_weights,
                                                        rpn_bbox_outside_weights, sigma=3, dim=[1,2,3])

def _smooth_l1_loss(bbox_pred, bbox_targets, bbox_inside_weights, bbox_outside_weights, sigma=1.0, dim=[1]):
    sigma_2 = sigma ** 2
    box_diff = bbox_pred - bbox_targets
    in_box_diff = bbox_inside_weights * box_diff
    abs_in_box_diff = torch.abs(in_box_diff)
    smoothL1_sign = (abs_in_box_diff < 1. / sigma_2).detach().float()
    in_loss_box = torch.pow(in_box_diff, 2) * (sigma_2 / 2.) * smoothL1_sign \
                  + (abs_in_box_diff - (0.5 / sigma_2)) * (1. - smoothL1_sign)
    out_loss_box = bbox_outside_weights * in_loss_box
    loss_box = out_loss_box
    for i in sorted(dim, reverse=True):
      loss_box = loss_box.sum(i)
    loss_box = loss_box.mean()
    return loss_box
  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,以下是 Faster RCNNPyTorch 实现的代码及注释: ```python import torch import torch.nn as nn import torchvision.models as models import torchvision.ops as ops class FasterRCNN(nn.Module): def __init__(self, num_classes=21): super(FasterRCNN, self).__init__() # 加载预训练的 ResNet-50 模型 self.backbone = models.resnet50(pretrained=True) # 将 ResNet-50 模型的最后一层卷积层替换为3x3卷积层 self.backbone.layer4[2] = nn.Conv2d(2048, 512, kernel_size=3, stride=1, padding=1) # RPN网络 self.rpn_anchor_generator = ops.MultiScaleAnchorGenerator(sizes=((32, 64, 128, 256, 512),), aspect_ratios=((0.5, 1.0, 2.0),)) self.rpn_head = ops.RPNHead(in_channels=512, feat_channels=512, anchor_generator=self.rpn_anchor_generator) self.rpn_box_coder = ops.BoxCoder(weights=(1.0, 1.0, 1.0, 1.0)) # Fast R-CNN网络 self.roi_align = ops.RoIAlign(output_size=(7, 7), spatial_scale=1.0 / 16.0, sampling_ratio=-1) self.head = nn.Sequential( nn.Linear(7 * 7 * 512, 4096), nn.ReLU(inplace=True), nn.Linear(4096, 4096), nn.ReLU(inplace=True)) self.cls_score = nn.Linear(4096, num_classes) self.bbox_pred = nn.Linear(4096, num_classes * 4) def forward(self, x, gt_boxes=None, gt_labels=None): features = self.backbone(x) rpn_cls_scores, rpn_bbox_preds = self.rpn_head(features) if self.training: gt_boxes = gt_boxes.float() gt_labels = gt_labels.long() rpn_targets = ops.RPNTargets(self.rpn_anchor_generator, gt_boxes, gt_labels, features.shape[2:]) rpn_cls_loss, rpn_bbox_loss = ops.rpn_loss(rpn_cls_scores, rpn_bbox_preds, rpn_targets) rpn_loss = rpn_cls_loss + rpn_bbox_loss rpn_rois = ops.generate_proposals(rpn_cls_scores, rpn_bbox_preds, self.rpn_anchor_generator) rois, roi_indices = ops.box_roi_pool(features, rpn_rois, output_size=(7, 7), spatial_scale=1.0 / 16.0) cls_score, bbox_pred = self.head(rois.view(rois.size(0), -1)), self.bbox_pred(rois.view(rois.size(0), -1)) cls_score, bbox_pred = cls_score.mean(0, keepdim=True), bbox_pred.mean(0, keepdim=True) cls_loss, bbox_loss = ops.fast_rcnn_loss(cls_score, bbox_pred, roi_indices, gt_boxes, gt_labels) fast_rcnn_loss = cls_loss + bbox_loss return rpn_loss, fast_rcnn_loss else: rpn_rois = ops.generate_proposals(rpn_cls_scores, rpn_bbox_preds, self.rpn_anchor_generator) rois, roi_indices = ops.box_roi_pool(features, rpn_rois, output_size=(7, 7), spatial_scale=1.0 / 16.0) cls_score, bbox_pred = self.head(rois.view(rois.size(0), -1)), self.bbox_pred(rois.view(rois.size(0), -1)) cls_score, bbox_pred = cls_score.mean(0, keepdim=True), bbox_pred.mean(0, keepdim=True) return cls_score, bbox_pred ``` 这是一个比较基础的 Faster RCNN 实现,使用了预训练的 ResNet-50 模型作为特征提取器,然后通过 RPN 网络生成候选框,最后通过 Fast R-CNN 网络对候选框进行分类和回归。 在 `__init__` 函数中,我们首先加载预训练的 ResNet-50 模型,并将其最后一层卷积层替换为3x3卷积层。然后定义了 RPN 网络和 Fast R-CNN 网络。在 RPN 网络中,我们使用了 `MultiScaleAnchorGenerator` 作为 anchor 生成器,`RPNHead` 作为 RPN 网络的头部,`BoxCoder` 用来对 RPN 网络生成的 bbox 进行编码。在 Fast R-CNN 网络中,我们使用了 `RoIAlign` 作为 RoI pooling 层,`Sequential` 定义了 Fast R-CNN 网络的全连接层,最后使用 `Linear` 分别对分类和回归结果进行输出。 在 `forward` 函数中,我们首先将输入的图像通过 ResNet-50 模型得到特征,然后通过 RPN 网络生成候选框。如果是训练模式,我们需要计算 RPN 网络的 loss 和 Fast R-CNN 网络的 loss,然后返回总的 loss。如果是推理模式,我们只需要将候选框输入 Fast R-CNN 网络,然后返回分类和回归结果。在计算 loss 和输出结果时,我们都是先使用 `generate_proposals` 对 RPN 网络生成的 bbox 进行进一步筛选,然后使用 `box_roi_pool` 对候选框进行 RoI pooling,最后将 RoI pooling 的结果输入 Fast R-CNN 网络。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值