【目标检测——YOLO系列】YOLOv1 —《You Only Look Once: Unified, Real-Time Object Detection》

news/2024/11/29 19:53:52/

YOLOv1 —《You Only Look Once: Unified, Real-Time Object Detection》


论文地址:1506.02640] You Only Look Once: Unified, Real-Time Object Detection (arxiv.org)

代码地址:pjreddie/darknet: Convolutional Neural Networks (github.com)

1、YOLOv1概述


YOLOv1是一种end to end目标检测算法,由Joseph Redmon等人于2015年提出。它是一种基于单个神经网络的实时目标检测算法。

YOLOv1的中文名称是"你只看一次",这个名字源于算法的工作原理。相比于传统的目标检测算法,YOLOv1采用了全新的思路。**它将目标检测问题转化为一个回归问题,并将整个图像作为输入,一次性地在图像上进行目标检测和定位(单阶段检测模型)。这与传统的滑动窗口或区域提议方法不同,传统方法如RCNN系列(两阶段检测模型)**需要在图像上进行多次检测。

YOLOv1的网络结构由卷积层和全连接层组成,可以将输入图像分割成较小的网格(grid cell)。对于每个网格,YOLOv1预测多个边界框以及每个边界框中是否包含目标物体及其类别。通过对预测结果进行置信度评估和非极大值抑制,可以得到最终的目标检测结果。

相比于传统的目标检测算法,YOLOv1具有较快的检测速度可以实现实时目标检测。然而,由于网络结构的限制,YOLOv1在小目标检测和物体定位精度上可能存在一定的问题(主要因为YOLO中的一个gird cell只能预测判别一个物体,后文细说)。在后续的版本中,如YOLOv2和YOLOv3等,一些改进措施被提出来解决这些问题(比如从Faster Rcnn中引入anchor等),我们在后续文章中会对YOLO其他版本进行继续讲解。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FsCvRkdZ-1685955497733)(E:\学习笔记\深度学习笔记\目标检测专栏\YOLO\yolov1.assets\image-20230605141417299.png)]

我们可以将YOLOv1网络看作一个黑盒,在推理阶段,我们将图像resize到448 * 448后输入到网络中,输出的是一个SxSx(B*5+C)的张量,该张量是关于检测框的位置信息以及检测目标的类别信息等,再经过非极大值抑制处理(关于非极大值抑制NMS,如果不理解,可以参考我们以前的内容:【目标检测】 非极大值抑制—NMS_卖报的大地主的博客-CSDN博客),得到最后的检测结果(最终的预测框位置信息、类别及其置信度信息)。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6h745YOd-1685955497734)(E:\学习笔记\深度学习笔记\目标检测专栏\YOLO\yolov1.assets\image-20230605142559202.png)]


2、YOLOv1进行目标检测的详细步骤


  1. 网络输入和分割:将一幅待检测图像分割分割成S*S个网格,,比如7 * 7, 13 * 13,在原文中S=7。

  2. 边界框预测: 每个网格预测B个边界框(bounding box),在原文中B=2,**每个边界框包含5个基本参数:位置(x、y坐标)、宽度w、高度h和置信度C。**其中:(x、y)表示边界框的中心相对于其所属网格左上角的位置偏移量;宽度w、高度h表示相对于整个图像的比例;C表示边界框的置信度,confidence = Pr(object) * IOU (pred, truth), Pr(object)代表边界框中存在物体的概率,非0即1,IOU (pred, truth)代表预测框与真实标签框的交并比。

    在训练时,某物体的真实边界框的中心落在哪个网格上就由哪个网格负责预测该物体,并且在网格所生成的B个边界框中,与真实边界框的IOU最大的边界框负责预测该物体,这一部分在loss函数中有所体现。每个网格只能预测一个物体(这也是造成YOLOv1对小物体和密集型物体检测效果差的主要原因)。

  3. 类别预测: 每个网格还预测一组类别的条件概率Pr(class | object) ,即在当前边界框已包含物体的先验条件下该物体为各类别的概率,用于确定边界框中物体的类别。

    在进行推理预测时,每个边界框的类别预测概率与该边界框的置信度相乘,得到最终的类别置信度,即Pr(class | object) * Pr(object) * IOU (pred, truth) = Pr(class) * IOU (pred, truth)。根据同样的方法可以计算得到7 x 7 x 2 = 98个边界框的confidence score(当S =7, B =2时),然后根据confidence score对预测得到的98个边界框进行非极大值抑制,得到最终的检测结果。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vRUHwuzf-1685955497735)(E:\学习笔记\深度学习笔记\目标检测专栏\YOLO\yolov1.assets\image-20230605152842548.png)]

  4. 网络输出: 网络的输出是一个SxSx(B*5+C)的张量,相当于每个网格都输出一个形状为B * 5+C的张量,包含B个边界框中每个边界框的位置信息和置信度信息(边界框的中心点xy坐标和宽w高h以及置信度信息),以及该网格所负责预测物体的类别概率(C个值,所属C个类别的概率,原文中C等于20,即检测20个类别)。这些位置预测结果是相对于输入图像的尺度的,因此可以通过将相对尺度乘以图像的宽度和高度来得到实际的边界框。

  5. 置信度评估和非极大值抑制: 对于每个边界框,根据其置信度和类别置信度进行综合评估。通常,可以使用阈值来筛选置信度较高的边界框,并使用非极大值抑制来消除重叠的边界框,从而得到最终的目标检测结果。

3、YOLOv1的网络结构及pytorch实现


YOLOv1的网络结构比较简单,有24个卷积层以及两个全连接层组成。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MAwqdHLn-1685955497737)(E:\学习笔记\深度学习笔记\目标检测专栏\YOLO\yolov1.assets\image-20230605151335069.png)]

如下给出网络的PyTorch实现:

import torch
import torch.nn as nn
import torch.nn.functional as Fclass YOLOv1(nn.Module):def __init__(self, num_classes, num_boxes):super(YOLOv1, self).__init__()self.num_classes = num_classesself.num_boxes = num_boxes# 网络的各层定义self.conv1 = nn.Conv2d(3, 64, kernel_size=7, stride=2, padding=3)self.pool1 = nn.MaxPool2d(kernel_size=2, stride=2)self.conv2 = nn.Conv2d(64, 192, kernel_size=3, stride=1, padding=1)self.pool2 = nn.MaxPool2d(kernel_size=2, stride=2)self.conv3 = nn.Conv2d(192, 128, kernel_size=1, stride=1, padding=0)self.conv4 = nn.Conv2d(128, 256, kernel_size=3, stride=1, padding=1)self.conv5 = nn.Conv2d(256, 256, kernel_size=1, stride=1, padding=0)self.conv6 = nn.Conv2d(256, 512, kernel_size=3, stride=1, padding=1)self.pool3 = nn.MaxPool2d(kernel_size=2, stride=2)self.conv7 = nn.Conv2d(512, 256, kernel_size=1, stride=1, padding=0)self.conv8 = nn.Conv2d(256, 512, kernel_size=3, stride=1, padding=1)self.conv9 = nn.Conv2d(512, 256, kernel_size=1, stride=1, padding=0)self.conv10 = nn.Conv2d(256, 512, kernel_size=3, stride=1, padding=1)self.conv11 = nn.Conv2d(512, 256, kernel_size=1, stride=1, padding=0)self.conv12 = nn.Conv2d(256, 512, kernel_size=3, stride=1, padding=1)self.conv13 = nn.Conv2d(512, 256, kernel_size=1, stride=1, padding=0)self.conv14 = nn.Conv2d(256, 512, kernel_size=3, stride=1, padding=1)self.conv15 = nn.Conv2d(512, 512, kernel_size=1, stride=1, padding=0)self.conv16 = nn.Conv2d(512, 1024, kernel_size=3, stride=1, padding=1)self.pool4 = nn.MaxPool2d(kernel_size=2, stride=2)self.conv17 = nn.Conv2d(1024, 512, kernel_size=1, stride=1, padding=0)self.conv18 = nn.Conv2d(512, 1024, kernel_size=3, stride=1, padding=1)self.conv19 = nn.Conv2d(1024, 512, kernel_size=1, stride=1, padding=0)self.conv20 = nn.Conv2d(512, 1024, kernel_size=3, stride=1, padding=1)self.conv21 = nn.Conv2d(1024, 1024, kernel_size=3, stride=1, padding=1)self.conv22 = nn.Conv2d(1024, 1024, kernel_size=3, stride=2, padding=1)self.conv23 = nn.Conv2d(1024, 1024, kernel_size=3, stride=1, padding=1)self.conv24 = nn.Conv2d(1024, 1024, kernel_size=3, stride=1, padding=1)self.fc1 = nn.Linear(7 * 7 * 1024, 4096)self.fc2 = nn.Linear(4096, 7 * 7 * (self.num_classes + 5 * self.num_boxes))self.softmax = nn.Softmax(dim=1)def forward(self, x):x = F.relu(self.conv1(x))x = self.pool1(x)x = F.relu(self.conv2(x))x = self.pool2(x)x = F.relu(self.conv3(x))x = F.relu(self.conv4(x))x = F.relu(self.conv5(x))x = self.pool3(x)x = F.relu(self.conv6(x))x = F.relu(self.conv7(x))x = F.relu(self.conv8(x))x = F.relu(self.conv9(x))x = F.relu(self.conv10(x))x = F.relu(self.conv11(x))x = F.relu(self.conv12(x))x = F.relu(self.conv13(x))x = F.relu(self.conv14(x))x = F.relu(self.conv15(x))x = F.relu(self.conv16(x))x = self.pool4(x)x = F.relu(self.conv17(x))x = F.relu(self.conv18(x))x = F.relu(self.conv19(x))x = F.relu(self.conv20(x))x = F.relu(self.conv21(x))x = F.relu(self.conv22(x))x = F.relu(self.conv23(x))x = F.relu(self.conv24(x))x = x.view(x.size(0), -1)x = F.relu(self.fc1(x))x = self.fc2(x)x = self.softmax(x)return x

4、YOLOv1的loss函数及pytorch实现


网络的Loss部分:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-baemJNCP-1685955497738)(E:\学习笔记\深度学习笔记\目标检测专栏\YOLO\yolov1.assets\image-20230605155019661.png)]

YOLOv1将检测视为回归任务,所以选用回归常用的平方和损失作为loss函数,主要由位置坐标回归误差、置信度回归误差、类别预测误差三部分组成。

  1. 位置坐标误差只统计负责检测物体的边界框的定位误差,由边界框的中心坐标定位误差和宽高定位误差组成。

  2. 置信度回归误差对负责检测物体的边界框和不负责的边界框都进行惩罚,公式中的置信度预测值为模型正向计算的输出结果中的置信度,置信度真实值为confidence = Pr(object) * IOU (pred, truth)。

  3. 类别预测误差仅对存在物体的网格(真实边界框的中心点落在该网格中)进行错误惩罚.

Note:

  • 平方和loss对各类误差一视同仁,图像中不包含物体的网格的边界框置信度等于0,容易超过包含物体网格的gradient,会导致模型训练不稳定,所以论文中采用了不同的权重因子,对定位误差和分类误差进行权衡。λcoord=5 — 增强边界框定位误差;λnoobj=5 — 削弱不包含物体的边界框置信度误差;

  • 平方和loss对大小边界框的惩罚力度一致,但我们应该使得大框的小偏差比小框中的偏差对loss训练优化的影响要小,原文中将Loss中的宽高进行了开平方根,而不是直接使用宽高,使得小框的偏差对总体误差的影响更加敏感。

YOLOv1 Loss的Pytorch实现:

import torch
import numpy as np
import torch.nn as nn
import torch.nn.functional as Fclass MSEWithLogitsLoss(nn.Module):def __init__(self, ):super(MSEWithLogitsLoss, self).__init__()def forward(self, logits, targets):inputs = torch.clamp(torch.sigmoid(logits), min=1e-4, max=1.0 - 1e-4)pos_id = (targets==1.0).float()neg_id = (targets==0.0).float()pos_loss = pos_id * (inputs - targets)**2neg_loss = neg_id * (inputs)**2loss = 5.0*pos_loss + 1.0*neg_lossreturn lossdef generate_dxdywh(gt_label, w, h, s):xmin, ymin, xmax, ymax = gt_label[:-1]# 计算边界框的中心点c_x = (xmax + xmin) / 2 * wc_y = (ymax + ymin) / 2 * hbox_w = (xmax - xmin) * wbox_h = (ymax - ymin) * hif box_w < 1e-4 or box_h < 1e-4:# print('Not a valid data !!!')return False    # 计算中心点所在的网格坐标c_x_s = c_x / sc_y_s = c_y / sgrid_x = int(c_x_s)grid_y = int(c_y_s)# 计算中心点偏移量和宽高的标签tx = c_x_s - grid_xty = c_y_s - grid_ytw = np.log(box_w)th = np.log(box_h)# 计算边界框位置参数的损失权重weight = 2.0 - (box_w / w) * (box_h / h)return grid_x, grid_y, tx, ty, tw, th, weightdef gt_creator(input_size, stride, label_lists=[]):# 必要的参数batch_size = len(label_lists)w = input_sizeh = input_sizews = w // stridehs = h // strides = stridegt_tensor = np.zeros([batch_size, hs, ws, 1+1+4+1])# 制作训练标签for batch_index in range(batch_size):for gt_label in label_lists[batch_index]:gt_class = int(gt_label[-1])result = generate_dxdywh(gt_label, w, h, s)if result:grid_x, grid_y, tx, ty, tw, th, weight = resultif grid_x < gt_tensor.shape[2] and grid_y < gt_tensor.shape[1]:gt_tensor[batch_index, grid_y, grid_x, 0] = 1.0gt_tensor[batch_index, grid_y, grid_x, 1] = gt_classgt_tensor[batch_index, grid_y, grid_x, 2:6] = np.array([tx, ty, tw, th])gt_tensor[batch_index, grid_y, grid_x, 6] = weightgt_tensor = gt_tensor.reshape(batch_size, -1, 1+1+4+1)return torch.from_numpy(gt_tensor).float()def compute_loss(pred_conf, pred_cls, pred_txtytwth, targets):batch_size = pred_conf.size(0)# 损失函数conf_loss_function = MSEWithLogitsLoss()cls_loss_function = nn.CrossEntropyLoss(reduction='none')txty_loss_function = nn.BCEWithLogitsLoss(reduction='none')twth_loss_function = nn.MSELoss(reduction='none')# 预测pred_conf = pred_conf[:, :, 0]           # [B, HW,]pred_cls = pred_cls.permute(0, 2, 1)     # [B, C, HW]pred_txty = pred_txtytwth[:, :, :2]      # [B, HW, 2]pred_twth = pred_txtytwth[:, :, 2:]      # [B, HW, 2]# 标签gt_obj = targets[:, :, 0]                  # [B, HW,]gt_cls = targets[:, :, 1].long()           # [B, HW,]gt_txty = targets[:, :, 2:4]               # [B, HW, 2]gt_twth = targets[:, :, 4:6]               # [B, HW, 2]gt_box_scale_weight = targets[:, :, 6]     # [B, HW,]batch_size = pred_conf.size(0)# 置信度损失conf_loss = conf_loss_function(pred_conf, gt_obj)conf_loss = conf_loss.sum() / batch_size# 类别损失cls_loss = cls_loss_function(pred_cls, gt_cls) * gt_objcls_loss = cls_loss.sum() / batch_size# 边界框txty的损失txty_loss = txty_loss_function(pred_txty, gt_txty).sum(-1) * gt_obj * gt_box_scale_weighttxty_loss = txty_loss.sum() / batch_size# 边界框twth的损失twth_loss = twth_loss_function(pred_twth, gt_twth).sum(-1) * gt_obj * gt_box_scale_weighttwth_loss = twth_loss.sum() / batch_sizebbox_loss = txty_loss + twth_loss# 总的损失total_loss = conf_loss + cls_loss + bbox_lossreturn conf_loss, cls_loss, bbox_loss, total_loss

5、YOLOv1的不足


  • 因为YOLO中每个cell只预测两个边界框和一个物体,使得对小物体以及密集型物体检测效果差。
  • 此外,不像Faster R-CNN一样预测offset,YOLO是直接预测边界框的位置的,这就增加了训练的难度,并且识别精度弱于Faster R-CNN,但是快。
  • YOLO是根据训练数据来预测边界框的,但是当测试数据中的物体出现了训练数据中的物体没有的长宽比时,YOLO的泛化能力低。
  • 同时经过多次下采样,使得最终得到的feature的分辨率比较低,就是得到深语义的粗特征信息,忽略了细语义特征,造成定位精度下降。
  • 损失函数的设计存在缺陷,使得物体的定位误差有点儿大,尤其在不同尺寸大小的物体的处理上还有待加强。

6、其他


另外,YOLOv1中的训练过程中还是用到了Warm up、Dropout、数据增强(缩放、平移、HSV变换)等技巧,这些我们会在以后关于模型训练技巧的篇章中进行讲解。

参考链接:

  • 【精读AI论文】YOLO V1目标检测,看我就够了_哔哩哔哩_bilibili
  • 【目标检测论文阅读】YOLOv1 - 知乎 (zhihu.com)
  • yjh0410/PyTorch_YOLOv1: A new version of YOLOv1 (github.com)

http://www.ppmy.cn/news/234461.html

相关文章

docker水位如何清理(容器水位清理详细分析)

docker水位过高&#xff0c;清理怕出问题&#xff1f;&#xff0c;不知道清理什么&#xff1f;怕删错了&#xff1f;进入实践 第一步准备测试数据 创建 悬空的镜像&#xff08;即REPOSITORY和TAG均为的镜像&#xff09; docker pull busybox:musl docker tag busybox:musl b…

洛谷P1425 小鱼的游泳时间

题目描述 伦敦奥运会要到了&#xff0c;小鱼在拼命练习游泳准备参加游泳比赛&#xff0c;可怜的小鱼并不知道鱼类是不能参加人类的奥运会的。 这一天&#xff0c;小鱼给自己的游泳时间做了精确的计时&#xff08;本题中的计时都按24小时制计算&#xff09;&#xff0c;它发现…

洛谷-P1425-小鱼的游泳时间

小鱼的游泳时间 - 洛谷 解题思路&#xff1a; 1.由题可得&#xff0c;给定的两个时间点都是在当天完成的&#xff0c;所以后面的小时总是比前面大的&#xff0c;并且都是24小时制计算&#xff0c;所以不会出现后者比前者小的情况 2.那么直接判断分钟即可&#xff0c;如果后面…

superset、sqlalchemy链接数据库报错 ‘SSL connection error: error:1425F102:SSL routines:ssl_choose_client_vers

最近使用superset添加数据源的时候&#xff0c;总是报错(MySQLdb._exceptions.OperationalError) (2026, ‘SSL connection error: error:1425F102:SSL routines:ssl_choose_client_version:unsupported protocol’) 查了一番说是ssl版本不一致的问题&#xff0c;本人照着改了大…

洛谷题目P1425 小鱼的游泳时间

#题目描述 伦敦奥运会要到了&#xff0c;小鱼在拼命练习游泳准备参加游泳比赛&#xff0c;可怜的小鱼并不知道鱼类是不能参加人类的奥运会的。 这一天&#xff0c;小鱼给自己的游泳时间做了精确的计时&#xff08;本题中的计时都按24小时制计算&#xff09;&#xff0c;它发现…

【洛谷】P1425 小鱼的游泳时间

题目地址&#xff1a; https://www.luogu.com.cn/problem/P1425 题目描述&#xff1a; 伦敦奥运会要到了&#xff0c;小鱼在拼命练习游泳准备参加游泳比赛&#xff0c;可怜的小鱼并不知道鱼类是不能参加人类的奥运会的。这一天&#xff0c;小鱼给自己的游泳时间做了精确的计时…

XTU程设 1425 Dice I

题目描述 一颗骰子放在桌面上&#xff0c;1点在顶面&#xff0c;6点在底面&#xff0c;5点在正面&#xff0c;2点在背面&#xff0c;3点在左面&#xff0c;4点在右面。 我们分别使用”UDLRXY”分别表示按左手螺旋法则&#xff0c;沿x,y,z轴顺时钟和逆时钟转动90度。 比如在初始…

P1425 小鱼的游泳时间

P1425 小鱼的游泳时间 提交431.24k 通过215.36k 时间限制1.00s 内存限制125.00MB 提交答案加入题单 复制题目 题目提供者yeszy 难度入门 历史分数100 提交记录 查看题解 标签 查看算法标签 进入讨论版 相关讨论 查看讨论 推荐题目 查看推荐 洛谷推荐关闭 展开 题目…