RetinaNet 详解
RetinaNet: Focal Loss for Dense Object Detection
论文: Focal Loss for Dense Object Detection (ICCV 2017, best paper)
作者: Tsung-Yi Lin, Priya Goyal, Ross Girshick, Kaiming He, Piotr Dollár (FAIR)
代码: facebookresearch/Detectron
一句话总结:这篇论文找到了”一阶段检测器精度不如两阶段”的根因–类别极度不平衡导致训练被大量易分负样本主导–并用 Focal Loss 一举解决,使 RetinaNet 成为第一个精度超越两阶段的一阶段检测器。
⚠️ 论文的主角其实是 Focal Loss,RetinaNet 网络本身只是”顺便”提出、用来验证损失函数有效性的载体–结构上并无颠覆性创新(ResNet + FPN + anchor 双头都是已有组件)。
一、背景与动机:一阶段为什么打不过两阶段?
1 | 两阶段 (Faster R-CNN 等): RPN 筛选 -> ~1-2k 候选框 -> 第二阶段精修 |
当时的困惑:一阶段能直接回归出框、速度又快,为什么精度就是追不上两阶段?本文给出不同答案–不是结构不行,是训练被易分负样本主导。
根因:极端的类别不平衡
| 候选框数量 | 正负样本比 | 问题 | |
|---|---|---|---|
| 两阶段 | ~1-2k(RPN 已过滤) | 较均衡 | 训练目标清晰 |
| 一阶段 | ~1:1000 | 负样本远多,且大量是”一眼就能认出的背景” | |
- 两阶段的 RPN 把候选框压到千级,第二阶段还做启发式采样(如前景:背景=1:3)或 OHEM,正负比例可控;
- 一阶段对每个位置都预测,~100k 个位置中正样本寥寥无几,且大部分负样本是易分背景(纯天空、空地)。
易分负样本如何”淹没”训练
标准交叉熵对每个样本一视同仁:即使是 $p=0.9$ 的易分样本,仍贡献 $-\log 0.9 \approx 0.105$ 的损失。当易分负样本数量是正样本的 1000 倍时:
1 | 总损失 = Σ(少量正样本损失) + Σ(海量易分负样本损失) |
这导致两个问题:训练效率低下(易分样本不提供有用学习信号)+ 模型退化(易分负样本压倒训练)。
之前的缓解手段是 OHEM(Hard Negative Mining):按 loss 排序只取最难的负样本。但它粗暴丢弃了所有易分样本,且需额外选择逻辑。本文提出更优雅的 Focal Loss。
二、核心创新:Focal Loss 🔥
2.1 从交叉熵说起
二分类中,定义 $p_t$ 为”模型对真实类别的预测概率”:
$$
p_t = \begin{cases} p & \text{if } y = 1 \ 1-p & \text{if } y = 0 \end{cases}
$$
标准交叉熵:$CE(p_t) = -\log(p_t)$
问题:$p_t \to 1$(易分样本)时 $CE$ 仍不为 0,且易分样本数量巨大,累加后主导总损失。下图中蓝色 CE 曲线变化最平缓–即使 $p_t$ 已很大,损失仍偏高:

2.2 Focal Loss 公式
在交叉熵前加一个调制因子 $(1-p_t)^\gamma$:
$$
\boxed{;FL(p_t) = -(1-p_t)^\gamma \log(p_t);}
$$

调制因子的作用:
1 | p_t -> 1 (易分样本): (1-p_t)^γ -> 0 -> 损失被大幅压低 (几乎不算它了) |
直觉:已经分对的样本就少管它,把训练”焦点”(focal) 集中到难分样本上。 这与 Huber 等鲁棒损失的思路相反–鲁棒损失是降低大错误样本的权重,而 Focal Loss 是降低易分(小错误)样本的权重。
2.3 γ 的作用(focusing parameter)
$\gamma \ge 0$ 控制下压的力度。下表对比 $\gamma=0$(即标准 CE)与 $\gamma=2$ 时的单样本损失:
| $p_t$(预测置信度) | $CE = -\log p_t$ | $(1-p_t)^2$ | $FL_{\gamma=2}$ | 变化 |
|---|---|---|---|---|
| 0.9(易分) | 0.105 | 0.01 | 0.00105 | ↓ ~100 倍 |
| 0.5(临界) | 0.693 | 0.25 | 0.173 | ↓ ~4 倍 |
| 0.1(难分) | 2.303 | 0.81 | 1.866 | ↓ 仅 0.8 倍 |
- $\gamma=0$:退化为标准 CE;
- $\gamma$ 越大,对易分样本压得越狠,但过大会降低整体学习信号;
- 直观地说,调制因子减少了简单样本的损失贡献,并扩大了样本获得低损失的范围:$\gamma=2$ 时 $p_t=0.9$ 的样本损失降 100 倍,而 $p_t=0.5$ 的难样本仅降 4 倍;
- 论文最优 $\gamma = 2$。
2.4 α 平衡因子
类别不平衡的另一维度是正负数量悬殊,可再加权重 $\alpha$:
$$
FL(p_t) = -\alpha_t (1-p_t)^\gamma \log(p_t)
$$
- $\alpha$ 对前景/背景加权,典型 $\alpha = 0.25$(前景);
- ⚠️ $\alpha$ 与 $\gamma$ 会相互影响:加入 $\gamma$ 后最优 $\alpha$ 从 0.5 降到 0.25。两者需联合调参,不能各调各的。论文给出 $\gamma=2,\ \alpha=0.25$ 时 ResNet-101+FPN 最优。
- 注意:前景权重小(0.25)、背景权重大(0.75)看似反直觉,但因为背景数量远多于前景,这样能压低负样本的总损失贡献。
2.5 为什么 Focal Loss 优于 OHEM
| OHEM(Hard Negative Mining) | Focal Loss | |
|---|---|---|
| 做法 | 按 loss 排序,只取 top-K 最难负样本 | 连续加权,所有样本都参与 |
| 易分样本 | 完全丢弃 | 软性下压(仍保留少量信号) |
| 是否可微 | 需额外选择逻辑,非端到端 | 完全可微,端到端 |
| 粒度 | 硬阈值(一刀切) | 连续(按置信度平滑过渡) |
Focal Loss 是 OHEM 思想的”软化、可微”版本:不粗暴丢弃易分样本,而是让它们的贡献随置信度自动衰减。
2.6 难易正负样本的关注排序
Focal Loss 让训练对不同样本的关注度自然形成排序:
1 | 正难 > 负难 > 正易 > 负易 |

即:难分的正样本最受关注,其次是难分负样本,易分样本权重最低(其中易分负样本因数量最大、被压得最狠)。
📌 后续反思(GHM):本文”易分样本对模型提升很小”的假设其实有争议–GHM(Generalized Focal Loss 的相关工作)指出,数量极多的”极易分样本”和”极难分样本”(离群点)都应被抑制,这是对 Focal Loss 的改进方向之一。
三、网络架构
3.1 总览

1 | 输入图像 |
RetinaNet = ResNet + FPN + 两个解耦的预测头。结构本身并不新奇,论文真正的贡献是 Focal Loss 让这套密集预测结构第一次训得准。
详细的网络结构(含 P3-P7 各层 scale 与 ratio)如下:

3.2 Backbone:ResNet
- 使用 ResNet-50 或 ResNet-101,ImageNet 预训练初始化;
- 取 $C_3, C_4, C_5$ 三个 stage 的输出送入 FPN(不用 $C_2$,因为生成 $P_2$ 占用计算资源过多);
- ResNet 的残差结构解决了深层网络的梯度消失/爆炸。两种残差块如下:

左图为 basic block(用于 ResNet-18/34),右图为 bottleneck block(用于 ResNet-50/101/152)。RetinaNet 用 ResNet-50,配置
[3,4,6,3]为各 stage 残差块数量。工程细节:检测网络较大时,Backbone 前面部分通常冻结不训练,BN 也不更新参数(OpenMMLab 经验)。
3.3 Neck:FPN 特征金字塔

- 自下而上 + 自上而下 + 横向连接,构建多尺度特征金字塔;
- 输入 $C_3, C_4, C_5$,输出 $P_3$-$P_7$ 共 5 级,通道数均为 256,stride = (8,16,32,64,128);
- $P_6$ 由 $C_5$ 经 stride=2 的 3×3 卷积得到;$P_7$ 由 $P_6$ 经 ReLU + stride=2 的 3×3 卷积得到(PyTorch 官方实现;原论文 $P_6$ 用 maxpool 下采样);
- 大 stride(小特征图)检测大物体,小 stride(大特征图)检测小物体;$P_6, P_7$ 提供大感受野强语义特征,利于大/超大物体;
- ⚠️ RetinaNet 的 FPN 只含卷积,不含 BN 和 ReLU(除了生成 $P_7$ 时的那个 ReLU)。
FPN 代码(PyTorch,来自社区实现):
1 | class PyramidFeatures(nn.Module): |
注意:FPN 融合用 add(逐元素相加),而非 YOLOv3 的 concat。FPN 输出特征图大小由 Backbone 决定(stride 列表由 Backbone 确定)。
3.4 Head:分类与回归子网(解耦)
RetinaNet 对 5 张特征图各接两个并行、参数独立的子网:

- 分类子网:4×(3×3 conv + ReLU) → 3×3 conv →
K×A通道 → sigmoid(多标签,每类独立); - 回归子网:4×(3×3 conv + ReLU) → 3×3 conv →
4×A通道(预测 4 个偏移); - 两个子网结构相同但参数独立(实验发现共享参数会降精度 ~0.9 mAP);
- 但同一子网在 5 个金字塔层间共享参数;
- $K$ 为类别数(不含背景,COCO 为 80),$A=9$ 为每位置 anchor 数;
- 回归是类别不可知的(class-agnostic):所有类共享同一个框回归器,输出
4×A而非4×K×A。
Head 代码:
1 | class RegressionModel(nn.Module): # 框回归子网 |
3.5 Anchor 设计
每个金字塔层、每个位置预设 9 个 anchor = 3 尺度 × 3 宽高比:
1 | pyramid_levels = [3, 4, 5, 6, 7] |
- 小 anchor 配高分辨率层($P_3$,stride 8,base 32,检测小目标);大 anchor 配低分辨率层($P_7$,stride 128,base 512,检测大目标);
- 最小 scale 约 32(面积 $32^2$),最大接近 813。
特征图 cell 与原图的对应关系:stride=8 的特征图上每个 cell 对应原图 $32\times32$ 区域(人为近似设置,非实际感受野):

一个 cell 的通道维与 anchor 的对应关系(颜色对应 9 个 anchor):

Anchor 平移与铺满特征图:generate_anchors 生成 9 个 base anchor,shift 按固定步长平移铺满整张特征图:

Anchor 生成核心代码:
1 | def generate_anchors(base_size=16, ratios=None, scales=None): |
四、训练
4.1 Anchor 标签分配
对每个 anchor,按与 GT 的最大 IoU 分配:

| IoU 范围 | 标签 | 参与损失 |
|---|---|---|
| $\ge 0.5$ | 正样本 | cls + box |
| $< 0.4$ | 负样本 | 仅 cls |
| $[0.4, 0.5)$ | 忽略 | 不参与 |
- 每个 anchor 取与其 IoU 最大的 GT 匹配;
- 此外,每个 GT 至少匹配一个 anchor(保证所有 GT 都被覆盖);
- 这种”中间忽略”区间避免边界 anchor 歧义(与 YOLOv3 的 ignore 思路类似,见 [[YOLOv3 详解]])。
4.2 BBox 编解码(DeltaXYWHBBoxCoder)
为利用 anchor 信息加速收敛、平衡四个回归值的 loss,对 head 输出的 4 个值做编解码(同 SSD/Faster R-CNN 的 match+encode):
编码(GT -> target):
$$
t_x = \frac{g_x - p_x}{p_w},\quad t_y = \frac{g_y - p_y}{p_h},\quad t_w = \log\frac{g_w}{p_w},\quad t_h = \log\frac{g_h}{p_h}
$$
其中 $g$ 为 GT、$p$ 为 anchor(中心坐标与宽高)。$t_{x,y}$ 是中心相对 anchor 中心的偏移(除以 anchor 宽高归一化),$t_{w,h}$ 是宽高比取 log。
解码(预测 -> 框,推理时用):
$$
g_w = p_w e^{t_w},\quad g_h = p_h e^{t_h},\quad g_x = p_x + p_w t_x,\quad g_y = p_y + p_h t_y
$$
实际还叠了 target_means=[0,0,0,0]、target_stds=[1,1,1,1](或代码中的 /[0.1,0.1,0.2,0.2])做标准化。
4.3 损失函数
$$
L = \frac{1}{N_{pos}} \Big( \underbrace{FL_{cls}}{\text{分类(正+负样本)}} + \lambda \cdot \underbrace{SmoothL1{box}}_{\text{回归(仅正样本)}} \Big)
$$
- 分类损失:Focal Loss,对所有 anchor(正+负)计算–正是 FL 让海量负样本不致淹没训练;
- 回归损失:Smooth L1,仅对正样本计算;
- $N_{pos}$:正样本数,用于归一化。
Focal Loss 核心实现(mmdetection 版,最简洁):
1 | pred_sigmoid = pred.sigmoid() |
完整训练损失 forward 流程(社区实现要点):
1 | def forward(self, classifications, regressions, anchors, annotations): |
4.4 初始化的关键 trick:先验偏置
直接随机初始化会出问题:初始时网络对几乎所有 anchor 都预测”有物体”,而实际负样本占绝大多数 -> 第一轮 loss 爆炸,训练崩溃。
解法:分类子网最后一层的 bias 用特殊初始化:
$$
b = -\log!\frac{1-\pi}{\pi}, \quad \pi = 0.01
$$
- 让初始输出前景概率 $\approx 0.01$(很低的”有物体”先验);
- 使初始 loss 降到合理范围,训练稳定启动;
- ⚠️ 这个 trick 对 Focal Loss 训练至关重要,论文专门强调。(代码中
ClassificationModel的prior=0.01即此。)
4.5 优化与推理
1 | 训练: SGD + momentum 0.9, weight decay 1e-4 |
推理时取 top-k 是为控制 NMS 输入规模。Focal Loss 在推理阶段完全不用,只在训练时生效。
五、实验结果
5.1 主结果:一阶段首次超越两阶段
| 模型 | Backbone | mAP (COCO) | FPS | 类型 |
|---|---|---|---|---|
| YOLOv2 | Darknet-19 | 21.6 | 67 | 一阶段 |
| SSD300 | VGG-16 | 25.1 | 46 | 一阶段 |
| SSD512 | VGG-16 | 28.8 | 19 | 一阶段 |
| Faster R-CNN | ResNet-101 | 34.9 | 6 | 两阶段 |
| RetinaNet-50 | ResNet-50 | 36.3 | 12 | 一阶段 |
| RetinaNet-101 | ResNet-101 | 40.4 | 5 | 一阶段 |
RetinaNet-101 以 40.4 mAP 首次让一阶段在精度上超越当时最强的两阶段检测器,同时保持可接受速度。
5.2 速度-精度曲线

以精度为纵轴、速度为横轴,RetinaNet 曲线整体位于 SSD/YOLO 系列之上,并穿过了两阶段检测器的精度上界–即不存在”一阶段精度天花板”,之前的差距是 loss 设计问题,不是架构问题。
5.3 Focal Loss 消融
γ 的影响(α 固定 0.25):
| γ | mAP |
|---|---|
| 0(标准 CE) | 崩溃(被负样本主导) |
| 0.5 | 33.0 |
| 1.0 | 35.4 |
| 2.0 | 36.0 |
| 5.0 | 35.6 |
α 与 γ 的交互:无 γ 时最优 α≈0.25-0.5;加入 γ=2 后最优 α 仍≈0.25,但单独调 α 的增益变小(因为 γ 已在处理难易样本,α 主要处理数量平衡)。
5.4 架构消融
- Backbone 越深精度越高(ResNet-50 → 101:+1.7 mAP);
- 金字塔层数 $P_3$-$P_7$ 比只用 $P_3$-$P_5$ 更好,尤其大目标;
- 分类/回归头解耦比共享参数高约 0.9 mAP。
六、意义与影响
- 诊断了一阶段精度瓶颈的根因:不是架构不行,是 dense 训练下的易分负样本主导问题。这篇诊断本身比 Focal Loss 更有价值。
- Focal Loss 成为标准工具:被广泛用于分类不平衡、检测等任务。
- 但后续有反思:后来的一些工作(如 GHM、GFL、FreeAnchor,以及 YOLOv5/v8 的实践)发现,有了更好的标签分配(如 OTA、TAL)后,Focal Loss 的必要性下降–这反过来说明,类不平衡问题也可以从”样本匹配”侧解决,而非只能在”loss 加权”侧解决。
📌 有趣的对照:YOLOv3 也试过 Focal Loss,结果 mAP 反而下降约 2 个点(见 [[YOLOv3 详解]] 第十一节)。原因正是 YOLOv3 用了”1 GT = 1 正样本 + ignore_mask”的严格分配,正负比例已不像 RetinaNet 那样极端,Focal Loss 的收益消失反而带来调参负担。这说明 Focal Loss 是针对”密集分配+极端不平衡”场景的解药,不是万能药。
七、优缺点
优点 ✅
- 诊断精准:首次清晰指出一阶段精度瓶颈是类不平衡/易分负样本
- 方案优雅:Focal Loss 一行公式、完全可微、端到端
- 首个超越两阶段的一阶段:40.4 mAP,里程碑
- 分析扎实:消融实验充分,γ/α/初始化 trick 都有验证
- 结构清晰:ResNet + FPN + 双头,成为后续一阶段的范式模板
局限 ❌
- 仍不够快:5 FPS(ResNet-101)远不及 YOLO 的实时水准,工程实时部署仍首选 YOLO
- 超参敏感:γ、α、π 需联合调,换数据集/任务常需重调
- Focal Loss 非万能:在更好的标签分配策略下收益有限(甚至如 YOLOv3 般负作用)
- 后处理仍依赖 NMS:未摆脱 anchor + NMS 范式(直到 DETR 才真正去除)
- 训练 trick 依赖:bias 初始化等 trick 缺一不可,复现门槛较高
总结
RetinaNet 的核心贡献不是网络结构,而是 Focal Loss 这一损失函数以及它背后**”一阶段精度差是因为易分负样本淹没训练”的诊断**。它一举弥合了一阶段与两阶段的精度鸿沟,是目标检测史上的关键节点;同时也提醒我们:损失设计与样本分配策略往往是”同一问题的两面”,后续检测器多在这两条线上继续演进。
八、相关链接
- 📝 [[YOLOv3 详解]] - YOLOv3 性能表中含 RetinaNet 对比;且 YOLOv3 试 Focal Loss 失败的对照很有启发
- 📝 [[YOLO 系列总览]] - 一阶段检测器谱系
- 📝 [[YOLO 核心概念]] - Anchor / IoU / NMS / FPN 共享概念
- 📋 [[10.clippings/感知算法/2D检测/单阶段/RetinaNet/RetinaNet 论文和代码详解]] - 知乎 (周威, 含 PyTorch 代码)
- 📋 [[10.clippings/感知算法/2D检测/单阶段/RetinaNet/一阶段目标检测器-RetinaNet 详解]] - 知乎 (真真锋, 含 BBox 编解码)
- 📋 [[10.clippings/感知算法/2D检测/单阶段/RetinaNet/深度学习之目标检测(五)– RetinaNet网络结构详解-CSDN博客]] - CSDN (霹雳吧啦Wz 视频笔记)
- 📋 论文原文: arxiv.org/abs/1708.02002
- 📋 官方代码: facebookresearch/Detectron