-NeoZng【neozng1@hnu.edu.cn】
attention:5.2、5.3、5.4对于新人来说可能有一定难度。
若是新人或刚入门的 RMer,可以由此直接跳转道第六部分继续阅读,第六部分看完后再回来这里继续 ~~
时下RM赛场上的自瞄算法分为两个流派:传统特征提取和神经网络。前一个部分已经介绍了和比赛相关的OpenCV函数,因此为了保证行文的连贯性和整体性又不重复叙述,这个部分主要介绍新兴的基于神经网络的目标检测算法。在第六部分当中则会介绍传统算法的全部流程。
神经网络是时下最火热的ML的子领域,也就是我们常听说的“深度学习”、“深度神经网络”。而基于卷及神经网络的目标检测算法又是这个领域的超级明星。基于机器人学习的目标检测几乎在各方面都替代了传统算法:传统算法需要手工设计检测目标特征,并且很难分离前景和背景。而前者则是通过学习算法来习得检测目标的特征,并且在速度上随着各种trick的加入已经对算本身的改进,已经达到了实时性。虽然缺乏一些可解释性,但也不能阻止其在CV方面大放异彩、在各种顶会顶刊上乱杀的势头。这个部分会介绍卷积神经网络的基本原理和几个热门的目标检测算法的相关知识。检测装甲板、机器人、能量机关这些都只是冰山一角,这些目标检测算法的检测对象几乎是没有限制的,因此基本只需要更改训练集和很少的超参数就可以将算法部署到其他问题上。
目标检测算法经过近十年的发展已经成长为一颗参天大树,基于CNN的目标检测现在大致可以分为两类:1-stage和2-stage。也可以分为anchor-based和anchor-free,再加上使用transformer的后起之秀这三类。若要入门深度学习(神经网络),!!!强烈推荐吴恩达的深度学习系列课程!!!深入浅出贴近实践并且在coursera上有相关的练习。务必至少看完这个系列的前四部分视频再学习目标检测的相关内容。同时吴恩达教授的另一个系列课程机器学习经典名课也深受大家欢迎,若能学完这两个系列的课程,在AI方面就算是入门啦。
我们首先介绍目标检测的基础——神经网络。
当开始阅读5.2之后,笔者默认读者已经拥有了微积分、线性代数、机器学习的基础。
5.2.1.神经网络
和传统的统计学习方法如SVM、PCA、LDA等都是需要经过训练的。以最简单的单层感知机为例,其实我们可以将所有的神经网络看作是一个多维特征空间到另一个我们需要的得到的属性的映射。对神经网络的训练,就是用一对对标记好的数据(已知输入的特征向量和输出的属性,即数据标签)对这个“函数黑箱”的建模过程。其中的每个神经元的w、b即是我们需要得到函数的许多构成参数之一。然后我们根据BP(back propagation)算法等对网络进行最优化(让网络学会这个我们需要的函数,即学习过程),使得这个函数能够逼近我们需要的理想的映射。
分类问题就是在回归问题的基础上增加后处理,如加入SVM和softmax函数等。对于基于卷积神经网络的猫狗图像分类器,假设其输入是320x320的图像,那么就是有320x320=102400个特征(每个像素作为一个特征,单通道位图),其输出是一个三维向量[cat dog none],每个元素智能取0和1并且最多只能有一个元素为1。那么,某个元素的值为1时说明此图像是对应的那种物体,none为1则为说明图像中没有猫也没有狗。那么,训练出来的函数就是一个102400元函数,其输出为3维的离散序列,此函数通过映射将一张图片映射到上述的三维离散空间中。
在此网站:A visual proof that neural nets can compute any function,有对神经网络是如何拟合出任意函数的形象的、直观的解释。手机上还有一款就叫做神经网络的App,你可以手动设置隐藏层的数量、各个神经元的w、b和激活函数等,然后得到一张清晰的拟合出的函数的图片(当然只有3维空间以下的结果能够被可视化地输出)。
app神经网络的截图,内部还提供了著名的网络如shuffleNet、语言模型、TextCNN、自编码器等
对于深度学习或机器学习模型而言,我们不仅要求它对训练数据集有很好的拟合(训练误差小),同时也希望它可以对未知数据集(评价集、测试集、实际应用)有很好的拟合结果(泛化能力强,即训练生成的函数可以很好的根据输入预测输出且基本没有错误),所产生的测试误差被称为泛化误差。度量泛化能力的好坏,最直观的表现就是模型的过拟合(overfitting)和欠拟合(underfitting)。过拟合和欠拟合是用于描述模型在训练过程中的两种状态。这两个问题的都会导致神经网络的泛化能力下降,欠拟合一般是通过增大训练数据量和模型复杂度解决,而过拟合则是当前科研面临的主要问题,解决方法也多种多样。
训练模型得到的几种状态的图解,以回归分析为例
因此,在训练神经网络时,我们需要用一些trick来改善它的性能或加速训练过程,在此过程中就涉及到需要人为确定的许多“超参数”。这些超参数设定的数学依据较少甚至没有,很大部分都是启发式的(拍脑袋想出来的),而不是有一套严密优雅的数学方法来对神经网络的表达能力和学习有效性进行分析。这也是神经网络被传统的统计学习学派诟病为“炼丹”的原因:需要在实践中不断调整超参数的值和网络结构,根据训练结果才能改进网络的结构和超参数的值(就如控制领域的pid算法一样,虽然三板斧调参用起来猛,你却很难说出个所以然来)。
不过它们确实有效,所以我们还是来看看他们的作用吧:
正则化(Regularization)
个人认为这种翻译的有些生硬也不够直观,按照笔者的理解,应该被称做“规范化”或“约束化”。通过对损失函数加入额外的先验知识,防止w在训练中变化过于剧烈,进而在很大的程度上改善过拟合的问题。在一些教程中,常常用一个两参数的单元来可视化这种方法:
摘自周志华教授的《机器学习》,L1正则化和L2正则化的形象图解
通过对w添加额外的限制,倘若w在训练过程中变化过于迅速(这是过拟合的一个主要原因,对训练集数据过于“亲近”,以至于学习到了部分分布当中的一些偏差与噪声,而不是整体分布的信息),额外的惩罚项会约束w的增减,降低其变化速度。详细的解释,在Andrew Ng的深度学习课程第二部分p5.
数据增强(Data Augmentation)
以图像分类为例,通过将训练集中的图像进行翻转、反转、裁切、旋转、变形和添加色彩偏差等方法来扩充数据集,是最廉价最简单的防止overfit的方法。虽然其效果不如收集更多的图片来的得好,可架不住成本低、几乎无开销的特点。在小规模训集上,数据增强是最有效的方法之一。
通过翻转和旋转、裁切等操作,扩充了猫咪数据集
Dropout正则化(随机丢弃)
初看dropout方法的同学一般都会认为它深度学习中显得更加玄乎。其原理大概是在某个隐藏层中随机使某个神经元失活(使其输出为零),这样拟合出来的函数参数量下降,不容易拟合复杂的函数,就更不容易出现过拟合的现象。然而dropout会在反向传播中出现难以解决的问题,由于其丢弃的结点是随机的,无法在每一轮反向传播中确定要更新的参数,因而无法明确的定义损失函数,除非每一轮训练只使用一个样本(想想为什么?)。
从数学角度理解,似乎dropout能够产生L2正则化的效果,随机丢弃某一层中的一个结点,其结果就是该结点不会在本次反向传播对损失函数的计算产生影响。若该结点在之前的训练中变化过于剧烈,那么使其失活就有机会让其他结点的参数得到训练从而获得变化,而非将所有的赌注放在那个结点上(put all of its bets on)。因为每个结点失活的概率是相同的,那么他们应该会得到均等的训练机会。详细的解释参见深度学习课程第二部分p7。
dropout图示,使神经元失活即置零其输出
归一化和标准化(Normalization and Standalization)
这两个操作常常被混淆,在一些文献中也表示相同的意思,他们都是特征缩放的方法。因此本文将他们视作同一种类的不同操作。常见的归一化操作有minmax缩放、零均值化、方差归一化等。也可以把归一化理解成“单位化”、“统一化”。minmax缩放将所有数据通过线性映射把值变到一个区间内。零均值归一化操作会将数据特征的均值化为零,方差归一化则是将特征的方差变为1。后二者常结合在一起使用。
使用了零均值化和方差归一化后的数据,截取自深度学习系列课程
通过这些尺度缩放的操作,可以缩小存储数据需要的空间,同时可以让损失函数变得更加“圆润”。下图给出了未归一化和归一化后的损失函数的对比。显然通下降法,归一化后的数据能够更快地使网络收敛,因为在圆周上的任意一点梯度方向都是垂直于“等高线“的,梯度是函数变化最快的方向(多元微积分知识)。
使用前后的损失函数对比,下方为损失函数的等梯度线(标量)
防止陷入局部最优解
当损失函数可能不是一个凸函数,或者它有多个极值点(大多数情况下都是这样),那么,使用梯度下降方法可能会使神经网络拟合出来的函数陷入局部最优解(向着不是最小/大的极值点不断迭代)
多种初始化值(从头训练)
在初始化网络参数权重时,一次选取多组参数,相当于一次训练多个有相同结构的网络。这样,我们能够从参数空间中的不同位置开始运行梯度下降。即使部分解陷入了局部最优,我们还有其他的解。可以把这种方法看成”广撒网“式的训练。最后我们会测试这多个网络的性能,选取其中最好的一个。虽然这种方法的效果最好,但是其缺点也十分明显:极大的训练开销。
从参数空间的不同位置(蓝色点)开始梯度下降,即使有部分陷入了局部最优解,也可能有一些会收敛到全局最优,至少也是比较好的极值
局部扰动
使用局部扰动有一定的概率跳出局部最优解:在网络的Loss function不再减小或缓慢振荡的时候(也许此时陷入了局部最优解),随机地给网络参数进行微小的扰动。若在有限步迭代(人为设置地超参数)内能够往其他方向移动并且进一步缩小了Loss function,那么说明我们找到了一个比原来的解更好的解,此时再继续运行梯度下降。若迭代次数超出了我们设置地扰动次数上限,则返回原来的位置,认为函数已经收敛到比较好的极值点了,得到了较优的结果。如此,就有概率跳出局部最优解。
局部扰动和模拟退火算法在有某些相似之处。也有研究人员提出,可以利用模拟退火算法来在小规模网络上进行训练,收敛速度十分迅速。
对局部最优解加入扰动后,成功找到了更好的解
mini-batch(小批量训练)
不需要遍历整个数据集就进行一次剃度下降,选取部分样本作为一个批次来计算其loss。从另一个角度来理解,就是加快了迈步的频率。朴素的梯度下降是遍历整个数据集后再计算loss,对参数进行一次更新。minibatch则是把数据集分为32/64/128/256...这样的“小训练集“,在一个小训练集遍历后就进行更新。其优点很明显,就是可以提高训练速度。但它也同样存在一个小缺点:只使用部分训练集进行训练可能产生局部的过拟合或是欠拟合(小部分的数据集无法反映整体数据分布),还好在实践中对模型的影响似乎并不是很大。在数据集稍大的情况下,我们一般都会选择使用minibatch进行训练,否则收敛速度过慢且空间开销过大。
Batch-Norm(批量归一化)
batch-norm字如其名,对每一小批数据进行标准化。它会一批数据的每一个隐藏层的输出都进行归一化。我们在前面已经了解到了Normalization的好处,于是,有研究人员就想到为什么不能对神经网络中的每一层输出都进行类似的操作呢?于是,我们采用的Batch-Norm会将数据集的数据划分为一个个batch,并对每个batch中的数据在每一层的输出都进行归一化。(为什么不对所有数据在每一个输出层都进行normalization?)
在训练结束后进行推理时,一般都是单个数据的输入(特别是实时视频序列的处理),这时候要注意的问题就是每一个输出层的归一化使用的均值与方差的确定,显然对单个数据没有均值和方差可言。因此在训练过程中,我们会用指数加权平均的方式记录每一批norm的均值和方差,并以此作为推理时的参数(根据大数定律,这个值最终会近似实际的分布)。
Batch-Norm为什么奏效呢?在前面的归一化中我们已经了解过,这些操作能够让参数搜索空间中的loss function改变成“碗状”从而获得更好的梯度下降效果。但是既然数据分布已经完成了这件事,在中间的隐藏层为何还要继续使用normalization?这里就要涉及到一个叫做covariate shift的问题。想要理解covariate shift,请看下面这张图:
从第三层开始,可以将其看作整个网络的”子“网络。每一层的参数都试图从前一层的输出中习得这些数据到训练标签y的一个映射
这里只是以第三层为例,其实可以没去掉一层(计算一层)就把它看作是一组数据到y hat的映射。那么自然每一层的输出向量都可以看作是一个特征向量,则我们就对这一批“特征向量”进行归一化。Covariate Shift就是一个向量经过前层的计算,数据原来的分布已经改变了(每一层网络都是非线性映射,从极端的角度来看每一个隐藏层都是一个网络,单层感知机)。那么显然,如果数据的分布一直在改变,网络是很难习得一个映射的(数据分布随着参数更新始终发生变化,就像解方程我们需要一些确定的信心,而在这里这个信息却会一直发生变换表现得像未知数)。使用batch-norm则可以把每一层的输出都标准化为方差为1均值为0的分布,虽然它的具体形式还是未知,但至少我们可以线只它的均值和方差达到限制前层参数的更新对输出数据分布影响的目的。
同时由于batch-norm只对每一个batch的数据作用,显然一个batch不能体现整体数据的分布,因此会引入统计偏差和噪声,有时候这反而是有利的,其效果类似dropout,能够产生一定的正则化效果,避免网络拟合出来的映射过分依赖于某个神经元。
关于Batch-Norm的更多信息,可以参阅论文原文。文章:re-thinking batch in batchNorm 也是一篇很好的参考文章,介绍了更多关于统计和数学原理的知识
学习率衰减
学习率作为最重要的超参数之一,于我们来说有很大的操作空间。训练的开始阶段加速学习迈大脚步,采用较大的学习率;在接近最优解时、Loss function下降减慢时减小学习率,防止超调和振荡。
不同学习率对网络收敛的影响,对比蓝色曲线和绿色曲线,我们可以取二者的精华
有些训练策略还会在初始阶段进行warmup,先选择较小的学习率防止随机初始化的权重使得模型大幅振荡。Warmup也有两种:一种是常量warmup,在热身时选择一个固定的学习率。它的不足之处在于从一个很小的学习率一下变为比较大的学习率可能会导致训练误差突然增大。另一种是渐进warmup,即从最初的小学习率开始,每个step增大一点点,直到达到最初设置的比较大的学习率,再在之后的学习中逐渐减小学习率。还有warmup-restart、cycling、cosine等方法,每隔若干epoch就会重新启动一次warmup、让学习率从大到小以正弦规律往复变化等。需要更深入的了解可以自行查阅相关资料。
优化过的梯度下降方法
GDM(Gradient Descent with Momentum)
动量梯度下降法的收敛速度总是要快于普通的梯度下降法,这得益于它的”动量累积“思想(Andrew Ng说物理好的人容易理解这个算法)。假设在一个网络的训练中你遇到的参数搜索空间如下图所示,其等梯度线呈椭圆排布,那么在训练的过程中就会遇到比较大的振荡(梯度总是沿着等梯度线的切线的径向),即蓝线所示的学习路线。一旦你增大学习率,就会得到紫色的学习轨迹,产生过大的振荡甚至出现不能收敛的问题。我们希望在这个学习问题中能够让竖直方向的学习速度下降,水平方向上的学习速度增加(蓝字所示)。
一个二维的搜索空间,红点表示Loss的最小值
观察蓝线会发现其竖直方向上的振荡均值始终为零,走了很多没必要的路,是否可以通过某种方式让竖直方向上的学习率降低?(注意此处说的竖直方向和水平方向的都是以二维情形为例,显然我们的搜索空间没有这么小,在高维空间里就对应多个方向)GDM就是通过观察学习的特点得到的一种优化方法。
在每一个batch的迭代中,会记录下当前的dw和db,并且以指数加权平均的方式对dw、db进行累积,把得到的累积值用来更新参数。这样,GDM就会记录那些振荡项并把方向不同的dw、db分量抵消,而那些方向相同的分量则会随着迭代的进行不断增大,就好似GDM能够聪明地找到最快下降的方向,顺着“山坡”(最速降线)不断累积“动量”,迅速到达最小值点。
竖直方向上的分量随着迭代减小,水平方向不断增大
如果拿具象的例子类比,就是将一个小球置于碗中,并且给它一个切向的初速度(相当于一步迭代)。对于普通的GD,每次小球的切向速度为零时就让小球停下(把径向速度也归零,即重新计算dw),然后再次释放小球;而对于GMD,就只给小球一个切向速度,之后不去干扰小球(每次计算完成之后,dw都会加权累积),那么切向速度会随着往复运动而逐渐减小(dw的反方向分量会抵消缩小),径向由于有重力加速度(方向相同,累积后不断增大),动量会持续累积,以更快的速度到达碗底。
RMprop (Root Mean propagation)
RMprop和GDM异曲同工之处,同样会在迭代中保存每一次的dw值并对其进行均方加权平均。在更新参数的时候会使用当前一次迭代的dw除以前述得到的值即前几次迭代中dw的平方的加权均值(想想为什么用平方),这样,当dw变化很大的时候,该累计值也会很大,经过相除这个值就会相对变小;而dw小的时候,累计值也相对小,那么相除后用于用于更新的参数将会相对变大。这样的操作让每次更新都更加保守,不会像GDM一样速度越来越快而是始终稳步前进(此速度可以通过加权平均的系数修改)。这种特性也允许在训练时使用更大的学习率。
绿色的箭头就是是用了RMprop后的效果,可以看到学习的步伐非常稳健
Adam (Adaptive Moment Estimatation)
Adams融合了上面两种方法,最后得到的参数更新公式如下:
Adam方法“稳中求进”
Vdw是GDM的累计项,Sdw是RMprop的累积项,在计算结束后对Vdw和Sdw都进行了指数平均修正,确保在刚开始的时候指数加权平均不至于过小。
了解了神经网络的构成和基本原理后就可以开始进一步学习卷积神经网络了。这里也再次提醒读者应该至少至少先学习Ng的的一个和第四个视频再来了解相关方面的知识。拥有基本的了解和学习背景对于探索新领域是非常有必要的,否则你只会在n个不同的超链接里面反复横跳(这是啥?查一下,诶,这又是啥?再查一下......wft?怎么又回来了??)