YOLOv3 (You Only Look Once, Version 3) 是 YOLO 系列目标检测模型的第三个版本,相较于 YOLOv2 有了显著的改进和增强,尤其在检测速度和精度上表现优异。YOLOv3 的设计目标是在保持高速的前提下提升检测的准确性和稳定性。下面是对 YOLOv3 改进和优势的介绍,以及 YOLOv3 核心部分的代码展示。
多尺度特征金字塔
YOLOv3 引入了 FPN(Feature Pyramid Networks),即在三个不同尺度上进行预测,从而提高了小目标的检测效果。YOLOv2 只是在最后的输出层做预测,而 YOLOv3 则在网络的多个层级上进行预测,以保证不同尺度的目标都能被检测到。
残差网络(Residual Connections)
YOLOv3 采用了 ResNet 中的残差连接(ResNet residual connections)来构建其网络结构,改进了网络的深度,使得模型可以更好地学习复杂的特征,而不会遇到深度网络中的梯度消失问题。
新的分类损失函数
YOLOv3 使用了基于 sigmoid 的分类损失函数,取代了 YOLOv2 的 softmax 函数。这使得 YOLOv3 能够更好地应对多标签分类问题,并提高了分类精度。
更多的 anchor boxes
YOLOv3 引入了更多的 anchor boxes,以适应更为复杂的目标和场景。YOLOv2 只用了 5 个 anchor boxes,而 YOLOv3 则增加到了 9 个,从而能更好地捕捉到目标的形状和大小变化。
Darknet-53 作为 Backbone
YOLOv3 使用了 Darknet-53 作为其 backbone 网络。这是一个由 53 层卷积层组成的深度网络,较 YOLOv2 的 Darknet-19 网络更深,并且在计算效率和准确率上实现了较好的平衡。
以下是 YOLOv3 核心部分的简化代码展示,主要展示如何使用 Darknet-53 作为主干网络,如何进行多尺度预测。
import torch
import torch.nn as nn
class YOLOv3(nn.Module):
def __init__(self, num_classes):
super(YOLOv3, self).__init__()
self.num_classes = num_classes
self.backbone = Darknet53()
self.head = YOLOHead(num_classes)
def forward(self, x):
# 从 Darknet-53 提取多尺度特征
features_52x52, features_26x26, features_13x13 = self.backbone(x)
# 在三个尺度上进行预测
yolo_output_52x52 = self.head(features_52x52)
yolo_output_26x26 = self.head(features_26x26)
yolo_output_13x13 = self.head(features_13x13)
return [yolo_output_52x52, yolo_output_26x26, yolo_output_13x13]
class Darknet53(nn.Module):
def __init__(self):
super(Darknet53, self).__init__()
# 构建 53 层的卷积网络
self.layers = self._make_layers()
def _make_layers(self):
layers = []
# 这里可以根据具体的 Darknet-53 结构构建每一层
return nn.Sequential(*layers)
def forward(self, x):
# 分别输出 52x52, 26x26 和 13x13 的特征图
out_52x52 = ...
out_26x26 = ...
out_13x13 = ...
return out_52x52, out_26x26, out_13x13
class YOLOHead(nn.Module):
def __init__(self, num_classes):
super(YOLOHead, self).__init__()
self.num_classes = num_classes
# 定义 YOLO head 的网络结构,包括分类和回归任务
def forward(self, x):
# 根据输入的特征图进行分类和位置预测
return ...
好的,以下是一个更加详细的 YOLOv3 实现示例,涵盖了其核心部分,包括主干网络 (Darknet-53)、特征提取、3 个尺度上的预测模块,以及 YOLO 检测头部的详细实现。
import torch
import torch.nn as nn
# 1. 卷积块,包含卷积、BN 和 Leaky ReLU
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.leaky_relu = nn.LeakyReLU(0.1)
def forward(self, x):
return self.leaky_relu(self.bn(self.conv(x)))
# 2. 残差块(Residual Block)
class ResidualBlock(nn.Module):
def __init__(self, in_channels):
super(ResidualBlock, self).__init__()
self.conv1 = ConvBlock(in_channels, in_channels // 2, 1, 1, 0) # 1x1 卷积
self.conv2 = ConvBlock(in_channels // 2, in_channels, 3, 1, 1) # 3x3 卷积
def forward(self, x):
return x + self.conv2(self.conv1(x)) # 残差连接
# 3. Darknet-53 Backbone
class Darknet53(nn.Module):
def __init__(self):
super(Darknet53, self).__init__()
self.layers = nn.Sequential(
ConvBlock(3, 32, 3, 1, 1), # 输入是 RGB 图像,通道数为 3
ConvBlock(32, 64, 3, 2, 1),
ResidualBlock(64),
ConvBlock(64, 128, 3, 2, 1),
self._make_residual_layers(128, 2), # 两个残差块
ConvBlock(128, 256, 3, 2, 1),
self._make_residual_layers(256, 8), # 8 个残差块
ConvBlock(256, 512, 3, 2, 1),
self._make_residual_layers(512, 8), # 8 个残差块
ConvBlock(512, 1024, 3, 2, 1),
self._make_residual_layers(1024, 4) # 4 个残差块
)
def _make_residual_layers(self, in_channels, num_blocks):
layers = []
for _ in range(num_blocks):
layers.append(ResidualBlock(in_channels))
return nn.Sequential(*layers)
def forward(self, x):
# 返回不同尺度的特征图
out_52x52 = self.layers[:6](x) # 尺度 52x52 的特征图
out_26x26 = self.layers[6:8](out_52x52) # 尺度 26x26 的特征图
out_13x13 = self.layers[8:](out_26x26) # 尺度 13x13 的特征图
return out_52x52, out_26x26, out_13x13
# 4. YOLOv3 Head,预测分类与边界框
class YOLOHead(nn.Module):
def __init__(self, in_channels, num_classes):
super(YOLOHead, self).__init__()
self.num_classes = num_classes
# 卷积层,用于对特征图进行进一步处理
self.conv1 = ConvBlock(in_channels, in_channels * 2, 3, 1, 1)
self.conv2 = nn.Conv2d(in_channels * 2, 3 * (num_classes + 5), 1, 1, 0) # 输出通道为 3*(num_classes + 5)
def forward(self, x):
x = self.conv1(x)
x = self.conv2(x)
return x.view(x.size(0), 3, self.num_classes + 5, x.size(2), x.size(3)) # 调整输出形状 (batch_size, 3, num_classes + 5, h, w)
# 5. YOLOv3 完整模型
class YOLOv3(nn.Module):
def __init__(self, num_classes):
super(YOLOv3, self).__init__()
self.num_classes = num_classes
# Darknet-53 Backbone
self.backbone = Darknet53()
# YOLO 检测头,分别在 3 个不同的尺度上进行预测
self.yolo_head_52x52 = YOLOHead(256, num_classes) # 小目标
self.yolo_head_26x26 = YOLOHead(512, num_classes) # 中目标
self.yolo_head_13x13 = YOLOHead(1024, num_classes) # 大目标
def forward(self, x):
# 从主干网络中获取多尺度特征图
out_52x52, out_26x26, out_13x13 = self.backbone(x)
# 分别在 52x52, 26x26 和 13x13 尺度上进行 YOLO 预测
yolo_output_52x52 = self.yolo_head_52x52(out_52x52)
yolo_output_26x26 = self.yolo_head_26x26(out_26x26)
yolo_output_13x13 = self.yolo_head_13x13(out_13x13)
return [yolo_output_52x52, yolo_output_26x26, yolo_output_13x13]
# 6. 预测结果解码函数 (非必须, 在推理阶段需要用到)
def decode_yolo_output(output, anchors, num_classes, input_size):
batch_size, num_anchors, grid_size, _ = output.size(0), output.size(1), output.size(2), output.size(3)
# 进一步解码预测结果,转换为实际坐标和类别
# 这个部分会涉及到 anchors、sigmoid 函数等细节
return decoded_predictions
ConvBlock 和 ResidualBlock
ConvBlock
定义了 YOLOv3 使用的卷积模块,包含卷积、Batch Normalization 和 Leaky ReLU 激活函数。ResidualBlock
是 YOLOv3 中的残差模块,通过残差连接,能够缓解梯度消失问题。Darknet-53 Backbone
Darknet53
是 YOLOv3 的主干网络,借鉴了 ResNet 中的残差块,网络总共有 53 层卷积层,能够高效提取特征。YOLOHead
YOLOHead
是 YOLOv3 在三个不同尺度上进行预测的头部网络,最终的卷积层输出的通道数是 3 * (num_classes + 5)
,其中 3
是因为每个尺度对应 3 个 anchor,num_classes + 5
包含类别 (num_classes)、置信度 (1) 和边界框参数 (4)。YOLOv3 网络整体结构
YOLOv3
结合了主干网络和三个 YOLO 预测头部,分别在 13x13、26x26 和 52x52 尺度上进行目标检测。YOLOv3 相较于 YOLOv2 主要通过改进网络架构、引入多尺度预测和使用残差连接等方法,提高了目标检测任务中的精度和效率。它的多尺度特征金字塔设计尤其适用于检测小目标,同时新采用的 Darknet-53 网络提供了强大的特征提取能力。
这一设计使得 YOLOv3 依然能在实时检测中保持良好的性能。