MMDetection学习系列(4)——Cascade R-CNN深度探索与实战指南

引言

Cascade R-CNN作为目前主流常用的高性能目标检测算法中最广为人知的算法之一。它基于经典的Faster R-CNN架构,通过引入级联式的多阶段检测器来逐层提升检测精度,从而显著提高了模型在困难样本上的表现。

模型算法下载

Coovally AI Hub 公众号后台回复 「 模型算法 」, 即可获取!

背景

在Faster R-CNN算法中,RPN输出指定数量的RoI候选框,然后输入到R-CNN层进行分类和回归,一般R-CNN部分是设置IoU=0.5来进行正负样本划分。

直观上训练时设置IoU越大,检测的bbox越少,但是普遍质量更好,也就是说 IoU如果设置的比较小,那么在训练过程中可能会带来很多噪声,不利于训练,但是如果IoU太高,会导致正样本太少出现过拟合,结合如下曲线可以进一步验证。

针对不同IoU阈值的regressor和detector进行了实验。从图c可以看出,不同IoU阈值的detector对不同水平的bndbox的优化程度不同,bndbox IoU与detecor的训练阈值越接近,box regress提升越高。而在图c中,detector(u=0.5)在低IoU水平下比detector(u=0.6)表现优异,而在高IoU水平下则反之,而当u=0.7时,由于正样本的不足以及推理时输入的样本IoU较低,detector(u=0.7)的整体表现都降低了。只有输入proposal自身的 IoU分布和detector训练用的阈值IoU较为接近的时候,detector性能才最好,或者说阈值IoU设置应该和R-CNN训练的输入样本IoU分布接近的时候,性能最好,如果两个阈值相距比较远就是mismatch问题。 但图 (c)又说明不能一味的提高IoU来达到输出高质量bbox的目的(对应匹配正样本数目不够,会出现过拟合)。

模型介绍

因此,提出了Cascade R-CNN来解决上面的问题。Cascade R-CNN是一个顺序的多阶段extension,利用前一个阶段的输出进行下一阶段的训练,阶段越往后使用更高的IoU阈值,产生更高质量的bndbox。Cascade R-CNN简单而有效,能直接添加到其它R-CNN型detector中,带来巨大的性能提升(2-4%)

既然在Faster R-CNN中不能一味的提高IoU来达到输出高质量bbox的目的,那一个很自然的想法是级联结构,在每个层级采用不同的 IoU 阈值来提升。

Faster R-CNN

目前经典的two-stage架构如上图(a)。第一阶段是一个提框的子网H0,用于生成初步的bndbox。第二阶段为特定区域处理的检测子网H1,给定bndbox最终的分类分数C和bndbox坐标B。

Iterative BBox

有的研究者认为单次的box regress是不足以产生准确的位置信息的,因此需要进行多次迭代来精调bndbox,这就是iterative bounding box regression:


单次回归(regression)对于边界框(BBox)的精度提升是不够的,因此通常采用多次迭代回归来逐步改进BBox。然而,在多次迭代过程中,不能使用同一个检测头(detection head)。这是因为,每次迭代后的BBox分布都会发生显著变化。即使在初始阶段回归器对BBox的分布最优,经过几次迭代后,它可能不再适用于新的分布,从而导致性能下降。


此外,训练时,IoU阈值设为0.5的回归器对具有更高IoU的假设(hypothesis)产生了次优的效果。图(c)展示了,当IoU阈值为0.5时,迭代回归对IoU大于0.5的假设造成了损害,相比于基准模型(baseline),效果更差。因此,为了应对这种问题,迭代BBox回归通常需要进行提议积累(proposal accumulation)、框投票(box voting)等手工工程,且这种方法的增益(gain)并不稳定。研究表明,当迭代次数超过两次时,性能改进趋于平稳,甚至可能没有任何好处。

Integral Loss

IoU阈值(u)决定了正样本(positive)和负样本(negative)的划分。如果一个假设(hypothesis)与真实框(GT)的IoU高于u,则视为正样本;否则视为负样本。当u值较高时,正样本的数量较少,但背景区域(background)较少;而当u值较低时,可以获得更多样化的正样本,但这会导致检测器难以抑制接近的假阳性(close false positives)。因此,很难找到一个适用于所有IoU水平的分类器。


在推理阶段,RPN等提议生成器生成的假设大多质量较低,因此检测器需要具备更强的辨别能力,尤其是对低质量假设的辨识能力。


一种折衷方案是将IoU阈值设置为0.5,这虽然能提供较多的正样本,但也带来了较低质量的检测结果,通常被认为是接近的假阳性(如图(a)所示)。一种简单的解决思路是使用多个分类器,每个分类器在训练时使用不同的IoU阈值,并在推理时对这些分类器进行集成。在训练过程中,通过使用积分损失(integral loss),来优化多个质量层级。


然而,这种方法存在问题:它未能解决公式(14)中不同损失对正样本数量操作的差异。此外,随着IoU阈值增大,正样本数量迅速减少,这导致高质量分类器出现过拟合问题。而且,高质量分类器在推理时需要处理大量低质量提议,但它们并未针对这些低质量提议进行优化。因此,该方法在大多数质量层级上无法提高准确率,且相较于迭代边界框(Iterative BBox)方法,其增益有限。


因此,Integral loss在很多IoU水平难以表现出高的准确率。相对于原始的two-stage架构,Integral loss的架构收益相对较小

Cascade R-CNN

Cascaded BBox Regression

由于很难训练一个能应付所有IoU水平的regressor,可以把回归任务分解成一个级联的regression问题。


T是级联阶段数,每个regressor对于当前的级联输入都是最优的,随着阶段的深入,bndbox在不断的提升。cascade regression与iterative BBox有以下区别:

  • iteravtive BBox是后处理的方法,而cascaded regression是能够改变bndbox分布的重采样过程。
  • cascaded regression在训练和推理时是一致的,不存在区别。
  • cascaded regression的多个regressor对于对应阶段的输入分布是最优的,而iterative BBox仅对初始分布是最优的。


Bndbox在回归时,为了对scale和location有不变性,将对坐标的学习转移到对坐标差值的学习。由于坐标插值通常较小,因此将其进行归一化,以权衡定位与分类的loss。Cascade R-CNN在每一个stage结束后,都会马上进行计算这些均值/方差

Cascaded Detection

产生Cascade R-CNN的启发点主要有两个:

  • 初始的bndbox分布大多落在低质量的区域,这对于高质量classifiers来说是无效的学习。
  • 所有的曲线都高于对角线,即regressor都倾向于能够提升bndbox的IoU。

因此,以集合作为开始,通过级联regress来产生高IoU的集合。这种方法能在提升样本整体IoU水平的同时,使得样本的总数大致维持在一个水平,这会带来两个好处:

  • 不会存在某个阈值的regressor过拟合
  • 高阶段的detector对于高IoU阈值是最优的

随着阶段的深入,一些离群点会被过滤,这保证了特定阈值的detector的训练


在每一个阶段t,都独立一个对阈值最优的classifier 和regressor,是上一阶段的输出,是权重因子,是指示函数,表示背景的不加入计算。与integral loss不同,公式8保证了顺序地训练detectors来逐步提高bndbox质量。在推理时,bndbox的质量是顺序提高的,高质量的detectors只需要面对高质量的bndbox。**Cascade R-CNN训练流程**相比Faster R-CNN, Cascade R-CNN训练流程只是多了一个循环而已。

ini 复制代码
def forward_train(self,
                  x,
                  img_metas,
                  proposal_list,
                  gt_bboxes,
                  gt_labels,
                  gt_bboxes_ignore=None,
                  gt_masks=None):
    losses = dict()
    # 对每个 R-CNN 阶段单独操作( 相比 Faster R-CNN 多了这一层循环)
    for i in range(self.num_stages):
        self.current_stage = i
        # 注意每个 stage 的训练参数不一样,典型的例如 Iou 阈值
        rcnn_train_cfg = self.train_cfg[i]
        lw = self.stage_loss_weights[i]
        sampling_results = []
        # n 个 stage, 就会有 n 个 bbox_assigner 和 bbox_sampler
        if self.with_bbox
            bbox_assigner = self.bbox_assigner[i]
            bbox_sampler = self.bbox_sampler[i]
            num_imgs = len(img_metas)
            # 对每张图片单独分配正负样本和采样
            for j in range(num_imgs):
                assign_result = bbox_assigner.assign(
                    proposal_list[j], gt_bboxes[j], gt_bboxes_ignore[j],
                    gt_labels[j])
                sampling_result = bbox_sampler.sample(
                    assign_result,
                    proposal_list[j],
                    gt_bboxes[j],
                    gt_labels[j],
                    feats=[lvl_feat[j][None] for lvl_feat in x])
                sampling_results.append(sampling_result)
        # 按顺序,对每个 stage 进行 train
        # 相比 Faster R-CNN,不仅仅要输出当前 stage 的 loss,还需要额外输出
        # 预测后分类和 bbox 值,方便用于后续 stage refine
        bbox_results = self._bbox_forward_train(i, x, sampling_results,
                                                gt_bboxes, gt_labels,
                                                rcnn_train_cfg)
        # 记录当前 stage 输出的 loss
        for name, value in bbox_results['loss_bbox'].items():
            losses[f's{i}.{name}'] = (
                value * lw if 'loss' in name else value)
 
        # 如果不是最后一个 stage,则表示需要进行迭代训练
        if i < self.num_stages - 1:
            # pos_is_gts 表示哪些正样本是 gt bbox ( 因为在 R-CNN 阶段会注入 gt bbox 当做额外的
            # RoI 以加速收敛和防止训练不稳定),在 refine 阶段要把这部分正样本剔除
            pos_is_gts = [res.pos_is_gt for res in sampling_results]
            # roi_labels 是在当前 stage 中定义的每个 proposal 的 label, 背景类别是 num_classes
            roi_labels = bbox_results['bbox_targets'][0]
            with torch.no_grad():
                # 对于 label 为背景的 proposal 可能预测后依然是背景,也可能不是背景
                # 为了能够保证一致性,需要将标注为背景的 roi_labels 替换为网络真正预测类别值
                roi_labels = torch.where(
                    roi_labels == self.bbox_head[i].num_classes,
                    bbox_results['cls_score'][:, :-1].argmax(1),
                    roi_labels)
                # 利用 rois bbox_pred 可以得到新的 proposals,用于下个 stage 训练
                proposal_list = self.bbox_head[i].refine_bboxes(
                    bbox_results['rois'], roi_labels,
                    bbox_results['bbox_pred'], pos_is_gts, img_metas)
    return losses

------------------------------------------------

                            版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
                        
原文链接:https://blog.csdn.net/CooVally_AI/article/details/145210873

实验结果

作者在通用目标检测、实例分割数据集COCO上进行了实验,换上骨干网ResNeXt-152的Cascade R-CNN又刷出了新高度!AP达到50.9。如下图:

使用不同的检测器和骨干网,级联后都能获得大幅度的精度提升,如下图:

同时可见,级联后推断速度有少许变慢,但在可接受的范围内。

下图是将Cascade Mask R-CNN与基线版本比较的结果,在实例分割任务中,也取得了明显的精度提升。


作者还在一些特殊目标类、不同数据集上做了实验。


均表明该算法能一致性的提高精度。

Coovally AI模型训练与应用平台

Coovally AI模型训练与应用平台,它整合了整合30+国内外开源社区1000+模型算法

平台已部署Cascade R-CNN系列模型算法

在Coovally平台上,无需配置环境、修改配置文件等繁琐操作,可****一键另存为我的模型 ,上传数据集,即可使用Cascade R-CNN****等热门模型 进行训练与结果预测,全程高速零代码! 而且模型还可分享与下载,满足你的实验研究与产业应用。

总结

Cascade R-CNN通过引入多阶段级联结构,成功突破了传统目标检测方法在复杂样本处理上的局限性。它在小物体、遮挡物体和复杂背景下的表现尤为突出,成为了一种具有重要意义的目标检测算法。随着技术的不断发展,Cascade R-CNN也为后来的模型算法提供了非常好的创新思路。

相关推荐
菜鸟一枚在这2 分钟前
深度解析建造者模式:复杂对象构建的优雅之道
java·开发语言·算法
gyeolhada20 分钟前
2025蓝桥杯JAVA编程题练习Day5
java·数据结构·算法·蓝桥杯
阿巴~阿巴~21 分钟前
多源 BFS 算法详解:从原理到实现,高效解决多源最短路问题
开发语言·数据结构·c++·算法·宽度优先
给bug两拳32 分钟前
Day9 25/2/22 SAT
算法
_Itachi__1 小时前
LeetCode 热题 100 73. 矩阵置零
算法·leetcode·矩阵
夏末秋也凉2 小时前
力扣-贪心-376 摆动序列
算法·leetcode
Orange--Lin2 小时前
【用deepseek和chatgpt做算法竞赛】——还得DeepSeek来 -Minimum Cost Trees_5
人工智能·算法·chatgpt
01_2 小时前
力扣hot100 ——搜索二维矩阵 || m+n复杂度优化解法
算法·leetcode·矩阵
SylviaW082 小时前
python-leetcode 35.二叉树的中序遍历
算法·leetcode·职场和发展