对dropout的理解详细版[通俗易懂]

2022-08-18 13:05:44 浏览数 (1)

大家好,又见面了,我是你们的朋友全栈君。

dropout可以让模型训练时,随机让网络的某些节点不工作(输出置零),也不更新权重(但会保存下来,下次训练得要用,只是本次训练不参与bp传播),其他过程不变。我们通常设定一个dropout radio=p,即每个输出节点以概率p置0(不工作,权重不更新),假设每个输出都是独立的,每个输出都服从二项伯努利分布p(1-p),则大约认为训练时,只使用了(1-p)比例的输出,相当于每次训练一个子网络。测试的时候,可以直接去掉Dropout层,将所有输出都使用起来,为此需要将尺度对齐,即比例缩小输出 r=r*(1-p)。

训练的时候需要dropout,测试的时候直接去掉。

如果测试时的时候添加了dropout层,测试的时候直接把前一层的特征结果传到下一层:

dropout层相当于组合了N个网络,测试的时候去掉dropout,相当于N个网络的组合;

什么是Dropout

我们知道,典型的神经网络其训练流程是将输入通过网络进行正向传导,然后将误差进行反向传播。Dropout就是针对这一过程之中,随机地删除隐藏层的部分单元,进行上述过程。

综合而言,上述过程可以分步骤为:

随机删除网络中的一些隐藏神经元,保持输入输出神经元不变; 将输入通过修改后的网络进行前向传播,然后将误差通过修改后的网络进行反向传播; 对于另外一批的训练样本,重复上述操作1.

Dropout作用分析

从Hinton的原文以及后续的大量实验论证发现,dropout可以比较有效地减轻过拟合的发生,一定程度上达到了正则化的效果。

论其原因而言,主要可以分为两个方面:

达到了一种Vote的作用。对于全连接神经网络而言,我们用相同的数据去训练5个不同的神经网络可能会得到多个不同的结果,我们可以通过一种vote机制来决定多票者胜出,因此相对而言提升了网络的精度与鲁棒性。同理,对于单个神经网络而言,如果我们将其进行分批,虽然不同的网络可能会产生不同程度的过拟合,但是将其公用一个损失函数,相当于对其同时进行了优化,取了平均,因此可以较为有效地防止过拟合的发生。 减少神经元之间复杂的共适应性。当隐藏层神经元被随机删除之后,使得全连接网络具有了一定的稀疏化,从而有效地减轻了不同特征的协同效应。也就是说,有些特征可能会依赖于固定关系的隐含节点的共同作用,而通过Dropout的话,它强迫一个神经单元,和随机挑选出来的其他神经单元共同工作,达到好的效果。消除减弱了神经元节点间的联合适应性,增强了泛化能力。

由于每次用输入网络的样本进行权值更新时,隐含节点都是以一定概率随机出现,因此不能保证每2个隐含节点每次都同时出现,这样权值的更新不再依赖于有固定关系隐含节点的共同作用,阻止了某些特征仅仅在其它特定特征下才有效果的情况。

当前Dropout的使用情况,更多其他版本。。。。

当前Dropout被大量利用于全连接网络,而且一般人为设置为0.5或者0.3(链接讲不同层代码试验),而在卷积隐藏层由于卷积自身的稀疏化以及稀疏化的ReLu函数的大量使用等原因,Dropout策略在卷积隐藏层中使用较少drop out是用来防止过拟合的过多参数才会容易过拟合所以卷积层没必要。Ng课里也说越多参数的全链接层要drop out的概率就大一点。(个人觉得卷积核就是特征稀疏作用,卷积核还会有relu等非线性函数,降低特征直接的关联性)

所以一般都用Spatial Dropout,建议看配套的另一篇文章:分析了Dropout以及Inverted Dropout两个版本。里面对本篇的算法部分描述的更加详细些。而且文章下面有关于Inverted Dropout详细描述,因为Inverted Dropout也是现在通用的方法。

总体而言,Dropout是一个超参,需要根据具体的网路,具体的应用领域进行尝试。 ———————

一、相关工作

Dropout是2012年深度学习视觉领域的开山之作paper:《ImageNet Classification with Deep Convolutional》所提到的算法,用于防止过拟合。

网上都说dropout是让某些神经元以一定的概率不工作,但是具体代码怎么实现?原理又是什么,还是有点迷糊,所以就大体扫描了文献:《Improving neural networks by preventing co-adaptation of feature detectors》、《Improving Neural Networks with Dropout》、《Dropout: A Simple Way to Prevent Neural Networks from Overtting》,不过感觉看完以后,还是收获不是很大。下面是我的学习笔记,因为看的不是很细,也没有深入理解,有些地方可能有错,如有错误还请指出。

二、算法概述

我们知道如果要训练一个大型的网络,训练数据很少的话,那么很容易引起过拟合(也就是在测试集上的精度很低),可能我们会想到用L2正则化、或者减小网络规模。然而深度学习领域大神Hinton,在2012年文献:《Improving neural networks by preventing co-adaptation of feature detectors》提出了,在每次训练的时候,让一半的特征检测器停过工作,这样可以提高网络的泛化能力,Hinton又把它称之为dropout

Hinton认为过拟合,可以通过阻止某些特征的协同作用来缓解。在每次训练的时候,每个神经元有百分之50的几率被移除,这样可以让一个神经元的出现不应该依赖于另外一个神经元。

另外,我们可以把dropout理解为 模型平均。 假设我们要实现一个图片分类任务,我们设计出了100000个网络,这100000个网络,我们可以设计得各不相同,然后我们对这100000个网络进行训练,训练完后我们采用平均的方法,进行预测,这样肯定可以提高网络的泛化能力,或者说可以防止过拟合,因为这100000个网络,它们各不相同,可以提高网络的稳定性。而所谓的dropout我们可以这么理解,这n个网络,它们权值共享,并且具有相同的网络层数(这样可以大大减小计算量)。我们每次dropout后,网络模型都可以看成是整个网络的子网络。(需要注意的是如果采用dropout,训练时间大大延长,但是对测试阶段没影响)。

啰嗦了这么多,那么到底是怎么实现的?Dropout说的简单一点就是我们让在前向传导的时候,让某个神经元的激活值以一定的概率p,让其停止工作,Dropout的出现很好的可以解决这个问题,每次做完dropout,相当于从原始的网络中找到一个更的网络,示意图如下:

左边是原来的神经网络,右边是采用Dropout后的网络。这个说是这么说,但是具体代码层面是怎么实现的?怎么让某个神经元以一定的概率停止工作?这个我想很多人还不是很了解,代码层面的实现方法,下面就具体讲解一下其算法方面的实现。以前我们网络的计算公式是:

采用dropout后计算公式就变成了:

上面公式中Bernoulli函数,是为了以概率p,随机生成一个0、1的向量。

算法实现概述:

1、其实Dropout很容易实现,源码只需要几句话就可以搞定了,让某个神经元以概率p,停止工作,其实就是让它的激活值以概率p变为0。比如我们某一层网络神经元的个数为1000个,其激活值为x1,x2……x1000,我们dropout比率选择0.4,那么这一层神经元经过drop后,x1……x1000神经元其中会有大约400个的值被置为0。

2、经过上面屏蔽掉某些神经元,使其激活值为0以后,我们还需要对余下的非0的向量x1……x1000进行rescale(扩大倍数),也就是乘以1/(p)。如果你在训练的时候,经过置0后,没有对x1……x1000进行rescale,那么你在测试的时候,就需要对权重进行rescale:

在训练时,每个神经单元都可能以概率 p 去除; 在测试阶段,每个神经单元都是存在的,权重参数w要乘以p,成为:pw。

问题来了,上面为什么经过dropout需要进行rescale?查找了相关的文献,都没找到比较合理的解释,后面再结合源码说一下我对这个的见解。

所以在测试阶段:如果你既不想在训练的时候,对x进行放大,也不愿意在测试的时候,对权重进行缩小(乘以概率p)。那么你可以测试n次,这n次都采用了dropout,然后对预测结果取平均值,这样当n趋近于无穷大的时候,就是我们需要的结果了(也就是说你可以采用train阶段一模一样的代码,包含了dropout在里面,然后前向传导很多次,比如1000000次,然后对着1000000个结果取平均值)。这部分见 三、源码实现部分 有详细描述。

三、源码实现

下面我引用keras的dropout实现源码进行讲解,keras开源项目github地址为:

https://github.com/fchollet/keras/tree/master/keras。

https://github.com/fchollet/keras/blob/master/keras/backend/theano_backend.py,

dropout实现函数如下:

代码语言:javascript复制
#dropout函数的实现
def dropout(x, level):
	if level < 0. or level >= 1:#level是概率值,必须在0~1之间
	    raise Exception('Dropout level must be in interval [0, 1[.')
	retain_prob = 1. - level
    #我们通过binomial函数,生成与x(x表示输入数据,要对其dropout)一样的维数向量。
    #binomial函数就像抛硬币一样,我们可以把每个神经元当做抛硬币一样
    #硬币正面的概率为p,n表示每个神经元试验的次数
    #因为我们每个神经元只需要抛一次就可以了所以n=1,size参数是我们有多少个硬币。
    #即将生成一个0、1分布的向量,0表示这个神经元被屏蔽,不工作了,也就是dropout了
	sample=np.random.binomial(n=1,p=retain_prob,size=x.shape)
	print sample  #【0,1,0,1。。。】有点像独热
    #0、1与x相乘,我们就可以屏蔽某些神经元,让它们的值变为0。1则不影响
	x *=sample
	print x
    #对余下的非0的进行扩大倍数,因为p<0。0/x=0,所以0不影响
	x /= retain_prob
 
	return x
#对dropout的测试,大家可以跑一下上面的函数,了解一个输入x向量,经过dropout的结果
x=np.asarray([1,2,3,4,5,6,7,8,9,10],dtype=np.float32)
dropout(x,0.4)

函数中,x是本层网络的激活值。Level就是dropout就是每个神经元要被丢弃的概率。不过对于dropout后,为什么对余下的神经元需要进行rescale:

x /= retain_prob

有的人解释有点像归一化一样,就是保证网络的每一层在训练阶段和测试阶段数据分布相同。我查找了很多文献,都没找到比较合理的解释,除了在文献《Regularization of Neural Networks using DropConnect》稍微解释了一下,其它好像都没看到相关的理论解释。

被称为inverted dropout。当模型使用了dropout layer,训练的时候只有占比为 1-p 的隐藏层单元参与训练,那么在预测的时候,如果所有的隐藏层单元都需要参与进来,则得到的结果相比训练时平均要大 1/1-p ,为了避免这种情况,就需要测试的时候将输出结果乘以 1/1-p 使下一层的输入规模保持不变。

而利用inverted dropout,我们可以在训练的时候直接将dropout后留下的权重扩大 1/1-p 倍,这样就可以使结果的scale保持不变,而在预测的时候也不用做额外的操作了,更方便一些。

数学分析:

假设我们设置dropout probability为p, 那么该层大约有比例为p的单元会被drop掉,因为每个神经元是否drop就是一次伯努利实验,这层的dropout概率服从伯努利分布,而分布的期望就是np,以这个作为理论依据。

z ^L =w^L * a^(L-1) b^L

当 L-1层有比例为p的单元drop后, a^(L-1)层大约会变为原来的p倍,为了保证 L 层的z期望不变,所以要在a^(L-1) 与dropout矩阵乘积后,要除以1-p,即扩大1/1-p倍(因为0<p<1)。

个人总结:个人感觉除非是大型网络,才采用dropout,不然我感觉自己在一些小型网络上,训练好像很是不爽。之前搞一个比较小的网络,搞人脸特征点定位的时候,因为训练数据不够,怕过拟合,于是就采用dropout,最后感觉好像训练速度好慢,从此就对dropout有了偏见,感觉训练过程一直在波动,很是不爽。

参考文献:

1、《Improving neural networks by preventing co-adaptation of feature detectors》

2、《Improving Neural Networks with Dropout》

3、《Dropout: A Simple Way to Prevent Neural Networks from Overtting》

4、《ImageNet Classification with Deep Convolutional》 ———————

参考的原文:

https://blog.csdn.net/hjimce/article/details/50413257

https://www.zhihu.com/question/61751133 https://blog.csdn.net/wfei101/article/details/79398856

https://blog.csdn.net/williamyi96/article/details/77544536

https://blog.csdn.net/stdcoutzyx/article/details/49022443

发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/134584.html原文链接:https://javaforall.cn

0 人点赞