Dropout 是一种简单而强大的神经网络和深度学习模型正则化技术。
在这篇文章中,您将了解 Dropout 正则化技术以及如何在 PyTorch 模型中应用它。
阅读本文后,你将了解:
- Dropout 正则化技术的工作原理
- 如何在输入层使用 Dropout
- 如何在隐藏层使用 Dropout
- 如何根据您的问题调整 Dropout 级别
通过我的《用PyTorch进行深度学习》一书来启动你的项目。它提供了包含可用代码的自学教程。
让我们开始吧。

在 PyTorch 模型中使用 Dropout 正则化
图片由 Priscilla Fraire 提供。保留部分权利。
概述
这篇文章分为六个部分;它们是:
- 神经网络的 Dropout 正则化
- PyTorch 中的 Dropout 正则化
- 在输入层使用 Dropout
- 在隐藏层使用 Dropout
- 评估模式下的 Dropout
- 使用 Dropout 的技巧
神经网络的 Dropout 正则化
Dropout 是一种用于神经网络模型的正则化技术,大约在 2012 年至 2014 年提出。它是神经网络中的一层。在神经网络模型训练期间,它会从上一层获取输出,随机选择一些神经元并将它们归零,然后传递到下一层,从而有效地忽略它们。这意味着它们对下游神经元激活的贡献在正向传播时暂时被移除,并且任何权重更新都不会应用于反向传播时的神经元。
当模型用于推理时,dropout 层只是将所有神经元恒定缩放以补偿训练期间 dropout 的影响。
Dropout 具有破坏性,但令人惊讶的是可以提高模型的准确性。随着神经网络的学习,神经元权重会在网络中稳定下来。神经元的权重会针对特定特征进行调整,提供一定的专业化。相邻神经元会依赖这种专业化,如果过度依赖,可能会导致模型过于专业化而对训练数据过于敏感。这种神经元在训练期间对上下文的依赖被称为复杂协同适应。
您可以想象,如果在训练期间神经元被随机从网络中丢弃,其他神经元将不得不介入并处理所需的表示以对缺失神经元进行预测。这被认为会导致网络学习到多个独立的内部表示。
其结果是网络对神经元的特定权重变得不那么敏感。这反过来又使网络能够更好地泛化,并且更不容易过拟合训练数据。
想开始使用PyTorch进行深度学习吗?
立即参加我的免费电子邮件速成课程(附示例代码)。
点击注册,同时获得该课程的免费PDF电子书版本。
PyTorch 中的 Dropout 正则化
您不需要手动从 PyTorch 张量中随机选择元素来实现 dropout。PyTorch 的 `nn.Dropout()` 层可以引入到您的模型中。它通过在训练循环中以给定的概率 p(例如 20%)随机选择要丢弃的节点来实现。在 PyTorch 中,dropout 层进一步将结果张量按因子 $\dfrac{1}{1-p}$ 进行缩放,以保持平均张量值。由于这种缩放,dropout 层在推理时将是一个恒等函数(即,没有效果,只需将输入张量复制为输出张量)。在评估模型时,您应该确保将模型切换到推理模式。
让我们看看如何在 PyTorch 模型中使用 `nn.Dropout()`。
这些示例将使用 声纳数据集。这是一个二元分类问题,旨在从声纳回波中正确识别岩石和模拟水雷。它是一个很好的神经网络测试数据集,因为所有输入值都是数值的,并且具有相同的比例。
该数据集可以从 UCI 机器学习存储库下载。您可以将声纳数据集放置在当前工作目录中,文件名为 *sonar.csv*。
您将使用 scikit-learn 和 10 折交叉验证来评估开发的模型,以便更好地揭示结果差异。
有 60 个输入值和一个输出值。输入值在用于网络之前进行标准化。基线神经网络模型有两个隐藏层,第一个有 60 个单元,第二个有 30 个。随机梯度下降用于训练模型,学习率和动量相对较低。
完整的基线模型如下所示
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 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 |
import numpy as np import pandas as pd import torch import torch.nn as nn import torch.optim as optim from sklearn.preprocessing import LabelEncoder from sklearn.model_selection import StratifiedKFold # 读取数据 数据 = pd。read_csv("sonar.csv", header=无) X = 数据。iloc[:, 0:60] y = 数据。iloc[:, 60] # 将目标从字符串编码为整数 编码器 = LabelEncoder() 编码器。fit(y) y = 编码器。transform(y) # 转换为 2D PyTorch 张量 X = torch。tensor(X。values, dtype=torch。float32) y = torch。tensor(y, dtype=torch。float32)。reshape(-1, 1) # 定义 PyTorch 模型 类 SonarModel(nn。Module): def __init__(self): 超级()。__init__() 自身。layer1 = nn。Linear(60, 60) 自身。act1 = nn。ReLU() 自身。layer2 = nn。Linear(60, 30) 自身。act2 = nn。ReLU() 自身。output = nn。Linear(30, 1) 自身。sigmoid = nn。Sigmoid() 定义 forward(自身, x): x = 自身。act1(自身。layer1(x)) x = 自身。act2(自身。layer2(x)) x = 自身。sigmoid(自身。output(x)) return x # 用于训练模型并返回验证结果的辅助函数 定义 model_train(模型, X_train, y_train, X_val, y_val, n_epochs=300, batch_size=16): loss_fn = nn。BCELoss() 优化器 = optim。SGD(模型。parameters(), lr=0.01, momentum=0.8) batch_start = torch。arange(0, len(X_train), batch_size) model.train() 对于 epoch 在 range(n_epochs): 对于 start 在 batch_start: X_batch = X_train[start:start+batch_size] y_batch = y_train[start:start+batch_size] y_pred = 模型(X_batch) 损失 = loss_fn(y_pred, y_batch) 优化器。zero_grad() 损失。backward() 优化器。step() # 训练后评估准确性 model.eval() y_pred = 模型(X_val) acc = (y_pred。round() == y_val)。浮点数()。mean() acc = 浮点数(acc) 返回 acc # 运行 10 折交叉验证 kfold = StratifiedKFold(n_splits=10, shuffle=True) 准确率 = [] 对于 训练, 测试 在 kfold。split(X, y): # 创建模型,训练,并获取准确率 模型 = SonarModel() acc = model_train(模型, X[train], y[train], X[test], y[test]) print("准确率: %.2f" % acc) 准确率。append(acc) # 评估模型 mean = np。mean(accuracies) std = np。std(accuracies) print("基线: %.2f%% (+/- %.2f%%)" % (mean*100, std*100)) |
运行示例会生成 82% 的估计分类准确率。
1 2 3 4 5 6 7 8 9 10 11 |
准确率:0.81 准确率:0.81 准确率:0.76 准确率:0.86 准确率:0.81 准确率:0.90 准确率:0.86 准确率:0.95 准确率:0.65 准确率:0.80 基线:82.12%(+/- 7.78%) |
在输入层使用 Dropout
Dropout 可以应用于输入神经元,称为可见层。
在下面的示例中,在输入层和第一个隐藏层之间添加了一个新的 Dropout 层。Dropout 率设置为 20%,这意味着每五个输入中将有一个在每个更新周期中被随机排除。
继续上面的基线示例,下面的代码使用输入 dropout 运行相同的网络
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 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 |
import numpy as np import pandas as pd import torch import torch.nn as nn import torch.optim as optim from sklearn.preprocessing import LabelEncoder from sklearn.model_selection import StratifiedKFold # 读取数据 数据 = pd。read_csv("sonar.csv", header=无) X = 数据。iloc[:, 0:60] y = 数据。iloc[:, 60] # 将目标从字符串编码为整数 编码器 = LabelEncoder() 编码器。fit(y) y = 编码器。transform(y) # 转换为 2D PyTorch 张量 X = torch。tensor(X。values, dtype=torch。float32) y = torch。tensor(y, dtype=torch。float32)。reshape(-1, 1) # 定义 PyTorch 模型,输入层有 dropout 类 SonarModel(nn。Module): def __init__(self): 超级()。__init__() 自身。dropout = nn。Dropout(0.2) 自身。layer1 = nn。Linear(60, 60) 自身。act1 = nn。ReLU() 自身。layer2 = nn。Linear(60, 30) 自身。act2 = nn。ReLU() 自身。output = nn。Linear(30, 1) 自身。sigmoid = nn。Sigmoid() 定义 forward(自身, x): x = 自身。dropout(x) x = 自身。act1(自身。layer1(x)) x = 自身。act2(自身。layer2(x)) x = 自身。sigmoid(自身。output(x)) return x # 用于训练模型并返回验证结果的辅助函数 定义 model_train(模型, X_train, y_train, X_val, y_val, n_epochs=300, batch_size=16): loss_fn = nn。BCELoss() 优化器 = optim。SGD(模型。parameters(), lr=0.01, momentum=0.8) batch_start = torch。arange(0, len(X_train), batch_size) model.train() 对于 epoch 在 range(n_epochs): 对于 start 在 batch_start: X_batch = X_train[start:start+batch_size] y_batch = y_train[start:start+batch_size] y_pred = 模型(X_batch) 损失 = loss_fn(y_pred, y_batch) 优化器。zero_grad() 损失。backward() 优化器。step() # 训练后评估准确性 model.eval() y_pred = 模型(X_val) acc = (y_pred。round() == y_val)。浮点数()。mean() acc = 浮点数(acc) 返回 acc # 运行 10 折交叉验证 kfold = StratifiedKFold(n_splits=10, shuffle=True) 准确率 = [] 对于 训练, 测试 在 kfold。split(X, y): # 创建模型,训练,并获取准确率 模型 = SonarModel() acc = model_train(模型, X[train], y[train], X[test], y[test]) print("准确率: %.2f" % acc) 准确率。append(acc) # 评估模型 mean = np。mean(accuracies) std = np。std(accuracies) print("基线: %.2f%% (+/- %.2f%%)" % (mean*100, std*100)) |
运行示例后,分类准确率略有下降,至少在单次测试运行中是这样。
1 2 3 4 5 6 7 8 9 10 11 |
准确率:0.62 准确率:0.90 准确率:0.76 准确率:0.62 准确率:0.67 准确率:0.86 准确率:0.90 准确率:0.86 准确率:0.90 准确率:0.85 基线:79.40%(+/- 11.20%) |
在隐藏层使用 Dropout
Dropout 可以应用于网络模型主体中的隐藏神经元。这更常见。
在下面的示例中,Dropout 应用于两个隐藏层之间以及最后一个隐藏层和输出层之间。再次使用 20% 的 dropout 率
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 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 |
import numpy as np import pandas as pd import torch import torch.nn as nn import torch.optim as optim from sklearn.preprocessing import LabelEncoder from sklearn.model_selection import StratifiedKFold # 读取数据 数据 = pd。read_csv("sonar.csv", header=无) X = 数据。iloc[:, 0:60] y = 数据。iloc[:, 60] # 将目标从字符串编码为整数 编码器 = LabelEncoder() 编码器。fit(y) y = 编码器。transform(y) # 转换为 2D PyTorch 张量 X = torch。tensor(X。values, dtype=torch。float32) y = torch。tensor(y, dtype=torch。float32)。reshape(-1, 1) # 定义 PyTorch 模型,隐藏层有 dropout 类 SonarModel(nn。Module): def __init__(self): 超级()。__init__() 自身。layer1 = nn。Linear(60, 60) 自身。act1 = nn。ReLU() 自身。dropout1 = nn。Dropout(0.2) 自身。layer2 = nn。Linear(60, 30) 自身。act2 = nn。ReLU() 自身。dropout2 = nn。Dropout(0.2) 自身。output = nn。Linear(30, 1) 自身。sigmoid = nn。Sigmoid() 定义 forward(自身, x): x = 自身。act1(自身。layer1(x)) x = 自身。dropout1(x) x = 自身。act2(自身。layer2(x)) x = 自身。dropout2(x) x = 自身。sigmoid(自身。output(x)) return x # 用于训练模型并返回验证结果的辅助函数 定义 model_train(模型, X_train, y_train, X_val, y_val, n_epochs=300, batch_size=16): loss_fn = nn。BCELoss() 优化器 = optim。SGD(模型。parameters(), lr=0.01, momentum=0.8) batch_start = torch。arange(0, len(X_train), batch_size) model.train() 对于 epoch 在 range(n_epochs): 对于 start 在 batch_start: X_batch = X_train[start:start+batch_size] y_batch = y_train[start:start+batch_size] y_pred = 模型(X_batch) 损失 = loss_fn(y_pred, y_batch) 优化器。zero_grad() 损失。backward() 优化器。step() # 训练后评估准确性 model.eval() y_pred = 模型(X_val) acc = (y_pred。round() == y_val)。浮点数()。mean() acc = 浮点数(acc) 返回 acc # 运行 10 折交叉验证 kfold = StratifiedKFold(n_splits=10, shuffle=True) 准确率 = [] 对于 训练, 测试 在 kfold。split(X, y): # 创建模型,训练,并获取准确率 模型 = SonarModel() acc = model_train(模型, X[train], y[train], X[test], y[test]) print("准确率: %.2f" % acc) 准确率。append(acc) # 评估模型 mean = np。mean(accuracies) std = np。std(accuracies) print("基线: %.2f%% (+/- %.2f%%)" % (mean*100, std*100)) |
您可以看到,在这种情况下,添加 dropout 层略微提高了准确性。
1 2 3 4 5 6 7 8 9 10 11 |
准确率:0.86 准确率:1.00 准确率:0.86 准确率:0.90 准确率:0.90 准确率:0.86 准确率:0.81 准确率:0.81 准确率:0.70 准确率:0.85 基线:85.50%(+/- 7.36%) |
评估模式下的 Dropout
Dropout 会随机将某些输入重置为零。如果您想知道训练完成后会发生什么,答案是什么都不会!当模型处于评估模式时,PyTorch dropout 层应该像一个恒等函数一样运行。这就是为什么您在评估模型之前会使用 `model.eval()`。这很重要,因为 dropout 层的目标是确保网络学习到足够的关于输入的线索来进行预测,而不是依赖数据中罕见的现象。但在推理时,您应该向模型提供尽可能多的信息。
使用 Dropout 的技巧
关于 Dropout 的原始论文提供了在标准机器学习问题集上的实验结果。因此,他们提供了一些在使用 Dropout 时需要考虑的有用启发式方法。
- 通常,使用 20%-50% 的神经元的小 dropout 值,其中 20% 是一个很好的起点。概率太低效果最小,概率太高会导致网络学习不足。
- 使用更大的网络。当在更大的网络上使用 Dropout 时,您可能会获得更好的性能,从而为模型提供更多学习独立表示的机会。
- 在传入(可见)单元和隐藏单元上使用 Dropout。在网络每个层应用 Dropout 都显示出良好的结果。
- 使用较大的学习率和衰减以及较大的动量。将您的学习率提高 10 到 100 倍,并使用 0.9 或 0.99 的高动量值。
- 约束网络权重的尺寸。大的学习率可能导致非常大的网络权重。对网络权重尺寸施加约束,例如最大范数正则化,尺寸为 4 或 5,已被证明可以改善结果。
进一步阅读
以下是您可以用来了解更多关于神经网络和深度学习模型中 Dropout 的资源。
论文
在线资料
- 深度学习中的 dropout 方法是如何工作的?(Quora)
- PyTorch 文档中的 nn.Dropout
总结
在这篇文章中,您了解了深度学习模型中的 Dropout 正则化技术。您学到了
- 什么是 Dropout 以及它的工作原理
- 如何在您自己的深度学习模型中使用 Dropout。
- 从您自己的模型中获得最佳 Dropout 效果的技巧。
谢谢,很棒的教程。
我很好奇如何在推理阶段使用 dropout。有什么想法吗?
这种 dropout 的原因是为了有效地只训练一个模型,但同时您希望拥有一个模型集成(通过丢弃不同的神经元)并使用它们的预测来估计模型预测的不确定性。
这通常不这样做,但如果您坚持,PyTorch 有 model.eval() 和 model.train() 来在训练和推理模式之间切换。对于许多层,它们是相同的,但 dropout 层将在随机丢弃或不丢弃之间切换。
感谢这篇精彩的文章。您能分享一下如果使用数据加载器和数据集,KFoldStratified 将如何运行吗?
你好 Ganesh……最佳实践可以在这里找到
https://machinelearning.org.cn/repeated-k-fold-cross-validation-with-python/