动手学深度学习-13

敖炜 Lv5

暂退法/丢弃法(dropout)

重新审视过拟合

  1. 当面对更多的特征而样本不足时,线性模型往往会过拟合。相反,当给出更多样本而不是特征,通常线性模型不会过拟合。不幸的是,线性模型泛化的可靠性是有代价的。简单的说,线性模型没有考虑到特征之间的交互作用。对于每个特征,线性模型必须指定正的或负的权重,而忽略其他特征
  2. 泛化性和灵活性之间的这种基本权衡被描述为偏差-方差权衡。线性模型有很高的偏差:它们只能表示一小类函数。然而,这些模型的方差很低:它们在不同的随机样本数据上可以得出相似的结果
  3. 深度神经网络位于偏差-方差谱的另一端。与线性模型不同,神经网络并不局限于单独查看每个特征,而是学习特征之间的交互
  4. 即使我们有比特征多得多的样本,深度神经网络也有可能过拟合

扰动的稳健性

  1. 在探究泛化性之前,我们先来定义一下什么是一个“好”的预测模型?我们期待“好”的预测模型能在未知的数据上有很好的表现:经典泛化理论认为,为了缩小训练和测试性能之间的差距,应该以简单的模型为目标。
  2. 在训练过程中,在计算后续层之前向网络的每一层注入噪声。因为当训练一个有多层的深层网络时,注入噪声只会在输入-输出映射上增强平滑性
  3. 暂退法在前向传播过程中,计算每一内部层的同时注入噪声,这已经成为训练神经网络的常用技术。这种方法之所以被称为暂退法,因为我们从表面上看是在训练过程中丢弃(drop out)一些神经元。在整个训练过程的每一次迭代中,标准暂退法包括在计算下一次层之前将当前层的一些节点置零

实践中的暂退法

  1. 通常,我们在测试时不用暂退法。给定一个训练好的模型和一个新的样本,我们不会丢弃任何节点,因此不需要标准化。然而也有一些例外:一些研究人员在测试时使用暂退法,用于估计神经网络预测的“不确定性”:如果通过许多不同的暂退法遮盖后得到的预测结果都是一致的,那么我们可以说网络发挥更稳定

从零开始实现

  1. 要实现单层的暂退法函数,我们从均匀分布[0, 1]中抽取样本,样本数与这层神经网络的维度一致。然后我们保留那些对应样本大于的节点,把剩下的丢弃

  2. 我们实现dropout_layer函数,该函数一dropout的概率丢弃张量输入X中的元素,重新缩放剩余部分

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    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)

定义模型参数

  1. 我们使用Fashion-MNIST数据集,定义具有两个隐藏层的多层感知机,每个隐藏层包含256个单元

定义模型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
num_inputs, num_outputs, num_hiddens1, num_hiddens2 = 784, 10, 256, 256

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))))

if self.training == True:

H1 = dropout_layer(H1, dropout1)

H2 = self.relu(self.lin2(H1))

if self.training == True:

H2 = dropout_layer(H2, dropout2)

out = self.lin3(H2)

return out

简洁实现

  1. 对于深度学习框架的高级API,我们只需在每个全连接层之后添加一个Dropout层,将暂退概率作为唯一的参数传递给它的构造函数。在训练时,Dropout层将根据指定的暂退概率随机丢弃上一层的输出(相当于下一层的输入)。测试时,Drop层仅传递数据
1
2
3
4
5
6
7
8
9
10
11
12
13
14
net = nn.Sequential(nn.Flatten(),
nn.Linear(784, 256),
nn.ReLU(),
nn.Dropout(dropout1),
nn.Linear(256, 256),
nn.ReLU(),
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);

小结

  1. 暂退法在前向传播过程中,计算每一内部层的同时丢弃一些神经元
  2. 暂退法可以避免过拟合,它通常与控制权重向量的维数和大小结合使用的
  3. 暂退法将活性值h替换为具有期望值h的随机变量
  4. 暂退法仅在训练期间使用
  • 标题: 动手学深度学习-13
  • 作者: 敖炜
  • 创建于 : 2023-08-11 16:34:39
  • 更新于 : 2024-04-19 09:28:18
  • 链接: https://ao-wei.github.io/2023/08/11/动手学深度学习-13/
  • 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。
评论