【YOLO系列06】YOLOv4详解——Bag of Freebies与Bag of Specials
YOLOv4详解:目标检测新标杆 YOLOv4是Alexey Bochkovskiy团队在2020年提出的目标检测算法,通过系统整合多项先进技术,在保持实时性的同时达到43.5% AP精度。其核心创新包括: 采用CSPDarknet53骨干网络,相比Darknet53减少34%参数,提升23%速度 引入Mish激活函数,提供更平滑的非线性特性 使用SPP模块聚合多尺度特征,增强感受野 改进FPN为
【YOLO系列06】YOLOv4详解——Bag of Freebies与Bag of Specials
本文是YOLO系列博客的第六篇,深入解析YOLOv4的网络架构,包括CSPDarknet53骨干网络、SPP、PAN特征融合、Mish激活函数、Mosaic数据增强等大量技术改进。
1. 引言
2020年4月,Alexey Bochkovskiy等人发布了YOLOv4,这是Joseph Redmon退出后首个正式的YOLO版本。YOLOv4系统性地总结和整合了近年来目标检测领域的各种技术,在COCO数据集上达到了43.5% AP(65.7% AP50),同时保持实时推理速度。
论文信息:
- 标题:YOLOv4: Optimal Speed and Accuracy of Object Detection
- 作者:Alexey Bochkovskiy, Chien-Yao Wang, Hong-Yuan Mark Liao
- 发表:arXiv 2020
- 代码:https://github.com/AlexeyAB/darknet
2. 设计理念
2.1 Bag of Freebies (BoF)
定义:只增加训练成本、不增加推理成本的技术
| 类别 | 技术 |
|---|---|
| 数据增强 | Mosaic、CutMix、MixUp、Random Erasing |
| 正则化 | DropBlock、Label Smoothing |
| 损失函数 | CIoU Loss、Focal Loss |
| 其他 | 类别标签平滑、余弦退火学习率 |
2.2 Bag of Specials (BoS)
定义:增加少量推理成本、但显著提升性能的技术
| 类别 | 技术 |
|---|---|
| 注意力机制 | SE、CBAM、SAM |
| 特征融合 | SPP、ASPP、BiFPN、PAN |
| 激活函数 | Mish、Swish |
| 后处理 | DIoU-NMS、Soft-NMS |
2.3 YOLOv4技术选型
经过大量实验,YOLOv4选择的最优组合:
3. CSPDarknet53骨干网络
3.1 CSP(Cross Stage Partial)结构
CSPNet的核心思想:将特征图分成两部分,一部分经过卷积,另一部分直接拼接
优势:
- 减少计算量
- 降低内存占用
- 保持梯度流通畅
- 提升特征复用
3.2 CSP残差块实现
class CSPBlock(nn.Module):
def __init__(self, in_channels, out_channels, num_blocks):
super().__init__()
hidden = out_channels // 2
# 主分支
self.conv1 = ConvBNMish(in_channels, hidden, 1)
self.blocks = nn.Sequential(*[
DarknetBlock(hidden) for _ in range(num_blocks)
])
self.conv2 = ConvBNMish(hidden, hidden, 1)
# 跳跃分支
self.conv3 = ConvBNMish(in_channels, hidden, 1)
# 融合
self.conv4 = ConvBNMish(hidden * 2, out_channels, 1)
def forward(self, x):
# 主分支
x1 = self.conv1(x)
x1 = self.blocks(x1)
x1 = self.conv2(x1)
# 跳跃分支
x2 = self.conv3(x)
# 融合
x = torch.cat([x1, x2], dim=1)
return self.conv4(x)
3.3 CSPDarknet53结构
| 阶段 | 输入通道 | 输出通道 | 残差块数 | 输出尺寸 |
|---|---|---|---|---|
| Stem | 3 | 32 | - | 608×608 |
| Stage1 | 32 | 64 | 1 | 304×304 |
| Stage2 | 64 | 128 | 2 | 152×152 |
| Stage3 | 128 | 256 | 8 | 76×76 |
| Stage4 | 256 | 512 | 8 | 38×38 |
| Stage5 | 512 | 1024 | 4 | 19×19 |
3.4 与Darknet53对比
| 指标 | Darknet53 | CSPDarknet53 |
|---|---|---|
| 参数量 | 41.6M | 27.6M |
| FLOPs | 18.7B | 13.1B |
| Top-1 | 77.2% | 77.2% |
| 速度 | 基准 | 快23% |
参数减少34%,速度提升23%,精度不变!
4. Mish激活函数
4.1 定义
Mish(x)=x⋅tanh(softplus(x))=x⋅tanh(ln(1+ex))\text{Mish}(x) = x \cdot \tanh(\text{softplus}(x)) = x \cdot \tanh(\ln(1 + e^x))Mish(x)=x⋅tanh(softplus(x))=x⋅tanh(ln(1+ex))
4.2 导数
dMishdx=exωδ2\frac{d\text{Mish}}{dx} = \frac{e^x \omega}{\delta^2}dxdMish=δ2exω
其中 ω=4(x+1)+4e2x+e3x+ex(4x+6)\omega = 4(x+1) + 4e^{2x} + e^{3x} + e^x(4x+6)ω=4(x+1)+4e2x+e3x+ex(4x+6),δ=2ex+e2x+2\delta = 2e^x + e^{2x} + 2δ=2ex+e2x+2
4.3 与其他激活函数对比
| 激活函数 | 公式 | 特点 |
|---|---|---|
| ReLU | max(0,x)\max(0, x)max(0,x) | 简单高效,有死区 |
| Leaky ReLU | max(0.1x,x)\max(0.1x, x)max(0.1x,x) | 缓解死区 |
| Swish | x⋅σ(x)x \cdot \sigma(x)x⋅σ(x) | 平滑,自门控 |
| Mish | x⋅tanh(softplus(x))x \cdot \tanh(\text{softplus}(x))x⋅tanh(softplus(x)) | 更平滑,无上界 |
4.4 实现
class Mish(nn.Module):
def forward(self, x):
return x * torch.tanh(F.softplus(x))
# 或使用PyTorch内置
# F.mish(x) # PyTorch 1.9+
5. SPP(Spatial Pyramid Pooling)
5.1 设计动机
增大感受野,聚合多尺度上下文信息。
5.2 结构
5.3 实现
class SPP(nn.Module):
def __init__(self, in_channels, out_channels, pool_sizes=[5, 9, 13]):
super().__init__()
self.conv1 = ConvBNMish(in_channels, in_channels // 2, 1)
self.pools = nn.ModuleList([
nn.MaxPool2d(k, stride=1, padding=k//2)
for k in pool_sizes
])
self.conv2 = ConvBNMish(in_channels // 2 * (len(pool_sizes) + 1), out_channels, 1)
def forward(self, x):
x = self.conv1(x)
features = [x] + [pool(x) for pool in self.pools]
x = torch.cat(features, dim=1)
return self.conv2(x)
6. PAN(Path Aggregation Network)
6.1 与FPN对比
| 结构 | 特点 |
|---|---|
| FPN | 自顶向下,单向传递 |
| PAN | 双向传递,增强底层特征 |
6.2 PAN结构
6.3 YOLOv4中的PAN变体
YOLOv4将FPN中的加法操作改为拼接(Concat):
class PANet(nn.Module):
def __init__(self):
super().__init__()
# 自顶向下
self.upsample = nn.Upsample(scale_factor=2, mode='nearest')
self.conv_up1 = ConvBNMish(1024, 512, 1)
self.conv_up2 = ConvBNMish(512, 256, 1)
# 自底向上
self.downsample1 = ConvBNMish(256, 256, 3, stride=2)
self.downsample2 = ConvBNMish(512, 512, 3, stride=2)
# 特征处理
self.conv_set1 = ConvSet(512, 256)
self.conv_set2 = ConvSet(1024, 512)
self.conv_set3 = ConvSet(1024, 512)
def forward(self, c3, c4, c5):
# 自顶向下
p5 = self.conv_up1(c5)
p5_up = self.upsample(p5)
p4 = torch.cat([p5_up, c4], dim=1)
p4 = self.conv_set1(p4)
p4_up = self.upsample(self.conv_up2(p4))
p3 = torch.cat([p4_up, c3], dim=1)
p3 = self.conv_set2(p3)
# 自底向上
n3 = p3
n3_down = self.downsample1(n3)
n4 = torch.cat([n3_down, p4], dim=1)
n4 = self.conv_set2(n4)
n4_down = self.downsample2(n4)
n5 = torch.cat([n4_down, p5], dim=1)
n5 = self.conv_set3(n5)
return n3, n4, n5
7. Mosaic数据增强
7.1 原理
将4张图像拼接成一张进行训练:
7.2 优势
- 增加数据多样性:单次训练看到4张图像
- 增加小目标:缩小后更多小目标
- 减少Batch Size需求:一张图包含多场景
- 增强BN统计:更丰富的batch统计
7.3 实现
def mosaic_augment(images, labels, img_size=608):
"""Mosaic数据增强"""
s = img_size
# 随机中心点
xc, yc = [int(random.uniform(s*0.25, s*0.75)) for _ in range(2)]
mosaic_img = np.zeros((s, s, 3), dtype=np.uint8)
mosaic_labels = []
for i, (img, label) in enumerate(zip(images, labels)):
h, w = img.shape[:2]
# 确定放置位置
if i == 0: # 左上
x1, y1, x2, y2 = max(xc-w, 0), max(yc-h, 0), xc, yc
elif i == 1: # 右上
x1, y1, x2, y2 = xc, max(yc-h, 0), min(xc+w, s), yc
elif i == 2: # 左下
x1, y1, x2, y2 = max(xc-w, 0), yc, xc, min(yc+h, s)
else: # 右下
x1, y1, x2, y2 = xc, yc, min(xc+w, s), min(yc+h, s)
# 调整图像和标签
mosaic_img[y1:y2, x1:x2] = resize_and_crop(img, x2-x1, y2-y1)
adjusted_labels = adjust_labels(label, x1, y1, x2-x1, y2-y1, s)
mosaic_labels.extend(adjusted_labels)
return mosaic_img, mosaic_labels
8. CIoU损失
8.1 IoU系列演进
IoU→GIoU→DIoU→CIoU\text{IoU} \rightarrow \text{GIoU} \rightarrow \text{DIoU} \rightarrow \text{CIoU}IoU→GIoU→DIoU→CIoU
8.2 CIoU定义
CIoU=IoU−ρ2(b,bgt)c2−αv\text{CIoU} = \text{IoU} - \frac{\rho^2(b, b^{gt})}{c^2} - \alpha vCIoU=IoU−c2ρ2(b,bgt)−αv
其中:
- ρ(b,bgt)\rho(b, b^{gt})ρ(b,bgt):预测框和GT框中心点距离
- ccc:最小包围框对角线长度
- vvv:长宽比一致性
v=4π2(arctanwgthgt−arctanwh)2v = \frac{4}{\pi^2}\left(\arctan\frac{w^{gt}}{h^{gt}} - \arctan\frac{w}{h}\right)^2v=π24(arctanhgtwgt−arctanhw)2
α=v(1−IoU)+v\alpha = \frac{v}{(1 - \text{IoU}) + v}α=(1−IoU)+vv
8.3 CIoU损失
LCIoU=1−CIoU\mathcal{L}_{CIoU} = 1 - \text{CIoU}LCIoU=1−CIoU
8.4 实现
def ciou_loss(pred_boxes, target_boxes):
# IoU
inter = intersection(pred_boxes, target_boxes)
union = area(pred_boxes) + area(target_boxes) - inter
iou = inter / union
# 中心点距离
pred_center = center(pred_boxes)
target_center = center(target_boxes)
rho2 = (pred_center - target_center).pow(2).sum(dim=-1)
# 最小包围框对角线
enclose = enclosing_box(pred_boxes, target_boxes)
c2 = diagonal(enclose).pow(2)
# 长宽比
v = (4 / math.pi**2) * (
torch.atan(target_boxes[..., 2] / target_boxes[..., 3]) -
torch.atan(pred_boxes[..., 2] / pred_boxes[..., 3])
).pow(2)
alpha = v / (1 - iou + v + 1e-7)
ciou = iou - rho2 / c2 - alpha * v
return 1 - ciou
9. 其他技术细节
9.1 DropBlock
与Dropout类似,但丢弃连续区域:
class DropBlock(nn.Module):
def __init__(self, block_size=7, drop_prob=0.1):
super().__init__()
self.block_size = block_size
self.drop_prob = drop_prob
def forward(self, x):
if not self.training:
return x
# 计算gamma
gamma = self.drop_prob / (self.block_size ** 2)
gamma *= x.shape[2] * x.shape[3] / (
(x.shape[2] - self.block_size + 1) *
(x.shape[3] - self.block_size + 1)
)
# 生成mask
mask = (torch.rand_like(x[:, :1]) < gamma).float()
mask = F.max_pool2d(mask, self.block_size, stride=1,
padding=self.block_size//2)
mask = 1 - mask
# 应用mask并归一化
return x * mask * mask.numel() / mask.sum()
9.2 Label Smoothing
ysmooth=(1−ϵ)⋅yonehot+ϵKy_{smooth} = (1 - \epsilon) \cdot y_{onehot} + \frac{\epsilon}{K}ysmooth=(1−ϵ)⋅yonehot+Kϵ
其中 ϵ\epsilonϵ 通常取0.1,KKK 是类别数。
9.3 自对抗训练(SAT)
- 第一次前向传播,计算损失
- 反向传播到输入图像,生成对抗噪声
- 将对抗图像作为新的训练样本
9.4 DIoU-NMS
使用DIoU代替IoU进行NMS:
RDIoU=IoU−ρ2(M,Bi)c2R_{DIoU} = \text{IoU} - \frac{\rho^2(M, B_i)}{c^2}RDIoU=IoU−c2ρ2(M,Bi)
可以更好地处理相邻但不重叠的框。
10. 完整网络架构
10.1 YOLOv4架构图
输入: 608×608×3
↓
CSPDarknet53 (Backbone)
├── Stage3 → 76×76×256
├── Stage4 → 38×38×512
└── Stage5 → 19×19×1024
↓
SPP模块 → 19×19×512
↓
PANet (Neck)
├── P3: 76×76×128
├── P4: 38×38×256
└── P5: 19×19×512
↓
YOLOv3 Head
├── 76×76×255 (小目标)
├── 38×38×255 (中目标)
└── 19×19×255 (大目标)
10.2 输出格式
Output=B×(5+C)=3×(5+80)=255\text{Output} = B \times (5 + C) = 3 \times (5 + 80) = 255Output=B×(5+C)=3×(5+80)=255
11. 训练配置
11.1 超参数
| 超参数 | 值 |
|---|---|
| 输入尺寸 | 608×608 |
| Batch Size | 64(8×8 subdivisions) |
| Momentum | 0.949 |
| Weight Decay | 0.0005 |
| Learning Rate | 0.001 |
| Warmup | 1000 iterations |
| 总迭代 | 500500 |
11.2 学习率调度
- 余弦退火(Cosine Annealing)
- 在第400000和450000次迭代时降低
11.3 数据增强组合
- Mosaic
- 随机缩放(0.5~1.5)
- 色彩抖动
- 随机翻转
- CutMix(概率0.5)
12. 实验结果
12.1 COCO test-dev
| 方法 | mAP@0.5 | mAP@0.5:0.95 | FPS (V100) |
|---|---|---|---|
| YOLOv3 | 57.9 | 33.0 | 20 |
| EfficientDet-D2 | 55.1 | 43.0 | 26 |
| YOLOv4 | 65.7 | 43.5 | 38 |
12.2 消融实验
| 配置 | mAP@0.5:0.95 |
|---|---|
| Baseline (YOLOv3) | 36.8 |
| + CSP | 38.1 |
| + Mish | 38.5 |
| + SPP | 39.2 |
| + PAN | 40.3 |
| + Mosaic | 42.1 |
| + CIoU | 43.0 |
| + SAT | 43.5 |
13. 总结
YOLOv4通过系统性整合各种技术,实现了显著的性能提升:
| 类别 | 采用技术 | 效果 |
|---|---|---|
| Backbone | CSPDarknet53 + Mish | 更强特征提取 |
| Neck | SPP + PAN | 更好特征融合 |
| Head | YOLOv3 Head | 多尺度检测 |
| BoF | Mosaic, CutMix, SAT | 训练增强 |
| BoS | CIoU, DIoU-NMS | 精度提升 |
YOLOv4证明了通过合理组合现有技术,可以达到SOTA性能。
参考文献
- Bochkovskiy, A., et al. “YOLOv4: Optimal Speed and Accuracy of Object Detection.” arXiv 2020.
- Wang, C. Y., et al. “CSPNet: A New Backbone that can Enhance Learning Capability of CNN.” CVPR 2020.
- Misra, D. “Mish: A Self Regularized Non-Monotonic Activation Function.” BMVC 2020.
- Zheng, Z., et al. “Distance-IoU Loss: Faster and Better Learning for Bounding Box Regression.” AAAI 2020.
上一篇:【YOLO系列05】YOLOv3详解——多尺度预测与Darknet-53
下一篇:【YOLO系列07】YOLOv5详解——PyTorch时代的工程化典范
更多推荐



所有评论(0)