zoukankan      html  css  js  c++  java
  • Hard Example Mining

    Hard Example Mining

    Hard Negatie Mining与Online Hard Example Mining(OHEM)都属于难例挖掘,它是解决目标检测老大难问题的常用办法,运用于R-CNN,fast R-CNN,faster rcnn等two-stage模型与SSD等(有anchor的)one-stage模型训练时的训练方法。

    难例挖掘的思想可以解决很多样本不平衡/简单样本过多的问题,比如说分类网络,将hard sample 补充到数据集里,重新丢进网络当中,就好像给网络准备一个错题集,哪里不会点哪里。

    ps:本文中基本可以将“region proposal 区域提议”与“Region of Interest (RoI)”看成一个东西。

    最后,大家可能注意到OHEM和难负例挖掘名字上的不同。

      • Hard Negative Mining只注意难负例
      • OHEM 则注意所有难例,不论正负(Loss大的例子)

    什么是难例挖掘?

    难例挖掘与非极大值抑制 NMS 一样,都是为了解决目标检测老大难问题(样本不平衡+低召回率)及其带来的副作用。

    首先,目标检测与图像分类不同,图像分类往往只有一个输出,但目标检测的输出个数却是未知的。除了Ground-Truth(标注数据)训练时,模型永远无法百分百确信自己要在一张图上预测多少物体

    所以目标检测问题的老大难问题之一就是如何提高召回率。召回率(Recall)是模型找到所有某类目标的能力(所有标注的真实边界框有多少被预测出来了)。检测时按照是否检出边界框与边界框是否存在,可以分为下表四种情况:

    检测出边界框 未检出边界框
    边界框存在 真阳性(TP) 假阴性(FN)
    边界框不存在 误报(FP) 真阴性(TN)

    召回率是所有某类物体中被检测出的概率,并由下式给出:

    [Recall=frac{TP}{TP+FN} ]

    为了提高这个值,很直观的想法是“宁肯错杀一千,绝不放过一个”。因此在目标检测中,模型往往会提出远高于实际数量的区域提议(Region Proposal,SSD等one-stage的Anchor也可以看作一种区域提议)。

    但此时就会遇到一个问题,因为区域提议实在太多,导致在训练时绝大部分都是负样本,这导致了大量无意义负样本的梯度“淹没”了有意义的正样本。

    根据Focal Loss论文的统计,通常包含少量信息的“easy examples”(通常是负例),与包含有用信息的“hard examples”(正例+难负例)之比为100000:100!这导致这些简单例的损失函数值将是难例损失函数的40倍!

    img

    因此,为了让模型正常训练,我们必须要通过某种方法抑制大量的简单负例,挖掘所有难例的信息,这就是难例挖掘的初衷。

    难负例挖掘(Hard Negative Mining)就是在训练时,尽量多挖掘些难负例(hard negative)加入负样本集,这样会比easy negative组成的负样本集效果更好。

    R-CNN中的hard negative mining

    对于现在的我们,首先遇到难负例挖掘应该是R-CNN的论文,论文关于hard negative mining的部分引用了两篇论文:

    上述论文原文节选:

    Bootstrapping methods train a model with an initial subset of negative examples, and then collect negative examples that are incorrectly classified by this initial model to form a set of hard negatives. A new model is trained with the hard negative examples, and the process may be repeated a few times.
    we use the following “bootstrap” strategy that incrementally selects only those “nonface” patterns with high utility value:

    1. Start with a small set of “nonface” examples in the training database.
    2. Train the MLP classifier with the current database of examples.
    3. Run the face detector on a sequence of random images. Collect all the “nonface” patterns that the current system wrongly classifies as “faces”.Add these “nonface” patterns to the training database as new negative examples.
    4. Return to Step 2.

    而R-CNN中的难负例挖掘就是采用了这种自举法(bootstrap)的方法:

      • 先用初始的正负样本训练分类器(此时为了平衡数据,使用的负样本也只是所有负样本的子集)
      • 用(1)训练好的分类器对样本进行分类,把其中错误分类的那些样本(hard negative)放入负样本子集,
      • 再继续训练分类器,
      • 如此反复,直到达到停止条件(比如分类器性能不再提升).

    也就是说,R-CNN的Hard Negative Mining相当于给模型定制一个错题集,在每轮训练中不断“记错题”,并把错题集加入到下一轮训练中,直到网络效果不能上升为止。

    R-CNN中Hard Negative Mining的实现代码:

    rcnn/rcnn_train.m at master · rbgirshick/rcnn Line:214开始的函数定义

    在源文件rcnn_train.m中函数 sample_negative_feature用于采样负例特征,对函数定义的关键代码稍作解释。

    d.feat = rcnn_pool5_to_fcX(d.feat, opts.layer, rcnn_model); %将pool5层特征前向传播到fc7层特征
    d.feat = rcnn_scale_features(d.feat, opts.feat_norm_mean); % 缩放特征norm值,具体参见源码注释
    neg_ovr_thresh = 0.3;
    if first_time     % 首次直接取 最大IOU < 0.3 的region作为负例,用于后面训练SVM
      for cls_id = class_ids
        I = find(d.overlap(:, cls_id) < neg_ovr_thresh);
        X_neg{cls_id} = d.feat(I,:);
        keys{cls_id} = [ind*ones(length(I),1) I];
      end
    else   % 非首次负例采样
     % 先用当前更新过的SVM 预测region,即,应用SVM到 fc7的特征上,y'=w*x+b
      zs = bsxfun(@plus, d.feat*rcnn_model.detectors.W, rcnn_model.detectors.B);
      for cls_id = class_ids % 每个分类独立使用SVM
        z = zs(:, cls_id);   % 对当前分类,获取 SVM 计算值 y'
     % 下一行代码是关键
        I = find((z > caches{cls_id}.hard_thresh) & ...
                 (d.overlap(:, cls_id) < neg_ovr_thresh));
    % Avoid adding duplicate features
        keys_ = [ind*ones(length(I),1) I];
        if ~isempty(caches{cls_id}.keys_neg) && ~isempty(keys_)
          [~, ~, dups] = intersect(caches{cls_id}.keys_neg, keys_, 'rows');
          keep = setdiff(1:size(keys_,1), dups);
          I = I(keep);
        end
    % Unique hard negatives
        X_neg{cls_id} = d.feat(I,:);
        keys{cls_id} = [ind*ones(length(I),1) I];
      end
    end
    

    上述代码片段中,非首次负例采样时,要筛选出 难负例example,需要满足两个条件:

      • 负例,即最大IOU 小于 阈值 d.overlap(:, cls_id) < neg_ovr_thresh
      • 分类错误,既然是负例,那么SVM计算值wx+b 应该小于 1,

    以下图简单的回顾一下SVM,

    img

    位于H1和H2超平面上的实例就称为支持向量,对于y=1的正例点,有H1: wx + b =1

    对于y=-1的负例点,H2: wx + b = -1

    分类正确时应满足,

    wx+b>=1, if y = 1

    wx+b <=-1, if y = -1

    但是,SVM为了处理非严格线性可分的数据集,引入了松弛变量,于是如下图,

    img

    于是 只要 wx+b>-1,都可以认为是正例,只不过 wx+b越小,置信度越低。

    在inference阶段,同样是这个思路,比如rcnn_detect.m文件中

    thresh = -1
    scores = bsxfun(@plus, feat*rcnn_model.detectors.W, rcnn_model.detectors.B);
    for i = 1:num_classes
      I = find(scores(:, i) > thresh);
      scored_boxes = cat(2, boxes(I, :), scores(I, i));
      keep = nms(scored_boxes, 0.3); 
      dets{i} = scored_boxes(keep, :);
    end
    

    Fast RCNN中的hard negative mining

    Fast RCNN中,将真实标签边框(gt boxes)和区域提议边框(selective search boxes)一起打包为roidb。最后通过采样,将每个minibatch的数据输入训练。

    ps:在Faster RCNN中,由于使用了RPN生成区域提议,所以网络输入roidb只有gt boxes。

    对每个大小为N的mini-batch进行采样?

    随机选取N个图片(通常N=2),然后每个图片中选择64个ROIs(Region of Interests),其中25%为正例,即ROIs的最大IOU 大于等于0.5,剩余的为负例,选择最大IOU位于[0.1,0.5)范围内的roi作为负例。

    ps:如果正例数量不足25%,则增加负例数量,使得每个image中采样ROI数量为64,于是一个mini-batch中共有128个ROIs。

    img

    Fast RCNN 采用 random sampling 策略,即训练时随机取样

    如何确定正负例?

    Fast RCNN 中以区域提议与 groud truth 的 IoU 为标准区分正负例,假设阈值1为0.5,阈值2为0.1:

      • [0.5,1] 为正例
      • [0.1, 0.5) 之间标记为负例,
      • [0, 0.1) 的 example 用于 hard negative mining.

    不过很吊诡的是,即使直到要有难负例挖掘,训练的时候依然是用负例采样,那难负例哪去了?

    难负例哪去了?

    hard negative,顾名思义是难以正确分类的样本,也就是说在对负样本分类时候,loss比较大(label与prediction相差较大)的那些样本,也可以说是容易将负样本看成正样本的那些样本;

    那就怪了,比方说,如果有个IoU=0.4999的区域提议,它也会被当成负例。显然这张图片更难和正样本区分,那么训练不应该更难吗?

    我们可以先验的认为, 如果 RoI(或者说区域提议)里没有物体,全是背景,这时候分类器很容易正确分类成背景,这个就叫 easy negative, 如果RoI(或者说区域提议)里有二分之一个物体,标签仍是负样本,这时候分类器就容易把他看成正样本,这时候就是 hard negative。

    确实, 不是一个框中背景和物体越混杂, 越难区分吗? 框中都基本没有物体特征, 不是很容易区分吗?

    我看了很多文章的解释,目前只能认为 Fast RCNN 是这么想的:

    为了解决正负样本不均衡的问题(负例太多了), 我们应该剔除掉一些容易分类负例, 那么与 ground truth 的 IOU 在 [0, 0.1)之间的由于包含物体的特征很少, 应该是很容易分类的, 也就是说是 easy negitive, 为了让算法能够更加有效, 也就是说让算法更加专注于 hard negitive examples, 我们认为 hard negitive examples 包含在[0.1, 0.5) 的可能性很大, 所以训练时, 我们就在[0.1, 0.5)区间做 random sampling, 选择负例。

    但是,我们先验的认为 IoU 在[0, 0.1)之内的RoI是 easy example,是一种一厢情愿的想法。

    事实上,剔除IoU过低的数据后,模型可能反而对这些“背景”感到陌生。所以要对其做额外的 hard negitive mining, 找到其中的 hard negitive examples 用于训练网络。

    总而言之,在two-stage 模型中,提出的RoI Proposal在输入R-CNN子网络前,对正负样本(背景类和前景类)的比例进行调整。

    通常,背景类的RoI Proposal个数要远远多于前景类,Fast R-CNN的处理方式是随机对两种样本进行上采样和下采样,以使每一batch的正负样本比例保持在1:3,这一做法缓解了类别比例不均衡的问题,是两阶段方法相比单阶段方法具有优势的地方,也被后来的大多数工作沿用。

    四、Online Hard Example Mining(OHEM)

    代码:abhi2610/ohem

    CVPR2016的Oral论文:Training Region-based Object Detectors with Online Hard Example Mining[6]将难分样本挖掘(hard example mining)机制嵌入到SGD算法中,使得Fast R-CNN在训练的过程中根据区域提议的损失自动选取合适的区域提议作为正负例训练。

    在之前,RCNN采用了错题集的办法进行挖掘,Fast RCNN使用的是IoU阈值+随机采样的方法进行Hard Negative Mining,也就是都必须先结束一轮训练,有点事后诸葛亮的感觉。OHEM的工作中,作者提出:

    用R-CNN子网络对RoI Proposal预测的分数,来决定每个batch选用的样本,这样,输入R-CNN子网络的RoI Proposal总为表现不好的样本,提高了监督学习的效率。

    实际操作中,维护两个完全相同的R-CNN子网络,其中:

      • 一个只进行前向传播来为RoI Proposal的选择提供指导,
      • 另一个则为正常的R-CNN,参与损失的计算并更新权重,并且将权重复制到前者以使两个分支权重同步。

    OHEM以额外的R-CNN子网络的开销来改善RoI Proposal的质量,更有效地利用数据的监督信息,成为两阶段模型提升性能的常用部件之一。

    即:训练的时候选择hard negative来进行迭代,从而提高训练的效果。

      • 前向时: 全部的ROI通过网络,根据loss排序;
      • 反向时:根据排序,选择Batch-Size/N(minibatch中proposal数除以输入图片数)个loss值最大的(worst)样本来后向传播更新model的weights。

    在此之前必须注意,位置相近的ROI在map中可能对应的是同一个位置,loss值相近,所以选取minibatch的RoI之前,要先对Hard程度(loss)做NMS,然后再选择Batch-Size/N个ROI反向传播,这里nms选择的IoU阈值为0.7。

    ps:这里的NMS与这篇笔记提到的用于目标检测的NMS类似,不过这里的排序依据不是置信度,而是损失函数的大小。

    还有注意的一点是,前反向时使用的不是同一个网络:

      • 一个只用来前向传播的部分来筛选RoI,
      • 另一个把选完的ROIs进行后向传播,

    这样的虽然要在内存维护两个网络的参数,但好处是不需要(在计算所有区域提议loss的同时)计算所有区域提议的反向传播了。是一种空间换时间的策略!

    img

    给定图像和选择性搜索结果的RoI(现在不用selective search了,但道理一样),卷积网络计算转换特征映射。 在(a)中,只读RoI网络在特征映射和所有RoI上运行正向传递(以绿色箭头显示)。 然后Hard RoI模块使用这些RoI损失来选择B个样本。 在(b)中,RoI网络使用这些硬性示例来计算前向和后向通道(以红色箭头示出)。

    实验结果表明使用OHEM(Online Hard Example Mining)机制可以使得Fast R-CNN算法在VOC2007和VOC2012上mAP提高 4%左右。

    五、结语

    难例挖掘与非极大值抑制一样,是一种流行于计算机科学领域数十年的经典算法,经典到老前辈不认为它需要细讲。然而对一些从深度学习算法入手的萌新来说,这种细枝末节的东西就有些麻烦了。

    不过也正因如此,难例挖掘的确是一个用于平衡样本的好办法,因此它可以运用的范围远远不止于目标检测中。分类、分割等问题都可以找到它的影子。

    本文来自博客园,作者:甫生,转载请注明原文链接:https://www.cnblogs.com/fusheng-rextimmy/p/15469516.html

  • 相关阅读:
    计算机网络概述---传输层 UDP和TCP
    计算机网络概述---网络层
    计算机网络概述---数据链路层
    计算机网络概述---物理层
    计算机网络概述---OSI参考模型
    Java 集合系列18之 面试题
    【Vue-01】基础Vue语法+JS复习
    SpringBoot 注册Servlet三大组件【Servlet、Filter、Listener】-原生代码+@Bean+效果展示
    Springboot的 get查看,post创建,put更新,delete删除 -四种请求实例(form + controller)
    【Bug档案01】Spring Boot的控制器+thymeleaf模板 -使用中出现静态资源加载路径不当的问题 -解决时间:3h
  • 原文地址:https://www.cnblogs.com/fusheng-rextimmy/p/15469516.html
Copyright © 2011-2022 走看看