项目中用到的Yolov3详解

概述

目前在做工业上的目标检测项目,用了很久的Yolov3算法模型,刚开始是基于Keras平台的,后来使用基于Darknet平台的训练。项目要结束了,终于有时间好好的整理下笔记。先推出算法网络的原理,后续写基于darknet的C++的部署和利用Opencv的dnn模块调用模型的C++部署。

网络介绍

Yolov3和V2、V1的比较

Yolov3算法相比YOLOV2和YOLOV1算法保留的功能有:

  1. “分而治之”,从yolov1开始,yolo算法就是通过划分单元格来做检测,只是划分的数量不一样。Yolov1分成7x7网格,yolov3分成13x13网格。
  2. 采用"leaky ReLU"作为激活函数。
  3. 端到端进行训练。一个损失函数(loss function)搞定训练,只需关注输入端和输出端。
  4. 从yolov2开始,yolo就用BN算法(batch normalization)作为正则化、加速收敛和避免过拟合,把BN层和leaky relu层接到每一层卷积层之后。
  5. 多尺度训练。在速度和准确率之间权衡。想速度快点,可以牺牲准确率;想准确率高点儿,可以牺牲一点速度。
  6. Yolo系列的每一代的提升很大一部分决定于神经网络(backbone)的提升,从v2的darknet-19到v3的darknet-53。Yolov3还提供替换神经网络backbone的tiny darknet网络。tiny darknet网络是darknet-53的轻量级网络。

Yolov3网络结构

Yolov3的网络结构如下所示:
Yolov3网络结构
如上所示,Yolov3的网络结构可以分为三个部分。分别为输入、基础网络Darknet-53和Yolov3的三个分支(y1,y2,y3)。其中Darknet-53网络中的DBL模块是卷积、BN算法、线性激活函数Leaky relu的总称,也是yolov3算法的最基本单元。
BN算法:Batch Normalization是2015年一篇论文中提出的数据归一化方法,往往用在深度神经网络中激活层之前。其作用可以加快模型训练时的收敛速度,使得模型训练过程更加稳定,避免梯度爆炸或者梯度消失。并且起到一定的正则化作用,几乎代替了Dropout。
核心公式为:
在这里插入图片描述

  1. 输入为数值集合​​​​​( B B B),可训练参数 [ γ \gamma γ, β \beta β] ;
  2. BN的具体操作为:先计算的 B B B均值和方差,之后将 B B B集合的均值、方差变换为0和1(对应上式中 x i − u B σ B 2 + ϵ \frac{x_i - u_B}{\sqrt{\sigma^2_B + \epsilon}} σB2+ϵ xiuB),最后将 x i − u B σ B 2 + ϵ \frac{x_i - u_B}{\sqrt{\sigma^2_B + \epsilon}} σB2+ϵ xiuB乘以 γ \gamma γ再加上 β \beta β输出。[ γ \gamma γ, β \beta β]是可训练参数,参与整个网络的BP。

归一化的目的:将数据规整到统一区间,减少数据的发散程度,降低网络的学习难度。BN的精髓在于归一之后,使用作为还原参数,在一定程度上保留原数据的分布。
线性激活函数Leaky ReLU:是线性激活函数ReLu的改进版,它在负轴保留了非常小的常数leak,使得输入信息小于0时,信息没有完全丢掉,进行了相应的保留。即ReLU在取值小于零部分没有梯度,Leaky relu在取值小于0部分给一个很小的梯度。函数坐标图如下:
Leaky ReLu 坐标图

公式如下: L e a k y R e L u ( x ) = { x , x > 0 l e a k ∗ x x < 0 Leaky ReLu(x)=\begin{cases}x,&x>0\\leak*x&x<0 \end{cases} LeakyReLu(x)={x,leakxx>0x<0
其中leak是小数,例如leak=0.1。
将激活函数代入公式中:

  1. 将Leak ReLu看作神经网络中的一层,设第 l l l层输出的是 x l x^l xl,然后输入Leak ReLu激活函数输出的是 x l + 1 x^{l+1} xl+1
  2. 设损失函数 L L L关于第l层输出的 x l x^l xl偏导是
    在这里插入图片描述

resn:n代表数字,有res1,res2, … ,res8等等,表示这个res_block里含有多少个res_unit。这是yolov3的大组件,yolov3开始借鉴了ResNet的残差结构,使用这种结构可以让网络结构更深(从v2的darknet-19上升到v3的darknet-53,前者没有残差结构)。对于res_block,可以在上图的右下角直观看到,其基本组件也是DBL。
concat:张量拼接。将darknet中间层和后面的某一层的上采样进行拼接。拼接的操作和残差层add的操作是不一样的,拼接会扩充张量的维度,而add只是直接相加不会导致张量维度的改变。
整个yolov3网络包含252层,组成如下:
网络组成图
从上图可以看出,对于代码层面的layers数量一共有252层,包括add层23层(主要用于res_block的构成,每个res_unit需要一个add层,一共有1+2+8+8+4=23层)。除此之外,BN层和LeakyReLU层数量完全一样(72层),在网络结构中的表现为:每一层BN后面都会接一层LeakyReLU。卷积层一共有75层,其中有72层后面都会接BN+LeakyReLU的组合构成基本组件DBL。看结构图,可以发现上采样和concat都有2次,和表格分析中对应上。每个res_block都会用上一个零填充,一共有5个res_block。
整个yolov3结构里面,是没有池化层和全连接层的。前向传播过程中,张量的尺寸变换是通过改变卷积核的步长来实现的,比如步长stride=(2, 2),这就等于将图像边长缩小了一半(即面积缩小到原来的1/4)。在yolov2中,要经历5次缩小,会将特征图缩小到原输入尺寸的 ,即1/32。输入为416x416,则输出为13x13(416/32=13)。Yolov3也和yolov2一样,神经网络backbone都会将输出特征图缩小到输入的1/32。所以,通常都要求输入图片是32的倍数。
下图所示为yolov2和yolov3的结构对比图。
对比图
yolov2中对于前向过程中张量尺寸变换,都是通过最大池化来进行,一共有5次。而yolov3是通过卷积核增大步长来进行,也是5次。(darknet-53最后面有一个全局平均池化,在yolov3里面没有这一层,所以张量维度变化只考虑前面那5次)。
yolov3输出了3个不同尺度的特征图feature map,这个借鉴了FPN(feature pyramid networks)网络,采用多尺度来对不同size的目标进行检测,越精细的grid cell就可以检测出越精细的物体。y1,y2和y3的深度都是255,边长的规律是13:26:52。对于COCO类别而言,有80个种类,所以每个box应该对每个种类都输出一个概率。yolov3设定的是每个网格单元预测3个box,所以每个box需要有(x, y, w, h, confidence)五个基本参数,然后还要有80个类别的概率。所以3*(5 + 80) = 255。Yolov3用上采样的方法来实现这种多尺度的特征图feature map,结合yolov3的网络结构图中concat连接的两个张量是具有一样尺度的(两处拼接分别是26x26尺度拼接和52x52尺度拼接,通过上采样来保证concat拼接的张量尺度相同)。

目标位置预测以及损失函数选择

位置预测结构图:
在这里插入图片描述
如上图所示, b x b_x bx b y b_y by为检测框的中心点, t x t_x tx t y t_y ty为上图左上角网格坐标(1,1)的偏移量。 σ ( t x ) \sigma(t_x) σ(tx) σ ( t y ) \sigma(t_y) σ(ty)为对应(y1,y2,y3)尺寸归一化后的偏移量。 c x c_x cx c y c_y cy为单元网格的距离,这里都为1。 p w p_w pw p y p_y py为anchor的宽、高与特征图(feature map)的宽、高的比值。yolov3算法利用k-mean算法对数据中的标记框进行聚类,生成9个anchor,每个输出(y1,y2,y3)各有三个anchor。 b w b_w bw b h b_h bh为检测框的宽和高。
Yolov3损失函数:
在这里插入图片描述
在YOLOV3中,Loss分成三个部分
1、目标框位置(左上角和长宽)带来的误差,也即是box带来的loss。而在box带来的loss中又分为带来的BCE Loss以及带来的MSE Loss。
2、目标置信度带来的误差,也就是目标obj带来的loss(BCE Loss)。
3、类别带来的误差,也就是种类class带来的loss(类别数个BCE Loss)。
其中BCE Loss为二值交叉熵损失函数,MSE Loss为均方误差损失函数。2- w i ∗ h i w_i*h_i wihi为制衡值。因为当存在小目标的时候,小目标与anchor的IOU会相对变小,所以 w i ∗ h i w_i*h_i wihi越小,2- w i ∗ h i w_i*h_i wihi越大,相当于制衡值。
在keras版本的yolov3训练时,有利用k-mean算法生成9个anchor,现在对k-mean做个简单介绍。
K-mean算法:聚类是基于数据中的模式将整个数据划分为组(也称为簇)的过程。K-Means算法是一种无监督分类算法,假设有无标签数据集:
x = ∣ x ( 1 ) x ( 2 ) ⋮ x ( n ) ∣ x=\left| \begin{matrix}x^{(1)}\\x^{(2)}\\\vdots\\x^{(n)} \end{matrix} \right| x=x(1)x(2)x(n)
该算法的任务是将数据集聚类成 k k k个簇 C = C 1 , C 2 , ⋯   , C k . C=C_1,C_2,\cdots,C_k. C=C1,C2,,Ck.最小损失函数为 E = ∑ i = 1 k ∑ x ϵ C i ∣ ∣ x − u i ∣ ∣ 2 E=\sum_{i=1}^k\sum_{x\epsilon{C_i}}{||{x-u_i}||^2} E=i=1kxϵCixui2
其中 u i u_i ui为簇 C i C_i Ci的中心点: u i = 1 ∣ C i ∣ ∑ x ϵ C i x u_i =\frac{1}{|C_i|}\sum_{x\epsilon{C_i}}x ui=Ci1xϵCix
要找到以上问题的最优解需要遍历所有可能的簇划分,K-Means算法使用贪心策略求得一个近似解,具体步骤如下:

  1. 在样本中随机选取k个样本点充当各个簇的中心点 u 1 , u 2 , ⋯   , u k . {u_1,u_2,\cdots,u_k.} u1,u2,,uk.
  2. 计算所有样本点与各个簇中心之间的距离 d i s t ( x ( i ) , u j ) dist(x^{(i)},u_j) dist(x(i),uj),然后把样本点划入最近的簇中, x ( i ) ϵ u n e a r e s t x^{(i)}\epsilon{u_{nearest}} x(i)ϵunearest
  3. 根据簇中已有的样本点,重新计算簇中心 u i = 1 ∣ C i ∣ ∑ x ϵ C i x u_i =\frac{1}{|C_i|}\sum_{x\epsilon{C_i}}x ui=Ci1xϵCix
  4. 重复2、3
  5. 当样本点不更换簇中心,或者到达设定的迭代上限等停止迭代。

总结

在工程上使用yolov3有很多的技巧,包括部署时候,如何提高速度和准确率等。

  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

__JDM__

希望能得到您的打赏

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值