PaddleDetection RetinaNet 实战指南:Focal Loss 单阶段密集目标检测的配置、训练与蒸馏
RetinaNet(Focal Loss for Dense Object Detection)是单阶段密集检测范式的代表性模型,通过 Focal Loss 与特征金字塔(FPN)解决了单阶段检测中正负样本极度不均衡的问题,在 PaddleDetection 中以 RetinaNet 架构实现,覆盖 ResNet50/ResNet101 骨干与 1x/2x 训练计划。本指南以仓库 configs/retinanet/README.md 为骨架,结合 _base_ 配置体系与 ppdet/modeling 下的源码实现,完整讲解其模型配置、数据管线、训练评估命令以及基于 FGD 的知识蒸馏变体,读者读完后可以独立复现 COCO 上的 Box AP 结果,并理解每个关键超参(如 octave_base_scale、prior_prob、positive_overlap)背后的原理。
一、RetinaNet 与 Focal Loss:单阶段密集检测为何需要"焦点损失"
RetinaNet 由 Tsung-Yi Lin 等人提出(论文 Focal Loss for Dense Object Detection,ICCV 2017),其核心动机是:单阶段检测器在图像上密集铺排数十万候选框,其中绝大部分是易分类的负样本(背景),这会导致标准交叉熵损失被大量简单负样本主导,模型难以学到困难样本的信息。为此论文提出 Focal Loss,在交叉熵基础上引入调制因子 (1-p_t)^γ,让"易分样本"的损失贡献快速衰减,将训练焦点转移到难分样本上。
PaddleDetection 的 RetinaNet 实现即沿用了论文结构:ResNet + FPN 提供多尺度特征,RetinaHead 在每个金字塔层级上预测分类与回归,并用 Focal Loss(分类)+ SmoothL1 Loss(回归)联合训练。整体结构在配置中一目了然(见 configs/retinanet/base/retinanet_r50_fpn.yml):
architecture: RetinaNet
pretrain_weights: https://paddledet.bj.bcebos.com/models/pretrained/ResNet50_cos_pretrained.pdparams
RetinaNet:
backbone: ResNet
neck: FPN
head: RetinaHead
RetinaNet 这一架构名在 ppdet/modeling/architectures 目录中注册,运行训练/评估时会按此名称组装 backbone(ResNet)、neck(FPN)与 head(RetinaHead)三部分。
二、Model Zoo:COCO 上的性能与权重一览
原文档给出的模型库表格是本文的基线事实,完整继承如下。所有模型均在 COCO train2017 上训练,并在 val2017 上评估,Box AP 指标为 mAP(IoU=0.5:0.95):
| Backbone | Model | imgs/GPU | lr schedule | FPS | Box AP | config |
|---|---|---|---|---|---|---|
| ResNet50-FPN | RetinaNet | 2 | 1x | --- | 37.5 | retinanet_r50_fpn_1x_coco.yml |
| ResNet50-FPN | RetinaNet | 2 | 2x | --- | 39.1 | retinanet_r50_fpn_2x_coco.yml |
| ResNet101-FPN | RetinaNet | 2 | 2x | --- | 40.6 | retinanet_r101_fpn_2x_coco.yml |
| ResNet50-FPN | RetinaNet + FGD | 2 | 2x | --- | 40.8 | retinanet_r50_fpn_2x_coco.yml + retinanet_resnet101_coco_distill.yml |
原文档给出的两点重要说明,一并保留:
- ResNet50-FPN 系列使用 8 张 GPU 在 COCO train2017 上训练;ResNet101-FPN 以及带 FGD 蒸馏的 ResNet50-FPN 使用 4 张 GPU 训练。
- 所有模型均在 val2017 上评估,Box AP 即
mAP(IoU=0.5:0.95)。
阅读表格可以得到的规律:在相同训练计划下,骨干从 ResNet50 升级到 ResNet101(2x),Box AP 从 39.1 提升到 40.6;而"轻骨干 + 知识蒸馏"路线(ResNet50 学 ResNet101 的蒸馏模型)达到了 40.8,超过直接使用 ResNet101,这正是蒸馏的价值所在(详见第六节)。
注:权重下载地址均记录在各配置文件的
weights字段中(如 retinanet_r101_distill_r50_2x_coco.yml 的weights直接指向预训练权重链接),训练时若设置-o weights=...或在配置中声明,框架会自动下载加载,无需手动管理。
三、配置文件体系:五层 _BASE_ 继承结构
PaddleDetection 采用"基础配置 + 增量覆盖"的 _BASE_ 机制。以 retinanet_r50_fpn_1x_coco.yml 为例:
_BASE_: [
'../datasets/coco_detection.yml',
'../runtime.yml',
'_base_/retinanet_r50_fpn.yml',
'_base_/optimizer_1x.yml',
'_base_/retinanet_reader.yml'
]
weights: output/retinanet_r50_fpn_1x_coco/model_final
五层基础配置各司其职,下文逐一拆解。weights 指定训练产出的最终模型存放路径(含前缀 model_final),评估与导出时会自动寻找该路径下的权重文件。
3.1 模型定义层:_base_/retinanet_r50_fpn.yml
这是 RetinaNet 的核心配置,完整内容如下:
architecture: RetinaNet
pretrain_weights: https://paddledet.bj.bcebos.com/models/pretrained/ResNet50_cos_pretrained.pdparams
RetinaNet:
backbone: ResNet
neck: FPN
head: RetinaHead
ResNet:
depth: 50
variant: b
norm_type: bn
freeze_at: 0
return_idx: [1, 2, 3]
num_stages: 4
FPN:
out_channel: 256
spatial_scales: [0.125, 0.0625, 0.03125]
extra_stage: 2
has_extra_convs: true
use_c5: false
RetinaHead:
conv_feat:
name: RetinaFeat
feat_in: 256
feat_out: 256
num_convs: 4
norm_type: null
use_dcn: false
anchor_generator:
name: RetinaAnchorGenerator
octave_base_scale: 4
scales_per_octave: 3
aspect_ratios: [0.5, 1.0, 2.0]
strides: [8.0, 16.0, 32.0, 64.0, 128.0]
bbox_assigner:
name: MaxIoUAssigner
positive_overlap: 0.5
negative_overlap: 0.4
allow_low_quality: true
loss_class:
name: FocalLoss
gamma: 2.0
alpha: 0.25
loss_weight: 1.0
loss_bbox:
name: SmoothL1Loss
beta: 0.0
loss_weight: 1.0
nms:
name: MultiClassNMS
nms_top_k: 1000
keep_top_k: 100
score_threshold: 0.05
nms_threshold: 0.5
关键参数语义与实现依据:
- ResNet:
depth: 50、variant: b为经典 ResNet50-b;return_idx: [1,2,3]表示取出第 2~4 个 stage 的输出送入 FPN;freeze_at: 0表示不冻结任何层(从头开始微调骨干);num_stages: 4为完整四阶段结构。 - FPN:
out_channel: 256统一各层输出通道;spatial_scales: [0.125, 0.0625, 0.03125]对应 P3/P4/P5 相对输入的下采样比例(1/8、1/16、1/32);extra_stage: 2额外生成 P6、P7 两个金字塔层级;use_c5: false表示 P5 由 C5 经 1x1 卷积得到而非直接使用 C5。 - RetinaHead:
RetinaFeat用 4 个 3x3 卷积(num_convs: 4)分别提取分类与回归特征;norm_type: null说明特征卷积不带归一化层(与原始论文一致);use_dcn: false表明当前基线不使用可变形卷积。 - anchor_generator:
octave_base_scale: 4、scales_per_octave: 3、aspect_ratios: [0.5, 1.0, 2.0]构成每个位置 9 个 anchor 的经典设定;strides与 FPN 的 5 个层级(P3~P7)一一对应。 - bbox_assigner:
positive_overlap: 0.5/negative_overlap: 0.4是论文中的 IoU 阈值设定(>0.5 为正样本、<0.4 为负样本、介于两者之间为忽略样本);allow_low_quality: true保证每个 GT 至少匹配一个 anchor,防止 GT 无正样本可学。 - loss_class:Focal Loss 的
gamma: 2.0、alpha: 0.25即论文推荐值;loss_weight: 1.0与回归损失等权。 - nms:推理阶段
MultiClassNMS先在每类内取nms_top_k: 1000个高分框做 NMS(nms_threshold: 0.5),再全局保留keep_top_k: 100个检测结果;score_threshold: 0.05过滤低分框。
3.2 优化器与学习率调度层:optimizer_1x.yml 与 optimizer_2x.yml
1x 计划(12 epoch):
epoch: 12
LearningRate:
base_lr: 0.01
schedulers:
- !PiecewiseDecay
gamma: 0.1
milestones: [8, 11]
- !LinearWarmup
start_factor: 0.001
steps: 500
OptimizerBuilder:
optimizer:
momentum: 0.9
type: Momentum
regularizer:
factor: 0.0001
type: L2
2x 计划(24 epoch)除 epoch: 24 与 milestones: [16, 22] 外其余一致,即把 1x 的分段衰减节点整体翻倍。要点:
- 使用带动量的 SGD(
Momentum,momentum=0.9),基础学习率base_lr: 0.01(对应 8 卡、batch_size 16 的线性缩放); PiecewiseDecay在milestones处将学习率乘以gamma: 0.1(1x 在 8/11 epoch、2x 在 16/22 epoch 各衰减一次);LinearWarmup在前 500 步以start_factor: 0.001从极小学习率线性升温到 0.01,稳定训练初期;- L2 权重衰减系数
0.0001,是检测任务的标准设定。
3.3 数据读取层:_base_/retinanet_reader.yml
训练/评估/测试三套 Reader 共享相同的图像预处理(ImageNet 均值的 NormalizeImage、Permute 转 CHW、PadBatch 按 32 对齐补齐),差异仅在增强策略与 batch 大小:
- TrainReader:
RandomResize从[640, 1333]到[800, 1333]共 6 档短边多尺度随机选择(保持宽高比),配合RandomFlip实现尺度与水平翻转增强;batch_size: 2、shuffle: True、drop_last: True。 - EvalReader:固定
Resize到[800, 1333](与论文评估一致),batch_size: 8,不做随机增强。 - TestReader:与 EvalReader 相同的输入尺寸,
batch_size: 1,便于逐图推理。 worker_num: 2控制数据加载子进程数。
3.4 数据集与运行环境层:coco_detection.yml 与 runtime.yml
configs/datasets/coco_detection.yml 声明 COCO 检测任务的公共信息:
metric: COCO指定使用 COCO 评估协议(即 mAP 系列指标);num_classes: 80与 COCO 类别数一致;TrainDataset使用COCODataSet,数据目录dataset/coco,标注annotations/instances_train2017.json,data_fields声明训练所需字段(图像、GT 框、类别、is_crowd);EvalDataset使用 val2017,allow_empty: true允许空标注图参与评估;TestDataset使用ImageFolder,支持传入图片目录或标注文件做推理输入。
../runtime.yml(configs/runtime.yml)承载运行环境(如 GPU 使用、日志与输出目录等通用设置),四份 RetinaNet 配置均继承之,保证训练/评估行为一致。实际训练时可通过命令行 -o 覆盖,例如切换 GPU 设备或调整 batch size。
四、源码级实现:从配置到前向的完整调用链
配置中的每个 name 字段都会在 ppdet/modeling 中被注册并实例化。核心实现路径如下:
4.1 RetinaHead:分类/回归双分支与先验偏置初始化
实现位于 ppdet/modeling/heads/retina_head.py,RetinaHead 的 __inject__ 声明了 conv_feat、anchor_generator、bbox_assigner、loss_class、loss_bbox、nms 六个可注入组件,与配置一一对应。两个值得注意的实现细节:
- 先验偏置初始化(prior_prob):源码第 73 行
bias_init_value = -math.log((1 - prior_prob) / prior_prob),当prior_prob=0.01时偏置约为 -4.595。这一"把分类 logit 初始化到极小值"的技巧,使得训练初期每个 anchor 预测为前景的概率约为 0.01,避免海量背景 anchor 的梯度在起步阶段就淹没正样本,是 Focal Loss 训练稳定性的关键工程细节。 - 分类与回归分支:
retina_cls输出通道为num_classes * num_anchors(80×9=720),retina_reg输出通道为4 * num_anchors(36),每个金字塔层级共享同一组卷积。前向时对neck_feats逐层产出cls_logits_list与bboxes_reg_list,训练模式走get_loss,推理模式直接输出 logits 供 NMS 后处理。此外,RetinaFeat直接复用FCOSFeat(retina_head.py中class RetinaFeat(FCOSFeat)),说明两个检测头的特征提取卷积结构是共享的。
4.2 RetinaAnchorGenerator:多尺度多比例 anchor 生成
实现位于 ppdet/modeling/proposal_generator/anchor_generator.py(RetinaAnchorGenerator)。octave_base_scale: 4 表示基础尺度为 4,配合 scales_per_octave: 3 在每个 octave 内生成 3 个尺度(4 * 2^(i/3),i=0,1,2),再叠加 aspect_ratios: [0.5, 1.0, 2.0],形成每个位置 9 个 anchor。strides: [8, 16, 32, 64, 128] 与 FPN 的 P3~P7 对齐,保证 anchor 覆盖从 32×32 到 813×813 的广泛尺度范围(论文设定的 anchor 面积从 32² 到 512² 的倍数扩展)。这也解释了为何 FPN 需要 extra_stage: 2 额外生成 P6、P7:没有这两层,128 步长的 anchor 将无从附着。
4.3 FocalLoss 与 MaxIoUAssigner、SmoothL1Loss
- FocalLoss 实现于 ppdet/modeling/losses/focal_loss.py,是对
paddle.nn.functional.sigmoid_focal_loss的封装,gamma=2.0、alpha=0.25直接透传给底层算子,实现"易分负样本降权、难分样本聚焦"的损失计算。 - MaxIoUAssigner 负责 anchor 与 GT 的匹配:
positive_overlap=0.5之上为正样本、negative_overlap=0.4之下为负样本,中间为忽略;allow_low_quality=true时为每个 GT 强制保留 IoU 最高(即使低于 0.5)的 anchor,缓解小目标/稀疏目标的正样本缺失问题。 - SmoothL1Loss 用于回归,
beta: 0.0时等价于 L1 损失,对离群框回归误差更鲁棒(梯度不会随误差增大而无限放大),与论文实现一致。
五、训练、评估与推理:完整命令与流程
以下命令均基于本仓库 tools 目录下的标准入口脚本,配置路径为仓库根目录相对路径。
5.1 训练
单机多卡训练(以 1x 计划为例):
# 默认使用 config 中声明的 GPU 数量与 batch
python -m paddle.distributed.launch --gpus 0,1,2,3,4,5,6,7 tools/train.py \
-c configs/retinanet/retinanet_r50_fpn_1x_coco.yml
单卡调试或小数据验证:
python tools/train.py -c configs/retinanet/retinanet_r50_fpn_1x_coco.yml
如需调整超参,用 -o 覆盖(不修改仓库文件):
python tools/train.py -c configs/retinanet/retinanet_r50_fpn_1x_coco.yml \
-o LearningRate.base_lr=0.005 TrainReader.batch_size=1
5.2 评估
python tools/eval.py -c configs/retinanet/retinanet_r50_fpn_1x_coco.yml \
-o weights=output/retinanet_r50_fpn_1x_coco/model_final.pdparams
-c 指定配置文件(决定网络结构与 Reader),-o weights=... 指定待评估权重;不传 -o weights 时默认读取配置中 weights 字段路径。评估输出 COCO 协议的 mAP(IoU=0.5:0.95)等指标,用于对照 Model Zoo 表格复现。
5.3 单图推理
python tools/infer.py -c configs/retinanet/retinanet_r50_fpn_1x_coco.yml \
-o weights=output/retinanet_r50_fpn_1x_coco/model_final.pdparams \
--infer_img=demo/000000014439.jpg
tools/infer.py 支持 --infer_img(单图)与 --infer_dir(目录批量)两种输入方式,结果可视化输出到 output/ 下。
5.4 换骨干与换训练计划
升级到 ResNet101、2x 计划只需切换配置(该配置已继承同一套 _base_,仅覆盖 ResNet.depth 与预训练权重,见 retinanet_r101_fpn_2x_coco.yml):
python -m paddle.distributed.launch --gpus 0,1,2,3 tools/train.py \
-c configs/retinanet/retinanet_r101_fpn_2x_coco.yml
六、FGD 知识蒸馏变体:轻模型获得重模型精度
Model Zoo 最后一行展示了"RetinaNet(ResNet50)+ FGD 蒸馏"达到 40.8 Box AP 的成果——比直接训练 ResNet101 的 40.6 更高,而推理成本仍是 ResNet50 的轻量级。其配置入口为 retinanet_r101_distill_r50_2x_coco.yml:
_BASE_: [
'../datasets/coco_detection.yml',
'../runtime.yml',
'_base_/retinanet_r50_fpn.yml',
'_base_/optimizer_2x.yml',
'_base_/retinanet_reader.yml'
]
weights: https://paddledet.bj.bcebos.com/models/retinanet_r101_distill_r50_2x_coco.pdparams
该配置以 ResNet50 的学生网络为基础结构,weights 字段直接指向已蒸馏完成的模型权重,可用于开箱即用的评估与推理。训练阶段需要配合蒸馏专用配置 configs/slim/distill/retinanet_resnet101_coco_distill.yml,其蒸馏方法为 FGD(Focal and Global Distillation,焦点与全局蒸馏),通过特征图级的局部焦点对齐与全局关系蒸馏,将 ResNet101 教师网络的知识迁移到 ResNet50 学生网络。蒸馏训练的入口为:
python -m paddle.distributed.launch --gpus 0,1,2,3 tools/train.py \
-c configs/slim/distill/retinanet_resnet101_coco_distill.yml
该路线对于"精度与速度兼得"的落地场景(如边缘设备、实时服务)尤其有价值:训练阶段付出教师网络的计算代价,推理阶段只保留学生网络。完整的蒸馏方法说明见 configs/slim/distill/README.md。
七、实战注意事项与扩展方向
- 显存与 batch 的关系:配置默认
imgs/GPU=2、TrainReaderbatch_size: 2,这是在 8 卡 V100 级别显存下的设定。显存不足时可减小 batch size 并同步按比例调低base_lr(例如 batch 减半则 lr 减半),并用-o覆盖,无需改动仓库文件。 - 预训练权重:ResNet50 使用
ResNet50_cos_pretrained.pdparams(cos 学习率预训练),ResNet101 使用ResNet101_pretrained.pdparams,首次训练时框架会自动下载;离线环境需提前手动放置。 - 多尺度训练与评估不一致:训练采用 640~800 随机短边多尺度,评估固定 800×1333,这是检测任务的标准做法(训练增强、评估对齐论文设置),不要混用。
- 蒸馏模型的落地:蒸馏权重对应 ResNet50 结构,评估/导出时应使用
retinanet_r101_distill_r50_2x_coco.yml(而非教师网络的 r101 配置),保证网络结构与学生权重匹配。 - 进一步扩展:RetinaHead 支持
use_dcn: true切换为可变形卷积版本以提升精度(代价是推理速度);anchor 配置(octave_base_scale、aspect_ratios)可针对小目标或特殊长宽比数据集(如文字检测、遥感检测)进行调整;Focal Loss 的gamma/alpha在正负样本比例极端的数据集上值得重新调优。
结语
RetinaNet 作为单阶段密集检测的经典基线,在 PaddleDetection 中拥有完整、可复现的配置闭环:从 _BASE_ 五层配置继承、RetinaHead 的先验偏置初始化,到 Focal Loss 与 MaxIoUAssigner 的匹配逻辑,再到 FGD 蒸馏的精度提升路线,读者既可开箱复现 Model Zoo 的 COCO 结果,也可基于本指南的源码级解读自由调整骨干、anchor、损失与训练计划,将其迁移到自己的检测数据集上。
引用
@inproceedings{lin2017focal,
title={Focal loss for dense object detection},
author={Lin, Tsung-Yi and Goyal, Priya and Girshick, Ross and He, Kaiming and Doll{\'a}r, Piotr},
booktitle={Proceedings of the IEEE international conference on computer vision},
year={2017}
}
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



