MMDetection 开发规范详解:图像尺寸约定、Loss 返回结构、空 Proposal 处理与 COCO Panoptic 约定
发布时间:2026/9/20 11:54:40 锦皓数字建站

人工智能计算机视觉深度学习模型评测【免费下载链接】mmdetectionOpenMMLab Detection Toolbox and Benchmark项目地址https://gitcode.com/gh_mirrors/mm/mmdetection点击查看免费下载导读本文以 MMDetection 官方文档 docs/en/advanced_guides/conventions.md 为主线系统梳理在 OpenMMLab 2.0 架构下自定义检测器时必知的四类约定数据变换管线中的图像尺寸顺序、model(**data)返回的 loss 字典结构、两阶段检测器对空 Proposal 的专项处理、以及 COCO Panoptic 数据集的标签与结果编码约定。读完本文你将掌握如何正确编写自定义 Transform 与 RoIHead、如何理解并扩展 loss 回传机制、如何安全处理空 batch以及 Panoptic 任务中标签语义与结果解码的正确姿势从而避免在二次开发中最常见的一类隐性 bug。一、图像尺寸顺序约定(width, height)与(height, width)的分界线1.1 为什么会有两套顺序在 OpenMMLab 2.0即 MMDetection 3.x 系列中图像形状参数的顺序存在刻意区分的两套约定数据变换管线的构造参数为了与 OpenCV 的输入参数习惯保持一致所有关于图像形状的初始化参数一律使用(width, height)顺序。例如Resize(scale(1333, 800))、Mosaic(img_scale(640, 640))中的宽在前、高在后数据管线中流转的字段与模型内部为了计算方便经过数据管线与模型的所有形状字段一律使用(height, width)顺序。这样设计的原因很实际OpenCV 的resize、imresize等接口的第一个参数是(width, height)直接透传能减少转换出错而 NumPy/Tensor 的 shape 天然是(H, W, C)用(height, width)可以直接与张量形状对齐。1.2 管线中各形状字段的含义在数据变换管线处理后的结果 dict 中与形状相关的字段及其取值含义如下均为(height, width)字段含义顺序img_shape变换后如 Resize、Mosaic 后图像的高宽(height, width)ori_shape原始图像的高宽(height, width)pad_shapePadding 之后图像的高宽(height, width)batch_input_shapebatch 内统一 padding 后的高宽(height, width)其中batch_input_shape与pad_shape并不是在数据变换管线里产生的而是由数据预处理器在模型前向时写入的。在 mmdet/models/data_preprocessors/data_preprocessor.py 中DetDataPreprocessor会基于inputs[0].size()[-2:]计算batch_input_shape并将 batch 内每张图实际 pad 后的形状记为pad_shape写入每个data_sample的 meta 信息batch_input_shape tuple(inputs[0].size()[-2:]) for data_sample, pad_shape in zip(data_samples, batch_pad_shape): data_sample.set_metainfo({ batch_input_shape: batch_input_shape, pad_shape: pad_shape })值得注意的是pad_shape是逐图的因为AspectRatioBatchSampler允许同一 batch 内不同图按各自长宽比 pad而batch_input_shape是整个 batch 统一的。这两者都是 (height, width) 顺序。1.3 以 Mosaic 为例参数与结果的顺序对照文档以Mosaic变换为典型示例。其构造参数img_scale为(width, height)顺序而写入结果 dict 的img_shape是(height, width)顺序TRANSFORMS.register_module() class Mosaic(BaseTransform): def __init__(self, img_scale: Tuple[int, int] (640, 640), center_ratio_range: Tuple[float, float] (0.5, 1.5), bbox_clip_border: bool True, pad_val: float 114.0, prob: float 1.0) - None: # img_scale order should be (width, height) self.img_scale img_scale def transform(self, results: dict) - dict: ... results[img] mosaic_img # (height, width) results[img_shape] mosaic_img.shape[:2]对照 mmdet/datasets/transforms/transforms.py 中的真实实现可以看得更清楚img_scale声明为(width, height)因此创建 mosaic 画布时使用np.full((int(self.img_scale[1] * 2), int(self.img_scale[0] * 2), 3), ...)即先取img_scale[1]高再取img_scale[0]宽中心点采样center_x random.uniform(*self.center_ratio_range) * self.img_scale[0]、center_y ... * self.img_scale[1]同样遵循 x 对应宽、y 对应高最终results[img_shape] mosaic_img.shape[:2]直接取 NumPy shape天然是(height, width)子图 keep-ratio resize 时scale_ratio_i min(self.img_scale[1] / h_i, self.img_scale[0] / w_i)也是用img_scale[1]与高度比、img_scale[0]与宽度比边界裁剪mosaic_bboxes.clip_([2 * self.img_scale[1], 2 * self.img_scale[0]])同样是 (h, w) 顺序传入。这类约定在仓库中还有一套辅助校验mmdet/utils提供的log_img_scale工具Mosaic构造时以shape_orderwh调用会在配置了非方形img_scale时打印提示日志帮助开发者第一时间发现顺序混淆。在 tests/test_datasets/test_transforms/test_transforms.py 中TestMosaic覆盖了多种校验Mosaic(img_scale640)非 tuple会触发AssertionError、Mosaic(prob1.5)超出[0, 1]范围会触发AssertionError且各测试均断言results[img_shape] results[img].shape[:2]把“结果字段必须是 (H, W)”固化成了回归测试。1.4 给自定义 Transform 作者的检查清单当你编写自定义数据变换时请按以下清单自检构造参数中表示图像尺寸的元组一律写成(width, height)并在 docstring 中注明 The shape order should be (width, height)输出到结果 dict 的img_shape、ori_shape、pad_shape等字段一律取(height, width)使用 OpenCV / mmcv 的 resize 类接口时目标尺寸参数传(width, height)读写 NumPy 数组时用shape[:2]得到(height, width)不要混用。二、Loss 约定以 dict 返回、按 key 回传2.1model(**data)返回 loss dict在 MMDetection 中训练时model(**data)会返回一个包含 loss 与指标metric的 dict。该行为由 mmdet/models/detectors/base.py 中BaseDetector.forward的modeloss分支触发return self.loss(inputs, data_samples)。也就是说loss()抽象方法的返回类型是Union[dict, tuple]而各检测器的loss()内部会把各个 head 返回的 loss 汇总成一个 dict。以 bbox head 为例其loss()方法mmdet/models/roi_heads/bbox_heads/bbox_head.py的返回结构如下class BBoxHead(nn.Module): ... def loss(self, ...): losses dict() # classification loss losses[loss_cls] self.loss_cls(...) # classification accuracy losses[acc] accuracy(cls_score, labels) # bbox regression loss losses[loss_bbox] self.loss_bbox(...) return lossesbbox_head.loss()会在模型前向loss()方法过程中被调用。返回的 dict 包含三个 keyloss_bbox、loss_cls、acc。2.2 只有 key 含loss的项参与反传这是本小节最核心的一条规则loss_bbox、loss_cls是真正的损失项会参与反向传播acc只是分类精度指标仅用于监控训练过程不参与反向传播。默认情况下框架只对 key 中包含loss的项做反向传播。这一行为由BaseDetector.train_step()控制——即基类中负责梯度回传与参数更新的训练入口forward本身只负责计算不做反传与参数更新这一点在 base.py 的 docstring 中有明确说明。如果你想改变“只有含loss的 key 才回传”的默认行为文档明确指出修改BaseDetector.train_step()即可。例如自定义一个带额外约束项如中间层特征正则、辅助自监督任务的检测器时可以覆写train_step()将额外的张量项并入回传列表或调整 loss 加权逻辑。补充一个工程细节mmdet/engine/hooks/checkloss_hook.py中的CheckInvalidLossHook会每隔interval次迭代检查outputs[loss]是否有限值若出现 NaN/Inf 会立即断言失败并输出日志。这说明框架默认约定train_step汇总出的总 loss 挂在loss这个 key 下自定义train_step时也应遵循该命名以便训练监控钩子正常工作。2.3 给自定义 Head 作者的约定head 的loss()方法必须返回dictkey 命名遵循loss_xxx模式如loss_cls、loss_bbox、loss_mask、loss_centerness非 loss 的监控指标如acc、iou可以放在同一 dict 中但不要命名为loss_前缀否则会被误当作损失参与反传若涉及对 loss 的加权在 head 内部用weight参数或loss_xxx.weight配置项处理保证最终 dict 中各项仍是可直接 sum 的张量。三、空 Proposal 约定两阶段模型必须同时处理整 batch 空与单图空3.1 为什么需要专门处理两阶段检测器的 RoIHead 依赖 RPN 输出的 proposals 作为输入。在实际推理中会频繁遇到两种情况整个 batch 没有任何 proposal例如输入全是背景图batch 中某一张图没有 proposal但其他图有。如果不做特殊处理空张量参与后续的bbox2roi、predict_by_feat、级联 refine 等流程会引发形状不匹配的报错。因此 MMDetection 对两阶段模型的空 proposals 做了专门处理并提供单元测试覆盖。3.2 CascadeRoIHead 中的处理范式文档以 mmdet/models/roi_heads/cascade_roi_head.py 的simple_test为例给出两段处理逻辑。第一段处理整 batch 无 proposal 的情况# simple_test method ... # There is no proposal in the whole batch if rois.shape[0] 0: bbox_results [[ np.zeros((0, 5), dtypenp.float32) for _ in range(self.bbox_head[-1].num_classes) ]] * num_imgs if self.with_mask: mask_classes self.mask_head[-1].num_classes segm_results [[[] for _ in range(mask_classes)] for _ in range(num_imgs)] results list(zip(bbox_results, segm_results)) else: results bbox_results return results当rois.shape[0] 0rois是bbox2roi拼接后的结果行数为 0 即整个 batch 无任何 proposal时直接按类别数量构造空结果每个类别对应一个(0, 5)的空数组5 列对应[x1, y1, x2, y2, score]mask 分支则用空列表占位然后提前返回不再进入后续的 refine 与预测流程。第二段处理单张图无 proposal 的情况级联 refine 阶段# There is no proposal in the single image for i in range(self.num_stages): ... if i self.num_stages - 1: for j in range(num_imgs): # Handle empty proposal if rois[j].shape[0] 0: bbox_label cls_score[j][:, :-1].argmax(dim1) refine_roi self.bbox_head[i].regress_by_class( rois[j], bbox_label, bbox_pred[j], img_metas[j]) refine_roi_list.append(refine_roi)在级联阶段之间对每张图单独判断rois[j].shape[0] 0只有非空图才执行regress_by_class精修 proposal空图直接跳过从而避免对空张量做按类回归。3.3 当前仓库实现中的演进与验证需要说明的是在本文所基于的 MMDetection 3.x 源码中上述范式进一步演进为更统一的empty_instances()工具。在 cascade_roi_head.py 的predict_bbox中num_proposals_per_img tuple(len(p) for p in proposals) rois bbox2roi(proposals) if rois.shape[0] 0: return empty_instances( batch_img_metas, rois.device, task_typebbox, box_typeself.bbox_head[-1].predict_box_type, num_classesself.bbox_head[-1].num_classes, score_per_clsrcnn_test_cfg is None)empty_instances会按num_classes生成空预测结果bboxes/labels/scores/masks 均为空其输出可直接被DetDataSample消费同理predict_mask中也有if mask_rois.shape[0] 0的空处理分支cascade_roi_head.py。给自定义 RoIHead 作者的建议文档明确给出的指引如果你实现了自定义RoIHead请参照上述方式处理空 proposals在拼接rois之后先判断rois.shape[0] 0用empty_instances或手工构造空结果提前返回在逐图循环、逐 stage 循环中对每张图的rois[j].shape[0] 0做判空保证空图不进入 refine/regress_by_class 等依赖非空张量的算子同时覆盖“整 batch 空”与“单图空”两个层次缺一不可。四、COCO Panoptic 数据集约定标签语义与结果编码4.1 语义分割标签的 VOID 约定变迁MMDetection 对CocoPanopticDataset的实现约定如下这是与普通检测标签最不同、也最容易被忽略的一点mmdet ≤ 2.16.0语义分割中的前景/背景标签范围与 MMDetection 默认设置不同——标签0表示VOID空洞/忽略标签类别标签从1开始自 mmdet 2.17.0 起为了与边界框标签保持一致语义分割的类别标签改为从0开始标签255表示VOID。也就是说在 3.x 系列中语义分割的类别编号与 bbox 的类别编号统一0 ~ num_classes-1是有效类别255是忽略区域。如果从旧版本2.16.0 及以前迁移自定义数据集或训练脚本务必核对标注文件中的标签语义否则会出现“背景变类别、VOID 被当成真值”的严重错误。4.2 Pad 管线对 seg 填充值的支持为了支持上述255作为 VOID 的约定Pad变换专门提供了对seg语义分割图设置填充值的能力。在 mmdet/datasets/transforms/transforms.py 中Pad的pad_val参数支持两种形式单个数值用于 pad 图像同时语义分割图固定用255填充这正是为了保持 VOID 语义避免 pad 区域被当作有效类别参与 lossdict 形式可分别为不同字段指定填充值其中seg字段通常应配置为255。在 panoptic 训练中Pad之后由数据预处理器进一步执行pad_gt_sem_segdata_preprocessor.py同样按batch_input_shape以 VOID 值填充语义分割真值保证 loss 计算时忽略 pad 区域。4.3 结果图的编码公式评估阶段panoptic 结果是与原始图像同尺寸的一张图map。结果图中每个像素值的编码格式为panoptic_value instance_id * INSTANCE_OFFSET category_id即instance_id * INSTANCE_OFFSET category_id。在 mmdet/evaluation/functional/panoptic_utils.py 中# pan_id ins_id * INSTANCE_OFFSET cat_id INSTANCE_OFFSET 1000INSTANCE_OFFSET取值为 1000。解码时对结果图的像素值整除 1000 得到实例 idins_id取模 1000 得到类别 idcat_id。这样单个像素同时编码了“属于哪个实例”和“属于哪个语义类别”实例分割与语义分割共用一张图。4.4 配套实现与配置文件CocoPanopticDataset的实现位于 mmdet/datasets/coco_panoptic.py继承自CocoDataset其data_prefix中seg前缀指向 panoptic 分割图目录METAINFO同时定义了classes含 thing 与 stuff与thing_classes。完整的训练/验证配置可参考 configs/base/datasets/coco_panoptic.py训练管线使用LoadImageFromFileLoadPanopticAnnotationsResize(scale(1333, 800), keep_ratioTrue)RandomFlipPackDetInputs数据路径中ann_fileannotations/panoptic_train2017.json、data_prefixdict(imgtrain2017/, segannotations/panoptic_train2017/)评估器使用CocoPanopticMetric传入ann_file与seg_prefix。单测方面tests/test_datasets/test_coco_panoptic.py 对CocoPanopticDataset的加载、panoptic json 中的重复 id 处理、以及filter_cfgdict(filter_empty_gtTrue, min_size32)下的行为均有覆盖结合 configs 与 panoptic_utils.py可以完整还原从数据加载到 PQ 指标计算的整条链路。五、总结二次开发前必读的四条约定约定核心要点主要出处图像尺寸顺序构造参数(width, height)管线字段与模型内部(height, width)img_shape/ori_shape/pad_shape/batch_input_shape均为 (H, W)mmdet/datasets/transforms/transforms.py、data_preprocessor.pyLoss 返回结构model(**data)返回 dict仅 key 含loss的项参与反传acc等仅为监控指标修改train_step()可改变回传行为base.py、bbox_head.py空 Proposal同时处理整 batch 空rois.shape[0] 0与单图空rois[j].shape[0] 0cascade_roi_head.pyCOCO Panoptic3.x 起类别标签从 0 开始、255 为 VOID结果像素编码ins_id * 1000 cat_idcoco_panoptic.py、panoptic_utils.py这些约定不是“风格建议”而是框架硬性契约自定义 Transform、Head、RoIHead 或迁移老版本数据时违反其中任何一条都会导致难以排查的维度错误、梯度异常或指标失真。建议在动手改代码前先对照本文四条约定逐项自检如果只是基于现有算法做配置层面的二次开发则重点关注第一节与第四节的内容即可。赞分享人工智能计算机视觉深度学习模型评测【免费下载链接】mmdetectionOpenMMLab Detection Toolbox and Benchmark项目地址https://gitcode.com/gh_mirrors/mm/mmdetection点击查看免费下载相关推荐XiaoMi-Pro-Hackintosh性能调优CPU频率管理与温度控制终极指南XiaoMi Pro Hackintosh性能调优CPU频率管理与温度控制终极指南 XiaoMi Pro Hackintosh项目为小米笔记本Pro系列提供了固件驱动开发Process Hacker项目开发指南构建规范与编码约定详解Process Hacker项目开发指南构建规范与编码约定详解 项目概述 Process Hacker是一个功能强大的系统监控工具它提供了对进程、线程、服务桌面应用调试器应用安全驱动开发实验记录完全指南ma-gym Monitor 包装器的统计、视频与多智能体日志用法实验记录完全指南ma gym Monitor 包装器的统计、视频与多智能体日志用法 本文带你完整掌握 ma gym Monitor 包装器 的用法它为创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。