深度学习目标检测之 R-CNN 系列:Faster R-CNN 网络详解

深度学习目标检测之 R-CNN 系列:Faster R-CNN 网络详解

深度学习目标检测之 R-CNN 系列包含 3 篇文章:

1. 前言

这一篇文章主要是深入的理解 Faster R-CNN 网络结构。

为了深入的了解 Faster R-CNN,我这里 clone 了一份 caffe 版本的 py-faster-rcnn,方便查看源代码。如果编译过程遇到 CUDNN 版本问题,可以参考 Caffe编译时提示 CUDNN 版本不兼容的解决办法

如果想对 Faster R-CNN 文件层次有更深入的认识,可以参考 Faster RCNN算法demo代码解析

2. Faster R-CNN 网络概览

下图大致描述了 Faster R-CNN 的过程。

下面这幅图可能更清楚的表达了 Faster R-CNN 是如何工作的。只是需要注意的是下图是 test 网络。

    1. 将图片输入 CNN 网络(比如 ZF/VGG 等预训练好的分类模型),得到 feature maps;
    1. 将 feature maps 喂入 RPN(Region Proposal Network) 网络得到 region proposals (包含第一次回归)。更具体的,RPN 首先生成一堆Anchor box,对其进行裁剪过滤后通过 softmax 判断 anchors 属于前景(foreground)或者背景(background),即是不是物体,所以这是一个二分类任务;同时,另一分支 bounding box regression 修正(回归) anchor box,形成较精确的 proposal,注意这并是最后得到的 proposal,后面还会再进行第二次回归;
    1. 将上两步的输出:feature maps 和 region proposals 喂入 RoI Pooling 层得到统一 size 的 feature maps;
    1. 将 3 输出的 feature maps 输入全连接层,利用 Softmax 进行具体类别的分类,同时,利用 L1 Loss 完成 bounding box regression 回归(第二次回归)操作获得物体的精确位置。

3. CNN

这里的 CNN 网络也可以叫做 conv layers,因为就是 conv + ReLU + Pooling 的组合,是没有 fc 层的。以 VGG16(13 个 conv + 3 个 fc) 为例,这里仅仅保留了其 13 个 conv 层。

4. RPN

下图是 RPN 网络图,通过两个 1x1 的卷积层(conv)将 feature map 的 channel 分别变为 18 和 36,分别用来做分类和 bonding box 回归。这里面最为关键的 layer 就是 rpn-data,几乎所有核心的功能也都是在这个 layer 中完成。

4.1 rpn-data layer

下面我们来重点看一下 rpn-data layer。

layer {
  name: 'rpn-data'
  type: 'Python'
  bottom: 'rpn_cls_score'
  bottom: 'gt_boxes'
  bottom: 'im_info'
  bottom: 'data'
  top: 'rpn_labels'
  top: 'rpn_bbox_targets'
  top: 'rpn_bbox_inside_weights'
  top: 'rpn_bbox_outside_weights'
  python_param {
    module: 'rpn.anchor_target_layer'
    layer: 'AnchorTargetLayer'
    param_str: "'feat_stride': 16"
  }
}

这里的 rpn-data 的类型是 AnchorTargetLayer。具体的代码可以参考

py-faster-rcnn/lib/rpn/anchor_target_layer.py

关于 AnchorTargetLayer 的解读可以参考:

总的来说,这层干了如下这些事情。

  • 生成 anchor,并将超出图像区域的anchor去除,得到有效的 anchor;

  • 给每一个 anchor 分配 label,-1表示忽略该 anchor,0表示背景,1表示前景(物体),得到 labels;

  • 计算 RPN 阶段的回归目标 bbox_targets;

  • 计算 bbox_inside_weights(将 label 为 1 的 anchor 权重赋为 1,其他的都赋为 0)、bbox_outside_weights(将前景和背景 anchor 的总数记为 n,则前景和背景 anchor 权重都赋为 1/n,其它的 anchor 权重都赋为 0),在计算 SmoothL1Loss 时用于加权;

  • 上述过程得到的 labels, bbox_targets, bbox_inside_weights, bbox_outside_weights,它们第一个维度的长度和有效 anchor 的个数是相同的,最后对它们进行扩充,将无效的 anchor 所对应的 labels, bbox_targets, bbox_inside_weights, bbox_outside_weights 分别加入其中,使这四个输出的第一个维度的长度等于生成anchor的个数。

4.2 Softmax with Loss

这个是 caffe 标准的 layer, 就是对 softmax 的输出求 crossentropy loss。对于 RPN 而言就是一个二分类的交叉熵损失函数。

4.3 BBox 回归

假设我们对 anchor a 进行回归得到 predicated bbox p,那我们归一化的目标是希望 p 无限接近于 ground true(标记的结果)t。

在二维平面上,从矩形框 p 变换到矩形框 t,显然就是平移和缩放操作。假设 x,y,w,hx, y, w, h 分别为 p 的中心座标及宽和高;xa,ya,wa,hax_a, y_a, w_a, h_a 分别为 a 的中心座标及宽和高;x,y,w,hx^*, y^*, w^*, h^* 分别为 t 的中心座标及宽和高。那么从 p 到 a 的变换可以表示如下。

{x=xa+xy=ya+yw=wawh=hah\left\{ \begin{aligned} & x = x_a +\nabla x\\ & y = y_a +\nabla y\\ & w = w_a \cdot \nabla w\\ & h = h_a \cdot \nabla h\\ \end{aligned} \right.

上面的 \nabla 部分都是基于 w,hw, h 计算出来的,表示如下。

{x=watxy=hatyw=waetwh=haeth\left\{ \begin{aligned} & \nabla x = w_a \cdot t_x\\ & \nabla y = h_a \cdot t_y\\ & \nabla w = w_a \cdot e^{t_w}\\ & \nabla h = h_a \cdot e^{t_h}\\ \end{aligned} \right.

其中 tx,ty,tw,tht_x, t_y, t_w, t_h 是预测的偏移量。根据上面的两个式子可以将其表示如下。

{tx=xxawaty=yyahatw=log(wwa)th=log(hha)\left\{ \begin{aligned} & t_x = \frac {x-x_a}{w_a}\\ & t_y = \frac {y-y_a}{h_a}\\ & t_w = log(\frac {w}{w_a})\\ & t_h = log(\frac {h}{h_a})\\ \end{aligned} \right.

同理。

{tx=xxawaty=yyahatw=log(wwa)th=log(hha)\left\{ \begin{aligned} & t^*_x = \frac {x^*-x_a}{w_a}\\ & t^*_y = \frac {y^*-y_a}{h_a}\\ & t^*_w = log(\frac {w^*}{w_a})\\ & t^*_h = log(\frac {h^*}{h_a})\\ \end{aligned} \right.

这里需要注意下面几个问题:

  • 为什么上面要用到 ee 或者 loglog,上面可以看到 ee 用来表示 scale 系数,显然 scale 系数应该大于零,所以用 ee 表示正好可以满足,那为什么用 loglog 呢? loglog 一个很重要的特性就是压缩大值,也就是说采用这种 loss 可以减小大的目标对最后结果的影响,这样可以改善模型只能学到大的目标的缺点。

  • 对于 x,yx,y 采用的是线性变化,但是对于 w,hw, h 却是用的非线性变化,这样合理吗?其实当 a 趋近于 t 时,可以认为 loglog 是线性变化。

4.4 Smooth L1 Loss

Smooth L1 Loss 的数学表达式如下。

f(x)={0.5x2, x<1x0.5, x<1 or x>1 f(x)=\left\{ \begin{aligned} & 0.5x^2, \space |x| < 1 \\ & |x| -0.5, \space x < -1 \space or \ x > 1 \\ \end{aligned} \right.

对于边框预测回归问题,通常也可以选择平方损失函数( L2 损失),但 L2 范数的缺点是当存在离群点(outliers) 的时候,这些点会占 loss 的主要组成部分。比如说真实值为1,预测 10 次,有一次预测值为 1000,其余次的预测值为 1 左右,显然 loss 值主要由1000 主宰。所以 Fast RCNN 采用稍微缓和一点的绝对损失函数(smooth L1损失),它是随着误差线性增长,而不是平方增长。

注意:smooth L1 和 L1-loss 函数的区别在于,L1-loss 在 0 点处导数不唯一,可能影响收敛。smooth L1 的解决办法是在 0 点附近使用平方函数使得它更加平滑。

更多内容可以参考 损失函数:L1 loss, L2 loss, smooth L1 loss

5. RoI Pooling

ROI Pooling 的作用是将 RPN 输出的 region proposal 转换到相同的 size,然后再喂入 fc 层。RoI Pooling 还有一个进化版本 RoI Align。关于RoI Pooling 和 RoI Align 的介绍可以参考 ROI Pool和ROI Align

6. 损失函数

Faster R-CNN 总共有四个损失函数,RPN 的分类和回归损失函数, Fast R-CNN 的分类和回归损失函数。RPN 的损失函数上面已经讲过,和 Fast R-CNN 的损失函数类似。Fast R-CNN 的分类损失为多分类交叉损失, 回归损失依然为 smooth L1 loss。

L(pi,ti)=1NclsiLcls(pi,pi)+λ1NregipiLreg(ti,ti) L({p_i},{t_i})= \begin{aligned} & \frac{1}{N_{cls}} \sum_iL_{cls}(p_i, p_i^*) +\lambda \frac{1}{N_{reg}} \sum_ip_i^*L_{reg}(t_i, t_i^*) \\ \end{aligned}

更为详细的介绍可以参考 [https://blog.csdn.net/Mr_health/article/details/84970776](【Faster RCNN】损失函数理解).

7. Anchor 的压缩

在整个过程中对 Anchor 进行了四次压缩,分别如下:

  • 在最开始划分 anchor 的时候,忽略超出边界的 anchor,大约 17000 个左右;
  • 在 RPN 中进行 background(iou < 0.3)和 foreground(iou > 0.7) 划分时,忽略介于 0.3 和 0.7 之间的 anchor(标记为 -1);
  • 对每一个预测为 foreground 的 anchor 的 score,进行排序,保留排名考前的大约 12000 个 anchor;
  • 非极大值抑制(NMS),首先找到 score 最高的 anchor,然后以此 anchor 作为基准,计算其他的 anchor 与这个 anchor 的 iou,如果 iou 大于阈值,就认为是同一个目标,直接舍弃这个 anchor,最后大约剩余 2000 个左右的 anchor。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章