深度学习入门(十二)drop out 丢弃法
前言
核心内容来自博客链接1博客连接2希望大家多多支持作者
本文记录用,防止遗忘
drop out 丢弃法
课件
1 动机
一个好的模型需要对输入数据的扰动鲁棒
- 使用有噪音的数据等价于Tikhonov 正则
- 丢弃法:在层之间加入噪音
2 无偏差的加入噪音
对
x
x
x加入噪音得到
x
′
x'
x′,我们希望
E
[
x
′
]
=
x
E[x']=x
E[x′]=x
丢弃法
x
i
′
=
{
0
概率为
p
x
i
1
−
p
其他情况
\begin{split}\begin{aligned} x_i' = \begin{cases} 0 & \text{ 概率为 } p \\ \frac{x_i}{1-p} & \text{ 其他情况} \end{cases} \end{aligned}\end{split}
xi′={01−pxi 概率为 p 其他情况
3 使用丢弃法
通常将丢弃法作用在隐藏全连接层的输出上
h
=
σ
(
W
1
x
+
b
1
)
h
′
=
d
r
o
p
o
u
t
(
h
)
o
=
W
2
h
′
+
b
2
y
=
s
o
f
t
m
a
x
(
o
)
\mathbf{h=\sigma(W_1x+b_1)} \\ \mathbf{h'=dropout(h)} \\ \mathbf{o=W_2h'+b_2} \\ \mathbf{y=softmax(o)} \\
h=σ(W1x+b1)h′=dropout(h)o=W2h′+b2y=softmax(o)
推理中的丢弃法
正则项只在训练中使用:他们影响模型参数的更新
在推理过程中,丢弃法直接返回输入
h
=
d
r
o
p
o
u
t
(
h
)
h = dropout(h)
h=dropout(h)
- 这样也能保证确定性的输出
4 总结
- 丢弃法将一些输出项随机置0来控制模型复杂度
- 常作用在多层感知机的隐藏层输出上
- 丢弃概率是控制模型复杂度的超参数
教材
1 重新审视过拟合
当面对更多的特征而样本不足时,线性模型往往会过拟合。 相反,当给出更多样本而不是特征,通常线性模型不会过拟合。 不幸的是,线性模型泛化的可靠性是有代价的。 简单地说,线性模型没有考虑到特征之间的交互作用。 对于每个特征,线性模型必须指定正的或负的权重,而忽略其他特征。
泛化性和灵活性之间的这种基本权衡被描述为偏差-方差权衡(bias-variance tradeoff)。 线性模型有很高的偏差:它们只能表示一小类函数。 然而,这些模型的方差很低:它们在不同的随机数据样本上可以得出相似的结果。
深度神经网络位于偏差-方差谱的另一端。 与线性模型不同,神经网络并不局限于单独查看每个特征,而是学习特征之间的交互。 例如,神经网络可能推断“尼日利亚”和“西联汇款”一起出现在电子邮件中表示垃圾邮件, 但单独出现则不表示垃圾邮件。
即使我们有比特征多得多的样本,深度神经网络也有可能过拟合。 2017年,一组研究人员通过在随机标记的图像上训练深度网络。 这展示了神经网络的极大灵活性,因为人类很难将输入和随机标记的输出联系起来, 但通过随机梯度下降优化的神经网络可以完美地标记训练集中的每一幅图像。 想一想这意味着什么? 假设标签是随机均匀分配的,并且有10个类别,那么分类器在测试数据上很难取得高于10%的精度, 那么这里的泛化差距就高达90%,如此严重的过拟合。
深度网络的泛化性质令人费解,而这种泛化性质的数学基础仍然是悬而未决的研究问题。 我们鼓励喜好研究理论的读者更深入地研究这个主题。 本节,我们将着重对实际工具的探究,这些工具倾向于改进深层网络的泛化性。
2 扰动的稳健性
在探究泛化性之前,我们先来定义一下什么是一个“好”的预测模型? 我们期待“好”的预测模型能在未知的数据上有很好的表现: 经典泛化理论认为,为了缩小训练和测试性能之间的差距,应该以简单的模型为目标。 简单性以较小维度的形式展现, 我们在讨论线性模型的单项式函数时探讨了这一点。 此外,正如我们在讨论权重衰减( L 2 L_2 L2正则化)时看到的那样, 参数的范数也代表了一种有用的简单性度量。
简单性的另一个角度是平滑性,即函数不应该对其输入的微小变化敏感。 例如,当我们对图像进行分类时,我们预计向像素添加一些随机噪声应该是基本无影响的。 1995年,克里斯托弗·毕晓普证明了 具有输入噪声的训练等价于Tikhonov正则化 [Bishop, 1995]。 这项工作用数学证实了“要求函数光滑”和“要求函数对输入的随机噪声具有适应性”之间的联系。
然后在2014年,斯里瓦斯塔瓦等人 [Srivastava et al., 2014] 就如何将毕晓普的想法应用于网络的内部层提出了一个想法: 在训练过程中,他们建议在计算后续层之前向网络的每一层注入噪声。 因为当训练一个有多层的深层网络时,注入噪声只会在输入-输出映射上增强平滑性。
这个想法被称为暂退法(dropout)。 暂退法在前向传播过程中,计算每一内部层的同时注入噪声,这已经成为训练神经网络的常用技术。 这种方法之所以被称为暂退法,因为我们从表面上看是在训练过程中丢弃(drop out)一些神经元。 在整个训练过程的每一次迭代中,标准暂退法包括在计算下一层之前将当前层中的一些节点置零。
需要说明的是,暂退法的原始论文提到了一个关于有性繁殖的类比: 神经网络过拟合与每一层都依赖于前一层激活值相关,称这种情况为“共适应性”。 作者认为,暂退法会破坏共适应性,就像有性生殖会破坏共适应的基因一样。
那么关键的挑战就是如何注入这种噪声。 一种想法是以一种无偏向(unbiased)的方式注入噪声。 这样在固定住其他层时,每一层的期望值等于没有噪音时的值。
在毕晓普的工作中,他将高斯噪声添加到线性模型的输入中。 在每次训练迭代中,他将从均值为零的分布 ϵ ∼ N ( 0 , σ 2 ) \epsilon \sim \mathcal{N}(0,\sigma^2) ϵ∼N(0,σ2)采样噪声添加到输入 x \mathbf{x} x, 从而产生扰动点 x ′ = x + ϵ \mathbf{x}' = \mathbf{x} + \epsilon x′=x+ϵ, 预期是 E [ x ′ ] = x E[\mathbf{x}'] = \mathbf{x} E[x′]=x。
在标准暂退法正则化中,通过按保留(未丢弃)的节点的分数进行规范化来消除每一层的偏差。 换言之,每个中间活性值以暂退概率由随机变量替换,如下所示:
h ′ = { 0 概率为 p h 1 − p 其他情况 \begin{split}\begin{aligned} h' = \begin{cases} 0 & \text{ 概率为 } p \\ \frac{h}{1-p} & \text{ 其他情况} \end{cases} \end{aligned}\end{split} h′={01−ph 概率为 p 其他情况
根据此模型的设计,其期望值保持不变,即 E [ h ′ ] = h E[h'] = h E[h′]=h3 实践中的暂退法
回想一下 图中带有1个隐藏层和5个隐藏单元的多层感知机。 当我们将暂退法应用到隐藏层,以 p p p的概率将隐藏单元置为零时, 结果可以看作是一个只包含原始神经元子集的网络。 比如在又中,删除了 h 2 h_2 h2和 h 5 h_5 h5, 因此输出的计算不再依赖于或,并且它们各自的梯度在执行反向传播时也会消失。 这样,输出层的计算不能过度依赖于 h 1 , … , h 5 h_1, \ldots, h_5 h1,…,h5的任何一个元素。
通常,我们在测试时不用暂退法。 给定一个训练好的模型和一个新的样本,我们不会丢弃任何节点,因此不需要标准化。 然而也有一些例外:一些研究人员在测试时使用暂退法, 用于估计神经网络预测的“不确定性”: 如果通过许多不同的暂退法遮盖后得到的预测结果都是一致的,那么我们可以说网络发挥更稳定。4 方法
本节中提到的丢弃法特指倒置丢弃法(inverted dropout)。
回忆一下,多层感知机描述了一个单隐藏层的多层感知机。其中输入个数为4,隐藏单元个数为5,且隐藏单元 h i ( i = 1 , … , 5 ) h_i(i=1,\dots,5) hi(i=1,…,5)的计算表达式为
这里 ϕ \phi ϕ是激活函数, x 1 , … , x 4 x_1,\dots,x_4 x1,…,x4 是输入,隐藏单元 i i i的权重参数为 w 1 i , … , w 4 i w_{1i},\dots,w_{4i} w1i,…,w4i ,偏差参数为 b i b_i bi。当对该隐藏层使用丢弃法时,该层的隐藏单元将有一定概率被丢弃掉。设丢弃概率为p,那么有p的概率 h i h_i hi会被清零,有1−p的概率 h i h_i hi会除以1−p做拉伸。丢弃概率是丢弃法的超参数。具体来说,设随机变量 ξ i ξ_i ξi为0和1的概率分别为p和1−p。使用丢弃法时我们计算新的隐藏单元 h i ′ h_i' hi′
由于 E ( ξ i ) = 1 − p E(ξ_i)=1−p E(ξi)=1−p因此
即丢弃法不改变其输入的期望值。让我们对图中的隐藏层使用丢弃法,一种可能的结果如图所示,其中 h 2 h_2 h2和 h 5 h_5 h5被清零。这时输出值的计算不再依赖 h 2 h_2 h2和 h 5 h_5 h5,在反向传播时,与这两个隐藏单元相关的权重的梯度均为0。由于在训练中隐藏层神经元的丢弃是随机的,即 h 1 , … , h 5 h_1,\dots,h_5 h1,…,h5都有可能被清零,输出层的计算无法过度依赖 h 1 , … , h 5 h_1,\dots,h_5 h1,…,h5中的任一个,从而在训练模型时起到正则化的作用,并可以用来应对过拟合。在测试模型时,我们为了拿到更加确定性的结果,一般不使用丢弃法。
代码实现(从零开始)
要实现单层的暂退法函数, 我们从均匀分布 U [ 0 , 1 ] U[0, 1] U[0,1]中抽取样本,样本数与这层神经网络的维度一致。 然后我们保留那些对应样本大于p的节点,把剩下的丢弃。
在下面的代码中,我们实现
dropout_layer
函数, 该函数以dropout的概率丢弃张量输入X中的元素, 如上所述重新缩放剩余部分:将剩余部分除以1.0-dropout。import torch from torch import nn from d2l import torch as d2l def dropout_layer(X, dropout): assert 0 <= dropout <= 1 # 在本情况中,所有元素都被丢弃 if dropout == 1: return torch.zeros_like(X) # 在本情况中,所有元素都被保留 if dropout == 0: return X mask = (torch.rand(X.shape) > dropout).float() return mask * X / (1.0 - dropout)
我们可以通过下面几个例子来测试dropout_layer函数。 我们将输入X通过暂退法操作,暂退概率分别为0、0.5和1。
X= torch.arange(16, dtype = torch.float32).reshape((2, 8)) print(X) print(dropout_layer(X, 0.)) print(dropout_layer(X, 0.5)) print(dropout_layer(X, 1.))
1 定义模型参数
使用Fashion-MNIST数据集。 我们定义具有两个隐藏层的多层感知机,每个隐藏层包含256个单元。
num_inputs, num_outputs, num_hiddens1, num_hiddens2 = 784, 10, 256, 256
2 定义模型
我们可以将暂退法应用于每个隐藏层的输出(在激活函数之后), 并且可以为每一层分别设置暂退概率: 常见的技巧是在靠近输入层的地方设置较低的暂退概率。 下面的模型将第一个和第二个隐藏层的暂退概率分别设置为0.2和0.5, 并且暂退法只在训练期间有效
dropout1, dropout2 = 0.2, 0.5 class Net(nn.Module): def __init__(self, num_inputs, num_outputs, num_hiddens1, num_hiddens2, is_training = True): super(Net, self).__init__() self.num_inputs = num_inputs self.training = is_training self.lin1 = nn.Linear(num_inputs, num_hiddens1) self.lin2 = nn.Linear(num_hiddens1, num_hiddens2) self.lin3 = nn.Linear(num_hiddens2, num_outputs) self.relu = nn.ReLU() def forward(self, X): H1 = self.relu(self.lin1(X.reshape((-1, self.num_inputs)))) # 只有在训练模型时才使用dropout if self.training == True: # 在第一个全连接层之后添加一个dropout层 H1 = dropout_layer(H1, dropout1) H2 = self.relu(self.lin2(H1)) if self.training == True: # 在第二个全连接层之后添加一个dropout层 H2 = dropout_layer(H2, dropout2) out = self.lin3(H2) return out net = Net(num_inputs, num_outputs, num_hiddens1, num_hiddens2)
3 训练和测试
num_epochs, lr, batch_size = 10, 0.5, 256 loss = nn.CrossEntropyLoss(reduction='none') train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size) trainer = torch.optim.SGD(net.parameters(), lr=lr) d2l.train_ch3(net, train_iter, test_iter, loss, num_epochs, trainer)
代码实现(简洁实现)
对于深度学习框架的高级API,我们只需在每个全连接层之后添加一个Dropout层, 将暂退概率作为唯一的参数传递给它的构造函数。 在训练时,Dropout层将根据指定的暂退概率随机丢弃上一层的输出(相当于下一层的输入)。 在测试时,Dropout层仅传递数据。
在PyTorch中,我们只需要在全连接层后添加Dropout层并指定丢弃概率。在训练模型时,Dropout层将以指定的丢弃概率随机丢弃上一层的输出元素;在测试模型时(即model.eval()后),Dropout层并不发挥作用。
net = nn.Sequential(nn.Flatten(), nn.Linear(784, 256), nn.ReLU(), # 在第一个全连接层之后添加一个dropout层 nn.Dropout(dropout1), nn.Linear(256, 256), nn.ReLU(), # 在第二个全连接层之后添加一个dropout层 nn.Dropout(dropout2), nn.Linear(256, 10)) def init_weights(m): if type(m) == nn.Linear: nn.init.normal_(m.weight, std=0.01) net.apply(init_weights);
接下来,我们对模型进行训练和测试。
trainer = torch.optim.SGD(net.parameters(), lr=lr) d2l.train_ch3(net, train_iter, test_iter, loss, num_epochs, trainer)
小结
暂退法在前向传播过程中,计算每一内部层的同时丢弃一些神经元。
暂退法可以避免过拟合,它通常与控制权重向量的维数和大小结合使用的。
暂退法将活性值h替换为具有期望值h的随机变量。
暂退法仅在训练期间使用。
QA
1、drop out一般用于全连接层,BN一般用于卷积层
2、drop out思想类似随机森林多决策树做投票