【YOLO系列07】YOLOv5详解——PyTorch时代的工程化典范

本文是YOLO系列博客的第七篇,深入解析YOLOv5的网络架构,包括Focus模块、CSP结构、自适应Anchor、完善的训练策略以及Ultralytics的工程化实现。

1. 引言

2020年6月,Ultralytics公司发布了YOLOv5。尽管没有正式论文,YOLOv5凭借其优秀的工程实现易用的API活跃的社区维护,成为目前最流行的目标检测框架之一。

项目信息

  • 作者:Glenn Jocher(Ultralytics)
  • 框架:PyTorch
  • 代码:https://github.com/ultralytics/yolov5
  • Star:40K+

2. YOLOv5的争议与定位

2.1 命名争议

YOLOv5发布时引发了社区争议:

  • 没有发表正式论文
  • 性能数据受质疑
  • 是否有资格称为"v5"

2.2 实际价值

尽管有争议,YOLOv5的价值不可否认:

优势说明
工程化完善的训练、评估、部署流程
易用性简洁的API,快速上手
社区活跃的维护和更新
多格式导出ONNX、TensorRT、CoreML等
文档详尽的使用文档

3. 网络架构总览

3.1 整体结构

检测头

Detect

三尺度输出

颈部

PANet

特征融合

骨干网络

Focus

CSPDarknet

SPPF

3.2 模型变体

YOLOv5提供5种规模的模型:

模型深度倍数宽度倍数参数量FLOPsmAP
YOLOv5n0.330.251.9M4.5G28.0
YOLOv5s0.330.507.2M16.5G37.4
YOLOv5m0.670.7521.2M49.0G45.4
YOLOv5l1.001.0046.5M109.1G49.0
YOLOv5x1.331.2586.7M205.7G50.7

缩放公式

channels=base_channels×width_multiple\text{channels} = \text{base\_channels} \times \text{width\_multiple}channels=base_channels×width_multiple
num_blocks=base_blocks×depth_multiple\text{num\_blocks} = \text{base\_blocks} \times \text{depth\_multiple}num_blocks=base_blocks×depth_multiple

4. Focus模块

4.1 设计动机

将空间信息重组到通道维度,在不丢失信息的情况下进行下采样。

4.2 工作原理

输出

Focus切片

输入

concat

640×640×3

patch 1

patch 2

patch 3

patch 4

320×320×12

Conv → 320×320×64

4.3 切片方式

将图像按照步长2进行切片:

class Focus(nn.Module):
    def __init__(self, c1, c2, k=1):
        super().__init__()
        self.conv = Conv(c1 * 4, c2, k)

    def forward(self, x):
        # x: (b, c, h, w) -> (b, 4c, h/2, w/2)
        return self.conv(torch.cat([
            x[..., ::2, ::2],    # 左上
            x[..., 1::2, ::2],   # 右上
            x[..., ::2, 1::2],   # 左下
            x[..., 1::2, 1::2]   # 右下
        ], dim=1))

4.4 Focus vs 卷积

方式输入输出信息保留
Conv 6×6/2640×640×3320×320×64部分重叠
Focus640×640×3320×320×64完全保留

注意:YOLOv5 v6.0后,Focus被替换为普通6×6卷积以提升部署兼容性。

5. CSP结构

5.1 C3模块

YOLOv5使用C3模块(CSP Bottleneck with 3 convolutions):

C3模块

输入

Conv 1×1

Conv 1×1

Bottleneck ×n

Concat

Conv 1×1

输出

5.2 实现代码

class Bottleneck(nn.Module):
    def __init__(self, c1, c2, shortcut=True, g=1, e=0.5):
        super().__init__()
        c_ = int(c2 * e)
        self.cv1 = Conv(c1, c_, 1, 1)
        self.cv2 = Conv(c_, c2, 3, 1, g=g)
        self.add = shortcut and c1 == c2

    def forward(self, x):
        return x + self.cv2(self.cv1(x)) if self.add else self.cv2(self.cv1(x))

class C3(nn.Module):
    def __init__(self, c1, c2, n=1, shortcut=True, g=1, e=0.5):
        super().__init__()
        c_ = int(c2 * e)
        self.cv1 = Conv(c1, c_, 1, 1)
        self.cv2 = Conv(c1, c_, 1, 1)
        self.cv3 = Conv(2 * c_, c2, 1)
        self.m = nn.Sequential(*[Bottleneck(c_, c_, shortcut, g, e=1.0) for _ in range(n)])

    def forward(self, x):
        return self.cv3(torch.cat([self.m(self.cv1(x)), self.cv2(x)], dim=1))

6. SPPF模块

6.1 与SPP对比

模块结构速度
SPP并行多尺度池化较慢
SPPF串行多次5×5池化快2倍

6.2 SPPF结构

输入

Conv

MaxPool 5×5

MaxPool 5×5

MaxPool 5×5

Concat

Conv

6.3 实现

class SPPF(nn.Module):
    def __init__(self, c1, c2, k=5):
        super().__init__()
        c_ = c1 // 2
        self.cv1 = Conv(c1, c_, 1, 1)
        self.cv2 = Conv(c_ * 4, c2, 1, 1)
        self.m = nn.MaxPool2d(kernel_size=k, stride=1, padding=k // 2)

    def forward(self, x):
        x = self.cv1(x)
        y1 = self.m(x)
        y2 = self.m(y1)
        return self.cv2(torch.cat([x, y1, y2, self.m(y2)], dim=1))

7. 自适应Anchor

7.1 自动计算

YOLOv5在训练开始时自动为数据集计算最优Anchor:

def kmean_anchors(dataset, n=9, img_size=640, thr=4.0, gen=1000):
    """使用K-Means计算最优Anchor"""
    from scipy.cluster.vq import kmeans

    # 获取所有GT框的宽高
    shapes = np.concatenate([
        l[:, 3:5] * img_size for l in dataset.labels
    ], axis=0)

    # K-Means聚类
    anchors, _ = kmeans(shapes, n)

    # 按面积排序
    anchors = anchors[np.argsort(anchors.prod(1))]

    return anchors

7.2 Anchor分配

YOLOv5的默认Anchor(640×640输入):

尺度特征图Anchors (w, h)
P3/880×80(10,13), (16,30), (33,23)
P4/1640×40(30,61), (62,45), (59,119)
P5/3220×20(116,90), (156,198), (373,326)

7.3 正样本分配

使用基于Anchor的匹配策略:

match=max⁡(wgtwanchor,wanchorwgt)<anchor_t\text{match} = \max\left(\frac{w_{gt}}{w_{anchor}}, \frac{w_{anchor}}{w_{gt}}\right) < \text{anchor\_t}match=max(wanchorwgt,wgtwanchor)<anchor_t

同时检查宽度和高度,anchor_t默认为4.0。

8. 损失函数

8.1 总损失

L=λboxLbox+λobjLobj+λclsLcls\mathcal{L} = \lambda_{box}\mathcal{L}_{box} + \lambda_{obj}\mathcal{L}_{obj} + \lambda_{cls}\mathcal{L}_{cls}L=λboxLbox+λobjLobj+λclsLcls

默认权重:λbox=0.05\lambda_{box}=0.05λbox=0.05λobj=1.0\lambda_{obj}=1.0λobj=1.0λcls=0.5\lambda_{cls}=0.5λcls=0.5

8.2 边界框损失

使用CIoU Loss:

Lbox=1−CIoU\mathcal{L}_{box} = 1 - \text{CIoU}Lbox=1CIoU

8.3 目标损失

使用BCE Loss,带有目标置信度:

Lobj=BCE(o^,o⋅IoU)\mathcal{L}_{obj} = \text{BCE}(\hat{o}, o \cdot \text{IoU})Lobj=BCE(o^,oIoU)

其中 IoU\text{IoU}IoU 是预测框与GT的IoU,作为软标签。

8.4 分类损失

使用BCE Loss(多标签):

Lcls=BCE(p^,p)\mathcal{L}_{cls} = \text{BCE}(\hat{p}, p)Lcls=BCE(p^,p)

8.5 平衡权重

不同尺度的损失使用不同权重:

尺度权重
P3 (80×80)4.0
P4 (40×40)1.0
P5 (20×20)0.4

小目标尺度权重更高,以平衡样本数量。

9. 数据增强

9.1 Mosaic

def load_mosaic(self, index):
    """4图Mosaic增强"""
    labels4 = []
    s = self.img_size

    # 随机中心点
    yc, xc = [int(random.uniform(s*0.5, s*1.5)) for _ in range(2)]

    # 随机选择3张其他图像
    indices = [index] + random.choices(range(len(self)), k=3)

    for i, idx in enumerate(indices):
        img, labels = self.load_image(idx)
        h, w = img.shape[:2]

        # 放置位置
        if i == 0:  # 左上
            x1a, y1a, x2a, y2a = max(xc-w, 0), max(yc-h, 0), xc, yc
            x1b, y1b, x2b, y2b = w-(x2a-x1a), h-(y2a-y1a), w, h
        # ... 其他位置类似

        img4[y1a:y2a, x1a:x2a] = img[y1b:y2b, x1b:x2b]
        labels4.append(adjusted_labels)

    return img4, np.concatenate(labels4, 0)

9.2 MixUp

def mixup(img1, labels1, img2, labels2, alpha=0.5):
    """MixUp增强"""
    r = np.random.beta(alpha, alpha)
    img = (img1 * r + img2 * (1 - r)).astype(np.uint8)
    labels = np.concatenate([labels1, labels2], 0)
    return img, labels

9.3 Copy-Paste

def copy_paste(img, labels, segments, p=0.5):
    """Copy-Paste增强(需要分割标注)"""
    n = len(segments)
    if p and n:
        for j in random.sample(range(n), k=int(n * p)):
            # 随机缩放和翻转
            # 粘贴到新位置
            pass
    return img, labels

9.4 增强策略总结

增强方法默认概率作用
Mosaic1.0多图融合
MixUp0.0图像混合
Copy-Paste0.0实例粘贴
HSV增强1.0色彩变换
翻转0.5水平翻转
缩放0.5随机缩放
平移0.1随机平移

10. 训练策略

10.1 学习率调度

使用余弦退火(Cosine Annealing):

ηt=ηmin+12(ηmax−ηmin)(1+cos⁡(tTπ))\eta_t = \eta_{min} + \frac{1}{2}(\eta_{max} - \eta_{min})(1 + \cos(\frac{t}{T}\pi))ηt=ηmin+21(ηmaxηmin)(1+cos(Ttπ))

10.2 Warmup

前3个epoch使用线性warmup:

# 学习率warmup
if ni < nw:
    xi = [0, nw]
    for j, x in enumerate(optimizer.param_groups):
        x['lr'] = np.interp(ni, xi, [warmup_bias_lr if j == 2 else 0.0, x['initial_lr'] * lf(epoch)])
        if 'momentum' in x:
            x['momentum'] = np.interp(ni, xi, [warmup_momentum, momentum])

10.3 EMA(指数移动平均)

class ModelEMA:
    def __init__(self, model, decay=0.9999):
        self.ema = deepcopy(model).eval()
        self.decay = decay

    def update(self, model):
        with torch.no_grad():
            for ema_p, p in zip(self.ema.parameters(), model.parameters()):
                ema_p.data.mul_(self.decay).add_(p.data, alpha=1 - self.decay)

10.4 训练超参数

# hyp.scratch-low.yaml
lr0: 0.01        # 初始学习率
lrf: 0.01        # 最终学习率 = lr0 * lrf
momentum: 0.937
weight_decay: 0.0005
warmup_epochs: 3.0
warmup_momentum: 0.8
warmup_bias_lr: 0.1
box: 0.05        # 边界框损失权重
cls: 0.5         # 分类损失权重
obj: 1.0         # 目标损失权重
anchor_t: 4.0    # Anchor匹配阈值

11. 推理与部署

11.1 推理流程

from ultralytics import YOLO

# 加载模型
model = YOLO('yolov5s.pt')

# 推理
results = model('image.jpg')

# 获取结果
for result in results:
    boxes = result.boxes.xyxy      # 边界框
    scores = result.boxes.conf      # 置信度
    classes = result.boxes.cls      # 类别

11.2 模型导出

# 导出ONNX
python export.py --weights yolov5s.pt --include onnx

# 导出TensorRT
python export.py --weights yolov5s.pt --include engine --device 0

# 导出CoreML
python export.py --weights yolov5s.pt --include coreml

# 导出TFLite
python export.py --weights yolov5s.pt --include tflite

11.3 支持的导出格式

格式用途
PyTorch训练和研究
ONNX跨平台部署
TensorRTNVIDIA GPU加速
CoreMLiOS/macOS部署
TFLiteAndroid/嵌入式
OpenVINOIntel硬件
PaddlePaddle百度生态

12. 完整配置文件

12.1 模型配置 (yolov5s.yaml)

# YOLOv5s
nc: 80  # 类别数
depth_multiple: 0.33  # 深度倍数
width_multiple: 0.50  # 宽度倍数
anchors:
  - [10,13, 16,30, 33,23]     # P3/8
  - [30,61, 62,45, 59,119]    # P4/16
  - [116,90, 156,198, 373,326]  # P5/32

backbone:
  # [from, number, module, args]
  [[-1, 1, Conv, [64, 6, 2, 2]],      # 0-P1/2
   [-1, 1, Conv, [128, 3, 2]],        # 1-P2/4
   [-1, 3, C3, [128]],
   [-1, 1, Conv, [256, 3, 2]],        # 3-P3/8
   [-1, 6, C3, [256]],
   [-1, 1, Conv, [512, 3, 2]],        # 5-P4/16
   [-1, 9, C3, [512]],
   [-1, 1, Conv, [1024, 3, 2]],       # 7-P5/32
   [-1, 3, C3, [1024]],
   [-1, 1, SPPF, [1024, 5]],          # 9
  ]

head:
  [[-1, 1, Conv, [512, 1, 1]],
   [-1, 1, nn.Upsample, [None, 2, 'nearest']],
   [[-1, 6], 1, Concat, [1]],
   [-1, 3, C3, [512, False]],

   [-1, 1, Conv, [256, 1, 1]],
   [-1, 1, nn.Upsample, [None, 2, 'nearest']],
   [[-1, 4], 1, Concat, [1]],
   [-1, 3, C3, [256, False]],         # 17 (P3/8-small)

   [-1, 1, Conv, [256, 3, 2]],
   [[-1, 14], 1, Concat, [1]],
   [-1, 3, C3, [512, False]],         # 20 (P4/16-medium)

   [-1, 1, Conv, [512, 3, 2]],
   [[-1, 10], 1, Concat, [1]],
   [-1, 3, C3, [1024, False]],        # 23 (P5/32-large)

   [[17, 20, 23], 1, Detect, [nc, anchors]],
  ]

13. 实验结果

13.1 COCO val2017

模型尺寸mAP@0.5mAP@0.5:0.95速度(V100)
YOLOv5n64045.728.00.6ms
YOLOv5s64056.837.41.0ms
YOLOv5m64064.145.42.2ms
YOLOv5l64067.349.03.8ms
YOLOv5x64068.950.76.3ms

13.2 与其他模型对比

模型参数量mAPFPS
YOLOv464M43.538
YOLOv5l47M49.050
EfficientDet-D28M43.026

14. 总结

YOLOv5的成功在于:

方面特点
架构CSP + SPPF + PANet的成熟组合
工程完善的训练、评估、部署流程
易用简洁API,丰富文档
社区持续更新,活跃维护
部署多格式导出,跨平台支持

YOLOv5虽然没有论文,但其工程化实现为目标检测的实际应用树立了标杆。


参考资源

  • 官方仓库:https://github.com/ultralytics/yolov5
  • 官方文档:https://docs.ultralytics.com/yolov5/
  • 预训练模型:https://github.com/ultralytics/yolov5/releases

上一篇:【YOLO系列06】YOLOv4详解——Bag of Freebies与Bag of Specials
下一篇:【YOLO系列08】YOLOv6详解——美团的工业级检测器

Logo

助力广东及东莞地区开发者,代码托管、在线学习与竞赛、技术交流与分享、资源共享、职业发展,成为松山湖开发者首选的工作与学习平台

更多推荐