Dropout原理与实现

Dec 29, 2024
1 views
Deep Learning

Dropout的运作方式

在神经网络的训练过程中,对于一次迭代中的某一层神经网络,先随机选择中的一些神经元并将其临时隐藏(丢弃),然后再进行本次训练和优化。在下一次迭代中,继续随机隐藏一些神经元,如此直至训练结束。由于是随机丢弃,故而每一个mini-batch都在训练不同的网络。

在训练时,每个神经单元以概率\(𝑝\) 被保留(Dropout丢弃率为\(1−𝑝\));在预测阶段(测试阶段),每个神经单元都是存在的,权重参数\(𝑤\)要乘以\(𝑝\),输出是:\(𝑝𝑤\)。示意图如下:

image

预测阶段需要乘上\(p\) 的原因:

前一层隐藏层的一个神经元在dropout之前的输出是\(x\),训练时dropout之后的期望值是\(E=px+0*(1−p)\); 在预测阶段该层神经元总是激活,为了保持同样的输出期望值并使下一层也得到同样的结果,需要调整\(x\rightarrow px\). 其中\(p\) 是Bernoulli分布(0-1分布)中值为1的概率。

Dropout 实现

如前文所述,在训练时随机隐藏部分神经元,在预测时必须要乘上p。代码如下:

import numpy as np

p = 0.5  # 神经元激活概率

def train_step(X):
    """ X contains the data """

    # 三层神经网络前向传播为例
    H1 = np.maximum(0, np.dot(W1, X) + b1)
    U1 = np.random.rand(*H1.shape) < p   # first dropout mask
    H1 *= U1 # drop!
    H2 = np.maximum(0, np.dot(W2, H1) + b2)
    U2 = np.random.rand(*H2.shape) < p # second dropout mask
    H2 *= U2 # drop!
    out = np.dot(W3, H2) + b3


def predict(X):
    # ensembled forward pass
    H1 = np.maximum(0, np.dot(W1, X) + b1) * p    # NOTE: scale the activations
    H2 = np.maximum(0, np.dot(W2, H1) + b2) * p   # NOTE: scale the activations
    out = np.dot(W3, H2) + b3

反向Dropout

一个略有不同的方法是使用反向Dropout(Inverted Dropout)。该方法包括在训练阶段缩放激活函数,从而使得其测试阶段保持不变。比例因子是保持概率的倒数即\(1/p\)。所以我们换一个思路,在训练时候对数据进行\(1/p\) 缩放,在训练时,就不需要做什么了。

反向Dropout只定义一次模型并且只改变了一个参数(保持/丢弃概率)以使用同一模型进行训练和测试。相反,直接Dropout,必须要在测试阶段修改网络。因为如果你不乘以比例因子p,神经网络的输出将产生更高的相对于连续神经元所期望的值(因此神经元可能饱和):因此反向Dropout是更加常见的实现方式。代码如下:

p = 0.5 # probability of keeping a unit active. higher = less dropout

def train_step(X):
    # forward pass for example 3-layer neural network
    H1 = np.maximum(0, np.dot(W1, X) + b1)
    U1 = (np.random.rand(*H1.shape) < p) / p # first dropout mask. Notice /p!
    H1 *= U1 # drop!
    H2 = np.maximum(0, np.dot(W2, H1) + b2)
    U2 = (np.random.rand(*H2.shape) < p) / p # second dropout mask. Notice /p!
    H2 *= U2 # drop!
    out = np.dot(W3, H2) + b3

    # backward pass: compute gradients... (not shown)
    # perform parameter update... (not shown)

def predict(X):
    # ensembled forward pass
    H1 = np.maximum(0, np.dot(W1, X) + b1) # no scaling necessary
    H2 = np.maximum(0, np.dot(W2, H1) + b2)
    out = np.dot(W3, H2) + b3

Dropout为什么可以防止过拟合?

(1)取平均的作用

先回到标准的模型即没有dropout,我们用相同的训练数据去训练5个不同的神经网络,一般会得到5个不同的结果,此时我们可以采用 “5个结果取均值”或者“多数取胜的投票策略”去决定最终结果。例如3个网络判断结果为数字9,那么很有可能真正的结果就是数字9,其它两个网络给出了错误结果。这种“综合起来取平均”的策略通常可以有效防止过拟合问题。因为不同的网络可能产生不同的过拟合,取平均则有可能让一些“相反的”拟合互相抵消。dropout掉不同的隐藏神经元就类似在训练不同的网络,随机删掉一半隐藏神经元导致网络结构已经不同,整个dropout过程就相当于对很多个不同的神经网络取平均。而不同的网络产生不同的过拟合,一些互为“反向”的拟合相互抵消就可以达到整体上减少过拟合。

(2)减少神经元之间复杂的共适应关系

用作者原话是“在标准神经网络中,每个参数接收的导数表明其应该如何变化才能使最终损失函数降低,并给定所有其它神经网络单元的状态。因此神经单元可能以一种可以修正其它神经网络单元的错误的方式进行改变。而这就可能导致复杂的共适应(co-adaptations)。由于这些共适应现象没有推广到未见的数据,将导致过拟合。我们假设对每个隐藏层的神经网络单元,Dropout通过使其它隐藏层神经网络单元不可靠从而阻止了共适应的发生。因此,一个隐藏层神经元不能依赖其它特定神经元去纠正其错误。”

因为dropout程序导致两个神经元不一定每次都在一个dropout网络中出现。这样权值的更新不再依赖于有固定关系的隐含节点的共同作用,阻止了某些特征仅仅在其它特定特征下才有效果的情况 。迫使网络去学习更加鲁棒的特征 ,这些特征在其它的神经元的随机子集中也存在。换句话说假如我们的神经网络是在做出某种预测,它不应该对一些特定的线索片段太过敏感,即使丢失特定的线索,它也应该可以从众多其它线索中学习一些共同的特征。从这个角度看dropout就有点像L1,L2正则,减少权重使得网络对丢失特定神经元连接的鲁棒性提高。

(3)Dropout类似于性别在生物进化中的角色

物种为了生存往往会倾向于适应这种环境,环境突变则会导致物种难以做出及时反应,性别的出现可以繁衍出适应新环境的变种,有效的阻止过拟合,即避免环境改变时物种可能面临的灭绝。

BN和Dropout共同使用时会出现的问题

BN和Dropout单独使用都能减少过拟合并加速训练速度,但如果一起使用的话并不会产生1+1>2的效果,相反可能会得到比单独使用更差的效果。

相关的研究参考论文:Understanding the Disharmony between Dropout and Batch Normalization by Variance Shift

本论文作者发现理解 Dropout 与 BN 之间冲突的关键是网络状态切换过程中存在神经方差的(neural variance)不一致行为。试想若有图一中的神经响应 X,当网络从训练转为测试时,Dropout 可以通过其随机失活保留率(即 p)来缩放响应,并在学习中改变神经元的方差,而 BN 仍然维持 X 的统计滑动方差。这种方差不匹配可能导致数值不稳定(见下图中的红色曲线)。而随着网络越来越深,最终预测的数值偏差可能会累计,从而降低系统的性能。简单起见,作者们将这一现象命名为「方差偏移」。事实上,如果没有 Dropout,那么实际前馈中的神经元方差将与 BN 所累计的滑动方差非常接近(见下图中的蓝色曲线),这也保证了其较高的测试准确率。

image

作者采用了两种策略来探索如何打破这种局限。一个是在所有 BN 层后使用 Dropout,另一个就是修改 Dropout 的公式让它对方差并不那么敏感,就是高斯Dropout。

第一个方案比较简单,把Dropout放在所有BN层的后面就可以了,这样就不会产生方差偏移的问题,但实则有逃避问题的感觉。

第二个方案来自Dropout原文里提到的一种高斯Dropout,是对Dropout形式的一种拓展。作者进一步拓展了高斯Dropout,提出了一个均匀分布Dropout,这样做带来了一个好处就是这个形式的Dropout(又称为“Uout”)对方差的偏移的敏感度降低了,总得来说就是整体方差偏地没有那么厉害了。

Reference

Dropout原理与实现