正则化(Regularization)是机器学习和深度学习中最核心的技术之一,其目的是防止模型过拟合,提升模型在未知数据上的泛化能力。过拟合发生时,模型"死记硬背"了训练数据中的噪声和细节,而非学习真正的数据分布规律。正则化通过对模型施加约束——显式地限制参数大小、隐式地扰动训练过程或增强数据多样性——迫使模型学习更简单、更鲁棒的表示。
从数学本质上看,正则化是在经验风险最小化(Empirical Risk Minimization, ERM)的基础上加入了结构风险最小化(Structural Risk Minimization, SRM)的约束:
其中 是损失函数, 是正则化项, 是控制正则化强度的超参数。 越大,模型受到的约束越强,参数空间越小,模型越简单。
要理解正则化为何有效,需要先理解偏差-方差权衡(Bias-Variance Tradeoff)。模型的总误差可以分解为三个部分:
| 分量 | 含义 | 与复杂度的关系 |
|---|---|---|
| Bias²(偏差平方) | 模型预测的期望与真实值的差距 | 复杂度越高,偏差越低 |
| Variance(方差) | 模型对不同训练集的预测波动程度 | 复杂度越高,方差越高 |
| (不可约误差) | 数据本身的噪声 | 与模型无关 |
正则化通过限制模型复杂度来减小方差,虽然可能略微增加偏差,但总体误差通常显著降低。
假设我们在一个简单的回归问题中比较不同正则化强度下的表现:
| 训练 MSE | 验证 MSE | 偏差² | 方差 | |
|---|---|---|---|---|
| 0(无正则化) | 0.02 | 1.85 | 0.01 | 1.84 |
| 0.01 | 0.05 | 0.42 | 0.03 | 0.39 |
| 0.1 | 0.12 | 0.18 | 0.08 | 0.10 |
| 1.0 | 0.35 | 0.38 | 0.30 | 0.08 |
| 10.0 | 0.78 | 0.80 | 0.76 | 0.04 |
从上表可见, 时验证误差最小,虽然偏差从 0.01 上升到 0.08,但方差从 1.84 骤降到 0.10,总误差大幅降低。
L1 正则化也称为 Lasso(Least Absolute Shrinkage and Selection Operator),其正则化项为参数绝对值的和:
完整的目标函数为:
L1 正则化的关键数学性质是:在最优解处,许多参数会被精确压缩为零。这是因为 L1 范数的等值线是菱形(在二维空间中),与目标函数的等值线相切时,切点往往落在坐标轴上。
具体数值示例:设损失函数为 ,加入 L1 正则化 :
这种稀疏性使 Lasso 天然具备特征选择能力——哪些特征的系数为零,就意味着它们对预测没有贡献。
L1 正则化不可导的缺点可以通过近端梯度下降(Proximal Gradient Descent)解决:
其中 soft-threshold 函数为:
L2 正则化也称为 Ridge 回归(岭回归)或权重衰减(Weight Decay),其正则化项为参数平方和的一半:
完整目标函数:
L2 正则化在梯度下降中的效果非常直观。标准的梯度下降更新为:
加入 L2 正则化后:
可以看到,每次更新前权重先乘上 —— 这就是"权重衰减"名称的由来。这个系数小于 1,相当于每个权重都在指数衰减,迫使模型不能过分依赖任何单个特征。
训练一个包含 5 个特征的线性回归模型:
| 特征 | 无正则化() | Ridge() | Ridge() |
|---|---|---|---|
| 3.82 | 2.15 | 0.93 | |
| -1.45 | -1.02 | -0.47 | |
| 0.67 | 0.51 | 0.22 | |
| -0.03 | -0.02 | -0.01 | |
| 4.21 | 1.98 | 0.76 |
L2 正则化将所有权重向零收缩,但不会精确为零。相比于 Lasso,Ridge 更适用于特征间存在共线性的情况。
| 特性 | L1(Lasso) | L2(Ridge) |
|---|---|---|
| 正则化项 | ||
| 梯度更新 | ||
| 解的稀疏性 | 产生稀疏解(特征选择) | 不产生稀疏解 |
| 导数 | 在 0 处不可导 | 处处可导 |
| 多重共线性 | 不稳定(随机选择其中一个) | 稳定(平均分配权重) |
| 优化方法 | 近端梯度下降 | 标准 SGD |
| 最佳场景 | 高维特征选择 | 特征间存在相关性 |
| 图示(2D 等值线) | 菱形 | 圆形 |
Elastic Net 同时使用 L1 和 L2 正则化,兼顾两者的优点:
或者等价地写成:
其中 控制 L1 和 L2 的混合比例:
为什么会需要 Elastic Net? 当特征数量远大于样本数时,Lasso 最多只能选出 N 个非零特征(N 为样本数)。Elastic Net 可以选出更多特征,同时在处理高度相关的特征组时,Lasso 会随机选择其中一个,而 Elastic Net 会同时选择(或同时丢弃)整组相关特征。
Dropout 是由 Hinton 等人于 2012 年提出的极其有效的正则化技术,特别适用于深度神经网络。
在每次训练迭代中,以概率 随机丢弃(置零)每个神经元:
其中 是掩码向量,元素为 0 或 1, 表示逐元素乘法。 是丢弃概率, 保持概率。
关键实现细节——Inverted Dropout:
在实践中,使用 inverted dropout 变体:训练时除以 以保持期望输出不变,推理时不执行任何操作:
def forward(self, x, training=True, p=0.5):
"""Inverted Dropout 实现"""
if training:
mask = torch.rand_like(x) > p
return x * mask / (1.0 - p)
else:
return x # 推理时无需缩放
| 参数 | 推荐值 | 说明 |
|---|---|---|
| 输入层 | 0.0 - 0.2 | 输入层不宜丢弃太多信息 |
| 隐藏层 | 0.2 - 0.5 | 较大网络可用更高的丢弃率 |
| 卷积层 | 通常不使用或 0.1 - 0.2 | CNN 可考虑 Spatial Dropout |
| 循环神经网络 | 0.2 - 0.3 | 仅丢弃非循环连接(变体 DropConnect) |
| 推理时 | 关闭 | 全部神经元激活,权重缩放 |
具体数值示例:在 MNIST 数据集上训练一个 3 层全连接网络:
| Dropout 率 | 训练准确率 | 测试准确率 | 过拟合程度 |
|---|---|---|---|
| 0.0(无) | 99.8% | 97.2% | 严重 |
| 0.2 | 99.1% | 98.3% | 中等 |
| 0.5 | 97.5% | 98.5% | 轻微 |
| 0.7 | 94.2% | 96.1% | 欠拟合 |
Dropout 率 0.5 时训练准确率从 99.8% 降至 97.5%,但测试准确率从 97.2% 提升到 98.5%,泛化能力显著增强。
早停法是最简单、最有效的正则化方法之一:在验证集误差不再改善时停止训练。
1. 将数据分为训练集和验证集
2. 训练模型,每隔 K 个 epoch 计算验证损失
3. 如果验证损失连续 P 个 epoch 没有改善,停止训练
4. 回滚到验证损失最小的 epoch 对应的模型参数
其中 称为 patience(耐心值), 称为检查频率。
从优化角度看,随机梯度下降从初始化点出发,逐步收敛到经验风险最小化点。早停相当于约束了参数空间的搜索半径——迭代次数越少,最终参数离初始化点越近。这与 L2 正则化的效果高度相似:
研究表明,对于线性模型,早停和 L2 正则化存在精确的对应关系。
best_val_loss = float('inf')
patience_counter = 0
best_model_state = None
for epoch in range(max_epochs):
train_loss = train_one_epoch(model, train_loader)
val_loss = evaluate(model, val_loader)
if val_loss < best_val_loss:
best_val_loss = val_loss
patience_counter = 0
best_model_state = deepcopy(model.state_dict())
else:
patience_counter += 1
if patience_counter >= patience:
print(f"Early stopping at epoch {epoch}")
model.load_state_dict(best_model_state)
break
| 参数 | 典型值 | 场景 |
|---|---|---|
| patience | 5-20 | 小数据集用小值,大数据集用大值 |
| min_delta | 1e-4 | 最小改善幅度,低于此视为无改善 |
| 检查频率 K | 1 epoch | 计算开销允许的情况下频率越高越好 |
数据增强通过对训练数据施加保持标签语义的变换,隐式地扩大了训练集的规模和多样性,是最直观的正则化方式。
| 技术 | 参数范围 | 效果 |
|---|---|---|
| 随机翻转 | 水平翻转(概率 0.5) | 利用对称性 |
| 随机旋转 | 对方向不敏感的特征 | |
| 随机裁剪 | 原图 80%-100% | 关注局部特征 |
| 色彩抖动 | 亮度/对比度/饱和度 | 对光照变化鲁棒 |
| 高斯噪声 | 提高噪声鲁棒性 | |
| Cutout / Random Erasing | 随机遮挡 16% 区域 | 防止过拟合局部特征 |
| Mixup | 线性插值图像和标签 | |
| CutMix | 区域替换 | 结合 Cutout 和 Mixup |
数据增强效果的数值示例(CIFAR-10, ResNet-20):
| 增强策略 | 测试准确率 | 相比基线提升 |
|---|---|---|
| 无增强 | 91.2% | — |
| 随机翻转 + 裁剪 | 92.8% | +1.6% |
| + Cutout | 93.5% | +2.3% |
| + Mixup () | 94.1% | +2.9% |
| + CutMix | 94.4% | +3.2% |
| + AutoAugment | 95.0% | +3.8% |
| 技术 | 方法 | 适用场景 |
|---|---|---|
| 同义词替换 | 随机替换词为其同义词 | 通用 |
| 回译 | 翻译到中间语言再译回 | 高质量增强 |
| 随机插入/删除 | 插入/删除随机词 | 增加多样性 |
| EDA | 组合上述四种方法 | 少样本场景 |
| 对抗训练 | 加入小扰动 | 提升鲁棒性 |
标签平滑是一种用于分类任务的正则化技术。传统分类训练使用 one-hot 标签,这会鼓励模型输出极端概率(logits 趋向无穷大),导致过拟合。标签平滑将硬标签替换为软标签:
其中 是平滑参数, 是类别数。
三分类问题,原始标签为 [1, 0, 0]:
| 平滑后标签 | 效果 | |
|---|---|---|
| 0.0 | [1.0, 0.0, 0.0] | 原始 one-hot |
| 0.1 | [0.93, 0.033, 0.033] | 轻微平滑 |
| 0.3 | [0.80, 0.10, 0.10] | 强平滑 |
标签平滑的效果:
| 技术 | 核心思想 | 适用模型 | 超参数 | 计算开销 | 与 L2 的关系 |
|---|---|---|---|---|---|
| L2 正则化 | 权重衰减 | 任何参数化模型 | 极小 | 基准 | |
| L1 正则化 | 稀疏性 | 线性模型、稀疏网络 | 极小 | 不同范数 | |
| Dropout | 随机子网络集成 | 神经网络 | 训练时中等 | 隐式 L2 | |
| 早停 | 限制迭代步数 | 迭代训练模型 | patience | 极小 | 等价 L2 |
| 数据增强 | 扩大训练分布 | 数据可变换场景 | 增强参数 | 训练时显著 | 非参数 |
| 标签平滑 | 软化目标分布 | 分类模型 | 极小 | 与 L2 互补 | |
| BN | 稳定层输入分布 | 深度网络 | momentum | 极小 | 弱正则化 |
| 噪声注入 | 输入/权重加噪声 | 通用 | 中等 | 数据相关 L2 |
import torch
import torch.nn as nn
import torch.optim as optim
class RegularizedModel(nn.Module):
def __init__(self):
super().__init__()
self.fc1 = nn.Linear(784, 256)
self.fc2 = nn.Linear(256, 128)
self.fc3 = nn.Linear(128, 10)
self.dropout = nn.Dropout(p=0.5)
def forward(self, x):
x = torch.relu(self.fc1(x))
x = self.dropout(x) # Dropout
x = torch.relu(self.fc2(x))
x = self.dropout(x) # Dropout
return self.fc3(x)
model = RegularizedModel()
# L2 正则化:通过 weight_decay 参数
optimizer = optim.Adam(model.parameters(), lr=0.001, weight_decay=1e-4)
# L1 正则化:需要手动实现
l1_lambda = 1e-5
for epoch in range(epochs):
for x_batch, y_batch in dataloader:
optimizer.zero_grad()
outputs = model(x_batch)
loss = nn.CrossEntropyLoss()(outputs, y_batch)
# 手动添加 L1 正则化
l1_loss = sum(p.abs().sum() for p in model.parameters())
total_loss = loss + l1_lambda * l1_loss
total_loss.backward()
optimizer.step()
# 标签平滑
class LabelSmoothingLoss(nn.Module):
def __init__(self, classes, smoothing=0.1):
super().__init__()
self.confidence = 1.0 - smoothing
self.smoothing = smoothing
self.classes = classes
def forward(self, pred, target):
pred = torch.log_softmax(pred, dim=-1)
true_dist = torch.zeros_like(pred)
true_dist.fill_(self.smoothing / (self.classes - 1))
true_dist.scatter_(1, target.unsqueeze(1), self.confidence)
return torch.mean(torch.sum(-true_dist * pred, dim=-1))
正则化是机器学习工程实践中不可或缺的技术。它不是"要不要用"的问题,而是"怎么用、用多强"的问题。核心原则很简单: