YOLOv5介绍
YOLOv5 是 YOLO 系列的第五个版本,由 Ultralytics 团队发布。虽然 YOLOv5 并非 Joseph Redmon 原团队发布,但它在 YOLOv4 的基础上进行了重要的优化和改进,成为了深度学习目标检测领域中的热门模型之一。YOLOv5 的优势不仅体现在其性能上,还包括其简洁易用、部署便捷的特点。相较于 YOLOv4,YOLOv5 对于代码框架的重构、推理速度的提升,以及模型的轻量化等方面都有显著改进。
相比 YOLOv4 的改进与优势
-
PyTorch 原生实现
YOLOv5 是基于 PyTorch 框架实现的,减少了对 C 语言和 CUDA 的依赖,使得开发、训练和部署更加简洁。相比于 YOLOv4 使用的 Darknet 框架,YOLOv5 的 PyTorch 实现更易于在现代开发环境中集成。 -
模型尺寸优化
YOLOv5 提供了多个不同大小的模型,包括 YOLOv5s、YOLOv5m、YOLOv5l、YOLOv5x,它们适应不同场景和硬件设备的需求。YOLOv5s
是轻量级版本,适合嵌入式和移动端,YOLOv5x
是高性能版本,用于高精度任务。 -
自动学习锚点
YOLOv5 引入了自动学习锚点的机制,通过在训练过程中动态调整锚点,使得预测框的精度提升,减少了手动调整锚点的工作。 -
高效的训练策略
YOLOv5 引入了一些新的训练策略,如 混合精度训练 (Mixed Precision Training),可以有效减少显存占用,提高训练速度。此外,YOLOv5 默认使用了 数据增强 和 自适应图像缩放,进一步提高了模型的泛化能力。 -
支持导出多种推理格式
YOLOv5 支持将模型导出为多种格式,例如 ONNX、TensorRT、CoreML 等,使得部署更加方便,尤其是可以在移动设备、嵌入式设备和云环境中高效运行。 -
速度与性能的平衡
相较于 YOLOv4,YOLOv5 在速度和性能的平衡上表现更加出色,尤其是在轻量化版本上,如 YOLOv5s。在相同硬件条件下,YOLOv5 通常拥有更快的推理速度。
数据增强
YOLOv5 在数据增强方面引入了多种技术,主要目的是增加训练样本的多样性,从而提升模型的泛化能力和鲁棒性。以下是 YOLOv5 使用的几种主要数据增强方式:
1. Mosaic 数据增强
Mosaic 是 YOLOv5 中最重要的增强方式之一,它最早在 YOLOv4 中被引入。这种增强方法将 4 张图像拼接 成一张图像,通过随机缩放、裁剪和排列这四张图片来生成新的训练样本。这样可以让模型看到不同尺度和位置的物体,增加训练样本的多样性,并且帮助模型更好地学习全局和局部特征。
Mosaic 数据增强的优点包括:
- 增加了训练数据的多样性。
- 让模型能够看到图片的不同部分,减少了过拟合的风险。
- 更好地处理多目标的场景,增强对目标的定位能力。
2. MixUp
MixUp 是另一种常见的数据增强技术,它通过将两张图片及其标签按一定比例线性组合生成新的训练样本。MixUp 的主要作用是使模型在处理物体重叠时更加鲁棒。
具体而言,MixUp 操作为每一对图片 (x1, x2)
生成一个新的样本 x_mix = λ * x1 + (1 - λ) * x2
,其中 λ
是一个随机采样的系数。标签同样按同样比例进行线性组合。
MixUp 的优势:
- 减少过拟合风险。
- 使得模型更加鲁棒,特别是在物体有部分重叠的情况下。
- 增强了模型处理边界目标的能力。
3. 颜色空间增强
YOLOv5 还通过颜色增强的方式增加训练样本的多样性,常见的颜色增强方式包括:
- 亮度调整:随机改变图像的亮度,使得模型能够应对不同光照条件。
- 对比度调整:随机增强或减少图像的对比度,增加模型在不同对比度下的鲁棒性。
- 饱和度调整:随机调整图像的颜色饱和度,模拟不同的色彩环境。
- 色调调整:随机旋转图像的色调,从而增加对颜色变化的鲁棒性。
4. 随机裁剪与缩放
YOLOv5 中通过随机缩放、随机裁剪的方式对图像进行变换。这些变换有助于提升模型对物体不同大小和部分遮挡的鲁棒性。通过缩放和裁剪,模型可以看到不同大小的目标物体,提高对多尺度目标的检测能力。
- 随机缩放:通过放大或缩小图像来增强模型在不同尺度下的目标检测能力。
- 随机裁剪:裁剪掉部分图像,迫使模型在部分可见物体的情况下依然能够进行检测。
5. 翻转与旋转
YOLOv5 支持对图像进行随机水平翻转和旋转,这种增强方式主要用于增加模型的视角多样性,使得模型能够应对物体在不同视角下的检测任务。
- 水平翻转:随机将图像左右翻转,增强模型处理镜像图像的能力。
- 旋转:随机对图像进行旋转,可以让模型适应斜向或倒置的物体。
6. 随机擦除(Random Erase)
随机擦除是一种用于模拟遮挡物体的增强方式。它通过随机擦除图像的一部分,使得模型能够学习如何在部分信息缺失的情况下仍然进行有效的目标检测。这种方式特别适合处理目标被遮挡的场景。
7. 自适应图片缩放
YOLOv5 还采用了一种**自适应图片缩放(Adaptive Image Scaling)**的策略,通过动态调整图片的缩放比例,确保在输入模型时图片的长宽比保持一致。这种方式减少了图像变形对检测效果的影响,使得模型对不同尺寸的图片更加鲁棒。
YOLOv5 的数据增强策略结合了多种先进技术,包括 Mosaic、MixUp、颜色增强、随机裁剪与缩放、翻转、旋转、随机擦除等,进一步提高了模型的泛化能力。这些数据增强方法使得 YOLOv5 能够在不同环境、视角、光照等复杂情况下保持良好的检测效果。
核心代码展示
以下是 YOLOv5 的核心部分代码,包括主干网络(CSP 模块的改进)、FPN(特征金字塔网络)、以及 YOLO 预测头的实现。
import torch
import torch.nn as nn
# 1. 基础卷积模块,包含卷积、BN 和 SiLU 激活函数
class ConvBlock(nn.Module):
def __init__(self, in_channels, out_channels, kernel_size, stride, padding):
super(ConvBlock, self).__init__()
self.conv = nn.Conv2d(in_channels, out_channels, kernel_size, stride, padding, bias=False)
self.bn = nn.BatchNorm2d(out_channels)
self.activation = nn.SiLU() # YOLOv5 中使用 SiLU 激活函数(Sigmoid-Weighted Linear Units)
def forward(self, x):
return self.activation(self.bn(self.conv(x)))
# 2. Bottleneck 模块,类似于 YOLOv4 中的 CSPBlock
class Bottleneck(nn.Module):
def __init__(self, in_channels, out_channels, shortcut=True):
super(Bottleneck, self).__init__()
hidden_channels = out_channels // 2
self.conv1 = ConvBlock(in_channels, hidden_channels, 1, 1, 0)
self.conv2 = ConvBlock(hidden_channels, out_channels, 3, 1, 1)
self.use_shortcut = shortcut and in_channels == out_channels
def forward(self, x):
if self.use_shortcut:
return x + self.conv2(self.conv1(x)) # 残差连接
else:
return self.conv2(self.conv1(x))
# 3. C3 模块,YOLOv5 的核心改进,借鉴了 CSPNet 的结构
class C3(nn.Module):
def __init__(self, in_channels, out_channels, num_bottlenecks=3, shortcut=True):
super(C3, self).__init__()
hidden_channels = out_channels // 2
self.conv1 = ConvBlock(in_channels, hidden_channels, 1, 1, 0)
self.conv2 = ConvBlock(in_channels, hidden_channels, 1, 1, 0)
self.bottlenecks = nn.Sequential(
*[Bottleneck(hidden_channels, hidden_channels, shortcut) for _ in range(num_bottlenecks)]
)
self.conv3 = ConvBlock(2 * hidden_channels, out_channels, 1, 1, 0)
def forward(self, x):
x1 = self.conv1(x)
x2 = self.conv2(x)
x1 = self.bottlenecks(x1)
return self.conv3(torch.cat([x1, x2], dim=1)) # 将特征拼接后再进行卷积
# 4. YOLOv5 主干网络
class CSPDarknet53(nn.Module):
def __init__(self):
super(CSPDarknet53, self).__init__()
self.conv1 = ConvBlock(3, 32, 3, 1, 1)
self.conv2 = ConvBlock(32, 64, 3, 2, 1)
self.c3_1 = C3(64, 128, num_bottlenecks=3)
self.c3_2 = C3(128, 256, num_bottlenecks=9)
self.c3_3 = C3(256, 512, num_bottlenecks=9)
self.c3_4 = C3(512, 1024, num_bottlenecks=3)
def forward(self, x):
x = self.conv1(x)
x = self.conv2(x)
x_52x52 = self.c3_1(x)
x_26x26 = self.c3_2(x_52x52)
x_13x13 = self.c3_3(x_26x26)
x_final = self.c3_4(x_13x13)
return x_52x52, x_26x26, x_13x13, x_final
# 5. FPN + PANet 结构,增强多尺度特征聚合
class FPN_PANet(nn.Module):
def __init__(self, num_classes):
super(FPN_PANet, self).__init__()
self.num_classes = num_classes
# 上采样和下采样
self.upsample = nn.Upsample(scale_factor=2, mode='nearest')
# 通道数减少一半,用于特征融合
self.reduce_layer_1 = ConvBlock(1024, 512, 1, 1, 0)
self.reduce_layer_2 = ConvBlock(512, 256, 1, 1, 0)
# FPN(特征金字塔网络)部分
self.fpn_conv1 = ConvBlock(512, 512, 3, 1, 1)
self.fpn_conv2 = ConvBlock(256, 256, 3, 1, 1)
# PANet(路径聚合网络)部分
self.panet_conv1 = ConvBlock(256, 256, 3, 1, 1)
self.panet_conv2 = ConvBlock(512, 512, 3, 1, 1)
self.panet_conv3 = ConvBlock(1024, 1024, 3, 1, 1)
# 最终预测头
self.yolo_head_13x13 = YOLOHead(1024, num_classes)
self.yolo_head_26x26 = YOLOHead(512, num_classes)
self.yolo_head_52x52 = YOLOHead(256, num_classes)
def forward(self, x_52x52, x_26x26, x_13x13, x_final):
# FPN 上采样融合
x_fpn_26x26 = self.upsample(self.reduce_layer_1(x_final)) + x_13x13
x_fpn_52x52 = self.upsample(self.reduce_layer_2(x_fpn_26x26)) + x_26x26
# PANet 下采样融合
x_panet_13x13 = self.panet_conv3(x_fpn_26x26)
x_panet_26x26 = self.panet_conv2(x_fpn_26x26)
x_panet_52x52 = self.panet_conv1(x_fpn_52x52
)
# 预测输出
yolo_out_13x13 = self.yolo_head_13x13(x_panet_13x13)
yolo_out_26x26 = self.yolo_head_26x26(x_panet_26x26)
yolo_out_52x52 = self.yolo_head_52x52(x_panet_52x52)
return [yolo_out_52x52, yolo_out_26x26, yolo_out_13x13]
# 6. YOLO 预测头定义
class YOLOHead(nn.Module):
def __init__(self, in_channels, num_classes):
super(YOLOHead, self).__init__()
self.conv = ConvBlock(in_channels, in_channels * 2, 3, 1, 1)
self.pred = nn.Conv2d(in_channels * 2, 3 * (num_classes + 5), 1, 1, 0)
def forward(self, x):
x = self.conv(x)
return self.pred(x)
# 7. YOLOv5 完整模型
class YOLOv5(nn.Module):
def __init__(self, num_classes):
super(YOLOv5, self).__init__()
self.backbone = CSPDarknet53()
self.fpn_panet = FPN_PANet(num_classes)
def forward(self, x):
x_52x52, x_26x26, x_13x13, x_final = self.backbone(x)
return self.fpn_panet(x_52x52, x_26x26, x_13x13, x_final)
代码解释
-
SiLU 激活函数
YOLOv5 使用 SiLU 激活函数(Sigmoid-Weighted Linear Units),相比于 YOLOv4 中的 Mish,SiLU 更加高效,并且在性能上有微小提升。 -
C3 模块
YOLOv5 的核心结构是 C3 模块,它借鉴了 YOLOv4 的 CSPNet,并进一步优化,使用了更轻量的结构,在性能与速度之间达到了良好的平衡。 -
FPN + PANet
YOLOv5 使用 FPN 和 PANet 组合来处理多尺度特征,这使得它能够在不同尺度下更精确地检测物体,尤其对小目标和大目标都能很好地处理。 -
多尺度 YOLOHead
YOLOv5 保持了 YOLO 系列的多尺度检测特性,使用三个不同尺度的特征图进行目标检测,分别对应 13x13、26x26 和 52x52 的特征图。
结论
YOLOv5 相比 YOLOv4 进行了多项优化,特别是在框架简化、速度提升、锚点优化和轻量化设计方面具有显著优势。其基于 PyTorch 实现,便于扩展和部署,模型尺寸更灵活,适应性强,成为了目前目标检测任务中广泛应用的版本。