我的前馈神经网络系列文章如下,便于读者成体系学习:
6、🔎 正则化方法
在神经网络训练中,过拟合是常见的挑战 。正则化(Regularization) 是一类通过限制模型复杂度、引导模型学习更稳健模式来缓解过拟合的技术。以下是神经网络中常用的正则化方法:
6.1 L1/L2 正则化(权重正则化)
L1 和 L2 正则化是最经典的正则化方法,通过在损失函数中添加权重参数的惩罚项,限制权重的绝对值或平方和,避免模型过度依赖某些特征。
6.1.1 L2 正则化(权重衰减,Weight Decay)
-
定义:在损失函数中加入权重参数平方和的惩罚项,是实践中最常用的正则化方法。
-
原理:
-
原始损失函数为
(
为模型参数,如权重 W 和偏置 b)
-
L2 正则化后的损失为:
其中
为正则化系数(超参数,需调优),控制惩罚强度。
-
L2 会 “压制” 权重的绝对值,使权重分布更均匀(避免某些权重过大),模型更倾向于学习简单的、泛化性强的模式。
-
-
PyTorch 实现: PyTorch 中通过优化器的
weight_decay
参数直接支持 L2 正则化(无需手动修改损失函数),默认对所有可学习参数(权重和偏置)生效(通常偏置不正则化,可手动排除)。import torch import torch.nn as nn import torch.optim as optim # 定义简单模型 class SimpleModel(nn.Module): def __init__(self): super().__init__() self.fc = nn.Linear(10, 2) # 输入10维,输出2维 def forward(self, x): return self.fc(x) model = SimpleModel() criterion = nn.CrossEntropyLoss() # 分类损失 # 优化器设置weight_decay(L2正则化系数) optimizer = optim.SGD(model.parameters(), lr=0.01, weight_decay=1e-4) # λ=1e-4 # 训练过程(简化) x = torch.randn(32, 10) # 32个样本,10维特征 y = torch.randint(0, 2, (32,)) # 标签 pred = model(x) loss = criterion(pred, y) # 原始损失 optimizer.zero_grad() loss.backward() # 自动包含L2惩罚的梯度 optimizer.step()
-
适用场景:几乎所有神经网络(全连接、卷积、Transformer 等),尤其当模型参数较多、易过拟合时。
6.1.2 L1 正则化
定义:在损失函数中加入权重参数绝对值之和的惩罚项。
-
原理:
-
正则化后的损失为:
-
L1 会使部分权重变为 0(稀疏性),相当于自动 “剔除” 不重要的特征,实现特征选择。
-
-
PyTorch 实现: PyTorch 优化器不直接支持 L1 正则化,需手动在损失中添加惩罚项:
# 定义L1惩罚项(仅对权重,忽略偏置) def l1_regularization(model, lambda_l1=1e-4): l1_loss = 0.0 for param in model.parameters(): if param.dim() > 1: # 假设权重是2D及以上,偏置是1D l1_loss += torch.norm(param, 1) # L1范数 return lambda_l1 * l1_loss # 训练时的总损失 pred = model(x) ce_loss = criterion(pred, y) # 交叉熵损失 l1_loss = l1_regularization(model) # L1惩罚 total_loss = ce_loss + l1_loss # 总损失 optimizer.zero_grad() total_loss.backward() optimizer.step()
-
适用场景:需要特征选择的任务(如高维输入数据),但优化难度高于 L2(L1 损失在 0 点不可导),实际中不如 L2 常用。
6.2 Dropout
Dropout 是 Hinton 团队提出的针对神经网络的专用正则化方法,通过随机丢弃部分神经元,强制模型学习更鲁棒的特征(不依赖特定神经元)。
-
原理
-
训练阶段:对某一层的神经元,以概率 p(通常 0.5)随机将其输出设为 0(“丢弃”),其余神经元输出乘以 1/(1-p)(保持期望不变)。 例如:若某层有 4 个神经元,输出为 ([a, b, c, d]),Dropout 概率 (p=0.5),可能随机丢弃第 2 和第 4 个神经元,输出变为 ([2a, 0, 2c, 0])(乘以 (1/(1-0.5)=2))。
-
验证阶段:不丢弃神经元,所有神经元正常输出(无需缩放,因训练时已通过缩放保持期望)。
直观来说,Dropout 使模型在训练时 “随机瘦身”,避免对特定神经元的依赖,相当于同时训练多个子模型,最终集成它们的效果。
-
-
PyTorch 实现
通过 nn.Dropout
层实现,注意训练和验证时的模式切换(model.train()
和 model.eval()
):
class DropoutModel(nn.Module):
def __init__(self):
super().__init__()
self.fc1 = nn.Linear(10, 100)
self.dropout = nn.Dropout(p=0.5) # 丢弃概率50%
self.fc2 = nn.Linear(100, 2)
def forward(self, x):
x = torch.relu(self.fc1(x))
x = self.dropout(x) # 训练时生效,推理时关闭
x = self.fc2(x)
return x
model = DropoutModel()
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=0.001)
# 训练模式(开启Dropout)
model.train()
x_train = torch.randn(32, 10)
y_train = torch.randint(0, 2, (32,))
pred = model(x_train)
loss = criterion(pred, y_train)
loss.backward()
optimizer.step()
# 推理模式(关闭Dropout)
model.eval()
with torch.no_grad(): # 关闭梯度计算
x_test = torch.randn(10, 10)
pred_test = model(x_test) # 无丢弃
-
适用场景
-
全连接层(效果显著)、卷积层(可使用,但需较小的丢弃概率,如 0.1-0.3)。
-
不建议用于循环神经网络(RNN),可改用专门的
nn.Dropout2d
(空间维度丢弃)或nn.AlphaDropout
(保持均值和方差,适合自归一化网络)。
-
6.3 早停(Early Stopping)
早停是一种 “朴素” 但高效的正则化方法,通过监控验证集性能,在模型开始过拟合前停止训练,避免模型过度学习训练数据的噪声。
-
原理
-
训练过程中,模型在训练集上的损失会持续下降,但验证集损失会先下降后上升(过拟合的标志)。
-
早停的核心是:当验证集损失连续若干个 epoch(如 5-10 次)不再下降时,停止训练,并保存此时的模型参数(验证集性能最优的状态)。
-
-
PyTorch 实现
需手动记录验证集损失,判断是否停止:
model = SimpleModel()
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=0.001)
# 早停参数
patience = 5 # 最多容忍5个epoch无改进
best_val_loss = float('inf')
counter = 0 # 记录无改进的epoch数
# 假设已划分训练集和验证集
for epoch in range(100):
# 训练
model.train()
train_pred = model(train_x)
train_loss = criterion(train_pred, train_y)
optimizer.zero_grad()
train_loss.backward()
optimizer.step()
# 验证
model.eval()
with torch.no_grad():
val_pred = model(val_x)
val_loss = criterion(val_pred, val_y)
# 早停判断
if val_loss < best_val_loss:
best_val_loss = val_loss
best_model = model.state_dict() # 保存最优模型
counter = 0 # 重置计数器
else:
counter += 1
if counter >= patience:
print(f"早停于第{epoch}轮")
break
# 加载最优模型
model.load_state_dict(best_model)
-
适用场景
所有神经网络训练,尤其当训练数据有限、模型复杂度高时(如深层 CNN、Transformer)。需注意:验证集的划分需合理(与训练集独立同分布),否则可能导致早停失效。
6.4 数据增强(Data Augmentation)
数据增强是从数据层面缓解过拟合的方法,通过对训练数据进行随机变换(如旋转、裁剪、加噪等),生成 “新样本”,扩大训练集规模,迫使模型学习更通用的特征。
-
原理
-
变换后的样本保留原始数据的核心特征(如猫的图像旋转后仍是猫),但细节不同(如角度、位置)。
-
模型在多样化的样本上训练,能减少对特定细节(如训练集中猫的位置)的依赖,提高泛化能力。
-
-
PyTorch 实现
通过 torchvision.transforms
实现(以图像为例):
from torchvision import transforms
from torch.utils.data import Dataset, DataLoader
# 定义数据增强变换(仅用于训练集)
train_transform = transforms.Compose([
transforms.RandomResizedCrop(224), # 随机裁剪并缩放至224x224
transforms.RandomHorizontalFlip(p=0.5), # 50%概率水平翻转
transforms.RandomRotation(15), # 随机旋转±15度
transforms.ColorJitter(brightness=0.2), # 随机调整亮度
transforms.ToTensor(), # 转为Tensor
])
# 验证集不增强(保持原始分布)
val_transform = transforms.Compose([
transforms.Resize(256),
transforms.CenterCrop(224),
transforms.ToTensor(),
])
# 自定义数据集(假设已实现)
train_dataset = CustomDataset(data_dir, transform=train_transform)
val_dataset = CustomDataset(data_dir, transform=val_transform)
train_loader = DataLoader(train_dataset, batch_size=32, shuffle=True)
val_loader = DataLoader(val_dataset, batch_size=32)
-
适用场景
-
图像任务(最常用):通过裁剪、翻转、色彩变换等增强。
-
文本任务:通过同义词替换、随机插入 / 删除单词、回译等增强。
-
语音任务:通过加噪、语速调整、音调变换等增强。 数据增强的关键是变换需保留标签信息(如猫的图像旋转后标签仍是 “猫”)。
-
6.5 批量归一化(Batch Normalization, BN)
BN 主要用于加速神经网络训练(缓解梯度消失 / 爆炸),但同时具有一定正则化效果,可辅助缓解过拟合。
-
原理
-
训练时,对每一层的输入按批次标准化(减去均值、除以标准差),并引入可学习的缩放和平移参数。
-
正则化效果来源:批次内的随机噪声(不同批次的均值 / 标准差不同)会给模型带来微小扰动,类似 Dropout 的 “随机性”,迫使模型更稳健。
-
-
PyTorch 实现
class BNModel(nn.Module):
def __init__(self):
super().__init__()
self.fc1 = nn.Linear(10, 100)
self.bn = nn.BatchNorm1d(100) # 对100维特征做BN
self.fc2 = nn.Linear(100, 2)
def forward(self, x):
x = self.fc1(x)
x = self.bn(x) # 应用BN
x = torch.relu(x)
x = self.fc2(x)
return x
-
适用场景
几乎所有深度神经网络(CNN、全连接网络等),尤其深层模型。注意:BN 的正则化效果较弱,通常需与 Dropout、早停等配合使用。
6.6 其他正则化方法
-
标签平滑(Label Smoothing): 软化分类任务的 one-hot 标签(如将标签 “1” 改为 “0.9”,其他类别改为 “0.1/(C-1)”,C 为类别数),避免模型对预测过于自信,减少过拟合。 PyTorch 实现:
nn.CrossEntropyLoss(label_smoothing=0.1)
(PyTorch 1.10 + 支持)。 -
模型集成(Ensemble): 训练多个不同的模型(如不同初始化、不同架构),通过投票或平均输出得到最终结果。集成方法能显著提高泛化能力,但计算成本高。 示例:训练 3 个不同的 CNN,推理时取预测概率的平均值。
-
权重剪枝(Weight Pruning): 移除神经网络中绝对值较小的权重(认为其对模型贡献小),简化模型结构,减少过拟合。PyTorch 可通过
torch.nn.utils.prune
模块实现。
总结
正则化方法的核心是 “限制模型复杂度” 或 “增加数据多样性”,实际应用中通常组合多种方法(如 L2 正则化 + Dropout + 早停 + 数据增强)以达到最佳效果。选择时需结合任务类型(分类 / 回归)、数据规模(小数据更依赖数据增强和早停)、模型结构(深层模型需更多正则化)等因素综合考虑。