CentripetalNet 在 MMDetection 中的实现与实战指南:基于向心偏移的高质量角点配对检测器
【免费下载链接】mmdetectionOpenMMLab Detection Toolbox and Benchmark项目地址: https://gitcode.com/gh_mirrors/mm/mmdetection
CentripetalNet 是 OpenMMLab MMDetection 工具箱中内置的基于关键点(Keypoint)的 Anchor-Free 目标检测器实现。它以"向心偏移(Centripetal Shift)"替代 CornerNet 的嵌入向量(Embedding)来配对同一实例的左上/右下角点,并引入 Cross-star Deformable Convolution 做特征自适应,显著提升角点配对的准确率。本文将围绕 configs/centripetalnet/README.md 展开,结合仓库源码、完整配置与测试用例,讲解该算法在 MMDetection 中的结构、配置、训练、测试与 TTA 全流程,帮助你直接复现并深入理解这一经典检测器。
CentripetalNet 核心思想:从嵌入向量到向心偏移
关键点检测器(Keypoint-based Detector)通过预测物体的左上角与右下角关键点来生成边界框,其性能往往受限于角点匹配错误——即左上角与右下角点虽然各自预测准确,却无法正确配对到同一实例。CornerNet 使用关联嵌入(Associative Embedding)来解决配对问题,但嵌入向量的度量空间学习难度较大,容易出现误配。
CentripetalNet 提出了一种更直观、信息量更丰富的配对方式:为每个角点额外预测一个"向心偏移"(Centripetal Shift)。如 configs/centripetalnet/README.md 的 Abstract 所述:
CentripetalNet predicts the position and the centripetal shift of the corner points and matches corners whose shifted results are aligned. Combining position information, our approach matches corner points more accurately than the conventional embedding approaches do.
具体而言:
- 对左上角点,向心偏移指向物体中心的方向(向右下);
- 对右下角点,向心偏移指向物体中心的方向(向左上);
- 若两个角点属于同一实例,它们各自"向心移动"后的位置应在物体中心附近重合,据此即可完成配对。
该方法相比传统嵌入方法能更精确地匹配角点。同时,为了增强角点处对框内信息的感知,作者设计了Cross-star Deformable Convolution进行特征自适应(feature adaption)。论文还探索了在 Anchor-Free 检测器上通过掩码预测模块扩展实例分割任务,在 MS-COCO test-dev 上取得了 48.0% AP 的检测精度,并可与当时先进的实例分割方法(40.2% MaskAP)相媲美(引自该 README 的 Abstract)。
已发布模型与复现指标
README 的 "Results and Models" 表格给出了官方发布模型的复现信息(仅一个配置):
| Backbone | Batch Size | Step/Total Epochs | Mem (GB) | Inf time (fps) | box AP | Config | Download |
|---|---|---|---|---|---|---|---|
| HourglassNet-104 | 16 x 6 | 190/210 | 16.7 | 3.7 | 44.8 | config | model | log |
该表提供了两个重要复现注意事项(README Note 部分):
- TTA 设置:官方 TTA(Test-Time Augmentation)为单尺度 +
flip=True。要复现 TTA 精度,需在测试命令中追加--tta参数。 - 检查点选择:官方发布的是最优 checkpoint而非最后一个 checkpoint(box AP 44.8 vs 训练末期 44.6)。
模型结构与配置逐项解析
核心配置文件为 configs/centripetalnet/centripetalnet_hourglass104_16xb6-crop511-210e-mstest_coco.py,它基于default_runtime与 COCO 检测数据集配置组合而成。
数据预处理器
data_preprocessor = dict( type='DetDataPreprocessor', mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], bgr_to_rgb=True)使用 ImageNet 统计的均值/方差做归一化,bgr_to_rgb=True表示将 BGR 输入转为 RGB。注意后续RandomCenterCropPad中的to_rgb=data_preprocessor['bgr_to_rgb']注释明确说明"图像数据不会被转为 RGB"(Image data is not converted to rgb),即填充逻辑与预处理器的通道顺序保持一致。
模型主体
model = dict( type='CornerNet', data_preprocessor=data_preprocessor, backbone=dict( type='HourglassNet', downsample_times=5, num_stacks=2, stage_channels=[256, 256, 384, 384, 384, 512], stage_blocks=[2, 2, 2, 2, 2, 4], norm_cfg=dict(type='BN', requires_grad=True)), neck=None, bbox_head=dict( type='CentripetalHead', num_classes=80, in_channels=256, num_feat_levels=2, corner_emb_channels=0, loss_heatmap=dict( type='GaussianFocalLoss', alpha=2.0, gamma=4.0, loss_weight=1), loss_offset=dict(type='SmoothL1Loss', beta=1.0, loss_weight=1), loss_guiding_shift=dict( type='SmoothL1Loss', beta=1.0, loss_weight=0.05), loss_centripetal_shift=dict( type='SmoothL1Loss', beta=1.0, loss_weight=1)), train_cfg=None, test_cfg=dict( corner_topk=100, local_maximum_kernel=3, distance_threshold=0.5, score_thr=0.05, max_per_img=100, nms=dict(type='soft_nms', iou_threshold=0.5, method='gaussian')))各组件的作用如下:
- 检测器类型:整体使用
CornerNet单阶段检测器,源码位于 mmdet/models/detectors/cornernet.py,该类继承SingleStageDetector,neck=None表示不使用 Neck,HourglassNet 直接输出多层特征。 - Backbone:HourglassNet-104,
downsample_times=5,num_stacks=2(双沙漏堆叠,第二层输出用于最终预测),stage 通道与残差块数量按stage_channels/stage_blocks配置。 - Head:
CentripetalHead,实现位于 mmdet/models/dense_heads/centripetal_head.py。其中:num_feat_levels=2:HourglassNet-104 同时输出最终特征与中间监督特征,故为 2 个层级(源码注释说明 HourglassNet-52 仅输出最终特征,此时应为 1);corner_emb_channels=0:关闭嵌入分支,这是 CentripetalHead 与 CornerHead 的关键差异——用 guiding shift + centripetal shift 替代 embedding 完成配对;- 三个损失:角点热图使用
GaussianFocalLoss(alpha=2.0, gamma=4.0)(CornerNet 论文中变体 focal loss,实现见 mmdet/models/losses/gaussian_focal_loss.py),偏移与两种 shift 均使用SmoothL1Loss(beta=1.0)。
- test_cfg 解码参数:
corner_topk=100:每张图从热图中取前 100 个角点;local_maximum_kernel=3:3×3 局部极大值池化核,用于热图 NMS;distance_threshold=0.5:配对距离阈值,见下文解码逻辑;score_thr=0.05、max_per_img=100:输出过滤阈值与每图最大框数;nms:使用soft_nms(高斯法,iou_threshold=0.5)。
训练与测试数据流水线
训练流水线(train_pipeline)包含:
train_pipeline = [ dict(type='LoadImageFromFile', backend_args=_base_.backend_args), dict(type='LoadAnnotations', with_bbox=True), dict( type='PhotoMetricDistortion', brightness_delta=32, contrast_range=(0.5, 1.5), saturation_range=(0.5, 1.5), hue_delta=18), dict( type='RandomCenterCropPad', crop_size=(511, 511), ratios=(0.6, 0.7, 0.8, 0.9, 1.0, 1.1, 1.2, 1.3), test_mode=False, test_pad_mode=None, mean=data_preprocessor['mean'], std=data_preprocessor['std'], to_rgb=data_preprocessor['bgr_to_rgb']), dict(type='Resize', scale=(511, 511), keep_ratio=False), dict(type='RandomFlip', prob=0.5), dict(type='PackDetInputs'), ]要点:
PhotoMetricDistortion做颜色抖动增强;RandomCenterCropPad是 CornerNet 系算法训练的关键增强:随机裁剪中心区域并填充到方形。配置注释特别指出:"训练中裁剪图像会被填充为正方形,但尺寸可能小于 crop_size";Resize统一缩放到 511×511(COCO 上 CornerNet 系列的标准输入尺寸);RandomFlip(prob=0.5)随机翻转。
测试流水线(test_pipeline)与训练不同:不做 Resize,而是使用RandomCenterCropPad的test_mode=True,并以test_pad_mode=['logical_or', 127]将图像逻辑或填充至边界值 127;PackDetInputs需要保留border元信息(用于解码时还原裁剪偏移)。
test_pipeline = [ dict(type='LoadImageFromFile', to_float32=True, backend_args=_base_.backend_args), dict( type='RandomCenterCropPad', crop_size=None, ratios=None, border=None, test_mode=True, test_pad_mode=['logical_or', 127], mean=data_preprocessor['mean'], std=data_preprocessor['std'], to_rgb=data_preprocessor['bgr_to_rgb']), dict(type='LoadAnnotations', with_bbox=True), dict( type='PackDetInputs', meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape', 'border')) ]数据加载、优化器与学习率调度
train_dataloader = dict( batch_size=6, num_workers=3, batch_sampler=None, dataset=dict(pipeline=train_pipeline)) val_dataloader = dict(dataset=dict(pipeline=test_pipeline)) test_dataloader = val_dataloader- 单卡 batch_size=6、3 个 worker;
batch_sampler=None表示关闭 MMEngine 默认的 InfiniteSampler 行为; - 优化器使用Adam,lr=0.0005,并配合
clip_grad(max_norm=35, norm_type=2)梯度裁剪(HourglassNet 训练不稳定,裁剪必不可少);
optim_wrapper = dict( type='OptimWrapper', optimizer=dict(type='Adam', lr=0.0005), clip_grad=dict(max_norm=35, norm_type=2)) max_epochs = 210 param_scheduler = [ dict(type='LinearLR', start_factor=1.0 / 3, by_epoch=False, begin=0, end=500), dict( type='MultiStepLR', begin=0, end=max_epochs, by_epoch=True, milestones=[190], gamma=0.1) ] train_cfg = dict(type='EpochBasedTrainLoop', max_epochs=max_epochs, val_interval=1)学习率采用两段式:前 500 次迭代从 1/3 倍学习率线性预热(LinearLR),190 epoch 处按MultiStepLR衰减 0.1 倍,总计训练 210 epoch——这与 README 表格中的 "190/210" 完全对应。
自动学习率缩放
# base_batch_size = (16 GPUs) x (6 samples per GPU) auto_scale_lr = dict(base_batch_size=96)配置末尾通过auto_scale_lr = dict(base_batch_size=96)声明基准批量大小(16 卡 × 6 样本)。当实际训练批量不同时,MMEngine 会依据批量大小自动线性缩放学习率。注释明确提示该值由官方基准确定,用户不应修改。
源码级原理:CentripetalHead 的网络结构
CentripetalHead继承自 mmdet/models/dense_heads/corner_head.py 中的CornerHead,其构造逻辑(centripetal_head.py)先调用父类初始化,再构建两个新分支:
loss_guiding_shift:guiding shift 损失(默认权重 0.05,较小,因为它仅起"引导"作用);loss_centripetal_shift:向心偏移损失(默认权重 1)。
构造函数还断言centripetal_shift_channels == 2且guiding_shift_channels == 2(各对应 x/y 两个方向分量)。
各分支与 Cross-star Deformable Convolution
_init_centripetal_layers(centripetal_head.py)为每个特征层级构建了 8 组子模块,每组又分为tl_(左上)与br_(右下)两部分:
feat_adaption:DeformConv2d(可变形卷积),即论文中的 Cross-star Deformable Convolution,输入输出均为in_channels,卷积核feat_adaption_conv_kernel=3;guiding_shift:预测 2 通道 guiding shift(从角点指向中心),由 3×3 Conv + 1×1 Conv 堆叠而成;dcn_offset:由 guiding shift 特征预测可变形卷积的偏移量(通道数为kernel² × 2 = 18);centripetal_shift:预测 2 通道向心偏移。
前向流程(forward_single,centripetal_head.py)清晰地体现了 Cross-star Deformable Convolution 的自适应思想:
- 先通过父类
CornerHead.forward_single得到角点热图、偏移以及角点池化特征tl_pool/br_pool(CornerNet 的双向 Corner Pooling 模块BiCornerPool定义在 corner_head.py,沿 top/left 与 bottom/right 两个方向池化); - 从池化特征预测guiding shift;
- guiding shiftdetach 后经
dcn_offset生成可变形卷积偏移量——detach 的目的是让 guiding shift 分支作为稳定的"引导",不被 DCN 反向传播干扰; - 用该偏移量对池化特征做可变形卷积特征自适应;
- 在自适应后的特征上预测centripetal shift。
最终输出 8 个张量:tl_heat, br_heat, tl_off, br_off, tl_guiding_shift, br_guiding_shift, tl_centripetal_shift, br_centripetal_shift。
目标生成:三种监督信号的数学定义
CornerHead.get_targets(corner_head.py)同时承担 CentripetalNet 的目标生成,关键代码如下:
# Guiding shift is a kind of offset, from center to corner if with_guiding_shift: gt_tl_guiding_shift[batch_id, 0, top_idx, left_idx] = scale_center_x - left_idx gt_tl_guiding_shift[batch_id, 1, top_idx, left_idx] = scale_center_y - top_idx gt_br_guiding_shift[batch_id, 0, bottom_idx, right_idx] = right_idx - scale_center_x gt_br_guiding_shift[batch_id, 1, bottom_idx, right_idx] = bottom_idx - scale_center_y # Centripetal shift is also a kind of offset, from center to corner # and normalized by log. if with_centripetal_shift: gt_tl_centripetal_shift[batch_id, 0, top_idx, left_idx] = log(scale_center_x - scale_left) gt_tl_centripetal_shift[batch_id, 1, top_idx, left_idx] = log(scale_center_y - scale_top) gt_br_centripetal_shift[batch_id, 0, bottom_idx, right_idx] = log(scale_right - scale_center_x) gt_br_centripetal_shift[batch_id, 1, bottom_idx, right_idx] = log(scale_bottom - scale_center_y)- Guiding shift:从角点指向中心的普通偏移(角点坐标 → 中心坐标);
- Centripetal shift:从角点指向中心、但以中心到角点的距离取对数归一化的偏移(
log(scale_center_x - scale_left)等),即源码注释所说 "a kind of offset, from center to corner and normalized by log"。取对数可对不同尺度目标提供更均衡的回归目标。
热图目标沿用 CornerNet 的高斯半径方法:通过gaussian_radius(min_overlap=0.3)计算半径后,用gen_gaussian_target在真实角点位置绘制高斯核;偏移目标只存放下采样取整造成的亚像素残差。
损失计算
loss_by_feat(centripetal_head.py)组织 4 组损失:
det_loss:两角点热图的 GaussianFocalLoss;off_loss:角点偏移 SmoothL1Loss;guiding_loss:guiding shift 的 SmoothL1Loss;centripetal_loss:向心偏移的 SmoothL1Loss。
loss_by_feat_single(centripetal_head.py)的细节值得注意:所有 shift 损失都通过gt_*_heatmap.eq(1)生成掩码,即只在真实角点位置计算损失(tl_mask/br_mask,类无关、shape 为 batch×1×H×W),避免对背景像素引入无意义回归;guiding 与 centripetal 损失分别取左上/右下两分支的平均。
解码与配对:距离阈值与中心区域约束
推理时predict_by_feat与_decode_heatmap(corner_head.py)执行关键点解码与配对:
- 对热图做 3×3 局部极大值抑制(
get_local_maximum),取每类 top-k 角点; - 对角点坐标加上预测的亚像素偏移;
- 对向心偏移取指数还原(
tl_centripetal_shift.exp()),从而得到两个角点各自指向的"预测中心"(tl_ctxs, tl_ctys)与(br_ctxs, br_ctys); - 依据论文 4.1 节的魔法数计算中心区域
rcentral(mu = 1/2.4,大面积框area_bboxes > 3500时用1/2.1),并用面积比值计算距离度量dists = area_ct_bboxes / area_rcentral; - 通过约束过滤候选框:
- 预测中心落在
rcentral区域之外则剔除(scores[tl_ctx_inds] = -1等 4 个方向); - 距离度量超过
distance_threshold(配置为 0.5)则剔除(dist_inds); - 类别不一致、宽高非法(
br_xs <= tl_xs等)的框剔除;
- 预测中心落在
- 剩余候选按平均分数 top-k,最后经
soft_nms(_bboxes_nms)输出。
解码逻辑同时兼容 embedding 与 centripetal shift 两种配对方式,并通过断言with_embedding + with_centripetal_shift == 1强制二选一。代码注释还提到一个工程细节:热图 top-k 展开使用repeat而非expand,因为expand是浅拷贝,会导致测试阶段 mAP 下降约 10%。
如何训练与测试
在仓库根目录下,使用官方脚本即可完成训练(数据路径需按 configs/base/datasets/coco_detection.py 中的data_root准备 COCO 格式数据集):
# 单卡训练 python tools/train.py configs/centripetalnet/centripetalnet_hourglass104_16xb6-crop511-210e-mstest_coco.py # 多卡(8 卡)分布式训练 bash tools/dist_train.sh configs/centripetalnet/centripetalnet_hourglass104_16xb6-crop511-210e-mstest_coco.py 8 # 测试(普通推理) python tools/test.py configs/centripetalnet/centripetalnet_hourglass104_16xb6-crop511-210e-mstest_coco.py <checkpoint.pth> # 开启 TTA 复现 README 中的精度 python tools/test.py configs/centripetalnet/centripetalnet_hourglass104_16xb6-crop511-210e-mstest_coco.py <checkpoint.pth> --tta注意:单卡直接运行将使用配置的 batch_size=6,而auto_scale_lr声明的是 96 的基准批量;如需严格对齐官方指标,建议按 16 卡 × 6 的配置进行分布式训练,或使用自动学习率缩放。
TTA 配置解析
配置尾部定义了tta_model与tta_pipeline:
tta_model = dict( type='DetTTAModel', tta_cfg=dict( nms=dict(type='soft_nms', iou_threshold=0.5, method='gaussian'), max_per_img=100))tta_pipeline将测试变换组织为多组候选变换的笛卡尔积:RandomFlip(prob=1 与 prob=0 两档,实现"原始图 + 水平翻转")、RandomCenterCropPad(test_mode)、LoadAnnotations与PackDetInputs。配置注释特别提醒了一个易错点:
RandomFlipmust be placed beforeRandomCenterCropPad, otherwise bounding box coordinates after flipping cannot be recovered correctly.
即翻转变换必须位于中心裁剪填充之前,否则翻转后的边界框坐标无法正确还原。PackDetInputs的meta_keys因此额外包含了flip与flip_direction。
测试用例验证
仓库为CentripetalHead提供了独立单测:tests/test_models/test_dense_heads/test_centripetal_head.py,覆盖两种关键场景:
- 空 GT 场景:构造
num_classes=4, in_channels=1, corner_emb_channels=0的 head,输入两个特征层级,断言det_loss > 0(鼓励预测背景),而guiding_loss、centripetal_loss、off_loss均为 0(无真实框时不应产生回归损失); - 两个 GT 框场景:传入两个真实框,断言 4 组损失全部大于 0,验证训练信号完整生效。
该测试同时印证了 head 的输入规格:num_feat_levels个特征层级、forward输出可直接喂入loss_by_feat,并确认corner_emb_channels=0时不会构建 embedding 分支。
引用
如需引用 CentripetalNet,请使用官方提供的 BibTeX(见 configs/centripetalnet/README.md):
@InProceedings{Dong_2020_CVPR, author = {Dong, Zhiwei and Li, Guoxuan and Liao, Yue and Wang, Fei and Ren, Pengju and Qian, Chen}, title = {CentripetalNet: Pursuing High-Quality Keypoint Pairs for Object Detection}, booktitle = {Proceedings of the IEEE/CVF Conference on Computer Vision and Pattern Recognition (CVPR)}, month = {June}, year = {2020} }小结
CentripetalNet 在 MMDetection 中的实现完整覆盖了论文提出的三大技术点:向心偏移配对(替代嵌入向量)、Cross-star Deformable Convolution 特征自适应(feat_adaption+dcn_offset)以及中心区域约束解码(rcentral+distance_threshold)。通过 configs/centripetalnet/centripetalnet_hourglass104_16xb6-crop511-210e-mstest_coco.py 一份配置即可复现 44.8 box AP(COCO test-dev,配合 TTA);深入阅读 centripetal_head.py 与 corner_head.py 则能掌握角点类检测器从目标生成、损失计算到解码配对的完整链路,为在此基础上改进或迁移到新任务提供可靠参照。
【免费下载链接】mmdetectionOpenMMLab Detection Toolbox and Benchmark项目地址: https://gitcode.com/gh_mirrors/mm/mmdetection
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考