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

news2024/12/26 23:52:09

目录

YOLOv3 网络架构

YOLOv3 检测流程

YOLOv3 网络搭建


YOLOv3 网络架构

论文原址:https://arxiv.org/pdf/1804.02767

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

YOLO官方模型总览:https://pjreddie.com/darknet/yolo/#google_vignette

YOLOv3 目标检测算法由 Joseph Redmon 和 Ali Farhadi 在 2018 年提出,是一种用于实时目标检测的深度学习算法。YOLOv3 网络的模型架构如下:

CBL:Yolov3网络结构中的最小组件,由Conv+Bn+Leaky_relu激活函数三者组成。

Res unit:借鉴Resnet网络中的残差结构,让网络可以构建的更深。

ResX:由一个CBL和X个残差组件构成,是Yolov3中的大组件。每个Res模块前面的CBL都起到下采样的作用,因此经过5次Res模块后,得到的特征图是608->304->152->76->38->19大小。

Concat:张量拼接,会扩充两个张量的维度,例如26*26*256和26*26*512两个张量拼接,结果是26*26*768。Concat和cfg文件中的route功能一样。

add:张量相加,张量直接相加,不会扩充维度,例如104*104*128和104*104*128相加,结果还是104*104*128。add和cfg文件中的shortcut功能一样。

YOLOv3的网络架构基于其前身进行了改进,主要特点包括:

  • 主干网络:采用Darknet-53作为其主干网络,这是一个53层深的卷积神经网络,由多个卷积层和残差块组成,这些残差块有助于训练更深的网络而不会遭遇梯度消失问题。但YOLOv3中并没有使用Darknet-53的全连接层,并且为了降低池化带来的梯度负面效果,作者直接摒弃了池化层,直接使用步长为2的卷积来进行下采样

  • 多尺度预测:在基础网络的末端设计了三个不同的输出层(通常称为YOLO层),分别在不同尺度的特征图上进行预测。这样做是为了提高对不同大小目标的检测能力。每个输出层负责预测一组边界框及其相应的类别概率和置信度。
  • 锚框:使用了一组更丰富的预定义锚框(先验框),这些框具有不同的形状和比例,帮助模型适应多种尺寸的目标。(13\times 13负责大物体、26\times 26负责中物体、52\times 52负责小物体)

  • 特征融合:为了进一步增强对小目标的检测,在不同尺度的预测层之间进行了特征融合。这涉及上采样某些特征图并与较早层的高分辨率特征图相加,以结合深层次的语义信息和浅层次的位置信息。

YOLOv3 检测流程

简单来说,YOLOv3就是把一个图像划分成不同的网格,每个网格点负责一个区域的检测,只要物体的中心点落在这个区域,这个物体就有这个网格点来确定。(每个网格由其左上角的那个网格点负责,等同于每个网格点负责其右下区域的网格。锚点是每个网格的左上角)

下面介绍详细检测流程:

三个特征层的提取

  1. (52,52,256) 特征层:这是网络中较浅层次的特征图,提供了更多的空间细节,适用于检测图像中较大的目标。
  2. (26,26,512) 特征层:位于中间层次,提供了中等级别的空间细节和语义信息,适用于中等大小的目标。
  3. (13,13,1024) 特征层:这是最深的特征层,提供了最强的语义信息,但空间分辨率最低,适用于检测较小的目标。

特征层的处理与上采样

(13,13,1024) 特征层:首先,该特征层会经过几次卷积操作来进一步提取特征。然后,通过 1x1 卷积层减少通道数,并通过 3x3 卷积层进一步提取特征。最后,使用上采样(或称为转置卷积)将特征图的尺寸扩大两倍,变为 (26,26,512)。

特征层的拼接与再次处理

(26,26,512) 特征层:将上采样后的特征图与 Darknet-53 中原有的 (26,26,512) 特征层拼接,形成 (26,26,1024) 的特征图。然后,再次经过卷积操作,并通过 1x1 和 3x3 卷积层进行特征提取。最后,同样使用上采样将其尺寸扩大两倍,得到 (52,52,256) 的特征图。

生成预测结果

(13,13,75)、(26,26,75)、(52,52,75) 特征层:每个尺度的特征图都会通过一个 1x1 卷积层来预测边界框的数量、每个边界框的类别概率、目标的置信度等。这里的 75 是每个网格单元预测的边界框数量乘以每个边界框的参数数量。例如,如果每个网格单元预测 5 个边界框,每个边界框有 5 个参数(中心坐标 (x, y)、宽度 w、高度 h 和置信度 c),则 75 = 5 * 15。计算公式如下:

(c_{x},c_{y}):该点所在网格的左上角距离最左上角相差的格子数。

(p_{w},p_{h}):先验框的边长

(t_{x},t_{y}):目标中心点相对于该点所在网格左上角的偏移量

(t_{w},t_{h}):预测边框的宽和高

非极大值抑制(NMS)

      NMS的本质是搜索局部极大值,抑制非极大值元素。非极大值抑制,主要就是用来抑制检测时冗余的框。因为在目标检测中,在同一目标的位置上会产生大量的候选框,这些候选框相互之间可能会有重叠,所以需要利用非极大值抑制找到最佳的目标边界框,消除冗余的边界框。

算法流程:

  1. 对所有预测框的置信度降序排序
  2. 选出置信度最高的预测框,确认其为正确预测,并计算他与其他预测框的IOU
  3. 根据步骤2中计算的IOU去除重叠度高的,IOU > threshold阈值就直接删除4.剩下的预测框返回第1步,直到没有剩下的为止

YOLOv3 网络搭建

Backbone网络搭建:Darknet-53

import math
from collections import OrderedDict

import torch.nn as nn


#---------------------------------------------------------------------#
#   残差结构
#   利用一个1x1卷积下降通道数,然后利用一个3x3卷积提取特征并且上升通道数
#   最后接上一个残差边
#---------------------------------------------------------------------#
class BasicBlock(nn.Module):
    def __init__(self, inplanes, planes):
        super(BasicBlock, self).__init__()
        self.conv1  = nn.Conv2d(inplanes, planes[0], kernel_size=1, stride=1, padding=0, bias=False)
        self.bn1    = nn.BatchNorm2d(planes[0])
        self.relu1  = nn.LeakyReLU(0.1)
        
        self.conv2  = nn.Conv2d(planes[0], planes[1], kernel_size=3, stride=1, padding=1, bias=False)
        self.bn2    = nn.BatchNorm2d(planes[1])
        self.relu2  = nn.LeakyReLU(0.1)

    def forward(self, x):
        residual = x

        out = self.conv1(x)
        out = self.bn1(out)
        out = self.relu1(out)

        out = self.conv2(out)
        out = self.bn2(out)
        out = self.relu2(out)

        out += residual
        return out

class DarkNet(nn.Module):
    def __init__(self, layers):
        super(DarkNet, self).__init__()
        self.inplanes = 32
        # 416,416,3 -> 416,416,32
        self.conv1  = nn.Conv2d(3, self.inplanes, kernel_size=3, stride=1, padding=1, bias=False)
        self.bn1    = nn.BatchNorm2d(self.inplanes)
        self.relu1  = nn.LeakyReLU(0.1)

        # 416,416,32 -> 208,208,64
        self.layer1 = self._make_layer([32, 64], layers[0])
        # 208,208,64 -> 104,104,128
        self.layer2 = self._make_layer([64, 128], layers[1])
        # 104,104,128 -> 52,52,256
        self.layer3 = self._make_layer([128, 256], layers[2])
        # 52,52,256 -> 26,26,512
        self.layer4 = self._make_layer([256, 512], layers[3])
        # 26,26,512 -> 13,13,1024
        self.layer5 = self._make_layer([512, 1024], layers[4])

        self.layers_out_filters = [64, 128, 256, 512, 1024]

        # 进行权值初始化
        for m in self.modules():
            if isinstance(m, nn.Conv2d):
                n = m.kernel_size[0] * m.kernel_size[1] * m.out_channels
                m.weight.data.normal_(0, math.sqrt(2. / n))
            elif isinstance(m, nn.BatchNorm2d):
                m.weight.data.fill_(1)
                m.bias.data.zero_()

    #---------------------------------------------------------------------#
    #   在每一个layer里面,首先利用一个步长为2的3x3卷积进行下采样
    #   然后进行残差结构的堆叠
    #---------------------------------------------------------------------#
    def _make_layer(self, planes, blocks):
        layers = []
        # 下采样,步长为2,卷积核大小为3
        layers.append(("ds_conv", nn.Conv2d(self.inplanes, planes[1], kernel_size=3, stride=2, padding=1, bias=False)))
        layers.append(("ds_bn", nn.BatchNorm2d(planes[1])))
        layers.append(("ds_relu", nn.LeakyReLU(0.1)))
        # 加入残差结构
        self.inplanes = planes[1]
        for i in range(0, blocks):
            layers.append(("residual_{}".format(i), BasicBlock(self.inplanes, planes)))
        return nn.Sequential(OrderedDict(layers))

    def forward(self, x):
        x = self.conv1(x)
        x = self.bn1(x)
        x = self.relu1(x)

        x = self.layer1(x)
        x = self.layer2(x)
        out3 = self.layer3(x)
        out4 = self.layer4(out3)
        out5 = self.layer5(out4)

        return out3, out4, out5

def darknet53():
    model = DarkNet([1, 2, 8, 8, 4])
    return model

YOLOv3完整网络实现

from collections import OrderedDict

import torch
import torch.nn as nn

from nets.darknet import darknet53

def conv2d(filter_in, filter_out, kernel_size):
    pad = (kernel_size - 1) // 2 if kernel_size else 0
    return nn.Sequential(OrderedDict([
        ("conv", nn.Conv2d(filter_in, filter_out, kernel_size=kernel_size, stride=1, padding=pad, bias=False)),
        ("bn", nn.BatchNorm2d(filter_out)),
        ("relu", nn.LeakyReLU(0.1)),
    ]))

#------------------------------------------------------------------------#
#   make_last_layers里面一共有七个卷积,前五个用于提取特征。
#   后两个用于获得yolo网络的预测结果
#------------------------------------------------------------------------#
def make_last_layers(filters_list, in_filters, out_filter):
    m = nn.Sequential(
        conv2d(in_filters, filters_list[0], 1),
        conv2d(filters_list[0], filters_list[1], 3),
        conv2d(filters_list[1], filters_list[0], 1),
        conv2d(filters_list[0], filters_list[1], 3),
        conv2d(filters_list[1], filters_list[0], 1),
        conv2d(filters_list[0], filters_list[1], 3),
        nn.Conv2d(filters_list[1], out_filter, kernel_size=1, stride=1, padding=0, bias=True)
    )
    return m

class YoloBody(nn.Module):
    def __init__(self, anchors_mask, num_classes, pretrained = False):
        super(YoloBody, self).__init__()
        #---------------------------------------------------#   
        #   生成darknet53的主干模型
        #   获得三个有效特征层,他们的shape分别是:
        #   52,52,256
        #   26,26,512
        #   13,13,1024
        #---------------------------------------------------#
        self.backbone = darknet53()
        if pretrained:
            self.backbone.load_state_dict(torch.load("model_data/darknet53_backbone_weights.pth"))

        #---------------------------------------------------#
        #   out_filters : [64, 128, 256, 512, 1024]
        #---------------------------------------------------#
        out_filters = self.backbone.layers_out_filters

        #------------------------------------------------------------------------#
        #   计算yolo_head的输出通道数,对于voc数据集而言
        #   final_out_filter0 = final_out_filter1 = final_out_filter2 = 75
        #------------------------------------------------------------------------#
        self.last_layer0            = make_last_layers([512, 1024], out_filters[-1], len(anchors_mask[0]) * (num_classes + 5))

        self.last_layer1_conv       = conv2d(512, 256, 1)
        self.last_layer1_upsample   = nn.Upsample(scale_factor=2, mode='nearest')
        self.last_layer1            = make_last_layers([256, 512], out_filters[-2] + 256, len(anchors_mask[1]) * (num_classes + 5))

        self.last_layer2_conv       = conv2d(256, 128, 1)
        self.last_layer2_upsample   = nn.Upsample(scale_factor=2, mode='nearest')
        self.last_layer2            = make_last_layers([128, 256], out_filters[-3] + 128, len(anchors_mask[2]) * (num_classes + 5))

    def forward(self, x):
        #---------------------------------------------------#   
        #   获得三个有效特征层,他们的shape分别是:
        #   52,52,256;26,26,512;13,13,1024
        #---------------------------------------------------#
        x2, x1, x0 = self.backbone(x)

        #---------------------------------------------------#
        #   第一个特征层
        #   out0 = (batch_size,255,13,13)
        #---------------------------------------------------#
        # 13,13,1024 -> 13,13,512 -> 13,13,1024 -> 13,13,512 -> 13,13,1024 -> 13,13,512
        out0_branch = self.last_layer0[:5](x0)
        out0        = self.last_layer0[5:](out0_branch)

        # 13,13,512 -> 13,13,256 -> 26,26,256
        x1_in = self.last_layer1_conv(out0_branch)
        x1_in = self.last_layer1_upsample(x1_in)

        # 26,26,256 + 26,26,512 -> 26,26,768
        x1_in = torch.cat([x1_in, x1], 1)
        #---------------------------------------------------#
        #   第二个特征层
        #   out1 = (batch_size,255,26,26)
        #---------------------------------------------------#
        # 26,26,768 -> 26,26,256 -> 26,26,512 -> 26,26,256 -> 26,26,512 -> 26,26,256
        out1_branch = self.last_layer1[:5](x1_in)
        out1        = self.last_layer1[5:](out1_branch)

        # 26,26,256 -> 26,26,128 -> 52,52,128
        x2_in = self.last_layer2_conv(out1_branch)
        x2_in = self.last_layer2_upsample(x2_in)

        # 52,52,128 + 52,52,256 -> 52,52,384
        x2_in = torch.cat([x2_in, x2], 1)
        #---------------------------------------------------#
        #   第一个特征层
        #   out3 = (batch_size,255,52,52)
        #---------------------------------------------------#
        # 52,52,384 -> 52,52,128 -> 52,52,256 -> 52,52,128 -> 52,52,256 -> 52,52,128
        out2 = self.last_layer2(x2_in)
        return out0, out1, out2

希望能够对大家理解YOLOv3 模型有所帮助呀!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1687279.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

K8S中YAML案例

目录 案例:自主式创建service并关联上面的pod 案例:部署redis 案例:部署myapp 案例:部署MySQL数据库 总结 1.K8S集群中访问流向 K8S集群外部:客户端——nodeIP:nodeport——通过target port——podIP…

LiteOS-A内核中的procfs文件系统分析

一、 procfs介绍 procfs是类UNIX操作系统中进程文件系统(process file system)的缩写,主要用于通过内核访问进程信息和系统信息,以及可以修改内核参数改变系统行为。需要注意的是,procfs文件系统是一个虚拟文件系统&a…

AI副业:3天涨粉10w+,这些人凭什么这么火? (附教程案例)

大家好,我是向阳 今天给大家分享一下免费的 AI 插画工具 & 某书图文涨粉项目 某书上有这么一类笔记:精美的插话 几句提供情绪价值的文案 这类笔记可以说几乎是发一篇爆一篇!获赞涨粉如喝水,并且评论区有不少人求图和求教程…

利用Axure模板快速设计,可视化大屏信息大屏,含近200例资源和各类部件

模板类别: **通用模板:**提供基础的布局和设计元素,适用于各种场景。 **行业特定模板:**如农业、医院、销售、能源、物流、政府机关等,针对不同行业提供专业模板。 **数据展示模板:**包括大数据驾驶舱、统…

正点原子LWIP学习笔记(一)lwIP入门

lwIP入门 一、lwIP简介(了解)二、lwIP结构框图(了解)三、如何学习lwIP(熟悉) 一、lwIP简介(了解) lwIP是一个小型开源的TCP/IP协议栈 阉割的TCP/IP协议 TCP/IP协议栈结构&#xff0…

什么是 UUID,uuid

文章目录 一、是什么二、为什么三、怎么用 标题:深入探讨UUID:全球唯一标识符的秘密 一、是什么 在当今数字化时代,唯一标识符(UUID)在计算机科学领域扮演着重要的角色。UUID是一种用于标识信息的唯一字符串&#xff0…

【数据结构】树、森林与二叉树的转换 |树的存储 |双亲表示法 |孩子表示法 |孩子兄弟表示法

📖专栏文章:数据结构学习笔记 🪪作者主页:格乐斯 前言 树的存储 双亲表示法孩子表示法孩子兄弟表示法 树、森林与二叉树的转换 树的存储 双亲表示法 首先给每个结点编号,再将这些结点的双亲结点的序号存储起来&a…

从零开始实现自己的串口调试助手(1) - ui界面搭建

UI 界面搭建 ui界面整体演示 ui对象拆分 更多的细节就不方便展开了,下面有提示完成ui设计的提示 在创建工程前 记得把编码改为utf-8 ui设计技巧: ctrl 鼠标左键实现拖动实现复制粘贴 groupBox : 带标题的文本框 栅格布局 -- 只有一个控件的时候会铺满…

C++使用范围for语句处理多维数组

循环访问二维数组时出现如下问题: error: invalid range expression of type int *; no viable begin function availablefor (auto col : row){^ ~~~ 1 error generated.问题复现 初始化数组 constexpr size_t rowCnt 3, colCnt 4; int ia[rowCnt][colCnt]; fo…

【网站项目】SpringBoot380百天公司财务管理系统

🙊作者简介:拥有多年开发工作经验,分享技术代码帮助学生学习,独立完成自己的项目或者毕业设计。 代码可以私聊博主获取。🌹赠送计算机毕业设计600个选题excel文件,帮助大学选题。赠送开题报告模板&#xff…

AD162A低功耗语音解码芯片,助眠耳机方案推荐—云信通讯

睡眠一直是很多人所追寻的目标,无论是因为工作压力过大,还是日常烦扰的思绪,一个好的睡眠质量对每个人来说都至关重要。为了解决这个问题,一些科技公司开发了高品质的助眠耳机,通过音乐和声音来帮助入睡,为…

618好物推荐,每一款都是心头好

618买什么?!是不是又要开始剁手了?!买贵不如买对,收下这份好物推荐,无论你是有购物需求还是观望中,无论是日常好物还是智能家电,无论你是追求平价还是实用,这一篇好物推荐…

eNSP-集线器(hub)连接局域网

一、拓扑结构搭建 二、主机配置 pc1、pc2、pc3 三、测试 Hub相当于大家共享一条线路(类似于电线搭电),线路上的所有的设备都会接收同样的信息。

【MySQL02】【 InnoDB 记录存储结构】

文章目录 一、前言二、InnoDB 行格式1. COMPACT 行格式1.1 记录的额外信息1.2 记录的真实数据1.3 综上 2. REDUNDANT 行格式2.1 字段长度偏移列表2.2 记录头信息 3. DYNAMIC 行格式和 COMPPESED 行格式 三、InnoDB 数据页结构1. File Header (文件头部)2. Page Header (页面头部…

搭建淘宝扭蛋机小程序:技术选型与最佳实践

随着移动互联网的快速发展,小程序作为一种轻量级应用,以其无需安装、即用即走的特点,受到了广大用户的喜爱。在电商领域,淘宝作为国内最大的电商平台之一,也积极拥抱小程序技术,为用户提供更加便捷、个性化…

纯CSS画浮动卡通蓝天白云草坪动画效果

文章目录 效果展示 背景效果实现效果展示HTML结构CSS样式 云效果实现效果展示HTML结构CSS样式 草效果实现单颗小草效果展示HTML结构CSS样式 组合小草效果展示HTML结构CSS样式 完整代码 在网页设计中,添加动态元素如浮动云朵可以为用户带来更加生动和自然的体验。虽然…

BeanDefinition作用

BeanDefinition接口 BeanDefinition 描述一个 Bean 实例,这个实例有哪些属性值、构造函数以及一些其他信息,就是描述Bean实例的信息。 BeanDefinition是一个接口,允许BeanFactoryPostProcessor 内省和修改属性值和其他 Bean 元数据。 点击了…

【simple-admin】simple-admin-core 首次服务启动 如何配置mysql数据库表 | 如何docker启动core

一、下载启动S-A 1、下载源码 https://github.com/suyuan32/simple-admin-core.git git clone https://github.com/suyuan32/simple-admin-core.git2、修改etc下yaml配置 需要对RPC和API 分别2个文件夹下的etc下的yaml进行修改 替换成我们的数据库 3、初始化数据库 核心代…

芯课堂 | UI Creator 物理键盘移植指南

LVGL提供输入设备的种类一共有5种,分别是:touchpad(触摸板)、mouse(鼠标)、keypad(键盘)、encoder(编码器)、button(外部按键)。而基于…

抖音直播预告|换新·升级 Aigtek安泰电子2024新产品发布会

多系列功放产品大升级! 深耕电子测试仪器领域,攻坚行业尖端技术,不断自主研发与创新,是安泰电子一如既往的坚持!通过长久技术积淀,Aigtek安泰电子多系列功放也在2024年迎来了全新升级! 本次我们…