趋近智
通过向神经网络添加 Dropout 层,可以直接应用这一正则化技术。使用像 PyTorch 这样的框架将 Dropout 集成到模型中是直接的。如何添加 nn.Dropout 层将得到演示,并讨论其对训练过程的影响。
假设我们有一个用于分类任务的简单多层感知器 (MLP)。一个没有 Dropout 的可能架构可能如下所示:
import torch
import torch.nn as nn
class SimpleMLP(nn.Module):
def __init__(self, input_size, hidden_size1, hidden_size2, output_size):
super(SimpleMLP, self).__init__()
self.layer_1 = nn.Linear(input_size, hidden_size1)
self.relu_1 = nn.ReLU()
self.layer_2 = nn.Linear(hidden_size1, hidden_size2)
self.relu_2 = nn.ReLU()
self.output_layer = nn.Linear(hidden_size2, output_size)
def forward(self, x):
x = self.layer_1(x)
x = self.relu_1(x)
x = self.layer_2(x)
x = self.relu_2(x)
x = self.output_layer(x)
return x
# 示例实例化
# model_no_dropout = SimpleMLP(input_size=784, hidden_size1=256, hidden_size2=128, output_size=10)
# print(model_no_dropout)
这是一个标准的前馈网络。如果此模型容易在我们的数据集上过拟合,我们可以引入 Dropout 层。
PyTorch 中的 nn.Dropout 模块实现了 Dropout 技术。它接受 Dropout 概率 p 作为参数,即训练期间任何给定神经元输出被设置为零的概率。一种常见做法是将 Dropout 层放置在隐藏层的激活函数之后。
以下是我们如何修改 SimpleMLP 以包含 Dropout 的方法:
import torch
import torch.nn as nn
class MLPWithDropout(nn.Module):
def __init__(self, input_size, hidden_size1, hidden_size2, output_size, dropout_prob=0.5):
super(MLPWithDropout, self).__init__()
self.layer_1 = nn.Linear(input_size, hidden_size1)
self.relu_1 = nn.ReLU()
# 在第一个隐藏层激活后应用 Dropout
self.dropout_1 = nn.Dropout(p=dropout_prob)
self.layer_2 = nn.Linear(hidden_size1, hidden_size2)
self.relu_2 = nn.ReLU()
# 在第二个隐藏层激活后应用 Dropout
self.dropout_2 = nn.Dropout(p=dropout_prob)
self.output_layer = nn.Linear(hidden_size2, output_size)
def forward(self, x):
x = self.layer_1(x)
x = self.relu_1(x)
x = self.dropout_1(x) # 应用 dropout
x = self.layer_2(x)
x = self.relu_2(x)
x = self.dropout_2(x) # 应用 dropout
x = self.output_layer(x)
return x
# 使用默认 Dropout 概率 0.5 的示例实例化
model_with_dropout = MLPWithDropout(input_size=784, hidden_size1=256, hidden_size2=128, output_size=10)
print(model_with_dropout)
# 或者指定不同的概率
# model_with_dropout_p25 = MLPWithDropout(input_size=784, hidden_size1=256, hidden_size2=128, output_size=10, dropout_prob=0.25)
# print(model_with_dropout_p25)
在此修改后的版本中:
dropout_prob 参数,默认值为 0.5,这是一个常见的起始值。nn.Dropout 层(self.dropout_1、self.dropout_2)。forward 方法中,我们在隐藏层的 ReLU 激活之后立即应用这些 Dropout 层。请注意,Dropout 通常不应用于输出层。使用 Dropout(以及批归一化等其他层)的一个重要方面是训练和评估阶段之间的区别。
model.eval() 时,PyTorch 的 nn.Dropout 会自动处理此缩放,从而实现了“反向 Dropout”技术)。PyTorch 模型具有处理此行为的模式。您必须在它们之间显式切换:
model.train():将模型设置为训练模式。Dropout 层处于活跃状态。model.eval():将模型设置为评估模式。Dropout 层处于非活跃状态,激活值会相应地进行缩放。以下是此在典型训练循环中的样子:
# 假设模型、train_loader、val_loader、optimizer、criterion 已定义
num_epochs = 10
for epoch in range(num_epochs):
# --- 训练阶段 ---
model_with_dropout.train() # 将模型设置为训练模式
train_loss = 0.0
for data, target in train_loader: # 遍历训练批次
optimizer.zero_grad()
output = model_with_dropout(data)
loss = criterion(output, target)
loss.backward()
optimizer.step()
train_loss += loss.item() * data.size(0)
train_loss /= len(train_loader.dataset)
print(f"Epoch {epoch+1} Training Loss: {train_loss:.4f}")
# --- 验证阶段 ---
model_with_dropout.eval() # 将模型设置为评估模式
val_loss = 0.0
with torch.no_grad(): # 禁用验证的梯度计算
for data, target in val_loader: # 遍历验证批次
output = model_with_dropout(data)
loss = criterion(output, target)
val_loss += loss.item() * data.size(0)
val_loss /= len(val_loader.dataset)
print(f"Epoch {epoch+1} Validation Loss: {val_loss:.4f}")
# --- 在测试集上进行最终评估 ---
# model_with_dropout.eval() # 确保模型处于评估模式
# with torch.no_grad():
# # 执行测试...
在验证或测试期间忘记切换到 model.eval() 是一个常见错误。这会导致随机预测(由于 Dropout 活跃)和不正确的性能测量,因为输出没有正确缩放。
Dropout 通常有助于弥合训练性能和验证/测试性能之间的差距,表明过拟合减少。虽然使用 Dropout 时训练损失可能略高或收敛较慢(因为网络在每次迭代中都会有效改变),但与没有 Dropout 且过拟合的模型相比,验证损失通常应该更低、更稳定。
比较了有无 Dropout 的模型的训练和验证损失曲线。请注意,没有 Dropout 的模型验证损失开始增加(表明过拟合),而有 Dropout 的模型验证损失保持更低和更稳定,尽管训练损失略高。
本实践演示了基本实现。您现在可以尝试以下方面:
p): 尝试不同的值(例如,0.2、0.3、0.5)。更高的值提供更强的正则化效果,但如果设置过高,可能会减慢收敛速度或导致欠拟合。添加 Dropout 是您应对过拟合的有力工具。请记住正确使用 model.train() 和 model.eval(),以确保它在不同阶段表现出预期行为。
这部分内容有帮助吗?
© 2026 ApX Machine Learning用心打造