YOLOv3: An Incremental Improvement

论文: YOLOv3: An Incremental Improvement (2018)
作者: Joseph Redmon, Ali Farhadi
代码: pjreddie.com/darknet/yolo

论文标题就叫 “An Incremental Improvement”(一点小改进),作者自嘲”没做啥特别的研究,就是把它做得更好了”。但这篇”技术报告”在工程界大受欢迎,成为后续 v4/v5/v7 的基础,也是 Redmon 关于 YOLO 的最后一篇(2020 年因反对将 YOLO 用于军事/隐私而停止更新)。


一、概述:v3 改了什么

1
2
3
4
YOLOv3 = v2 的底座 + 三大新组件:
① Darknet-53 ← 残差骨干网,53 层纯卷积,无池化无全连接
② FPN 多尺度 ← 3 个尺度同时预测,大幅提升小目标
③ Logistic 分类器 ← 多标签,BCE 损失,替代 Softmax

1.1 v1 → v2 → v3 演进对比

维度 YOLOv1 YOLOv2 YOLOv3
Backbone 类 GoogLeNet (24层) Darknet-19 (19层) Darknet-53 (53层 + 残差)
下采样 Max Pooling Max Pooling 步长=2 卷积 (无池化)
检测尺度 1 个 (7×7) 1 个 (13×13) 3 个 (13×13, 26×26, 52×52)
Anchor 数 无 (直接回归) 5 (K-means) 9 (3 尺度 × 3 Anchor)
分类器 Softmax Softmax Sigmoid (多标签, BCE)
置信度目标 IoU (动态) IoU / 1 (rescore) 1 (二分类)
每格预测框 2 5 3 (每尺度)
总预测框 98 845 10647
mAP (COCO) - 21.6 33.0

1.2 保留自 v2 的特性

  • 端到端训练,一个 loss 搞定,统一为回归问题
  • 每个卷积层后接 BN + LeakyReLU
  • Anchor Box 机制 + 维度聚类 (K-means,距离用 1-IoU)
  • 边框解码公式(同 v2,见 [[YOLOv2 详解]])
  • 多尺度训练(每 10 batch 随机换输入尺寸)

二、网络架构:Darknet-53

2.1 三个基本单元

1
2
3
4
5
6
7
8
CBL      = Conv2D + BatchNorm + LeakyReLU            ← 最小组件

Res unit = CBL(1×1) -> CBL(3×3) -> add(输入) ← 残差单元
借鉴 ResNet: 输出 = F(x) + x, 解决深层梯度消失

ResX = ZeroPad + CBL(3×3, stride=2) + X个Res unit ← 残差块组
每个 ResX 的第一个 CBL 负责 2× 下采样
5 个 ResX 实现 32× 总下采样: 416 -> 13

基础操作辨析Concat(张量拼接,通道叠加,扩充维度,对应 cfg 的 route)vs add(张量相加,维度不变,对应 cfg 的 shortcut,即残差连接)。v3 的 FPN 融合用 Concat,残差块内部用 add

2.2 骨干网结构(5 个 ResX)

输入 416×416×3 经过 5 次步长=2 的卷积下采样,逐级缩小到 13×13:

1
2
3
4
5
6
7
8
输入 416×416×3
↓ CBL 32×3×3
416×416×32
↓ Res1 (×1) CBL 64/s2 + 1×Res → 208×208×64
↓ Res2 (×2) CBL 128/s2 + 2×Res → 104×104×128
↓ Res8 (×8) CBL 256/s2 + 8×Res → 52×52×256 ★ Y3 源特征
↓ Res8 (×8) CBL 512/s2 + 8×Res → 26×26×512 ★ Y2 源特征
↓ Res4 (×4) CBL 1024/s2 + 4×Res → 13×13×1024 ★ Y1 源特征

2.3 完整架构(骨干 + FPN 颈部 + 检测头)

1
2
3
4
5
6
7
8
9
10
11
12
13
13×13×1024 ──→ 5×CBL ──→ 输出 Y1 (13×13×255)   大目标
│ ↑ 3×(4+1+80)=255
├── CBL(1×1) → 上采样(×2) → 26×26×256
│ ↓
│ Concat ← 26×26×512 (Res8 源特征)
│ ↓
│ 5×CBL ──→ 输出 Y2 (26×26×255) 中目标
│ │
│ ├── CBL(1×1) → 上采样(×2) → 52×52×128
│ │ ↓
│ │ Concat ← 52×52×256 (Res8 源特征)
│ │ ↓
│ │ 5×CBL ──→ 输出 Y3 (52×52×255) 小目标

关键设计

  • 无池化层、无全连接层:所有下采样通过 conv stride=2 实现,保留更多空间信息;也正因全卷积,才能支持任意尺寸输入与多尺度训练。
  • 每个卷积层后都接 BN + LeakyReLU
  • 53 层怎么数1 + (1+1×2) + (1+2×2) + (1+8×2) + (1+8×2) + (1+4×2) = 52 个卷积层 + 1 个全连接(分类版)= 53。检测版去掉全连接,实际约 52 个卷积层 + 检测头。

2.4 与 Darknet-19 对比

特性 Darknet-19 (v2) Darknet-53 (v3)
下采样 5× MaxPooling 5× Conv stride=2
残差连接 ❌ 无 ✅ 5 组 Res block
卷积层数 19 53
ImageNet Top-1 72.9% 77.2%
ImageNet Top-5 91.2% 93.8%
计算量 (224×224) 55.8 亿次 65.9 亿次

层数翻了近 3 倍,计算量只增加 18%。残差结构使深层网络训练成为可能;Darknet-53 比 ResNet-101/152 更高效,且更好地利用了 GPU。


三、多尺度预测:FPN 思想 🔥

v3 最重要的改进。借鉴 FPN(Feature Pyramid Networks),用 3 个尺度的特征图同时预测,一举扭转了 YOLO 在小目标上的劣势。

3.1 为什么需要多尺度?

1
2
3
4
5
6
7
8
v2 只在 13×13 一层做预测:
13×13 特征图 → 一个网格覆盖 32×32 像素
→ 小鸟可能只有 10×10 像素 → 小于一个网格 → 检测不到!

v3 在 3 层同时预测:
13×13 (stride 32): 大感受野 → 汽车、人
26×26 (stride 16): 中感受野 → 书本、猫
52×52 (stride 8): 小感受野 → 小鸟、瓶盖

3.2 FPN 背景:四种特征金字塔方案

理解 v3 的多尺度,先看 FPN 论文总结的四种多尺度方案:

方案 做法 缺点
(a) 图像金字塔 原图多次缩放成金字塔,每级分别算特征 算力/内存巨大,训练测试不一致
(b) 单层特征 只取最后一层特征(Fast/Faster R-CNN 默认) 高分辨率层语义弱,不利于识别;低分辨率不利于小目标
(c) SSD 式 从不同层各取特征图组金字塔 仅用深层,丢失高分辨率特征,小目标不利
(d) FPN 单输入 + 自上而下上采样 + 横向连接融合 — ✅ v3 采用

FPN 核心思想:低层特征位置准但语义弱;高层特征语义强但位置粗。通过自上而下上采样 + 横向连接,把深层语义注入浅层,让所有层都兼具强语义与高分辨率。

3.3 FPN 融合过程

1
2
3
4
5
深层特征 (13×13)  ← 语义强、位置粗 → 适合大目标
↓ 上采样 2×
+ 中层特征 (26×26) ← 位置较准 → 适合中目标
↓ 上采样 2×
+ 浅层特征 (52×52) ← 位置最准 → 适合小目标

⚠️ v3 的 FPN 融合用 Concat(通道拼接) 而非 Add(逐元素相加)

  • Concat 会扩充通道数(如 256+512→768),保留更多原始信息,源于 DenseNet 思路;
  • Add 维度不变,是 ResNet/Faster R-CNN 风格。

上采样用最近邻插值(不改变通道数),将小特征图放大到与目标层同尺寸后拼接。

3.4 三个尺度与 9 个 Anchor

尺度 特征图 下采样 Anchor 尺寸 (COCO) 检测目标 预测框数
Y1 13×13 32× (116×90), (156×198), (373×326) 大物体 13²×3 = 507
Y2 26×26 16× (30×61), (62×45), (59×119) 中物体 26²×3 = 2028
Y3 52×52 (10×13), (16×30), (33×23) 小物体 52²×3 = 8112
合计 9 种 Anchor 10647 框

9 个 Anchor 由 K-means 聚类得到(同 v2 方法,距离用 1-IoU),按尺寸从大到小均分给 3 个尺度。大 Anchor 配小特征图,小 Anchor 配大特征图——因为层数越深,特征图对小尺寸物体响应越弱,所以小目标交给高分辨率的浅层特征图 + 小 Anchor。


四、检测头与输出张量

每个尺度经过 5×CBL 后接一个 1×1 卷积输出预测张量。以 COCO(80 类)为例:

1
2
3
4
5
6
7
8
9
10
11
255 = 3 × (4 + 1 + 80)

每个网格有 3 个 Anchor, 每个 Anchor 预测:
tx, ty, tw, th ← 4 个坐标偏移
to (objectness) ← 1 个目标置信度
p1 ... p80 ← 80 个类别概率

三个尺度的输出张量:
Y1: 13 × 13 × 255 (大目标)
Y2: 26 × 26 × 255 (中目标)
Y3: 52 × 52 × 255 (小目标)

总预测框数:$(13^2 + 26^2 + 52^2) \times 3 = 10647$。一张图产生上万个框,绝大多数是背景,靠置信度阈值 + NMS 筛选(见第九节)。


五、边界框预测与解码

5.1 网络预测的四个偏移量

网络并不直接输出框的绝对坐标,而是输出相对于网格和先验框的偏移量 $t_x, t_y, t_w, t_h$。设:

  • $(c_x, c_y)$:该网格左上角距图像左上角的格子数;
  • $(p_w, p_h)$:先验框(Anchor)的宽高;
  • $\sigma$:sigmoid 函数。

5.2 解码公式

$$
b_x = \sigma(t_x) + c_x, \quad b_y = \sigma(t_y) + c_y
$$
$$
b_w = p_w , e^{t_w}, \quad b_h = p_h , e^{t_h}
$$

  • 中心点 $(b_x, b_y)$:sigmoid 把偏移压在 $[0,1]$,保证中心不会跑出当前网格;
  • 宽高 $(b_w, b_h)$:以先验框为基准做指数缩放,$e^{t_w}$ 是放大倍数。训练时对宽高取 log 还原成 $t_w = \log(G_w/p_w)$,取 log 是为了让训练更稳定。

训练用平方误差:若真实框对应 $\hat{t}*$,则梯度为 $\hat{t}* - t_*$,真实值可由上式反解得到。

5.3 为什么 x, y 用 sigmoid 而非线性?

作者尝试过用线性激活直接预测 $(x,y)$,导致 mAP 下降、模型不稳定。sigmoid 将中心约束在网格内,是必要的稳定性保证(见第十一节”失败的尝试”)。


六、多标签分类:Sigmoid 替代 Softmax

6.1 为什么要改?

1
2
3
4
5
6
7
8
9
10
Softmax 的问题: 假设类别互斥
输入 → Softmax → [猫=0.6, 狗=0.3, 人=0.1] → 总和=1
问题: 一个物体可能同时属于多个类!
比如 "女人" 既是 "Person" 也是 "Woman"
Softmax 只能选一个, 必然丢信息

Sigmoid 的解决: 每个类别独立判断
输入 → 80 个 Sigmoid → [Person=0.9, Woman=0.85, Cat=0.01, ...]
→ 不要求和为 1, 每个类独立做二分类
→ 完美支持多标签场景 (如 Open Images 数据集)

6.2 损失函数跟着改

组件 v1/v2 v3 为什么改
类别损失 Softmax + SSE Sigmoid + BCE 多标签 + 交叉熵更合理
置信度损失 SSE Sigmoid + BCE 二分类用交叉熵
定位损失 (x,y) SSE Sigmoid + BCE 中心坐标也改为 BCE
定位损失 (w,h) SSE SSE(保留) 宽高仍是连续回归

⚠️ v3 论文没有给出完整的损失函数公式,以上基于 Keras 源码(qqwweee/keras-yolo3)和社区分析总结。


七、训练:标签分配与损失函数

7.1 框分配机制(同 v2,参数有变)

1
2
3
4
5
6
7
8
9
10
11
12
分配流程:
物体中心落在哪个网格 → 该网格的 3 个 Anchor 中
→ 与 GT IoU 最大者当选 Responsible Anchor(正样本, 1:1 分配)

与 v2 的关键参数差异:
┌──────────────┬──────────┬──────────┐
│ 参数 │ v2 │ v3 │
├──────────────┼──────────┼──────────┤
│ ignore_thresh │ 0.6 │ 0.5 │ ← v3 更严格
│ 置信度 target │ IoU(动态) │ 1(固定) │ ← v3 最大的改动!
│ rescore │ 1(默认) │ 无此参数 │
└──────────────┴──────────┴──────────┘

v3 把置信度从”回归”改为”二分类”——常被忽略但很重要:

  • v2:target = IoU(预测框, GT),网络要学”框得有多准”。小物体 IoU 始终很小,confidence 学不上去,Recall 低。
  • v3:target = 1(正)/ 0(负),网络只学”这个 Anchor 有没有负责物体”,不受 IoU 大小影响。代价是定位质量不再体现在 confidence 中(这是后续版本改进方向)。

7.2 三类样本

1
2
3
4
5
6
7
8
正样本 (Positive): 与 GT IoU 最大的 Anchor(每个 GT 仅 1 个)
→ 计算定位损失 + 置信度损失(target=1) + 类别损失

忽略样本 (Ignore): IoU > 0.5 但不是最大的 Anchor
→ 不计算任何损失(预测得还行,不惩罚也不奖励)

负样本 (Negative): IoU < 0.5 的 Anchor
→ 只计算置信度损失(target=0)

1:1 严格分配是 YOLOv3 与其他检测器的区别:Faster R-CNN / RetinaNet 会把 IoU>0.7 的多个 anchor 都算正样本,YOLOv3 只选 1 个最佳。好处是训练目标明确不打架,代价是可能收敛慢。

7.3 Ignore Mask:10647 个框里只有几个”好人”

一张 COCO 训练图(假设 5 个 GT)的实际分布:

1
2
3
正样本:    5 个        (每个 GT 选 1 个最佳 Anchor)
忽略样本: ~50 个 (IoU > 0.5 但不够格, 不参与损失)
负样本: ~10592 个 (IoU < 0.5, 全被判"没物体")

为什么需要 Ignore Mask? 多尺度下同一物体可能在多个尺度都被检测到:若把那些”也检测到了、IoU>0.5”的框强行压向 0,会反向传播大量错误梯度,三个尺度互相打架,训练不收敛。Ignore 让它们”自由发挥”——既不惩罚也不鼓励。

为什么 YOLOv3 不需要 Hard Negative Mining?

  • 置信度用 BCE(二分类),负样本梯度比多分类 Softmax 小得多;
  • ignore_mask 已过滤掉”说得过去”的框,剩下的负样本是真·背景;
  • 正负比例虽悬殊(~5:10592),但 BCE 天然不会让负样本主导梯度方向。

7.4 跨尺度标签分配

一个中尺寸物体(如中等大小的狗)可能在多个尺度同时匹配:

1
2
3
Y2 (26×26) 某 Anchor IoU=0.75 → 正样本(唯一!)
Y1 (13×13) 某 Anchor IoU=0.65 → IoU>0.5 → 忽略!
Y3 (52×52) 某 Anchor IoU=0.35 → 负样本

有了跨尺度 ignore,三个尺度和平共处,不会互相抑制。

7.5 损失函数(基于 Keras 源码)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 定位损失 - 中心坐标用 BCE, 宽高用 MSE
xy_loss = object_mask * box_loss_scale * K.binary_crossentropy(
raw_true_xy, raw_pred[..., 0:2], from_logits=True)

wh_loss = object_mask * box_loss_scale * 0.5 * K.square(
raw_true_wh - raw_pred[..., 2:4])

# 置信度损失 - 全用 BCE (二分类), 负样本乘 ignore_mask
confidence_loss = (
object_mask * K.binary_crossentropy(object_mask, raw_pred[..., 4:5], from_logits=True) +
(1 - object_mask) * K.binary_crossentropy(object_mask, raw_pred[..., 4:5], from_logits=True) * ignore_mask
)

# 类别损失 - BCE (多标签二分类)
class_loss = object_mask * K.binary_crossentropy(
true_class_probs, raw_pred[..., 5:], from_logits=True)

w, h 仍用平方误差外,其余部分(xy、置信度、类别)均用 二元交叉熵 BCE,最后相加。

7.6 损失函数权重

参数 含义 说明
box_loss_scale 定位损失缩放 2 - w_gt × h_gt,小框获得更大权重(同 v2)
object_mask 正样本掩码 Responsible Anchor = 1, 其余 = 0
ignore_mask 忽略掩码 IoU > 0.5 但不负责的框 = 0,不参与置信度损失

八、训练配置

8.1 两阶段训练

1
2
3
4
5
6
7
8
9
10
11
第一阶段: ImageNet 分类预训练
网络: Darknet-53 (分类版) 数据: ImageNet 1000类 输入: 256×256
结果: Top-1 77.2%, Top-5 93.8%

第二阶段: 检测训练
网络修改: 去掉分类头 → 加 FPN 颈部 + 3 个检测头
数据: COCO train2014 输入: 多尺度 320~608
Epochs: 270 学习率: 初始 1e-3, 分段衰减
优化器: SGD + Momentum 0.9 权重衰减: 0.0005 Batch Size: 64
Anchor 数: 9 (3×3) ignore_thresh: 0.5
数据增强: 随机裁剪、翻转、HSV 色彩抖动

论文强调:在完整图像上训练,没有硬负样本挖掘,使用多尺度训练 + 大量数据增强 + BN——都是”标准操作”。

8.2 多尺度训练

同 v2:每 10 batch 随机切换输入尺寸 ∈ {320, 352, …, 608}(32 的倍数)。全卷积架构(无 FC)使其成为可能,让模型对不同分辨率鲁棒。


九、预测流程:阈值过滤 + NMS

网络输出 10647 个预测框后,需筛出最终结果:

1
2
3
4
5
6
① 置信度过滤
score = confidence × class_prob (类别置信度 = 目标置信度 × 类别概率)
→ 丢弃 score < 阈值 的框(去掉大部分背景框)

② 非极大值抑制 (NMS)
→ 同一物体只保留 score 最高的框, 去除重复框

NMS 流程(以保留 A、D 为例):

  1. 按类别概率排序剩余框,如 A > D > B > F > E > C;
  2. 取概率最大的 A,计算其余框与 A 的 IoU,IoU > 阈值者丢弃(B、C 与 A 重叠大被丢弃),保留 A;
  3. 从剩余框中再取最大的 D,重复上一步,保留 D;
  4. 直到所有框处理完,最终保留 A、D。

⚠️ NMS 的局限:若两个不同物体重叠很大(如狗压在自行车上),自行车的框可能因与狗框 IoU 过大被误删。改进方案有 Soft-NMS 等。


十、性能表现

10.1 COCO 数据集

模型 mAP@0.5 mAP@[0.5:0.95] 推理时间 FPS
YOLOv3-320 51.5 28.2 22 ms 45
YOLOv3-416 55.3 31.0 29 ms 35
YOLOv3-608 57.9 33.0 51 ms 20
SSD321 45.4 28.0 61 ms 16
SSD513 50.4 31.2 125 ms 8
RetinaNet-50-500 50.9 32.5 73 ms 14
RetinaNet-101-800 57.5 37.8 198 ms 5

YOLOv3-608 与 RetinaNet-101 精度相当,速度快 3.8 倍;320 轻量版精度逼近 SSD513,速度快 5.7 倍

注意:YOLOv3 在旧指标 mAP@0.5 上极强,但 mAP@[0.5:0.95] 随 IoU 阈值升高下降明显——说明它擅长”框出大概位置”,但框与目标精确对齐的能力较弱。

10.2 消融实验

配置 mAP 说明
YOLOv2 baseline 21.6 COCO 基准
+ Darknet-53 +2.2 残差骨干
+ FPN 多尺度 +3.1 三尺度检测
+ 9 Anchor +0.8 更多先验框
+ Logistic 分类 +0.5 多标签

10.3 不同目标大小的表现

1
2
3
4
                    AP_small   AP_medium   AP_large
YOLOv2 5.0 22.4 35.5
YOLOv3 18.3 ✅ 35.4 ✅ 41.9 ✅
(小目标提升最显著, 3.7×!)

FPN 三尺度使小目标 AP 从 5.0 → 18.3,是 v3 最大亮点;但中大目标提升不如小目标显著。


十一、论文中”失败”的尝试

YOLOv3 论文的独特之处——作者诚实地记录了几个尝试了但没用的方法:

尝试 结果 原因
预测 $(x,y)$ 与 $(w,h)$ 的线性关系 不稳定 非线性关系更强
用线性激活替代 sigmoid 预测 $(x,y)$ mAP ↓ sigmoid 约束是必要的
使用 Focal Loss mAP ↓ ~2 YOLO 的正负样本比例和 RetinaNet 不同,已有独立 objectness 预测
双 IoU 阈值分配(仿 Faster R-CNN) 效果不好 当前 1:1 分配已是局部最优

十二、优缺点与总结

优点 ✅

  1. 速度-精度最佳平衡:30 FPS @ 55 mAP,实际部署首选
  2. 小目标大幅改善:FPN 三尺度使小目标 AP 从 5.0 → 18.3
  3. 多标签支持:Sigmoid + BCE,贴合真实场景
  4. 全卷积无池化:更灵活、保留更多空间信息,支持任意尺寸输入
  5. 残差骨干可深可浅:Darknet-53 比 ResNet-101/152 更高效
  6. 工程生态成熟:Darknet(C)、PyTorch、Keras 多框架实现

局限 ❌

  1. 论文像”技术报告”:没有完整损失函数公式,需读源码
  2. Anchor 分配粒度粗:1 GT → 1 Anchor,密集场景有提升空间(v4/v5 改进)
  3. 置信度丢失 IoU 信息:改为二分类后,框质量无法从 confidence 区分(高质量框与低质量框 confidence 都是 1)
  4. 无数据增强创新:复用了 v2 的增强策略
  5. 大目标检测饱和:AP_large 从 35.5 → 41.9,提升不如小目标显著
  6. 高 IoU 阈值下精度下降:框对齐能力弱,mAP@[0.5:0.95] 不占优

总结

YOLOv3 的成功不靠单一突破,而是”融合经典方法 + 针对性优化”:架构上用”全卷积 + 残差连接”解决深层训练难题;检测逻辑上用”多尺度特征融合 + 9 种先验框”覆盖不同大小目标;预测逻辑上用”Logistic 激活”支持多标签。这些设计使其成为目标检测的经典基准,至今仍被广泛用于自动驾驶、安防、工业检测,也为 v4/v5 奠定基础。


十三、相关链接

  • 📝 [[YOLO 系列总览]] - 版本对比总览
  • 📝 [[YOLO 核心概念]] - Grid/Anchor/IoU/NMS 详解
  • 📝 [[YOLOv1 详解]] - v1 基础
  • 📝 [[YOLOv2 详解]] - v2 锚框 + BN 改进
  • 📝 [[YOLOv4 详解]] - v4 在 v3 基础上的优化
  • 📋 [[10.clippings/感知算法/2D检测/单阶段/YOLO/YOLO学习笔记3——YOLOv3详解]] - 知乎原文 (rtfff, 含 FPN 详解)
  • 📋 [[10.clippings/感知算法/2D检测/单阶段/YOLO/【YOLO系列】YOLOv3论文超详细解读(翻译 +学习笔记)-阿里云开发者社区]] - 论文翻译+精读
  • 📋 [[10.clippings/感知算法/2D检测/单阶段/YOLO/Yolov3算法详解]] - 博客园 (奥辰, 含 NMS 详解)
  • 📋 [[10.clippings/感知算法/2D检测/单阶段/YOLO/YOLOv3 详解:核心改进、网络架构与目标检测实践_目标检测_2201_75691511-九章云极普惠算力]] - 核心改进总结
  • 📦 [[30.resources/yolo-master/docs/Pytorch_YoLo_From_Scratch/v3/README|v3 From Scratch]]