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

  • bilibili电视版怎么开弹幕(bilibili电视版怎么用)

    bilibili电视版怎么开弹幕(bilibili电视版怎么用)

  • 魅族18有反向充电功能吗(魅族18pro反向充电)

    魅族18有反向充电功能吗(魅族18pro反向充电)

  • 小米10有至尊版吗(小米10至尊版换电池多少钱)

    小米10有至尊版吗(小米10至尊版换电池多少钱)

  • 小米手机的镜子在哪(小米手机的镜子功能怎样打开)

    小米手机的镜子在哪(小米手机的镜子功能怎样打开)

  • 小米8烧屏怎么恢复(小米烧屏怎么办)

    小米8烧屏怎么恢复(小米烧屏怎么办)

  • 内存1t是多少g(内存1t多大)

    内存1t是多少g(内存1t多大)

  • 微信取消赞会有提醒吗(微信取消赞会有提示吗)

    微信取消赞会有提醒吗(微信取消赞会有提示吗)

  • 抖音狂点爱心有什么用(抖音点爱心有钱吗)

    抖音狂点爱心有什么用(抖音点爱心有钱吗)

  • 苹果x怎么显示电量百分比(苹果x怎么显示电量)

    苹果x怎么显示电量百分比(苹果x怎么显示电量)

  • 网络攻击的三种类型(网络攻击的三种逻辑阶段)

    网络攻击的三种类型(网络攻击的三种逻辑阶段)

  • 知乎可以看最近访客吗(知乎看最近访客)

    知乎可以看最近访客吗(知乎看最近访客)

  • 苹果原相机自拍脸为什么是歪的(苹果原相机自拍鼻子为什么是斜的)

    苹果原相机自拍脸为什么是歪的(苹果原相机自拍鼻子为什么是斜的)

  • 支付宝怎么关闭运动步数(支付宝怎么关闭扣费服务)

    支付宝怎么关闭运动步数(支付宝怎么关闭扣费服务)

  • 华为m6可以插存储卡吗(华为m6可以扩展存储吗)

    华为m6可以插存储卡吗(华为m6可以扩展存储吗)

  • 照片kb太小怎么变大(照片kb过小怎么处理变大)

    照片kb太小怎么变大(照片kb过小怎么处理变大)

  • 淘宝网站提醒在哪里(淘宝 网站提醒)

    淘宝网站提醒在哪里(淘宝 网站提醒)

  • yy有手机版本的吗(yy有手机版本的软件吗)

    yy有手机版本的吗(yy有手机版本的软件吗)

  • 华为的标准进程是多少(华为 进程管理)

    华为的标准进程是多少(华为 进程管理)

  • 小米5x怎么设置呼叫转接(小米5x怎么设置双卡双网络)

    小米5x怎么设置呼叫转接(小米5x怎么设置双卡双网络)

  • 智慧团建如何在手机上登录(智慧团建如何在手机录入新团员)

    智慧团建如何在手机上登录(智慧团建如何在手机录入新团员)

  • 抖音怎么缩小屏幕比例(抖音缩小屏幕怎么变音频了)

    抖音怎么缩小屏幕比例(抖音缩小屏幕怎么变音频了)

  • vivoy53怎么扩大内存(vivoy53怎么扩大内存的步骤)

    vivoy53怎么扩大内存(vivoy53怎么扩大内存的步骤)

  • win10任务栏怎么合并? win10设置合并任务栏按钮的方法(win10任务栏怎么隐藏)

    win10任务栏怎么合并? win10设置合并任务栏按钮的方法(win10任务栏怎么隐藏)

  • 走进小程序【六】微信小程序架构之【视图层】万字详解(进入微信小程序)

    走进小程序【六】微信小程序架构之【视图层】万字详解(进入微信小程序)

  • 业务招待费福利费怎么算超支
  • 月末转出未交增值税时应借记的科目是
  • 一个月没有账务往来,现金日记账如何登记?
  • 如何确认产品销售收入
  • 全额工资是到手工资吗
  • 建筑行业增值税税负率一般控制在多少合适
  • 财务报表中的存货怎么做掉
  • 营改增一般纳税人标准
  • 一个公司控股另一个公司
  • 阿里巴巴新任董事长简历
  • 公司之间有哪些关系
  • 定额发票已经取得怎么办
  • 印花税为什么不计入资产成本
  • 增值税发票不见了可以重开吗
  • 小规模纳税人咨询费税率
  • 印花税是按含税还是不含税缴
  • 个人所得税成本费用包括哪些
  • 小规模纳税人残疾人纳税优惠
  • 发票作废时间有多长
  • 发票打错了重新打要交税吗
  • 应收账款坏账准备是信用减值损失还是资产
  • ukey开票界面
  • 怎么利用喜欢自己的人
  • linux强制删除文件夹
  • 定期存款利息收入怎么算
  • 临时设施是租入还是租入
  • 举办活动购买的东西会计分录
  • 电脑管家lol修复
  • 公司多交的公积金能退吗
  • php mb_convert_encoding
  • 怎么修改以前年度的账
  • php和aspnet哪个好
  • 什么叫php
  • 如何把电脑上锁屏密码
  • 电脑怎样设置共享文件夹
  • 孕妇不能吃哪些水果?
  • windows7增加桌面
  • 基于Python的flask百战电商流程图
  • php aes
  • 企业所得税核定征收方法有哪两种
  • 资产处置收益科目借贷方向
  • javascript图表
  • 企业取得交易性金融资产时
  • jasperreports入门
  • 前端手撕代码
  • web应用技术是什么
  • 车船税每年都要多少钱
  • discuz 首页设置
  • 扶贫资金是如何发放的
  • 预扣预缴和汇算清缴是什么意思
  • mysql查询语句慢怎么优化
  • 汇总记账凭证账务处理
  • 小规模纳税人个体户经营所得税怎么算
  • 固定资产核销是资产损失吗
  • 残保金如何做会计分录
  • 疫情期间增值税减免政策截止时间
  • 报销电话费计入什么科目
  • 增值税加计扣除最新政策2023
  • 物流辅助服务费属于什么费用
  • 资产减值损失如何计算
  • 内部债权债务的抵消分录
  • 费用报销单怎样写记账凭证呢
  • 对非本单位的营销方案
  • 强制执行的款在执行局领取吗
  • 零余额账户对账余额调节表
  • 发票隔年还能做账吗
  • 利用rpm安装软件包时,应使用命令选项
  • sql数据库分布式部署
  • mysql5.7分区表
  • Linux系统下Qt的基本安装和配置
  • Node.js中的construct构造函数
  • 超酷风格
  • unity xcode
  • js判断元素是否显示
  • Linux 中的 lo:0
  • [置顶]马粥街残酷史
  • js正则表达式变量
  • python批量修改文本内容
  • jquery简单吗
  • 没有代理记账资质做代理记账业务
  • 免责声明:网站部分图片文字素材来源于网络,如有侵权,请及时告知,我们会第一时间删除,谢谢! 邮箱:opceo@qq.com

    鄂ICP备2023003026号

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

    友情链接: 武汉网站建设