知行编程网知行编程网  2022-06-19 12:00 知行编程网 隐藏边栏 |   抢沙发  105 
文章评分 0 次,平均分 0.0

你一定从未看过如此通俗易懂的YOLO系列(从v1到v5)模型解读 (上)

来自 | 知乎  作者 | 科技猛兽
链接 | https://zhuanlan.zhihu.com/p/183261974
编辑 | 深度学习这件小事
本文仅作学术交流,如有侵权,请联系后台删除。

   0 前言

本文目的是用尽量浅显易懂的语言让零基础小白能够理解什么是YOLO系列模型,以及他们的设计思想和改进思路分别是什么。我不会把YOLO的论文给你用软件翻译一遍,这样做毫无意义;也不会使用太专业晦涩的名词和表达,对于每一个新的概念都会解释得尽量通俗一些,目的是使得你能像看故事一样学习YOLO模型,我觉得这样的学习方式才是知乎博客的意义所在。

为了使本文尽量生动有趣,我用葫芦娃作为例子展示YOLO的过程(真的是尽力了。。。)。

你一定从未看过如此通俗易懂的YOLO系列(从v1到v5)模型解读 (上)
葫芦娃

同时,会对YOLO v1和YOLOv5的代码进行解读,其他的版本就只介绍改进了。


   1 先从一款强大的app说起

你一定从未看过如此通俗易懂的YOLO系列(从v1到v5)模型解读 (上)
i detection APP

YOLO v5其实一开始是以一款app进入人们的视野的,就是上图的这个,叫:i detection(图上标的是YOLO v4,但其实算法是YOLO v5),使用iOS系列的小伙伴呢,就可以立刻点赞后关掉我这篇文章,去下载这个app玩一玩。在任何场景下(工业场景,生活场景等等)都可以试试这个app和这个算法,这个app中间还有一个button,来调节app使用的模型的大小,更大的模型实时性差但精度高,更小的模型实时性好但精度差。

值得一提的是,这款app就是YOLO v5的作者亲自完成的。而且,我写这篇文章的时候YOLO v5的论文还没有出来,还在实验中,等论文出来应该是2020年底或者2021年初了。


读到这里,你觉得YOLO v5的最大特点是什么?

答案就是:一个字:快,应用于移动端,模型小,速度快。


首先我个人觉得任何一个模型都有下面3部分组成:

  • 前向传播部分:90%

  • 损失函数部分

  • 反向传播部分

其中前向传播部分占用的时间应该在90%左右,即搞清楚前向传播部分也就搞清楚了这模型的实现流程和细节。本着这一原则,我们开始YOLO系列模型的解读:

   2 不得不谈的分类模型

在进入目标检测任务之前首先得学会图像分类任务,这个任务的特点是输入一张图片,输出是它的类别。

对于输入图片,我们一般用一个矩阵表示。

对于输出结果,我们一般用一个one-hot vector表示:  ,哪一维是1,就代表图片属于哪一类。

所以,在设计神经网络时,结构大致应该长这样:

img  cbrp16 cbrp32 cbrp64 cbrp128 ... fc256-fc[10]

这里的cbrp指的是conv,bn,relu,pooling的串联。

由于输入要是one-hot形式,所以最后我们设计了2个fc层(fully connencted layer),我们称之为“分类头”或者“决策层”

   3 YOLO系列思想的雏形:YOLO v0

有了上面的分类器,我们能不能用它来做检测呢?

要回答这个问题,首先得看看检测器和分类器的输入输出有什么不一样。首先他们的输入都是image,但是分类器的输出是一个one-hot vector,而检测器的输出是一个框(Bounding Box)。

,该怎么表示?

在一个图片里面表示一个框,有很多种方法,比如:

你一定从未看过如此通俗易懂的YOLO系列(从v1到v5)模型解读 (上)

x,y,w,h表示一个框

  • x,y,w,h(如上图)

  • p1,p2,p3,p4(4个点坐标)

  • cx,cy,w,h(cx,cy为中心点坐标)

  • x,y,w,h,angle(还有的目标是有角度的,这时叫做Rotated Bounding Box)

  • ......

所以表示的方法不是一成不变的,但你会发现:不管你用什么形式去表达这个Bounding Box,你模型输出的结果一定是一个vector,那这个vector和分类模型输出的vector本质上有什么区别吗?

答案是:没有,都是向量而已,只是分类模型输出是one-shot向量,检测模型输出是我们标注的结果。

所以你应该会发现,检测的方法呼之欲出了。那分类模型可以用来做检测吗?

当然可以,这时,你可以把检测的任务当做是遍历性的分类任务。

如何遍历?

我们的目标是一个个框,那就用这个框去遍历所有的位置,所有的大小。

比如下面这张图片,我需要你检测葫芦娃的脸,如图1所示:

你一定从未看过如此通俗易懂的YOLO系列(从v1到v5)模型解读 (上)
图1:检测葫芦娃的脸

我们可以对边框的区域进行二分类:属于头或者不属于头。

你先预设一个框的大小,然后在图片上遍历这个框,比如第一行全都不是头。第4个框只有一部分目标在,也不算。第5号框算是一个头,我们记住它的位置。这样不断地滑动,就是遍历性地分类。

接下来要遍历框的大小:因为你刚才是预设一个框的大小,但葫芦娃的头有大有小,你还得遍历框的大小,如下图2所示:

你一定从未看过如此通俗易懂的YOLO系列(从v1到v5)模型解读 (上)
图2:遍历框的大小

还没有结束,刚才滑窗时是挨个滑,但其实没有遍历所有的位置,更精确的遍历方法应该如下图3所示:

你一定从未看过如此通俗易懂的YOLO系列(从v1到v5)模型解读 (上)
图3:更精确地遍历框的位置

这种方法其实就是RCNN全家桶的初衷,专业术语叫做:滑动窗口分类方法

现在需要你思考一个问题:这种方法的精确和什么因素有关?

答案是:遍历得彻不彻底。遍历得越精确,检测器的精度就越高。所以这也就带来一个问题就是:检测的耗时非常大

举个例子:比如输入图片大小是(800,1000)也就意味着有800000个位置。窗口大小最小  ,最大  ,所以这个遍历的次数是无限次。我们看下伪代码:

你一定从未看过如此通俗易懂的YOLO系列(从v1到v5)模型解读 (上)
滑动窗口分类方法伪代码

那这种方法如何训练呢?

本质上还是训练一个二分类器。这个二分类器的输入是一个框的内容,输出是(前景/背景)

第1个问题:

框有不同的大小,对于不同大小的框,输入到相同的二分类器中吗?

是的。要先把不同大小的input归一化到统一的大小。

第2个问题:

背景图片很多,前景图片很少:二分类样本不均衡。


确实是这样,你看看一张图片有多少框对应的是背景,有多少框才是葫芦娃的头。

以上就是传统检测方法的主要思路:

  • 耗时。

  • 操作复杂,需要手动生成大量的样本。


到现在为止,我们用分类的算法设计了一个检测器,它存在着各种各样的问题,现在是优化的时候了(接下来正式进入YOLO系列方法了):

YOLO的作者当时是这么想的:你分类器输出一个one-hot vector,那我把它换成(x,y,w,h,c),c表示confidence置信度,把问题转化成一个回归问题,直接回归出Bounding Box的位置不就好了吗?

刚才的分类器是:img  cbrp16 cbrp32 cbrp64 cbrp128 ... fc256-fc[10]

现在我变成:img  cbrp16 cbrp32 cbrp64 cbrp128 ... fc256-fc[5],这个输出是(x,y,w,h,c),不就变成了一个检测器吗?

本质上都是矩阵映射到矩阵,只是代表的意义不一样而已。

传统的方法为什么没有这么做呢?我想肯定是效果不好,终其原因是算力不行,conv操作还没有推广。

好,现在模型是:

img  cbrp16 cbrp32 cbrp64 cbrp128 ... fc256-fc[5]  c,x,y,w,h

你一定从未看过如此通俗易懂的YOLO系列(从v1到v5)模型解读 (上)

那如何组织训练呢?找1000张图片,把label设置为  。这里  代表真值。有了数据和标签,就完成了设计。

我们会发现,这种方法比刚才的滑动窗口分类方法简单太多了。这一版的思路我把它叫做YOLO v0,因为它是You Only Look Once最简单的版本。

   4 YOLO v1终于诞生

  • 需求1:YOLO v0只能输出一个目标,那比如下图4的多个目标怎么办呢?

你一定从未看过如此通俗易懂的YOLO系列(从v1到v5)模型解读 (上)
图4:多个目标情况

你可能会回答:我输出N个向量不就行了吗?但具体输出多少个合适呢?图4有7个目标,那有的图片有几百个目标,你这个N又该如何调整呢?

答:为了保证所有目标都被检测到,我们应该输出尽量多的目标。

你一定从未看过如此通俗易懂的YOLO系列(从v1到v5)模型解读 (上)输出尽量多的目标

但这种方法也不是最优的,最优的应该是下图这样:

你一定从未看过如此通俗易懂的YOLO系列(从v1到v5)模型解读 (上)
图5:用一个(c,x,y,w,h)去负责image某个区域的目标

如图5所示:用一个(c,x,y,w,h)去负责image某个区域的目标。

比如说图片设置为16个区域,每个区域用1个(c,x,y,w,h)去负责:

你一定从未看过如此通俗易懂的YOLO系列(从v1到v5)模型解读 (上)
图6:图片设置为16个区域

就可以一次输出16个框,每个框是1个(c,x,y,w,h),如图6所示。

为什么这样子更优?因为conv操作是位置强相关的,就是原来的目标在哪里,你conv之后的feature map上还在哪里,所以图片划分为16个区域,结果也应该分布在16个区域上,所以我们的结果(Tensor)的维度size是:(5,4,4)

那现在你可能会问:c的真值该怎么设置呢?

答:看葫芦娃的大娃,他的脸跨了4个区域(grid),但只能某一个grid的c=1,其他的c=0。那么该让哪一个grid的c=1呢?就看他的脸的中心落在了哪个grid里面。根据这一原则,c的真值为下图7所示:

你一定从未看过如此通俗易懂的YOLO系列(从v1到v5)模型解读 (上)

图7:c的label值

但是你发现7个葫芦娃只有6个1,原因是某一个grid里面有2个目标,确实如此,第三行第三列的grid既有水娃又有隐身娃。这种一个区域有多个目标的情况我们目前没法解决,因为我们的模型现在能力就这么大,只能在一个区域中检测出一个目标,如何改进我们马上就讨论,你可以现在先自己想一想。

总之现在我们设计出了模型的输出结果,那距离完成模型的设计还差一个损失函数,那Loss咋设计呢?看下面的伪代码:

backward()

遍历所有图片,遍历所有位置,计算loss。

  • 好现在模型设计完了,回到刚才的问题:模型现在能力就这么大,只能在一个区域中检测出一个目标,如何改进?

答:刚才区域是  ,现在变成  ,或者更大,使区域更密集,就可以缓解多个目标的问题,但无法从根本上去解决。


  • 另一个问题,按上面的设计你检测得到了16个框,可是图片上只有7个葫芦娃的脸,怎么从16个结果中筛选出7个我们要的呢?

答:

法1:聚类。聚成7类,在这7个类中,选择confidence最大的框。听起来挺好。

法1的bug:2个目标本身比较近聚成了1个类怎么办?如果不知道到底有几个目标呢?为何聚成7类?不是3类?

法2:NMS(非极大值抑制)。2个框重合度很高,大概率是一个目标,那就只取一个框。

重合度的计算方法:交并比IoU=两个框的交集面积/两个框的并集面积。

具体算法:

你一定从未看过如此通俗易懂的YOLO系列(从v1到v5)模型解读 (上)
面试必考的NMS

法1的bug:2个目标本身比较近怎么办?依然没有解决。

如果不知道到底有几个目标呢?NMS自动解决了这个问题。

面试的时候会问这样一个问题:NMS的适用情况是什么?

答:1图多目标检测时用NMS。


到现在为止我们终于解决了第4节开始提出的多个目标的问题,现在又有了新的需求:

  • 需求2:多类的目标怎么办呢?

比如说我现在既要你检测葫芦娃的脸,又要你检测葫芦娃的葫芦,怎么设计?

img  cbrp16 cbrp32 cbrp64 cbrp128 ... fc256-fc[5+2]*N  [c,x,y,w,h,one-hot]*N

2个类,one-hot就是[0,1],[1,0]这样子,如下图8所示:

你一定从未看过如此通俗易懂的YOLO系列(从v1到v5)模型解读 (上)
图8:多类的目标的label

伪代码依然是:

loss.backward()

至此,多个类的问题也解决了,现在又有了新的需求:

  • 需求3:小目标检测怎么办呢?

小目标总是检测不佳,所以我们专门设计神经元去拟合小目标。

你一定从未看过如此通俗易懂的YOLO系列(从v1到v5)模型解读 (上)
图9:多类的小目标的label,分别预测大目标和小目标

对于每个区域,我们用2个五元组(c,x,y,w,h),一个负责回归大目标,一个负责回归小目标,同样添加one-hot vector,one-hot就是[0,1],[1,0]这样子,来表示属于哪一类(葫芦娃的头or葫芦娃的葫芦)。

伪代码变为了:

loss.backward()


至此,小目标的问题也有了解决方案。

到这里,我们设计的检测器其实就是YOLO v1,只是有的参数跟它不一样,我们看论文里的图:

你一定从未看过如此通俗易懂的YOLO系列(从v1到v5)模型解读 (上)
YOLO v1
你一定从未看过如此通俗易懂的YOLO系列(从v1到v5)模型解读 (上)
YOLO v1

YOLO v1其实就是把我们划分的16个区域变成了  个区域,我们预测16个目标,YOLO v1预测49个目标。我们是2类(葫芦娃的头or葫芦娃的葫芦),YOLO v1是20类。

backbone也是一堆卷积+检测头(FC层),所以说设计到现在,我们其实是把YOLO v1给设计出来了。

再看看作者的解释:

你一定从未看过如此通俗易懂的YOLO系列(从v1到v5)模型解读 (上)

发现train的时候用的小图片,检测的时候用的是大图片(肯定是经过了无数次试验证明了效果好)。

结构学完了,再看loss函数,并比较下和我们设计的loss函数有什么区别。

你一定从未看过如此通俗易懂的YOLO系列(从v1到v5)模型解读 (上)
YOLO v1 loss函数

解读一下这个损失函数:

我们之前说的损失函数是设计了3个for循环,而作者为了方便写成了求和的形式:

  • 前2行计算前景的geo_loss。

  • 第3行计算前景的confidence_loss。

  • 第4行计算背景的confidence_loss。

  • 第5行计算分类损失class_loss。

伪代码上面已经有了,现在我们总体看一下这个模型:

img  cbrp192 cbrp256 cbrp512 cbrp1024 ... fc4096-fc[5+2]*N  

检测层的设计:回归坐标值+one-hot分类

你一定从未看过如此通俗易懂的YOLO系列(从v1到v5)模型解读 (上)
检测层的设计
  • 样本不均衡的问题解决了吗?

没有计算背景的geo_loss,只计算了前景的geo_loss,这个问题YOLO v1回避了,依然存在。


最后我们解读下YOLO v1的代码:

1.模型定义:

定义特征提取层:

 x

定义检测头:

       )

整体模型:

 x

主函数:

shape)

2.模型训练:

主函数:

    train()

下面看train()函数:

step()

训练过程比较常规,先取1个batch的训练数据,分别得到inputs和labels,依次计算loss,反传,step等。

下面说下2个训练集的数据处理函数:

input_process:

 input_batch

batch[0]为image,batch[1]为label,batch_size为1个batch的图片数量。
batch[0][i]为这个batch的第i张图片,inputs_tmp2为尺寸变成了3,448,448之后的图片,再经过unsqueeze操作拓展1维,size=[1,3,448,448],存储在input_batch中。

最后,返回的是size=[batch_size,3,448,448]的输入数据。


target_process:

 target_batch

要从batch里面获得label,首先要想清楚label(就是bounding box)应该是什么size,输出的结果应该是  的,所以label的size应该是:[batch_size,7,7,30]。在这个程序里我们实现的是输出  。这个  就是x,y,w,h,所以label的size应该是:[batch_size,7,7,5]
batch_labels表示这个batch的第i个图片的label,number_box表示这个图有几个真值框。
接下来3重循环遍历每个grid的每个框,bbox表示正在遍历的这个框。
bbox = bbox/ torch.tensor([wimg,himg,wimg,himg])表示对x,y,w,h进行归一化。
接下来if语句得到confidence的真值,存储在target_batch中返回。

最后是loss函数:

 loss,loss_matrix,geo_loss_matrix,conf_loss_matrix

首先需要注意:label和output的size应该是:[batch_size,7,7,5]。
outputs[bi,wi,hi]就是一个5位向量:  。
我们分别计算了loss_confidence和loss_geo,因为我们实现的这个模型只检测1个类,所以没有class_loss。


终于把YOLO v1写完了,实在写不动了,接下来的几种模型等我的下一篇文章吧(附一个总结):

YOLO v1:直接回归出位置。

YOLO v2:全流程多尺度方法。

YOLO v3:多尺度检测头,resblock darknet53

YOLO v4:cspdarknet53,spp,panet,tricks

<section style="white-space: normal;line-height: 1.75em;text-align: center;"><strong style="color: rgb(0, 0, 0);font-family: -apple-system-font, system-ui, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;widows: 1;background-color: rgb(255, 255, 255);font-size: 16px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="max-width: 100%;letter-spacing: 0.5px;font-size: 14px;box-sizing: border-box !important;overflow-wrap: break-word !important;"><strong style="max-width: 100%;font-size: 16px;letter-spacing: 0.544px;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="max-width: 100%;letter-spacing: 0.5px;box-sizing: border-box !important;overflow-wrap: break-word !important;">—</span></strong>完<strong style="max-width: 100%;font-size: 16px;letter-spacing: 0.544px;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="max-width: 100%;letter-spacing: 0.5px;font-size: 14px;box-sizing: border-box !important;overflow-wrap: break-word !important;"><strong style="max-width: 100%;font-size: 16px;letter-spacing: 0.544px;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="max-width: 100%;letter-spacing: 0.5px;box-sizing: border-box !important;overflow-wrap: break-word !important;">—</span></strong></span></strong></span></strong></section><pre><pre style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"><section style="max-width: 100%;letter-spacing: 0.544px;white-space: normal;font-family: -apple-system-font, system-ui, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;widows: 1;box-sizing: border-box !important;overflow-wrap: break-word !important;"><section powered-by="xiumi.us" style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"><section style="margin-top: 15px;margin-bottom: 25px;max-width: 100%;opacity: 0.8;box-sizing: border-box !important;overflow-wrap: break-word !important;"><section style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"><section style="max-width: 100%;letter-spacing: 0.544px;box-sizing: border-box !important;overflow-wrap: break-word !important;"><section powered-by="xiumi.us" style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"><section style="margin-top: 15px;margin-bottom: 25px;max-width: 100%;opacity: 0.8;box-sizing: border-box !important;overflow-wrap: break-word !important;"><section><section style="margin-bottom: 15px;padding-right: 0em;padding-left: 0em;max-width: 100%;color: rgb(127, 127, 127);font-size: 12px;font-family: sans-serif;line-height: 25.5938px;letter-spacing: 3px;text-align: center;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="max-width: 100%;color: rgb(0, 0, 0);box-sizing: border-box !important;overflow-wrap: break-word !important;"><strong style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="max-width: 100%;font-size: 16px;font-family: 微软雅黑;caret-color: red;box-sizing: border-box !important;overflow-wrap: break-word !important;">为您推荐</span></strong></span></section><section style="margin-top: 5px;margin-bottom: 5px;padding-right: 0em;padding-left: 0em;max-width: 100%;min-height: 1em;font-family: sans-serif;letter-spacing: 0px;opacity: 0.8;line-height: normal;text-align: center;box-sizing: border-box !important;overflow-wrap: break-word !important;">这21张深度学习速查表让你代码能力突飞猛进</section><section style="margin-top: 5px;margin-bottom: 5px;padding-right: 0em;padding-left: 0em;max-width: 100%;min-height: 1em;font-family: sans-serif;letter-spacing: 0px;opacity: 0.8;line-height: normal;text-align: center;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="font-size: 14px;">LSTM终获正名,获IEEE 2021神经网络先驱奖!</span><br  /></section><section style="margin-top: 5px;margin-bottom: 5px;padding-right: 0em;padding-left: 0em;max-width: 100%;min-height: 1em;font-family: sans-serif;letter-spacing: 0px;opacity: 0.8;line-height: normal;text-align: center;box-sizing: border-box !important;overflow-wrap: break-word !important;">22课时、19大主题,CS 231n进阶版课程视频上线<br  /></section><section style="margin-top: 5px;margin-bottom: 5px;padding-right: 0em;padding-left: 0em;max-width: 100%;min-height: 1em;font-family: sans-serif;letter-spacing: 0px;opacity: 0.8;line-height: normal;text-align: center;box-sizing: border-box !important;overflow-wrap: break-word !important;">数据分析入门常用的23个牛逼Pandas代码</section><section style="margin-top: 5px;margin-bottom: 5px;padding-right: 0em;padding-left: 0em;max-width: 100%;min-height: 1em;font-family: sans-serif;letter-spacing: 0px;opacity: 0.8;line-height: normal;text-align: center;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="color: rgb(87, 107, 149);font-size: 14px;">如何在科研论文中画出漂亮的插图?</span><br  /></section></section></section></section></section></section></section></section></section>
你一定从未看过如此通俗易懂的YOLO系列(从v1到v5)模型解读 (上)

本篇文章来源于: 深度学习这件小事

本文为原创文章,版权归所有,欢迎分享本文,转载请保留出处!

知行编程网
知行编程网 关注:1    粉丝:1
这个人很懒,什么都没写

发表评论

表情 格式 链接 私密 签到
扫一扫二维码分享