暂退法
暂退法(Dropout)是深度学习中一种常用的正则化技术,由 Geoffrey Hinton 和他的同事们在 2012 年提出。它主要用于防止神经网络模型过拟合,提高模型的泛化能力。暂退法是一种简单而有效的正则化方法,被广泛应用于各种深度学习模型中,尤其是在图像和语音识别等领域。
暂退法的工作原理:
随机丢弃:在训练过程中,暂退法会在每次迭代时随机丢弃(即将输出设置为零)网络中的一部分神经元。丢弃的神经元在当前迭代中不会参与前向和反向传播。
保留概率:每个神经元被丢弃的概率是预先设定的,通常由超参数控制。例如,如果设置为 0.5,则每个神经元有 50% 的概率在每次迭代中被丢弃。
训练和测试差异:在训练过程中使用暂退法时,神经元的丢弃是随机的,而在测试或实际使用模型时,所有神经元都参与计算,但输出需要通过保留概率进行缩放,以保持输出的期望值不变。
暂退法的优点:
减少过拟合:通过随机丢弃神经元,暂退法迫使网络学习更加鲁棒的特征,而不是依赖于特定的神经元集合。
模型平均:暂退法可以看作是一种模型平均技术,因为它在训练过程中隐式地训练了多个不同的“稀疏”网络。这些网络的预测结果在测试时被平均,这通常比单个网络的预测更稳定。
防止共适应:暂退法通过随机丢弃神经元,防止了神经元之间形成过于紧密的依赖关系,即共适应(co-adaptation)。
暂退法的缺点:
计算效率:由于在训练过程中需要多次前向和反向传播,暂退法可能会增加训练时间。
超参数选择:合适的丢弃概率需要通过实验来确定,这可能会增加模型调优的工作量。
可能的副作用:在某些情况下,暂退法可能会引入额外的噪声,影响模型的学习效率。
暂退法是一种简单而有效的正则化方法,被广泛应用于各种深度学习模型中,尤其是在图像和语音识别等领域。
代码实例
import torch
from torch import nn
from d2l import torch as d2l
实现 dropout_layer 函数, 该函数以dropout的概率丢弃张量输入X中的元素, 将剩余部分除以1.0-dropout
def dropout_layer(X, dropout):assert 0 <= dropout <= 1 # 使用 assert 语句确保 dropout 参数的值在 0 到 1 之间# 在本情况中,所有元素都被丢弃if dropout == 1:return torch.zeros_like(X)# 在本情况中,所有元素都被保留if dropout == 0:return Xmask = (torch.rand(X.shape) > dropout).float()return mask * X / (1.0 - dropout)
解释一下mask那段代码:
创建一个与 X 形状相同的随机张量 mask,其中的元素是从均匀分布 [0, 1) 中随机抽取的。
将 mask 中的每个元素与 dropout 值进行比较,如果随机值大于 dropout,则在 mask 中保留该元素(值为 1.0),否则将该元素设置为 0。这样,mask 张量中大约有 dropout 比例的元素为 0,其余为 1。
将 mask 张量与输入 X 相乘,这样 X 中对应于 mask 中为 0 的元素将被丢弃(设置为 0)。
最后,为了保持输出张量的期望值不变,将丢弃操作后的张量除以 (1.0 - dropout)。这是因为在丢弃了一部分元素后,剩余元素的总和会减少,通过除以 (1.0 - dropout) 可以使得输出张量的期望值与原始输入 X 的期望值保持一致。
通过下面几个例子来测试dropout_layer函数,将输入X通过暂退法操作,暂退概率分别为0、0.5和1。
X= torch.arange(16, dtype = torch.float32).reshape((2, 8))
print(X, X.sum())
print(dropout_layer(X, 0.), dropout_layer(X, 0.).sum())
print(dropout_layer(X, 0.5), dropout_layer(X, 0.5).sum())
print(dropout_layer(X, 1.), dropout_layer(X, 1.).sum())
使用学习记录9-图像分类数据集中引入的Fashion-MNIST数据集。
定义具有两个隐藏层的多层感知机,每个隐藏层包含256个单元。
num_inputs, num_outputs, num_hiddens1, num_hiddens2 = 784, 10, 256, 256
定义模型
可以将暂退法应用于每个隐藏层的输出(在激活函数之后), 并且可以为每一层分别设置暂退概率: 常见的技巧是在靠近输入层的地方设置较低的暂退概率。 下面的模型将第一个和第二个隐藏层的暂退概率分别设置为0.2和0.5, 并且暂退法只在训练期间有效。
dropout1, dropout2 = 0.2, 0.5class 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_inputsself.training = is_trainingself.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))))# 只有在训练模型时才使用dropoutif 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 outnet = Net(num_inputs, num_outputs, num_hiddens1, num_hiddens2)
训练和测试
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层仅传递数据。
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);
nn.init.normal_(m.weight, std=0.01):如果模块是 nn.Linear 层,这行代码会使用均值为 0,标准差为 0.01 的正态分布来初始化该层的权重。权重的初始化对于模型的训练和性能至关重要。
net.apply(init_weights);:这行代码调用 apply 方法,将 init_weights 函数应用于模型中的每个模块。这意味着 init_weights 函数将被用来初始化模型中所有 nn.Linear 层的权重。
trainer = torch.optim.SGD(net.parameters(), lr=lr)
d2l.train_ch3(net, train_iter, test_iter, loss, num_epochs, trainer)
封面图片来源
欢迎点击我的主页查看更多文章。
本人学习地址https://zh-v2.d2l.ai/
恳请大佬批评指正。