目标检测——YOLO系列学习(一)YOLOv1

YOLO可以说是单阶段的目标检测方法的集大成之作,必学的经典论文,从准备面试的角度来学习一下yolo系列。


YOLOv1

1.RCNN系列回顾

RCNN系列,无论哪种算法,核心思路都是Region Proposal(定位)+ classifier(修正定位+分类)。所以也被称为两阶段算法。但是难以达到实时检测的效果,因此yolov1将其修改为单阶段的算法,yolov1虽然牺牲了一定的精度,但是检测速度大幅提升,而后续的yolo版本在其之上改进,现在已经有yolov9和yolo-world了,成为主流的目标检测模型。

2.YOLOv1

(部分内容和图参考保姆级教程:图解目标检测算法YOLOv1 - 知乎 (zhihu.com))

论文原文:

1506.02640.pdf (arxiv.org)icon-default.png?t=N7T8https://arxiv.org/pdf/1506.02640.pdf在讲解过程中会出现很多专业词汇,会挨着进行说明。

YOLOv1的核心思路就是舍弃Region Proposal这个极其耗时的过程,转而进行回归。怎么实现舍弃RP的,就是学习的关键。

(1)核心思想

采用利用整张图作为网络的输入,将图像划分为S*S个grid,某一个grid只关注于预测物体中心在这个grid中的目标,整个网络最后直接在输出层回归 bounding box 的位置和 bounding box 所属的类别。

Grid和Bouding Box

这里可能会产生一点误解,故区分一下。

Grid:将图片直接划分为S*S个grid,位置是固定死的,比如上图中,划分为了7*7个grid。

Bouding Box:就是最后检测出物体的框,如上图中框住狗狗的红色框,在算法流程中,可以用两种数据形式表示,一种是使用中心坐标+长宽的形式(Cx,Cy,H,W),一种是使用左上和右下角点坐标的形式(x1,y1,x2,y2)。而每个框除了要包含位置信息,还包含了该框是否包含物体的置信度,这个置信度怎么计算的我们后面讲解,这里只需要记住每个Bounding Box其实对应了5个数据。

置信度(Confidence)的计算

置信度就是算法的自信心得分,这个值越高,代表这个BoundigBox里越有可能包含物体。计算方式如下:

Confidence=Pr(Object)*IOU^{truth}_{pred}

Pr(Object)为边界框内存在对象的概率,若存在对象,Pr⁡(Object)=1,否则Pr⁡(Object)=0。

但是这里要注意一下,其实我们整个网络的计算中是不需要用这个公式计算的,网络输出一个0~1的值就好。

IOU(Intersection over Union ratio)

IOU又叫做交并比,其实很好理解,就是两个框计算出来的一个值,意义上来看,IOU值越大,表示两个框的重合度越高,从公式上来看:

IOU=\frac{GT\cap Pred}{GT\cup Pred}

一个实现代码如下:

def calculate_iou(bbox1,bbox2):"""计算bbox1=(x1,y1,x2,y2)和bbox2=(x3,y3,x4,y4)两个bbox的iou"""intersect_bbox = [0., 0., 0., 0.]  # bbox1和bbox2的交集if bbox1[2]<bbox2[0] or bbox1[0]>bbox2[2] or bbox1[3]<bbox2[1] or bbox1[1]>bbox2[3]:passelse:intersect_bbox[0] = max(bbox1[0],bbox2[0])intersect_bbox[1] = max(bbox1[1],bbox2[1])intersect_bbox[2] = min(bbox1[2],bbox2[2])intersect_bbox[3] = min(bbox1[3],bbox2[3])area1 = (bbox1[2] - bbox1[0]) * (bbox1[3] - bbox1[1])  # bbox1面积area2 = (bbox2[2] - bbox2[0]) * (bbox2[3] - bbox2[1])  # bbox2面积area_intersect = (intersect_bbox[2] - intersect_bbox[0]) * (intersect_bbox[3] - intersect_bbox[1])  # 交集面积# print(bbox1,bbox2)# print(intersect_bbox)# input()if area_intersect>0:return area_intersect / (area1 + area2 - area_intersect)  # 计算iouelse:return 0

按照这个思路,我们可以简要理一下网络的输入输出:

输入一张固定大小的图像,规定划分的格子数S*S,规定每个格子要预测几个框B。

输出为一个S*S*(B*5+Class),S和B对应输入,5就是boundingbox中包含的五个信息,class就是类别的预测,这里类别使用的是one-hot编码。

以作者在论文里提到的PASCAL VOC上的实验为例:

S=7,B=5,有20个类别,故输出tensor的维度为7*7*(5*2)

这里有个很容易错误理解的点,就是这个class的分类结果其实是对应了这一个grid的,一个grid输出一个20维的分类结果,而不是整个grid所得到的两个BoundingBox的分类结果,

(2)网络结构

YOLOv1的数据流如下:

  • resize图片尺寸(没有ROI)
  • 输入网络,输出tensor
  • 非极大值抑制(NMS)

网络的结构如下:

这里光看图可能很多初学的同学不是很看的懂,我们来看看一个简单的pytorch版本:

参考:动手学习深度学习pytorch版——从零开始实现YOLOv1_自己实现的yolov-CSDN博客

这一部分需要说明一下,由于原论文是采用自己设计的20层卷积层先在ImageNet上训练了一周,完成特征提取部分的训练。我们作为学习者而非发明者来说,花一周时间训练实在是太长了。因此,在这里我打算对原论文的结构做一点改变。YOLOv1的前20层是用于特征提取的,也就是随便替换为一个分类网络(除去最后的全连接层)其实都行。因此,我打算使用ResNet34的网络作为特征提取部分。这样做的好处是,pytorch的torchvision中提供了ResNet34的预训练模型,训练集也是ImageNet,等于说有先成训练好的模型可以直接使用,从而免去了特征提取部分的训练时间。然后,除去ResNet34的最后两层,再连接上YOLOv1的最后4个卷积层和两个全连接层,作为我们训练的网络结构。
  此外,还进行了一些小调整,比如最后增加了一个Sigmoid层,以及在卷积层后增加了BN层等等。具体代码如下:

import torchvision.models as tvmodel
import torch.nn as nn
import torchclass YOLOv1_resnet(nn.Module):def __init__(self):super(YOLOv1_resnet,self).__init__()resnet = tvmodel.resnet34(pretrained=True)  # 调用torchvision里的resnet34预训练模型resnet_out_channel = resnet.fc.in_features  # 记录resnet全连接层之前的网络输出通道数,方便连入后续卷积网络中self.resnet = nn.Sequential(*list(resnet.children())[:-2])  # 去除resnet的最后两层# 以下是YOLOv1的最后四个卷积层self.Conv_layers = nn.Sequential(nn.Conv2d(resnet_out_channel,1024,3,padding=1),nn.BatchNorm2d(1024),  # 为了加快训练,这里增加了BN层,原论文里YOLOv1是没有的nn.LeakyReLU(),nn.Conv2d(1024,1024,3,stride=2,padding=1),nn.BatchNorm2d(1024),nn.LeakyReLU(),nn.Conv2d(1024, 1024, 3, padding=1),nn.BatchNorm2d(1024),nn.LeakyReLU(),nn.Conv2d(1024, 1024, 3, padding=1),nn.BatchNorm2d(1024),nn.LeakyReLU(),)# 以下是YOLOv1的最后2个全连接层self.Conn_layers = nn.Sequential(nn.Linear(7*7*1024,4096),nn.LeakyReLU(),nn.Linear(4096,7*7*30),nn.Sigmoid()  # 增加sigmoid函数是为了将输出全部映射到(0,1)之间,因为如果出现负数或太大的数,后续计算loss会很麻烦)def forward(self, input):input = self.resnet(input)input = self.Conv_layers(input)input = input.view(input.size()[0],-1)input = self.Conn_layers(input)return input.reshape(-1, (5*NUM_BBOX+len(CLASSES)), 7, 7)  # 记住最后要reshape一下输出数据

这里我们主要关注最后两个fc层,是没有使用池化操作的,直接使用view和resize就实现了三维张量和二维张量的转换。

(3)非极大值抑制

非极大值抑制的目的就是去掉一些冗余框。

这一部分可以参考一下:目标检测入门之非最大值抑制(NMS)算法 - 知乎 (zhihu.com)

(4)损失函数

损失函数是理解YOLOv1训练的关键,具体形式如下:

这里的损失函数包括五项:

前两项对应BoundingBox的损失函数(针对x, y, H, W进行学习)

接下来两项对应Confidence的损失函数(针对置信度进行学习)

最后一项对应分类的损失(针对类别label进行学习)

细节上来说:

1.公式中每一个均方误差的系数: 1^{obj}_{i,j}表示的是第i个grid的第j个BoundingBox是否负责Object,每个grid对应的B个BoudingBox中,与GT的IOU最大的BoundingBox才负责这个Object,其余的为1^{noobj}_{i,j},这一部分可以简单看一下代码:

if iou1 >= iou2:coor_loss = coor_loss + 5 * (torch.sum((pred[i,0:2,m,n] - labels[i,0:2,m,n])**2) \+ torch.sum((pred[i,2:4,m,n].sqrt()-labels[i,2:4,m,n].sqrt())**2))obj_confi_loss = obj_confi_loss + (pred[i,4,m,n] - iou1)**2# iou比较小的bbox不负责预测物体,因此confidence loss算在noobj中,注意,对于标签的置信度应该是iou2noobj_confi_loss = noobj_confi_loss + 0.5 * ((pred[i,9,m,n]-iou2)**2)

这里计算obj_confi_loss和noobj_confi_loss使用的pred和IOU都是不一样的,pred[i,4,m,n]中的4对应的是IOU更大的框,9对应的是IOU更小的框。

2.这里对 (w,ℎ) 在损失函数中的处理分别取了根号,原因在于,如果不取根号,损失函数往往更倾向于调整尺寸比较大的预测框。例如,20 个像素点的偏差,对于 800x600 的预测框几乎没有影响,此时的IOU数值还是很大,但是对于 30x40 的预测框影响就很大。取根号是为了尽可能的消除大尺寸框与小尺寸框之间的差异。

3.此时再来看 \lambda _{coord}与 \lambda _{noobj} ,YOLO 面临的物体检测问题,是一个典型的类别数目不均衡的问题(Focal Loss就是解决这个问题的,一个面试中常问的点)。其中 49 个格点,含有物体的格点往往只有 3、4 个,其余全是不含有物体的格点。此时如果不采取点措施,那么物体检测的mAP不会太高,因为模型更倾向于不含有物体的格点。 \lambda _{coord}与 \lambda _{noobj}的作用,就是让含有物体的格点,在损失函数中的权重更大,让模型更加“重视”含有物体的格点所造成的损失。在论文中,\lambda _{coord} 与 \lambda _{noobj}的取值分别为 5 与 0.5 。

最后整个Loss部分的代码如下:

class Loss_yolov1(nn.Module):def __init__(self):super(Loss_yolov1,self).__init__()def forward(self, pred, labels):""":param pred: (batchsize,30,7,7)的网络输出数据:param labels: (batchsize,30,7,7)的样本标签数据:return: 当前批次样本的平均损失"""num_gridx, num_gridy = labels.size()[-2:]  # 划分网格数量num_b = 2  # 每个网格的bbox数量num_cls = 20  # 类别数量noobj_confi_loss = 0.  # 不含目标的网格损失(只有置信度损失)coor_loss = 0.  # 含有目标的bbox的坐标损失obj_confi_loss = 0.  # 含有目标的bbox的置信度损失class_loss = 0.  # 含有目标的网格的类别损失n_batch = labels.size()[0]  # batchsize的大小# 可以考虑用矩阵运算进行优化,提高速度,为了准确起见,这里还是用循环for i in range(n_batch):  # batchsize循环for n in range(7):  # x方向网格循环for m in range(7):  # y方向网格循环if labels[i,4,m,n]==1:# 如果包含物体# 将数据(px,py,w,h)转换为(x1,y1,x2,y2)# 先将px,py转换为cx,cy,即相对网格的位置转换为标准化后实际的bbox中心位置cx,xy# 然后再利用(cx-w/2,cy-h/2,cx+w/2,cy+h/2)转换为xyxy形式,用于计算ioubbox1_pred_xyxy = ((pred[i,0,m,n]+n)/num_gridx - pred[i,2,m,n]/2,(pred[i,1,m,n]+m)/num_gridy - pred[i,3,m,n]/2,(pred[i,0,m,n]+n)/num_gridx + pred[i,2,m,n]/2,(pred[i,1,m,n]+m)/num_gridy + pred[i,3,m,n]/2)bbox2_pred_xyxy = ((pred[i,5,m,n]+n)/num_gridx - pred[i,7,m,n]/2,(pred[i,6,m,n]+m)/num_gridy - pred[i,8,m,n]/2,(pred[i,5,m,n]+n)/num_gridx + pred[i,7,m,n]/2,(pred[i,6,m,n]+m)/num_gridy + pred[i,8,m,n]/2)bbox_gt_xyxy = ((labels[i,0,m,n]+n)/num_gridx - labels[i,2,m,n]/2,(labels[i,1,m,n]+m)/num_gridy - labels[i,3,m,n]/2,(labels[i,0,m,n]+n)/num_gridx + labels[i,2,m,n]/2,(labels[i,1,m,n]+m)/num_gridy + labels[i,3,m,n]/2)iou1 = calculate_iou(bbox1_pred_xyxy,bbox_gt_xyxy)iou2 = calculate_iou(bbox2_pred_xyxy,bbox_gt_xyxy)# 选择iou大的bbox作为负责物体if iou1 >= iou2:coor_loss = coor_loss + 5 * (torch.sum((pred[i,0:2,m,n] - labels[i,0:2,m,n])**2) \+ torch.sum((pred[i,2:4,m,n].sqrt()-labels[i,2:4,m,n].sqrt())**2))obj_confi_loss = obj_confi_loss + (pred[i,4,m,n] - iou1)**2# iou比较小的bbox不负责预测物体,因此confidence loss算在noobj中,注意,对于标签的置信度应该是iou2noobj_confi_loss = noobj_confi_loss + 0.5 * ((pred[i,9,m,n]-iou2)**2)else:coor_loss = coor_loss + 5 * (torch.sum((pred[i,5:7,m,n] - labels[i,5:7,m,n])**2) \+ torch.sum((pred[i,7:9,m,n].sqrt()-labels[i,7:9,m,n].sqrt())**2))obj_confi_loss = obj_confi_loss + (pred[i,9,m,n] - iou2)**2# iou比较小的bbox不负责预测物体,因此confidence loss算在noobj中,注意,对于标签的置信度应该是iou1noobj_confi_loss = noobj_confi_loss + 0.5 * ((pred[i, 4, m, n]-iou1) ** 2)class_loss = class_loss + torch.sum((pred[i,10:,m,n] - labels[i,10:,m,n])**2)else:  # 如果不包含物体noobj_confi_loss = noobj_confi_loss + 0.5 * torch.sum(pred[i,[4,9],m,n]**2)loss = coor_loss + obj_confi_loss + noobj_confi_loss + class_loss# 此处可以写代码验证一下loss的大致计算是否正确,这个要验证起来比较麻烦,比较简洁的办法是,将输入的pred置为全1矩阵,再进行误差检查,会直观很多。return loss/n_batch

3.YOLOv1的缺点

  • 由于输出层为全连接层,因此在检测时,YOLO训练模型只支持与训练图像相同的输入分辨率。
  • 虽然每个格子可以预测B个bounding box,但是最终只选择只选择IOU最高的bounding box作为物体检测输出,即每个格子最多只预测出一个物体。当物体占画面比例较小,如图像中包含畜群或鸟群时,每个格子包含多个物体,但却只能检测出其中一个。这是YOLO方法的一个缺陷。
  • YOLO loss函数中,大物体IOU误差和小物体IOU误差对网络训练中loss贡献值接近(虽然采用求平方根方式,但没有根本解决问题)。因此,对于小物体,小的IOU误差也会对网络优化过程造成很大的影响,从而降低了物体检测的定位准确性。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【蓝桥杯嵌入式】串口通信与RTC时钟

【蓝桥杯嵌入式】串口通信与RTC时钟 串口通信cubemx配置串口通信程序设计 RTC时钟cubemx配置程序设计 串口通信 cubemx配置 打开串口通信&#xff0c;并配置波特率为9600 打开串口中断 重定义串口接收与发送引脚&#xff0c;默认是PC4&#xff0c;PC5&#xff0c;需要改为P…

UVA12538 Version Controlled IDE 题解 crope

Version Controlled IDE 传送门 题面翻译 维护一种数据结构&#xff0c;资磁三种操作。 1.在p位置插入一个字符串s 2.从p位置开始删除长度为c的字符串 3.输出第v个历史版本中从p位置开始的长度为c的字符串 1 ≤ n ≤ 50000 1 \leq n \leq 50000 1≤n≤50000&#xff0c;所…

Jmeter如何录制https的系统性能脚本

在使用jmeter录制性能测试脚本时&#xff0c;会遇到网站为http和https两种情况&#xff0c;略有不同&#xff0c;下面介绍一下&#xff1a; 1.Jmeter录制http 1.测试计划–>添加–>非测试元件–>HTTP(S)测试脚本记录器 【HTTP(S)测试脚本记录器】有的版本叫【HTTP代…

element UI table合并单元格方法

废话不多讲&#xff0c;直接上代码&#xff0c;希望能帮到需要的朋友 // 合并单元格function spanMethod({ row, column, rowIndex, columnIndex }) {//定义需要合并的列字段&#xff0c;有哪些列需要合并&#xff0c;就自定义添加字段即可const fields [declareRegion] // …

python课后习题三

题目&#xff1a; 解题过程&#xff1a; 模式A&#xff1a; num int(input("&#xff08;模式A&#xff09;输入数字&#xff1a;")) for i in range(num): for j in range(num): if j < i 1: …

【Flutter】三个Channel(Android-java / Ios-swift)

Channel 实现与原生通信 【1】MethodChannel flutter MethodChannel官方文档 通过MethodChannel来传递数据&#xff0c;调用方法 案例 分别调用Android和Ios原生的获取电量的方法 Flutter端 实例一个MethodChannel&#xff0c; 唯一标识name&#xff0c;定义方法名称get…

微信小程序Skyline模式下瀑布长列表优化成虚拟列表,解决内存问题

微信小程序长列表&#xff0c;渲染的越多就会导致内存吃的越多。特别是长列表的图片组件和广告组件。 为了解决内存问题&#xff0c;所以看了很多人的资料&#xff0c;都不太符合通用的解决方式&#xff0c;很多需要固定子组件高度&#xff0c;但是瀑布流是无法固定的&#xf…

MYSQL 8.0版本修改用户密码(知道登录密码)和Sqlyog错误码2058一案

今天准备使用sqlyog连接一下我Linux上面的mysql数据库&#xff0c;然后就报如下错误 有一个简单的办法就是修改密码为password就完事!然后我就开始查找如何修改密码! 如果是需要解决Sqlyog错误码2058的话&#xff0c;执行以下命令&#xff0c;但是注意root对应host是不是loca…

开源铱塔切换MySQL数据库启动报异常

1.错误日志&#xff1a; 铱塔切换数据库配置为MySQL之后&#xff0c;启动后报错如下&#xff1a; SqlExceptionHelper - Table iotkit.task_info doesnt exist SqlExceptionHelper - Table iotkit.rule_info doesnt exist SqlExceptionHelper - Table iotkit.device_info does…

Word 画三线表模板---一键套用

1、制作三线表 1&#xff09;设置为无边框 选中表格&#xff0c;点击「右键」——「边框」——「无框线」。 2&#xff09;添加上下边框线 选中表格后&#xff0c;点击【右键】——【表格属性】——【边框和底纹】&#xff0c;边框线选择【1.5磅】&#xff0c;然后点击【上框…

JavaScript - 请你为数组自定义一个方法myFind,使其实现find方法的功能

难度级别:中级及以上 提问概率:50% 我们知道数组的find方法是ES6之后出现的,它强调找到第一个符合条件的元素后即跳出循环,不再继续执行,那么如果不用ES6的知识,为数组添加一个自定义方法实现find方法的功能,首先要想到在数组的原型pro…

解决MySQL服务无法启动问题

本地计算机上的 mysq 服务启动后停止。某些服务在未由其他服务或程序使用时将自动停止。 笔记本电脑上的MySQL也是经常在使用的&#xff0c;有一天使用dbeaver连接时突然就连接不上了&#xff01;&#xff01;&#xff01;分析报错信息&#xff0c;也不是口令问题&#xff01;于…

抓住风口,快速上手RAG应用开发!

免责声明~ 任何文章不要过度深思&#xff01; 万事万物都经不起审视&#xff0c;因为世上没有同样的成长环境&#xff0c;也没有同样的认知水平&#xff0c;更「没有适用于所有人的解决方案」&#xff1b; 不要急着评判文章列出的观点&#xff0c;只需代入其中&#xff0c;适度…

c++的学习之路:20、继承(1)

摘要 本章主要是讲以一下继承的一些概念以及使用方法等等。 目录 摘要 一、继承的概念及定义 1、继承的概念 2、继承定义 1.2.1、定义格式 1.2.2、继承关系和访问限定符 1.2.3、继承基类成员访问方式的变化 3、总结 二、基类和派生类对象赋值转换 三、继承中的作用…

【Qt 学习笔记】Qt信号和槽的其他说明及Lambda表达式

博客主页&#xff1a;Duck Bro 博客主页系列专栏&#xff1a;Qt 专栏关注博主&#xff0c;后期持续更新系列文章如果有错误感谢请大家批评指出&#xff0c;及时修改感谢大家点赞&#x1f44d;收藏⭐评论✍ Qt信号和槽的其他说明及Lambda表达式 文章编号&#xff1a;Qt 学习笔记…

JVS-智能BI数据分析:数仓+流程化加工的独特优势

在JVS-BI中采用了数仓流程化加工的方式进行数据分析&#xff0c;主要涉及到数据的离线抽取&#xff0c;流程化加工&#xff0c;生成标准的数据分析结果。 下面我们先看下什么是数仓&#xff1a; 数仓&#xff08;Data Warehouse&#xff09;是一个用于集中存储和管理企业中各种…

iOS 开发中上传 IPA 文件的方法(无需 Mac 电脑)

引言 在 iOS 开发中&#xff0c;将 IPA 文件上传到苹果开发者中心是一个重要的步骤。通常情况下&#xff0c;我们需要使用 Mac 电脑上的 Xcode 或 Application Loader 工具来完成这个任务。然而&#xff0c;如果你没有 Mac 电脑&#xff0c;也没有关系&#xff0c;本文将介绍一…

2024 抖音欢笑中国年(三):编辑器技巧与实践

前言 本次春节活动中&#xff0c;我们大部分场景使用内部的 SAR Creator互动方案来实现。 SAR Creator 是一款基于 TypeScript 的高性能、轻量化的互动解决方案&#xff0c;目前支持了Web和字节内部跨端框架平台&#xff0c;服务于字节内部的各种互动业务&#xff0c;包括但不限…

RabbitMQ的自动应答和手动应答,解决重试死循环

RabbitMQ的自动应答和手动应答&#xff0c;解决重试死循环 1.应答模式 RabbitMQ 中的消息应答模式主要包括两种&#xff1a;自动应答&#xff08;Automatic Acknowledgement&#xff09;和手动应答&#xff08;Manual Acknowledgement&#xff09;。 1、自动应答&#xff1a;…

保研线性代数复习4

一.范数&#xff08;Norms&#xff09; 1.什么是范数&#xff1f; 范数是一个向量空间V的函数&#xff0c;每一个属于向量空间V的向量x都匹配了一个实数&#xff08;它的长度&#xff09;&#xff1a; 2.范数的性质&#xff1f; 齐次性&#xff1a; 正定性&#xff1a; 三…