深度学习——暂退法

深度学习——暂退法


前言

上一章介绍了权重衰减这一正则化技术,本章将记录一下另外一种常见的正则化技术:暂退法。


二、暂退法

2.1. 重新审视过拟合

当面对更多的特征而样本不足时,线性模型往往会过拟合。 相反,当给出更多样本而不是特征,通常线性模型不会过拟合。

不幸的是,线性模型没有考虑到特征之间的交互作用。 对于每个特征,线性模型必须指定正的或负的权重,而忽略其他特征。

泛化性和灵活性之间的这种基本权衡被描述为偏差-方差权衡
线性模型有很高的偏差:它们只能表示一小类函数。 然而,这些模型的方差很低:它们在不同的随机数据样本上可以得出相似的结果。

与线性模型不同,神经网络并不局限于单独查看每个特征,而是学习特征之间的交互。 例如,神经网络可能推断“尼日利亚”和“西联汇款”一起出现在电子邮件中表示垃圾邮件, 但单独出现则不表示垃圾邮件。

即使我们有比特征多得多的样本,深度神经网络也有可能过拟合。

2.2. 扰动的稳健性

我们期待“好”的预测模型能在未知的数据上有很好的表现:经典泛化理论认为,为了缩小训练和测试性能之间的差距,应该以简单的模型为目标。

  1. 简单性以较小维度的形式展现,参数的范数也代表了一种有用的简单性度量。

  2. 简单性的另一个角度是平滑性,即函数不应该对其输入的微小变化敏感。
    例如,当我们对图像进行分类时,我们预计向像素添加一些随机噪声应该是基本无影响的。

在训练过程中,建议在计算后续层之前向网络的每一层注入噪声。因为当训练一个有多层的深层网络时,注入噪声只会在输入-输出映射上增强平滑性。

这个想法被称为暂退法(dropout)
暂退法在前向传播过程中,计算每一内部层的同时注入噪声,这已经成为训练神经网络的常用技术。
这种方法之所以被称为暂退法,因为我们从表面上看是在训练过程中丢弃一些神经元。
在整个训练过程的每一次迭代中,标准暂退法包括在计算下一层之前将当前层中的一些节点置零。

那么关键的挑战就是如何注入这种噪声。

  1. 一种想法是以一种无偏向(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

  1. 在标准暂退法正则化中,通过按保留(未丢弃)的节点的分数进行规范化来消除每一层的偏差。
    换言之,每个中间活性值 h h h暂退概率 p p p由随机变量 h ′ h' h替换,如下所示:

h ′ = { 0  概率为  p h 1 − p  其他情况 \begin{aligned} h' = \begin{cases} 0 & \text{ 概率为 } p \\ \frac{h}{1-p} & \text{ 其他情况} \end{cases} \end{aligned} h={01ph 概率为 p 其他情况

根据此模型的设计,其期望值保持不变,即 E [ h ′ ] = h E[h'] = h E[h]=h

2.3. 实践中的暂退法

当我们将暂退法应用到隐藏层,以 p p p的概率将隐藏单元置为零时, 结果可以看作一个只包含原始神经元子集的网络。

比如在下图中,删除了 h 2 h_2 h2 h 5 h_5 h5
因此输出的计算不再依赖于 h 2 h_2 h2 h 5 h_5 h5,并且它们各自的梯度在执行反向传播时也会消失。这样,输出层的计算不能过度依赖于 h 1 , … , h 5 h_1, \ldots, h_5 h1,,h5的任何一个元素。

在这里插入图片描述

通常,我们在测试时不用暂退法。给定一个训练好的模型和一个新的样本,我们不会丢弃任何节点,因此不需要标准化。

然而也有一些例外:一些研究人员在测试时使用暂退法,用于估计神经网络预测的“不确定性”
如果通过许多不同的暂退法遮盖后得到的预测结果都是一致的,那么我们可以说网络发挥更稳定

2.4. 从零开始实现

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

2.4.1. 定义模型参数

我们实现 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 #一个断言语句,用于确保dropout的值在0到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)
"""
生成一个与输入张量x形状相同的掩码(mask),其中掩码的每个元素都是从0到1之间的随机数。
如果随机数大于dropout概率,对应的元素在掩码中的值为1,表示保留;
如果随机数小于等于dropout概率,对应的元素在掩码中的值为0,表示丢弃.
最后,函数返回掩码乘以输入张量x,再除以保留概率(1.0 - dropout),以确保期望值不变
"""

#我们将输入`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))


#结果:
tensor([[ 0.,  1.,  2.,  3.,  4.,  5.,  6.,  7.],
        [ 8.,  9., 10., 11., 12., 13., 14., 15.]])
tensor([[ 0.,  1.,  2.,  3.,  4.,  5.,  6.,  7.],
        [ 8.,  9., 10., 11., 12., 13., 14., 15.]])
tensor([[ 0.,  0.,  0.,  0.,  0., 10., 12., 14.],
        [ 0.,  0., 20.,  0.,  0.,  0., 28., 30.]])
tensor([[0., 0., 0., 0., 0., 0., 0., 0.],
        [0., 0., 0., 0., 0., 0., 0., 0.]])

2.4.2. 定义模型

我们定义具有两个隐藏层的多层感知机,每个隐藏层包含256个单元
我们可以将暂退法应用于每个隐藏层的输出(在激活函数之后),
并且可以为每一层分别设置暂退概率:
常见的技巧是在靠近输入层的地方设置较低的暂退概率。
下面的模型将第一个和第二个隐藏层的暂退概率分别设置为0.2和0.5,
并且暂退法只在训练期间有效。


# 定义模型
num_inputs, num_outputs, num_hiddens1, num_hiddens2 = 784, 10, 256, 256
dropout1, dropout2 = 0.2, 0.5


class Net(nn.Module): #继承自nn.Module
    def __init__(self, num_inputs, num_outputs, num_hiddens1, num_hiddens2,
                 is_training = True):
        super(Net, self).__init__()  #调用父类nn.Module的构造函数,初始化父类的属性
        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):  #定义了前向传播函数forward
        H1 = self.relu(self.lin1(X.reshape((-1, self.num_inputs)))) #将输入X进行形状变换,并经过线性层1和激活函数层,得到H1
        # 只有在训练模型时才使用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)

2.4.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)
d2l.plt.show()

在这里插入图片描述

2.5. 简洁实现

net = nn.Sequential(nn.Flatten(),nn.Linear(784,256),
                    nn.ReLU(),
                    #在第一个全连接层之后添加一个dropout层
                    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.1)

net.apply(init_weights)

trainer = torch.optim.SGD(net.parameters(),lr)
d2l.train_ch3(net,train_iter,test_iter,loss,num_epochs,trainer)
d2l.plt.show()

在这里插入图片描述


总结

权重衰减和暂退法都可以有效地降低模型的复杂度,减少过拟合的风险,提高模型的泛化能力。前者通过对权重参数施加惩罚项来控制模型复杂度,而后者通过随机丢弃神经元来增强网络的鲁棒性。这两种方法在实际应用中常常结合使用,以进一步提高模型性能。

曲则全,枉则直,洼则盈,敝则新,少则得,多则惑。

–2023-10-2 进阶篇

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

星石传说

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值