目录
1. YOLO 的作用
2. YOLO(v1,v2,v3)的技术演化
1. YOLO 的作用
yolo 是当前目标检测最顶级的算法之一, v1 版本是 2016 年提出来的, v2 是 2017 年提出来的, v3 是 2018 年提出的.
官网地址: https://pjreddie.com/darknet/yolo/
说它最牛掰, 有两点:
一是因为它采用深层卷积神经网络, 吸收了当前很多经典卷积神经网络架构的优秀思想, 在位置检测和对象的识别方面,
性能达到最优(准确率非常高的情况下还能达到实时检测).
二是因为作者还将代码开源了. 真心为作者这种大公无私的心胸点赞.
美中不足的是: 作者虽然将代码开源, 但是在论文介绍架构原理的时候比较模糊, 特别是对一些重要改进, 基本上是一笔带过.
现在在网络上有很多关于 YOLO 原理的讲解, 个人感觉讲得不是很清楚, 总感觉有点知其然而不知其所以然. 比如:
yolo 是在什么背景下出现的?
v1 是在哪个经典网络架构上发展起来的? 解决了什么问题? 又存在什么问题?
v2 针对 v1 的缺点做了哪些改进? 改进后效果怎么样? 又存在什么问题?
v3 针对 v2 的缺点做了哪些改进?
这些问题不搞清楚, 我觉得对 yolo 就谈不上真正的理解. 废话不多说, 下面就来介绍 yolo 的技术演进.
2. YOLO(v1,v2,v3)的技术演化
问题 1:yolo v1 是在什么背景下出现的?
yolo v1 是在 R-CNN 基础上发展起来的.
R-CNN(region proposals + cnn), 采用卷积神经网络进行目标检测的开山之作.
主要思想: 对输入图片采用 selective search 搜索查询算法, 提取出大约 2000 个人眼感兴趣的候选边框, 然后每个边框都通过一个独立的
卷积神经网络进行预测输出, 后面再加上一个 SVM(支持向量机)预测分类.
优点: 位置检测与对象分类准确率非常高.
缺点: 运算量大, 检测速度非常慢, 在 GPU 加持下一帧检测时间要 13s 左右, 这在工程应用上是不可接受的.
造成速度慢的原因主要有 2 个:
> 用搜索查询算法提取 2000 个候选边框
> 每个候选边框都采用一个独立的 CNN 通道, 这意味着卷积核的参数和全连接的参数都是不一样的, 总的参数个数是非常恐怖的.
针对这个缺点, yolo v1 做了哪些改进呢.
上面的图片是官网论文给出的结构图, 但是个人感觉画得不太好, 它容易让人误认为对输入图片进行网格化处理, 每个网格化的小窗口是 7*7.
真实情况是, 由最终输出是 7*7*30 大小代表的物理含义是: 你可以把输入图片看成是经过了网格化(grid cell 7*7), 每个网格化后的小窗口通过 CNN 预测出 1*30.
这里可能有点不好理解. 总之一句话, 在真正网络架构流程中, 没有对输入图片进行任何网格化处理.
v1 改进点:
> Backbone: googLeNet22 (采用 googLeNet22 层卷积结构)
> 输入图片只处理一次(yolo 名称的由来), 通过多个卷积层提取不同的特征, 每次卷积的时候共享卷积核参数.
输入图片只处理一次: 表示输入图片只在第一次卷积的时候作为输入进行卷积运算, 第一次卷积后的输出作为第二次卷积的输入, 通过多个卷积层递进的方式来提取不同的特征.
并且这些特征通过的 CNN 通道都是相同的, 从而可以共享卷积核参数.
> 每个 3*3 卷积核前面引入了 1*1 卷积核, 作用有两个, 一是提取更丰富的特征, 二是减少卷积核的参数个数.
大家可能对减少卷积核参数个数的作用比较难以理解, 这里举个例子.
比如输入图片大小是 56*56*256 最终转化的目标大小是 28*28*512.
直接卷积: 56*56*256 & 3*3*256*512 -> 56*56*512 & pooling -> 28*28*512
卷积核参数: 3*3*256*512 = 1179648
引入 1*1 卷积: 56*56*256 & 1*1*256*128 -> 56*56*128 & 3*3*128*512 -> 56*56*512 & pooling -> 28*28*512
卷积核参数: 1*1*256*128 + 3*3*128*512 = 622592
经过改进后, 图片检测速度非常快, 基本上可以达到实时. 但是缺点是位置检测准确度低, 并且不能检测出小对象物体.
针对 v1 的缺点, v2 做了哪些改进呢?
v2 改进点:
> Backbone:darknet19 (采用 darknet 19 层卷积结构)
> 输入图片批归一化处理(BN), 作用是降低不是重要特征的重要性.
这句话可能听得有点晕, 举个例子,
你的样本里有两个特征列, 一个特征列的数值在 [1,10] 范围内, 另一个特征列的数值在 [1000,10000] 范围内,
但是真实情况是, 你的这两个特征列重要性可能是一样的, 只不过你拿到的数据就是这样的, 我们知道,
卷积神经网络训练, 实质就是一系列数值运算的过程, 如果你将这两个特征列直接通过卷积神经网络进行训练,
那最终生成的模型准确率肯定是不高的, 所以需要进行归一化处理, 将数值归一化到 [0,1] 范围内,
从而是损失能量在收敛的时候更加平稳.
> 采用 passthrough 算法, 解决池化信息丢失的问题, 增加细粒度特征的检测(小对象).
passthrough 算法主要是为了解决 pooling(池化)的缺点, 不管是最大值池化, 还是平均值池化, 都有一个很明显的问题,
就是会造成信息丢失, passthrough 主要思想是在池化之前, 将输入信息进行拆分, 一拆为四, 经过拆分后的大小就和池化后的输出大小相同,
然后叠加, 叠加后的结果主要就是维度变化, 这样就能解决池化会造成信息丢失的问题.
> 去掉全连接 (FC), 将输入图片拉伸(resize) 到不同尺寸然后通过卷积神经网络, 这样就得到了多尺寸的输出, 从而能提升对不同大小对象的预测准确度.
全连接其实就是矩阵乘法运算, 矩阵乘法有一个前提, 矩阵 A 的列必须与矩阵 B 的行个数相同, 否则是不能进行矩阵乘法运算的.
全连接的参数大小是固定的, 那么你的输入大小自然就固定了, 这样就无法实现多尺寸的输出, 所以这里去掉了全连接层.
经过改进后, 精度提升明显, 特别是对小对象的检测, 缺点是对小对象检测准确度不高.
针对 v2 的缺点, v3 又做了哪些改进呢?
v3 改进点:
上面是 v3 的结构图, 是我跟踪代码, 查找资料, 绘制这张图真心不容易, 正确性绝对有保证, 大家如果觉得这张图对你理解 yolo 有帮助, 麻烦点个赞.
输入大小这里是 416*416, 输出 13*13,26*26,52*52, 这里一般要求输入图片大小是 32 的倍数, 因为整个卷积神经网络会将图片缩小 32 倍, 16 倍, 8 倍,
这里取最大公倍数 32. 123 = 3*(边框坐标 4 + 置信度 1 + 类对象 36).
> Backbone: darknet53 (采用 darknet 53 层卷积结构, 实际是 52 层卷积, 去掉了全连接层)
可以看出 v3 的卷积层数是 v2 的 2.8 倍, 有个潜在的共识: 增加模型准确率的一个直接做法是增加网络的深度和厚度,
(深度是指卷积层数, 厚度指卷积核的维度或者是种类数), 这里作用自然就是提升精度了.
> 用卷积取代池化
之前我们提到过池化的问题, 会造成信息丢失, 这里用卷积来实现池化的功能(使图片大小缩小 2 倍), 同时不会造成信息的明显丢失.
> 采用残差网络 (resnet) 防止梯度消失
梯度消失或者梯度爆炸是在深层的卷积神经网络中才有可能出现的, 梯度的计算是通过链式求导得到的, 随着网络层的增加, 链式求导项就会越来越长,
因为在每一层卷积后的输出都做了归一化处理, 所以梯度只会越来越小, 有可能为 0, 而 0 在后面模块运算中都为 0, 这样导致的直接后果是:
损失能量在收敛到某一阶段后就停止收敛, 最后生成模型的精度自然就不高. 而这里采用残差网络就能防止梯度消失, v3 结构图里左下角就是残差网络的结构图.
残差网络的思想: 每次卷积后的输出当做残差, 将卷积前的输入与残差融合, 作为整个输出, 即使残差为 0, 整个输出也不会为 0.
从残差网络结构图可以看出, 每个 3*3 卷积核前都引入了 1*1 卷积, 这里沿用了 v1 的思想.
从 v3 的结构图可以看出, darknet53 网络骨架里大量的引入了残差网络的思想.
> 使用空间金字塔池化网络算法 (sppnet spatial pyramid pooling) 实现多尺寸的输出
空间金字塔池化网络算法主要思想: 不同尺寸的输入通过 sppnet 模块后生成一个固定尺寸的输出.
在 v3 结构图里, 有两个地方用到这个思想:
一个是 13*13*512 经过 1*1 卷积, 改变特征维度, 变成 13*13*256, 经过上采样(upsample, 这里采用相邻像素插入算法),
改变特征尺寸, 变成 26*26*256, 然后与 26*26*512 叠加, 生成 26*26*768.
另一个是 26*26*256 与 52*52*256 叠加后生成 52*52*384.
v3 的多尺寸输出与 v2 的多尺寸输出有本质不同, v2 多尺寸输出是对输入图片拉伸到不同的尺寸, 然后通过卷积神经网络得到不同的输出,
但是这样就存在一个图片失真的问题, 因为你是对图片进行的拉伸处理. 而 v3 通过 sppnet 实现的多尺寸输出, 就能有效避免图片失真的问题.
> 13*13*123,26*26*123,52*52*123 物理意义
这里用 v1 版本论文图片来解释, 物理意义:
表示将输入图片网格化, 有 13*13,26*26,52*52 大小, 每个网格化的小窗口 (grid cell) 预测 3 个边框(bounding box),
每个边框包含 4 个位置坐标, 1 个置信度, 36 个对象种类.
123 = 3 *(4 + 1 + 36)
这里就存在一个问题: 预测输出如此之多, 直接用于损失能量的计算, 运算量岂不是很恐怖?
确实是这样, 所以这里先经过下面的两个步骤的处理:
(1)每个小窗口只取置信度最大的边框, 因为 yolo 规定, 只能有一个真实的对象中心坐标属于每个小窗口.
这样, 就得出 13*13*3*41 => 13*13*41,26*26*3*41 => 26*26*41,52*52*3*41 => 52*52*41
13*13*41,26*26*41,52*52*41 表示一个真实对象有很多预测重叠边框, 比如说上面图里属于狗的预测边框非常多,
但是我们只需要预测最准确的边框, 去掉其他属于狗的重叠边框.
(2)采用 NMS(非极大值抑制算法)去除重叠边框.
这样 13*13*41,26*26*41,52*52*41 => N*41 (N 表示不同对象预测数目, 比如说上面图理想情况下, N = 3).
> 损失能量(采用交叉熵)
损失能量的计算是 v1 版本提出来的, 这里放到了 v3 来说, 有 3 个改进点:
(1)将位置检测与对象识别作为一个整体, 进行训练预测, 这从损失能量的计算可以直接反应出来.
(2)位置的宽度和高度先开根号, 与归一化的作用相同, 降低不是重要特征的重要性.
(3)增加权重参数 , 当边框预测出含有对象时, 增大它的权重值, 当边框预测出不含有对象时, 减小它的权重值, 这样就能使损失能量计算更准确.
不要让懒惰占据你的大脑, 不要让妥协拖垮了你的人生. 青春就是一张票, 能不能赶上时代的快车, 你的步伐就掌握在你的脚下.
来源: https://www.cnblogs.com/further-further-further/p/12072225.html