写在前面
YOLOv3 发布于 2018 年,论年纪早就算"老前辈"了。但直到今天,它依然是目标检测入门最好的教材:结构不复杂、代码量不大、论文写得像博客一样口语化,而且几乎所有后续的 YOLO 变体(v4/v5/v7/v8...)都能在它身上找到影子------多尺度检测、Anchor 机制、特征拼接、损失构成,这些概念都是从这一版开始定型并被反复沿用的。
所以这篇文章不讲怎么训练、不讲怎么调参,就老老实实回答一个问题:
YOLOv3 是怎么把一张图片,变成"一堆带类别的方框"的?
先把官方的经典测试图摆出来,感受一下它要解决的问题------输入一张普通照片,输出每个物体的位置和类别:



一、先给 YOLOv3 一个准确的定位
很多文章把 YOLOv3 讲得很玄,好像它发明了什么划时代的新结构。其实论文作者自己说得很直白:这版就是**"a bunch of little design changes"(一堆小的设计改动)**,它并没有引入全新的检测范式。
它属于 one-stage(单阶段)检测器------不生成候选区域,一次前向传播直接回归出框的位置和类别。对比两阶段检测器(如 Faster R-CNN)的"先出候选框、再分类回归",YOLOv3 把这两件事塞进了同一个网络。
同时代的竞争对手是 RetinaNet(提出 Focal Loss 的那篇),两者的路线差异很典型:
| YOLOv3 | RetinaNet | |
|---|---|---|
| 类别预测 | 每个类别独立的 sigmoid | softmax / sigmoid + Focal Loss |
| 难例挖掘 | 不做 | Focal Loss 自动降权易样本 |
| 精度表现 | AP50 高、AP75 偏低 | 高 IoU 下更稳 |
| 速度 | 很快 | 相对慢 |
论文里甚至专门写了一节"Things We Tried That Didn't Work(试过但没用的东西) ",承认了几件失败尝试:给 anchor 也预测 x/y 偏移、用线性激活预测 x/y 而不是 sigmoid、用 Focal Loss、用双 IoU 阈值做软训练。这种坦诚很值得一读------它告诉你哪些"看起来很美"的改动实际上没用。
二、整体结构:一张图看懂

整条链路可以拆成四步:
- 输入图像:416×416×3(也可以是 320、608 等);
- Darknet-53 主干网络:把图像层层下采样,抽出一串特征图;
- 多尺度融合:把不同分辨率的特征图用「上采样 + 拼接」拼成三个尺度;
- 三个检测头:每个尺度各自用卷积直接预测出方框坐标、置信度和类别。
最终输出的是三个张量:
13 × 13 × 255 ← 大目标
26 × 26 × 255 ← 中目标
52 × 52 × 255 ← 小目标
255 这个数字是怎么来的? 这是理解 YOLOv3 的第一个关键点:
255 = 3 × 85 = 3 × (4 + 1 + 80)
↑ ↑ ↑ ↑ ↑
| | | | └─ 80 个类别各自的得分(COCO 数据集)
| | | └───── 1 个 objectness(这个格子里有没有物体)
| | └───────── 4 个坐标 tx, ty, tw, th
| └────────────────── 每个格子预测 3 个框
└────────────────────── 3 个先验框(anchor)
也就是说:特征图上的每一个格子,都会预测 3 个边界框;每个框除了位置,还要给出"有没有物体"的置信度和 80 个类别的得分。13×13 的格子数最少,52×52 的格子数最多,这就是三个尺度输出形状不同的原因。
三、主干网络 Darknet-53

3.1 结构本身:5 级下采样 + 残差堆叠
Darknet-53 的名字来自它53 个卷积层。它最鲜明的两个特点:
- 没有池化层 。所有下采样都由
stride = 2的 3×3 卷积完成,特征图尺寸依次是 416 → 208 → 104 → 52 → 26 → 13,一共下采样 5 次,正好 25=322^5 = 3225=32 倍。这也是为什么输入边长必须是 32 的倍数------否则 13×13 那个尺度得不到整数尺寸。 - 残差连接。每个基本单元是「Conv 1×1(通道砍半)→ Conv 3×3 → 与输入相加」,借鉴了 ResNet 的跳连思路,让深层网络能训得下去。
3.2 为什么不直接用 ResNet?
因为要在精度和速度之间取平衡。论文给了一张对比表:
| 主干网络 | Top-1 精度 | Top-5 精度 | Bn Ops | FPS |
|---|---|---|---|---|
| Darknet-19 | 74.1% | 91.8% | 7.29 | 171 |
| ResNet-101 | 77.1% | 93.7% | 19.7 | 53 |
| ResNet-152 | 77.6% | 93.8% | 29.4 | 37 |
| Darknet-53 | 77.2% | 93.8% | 18.7 | 78 |
看最后两行:Darknet-53 的精度和 ResNet-152 几乎持平,但速度快了一倍多,算力(Bn Ops)也更少。论文的说法很实在:"It's more powerful than Darknet-19 but still more efficient than ResNet-101 or ResNet-152."
3.3 顺带说一声 DBL
后面的图里会反复出现 DBL,它是 Darknet 里的基本卷积单元:
DBL = Conv(卷积层) + BN(批归一化) + Leaky ReLU(斜率 0.1)
YOLOv3 的主干和检测头,都是靠 DBL 一层层堆出来的。
四、三尺度预测:YOLOv3 最核心的改动
这是 YOLOv3 相对前代最大的一次结构升级。YOLOv2 只有一个尺度的输出,YOLOv3 变成了三个。

4.1 上采样 + 拼接:把"深层语义"喂给"浅层细节"
思路源自 FPN(特征金字塔网络):
- 主干最深处拿到 13×13 的特征,语义最强,但分辨率最低------它负责大物体;
- 把 13×13 的特征用 1×1 卷积压到 256 通道,再上采样 ×2 变成 26×26;
- 与主干里同样 26×26 的那张特征图(512 通道)拼接(Concat),得到 26×26×768;
- 26×26 这一支再走一遍上面的流程,得到 52×52×384 的特征,负责小物体。
通道数要能对上,这里列清楚:
| 尺度 | 主干特征图 | 上采样分支 | 拼接后 | 最终输出 |
|---|---|---|---|---|
| 13×13 | 1024 通道 | --- | --- | 13×13×255 |
| 26×26 | 512 通道 | 256 通道(13² 上来) | 768 | 26×26×255 |
| 52×52 | 256 通道 | 128 通道(26² 上来) | 384 | 52×52×255 |
之所以能把深层特征"喂"给浅层,是因为拼接而不是相加:检测头需要的既有"这是什么"(语义),也有"在哪里"(位置精度)。相加会互相污染,拼接则把两种信息都留给后面的卷积自己组合。
4.2 三个尺度分别管什么?
| 特征图 | 单个格子对应的感受野 | 适合的目标 |
|---|---|---|
| 13 × 13 | 最大(粗略但看得远) | 大目标:公交车、卡车、人物特写 |
| 26 × 26 | 中等 | 中等目标 |
| 52 × 52 | 最小(精细) | 小目标:远处的行人、飞鸟 |
13×13 的格子大,一个大物体正好落在一个格子里;52×52 的格子小,小物体才有机会被"单独分到一个格子"。如果没有多尺度,小目标检测会明显更差------这正是后面第十节要说到的短板。
五、先验框 Anchor:9 个框是从数据里"聚"出来的
每个格子预测 3 个框,但网络不是从零开始"瞎猜"框的大小------它先有 9 个**先验框(anchor)**作为参考,网络只负责在它们基础上做微调。
这 9 个框不是手工设计的,而是在 COCO 数据集上对真实框的宽高做 k-means 聚类得到的,然后按尺度大小分成 3 组,每组配给一个尺度:
| 尺度 | 特征图 | 分配到的 3 个先验框 (w×h) |
|---|---|---|
| 52 × 52 | 小目标 | (10,13)、 (16,30)、 (33,23) |
| 26 × 26 | 中目标 | (30,61)、 (62,45)、 (59,119) |
| 13 × 13 | 大目标 | (116,90)、 (156,198)、 (373,326) |
注意规律:特征图越大(格子越密),配的先验框越小。因为 52×52 是给小目标用的,如果给它配一个 373×326 的大框,这个框会横跨几十个格子,完全没有意义。
六、边界框解码:σ 和 e^t 分别在做什么

图上每个框的坐标不是直接回归出来的绝对像素值,而是四个相对量 tx,ty,tw,tht_x, t_y, t_w, t_htx,ty,tw,th,需要按下式解码成真实框:
bx=σ(tx)+cx b_x = \sigma(t_x) + c_x bx=σ(tx)+cx
by=σ(ty)+cy b_y = \sigma(t_y) + c_y by=σ(ty)+cy
bw=pw⋅etw b_w = p_w \cdot e^{t_w} bw=pw⋅etw
bh=ph⋅eth b_h = p_h \cdot e^{t_h} bh=ph⋅eth
其中 cx,cyc_x, c_ycx,cy 是当前格子的左上角坐标(相对特征图),pw,php_w, p_hpw,ph 是先验框的宽高,σ\sigmaσ 是 sigmoid。
为什么中心点要用 σ?
预测物体中心时,网络会在特征图上"滑动"。如果让 txt_xtx 无限取值,可能出现中心点漂到十个格子外的情况,训练时会很不稳定。加上 sigmoid 之后,σ(tx)∈(0,1)\sigma(t_x) \in (0, 1)σ(tx)∈(0,1),意味着预测中心永远被限制在负责它的那个格子内部------谁负责这个物体,就由谁的格子来预测中心,职责清晰,梯度也稳。
为什么宽高要用 e^t?
宽高的缩放比例可以是 1/10,也可以是 10 倍,是一个乘性关系 。用 ete^{t}et 表示倍率,正好把"乘性调整"变成"加性偏移",tw=0t_w = 0tw=0 表示保持先验框原样,tw>0t_w > 0tw>0 表示放大、tw<0t_w < 0tw<0 表示缩小。再加上 ln 的对称性,训练时正负方向的梯度尺度一致。而且解码出来的框可以远远大于所在格子,这正是大目标所必需的。
论文还提到一个细节:他们试过让网络直接预测未裁剪的 x/y 偏移(不用 sigmoid),结果 mAP 掉了 0.1 左右。这就是第六节说的"试过但没用"的改动之一。
七、类别预测:为什么把 softmax 换成一群 sigmoid
这是一个非常容易被忽略、但设计意图很明确的改动。
YOLOv2 用 softmax 输出类别概率------它默认"一个框只属于一个类别",各类概率互斥。YOLOv3 改成了对每个类别各做一次独立的 sigmoid(logistic 分类)。
原因在论文里写得很清楚:数据集中存在类别重叠的情况。比如 Open Images 里既有 "Person" 也有 "Woman",被标注成同一个人的框,用 softmax 就会被强行逼着二选一(压掉另一个类别的分数),而用独立的 sigmoid,就可以同时给出 "Person = 0.95" 和 "Woman = 0.92"。
换句话说:
softmax 假设"标签互斥",独立的 logistic 假设"标签可以叠加"。YOLOv3 选择了后者,因为它更符合真实标注数据。
顺带一提,"这个格子里到底有没有物体"的 objectness 置信度,用的也是 sigmoid。
八、损失函数:三部分怎么分工,正负样本怎么分
YOLOv3 的总损失是三块相加:

| 损失项 | 形式 | 作用范围 |
|---|---|---|
| 位置损失 LboxL_{box}Lbox | 平方和误差(MSE) | 只对正样本计算 (t_x, t_y, t_w, t_h) |
| 置信度损失 LobjL_{obj}Lobj | 二元交叉熵(BCE) | 正样本和负样本都算 |
| 分类损失 LclsL_{cls}Lcls | 二元交叉熵(BCE) | 只对正样本计算,80 类各自独立 |
比公式更重要的是正负样本的分配规则,这也是初学者最容易卡住的地方:
- 一个真实框(ground truth)只分配给"和它 IoU 最大"的那一个先验框,被选中的框称为正样本,负责预测这个物体的位置、置信度和类别;
- 剩下的框里,如果 IoU > 0.5 但不是最大的 ,直接忽略------既不算正样本去逼它拟合,也不算负样本去惩罚它。这就是论文里说的 "we ignore the prediction",阈值取 0.5;
- IoU ≤ 0.5 的算负样本,只参与置信度损失,让网络学会在该输出"这里没东西"的时候输出 0。
第 2 条是精髓:一个 20×20 的物体,可能和旁边的框有 0.7 的 IoU,但它并不是最佳匹配。如果强行把它当负样本,就等于让网络"不要预测这个真实存在的物体",模型会越训越糊涂。用"忽略"来绕开这种歧义,比硬区分正负要聪明得多。
九、训练与推理中的几个工程细节
1)输入尺寸是"可变的"
由于网络是全卷积的、且只做 32 倍下采样,它天然可以接受任意 32 倍数的输入。论文给出了三档常用配置,正好对应三种精度/速度取舍:
| 输入尺寸 | mAP (0.5:0.95) | AP50 | 推理时间(Titan X) |
|---|---|---|---|
| 320 × 320 | 28.2 | 51.5 | 22 ms |
| 416 × 416 | 31.0 | 55.3 | 29 ms |
| 608 × 608 | 33.0 | 57.9 | 51 ms |
2)多尺度训练
训练时随机切换输入尺寸(如 320/352/384/.../608),让同一个网络适应多种分辨率。代价是 BatchNorm 之类的统计量会波动,但换来的是推理时"改尺寸不用重训"的灵活性。
3)推理阶段要 NMS
三个尺度、9 个先验框、每个格子 3 个框,同一个物体必然被重复预测多次。所以最后要用非极大值抑制(NMS):先按置信度阈值过滤,再把同一类别里 IoU 过高的框合并掉,只留分数最高的那个。
十、YOLOv3 的得与失
论文里最诚实的一段,是它自己承认的短板。看这张精度-速度图:

- 速度上是压倒性的:608×608 输入,精度 33.0 mAP,只要 51 ms;
- AP50 非常能打 :57.9 AP50 @ 51ms,而 RetinaNet-101-800 拿到 57.5 AP50 却要 198 ms,YOLOv3 快了约 3.8 倍;
- 但 AP75 / 高 IoU 下偏弱:IoU 要求越严格,YOLOv3 的优势越小;
- 小目标偏弱:这是它公开承认的问题(论文提到对小物体的表现较差)。
为什么会这样?因为 YOLOv3 走的是"简单粗暴"的路线:没有 Focal Loss 去专门处理难例,也没有 hard negative mining,全靠大量先验框 + 多尺度 + BCE 的均衡。在"框大致对得上"的场景里它极快极划算;在"必须框得很准"的场景里,RetinaNet 那类方法更有优势。