Detectron2实战指南:从配置驱动到目标检测与实例分割全流程
1. 从零认识 Detectron2它到底是什么能解决什么问题第一次接触 Detectron2 是在一个商品图像识别的需求里当时团队需要在一批货架照片中框出每一件商品并分类。试过几个开源方案之后最终落在了 Detectron2 上原因很简单它把目标检测、实例分割、关键点检测这些任务统一到了一套配置驱动的框架里换任务基本只需要换配置文件和数据集注册方式代码改动量极小。这篇文章就把我这几年用 Detectron2 的经验完整梳理一遍从它是什么、为什么这么设计到环境配置、训练调参、踩坑排查尽量写成一份可以直接照着复现的实操记录。Detectron2 是某实验室开源的一个基于 PyTorch 的计算机视觉库前身是第一代 Detectron基于 Caffe2。它的核心定位是提供目标检测、实例分割、全景分割、关键点检测等任务的统一实现内置了 Faster R-CNN、Mask R-CNN、RetinaNet、FCOS、PointRend 等一批主流模型。和很多一个模型一个仓库的项目不同Detectron2 的设计哲学是配置驱动 模块化注册模型结构、数据增强、学习率策略、训练轮次全部写在 YAML 配置里代码层面通过注册机制把数据集、模型、评估器挂载进去。它适合谁如果你是要做视觉检测类项目的算法工程师Detectron2 能帮你省掉大量重复造轮子的时间如果你是学生或者刚入门视觉方向它也是一套很好的学习范本因为它的模块划分非常清晰backbone、proposal generator、roi heads、box head 各司其职。但它不是零代码工具你需要理解配置文件里每个字段的含义否则调参时会非常盲目。这篇文章会覆盖四块内容整体设计思路、核心配置项拆解、完整实操流程、以及常见问题排查。每一块我都会结合自己实际跑过的场景来讲尽量把为什么这么配说清楚而不是只给一份能跑的配置。2. 整体设计与思路拆解为什么是配置驱动2.1 配置驱动背后的工程考量很多人第一次看 Detectron2 的配置文件会觉得字段太多、太杂一个 BASE 配置动辄几百行。但如果你从工程角度想这套设计其实解决了一个很现实的问题视觉模型的实验组合爆炸。一个检测模型涉及 backbone 类型、FPN 开关、anchor 尺寸、采样比例、损失权重、学习率调度、数据增强策略等几十个变量如果每个组合都写一份训练脚本维护成本会失控。Detectron2 的做法是把所有可变项抽到 YAML 里代码只负责读配置、建模型、跑训练。这样带来三个直接好处第一实验可复现一份配置文件就是一个完整实验记录第二继承机制让配置可以复用你写一个my_config.yaml里面只写BASE: COCO-Detection/faster_rcnn_R_50_FPN_1x.yaml然后只覆盖你想改的字段其余全部继承第三配置和代码解耦调参不需要改 Python 文件降低了出错概率。我个人的体会是刚开始用的时候会觉得这套继承机制有点绕但一旦习惯了做消融实验的效率会高很多。比如我想对比不同 anchor 尺寸对召回率的影响只需要写几份只改ANCHOR_GENERATOR.SIZES的配置其余全部继承跑完直接对比指标就行。2.2 模块化注册机制的价值Detectron2 另一个核心设计是注册机制Registry。数据集、模型、评估器、数据加载器都通过装饰器注册到全局字典里配置里写的是字符串名字运行时按名字查找。这套机制的好处是扩展性极强你想加一个自定义数据集写个函数用DatasetRegistry.register()装饰一下就行想加一个自定义 backbone同样注册进去配置里改个名字就能用。这种名字到实现的映射本质上是把依赖注入的思想用在了视觉框架里。它避免了硬编码的 if-else 分支也让第三方扩展变得干净。我在一个项目里需要加一个自定义的注意力模块到 backbone 里就是通过注册一个新的 backbone 实现的没有改动框架任何源码升级版本时也不会冲突。2.3 和同类框架的取舍市面上做检测的框架不少比如 MMDetection、YOLO 系列、Detectron2。选哪个其实取决于你的需求。YOLO 系列胜在推理速度和部署友好适合对实时性要求高的场景MMDetection 的模型库更全覆盖的论文实现更多Detectron2 的优势在于工程规范性和模块清晰度它的配置系统、注册机制、训练循环抽象都做得比较成熟适合做需要长期维护的项目。我选 Detectron2 的一个具体原因是它的DefaultTrainer和Hook机制。训练过程中的日志、检查点保存、学习率调度、评估都可以通过 Hook 挂载想加一个自定义的早停策略或者指标记录写个 Hook 注册进去就行不用改训练主循环。这在需要频繁调整训练策略的项目里非常省事。3. 核心配置项拆解与实操要点3.1 环境配置版本匹配是第一道坎Detectron2 的安装是新手最容易卡住的地方核心原因是它依赖 PyTorch 和 CUDA而这三者的版本必须严格匹配。我踩过的坑是先装了最新版 PyTorch结果 Detectron2 编译时提示 CUDA 版本不兼容折腾了半天才发现是 PyTorch 自带的 CUDA 运行时和系统 CUDA 版本对不上。正确的顺序应该是先确定显卡驱动支持的 CUDA 版本再选对应的 PyTorch最后装匹配的 Detectron2。具体来说用nvidia-smi看驱动支持的最高 CUDA 版本然后去 PyTorch 官网找对应 CUDA 版本的安装命令。比如驱动支持 CUDA 11.8就装torch2.0.1cu118这类版本。安装 Detectron2 本身有两种方式从源码编译和用预编译包。源码编译更稳妥能确保和你的环境匹配git clone https://github.com/facebookresearch/detectron2.git cd detectron2 pip install -e .编译过程中如果报错八成是 CUDA 环境变量没配好检查CUDA_HOME是否指向正确的 CUDA 安装路径。另外gcc版本太新也可能导致编译失败Detectron2 对 gcc 版本有一定要求太新的版本会有兼容问题必要时降级到 gcc 9 或 10。提示如果你只是想做推理不想编译可以找对应 CUDA 和 PyTorch 版本的预编译 wheel 包但要注意 wheel 包的版本必须和你的 torch 版本完全一致否则运行时会报符号找不到的错误。3.2 配置文件结构从 BASE 到具体字段Detectron2 的配置文件是 YAML 格式顶层字段包括MODEL、DATASETS、SOLVER、INPUT、TEST等。理解配置结构的关键是搞清楚继承链。官方提供的配置文件放在configs/目录下按任务和模型分文件夹比如COCO-Detection/faster_rcnn_R_50_FPN_1x.yaml。一份典型的自定义配置长这样_BASE_: ../Base-RCNN-FPN.yaml MODEL: WEIGHTS: detectron2://ImageNetPretrained/MSRA/R-50.pkl MASK_ON: False ROI_HEADS: NUM_CLASSES: 5 DATASETS: TRAIN: (my_train,) TEST: (my_val,) SOLVER: BASE_LR: 0.001 MAX_ITER: 20000 STEPS: (14000, 18000)这里_BASE_指向基础配置MODEL.ROI_HEADS.NUM_CLASSES改成你的类别数DATASETS填注册好的数据集名字SOLVER控制训练轮次和学习率。注意STEPS是学习率衰减的迭代点一般设在总迭代数的 70% 和 90% 左右。3.3 数据集注册格式转换是重头戏Detectron2 支持 COCO 格式的数据集如果你的数据是 VOC 格式或者其他格式需要先转成 COCO 的 JSON 标注。注册数据集的代码大致如下from detectron2.data.datasets import register_coco_instances register_coco_instances( my_train, {}, annotations/train.json, images/train ) register_coco_instances( my_val, {}, annotations/val.json, images/val )这里有个容易忽略的点COCO 标注里的category_id必须从 1 开始连续编号不能有跳号。我有一次数据里类别 id 是 1、3、5结果训练时类别数对不上报了个很隐晦的索引错误排查了很久才发现是标注问题。另外图片路径和标注里的file_name要能对上否则会报找不到文件的错误。3.4 数据增强配置别小看这几个开关INPUT字段下控制数据增强常用的有MIN_SIZE_TRAIN、MAX_SIZE_TRAIN、RANDOM_FLIP、CROP等。这些参数直接影响模型的泛化能力。比如MIN_SIZE_TRAIN控制训练时图片短边缩放的尺寸默认是(640, 672, 704, 736, 768, 800)也就是多尺度训练。如果你的显存有限可以固定成一个值比如(800,)减少显存波动。RANDOM_FLIP默认是horizontal做水平翻转增强。对于大多数自然图像任务这是有益的但如果你的任务里左右方向有语义区别比如文字识别、某些工业检测就要关掉。我在一个字符检测任务里忘了关翻转结果模型对左右对称的字符识别率明显下降后来关掉翻转才恢复正常。4. 完整实操流程从数据到推理4.1 数据准备与格式转换实操假设你手上是一批标注好的图片标注格式是 VOC 的 XML。第一步是转成 COCO JSON。转换脚本的核心逻辑是遍历 XML提取bndbox的坐标和类别名按 COCO 的结构组织成images、annotations、categories三个列表。这里要注意坐标格式VOC 是xmin, ymin, xmax, ymaxCOCO 的bbox是[x, y, width, height]需要做一次转换width xmax - xminheight ymax - ymin。转换完成后建议写个小脚本统计一下每个类别的样本数。我遇到过类别极度不均衡的情况某个类别只有十几张图训练出来这个类别的 AP 几乎为零。这种时候要么补数据要么在损失函数里加类别权重要么用重采样策略。4.2 训练配置与启动配置写好之后训练启动的代码很简洁from detectron2.engine import DefaultTrainer from detectron2.config import get_cfg cfg get_cfg() cfg.merge_from_file(my_config.yaml) cfg.OUTPUT_DIR ./output cfg.freeze() trainer DefaultTrainer(cfg) trainer.resume_or_load(resumeFalse) trainer.train()DefaultTrainer会自动处理模型构建、优化器、学习率调度、检查点保存和评估。如果你想自定义训练循环可以继承DefaultTrainer并重写build_hooks方法挂载自己的 Hook。训练过程中要盯几个指标total_loss、loss_cls、loss_box_reg、loss_rpn_cls、loss_rpn_loc。正常情况下这些 loss 应该稳步下降如果某个 loss 一直不降或者震荡剧烈说明对应的模块可能有问题。比如loss_rpn_cls不降可能是 anchor 设置和你的目标尺寸不匹配。4.3 学习率与迭代次数的选择学习率和迭代次数是训练里最关键的两个超参。Detectron2 默认的BASE_LR是 0.001对应 batch size 16如果你改了 batch size学习率要按比例调整。经验公式是lr base_lr * (batch_size / 16)。比如 batch size 改成 8学习率就设成 0.0005。迭代次数取决于数据集大小和任务难度。一个粗略的参考是小数据集几千张图跑 1 万到 2 万次迭代中等数据集几万张跑 3 万到 5 万次。判断是否训练充分看验证集指标是否还在提升如果连续几千次迭代指标都不动了基本可以停了。STEPS一般设在总迭代数的 70% 和 90%让学习率在后期衰减帮助模型收敛到更优的解。4.4 推理与结果可视化训练完成后用DefaultPredictor做推理from detectron2.engine import DefaultPredictor predictor DefaultPredictor(cfg) outputs predictor(image) instances outputs[instances] boxes instances.pred_boxes.tensor.cpu().numpy() scores instances.scores.cpu().numpy() classes instances.pred_classes.cpu().numpy()outputs[instances]里包含预测框、分数、类别如果是分割任务还有 mask。可视化可以用Visualizer类把框和类别画到图上。这里有个细节DefaultPredictor会读取配置里的TEST字段包括测试时的图片尺寸和分数阈值。如果推理结果框太多或太少调MODEL.ROI_HEADS.SCORE_THRESH_TEST这个阈值默认是 0.05实际用的时候一般调到 0.5 左右比较合适。5. 常见问题与排查技巧实录5.1 训练不收敛的排查思路训练不收敛是最常见也最让人头疼的问题。我的排查顺序是这样的先看数据确认标注格式正确、类别 id 连续、图片路径能对上再看学习率太大导致震荡太小导致下降缓慢然后看 anchor 设置如果目标尺寸和 anchor 尺寸差距太大RPN 很难产生正样本最后看预训练权重MODEL.WEIGHTS是否加载成功。有一次我遇到 loss 一直卡在 2.0 左右不降排查了半天发现是NUM_CLASSES设错了比实际类别数多了一个导致分类头多了一个永远没有正样本的类别梯度被稀释了。这种问题很隐蔽因为不会报错只是效果差。5.2 显存不足的应对策略显存不足在训练大模型或大分辨率图片时很常见。应对策略有几个降低IMS_PER_BATCH每批图片数降低MIN_SIZE_TRAIN输入尺寸开启梯度累积用多个小 batch 模拟大 batch或者用混合精度训练。Detectron2 支持 AMP自动混合精度在配置里设SOLVER.AMP.ENABLED: True就能开启能省不少显存。我实测下来把IMS_PER_BATCH从 4 降到 2同时把学习率减半效果基本能保持显存占用能降 40% 左右。如果还是不够再降输入尺寸但要注意尺寸降太多会影响小目标的检测效果。5.3 评估指标异常的分析评估指标异常通常表现为 AP 很低或者某个类别 AP 为零。如果整体 AP 低先检查数据质量和标注准确性如果某个类别 AP 为零大概率是这个类别样本太少或者标注有问题。还有一种情况是评估时类别 id 对不上比如训练时类别 id 是 0-4评估时按 1-5 去匹配结果全错。Detectron2 的COCOEvaluator会输出每个类别的 AP建议训练时挂上这个评估器定期看各类别指标。如果发现某个类别特别差可以针对性地补数据或者调整该类别的采样权重。5.4 常见问题速查表问题现象可能原因排查方向编译报错 CUDA 相关CUDA 版本不匹配检查 torch 和系统 CUDA 版本训练 loss 不降学习率不当或数据问题先查数据标注再调学习率显存不足batch 或尺寸太大降 batch、开 AMP、降尺寸某类别 AP 为零样本太少或标注错误统计类别分布检查标注推理框太多分数阈值太低调高 SCORE_THRESH_TEST找不到数据集注册名字不匹配检查配置和注册代码的名字5.5 几个容易被忽略的实操心得第一个心得是关于预训练权重的。Detectron2 的MODEL.WEIGHTS支持从 URL 加载但国内网络下载官方权重经常超时。我的做法是提前把权重文件下载到本地配置里直接写本地路径避免训练时卡在下载上。第二个心得是关于日志的。DefaultTrainer默认每 20 次迭代打一次日志如果迭代很快日志会刷屏。可以在配置里调SOLVER.LOG_PERIOD或者把日志重定向到文件训练完再分析。我习惯把日志存下来用脚本画 loss 曲线比盯着终端看直观得多。第三个心得是关于检查点的。Detectron2 默认只保存最新的和最好的检查点但做实验时最好把每个阶段的检查点都留着方便回溯。可以调SOLVER.CHECKPOINT_PERIOD控制保存频率或者写个 Hook 在特定迭代点保存。第四个心得是关于多卡训练的。Detectron2 支持DistributedDataParallel但多卡训练时学习率要按卡数放大IMS_PER_BATCH是每张卡的 batch size总 batch 是卡数乘以这个值。多卡训练还有个坑是评估只在主卡做如果评估代码里有随机性结果可能不一致建议固定随机种子。6. 自定义扩展与进阶方向6.1 自定义数据集加载器虽然register_coco_instances能覆盖大部分场景但有些特殊数据格式需要自定义加载器。比如你的标注是点标注或者多边形需要转成 Detectron2 的Instances格式。核心是实现一个函数返回一个字典列表每个字典包含image、instances、file_name等字段然后用DatasetCatalog.register注册。自定义加载器的好处是可以在加载时做数据清洗和过滤比如过滤掉面积过小的目标、修正越界的坐标。我在一个项目里就加了坐标裁剪逻辑把超出图片边界的框裁到边界内避免了训练时的坐标越界错误。6.2 自定义模型组件Detectron2 的模型是模块化组装的想换 backbone 或者加模块通过注册机制实现。比如换一个 backbone写个函数返回新的 backbone 网络用BACKBONE_REGISTRY.register()注册配置里改MODEL.BACKBONE.NAME就行。想加一个注意力模块可以在 ROI heads 里插入或者直接改 backbone 的输出。这种扩展方式的好处是不侵入框架源码升级版本时不会冲突。但要注意自定义组件的输入输出维度必须和框架约定的一致否则会在拼接时出错。建议先看官方对应组件的实现照着接口写。6.3 模型导出与部署考量训练好的模型如果要部署需要导出成推理友好的格式。Detectron2 支持导出成 TorchScript也可以用 ONNX 导出。TorchScript 导出相对简单torch.jit.script或者torch.jit.trace都行但要注意模型里的动态控制流可能不被支持。ONNX 导出需要装onnx和onnxruntime导出后可以用 ONNX Runtime 推理速度通常比原生 PyTorch 快一些。部署时还要考虑预处理和后处理的移植。Detectron2 的预处理包括 resize、归一化、通道转换后处理包括 NMS、分数过滤。这些逻辑在导出时要么固化进模型要么在部署端用其他语言重新实现。我一般倾向于把预处理固化进模型后处理在部署端实现这样灵活性更好。7. 一些个人体会用 Detectron2 这几年最大的感受是它把配置和代码的边界划得很清楚这在一开始会让人觉得繁琐但项目做大之后这种清晰的分层能省很多事。我见过太多项目因为配置散落在代码各处最后没人敢改Detectron2 这套设计至少避免了这个问题。另一个体会是不要一上来就调模型结构先把数据和配置调对。我踩过的坑里八成以上是数据问题或者配置写错真正需要改模型结构的场景很少。所以遇到效果不好先回头查数据和配置比盲目换模型有效得多。最后分享一个小技巧Detectron2 的配置可以用cfg.dump()打印出完整的合并后配置调参时把这个打印出来能清楚看到每个字段最终生效的值避免继承链里某层覆盖了你以为的设置。这个习惯帮我省了不少排查时间。