目标侦测rcnn系列学习笔记

Task


传统侦测方法

  • 使用滑动窗法依次判断所有可能的区域
  • CNN输出向量【是否有物体,X, Y,W, H,N类的One-Hot 】



首先放一张高明豪dalao的历代整理图:

history

RCNN

RCNN算法步骤

  • 候选区域生成: 一张图像生成1K~2K个候选区域 (采用Selective Search 方法)
  • 特征提取: 对每个候选区域,使用深度卷积网络提取特征 (CNN
  • 类别判断: 特征送入每一类的SVM 分类器,判别是否属于该类
  • 位置精修: 使用回归器精细修正候选框位置

一些概念

  • Selective Search 主要思想
  • 有监督预训练与无监督预训练

    采用有监督训练,CNN使用迁移学习的思想

  • 重叠度(IOU)

    定位精度评价公式:IOU,定义了两个bounding box的重叠度

    1
    IOU = (A∩B)/(AUB)
  • 非极大值抑制(NMS)

1
2
3
- 选取概率最大框
- 判断剩余框重叠度IOU是否超过阈值,进行丢弃
- 保留概率最大框,循环上述步骤直到找到所有被保留下来的矩形框

流程各阶段详解

总体思路

  • 找出候选框
  • 利用CNN提取特征向量
  • 利用SVM进行特征向量分类

候选框搜索阶段

  • Selective Search方法得出2k个候选框

  • 缩放到固定的大小,以便输入CNN

1
2
3
4
5
6
(1)各向异性缩放:
不管图片的长宽比例,管它是否扭曲,进行缩放
(2)各向同性缩放:
A、先扩充后裁剪
B、先裁剪后扩充
+、padding处理

CNN特征提取阶段

  • 第一选择经典的Alexnet;第二选择VGG16
  • 输入候选框图片,输出一个4096维的特征向量
  • 可采用预训练,加上分类头(N类+1背景类),进行fine-tuning
  • 我们batch size大小选择128,其中32个是正样本、96个是负样本(起决于IoU大于0.5)
  • 去掉最后的softmax而采用SVM原因: SVM和CNN的正负样本定义方式各有不同,SVM对IOU要求比较严格

SVM特征向量分类阶段

  • 得到的特征输入到SVM进行分类看看这个feature vector所对应的region proposal是需要的物体还是无关的实物(background)

  • 排序,canny边界检测之后就得到了我们需要的bounding-box

  • 位置精修: 对每一类目标,使用一个线性脊回归器进行精修

    1
    2
    3
    正则项λ=10000。 
    输入为深度网络pool5层的4096维特征,输出为xy方向的缩放和平移。
    训练样本:判定为本类的候选框中和真值重叠面积大于0.6的候选框。

测试阶段

搬运:

使用selective search的方法在测试图片上提取2000个region propasals ,将每个region proposals归一化到227x227,然后再CNN中正向传播,将最后一层得到的特征提取出来。然后对于每一个类别,使用为这一类训练的SVM分类器对提取的特征向量进行打分,得到测试图片中对于所有region proposals的对于这一类的分数,再使用贪心的非极大值抑制(NMS)去除相交的多余的框。再对这些框进行canny边缘检测,就可以得到bounding-box(then B-BoxRegression)。

(非极大值抑制(NMS)先计算出每一个bounding box的面积,然后根据score进行排序,把score最大的bounding box作为选定的框,计算其余bounding box与当前最大score与box的IoU,去除IoU大于设定的阈值的bounding box。然后重复上面的过程,直至候选bounding box为空,然后再将score小于一定阈值的选定框删除得到这一类的结果(然后继续进行下一个分类)。


SPPNet

——引入空间金字塔池化改进RCNN,主要是解决深度网络固定输入层尺寸的这个限制

主要疑难

  • 原始图像的ROI如何映射到特征图(一系列卷积层的最后输出)

    ROI to Feature_Map

    道理解释:https://zhuanlan.zhihu.com/p/24780433

  • ROI的在特征图上的对应的特征区域的维度不满足全连接层的输入要求怎么办(又不可能像在原始ROI图像上那样进行截取和缩放)?

SPPnet

Fast R-CNN

Fast_R-CNN

​ 注:Bounding-box Regression

ROI pooling

这个实际上是SPP的变种,SPP是pooling成多个固定尺度,而RoI只pooling到一个固定的尺度(6×6)

新的训练方式

把同张图片的prososals作为一批进行学习,而proposals的坐标直接映射到conv5层上,这样相当于一个batch一张图片的所以训练样本只卷积了一次

Faster R-CNN

Faster-RCNN

  • 搬运模型分析:

论文借鉴SPP和ROI中的思想 在feature map中提取proposal。

先通过对应关系把feature map的点映射回原图(参看:原始图片中的ROI如何映射到到feature map?),

在每一个对应的原图设计不同的固定尺度窗口(bbox),

根据该窗口与ground truth的IOU给它正负标签,让它学习里面是否有object,

这样就训练一个网络(Region Proposal Network)。

  • 搬运另一种流程解释:

    ​ 五步:vgg、get anchor、rpn、proposal、prediction

FasterRcnn

  • RPN——区域生成网络 ( Region Proposal Networks )

RPN

  • 具体概念科普见大佬知乎

  • 注意点一:

    计算Anchors: 在feature map上的每个特征点预测多个region proposals

Anchors

  • 注意点二:

    有关于RPN的Regression:

    表示predict box相对于anchor box的偏移 对 表示ground true box相对于anchor box的偏移

    reg_loss

  • 注意点三:

    ​ Proposal部分,参见

    >概要:  这一过程是再根据每个box的值选取出合适的部分box,并再反向投影到前面得到的[1, P, Q, 512]的feature map上
    
  • 注意点四:

    ​ Faster R-CNN的四种训练方式,见

Faster R-CNN源码分析

流程图

数据处理

train_frcnn.py

1
all_imgs, classes_count, class_mapping = get_data(options.train_path)

get_data函数来自simple_parser.py或者pascal_voc_parser.py,查看三个返回值:

  • all_imgs

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    # 内容:annotation_data的list:
    annotation_data = {
    'filepath': os.path.join(imgs_path, element_filename),
    'width': element_width,
    'height': element_height,
    'bboxes': [{'class': class_name, 'x1': x1, 'x2': x2, 'y1': y1, 'y2': y2, 'difficult': difficulty}, ...]}

    # 调用:
    #先区分训练和验证集
    train_imgs = [s for s in all_imgs if s['imageset'] == 'trainval']
    val_imgs = [s for s in all_imgs if s['imageset'] == 'test']

    #再喂给数据生成器
    data_gen_train = data_generators.get_anchor_gt(train_imgs, classes_count, C, nn.get_img_output_length, K.image_dim_ordering(), mode='train')

    #生成器生成batch数据
    X, Y, img_data = next(data_gen_train)
    loss_rpn = model_rpn.train_on_batch(X, Y)
    P_rpn = model_rpn.predict_on_batch(X)
    # X:img_data
    # Y: [np.copy(y_rpn_cls), np.copy(y_rpn_regr)]
    # img_data: img_data_aug, 即宽高

    #使用RPN获得region proposals
    R = roi_helpers.rpn_to_roi(P_rpn[0], P_rpn[1], C, K.image_dim_ordering(), use_regr=True, overlap_thresh=0.7, max_boxes=300)

    #转换region proposals形式
    # note: calc_iou converts from (x1,y1,x2,y2) to (x,y,w,h) format
    X2, Y1, Y2, IouS = roi_helpers.calc_iou(R, img_data, C, class_mapping)

    #喂入最终的分类器
    sel_samples = selected_pos_samples + selected_neg_samples #这步限制pos_roi和neg_roi数目
    loss_class = model_classifier.train_on_batch([X, X2[:, sel_samples, :]], [Y1[:, sel_samples, :], Y2[:, sel_samples, :]])
  • class_count

1
2
3
4
5
6
7
8
9
10
11
#内容:
#统计每个class的数目
classes_count[class_name] = xxx

#用途列举:
data_gen_train = data_generators.get_anchor_gt(train_imgs, classes_count, C, nn.get_img_output_length, K.image_dim_ordering(), mode='train')
data_gen_val = data_generators.get_anchor_gt(val_imgs, classes_count, C, nn.get_img_output_length,K.image_dim_ordering(), mode='val')

classifier = nn.classifier(shared_layers, roi_input, C.num_rois, nb_classes=len(classes_count), trainable=True)

model_classifier.compile(optimizer=optimizer_classifier, loss=[losses.class_loss_cls, losses.class_loss_regr(len(classes_count)-1)], metrics={'dense_class_{}'.format(len(classes_count)): 'accuracy'})
  • class_mapping
1
2
3
4
5
6
7
#内容:
#将每个class映射为id
class_mapping[class_name] = x_id

#用途:
#在处理RPN的输出时
X2, Y1, Y2, IouS = roi_helpers.calc_iou(R, img_data, C, class_mapping)

模型输入输出

训练时有3个model,分别是model_rpn, model_classifiermodel_all

  • model_rnp

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    #位置: X, Y, img_data = next(data_gen_train)下句

    loss_rpn = model_rpn.train_on_batch(X, Y)
    P_rpn = model_rpn.predict_on_batch(X)

    #输入
    #X:img_input Y: class

    #输出
    [x_class, x_regr, base_layers]
    - x_class:二分类结果,表明该anchor是否可用
    - x_regr: bbox的回归(tx,ty,tw,th)
    - base_layers: 输入的feature_map
  • model_classifier

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 位置:RPN产生ROI并对pos和neg采样后喂入model_classifier

loss_class = model_classifier.train_on_batch([X, X2[:, sel_samples, :]], [Y1[:, sel_samples, :], Y2[:, sel_samples, :]])

# 输入:
[img_input, roi_input]
- img_input:变量X
- roi_input:变量X2

#输出:
classifier,
#位置:classifier = nn.classifier(shared_layers, roi_input, C.num_rois, nb_classes=len(classes_count), trainable=True)
#注意这里是 nn.classifier,且输入是shared_layers,即feature_map

#对应Proposal步骤,在classifier里ROI先反向投影到前面得到的[1, P, Q, 512]的feature map上
#主要通过RoiPoolingConv

#模型classifier的模组RoiPoolingConv和TimeDistributed不分析拉有点累,有空再说···

#内容: 即[out_class, out_regr]
- Y1:y_class_num One-Hot的Label
- Y2:np.concatenate([np.array(y_class_regr_label),np.array(y_class_regr_coords)],axis=1)

重要的函数

这里只对函数的位置输入输出功能进行简介,详细的有空补完,也可以参见Refence里的源码解析。

  • roi_helpers.rpn_to_roi

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    #调用位置:
    #对RPN的结果[x_class, x_regr]
    R = roi_helpers.rpn_to_roi(P_rpn[0], P_rpn[1], C, K.image_dim_ordering(), use_regr=True, overlap_thresh=0.7, max_boxes=300)

    #输入:
    P_rpn[0]和P_rpn[1], 即RPN的[x_class, x_regr]

    #输出:
    result, 即[boxes, probs]
    - boxs: bounding boxes
    - probs: regions对应每个box的最优集合
    #注:通过non_max_suppression_fast()过滤掉重合度高的region并保留最优的

    注:non_max_suppression_fast()解析原文

    部分搬运:

    对region的index按照概率值进行排序,然后从后往前取,那么每次取到的都是剩下的region中概率最大的添加到pick[]里,然后把剩下的region中,与之重合度很高的删掉。循环下来,找出所有合适的region。作为model_classifier的输入之一

  • datageneraters.get_achor_gt

    注: 核心函数datagenerater.calc_rpn流程见源码底下的流程图

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    #调用位置:
    data_gen_train = data_generators.get_anchor_gt(train_imgs, classes_count, C, nn.get_img_output_length, K.image_dim_ordering(), mode='train')
    data_gen_val = data_generators.get_anchor_gt(val_imgs, classes_count, C, nn.get_img_output_length,K.image_dim_ordering(), mode='val')

    #实例调用后的输出:
    yield np.copy(x_img), [np.copy(y_rpn_cls), np.copy(y_rpn_regr)], img_data_aug
    #生成器生成batch数据
    X, Y, img_data = next(data_gen_train)
    loss_rpn = model_rpn.train_on_batch(X, Y)
    P_rpn = model_rpn.predict_on_batch(X)
    # X:img_data
    # Y: [np.copy(y_rpn_cls), np.copy(y_rpn_regr)]
    # img_data: img_data_aug, 即宽高
    其中核心函数:
    =============================================================
    #datagenerater.calc_rpn
    =============================================================
    calc_rpn:
    #位置:y_rpn_cls, y_rpn_regr = calc_rpn(C, img_data_aug, width, height, resized_width, resized_height, img_length_calc_function)

    #输出:y_rpn_cls, y_rpn_regr

    y_rpn_cls = np.concatenate([y_is_box_valid, y_rpn_overlap], axis=1)
    #y_is_box_valid列表记录region proposal过程中的每个anchor是否可用
    #y_rpn_overlap: 记录anchor是否有物体

    y_rpn_regr = np.concatenate([np.repeat(y_rpn_overlap, 4, axis=1), y_rpn_regr], axis=1)
    #y_rpn_regr: 边框回归
  • roi_helpers.calc_iou

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    #调用位置:
    #在roi_helpers.rpn_to_roi调用之后
    # note: calc_iou converts from (x1,y1,x2,y2) to (x,y,w,h) format
    X2, Y1, Y2, IouS = roi_helpers.calc_iou(R, img_data, C, class_mapping)

    #输入:R, img_data, C, class_mapping
    - R:[boxes, probs],即Boundding Boxes和对用最优anchors
    - img_data: 图片的宽高
    - C: 各类参数
    - class_mapping: class2id

    #输出:
    - X2:即model_classifier的输入roi_input, 由[x1, y1, w, h]组成的list
    - Y1:y_class_num One-Hot的Label
    - Y2:np.concatenate([np.array(y_class_regr_label),np.array(y_class_regr_coords)],axis=1)
    #注:
    y_class_regr_label是由[1, 1, 1, 1]组成的array
    y_class_regr_coords是由[sx*tx, sy*ty, sw*tw, sh*th]组成的array

    #与datagenerater.calc_rpn区分
    - datagenerater.calc_rpn:嵌在datageneraters.get_achor_gt,是给model_rpn提供输入
    - roi_helpers.calc_iou是处理成给model_classifier的输入

    #作用搬运:
    通过calc_iou()找出剩下的不多的region对应ground truth里重合度最高的bbox,从而获得model_classifier的和标签

训练心得

  • 如果使用PASCAL_VOC数据集,记得删除parser里的data_paths的VOC2017,原Github并没有给出
  • 只可以使用Keras==2.0.3,枯了55
  • 先用小数据集训练,便于快速看到效果
  • 调整num_rois数量,可加快训练速度
  • 放两个数据集:

PASCAL_VOC

PASCAL_VOC

地址

下载地址

辛普森一家~

The_Simpson

地址

——Edit By Kevin