目标检测——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.hqwc.cn/news/623907.html

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

相关文章

【数据结构】遍历二叉树(递归和非递归遍历的先序、中序和后序遍历、层次遍历法)

目录 【数据结构】遍历二叉树&#xff08;递归和非递归遍历的先序、中序和后序遍历、层次遍历法&#xff09;一、递归算法先&#xff08;根&#xff09;序的遍历算法中&#xff08;根&#xff09;序的遍历算法后&#xff08;根&#xff09;序的遍历算法 二、非递归算法层次遍历…

【vue】Pinia-2 安装Pinia,使用store

1. 安装Pinia 在项目路径下执行npm install pinia 在package.json中查看 2. 使用store 在main.js中添加 import { createPinia } from pinia const pinia createPinia()修改createApp方法 最后示例如下&#xff08;三处修改&#xff09; import { createApp } from vue //…

JRT多服务器同步程序

之前的JRT只部署在一个服务器&#xff0c;实际运用可能会有数台、数十台、或者更多服务器。那么多台服务器就需要程序同步机制。这里借助Rsync同步&#xff0c;但是有个问题是Rsync同步jar之后他不知道是否需要重启站点&#xff0c;为此实现java控制台驱动Rsync&#xff0c;重定…

车载摄像头畸变校正解决方案,打造无畸变高清视界

在车载摄像头日益普及的今天&#xff0c;摄像头图像的畸变问题成为了制约图像质量提升的一大瓶颈。畸变不仅影响画面的美观度&#xff0c;更关键的是它可能导致智能驾驶系统对环境的误判&#xff0c;进而威胁到行车安全。美摄科技凭借其在图像处理领域的深厚实力&#xff0c;推…

cpcie和cpci差别

CPCI总线 •PCI总线作为处理器系统的局部总线&#xff0c;主要目的是为了连接外部设备&#xff0c;而不是作为处理器的系统总线连接Cache和主存储器 •(1) PCI总线空间与处理器空间隔离 •(2) 可扩展性 桥 •(3) 动态配置机制即插即用 •(4) 总线带宽 •(5) 共享总线机制 •…

2024年nodejs调用小红书最新关注(粉丝)follow接口,api接口分析2004-04-16

一、打开chrome按f12&#xff0c;点击右上角的“关注”按钮&#xff0c;抓包位置如下&#xff1a; (图1 follow接口) 二、follow接口分析 1、请求地址 https://edith.xiaohongshu.com/api/sns/web/v1/user/follow 2、请求方法: POST 3、请求头&#xff1a; :authority: edith…

Web3.0与AI的交融:开启智能互联网新时代

目前有140 多个 Web3 AI 概念项目&#xff0c;覆盖了基础设施、数据、预测市场、计算与算力、教育、DeFi & 跨链、安全、NFT & 游戏 & 元宇宙、搜索引擎、社交 & 创作者经济、AI 聊天机器人、DID & 消息传递、治理、医疗、交易机器人等诸多方向。持续关注…

MongoDB的go SDK使用集锦

在上一章解读MongoDB官方文档获取mongo7.0版本的安装步骤与基本使用介绍了如何使用mongo shell操作mongo数据库&#xff0c;接下来介绍如何使用sdk来操作数据库&#xff0c;这里以go语言为例&#xff0c;其他语言请查看源文档mongo docs Quick Start 内置数据结构 MongoDB是存…

关于idea中mybatis插件,下载后,无法生成代码模板--解决方法

一、不用相信网上其他解决方法 1.1试过&#xff0c;无效 二、解决方法 2.1【注&#xff1a;多试几次】重新下载&#xff0c;并重新启动idea 三、操作方法 3.1步骤 3.2idea重启&#xff0c;【如果没有重启】手动重启&#xff0c;必须有&#xff0c;很重要 3.3重新下载mybat…

电商技术揭秘九:搜索引擎中的SEO数据分析与效果评估

相关系列文章 电商技术揭秘一&#xff1a;电商架构设计与核心技术 电商技术揭秘二&#xff1a;电商平台推荐系统的实现与优化 电商技术揭秘三&#xff1a;电商平台的支付与结算系统 电商技术揭秘四&#xff1a;电商平台的物流管理系统 电商技术揭秘五&#xff1a;电商平台的个性…

【触想智能】如何选购到一款合适的工业电脑一体机

工业电脑一体机是专为工业环境而设计的一种工业计算机。工业电脑一体机和普通的计算机不一样&#xff0c;它对产品的参数性能要求很高&#xff0c;因为它们通常会运行在高低温、电磁干扰、高粉尘、湿度大的恶劣环境中&#xff0c;所以相应的要求工业电脑一体机必须具备良好的宽…

47.HarmonyOS鸿蒙系统 App(ArkUI)创建轮播效果

创建轮播效果&#xff0c;共3页切换 Entry Component struct Index {State message: string Hello Worldprivate swiperController: SwiperController new SwiperController()build() {Swiper(this.swiperController) {Text("第一页").width(90%).height(100%).bac…