位置: 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官方文档)

  • 华为mate30pro丹霞橙是皮革的吗(华为mate30pro丹霞橙)

    华为mate30pro丹霞橙是皮革的吗(华为mate30pro丹霞橙)

  • b站设置清晰度不见了(b站怎么设置清晰度)

    b站设置清晰度不见了(b站怎么设置清晰度)

  • 32位win7支持多少内存(32位win7系统支持多大内存)

    32位win7支持多少内存(32位win7系统支持多大内存)

  • 80plus电源是什么意思(80v电源)

    80plus电源是什么意思(80v电源)

  • 一个钉钉怎么登录两个账号(一个钉钉怎么登录两个班级)

    一个钉钉怎么登录两个账号(一个钉钉怎么登录两个班级)

  • 电车充电器红灯一闪一闪什么情况(电车充电器红灯一闪一闪的什么情况)

    电车充电器红灯一闪一闪什么情况(电车充电器红灯一闪一闪的什么情况)

  • 闲鱼多久收货不会被冻结(闲鱼多久不收货会被自动确认)

    闲鱼多久收货不会被冻结(闲鱼多久不收货会被自动确认)

  • 华为手机怎么关机重启(华为手机怎么关闭云空间)

    华为手机怎么关机重启(华为手机怎么关闭云空间)

  • 苹果手机怎么设置一个圈(苹果手机怎么设置密码锁屏)

    苹果手机怎么设置一个圈(苹果手机怎么设置密码锁屏)

  • 什么是开放式耳机(什么是开放耳选配)

    什么是开放式耳机(什么是开放耳选配)

  • 手机dc调光有什么好处(手机dc调光有什么用)

    手机dc调光有什么好处(手机dc调光有什么用)

  • 微型计算机的基本功能(微型计算机的基本输入设备有)

    微型计算机的基本功能(微型计算机的基本输入设备有)

  • ps钢笔生成选区快捷键(ps钢笔工具如何建立选区)

    ps钢笔生成选区快捷键(ps钢笔工具如何建立选区)

  • 飞行模式可以用wifi吗(飞行模式可以用蓝牙耳机吗)

    飞行模式可以用wifi吗(飞行模式可以用蓝牙耳机吗)

  • i3 530配什么主板(i3 530 cpu怎么样)

    i3 530配什么主板(i3 530 cpu怎么样)

  • 1pps是什么意思(pp1是什么意思哦)

    1pps是什么意思(pp1是什么意思哦)

  • 手机怎么拦截广告(手机怎么拦截广告弹窗vivo)

    手机怎么拦截广告(手机怎么拦截广告弹窗vivo)

  • 文档页面怎么变成一页(文档页面怎么变成竖的)

    文档页面怎么变成一页(文档页面怎么变成竖的)

  • 微信爱心怎么打出来(微信爱心怎么打字打出心形)

    微信爱心怎么打出来(微信爱心怎么打字打出心形)

  • 苹果6怎么连蓝牙耳机(苹果怎么连蓝牙耳机)

    苹果6怎么连蓝牙耳机(苹果怎么连蓝牙耳机)

  • a1778是否卡贴机(a1518卡贴)

    a1778是否卡贴机(a1518卡贴)

  • 苹果6s微信怎么更新(苹果6s微信怎么分身)

    苹果6s微信怎么更新(苹果6s微信怎么分身)

  • oppor15怎么关闭耳机模式(oppor15怎么关闭速览)

    oppor15怎么关闭耳机模式(oppor15怎么关闭速览)

  • 苹果双卡怎么切换发短信(苹果双卡怎么切换打电话)

    苹果双卡怎么切换发短信(苹果双卡怎么切换打电话)

  • excel整列重复值统计(一列的重复值)

    excel整列重复值统计(一列的重复值)

  • 专项附加扣除可以随时填报吗
  • 一般计税预缴增值税3%怎么办
  • 个人收到银行承兑汇票
  • 被投资方宣告发放股票股利
  • 营业账簿指什么
  • 企业所得税季度申报时间
  • 税控服务费抵税
  • 小企业会计准则适用于哪些企业
  • 税收和税法是什么关系
  • 增资导致丧失控制权
  • 以资抵债是利空还是利好
  • 税法上的营业收入与会计差异
  • 收到小额贷款怎么做账
  • 销售原材料收到商业承兑汇票会计分录
  • 企业收到跨年度的政府补贴款,是否计入当年企业所得税
  • 用库存物资抵货怎么做账
  • 管理费用的明细科目怎么写
  • 增值税抵扣凭证包括桥闸通行费发票
  • 涂料发票几个点的税
  • 补缴地价款是什么意思
  • 开票方开错发票
  • 计提的电费和支付电费差额如何调整
  • 发票进项抵扣有什么用
  • 分公司年报怎么查
  • 展厅设计费用计什么科目
  • 土地使用权出让金收费标准
  • 增值税退税金额怎么算
  • 融资租赁纳税人是指
  • 职工教育扣除标准是什么
  • 以前年度损益调整属于哪类科目
  • 平板电脑的windows更新有必要吗
  • 笔记本电脑按键掉了怎么修
  • 开办费的账务处理实例
  • 营改增改了什么
  • 低值易耗品现在还用吗
  • PHP:imagecreatefromgd()的用法_GD库图像处理函数
  • thinkphp post
  • 布兹洛夫
  • node.js in action
  • npm ERR! code ERESOLVEnpm ERR! ERESOLVE could not resolve dependency
  • echarts图大小设置
  • 银行利息的收入计入什么科目
  • 成本会计科目的三级科目有哪些
  • 小企业机械作业指导书
  • 出差餐补属于福利费吗
  • 企业存货的账务处理办法
  • 小微企业的认定标准企业所得税
  • 小规模纳税人水利基金税率
  • 内部审计主要目的
  • 长期待摊费用会计分录
  • 工商年报和税务年报有什么区别
  • 基建结束后该做哪些工作
  • 招待费进项税额可以抵扣吗
  • 坏账准备的核算公式
  • 作废的发票对方还能认证吗
  • 企业申请支付宝账号步骤
  • 预收账款账务处理流程图
  • 房屋装修支出怎么做账
  • mysql时间语句
  • win10简单版
  • win10预览版21277
  • ubuntu安装transmission
  • drivemgr.exe 病毒介绍
  • SCHDPL32.EXE - SCHDPL32是什么进程 有什么用
  • win7架设ftp服务器
  • linux设置utf8编码
  • win8一直配置更新
  • linux定时任务详解
  • cocos2dx游戏
  • jquery删除所有class
  • js格式化输出
  • unity如何导入资源文件
  • Unity3D Vuforia Android 相机调焦
  • js实现双击屏幕放大
  • Python 基于豆瓣电影的可视化
  • 详解各种汽油一吨等于多少升
  • 使用权资产
  • Jquery实现select multiple左右添加和删除功能的简单实例
  • 12366纳税服务热线坐席人员
  • 税控专用设备包括哪些
  • 免责声明:网站部分图片文字素材来源于网络,如有侵权,请及时告知,我们会第一时间删除,谢谢! 邮箱:opceo@qq.com

    鄂ICP备2023003026号

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

    友情链接: 武汉网站建设