Detectron2 公共 LazyConfig 组件库完全指南:以 configs/common 复用模型、数据加载器、学习率调度器与优化器
【免费下载链接】detectron2Detectron2 is a platform for object detection, segmentation and other visual recognition tasks.项目地址: https://gitcode.com/GitHub_Trending/de/detectron2
Detectron2 的configs/common/目录沉淀了一套基于 LazyConfig(懒实例化)体系封装的"公共积木":常用模型结构、COCO 数据加载器、多阶段学习率调度器与优化器,以及配套的训练流程约定。本文围绕这份官方 README 展开,结合底层LazyConfig.load/instantiate实现与多个真实配置文件,说明这套公共组件的设计动机、每个组件的字段含义,以及如何在自有训练配置中 import 后按需覆盖,快速拼出 Mask R-CNN、Cascade R-CNN、RetinaNet、FCOS、Keypoint R-CNN 等训练任务。
一、目录定位:给"可编辑后再构造"的对象提供一个仓库级公共命名空间
configs/common/README.md用三句话阐明了该目录的核心价值:
- 它集中定义了训练中经常被复用的模型(models)、数据加载器(dataloaders)、调度器(scheduler)与优化器(optimizers);
- 这些对象全部以lazy instantiation(懒实例化)的形式描述——即用户可以在真正构造对象之前,先用 Python 语句自由修改其参数;
- 它们既可以被其他配置文件通过**相对导入(import)**引用,也可以通过
model_zoo.get_configAPI加载。
换句话说,configs/common/不是一个独立可运行的任务配置,而是一套"官方预置的部件仓库",真正的任务配置文件(如configs/COCO-InstanceSegmentation/mask_rcnn_R_50_FPN_1x.py、configs/new_baselines/*_LSJ.py)都从这里取料并做增量覆盖。整个目录由四类文件组成:
| 类别 | 文件(相对仓库根目录) | 对外导出的对象 |
|---|---|---|
| 数据 | data/coco.py | dataloader(含dataloader.train/dataloader.test/dataloader.evaluator) |
| 数据常量 | data/constants.py | constants(ImageNet 像素均值/标准差) |
| 模型 | models/mask_rcnn_fpn.py、models/retinanet.py | model |
| 调度器 | coco_schedule.py | lr_multiplier_1x / _2x / _3x / _6x / _9x |
| 优化器 | optim.py | SGD、AdamW |
| 训练约定 | train.py | train(纯字典,非懒调用) |
| 复合模型 | models/cascade_rcnn.py、models/fcos.py、models/keypoint_rcnn_fpn.py、models/mask_rcnn_c4.py、models/mask_rcnn_vitdet.py、models/panoptic_fpn.py | 基于基底模型二次改造得到的model |
二、底层机制:LazyCall 与递归实例化(instantiate)
要理解configs/common/,必须先理解它所谓的"lazy instantiation"具体指什么。官方教程 lazyconfigs.md 给出了完整背景。
2.1 字典描述一次"调用"
懒实例化的核心思想是:用一个字典来描述"调用某个类/函数并传入若干参数"这件事,而不立即执行调用。字典包含两类键:
_target_:可调用对象的路径字符串,例如detectron2.modeling.meta_arch.GeneralizedRCNN;- 其余键:要传给该可调用对象的参数,参数本身也可以递归地用同构字典描述。
辅助函数LazyCall(定义于 detectron2/config/lazy.py,LazyCall.__call__会把_target_注入并返回一个带allow_objects=True标志的omegaconf.DictConfig)可以帮我们生成这类字典。例如 optim.py 中:
from detectron2.config import LazyCall as L SGD = L(torch.optim.SGD)( params=L(get_default_optimizer_params)(weight_decay_norm=0.0), lr=0.02, momentum=0.9, weight_decay=1e-4, )L(torch.optim.SGD)(...)并不真正创建优化器,而是生成一个形如{"_target_": "torch.optim.SGD", "lr": 0.02, ...}的配置字典。这些字典被写入模块全局变量后,LazyConfig.load会收集模块顶层所有"字典值"并把它们统一转成omegaconf.DictConfig(参见 lazy.py 中LazyConfig.load对返回值的过滤逻辑),从而获得 omegaconf 的属性访问语法与变量插值(interpolation)能力。
2.2 instantiate:递归地把配置变回对象
真正执行构造的是 detectron2/config/instantiate.py 中的instantiate(cfg):它递归遍历配置树,遇到含_target_的节点就先实例化所有子参数、再locate出目标类并执行cls(**cfg)。因此:
optim = instantiate(cfg.optimizer) # 真正创建 torch.optim.SGD model = instantiate(cfg.model) # 真正创建 GeneralizedRCNN train_loader = instantiate(cfg.dataloader.train)这套机制的价值在于:配置在"加载——覆盖——实例化"三个阶段彻底解耦,用户可以先 import 公共组件、用普通 Python 代码任意改字段,再一次性实例化,这正是 lazyconfigs.md 中强调的"dict 可编辑性"优于传统 YAML 的地方。
三、公共数据配置(configs/common/data/)
3.1 coco.py:train / test 加载器与评估器的"三件套"
data/coco.py 是最常用的公共组件,它在顶层导出一个用OmegaConf.create()创建的空配置dataloader,随后挂上三个子键:
训练加载器dataloader.train:由build_detection_train_loader描述,包含
dataset:get_detection_dataset_dicts(names="coco_2017_train");mapper:DatasetMapper(is_train=True),其augmentations列表使用了经典的多尺度短边抖动:ResizeShortestEdge(short_edge_length=(640, 672, 704, 736, 768, 800), sample_style="choice", max_size=1333),配合RandomFlip(horizontal=True);image_format="BGR"、use_instance_mask=True;total_batch_size=16、num_workers=4。注意这里的 16 是整个训练的全局 batch size(与后方调度器以 batch 16 为基准计算迭代数呼应)。
测试加载器dataloader.test:build_detection_test_loader+DatasetMapper(is_train=False),测试仅做单尺度ResizeShortestEdge(short_edge_length=800, max_size=1333),且图像格式通过 omegaconf 插值引用训练 mapper:image_format="${...train.mapper.image_format}"——即"父配置的 train 分支下的 mapper 的 image_format",避免重复定义。
评估器dataloader.evaluator:COCOEvaluator,其数据集名同样用插值${..test.dataset.names}从测试加载器同步,保证评估数据与测试数据一致。
这种"插值引用而非复制值"的写法是公共组件的关键设计,让后续覆盖test.dataset.names时评估器自动跟随。
3.2 constants.py:像素归一化常量
data/constants.py 导出constants字典,集中存放 ImageNet 预训练权重所需的像素统计量,共四组:
imagenet_rgb256_mean/imagenet_rgb256_std(标准 RGB 顺序,std 为真实 ImageNet 标准差[58.395, 57.12, 57.375]);imagenet_bgr256_mean/imagenet_bgr256_std(BGR 顺序,供input_format="BGR"的骨干使用)。注释特别说明:使用 Detectron1 或 MSRA 系列预训练模型时,std 已被吸收进 conv1 权重,必须置为[1.0, 1.0, 1.0],因此这里的 BGR std 默认取 1;若自训练从头开始,可换回真实 ImageNet 标准差[57.375, 57.120, 58.395]。
这些常量随后被 models/mask_rcnn_fpn.py 与 models/retinanet.py 通过from ..data.constants import constants引用并填充模型的pixel_mean/pixel_std/input_format字段。
四、公共模型定义(configs/common/models/)
4.1 基石:mask_rcnn_fpn.py 组装了一条完整的 Mask R-CNN 流水线
models/mask_rcnn_fpn.py 是整套模型的"地基",用不到 100 行字典嵌套完整描述了 Detectron2 标准 Mask R-CNN + FPN(R-50)结构,顶层model为L(GeneralizedRCNN),内部自底向上分层:
- backbone:
FPN,其bottom_up是ResNet——stem 用BasicStem(in_channels=3, out_channels=64, norm="FrozenBN"),主干阶段由ResNet.make_default_stages(depth=50, stride_in_1x1=True, norm="FrozenBN")生成,out_features=["res2","res3","res4","res5"];FPN 的in_features用插值${.bottom_up.out_features}复用同一列表,top_block=LastLevelMaxPool负责在 P5 之上追加 P6; - proposal_generator:
RPN+StandardRPNHead(in_channels=256, num_anchors=3)+DefaultAnchorGenerator(5 个金字塔层,anchor sizes[32,64,128,256,512],纵横比[0.5,1.0,2.0],strides[4,8,16,32,64]);Matcher(thresholds=[0.3, 0.7], labels=[0, -1, 1], allow_low_quality_matches=True)区分背景/忽略/前景;每图 256 个 proposal 样本,pre_nms_topk=(2000, 1000)、post_nms_topk=(1000, 1000)、nms_thresh=0.7; - roi_heads:
StandardROIHeads(num_classes=80, ...),内部box_in_features=["p2","p3","p4","p5"]+ROIPooler(output_size=7, ..., pooler_type="ROIAlignV2")+FastRCNNConvFCHead(无卷积、两层 1024 维 FC)+FastRCNNOutputLayers(test_score_thresh=0.05, box2box_transform=Box2BoxTransform(weights=(10,10,5,5)));分割分支mask_in_features同四层、mask_pooler输出 14×14、mask_head=MaskRCNNConvUpsampleHead(5 层 256 通道卷积)。两个 head 的num_classes均通过${..num_classes}插值跟随 ROIHeads 顶层设置; - 输入归一化:
pixel_mean/pixel_std取自constants.imagenet_bgr256_mean / _std,input_format="BGR"。
注意RPN、StandardROIHeads的batch_size_per_image、positive_fraction等采样超参全部显式列出,便于后续配置直接点改。这份完整定义也被官方教程 lazyconfigs.md 以 "A Full Mask R-CNN described in recursive instantiation" 的折叠块直接引用,作为递归实例化的教学样例。
4.2 派生模型:在基底上做"增删改"
公共目录真正的威力在于派生:其他模型文件以mask_rcnn_fpn.model为基底,用纯 Python 对配置树做操作即可变换模型族。
- Cascade R-CNN(models/cascade_rcnn.py):先
[model.roi_heads.pop(k) for k in ["box_head", "box_predictor", "proposal_matcher"]]移除单级 box 部件,再用model.roi_heads.update(_target_=CascadeROIHeads, ...)替换目标类,并分别用列表推导构造 3 个级联的FastRCNNConvFCHead、3 个FastRCNNOutputLayers(回归权重依次(10,5)/(20,10)/(30,15))与 3 个Matcher(IoU 阈值 0.5/0.6/0.7)。 - Keypoint R-CNN(models/keypoint_rcnn_fpn.py):pop 掉 mask 分支三个键后
update为 keypoint 分支(KRCNNConvDeconvUpsampleHead,17 个关键点、8 层 512 通道卷积、loss_normalizer="visible"),并把 RPN 的post_nms_topk提到(1500, 1000)——注释解释 Detectron2 中该参数按图计算,1000 会损伤 box AP;同时smooth_l1_beta=0.5改善关键点 AP。 - RetinaNet(models/retinanet.py):独立从零定义
L(RetinaNet):ResNet 只保留res3..res5,FPN top 换成LastLevelP6P7,5 层特征图上接RetinaNetHead(每个空间位置 9 个 anchor,prior_prob=0.01初始化前景概率,focal lossalpha=0.25、gamma=2.0)。 - FCOS(models/fcos.py):直接 import RetinaNet 的
model后做减法和替换——model._target_ = FCOS,del掉 anchor 相关键,top_block 改从 P5 上采样出 P6/P7(对应论文 Sec 2.2),阈值换为基于sqrt(cls_score * centerness)的 0.2/0.6,head 换成FCOSHead(norm="GN")。
每一层改造都在 import 处即时生效,且不需要理解 YAML 继承的覆盖规则,这正是公共组件选择 Python 配置的根本原因。
五、调度器与优化器
5.1 coco_schedule.py:1x/3x 等"论文调度"的工厂函数
coco_schedule.py 把论文中常见的 "1x / 3x" 等训练协议实现为一个带参函数default_X_scheduler(num_X),并在模块末尾一次性导出lr_multiplier_1x / _2x / _3x / _6x / _9x五个成品。核心逻辑:
- "1x" 以 batch size 16 下的90,000 次迭代(对应约 1,440,000 张训练图像、约 12 个 COCO epoch)为基准,故总迭代数
total_steps_16bs = num_X * 90000; - LR 按三段阶梯衰减:
MultiStepParamScheduler(values=[1.0, 0.1, 0.01])。当num_X <= 2时直接用固定里程碑[60000, 80000, 90000](注释指出该调度对迭代数具有尺度不变性,等价于 YAML 配置里的milestones=[6, 8, 9]epoch);num_X > 2时则改为[total_steps_16bs - 60000, total_steps_16bs - 20000, total_steps_16bs]——即把最后两次衰减固定在距训练结束 60k/20k 迭代处,保证尾段衰减节奏不随总长度漂移,这也是 "Rethinking ImageNet Pre-training"(Sec 4)所提倡的策略; - 最外层套
WarmupParamScheduler:线性 warmup,长度1000 / total_steps_16bs(恒为 1000 次迭代的相对比例),warmup_factor=0.001。
也就是说"1x"本身包含 1000 步线性预热与两次阶梯式降 LR;任务配置只需from ..common.coco_schedule import lr_multiplier_1x即可复用完整协议。
5.2 optim.py:内置 SGD 与 AdamW 两套出厂优化器
optim.py 提供SGD与AdamW两个成品:
SGD:lr=0.02、momentum=0.9、weight_decay=1e-4(ResNet 系标准的 1x 学习率与正则);AdamW:lr=1e-4、betas=(0.9, 0.999)、weight_decay=0.1(适合 ViT / MViT 系主干)。
两者的params都由L(get_default_optimizer_params)(weight_decay_norm=0.0)描述。注释点明关键约定:params.model留待真正实例化优化器前,由训练脚本把已构造的 model 对象填进去。该脚本即 tools/lazyconfig_train_net.py,其do_train中顺序执行:
model = instantiate(cfg.model) # 1. 先构造模型 cfg.optimizer.params.model = model # 2. 把模型塞进优化器配置 optim = instantiate(cfg.optimizer) # 3. 再构造优化器,以获取参数分组get_default_optimizer_params定义于 detectron2/solver/build.py,负责按模块名把参数分成"含 norm 的"(weight_decay_norm=0.0,即 norm 层不做权重衰减)等若干组,从而保证weight_decay_norm这类参数能在公共文件里被预置、而在实例化时针对真实模型结构生效。
六、train.py:训练入口约定的"最小公共契约"
train.py 导出的train是一个普通 Python 字典(刻意不用 LazyCall,因为它不是某个类的参数,而是训练脚本直接读取的字段集)。文件头部注释声明它是为tools/lazyconfig_train_net.py设计的通用约定,使用者完全可以定义自己的结构并配套自己的 train_net.py。字段含:
| 字段 | 默认值 | 说明 |
|---|---|---|
output_dir | "./output" | 输出目录 |
init_checkpoint | "" | 初始权重(可指向 detectron2:// 托管的 ImageNet 预训练) |
max_iter | 90000 | 训练总迭代数 |
amp.enabled | False | 是否启用自动混合精度(决定用AMPTrainer还是SimpleTrainer) |
ddp | 见源码 | 传给create_ddp_model的参数(broadcast_buffers、find_unused_parameters、fp16_compression) |
checkpointer | period=5000, max_to_keep=100 | PeriodicCheckpointer参数 |
eval_period/log_period | 5000/20 | 评估与日志周期(迭代) |
device | "cuda" | 训练设备 |
对照 tools/lazyconfig_train_net.py 的 docstring 与do_train实现可见,该脚本对配置对象的期望恰好是:cfg.model、cfg.dataloader.{train,test,evaluator}、cfg.optimizer、cfg.lr_multiplier与cfg.train,与configs/common/各文件导出的对象一一对应——common目录其实是该训练脚本的"官方配套零件包"。
七、实战:三种使用姿势与完整命令
7.1 姿势一:import + 少量覆盖(官方任务配置的标准写法)
configs/COCO-InstanceSegmentation/mask_rcnn_R_50_FPN_1x.py全文件仅 5 行业务代码,其余全是 import:
from ..common.optim import SGD as optimizer from ..common.coco_schedule import lr_multiplier_1x as lr_multiplier from ..common.data.coco import dataloader from ..common.models.mask_rcnn_fpn import model from ..common.train import train model.backbone.bottom_up.freeze_at = 2 # 冻结 res2 之前的层 train.init_checkpoint = "detectron2://ImageNetPretrained/MSRA/R-50.pkl" # 使用 ImageNet 预训练同目录下mask_rcnn_R_50_C4_1x.py、mask_rcnn_R_50_FPN_3x.py,以及configs/COCO-Detection/fcos_R_50_FPN_1x.py、configs/COCO-Keypoints/keypoint_rcnn_R_50_FPN_1x.py、configs/COCO-PanopticSegmentation/panoptic_fpn_R_50_1x.py、configs/Misc/cascade_mask_rcnn_R_50_FPN_3x.yaml对应的cascade_mask_rcnn_R_50_FPN_1x/3x系列均遵循此模式。要点:公共配置通过相对导入路径..common.xxx获取,Detectron2 的LazyConfig在加载时会重写导入逻辑(见 lazy.py 的_patch_import:相对导入只允许加载同仓库的其他配置 .py 文件,不要求目录有__init__.py),保证任何位置的配置都能按相对位置解析文件。
7.2 姿势二:深度改造(以 new_baselines 的 LSJ 大尺度抖动方案为例)
configs/new_baselines/mask_rcnn_R_50_FPN_100ep_LSJ.py 展示了在同一套公共组件上做"换骨干训练范式"级别的改造:
- 重写数据增强为 LSJ:
dataloader.train.mapper.augmentations = [ResizeScale(min_scale=0.1, max_scale=2.0, ...), FixedSizeCrop(crop_size=(1024, 1024)), RandomFlip],并recompute_boxes=True(裁剪需重算框); - 全程 SyncBN:一次性把 stem、stages、FPN 的 norm 全部置
"SyncBN",box/mask head 换NaiveSyncBatchNorm; - 换 4conv1fc box head(
conv_dims=[256,256,256,256]+fc_dims=[1024])、2conv RPN head、从头训练freeze_at=0; - 放大 batch:
dataloader.train.total_batch_size = 64,train.max_iter = 184375(100 epoch),train.amp.enabled=True、train.ddp.fp16_compression=True; - 重写调度器
lr_multiplier与优化器超参optimizer.lr=0.1、weight_decay=4e-5。
这些改动全程使用"点路径赋值"与 Python 推导式,任何一处都能精准落到公共组件定义的具体字段上——这正是懒配置相比传统 YAML 无法做到、却是公共组件被大量项目采用的原因。
7.3 姿势三:通过 model_zoo.get_config 以编程方式加载
官方教程 lazyconfigs.md 明确说明这些 lazy 配置在安装后被视作模型库的一部分,可由 model_zoo.get_config 加载(其get_config对.py配置会走LazyConfig.load分支;若传入trained=True,还会把官方权重地址写入cfg.train.init_checkpoint,前提是配置中含train字段——这正说明为什么 common 的train.py是模型库 lazy 配置的共同基础)。典型用法:
from detectron2 import model_zoo cfg = model_zoo.get_config( "COCO-InstanceSegmentation/mask_rcnn_R_50_FPN_1x.py", trained=True ) cfg.train.output_dir = "./my_output" cfg.train.max_iter = 90000之后即可用instantiate(cfg.model)等构建对象,或直接python tools/lazyconfig_train_net.py --config-file <路径>启动训练。
7.4 运行:训练 / 评估 / 命令行覆盖
针对任意基于 common 组件的 lazy 配置,训练入口统一为 tools/lazyconfig_train_net.py:
# 单机多卡训练(使用公共组件组合出的 Mask R-CNN 1x 配置) python tools/lazyconfig_train_net.py \ --config-file configs/COCO-InstanceSegmentation/mask_rcnn_R_50_FPN_1x.py \ --num-gpus 8 # 仅评估:加载配置中 train.init_checkpoint 指定的权重 python tools/lazyconfig_train_net.py \ --config-file configs/new_baselines/mask_rcnn_R_50_FPN_100ep_LSJ.py \ --eval-only \ --num-gpus 8 # 用 --opts 做命令行覆盖(等价于 Python 内调用 LazyConfig.apply_overrides) python tools/lazyconfig_train_net.py \ --config-file configs/COCO-InstanceSegmentation/mask_rcnn_R_50_FPN_1x.py \ --num-gpus 1 \ train.max_iter=1000 dataloader.train.total_batch_size=8 \ train.eval_period=500其中--opts的a.b=c语法由 lazy.py 的LazyConfig.apply_overrides支持(优先借助 Hydra 的覆盖语法解析,缺失时回退到ast.literal_eval简单解析);脚本在main中会依次执行LazyConfig.load、apply_overrides,并在评估模式下直接instantiate(cfg.model)后调用do_test。
八、最佳实践与注意事项
- 公共组件是"基底"而非"成品":像
mask_rcnn_fpn.model这样的基底对象被多个配置文件 import 后各自独立覆盖,互不影响,因为每个配置 .py 在被LazyConfig.load时都会在新命名空间重新执行(见 lazy.py 中_patch_import的"不缓存模块、全局修改无副作用"设计)。 - 优先用插值而非复制:
data/coco.py中${...train.mapper.image_format}、${..test.dataset.names}等 omegaconf 插值保证了派生配置改一处、相关字段同步,应沿用此习惯。 - 增加新组件只需遵守契约:若给
common/增加新的公共模型或数据配置,保证导出model/dataloader/train等约定键名,即可无缝接入lazyconfig_train_net.py与model_zoo.get_config。 - 阅读延伸:完整设计理念见 lazyconfigs.md;
configs/common/README.md同时被官方教程引用为 "common baselines" 的示例入口;new_baselines/目录下的全部*_LSJ.py与各任务目录下的.py配置都是"common 组件 × 任务差异"的现成范本,可对照 configs 逐一阅读。
【免费下载链接】detectron2Detectron2 is a platform for object detection, segmentation and other visual recognition tasks.项目地址: https://gitcode.com/GitHub_Trending/de/detectron2
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考