位置: IT常识 - 正文

YOLOv5的head详解(yolov5 output)

编辑:rootadmin
YOLOv5的head详解

推荐整理分享YOLOv5的head详解(yolov5 output),希望有所帮助,仅作参考,欢迎阅读内容。

文章相关热门搜索词:yolov5讲解,yolov5的使用,yolov5 output,yolov5 result,yolov5实现,yolov5修改head,yolo head,yolov4 head,内容如对您有帮助,希望把文章链接给更多的朋友!

YOLOv5的head详解

在前两篇文章中我们对YOLO的backbone和neck进行了详尽的解读,如果有小伙伴没看这里贴一下传送门: YOLOv5的Backbone设计 YOLOv5的Neck端设计 在这篇文章中,我们将针对YOLOv5的head进行解读,head虽然在网络中占比最少,但这却是YOLO最核心的内容,话不多说,进入正题。

1 YOLOv5s网络结构总览

要了解head,就不能将其与前两部分割裂开。head中的主体部分就是三个Detect检测器,即利用基于网格的anchor在不同尺度的特征图上进行目标检测的过程。由下面的网络结构图可以很清楚的看出:当输入为640*640时,三个尺度上的特征图分别为:80x80、40x40、20x20。现在问题的关键变为,Detect的过程细节是怎样的?如何在多个检测框中选择效果最好的?

2 YOLO核心:Detect

首先看一下yolo中Detect的源码组成:

class Detect(nn.Module): stride = None # strides computed during build onnx_dynamic = False # ONNX export parameter 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 self.grid = [torch.zeros(1)] * self.nl # init grid 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.m = nn.ModuleList(nn.Conv2d(x, self.no * self.na, 1) for x in ch) # output conv self.inplace = inplace # use in-place 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 bs, _, ny, nx = x[i].shape # x(bs,255,20,20) to x(bs,3,20,20,85) 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.grid[i].shape[2:4] != x[i].shape[2:4] or self.onnx_dynamic: self.grid[i], self.anchor_grid[i] = self._make_grid(nx, ny, i) y = x[i].sigmoid() if self.inplace: y[..., 0:2] = (y[..., 0:2] * 2. - 0.5 + self.grid[i]) * self.stride[i] # xy 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 xy = (y[..., 0:2] * 2. - 0.5 + self.grid[i]) * self.stride[i] # xy wh = (y[..., 2:4] * 2) ** 2 * self.anchor_grid[i] # wh y = torch.cat((xy, wh, y[..., 4:]), -1) z.append(y.view(bs, -1, self.no)) return x if self.training else (torch.cat(z, 1), x) def _make_grid(self, nx=20, ny=20, i=0): d = self.anchors[i].device yv, xv = torch.meshgrid([torch.arange(ny).to(d), torch.arange(nx).to(d)]) grid = torch.stack((xv, yv), 2).expand((1, self.na, ny, nx, 2)).float() anchor_grid = (self.anchors[i].clone() * self.stride[i]) \ .view((1, self.na, 1, 1, 2)).expand((1, self.na, ny, nx, 2)).float() return grid, anchor_gridYOLOv5的head详解(yolov5 output)

Detect很重要,但是内容不多,那我们就将其解剖开来,一部分一部分地看。

2.1 initial部分 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 self.grid = [torch.zeros(1)] * self.nl # init grid 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.m = nn.ModuleList(nn.Conv2d(x, self.no * self.na, 1) for x in ch) # output conv self.inplace = inplace # use in-place ops (e.g. slice assignment) self.anchor=anchors

initial部分定义了Detect过程中的重要参数 1. nc:类别数目 2. no:每个anchor的输出,包含类别数nc+置信度1+xywh4,故nc+5 3. nl:检测器的个数。以上图为例,我们有3个不同尺度上的检测器:[[10, 13, 16, 30, 33, 23], [30, 61, 62, 45, 59, 119], [116, 90, 156, 198, 373, 326]],故检测器个数为3。 4. na:每个检测器中anchor的数量,个数为3。由于anchor是w h连续排列的,所以需要被2整除。 5. grid:检测器Detect的初始网格 6. anchor_grid:anchor的初始网格 7. m:每个检测器的最终输出,即检测器中anchor的输出no×anchor的个数nl。打印出来很好理解(60是因为我的数据集nc为15,coco是80):

ModuleList( (0): Conv2d(128, 60, kernel_size=(1, 1), stride=(1, 1)) (1): Conv2d(256, 60, kernel_size=(1, 1), stride=(1, 1)) (2): Conv2d(512, 60, kernel_size=(1, 1), stride=(1, 1)))2.2 forward def forward(self, x): z = [] # inference output for i in range(self.nl): x[i] = self.m[i](x[i]) # conv bs, _, ny, nx = x[i].shape # x(bs,255,20,20) to x(bs,3,20,20,85) 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.grid[i].shape[2:4] != x[i].shape[2:4] or self.onnx_dynamic: self.grid[i], self.anchor_grid[i] = self._make_grid(nx, ny, i) y = x[i].sigmoid() if self.inplace: y[..., 0:2] = (y[..., 0:2] * 2. - 0.5 + self.grid[i]) * self.stride[i] # xy 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 xy = (y[..., 0:2] * 2. - 0.5 + self.grid[i]) * self.stride[i] # xy wh = (y[..., 2:4] * 2) ** 2 * self.anchor_grid[i] # wh y = torch.cat((xy, wh, y[..., 4:]), -1) z.append(y.view(bs, -1, self.no)) return x if self.training else (torch.cat(z, 1), x)

在forward操作中,网络接收3个不同尺度的特征图,如下图所示:

for i in range(self.nl): x[i] = self.m[i](x[i]) # conv bs, _, ny, nx = x[i].shape # x(bs,255,20,20) to x(bs,3,20,20,85) x[i] = x[i].view(bs, self.na, self.no, ny, nx).permute(0, 1, 3, 4, 2).contiguous()

网络的for loop次数为3,也就是依次在这3个特征图上进行网格化预测,利用卷积操作得到通道数为no×nl的特征输出。拿128x80x80举例,在nc=15的情况下经过卷积得到60x80x80的特征图,这个特征图就是后续用于格点检测的特征图。

if not self.training: # inference if self.grid[i].shape[2:4] != x[i].shape[2:4] or self.onnx_dynamic: self.grid[i], self.anchor_grid[i] = self._make_grid(nx, ny, i) def _make_grid(self, nx=20, ny=20, i=0): d = self.anchors[i].device yv, xv = torch.meshgrid([torch.arange(ny).to(d), torch.arange(nx).to(d)]) grid = torch.stack((xv, yv), 2).expand((1, self.na, ny, nx, 2)).float() anchor_grid = (self.anchors[i].clone() * self.stride[i]) \ .view((1, self.na, 1, 1, 2)).expand((1, self.na, ny, nx, 2)).float() return grid, anchor_grid

随后就是基于经过检测器卷积后的特征图划分网格,网格的尺寸是与输入尺寸相同的,如20x20的特征图会变成20x20的网格,那么一个网格对应到原图中就是32x32像素;40x40的一个网格就会对应到原图的16x16像素,以此类推。

y = x[i].sigmoid() if self.inplace: y[..., 0:2] = (y[..., 0:2] * 2. - 0.5 + self.grid[i]) * self.stride[i] # xy 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 xy = (y[..., 0:2] * 2. - 0.5 + self.grid[i]) * self.stride[i] # xy wh = (y[..., 2:4] * 2) ** 2 * self.anchor_grid[i] # wh y = torch.cat((xy, wh, y[..., 4:]), -1) z.append(y.view(bs, -1, self.no))

这里其实就是预测偏移的主体部分了。

y[..., 0:2] = (y[..., 0:2] * 2. - 0.5 + self.grid[i]) * self.stride[i] # xy

这一句是对x和y进行预测。x、y在输入网络前都是已经归一好的(0,1),乘以2再减去0.5就是(-0.5,1.5),也就是让x、y的预测能够跨网格进行。后边self.grid[i]) * self.stride[i]就是将相对位置转为网格中的绝对位置了。

y[..., 2:4] = (y[..., 2:4] * 2) ** 2 * self.anchor_grid[i] # wh

这里对宽和高进行预测,没啥好说的。

z.append(y.view(bs, -1, self.no))

最后将结果填入z

本文链接地址:https://www.jiuchutong.com/zhishi/299952.html 转载请保留说明!

上一篇:Vite4+Pinia2+vue-router4+ElmentPlus搭建Vue3项目(组件、图标等按需引入)[保姆级]

下一篇:gdal概览(gdal官方文档)

  • 华为手机自动升级鸿蒙系统怎么回事(华为手机自动升级后密码错误)

    华为手机自动升级鸿蒙系统怎么回事(华为手机自动升级后密码错误)

  • 笔记本突然断电后开不了机(笔记本突然断电关机开不了机)

    笔记本突然断电后开不了机(笔记本突然断电关机开不了机)

  • word为什么有空格却删的是字(word为什么空白页删不掉)

    word为什么有空格却删的是字(word为什么空白页删不掉)

  • 小米手环可以定位吗(小米手环可以定位找人吗)

    小米手环可以定位吗(小米手环可以定位找人吗)

  • 苹果11掉漆可以换吗(苹果11掉漆可以免费维修吗)

    苹果11掉漆可以换吗(苹果11掉漆可以免费维修吗)

  • iphone6plus如何录屏(iphone6plus怎么录视频教程)

    iphone6plus如何录屏(iphone6plus怎么录视频教程)

  • 为什么qq文件安全扫描失败(为什么qq文件安全检查未通过)

    为什么qq文件安全扫描失败(为什么qq文件安全检查未通过)

  • iphone11信号不稳定怎么办(iphone11信号不稳定4g消失又出现)

    iphone11信号不稳定怎么办(iphone11信号不稳定4g消失又出现)

  • word中定位的快捷键是什么(word2010定位的快捷键)

    word中定位的快捷键是什么(word2010定位的快捷键)

  • 三星s8多长多少cm(三星s8尺寸长宽多少厘米)

    三星s8多长多少cm(三星s8尺寸长宽多少厘米)

  • vivo手机录制屏幕怎么没有声音(vivo手机录制屏幕在哪里开声音)

    vivo手机录制屏幕怎么没有声音(vivo手机录制屏幕在哪里开声音)

  • 饿了么恢复历史订单(饿了么恢复历史订单在哪里看)

    饿了么恢复历史订单(饿了么恢复历史订单在哪里看)

  • 网易云识别音乐在哪(网易云识别音乐能识别应用中播放的吗)

    网易云识别音乐在哪(网易云识别音乐能识别应用中播放的吗)

  • 拼多多满返的钱在哪里(拼多多满返的钱怎么提现)

    拼多多满返的钱在哪里(拼多多满返的钱怎么提现)

  • siri建议快捷指令怎么删除(iphone siri快捷指令怎么设置)

    siri建议快捷指令怎么删除(iphone siri快捷指令怎么设置)

  • 苹果按键音怎么设置声音大小(苹果按键音怎么开)

    苹果按键音怎么设置声音大小(苹果按键音怎么开)

  • 怎么知道手机是什么型号(怎么知道手机是安卓几)

    怎么知道手机是什么型号(怎么知道手机是安卓几)

  • 苹果5s屏幕录制在哪(苹果5s屏幕录制在哪里设置)

    苹果5s屏幕录制在哪(苹果5s屏幕录制在哪里设置)

  • 喜马拉雅小雅nano音箱使用攻略(喜马拉雅小雅nano音箱怎么样)

    喜马拉雅小雅nano音箱使用攻略(喜马拉雅小雅nano音箱怎么样)

  • 复制的文件在手机那能找到(复制的文件在手机哪里)

    复制的文件在手机那能找到(复制的文件在手机哪里)

  • 怎样安装全民k歌(全民k歌app怎么下载)

    怎样安装全民k歌(全民k歌app怎么下载)

  • 如何把文件夹加密码锁(如何把文件夹加密码锁手机)

    如何把文件夹加密码锁(如何把文件夹加密码锁手机)

  • 【Hadoop/Java】基于HDFS的Java Web网络云盘(hadoop java)

    【Hadoop/Java】基于HDFS的Java Web网络云盘(hadoop java)

  • phpcms有什么用(phpcms api)

    phpcms有什么用(phpcms api)

  • 小规模增值税附表一怎么填
  • 两处取得工资的成语
  • 辞退补偿款怎么算
  • 业务招待费扣除基数的收入包括哪些
  • 收到就业补贴财务如何做账
  • 工资个税计提多了实际交的少
  • 外商投资企业采取发包、出租经营
  • 销售之后发生销货折让收到红字发票如何做账?
  • 工程预付款增值税缴纳办法
  • 投资性房地产房租计入什么会计科目
  • 外购的自用小轿车
  • 计提汇算清缴所得税分录
  • 滞纳金是否有上限
  • 同一控制下股权转让印花税
  • 小规模纳税人会计核算健全,能够提供准确
  • 公司如何确定总股本
  • 应付职工薪酬借方负数是什么意思
  • 公司对外租赁
  • 什么是应收
  • 经营性支出包括哪些
  • 加油卡充值开票怎么开
  • win8.1怎么用
  • 如何备份计算机的注册表
  • Linux怎么使用vi
  • 一般纳税人企业所得税税率多少
  • 农产品增值税进项税额
  • 电脑怎样设置共享文件夹
  • 拼多多改地址怎么改视频
  • 对于企业已经入账而银行尚未入账
  • 注册造价师挂靠费如何缴个税?
  • 事业单位接受捐赠固定资产入账
  • 天窗漏进来的水去哪了
  • 商业汇票的承兑银行必须具备下列条件
  • tensorflow安装教程pycharm
  • 收到银行退回账号短信
  • php文件缓存方法有哪些
  • mysql_escape_string()函数用法分析
  • 固定资产进项税额怎么抵扣
  • 发放职工薪酬账务怎么做
  • 运输中合理损耗计入采购成本
  • but all failed
  • 微信支付php开发流程
  • php怎么获取post数据
  • discuz php版本
  • 顶账用什么东西顶好
  • 支付中间人佣金违法吗
  • 2201应付职工薪酬
  • g郭敬明
  • python 协程 yield
  • Apache RocketMQ 5.0 笔记
  • 进项抵扣了还要交附加嘛
  • 外商投资企业 外资企业
  • 简易计税的增值税税率
  • 预收账款是怎么算的
  • 个体户是什么样的
  • 主办会计的工作内容和职责
  • 以前年度损益调整怎么结转
  • 用于不动产的进项税额
  • 公司中标
  • 冲销预付账款后怎么做账
  • 垫付的资金
  • 免税收入啥意思
  • 公认会计原则是哪个国家的?
  • 初级会计现值的计算
  • window下mysql忘记密码
  • windowsxp能用pr吗
  • 复制system
  • vmware虚拟机怎么卸载不了
  • win1共享
  • gcasInstallHelper.exe是什么进程 作用是什么 gcasInstallHelper进程查询
  • windows8c盘满了怎么清理
  • win8.1怎么关闭防火墙
  • Unity3D-SLua+ProtocolBuffers构建说明
  • js date用法
  • viewpager2+fragment
  • real interval
  • win10 putty
  • nodejs indexof
  • 淄博市税务局投诉电话
  • 如何查看自己公司的税种
  • 免责声明:网站部分图片文字素材来源于网络,如有侵权,请及时告知,我们会第一时间删除,谢谢! 邮箱:opceo@qq.com

    鄂ICP备2023003026号

    网站地图: 企业信息 工商信息 财税知识 网络常识 编程技术

    友情链接: 武汉网站建设