yolov5源码解析--输出

2023-05-16

        本文章基于yolov5-6.2版本。主要讲解的是yolov5是怎么在最终的特征图上得出物体边框、置信度、物体分类的。

一、总体框架

        首先贴出总体框架,直接就拿官方文档的图了,本文就是接着右侧的那三层输出开始讨论。

  • Backbone: New CSP-Darknet53
  • Neck: SPPF, New CSP-PAN
  • Head: YOLOv3 Head

        这三个输出层分别就是浅、中、深层啦,浅层特征图分辨率是80乘80,中层是40乘40,深层是20乘20,一般来说浅层用于预测小物体,深层用于预测大物体。另外说明一下,浅、中、深三层的特征图输出通道数不一定是256、512、1024,要看你用的是哪一种规格的模型。比如yolov5s的话,那这三层的通道数分别是128、256、512,可以导出onnx格式用Netron看一下模型结构来确定。

        简要说一下原因,这个是由对应的模型配置文件,即models目录里的yolov5s.yaml,yolov5m.yaml等等来决定的,看你用哪一个,第二个红框里的就是每一层的输出通道数了,但是它是要乘上第一个红框里的值的,即width_multiple这个配置,你会发现几个模型配置文件的内容都差不多,区别就区别在这里的depth_multiple和width_multiple。

二、输出物体边框、置信度、物体分类

        接下来进入正题,每层特征图最终都会经过1乘1卷积,变成(5+分类数)乘3个通道:

0)首先为什么乘以3,因为每一层都有3个anchor,后面再细讲

        下面讲的是每一anchor对应的(5+分类数)个通道,假设分类数为2,那一共就是7个通道了,这7个通道分别是xywh(4个通道),置信度(1个通道),分类(此处2分类,就是2个通道)

1)物体边框的4个值,x,y,w,h啦,不过这个x,y并不直接是物体框中心点的坐标,而是它相对于自身所处的格子左上角的偏移,比如下图红色的这个格子(假设现在特征图就是4乘4),这个格子预测出7个值,前4个就是xywh,然后x是0.2,y是0.2,那么中心点就差不多在蓝点所处的位置了(其实这其中还有玄机,一步步来)。然后再把这个中心点的相对值作用到原图的尺度得到最终的坐标。

         但是呢如果像上面这样直接预测一个相对格子左上角的偏移的这样一个值呢,会比较不稳定,它可能预测的值很大,比如x给你预测一个10出来,那就是往右数10个格子了,偏差这么大不利用网络收敛,也没有意义,因为这个格子里的特征跟右边第10个格子的特征相差可能很大了。

        所以要加一个限制,首先给它sigmoid一下,这样其值范围就变成0-1了(小数),此时它的波动就在自己的这个格子内,然后乘以2再减0.5,如下图(直接拿官方文档的图了~~)

        这样它的波动范围就是下图的黄框的范围。

        限制为0-1好理解,自己这个格子的预测范围就在自己格子内麻,为啥又变成了-0.5-1.5呢,因为这样更容易得到0-1范围内的值。如果的范围限制为0-1,而且是用sigmoid来限制的话,那接近0和1这两个位置的导数就会很小,梯度更新的时候就会慢。

        然后就是宽高,宽高也不是直接预测出物体边框的宽高啦,而是基于anchor的,预测出来的值会乘上anchor的宽高得出最终的宽高,并且,这里仍然是先用sigmoid将输出值限制为0-1,然后再乘以2,再来个平方,这样最终的值的范围就是0-4了。

        之前说了每一层有3个anchor,这些anchor还是配置在模型的配置文件里的,比如models/yolov5s.yaml,P3就是浅层的(80乘80的格子),P4是中层的(40乘40),P5是深层的(20乘20),然后这里的anchor的大小呢就是绝对值(按照640乘640的图来算的,如果你的输入图不是640乘640,那输入图是会resize一下再进行推理的)

        比如现在是深层的输出,2分类,那么深层的特征图经过最后的1乘1卷积后,会得到3乘(5+2)=21个通道,每7个通道就对应一个anchor了,现在看第2个7个通道(即7-13,从0开始算),那么它对应的anchor就应该是156,198这个,那么预测出来的宽高值经过sigmoid,再乘2,再平方之后,还分别要乘上156和198,得出最终的物体宽高(基于640乘640的图的),然后再按比例得到原图的物体宽高。

2)置信度

        代表预测出的物体边框和分类的可信度,最终的范围肯定是0-1了(小数),跟前面的一样,会用sigmoid来把它的范围限制为0-1。

        这边可能有一个问题,那个xy不是sigmoid()乘2减0.5吗,这里咋不这么干,那是因为xy的值真的是可以达到-0.5或1.5的,那样的话就变成预测的物体中心点跑到相邻格子里去了,这也不是不行的啦。但置信度只能是0-1!

3)分类

        有几个分类,就会再加几个通道,分别代表对应分类的概率,都是用sigmoid把他们的概率限制为0-1,在计算损失的时候,标签对应分类所在通道的直值为1,其它都为0了,然后分别计算BCE损失。

三、源码

        最终输出层的相关源码主要就是models/yolo.py的Detect类的源码了,添加了相应的注释。

class Detect(nn.Module):
    stride = None  # strides computed during build
    onnx_dynamic = False  # ONNX export parameter
    export = False  # export mode
 
    def __init__(self, nc=80, anchors=(), ch=(), inplace=True):  # detection layer
        super().__init__()
        self.nc = nc  # number of classes
        self.no = nc + 5  # number of outputs per anchor
        self.nl = len(anchors)  # number of detection layers
        self.na = len(anchors[0]) // 2  # number of anchors,除以2是因为[10,13, 16,30, 33,23]这个长度是6,对应3个anchor
        self.grid = [torch.zeros(1)] * self.nl  # init grid,下面会计算grid,grid就是每个格子的x,y坐标(整数,比如0-19)
        self.anchor_grid = [torch.zeros(1)] * self.nl  # init anchor grid
        self.register_buffer('anchors', torch.tensor(anchors).float().view(self.nl, -1, 2))  # shape(nl,na,2),注意后面就可以通过self.anchors来访问它了
        self.m = nn.ModuleList(nn.Conv2d(x, self.no * self.na, 1) for x in ch)  # output conv,3个输出层最后的1乘1卷积
        self.inplace = inplace  # use inplace ops (e.g. slice assignment)
 
    def forward(self, x):
        z = []  # inference output
        for i in range(self.nl):  # 三个输出层分别处理
            x[i] = self.m[i](x[i])  # conv,经过这个1乘1卷积就变成(5+分类数)个通道了
            bs, _, ny, nx = x[i].shape  # x(bs,255,20,20) to x(bs,3,20,20,85)--这里的85对应coco数据集,5+80个分类
            x[i] = x[i].view(bs, self.na, self.no, ny, nx).permute(0, 1, 3, 4, 2).contiguous()
 
            if not self.training:  # inference
                if self.onnx_dynamic or self.grid[i].shape[2:4] != x[i].shape[2:4]:
                    self.grid[i], self.anchor_grid[i] = self._make_grid(nx, ny, i)
 
                y = x[i].sigmoid()
                if self.inplace:
                    # 这里的grid[i]即对应输出层的3个anchor层的每个格子的坐标,方便进行批量计算,乘上对应的stride[i](下采样率),就得到基于640乘640的图的坐标了
                    y[..., 0:2] = (y[..., 0:2] * 2 + self.grid[i]) * self.stride[i]  # xy
                    # anchor_grid[i]也是一样,不过它的形状是(1, self.na, 1, 1, 2),跟y[..., 2:4]计算时是会自动广播的,最终得到的宽高也是基于640乘640的图的宽高
                    y[..., 2:4] = (y[..., 2:4] * 2) ** 2 * self.anchor_grid[i]  # wh
                else:  # for YOLOv5 on AWS Inferentia https://github.com/ultralytics/yolov5/pull/2953
                    # 这段是非inplace操作,计算方法是一样的
                    xy, wh, conf = y.split((2, 2, self.nc + 1), 4)  # y.tensor_split((2, 4, 5), 4)  # torch 1.8.0
                    xy = (xy * 2 + self.grid[i]) * self.stride[i]  # xy
                    wh = (wh * 2) ** 2 * self.anchor_grid[i]  # wh
                    y = torch.cat((xy, wh, conf), 4)
                z.append(y.view(bs, -1, self.no))
 
        return x if self.training else (torch.cat(z, 1),) if self.export else (torch.cat(z, 1), x)
 
    def _make_grid(self, nx=20, ny=20, i=0, torch_1_10=check_version(torch.__version__, '1.10.0')):
        d = self.anchors[i].device
        t = self.anchors[i].dtype
        shape = 1, self.na, ny, nx, 2  # grid shape
        # grid其实就是特征图网络的坐标,比如20乘20的,其坐标分别是0,0 0,1...0,19 1,0 1,1...19,19,第2个维度na就是anchor数啦
        y, x = torch.arange(ny, device=d, dtype=t), torch.arange(nx, device=d, dtype=t)
        if torch_1_10:  # torch>=1.10.0 meshgrid workaround for torch>=0.7 compatibility
            yv, xv = torch.meshgrid(y, x, indexing='ij')
        else:
            yv, xv = torch.meshgrid(y, x)
        # 注意这边先给它把0.5给减了
        grid = torch.stack((xv, yv), 2).expand(shape) - 0.5  # add grid offset, i.e. y = 2.0 * x - 0.5
        # anchor_grid即每个格子对应的anchor宽高,stride是下采样率,三层分别是8,16,32,这里为啥要乘呢,因为在外面已经把anchors给除了对应的下采样率,这里再乘回来
        anchor_grid = (self.anchors[i] * self.stride[i]).view((1, self.na, 1, 1, 2)).expand(shape)
        return grid, anchor_grid

        此处单独说一下torch.meshgrid,它其实就是用于得到网格坐标的,简化代码如下,假设现在是2乘2的网络

y, x = torch.arange(2), torch.arange(2)
yv, xv = torch.meshgrid(y, x, indexing='ij')
print(f'yv={yv}')
print(f'xv={xv}')
 
grid = torch.stack((xv, yv), 2)
print(f'grid={grid}')

        输出如下:

        grid对应的就是如下图,得到这个网络坐标就可以直接跟输出层的x,y做批量运算了。

四、NMS

        Detect类foward之后确实是整个网络最终的输出,不过这个输出还得再经过NMS,提取出最终的答案,即这张图上到底有几个物体,边框、置信度、分类分别是什么。NMS后面再讨论~~

————————————————
原文链接:https://blog.csdn.net/ogebgvictor/article/details/127481011

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

yolov5源码解析--输出 的相关文章

  • mc_att_control源码解析

    目录 源码分析内环控制外环控制 之前写了博客分析了一下旋翼姿态控制的基础知识 mc att control基础知识 这次就对照代码将整个旋翼姿态控制过程呈现一遍 先看一下整个程序的框图 从图中可以看到 实际上整个控制分成内外两个环进行控制
  • Pycharm+Anaconda+yolov5-5.0部署(手把手教+解决一些运行过程中的问题+最全部署yolov5)

    目录 一 在pycharm打开克隆后的yolov5 5 0的项目二 配置pycharm的解释器1 依次打开 文件 gt 设置 gt 项目 gt python解释器2 打开配置好的deeplearn学习环境1 单击添加解释器 gt 单击添加本
  • Libev源码解析

    最近在看libev源码 xff0c 算是对libev的源码有个比较清晰的了解 总共分3部分来介绍libev 1 Libev是什么 Libev是基于Reactor模式的一个高性能 xff0c 支持高并发的事件库 它本身不仅支持IO xff0c
  • java源码解析JavaParser

    package com bootdo jparser import java io File import java io FileNotFoundException import com github javaparser JavaPar
  • c++部署yolov5模型

    C 43 43 部署yolov5模型 前言一 准备模型二 Fastdeploy准备三 调用总结 前言 不可否认 xff0c yolov5在目标检测方面大杀四方 xff0c 在 SOTA 榜上留下过万众瞩目的成绩 xff0c 但是官网代码给的
  • RTKlib源码解析:ppp和rtkpost中的周跳检测函数

    文章目录 前言detslp mwdetslp gfdetslp lldetslp dop 欢迎关注个人公众号 xff1a 导航员学习札记 前言 本文解析了RTKlib ppp c中两个周跳检测函数detslp mw和detslp gf xf
  • YOLOv5核心基础知识讲解

    我这主要是江大白老师的内容 xff01 xff01 深入浅出Yolo系列之Yolov3 amp Yolov4 amp Yolov5 amp Yolox核心基础知识完整讲解 xff08 CSDN xff09 深入浅出Yolo系列之Yolov5
  • Jetson Xavier NX 部署Yolov5

    大部分过程非原创 xff0c 写这篇文章主要是因为设备不同出的问题也各不相同 xff0c 需要到处去找解决方法 xff0c 因此我把配置过程中遇到的全部问题以及解决方法记录在此 注 xff1a 该博客原文来自Jetson nano NX 部
  • Pytorch学习(3) —— nn.Parameter nn.ParameterList nn.ParameterDict 源码解析

    为了更好理解Pytorch基本类的实现方法 xff0c 我这里给出了关于参数方面的3个类的源码详解 此部分可以更好的了解实现逻辑结构 xff0c 有助于后续代码理解 xff0c 学pytorch的话这个不是必须掌握的 xff0c 看不懂也没
  • YOLOv5和YOLOv7环境(GPU)搭建测试成功

    本来是用doc写的 xff0c 直接复制到这里很多图片加载缓慢 xff0c 我直接把doc上传到资源里面了 xff0c 0积分下载 xff1a 10条消息 YOLOv5和YOLOv7开发环境搭建和demo运行 Python文档类资源 CSD
  • 对象池(连接池):commons-pool2源码解析:GenericObjectPool的returnObject方法解析

    为什么会有对象池 在实际的应用工程当中 存在一些被频繁使用的 创建或者销毁比较耗时 持有的资源也比较昂贵的一些对象 比如 数据库连接对象 线程对象 所以如果能够通过一种方式 把这类对象统一管理 让这类对象可以被循环利用的话 就可以减少很多系
  • 【目标检测-YOLO】YOLOv5-5.0v-数据处理(第三篇)

    前文链接 YOLOv5 v5 0 yolov5s网络架构详解 第一篇 星魂非梦的博客 CSDN博客 YOLOv5 5 0v yaml 解析 第二篇 星魂非梦的博客 CSDN博客 1 性能分析 YOLOv5 P6 models 4 outpu
  • yolov5代码--注释

    yolov5目录结构 yolov5 detect py代码详解 https blog csdn net CharmsLUO article details 123422822 spm 1001 2014 3001 5506 yolov5 t
  • yolo毕业设计(车辆识别、行人识别、车牌识别)

    车牌识别视频 车辆识别视频 yolov5车辆识别视频 yolov5 yoloR对比行人车辆识别视频
  • YOLOv5训练目标检测数据集(小白)

    一 提前准备工作 1 利用labelimg软件给收集到的图片打标签 具体步骤网上都有 2 下载好yolov5 v6 1 源码 下载地址 https github com ultralytics yolov5 用pycharm打开 在项目目录
  • YOLOV5摔倒检测识别

    一 摔倒检测的实际意义 摔倒检测是一种人工智能技术 可用于监测和识别可能发生在老年人 幼儿 体育运动员等群体中的摔倒事件 在实际应用中 摔倒检测技术可以帮助本文 及时发现摔倒事件 通过监测设备和传感器 及时发现摔倒事件 可以避免因为无人发现
  • YOLOv5(PyTorch)目标检测:原理与源码解析

    PyTorch版YOLOv5目标检测 原理与源码解析 课程链接 https edu csdn net course detail 31428 Linux创始人Linus Torvalds有一句名言 Talk is cheap Show me
  • 睿智的目标检测56——Pytorch搭建YoloV5目标检测平台

    睿智的目标检测56 Pytorch搭建YoloV5目标检测平台 学习前言 源码下载 YoloV5改进的部分 不完全 YoloV5实现思路 一 整体结构解析 二 网络结构解析 1 主干网络Backbone介绍 2 构建FPN特征金字塔进行加强
  • 清晰、幽默、轻松地深入理解YOLOv5网络结构和一些细节(查阅无数资料文献总结)

    最近的一篇关于YOLOv5检测小目标博客的点击量很高 没想到YOLOv5还是很有影响力的 既然这样 今天本人就本着幽默 清晰 轻松的风格带大家深入了解一下YOLOv5那倾倒众生的网络结构 和它较之其他算法的改进之处 还是一句话 希望我的不经
  • 还是得从代码角度看yolov5(1)

    train 参考文章 又是经典写到一半发现别人写的更好 基础函数 1 setattr setattr opt k v 将给定对象上的命名属性设置为指定值 等价于opt k v 2 getattr callback getattr logge

随机推荐

  • 理解depth-wise 卷积

    EfficientNet利用depth wise卷积来减少FLOPs但是计算速度却并没有相应的变快 反而拥有更多FLOPs的RegNet号称推理速度是EfficientNet的5倍 非常好奇 xff0c 这里面发生了什么 xff0c 为什么
  • GIoU (Generalized Intersection over Union) 详解

    论文 xff1a Generalized Intersection over Union A Metric and A Loss for Bounding Box Regression 官方解读 xff1a Generalized Inte
  • Gitee push错误 Access denied: You do not have permission to push to the protected branch ‘master‘ via

    错误 xff1a 首次使用gitee向别人的repo提交代码 xff0c 发现出现权限问题无法push到master xff0c 提交命令如下 xff1a git push u origin master master 错误信息如下 xff
  • GDAL重采样与裁剪图像示例

    GDAL重采样 xff0c 可以通过写文件时改变图像尺寸和geo transformes的分辨率信息实现 核心代码示例如下 xff1a in ds 61 gdal Open fi gdal GA ReadOnly geotrans 61 i
  • pycharm专业版连接远程docker容器

    一 配置远程docker容器 1 启动带有端口的docker容器 6006端口是用来运行tensorboard的 xff0c 这里重要的是22端口 如果希望通过ssh远程连接docker xff0c 需要对容器的22端口做端口映射 dock
  • VScode 远程开发配置

    一 配置免密远程登录 因为是要远程登录 xff0c 那么需要通过使用ssh进行密钥对登录 xff0c 这样每次登录服务器就可以不用输入密码了 先来一句官方介绍 xff1a ssh 公钥认证是一种方便 高安全性的身份验证方法 xff0c 它将
  • np.meshgrid()与torch.meshgrid()的区别

    比如要生成一张图像 h 61 6 w 61 10 的xy坐标点 xff0c 看下两者的实现方式 xff1a 两种方式的差异在于 xff1a xs ys 61 np meshgrid np arange w np arange h xs ys
  • JSON是什么

    提起 JSON xff0c 作为如今最受欢迎的数据交换格式 xff0c 可以说是无人不知 无人不晓了 JSON 全称 JavaScript Object Notation xff08 JS 对象简谱 xff09 xff0c 自诞生之初的小目
  • 【C++】数组定义引发Stack overflow错误(运行时是报段错误)

    C 43 43 xff08 实际是C的语法 xff09 定义数组时出错 xff0c 代码如下 xff1a float t1 9830400 调试时触发Stack overflow错误 xff08 可执行文件运行时 xff0c 是报段错误 x
  • 【C/C++】数组初始化

    数组定义不初始化会被随机赋值 因此如果数组的所有元素在下面没有逐一赋值 xff0c 但是又会使用到的话 xff0c 最后不要只定义而不初始化 会带来问题 数组初始化的几种形式 可以直接用 xff1a a 10 61 xff0c 就可以让a
  • 【C++】指针数组与数组指针

    指针数组 指针数组可以说成是 指针的数组 xff0c 首先这个变量是一个数组 xff0c 其次 xff0c 指针 修饰这个数组 xff0c 意思是说这个数组的所有元素都是指针类型 xff0c 在32位系统中 xff0c 指针占四个字节 定义
  • 【旋转框目标检测】2201_The KFIoU Loss For Rotated Object Detection

    paper with code paper code Jittor Code https github com Jittor JDet PyTorch Code https github com open mmlab mmrotate Te
  • CUDA编译报错unsupported GNU version! gcc versions later than 10 are not supported!

    问题 xff1a python编译用于cuda的so文件中 xff0c 使用编译 cu文件出错 xff1a error unsupported GNU version gcc versions later than 10 are not s
  • RuntimeError: CUDA error: no kernel image is available for execution on the device

    问题 xff1a 代码换机器执行时 xff0c 使用包含自行编译的cuda算子库so时出错 xff1a RuntimeError CUDA error no kernel image is available for execution o
  • Ubuntu非LTS版本安装nvidia-docker出错:Unsupported distribution!

    问题 xff1a 按照Nvidia官方流程 xff0c 在Ubuntu22 10安装nvidia docker在执行以下命令时 distribution 61 etc os release echo ID VERSION ID amp am
  • 测试torch方法是否支持半精度

    并不是所有的torch方法都支持半精度计算 测试半精度计算需要在cuda上 xff0c cpu不支持半精度 因此首先需要创建半精度变量 xff0c 并放到cuda设备上 部分方法在低版本不支持 xff0c 在高版本支持半精度计算 xff0c
  • yolov5关闭wandb

    yolov5训练过程中wandb总是提示登入账号 xff0c 不登入还不能继续训练 xff0c 想要关闭wandb xff0c 直接不使用即可 在 yolov5 utils loggers wandb wandb utils py中 imp
  • 目标检测 YOLOv5的loss权重,以及与图像大小的关系

    1 目标检测 YOLOv5的loss权重 YOLOv5中有三个损失分别是 box obj cls 在超参数配置文件hyp yaml中可以设置基础值 xff0c 例如 box 0 05 cls 0 5 obj 1 训练使用时 xff0c 在t
  • 手写一个JSON反序列化程序

    上一篇文章 JSON是什么 给大家介绍了JSON的标准规范 xff0c 今天就自己动手写一个JSON的反序列化程序 xff0c 并命名它为 zjson 0 开始之前 本篇文章的目的是学习实践 xff0c 所以我们选择相对简单的Python实
  • yolov5源码解析--输出

    本文章基于yolov5 6 2版本 主要讲解的是yolov5是怎么在最终的特征图上得出物体边框 置信度 物体分类的 一 总体框架 首先贴出总体框架 xff0c 直接就拿官方文档的图了 xff0c 本文就是接着右侧的那三层输出开始讨论 Bac