目标检测网络:YOLOv5 模型复现

目录

YOLOv5 网络架构

输入端

自适应锚框

自适应图片缩放

Backbone网络

Focus 结构

YOLOv5 网络搭建


YOLOv5 网络架构

Code:https://github.com/ultralytics/yolov5

     YOLOv5与YOLOv4极其相似,只在少数模块进行了改动,因此这里我只写了YOLOv5的改进点。其余的结构大家可以查看上一篇文章:目标检测网络:YOLOv4 模型复现

YOLOv5网络的模型架构如下:

与YOLOv4相比,YOLOv5在几个关键方面进行了显著改进,主要包括:

  1. 输入端:提出一种自适应锚框计算与自适应图片缩放方法;
  2. Backbone网络:使用了 CSPDarknet53 和 Focus 结构作为基准网络;

下面对这些关键点进行详细介绍。

输入端

自适应锚框

YOLO算法中的锚框是用来预测物体边界框的初始估计值。网络在初始锚框的基础上输出预测框,进而和真实框groundtruth进行比对,计算两者差距,再反向更新,迭代网络参数。

在Yolov3、Yolov4中,锚框的计算通常是在训练之前通过一个单独的脚本或程序完成的。这意味着需要预先分析数据集中的边界框,以确定一组合适的锚框尺寸。但Yolov5中将此功能嵌入到代码中,每次训练时,自适应的计算不同训练集中的最佳锚框值。

锚框计算步骤:

  1. 获取数据集中所有目标的宽和高。
  2. 将数据集中的每张图片按照等比例缩放到指定的分辨率大小,通常保证宽度或高度的最大值符合指定的尺寸。
  3. 将边界框(bboxes)从相对坐标转换为绝对坐标,乘以缩放后的图像尺寸。
  4. 移除宽度或高度小于两个像素的边界框,因为这些框对于训练和检测没有意义。
  5. 使用K-means聚类算法对边界框的尺寸进行分组,以找到一组代表性的尺寸作为锚框的候选尺寸。根据K-means聚类的结果,选择n个最具代表性的尺寸作为初始的锚框尺寸。
  6. 利用遗传算法对锚框的尺寸进行微调。遗传算法通过随机变异和选择,逐步优化锚框的尺寸。倘若变异后的效果好,就将变异后的结果赋值给anchors;如果变异后效果变差就跳过,默认变异1000次。这里是使用anchor_fitness方法计算得到的适应度(fitness),然后再进行评估。
自适应图片缩放

实现步骤:

(1)根据原始图片大小以及输入到网络的图片大小计算缩放比例

原始缩放尺寸是416*416,都除以原始图像的尺寸后,可以得到0.52,和0.69两个缩放系数,选择小的缩放系数。

(2)根据原始图片大小与缩放比例计算缩放后的图片大小

原始图片的长宽都乘以最小的缩放系数0.52,宽变成了416,而高变成了312。

(3)计算黑边填充数值

将416-312=104,得到原本需要填充的高度。再采用numpy中np.mod取余数的方式,得到8个像素,再除以2,即得到图片高度两端需要填充的数值。

Backbone网络

Focus 结构

Focus模块在YOLOv5中是图片进入Backbone前,对图片进行切片操作。首先通过在原始图像的宽度和高度上每隔一个像素取样,将图像分割成四部分,每部分沿通道方向拼接,形成12通道的图像(原始图像3通道)。这种方法使得图像的宽度和高度减半,实现二倍下采样,同时保留空间信息。随后,卷积操作进一步提取特征,生成具有更高通道数的特征图,这些特征图在空间分辨率上是原始图像的一半,但在通道维度上是原始图像的四倍。

以YOLOv5s为例,原始的640× 640×3的图像输入Focus结构,采用切片操作,先变成320× 320×12的特征图,再经过一次卷积操作,最终变成320 ×320× 32的特征图。

切片操作如下:

YOLOv5 网络搭建

YOLOv5完整网络实现

import torch
import torch.nn as nn

from nets.ConvNext import ConvNeXt_Small, ConvNeXt_Tiny
from nets.CSPdarknet import C3, Conv, CSPDarknet
from nets.Swin_transformer import Swin_transformer_Tiny


#---------------------------------------------------#
#   yolo_body
#---------------------------------------------------#
class YoloBody(nn.Module):
    def __init__(self, anchors_mask, num_classes, phi, backbone='cspdarknet', pretrained=False, input_shape=[640, 640]):
        super(YoloBody, self).__init__()
        depth_dict          = {'s' : 0.33, 'm' : 0.67, 'l' : 1.00, 'x' : 1.33,}
        width_dict          = {'s' : 0.50, 'm' : 0.75, 'l' : 1.00, 'x' : 1.25,}
        dep_mul, wid_mul    = depth_dict[phi], width_dict[phi]

        base_channels       = int(wid_mul * 64)  # 64
        base_depth          = max(round(dep_mul * 3), 1)  # 3
        #-----------------------------------------------#
        #   输入图片是640, 640, 3
        #   初始的基本通道是64
        #-----------------------------------------------#
        self.backbone_name  = backbone
        if backbone == "cspdarknet":
            #---------------------------------------------------#   
            #   生成CSPdarknet53的主干模型
            #   获得三个有效特征层,他们的shape分别是:
            #   80,80,256
            #   40,40,512
            #   20,20,1024
            #---------------------------------------------------#
            self.backbone   = CSPDarknet(base_channels, base_depth, phi, pretrained)
        else:
            #---------------------------------------------------#   
            #   如果输入不为cspdarknet,则调整通道数
            #   使其符合YoloV5的格式
            #---------------------------------------------------#
            self.backbone       = {
                'convnext_tiny'         : ConvNeXt_Tiny,
                'convnext_small'        : ConvNeXt_Small,
                'swin_transfomer_tiny'  : Swin_transformer_Tiny,
            }[backbone](pretrained=pretrained, input_shape=input_shape)
            in_channels         = {
                'convnext_tiny'         : [192, 384, 768],
                'convnext_small'        : [192, 384, 768],
                'swin_transfomer_tiny'  : [192, 384, 768],
            }[backbone]
            feat1_c, feat2_c, feat3_c = in_channels 
            self.conv_1x1_feat1 = Conv(feat1_c, base_channels * 4, 1, 1)
            self.conv_1x1_feat2 = Conv(feat2_c, base_channels * 8, 1, 1)
            self.conv_1x1_feat3 = Conv(feat3_c, base_channels * 16, 1, 1)
            
        self.upsample   = nn.Upsample(scale_factor=2, mode="nearest")

        self.conv_for_feat3         = Conv(base_channels * 16, base_channels * 8, 1, 1)
        self.conv3_for_upsample1    = C3(base_channels * 16, base_channels * 8, base_depth, shortcut=False)

        self.conv_for_feat2         = Conv(base_channels * 8, base_channels * 4, 1, 1)
        self.conv3_for_upsample2    = C3(base_channels * 8, base_channels * 4, base_depth, shortcut=False)

        self.down_sample1           = Conv(base_channels * 4, base_channels * 4, 3, 2)
        self.conv3_for_downsample1  = C3(base_channels * 8, base_channels * 8, base_depth, shortcut=False)

        self.down_sample2           = Conv(base_channels * 8, base_channels * 8, 3, 2)
        self.conv3_for_downsample2  = C3(base_channels * 16, base_channels * 16, base_depth, shortcut=False)

        # 80, 80, 256 => 80, 80, 3 * (5 + num_classes) => 80, 80, 3 * (4 + 1 + num_classes)
        self.yolo_head_P3 = nn.Conv2d(base_channels * 4, len(anchors_mask[2]) * (5 + num_classes), 1)
        # 40, 40, 512 => 40, 40, 3 * (5 + num_classes) => 40, 40, 3 * (4 + 1 + num_classes)
        self.yolo_head_P4 = nn.Conv2d(base_channels * 8, len(anchors_mask[1]) * (5 + num_classes), 1)
        # 20, 20, 1024 => 20, 20, 3 * (5 + num_classes) => 20, 20, 3 * (4 + 1 + num_classes)
        self.yolo_head_P5 = nn.Conv2d(base_channels * 16, len(anchors_mask[0]) * (5 + num_classes), 1)

    def forward(self, x):
        #  backbone
        feat1, feat2, feat3 = self.backbone(x)
        if self.backbone_name != "cspdarknet":
            feat1 = self.conv_1x1_feat1(feat1)
            feat2 = self.conv_1x1_feat2(feat2)
            feat3 = self.conv_1x1_feat3(feat3)

        # 20, 20, 1024 -> 20, 20, 512
        P5          = self.conv_for_feat3(feat3)
        # 20, 20, 512 -> 40, 40, 512
        P5_upsample = self.upsample(P5)
        # 40, 40, 512 -> 40, 40, 1024
        P4          = torch.cat([P5_upsample, feat2], 1)
        # 40, 40, 1024 -> 40, 40, 512
        P4          = self.conv3_for_upsample1(P4)

        # 40, 40, 512 -> 40, 40, 256
        P4          = self.conv_for_feat2(P4)
        # 40, 40, 256 -> 80, 80, 256
        P4_upsample = self.upsample(P4)
        # 80, 80, 256 cat 80, 80, 256 -> 80, 80, 512
        P3          = torch.cat([P4_upsample, feat1], 1)
        # 80, 80, 512 -> 80, 80, 256
        P3          = self.conv3_for_upsample2(P3)
        
        # 80, 80, 256 -> 40, 40, 256
        P3_downsample = self.down_sample1(P3)
        # 40, 40, 256 cat 40, 40, 256 -> 40, 40, 512
        P4 = torch.cat([P3_downsample, P4], 1)
        # 40, 40, 512 -> 40, 40, 512
        P4 = self.conv3_for_downsample1(P4)

        # 40, 40, 512 -> 20, 20, 512
        P4_downsample = self.down_sample2(P4)
        # 20, 20, 512 cat 20, 20, 512 -> 20, 20, 1024
        P5 = torch.cat([P4_downsample, P5], 1)
        # 20, 20, 1024 -> 20, 20, 1024
        P5 = self.conv3_for_downsample2(P5)

        #---------------------------------------------------#
        #   第三个特征层
        #   y3=(batch_size,75,80,80)
        #---------------------------------------------------#
        out2 = self.yolo_head_P3(P3)
        #---------------------------------------------------#
        #   第二个特征层
        #   y2=(batch_size,75,40,40)
        #---------------------------------------------------#
        out1 = self.yolo_head_P4(P4)
        #---------------------------------------------------#
        #   第一个特征层
        #   y1=(batch_size,75,20,20)
        #---------------------------------------------------#
        out0 = self.yolo_head_P5(P5)
        return out0, out1, out2

  • 17
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
您好!对于 TPH-YOLOv5 的代码复现,可以按照以下步骤进行操作: 1. 克隆源代码库:首先,将 TPH-YOLOv5 的源代码库克隆到本地。可以通过在终端中执行以下命令完成: ``` git clone https://github.com/tphanson/yolov5.git ``` 2. 安装依赖项:进入克隆下来的 yolov5 文件夹,并使用以下命令安装所有依赖项: ``` cd yolov5 pip install -r requirements.txt ``` 3. 数据准备:将您的训练数据集放在 `data` 文件夹下,并按照要求的格式进行组织。具体的数据集准备方法可以参考 yolov5 官方文档中的说明。 4. 配置模型:在 `models` 文件夹中,可以根据自己的需求选择合适的 YOLOv5 模型结构和超参数配置文件。 5. 开始训练:通过运行以下命令开始训练模型: ``` python train.py --img {image size} --batch {batch size} --epochs {num epochs} --data {path to data.yaml} --cfg {path to model.yaml} ``` 其中,`image size` 是输入图像的尺寸,`batch size` 是每个批次的图像数量,`num epochs` 是训练的迭代次数,`path to data.yaml` 是数据集的配置文件路径,`path to model.yaml` 是模型的配置文件路径。 6. 模型推理:训练完成后,您可以使用训练得到的权重文件进行目标检测推理。使用以下命令运行推理脚本: ``` python detect.py --source {path to input image/video} --weights {path to trained weights} --conf {detection threshold} ``` 其中,`path to input image/video` 是输入图像/视频的路径,`path to trained weights` 是训练得到的权重文件路径,`detection threshold` 是目标检测的置信度阈值。 以上是 TPH-YOLOv5 代码复现的基本步骤。根据您的实际需求,可能还需要对数据集、模型和训练参数进行进一步的配置和调整。希望这些步骤对您有所帮助!如果还有其他问题,请随时提问。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

悠眠小虫

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值