2. 模型过拟合与欠拟合:偏差与方差权衡、正则化技术(L1/L2)、Dropout、早停法
做模型训练,说白了就是在跟两个「敌人」打架——欠拟合和过拟合。我刚开始带项目那会儿,总觉得模型在训练集上跑得越准越好,结果一上线就被现实狠狠教育了一顿。今天咱们就把这两个问题彻底聊透。
2.1 偏差与方差:模型的两副面孔
先看一张图,你马上就能明白。
偏差衡量的是模型预测的「准不准」,方差衡量的是模型在不同数据集上「稳不稳」。你想想看,一个模型如果偏差高,说明它连训练数据都没学好;如果方差高,说明它学得太死,换个数据就翻车。
核心口诀:
- 欠拟合 → 偏差高 → 模型太简单,没学到规律
- 过拟合 → 方差高 → 模型太复杂,记住了噪声
我记得有一次做工业缺陷检测,用了个很深的 ResNet,训练集准确率 99.8%,我高兴得不行。结果一上产线,新样本准确率直接掉到 72%。这就是典型的低偏差高方差——模型把训练集里的光照、角度变化全背下来了,反而没学会真正的缺陷特征。
2.2 正则化技术:给模型戴上「紧箍咒」
正则化说白了就是给损失函数加点惩罚,让模型不敢把权重学得太大。我个人的习惯是,只要模型参数超过 10 万,第一件事就是加正则化。
2.2.1 L1 正则化(Lasso)
L1 在损失函数后面加的是权重的值之和:
Loss = 原始损失 + λ * Σ|w_i|
它有个特别有意思的特性——会让部分权重直接变成 0。为什么?因为 L1 的梯度是常数,每次更新都在往 0 方向拉,拉到 0 就卡住了。
我的经验:L1 适合做特征选择。之前做一个用户画像项目,原始特征 2000 维,用 L1 跑完只剩 120 维,模型小了 90%,推理速度翻倍,精度反而没掉。
2.2.2 L2 正则化(Ridge)
L2 加的是权重的平方和:
Loss = 原始损失 + λ * Σw_i²
L2 不会把权重推到 0,而是让所有权重都变小、变均匀。它的梯度是 2λw,权重越大惩罚越狠,所以大权重会被压下来。
| 对比项 | L1 正则化 | L2 正则化 |
|---|---|---|
| 惩罚形式 | 权重值之和 | 权重平方和 |
| 权重结果 | 部分权重变为 0 | 权重整体变小 |
| 适用场景 | 特征选择、稀疏模型 | 防止过拟合、权重平滑 |
| 边缘端优势 | 模型体积小、速度快 | 数值稳定、推理一致 |
避坑指南:λ 值别拍脑袋设。我曾经在一个项目里把 λ 设成 0.1,结果模型直接废了——所有输出都接近 0。建议从 1e-4 开始试,每次乘 10 往上调,观察验证集损失的变化。
2.3 Dropout:随机「开除」神经元
Dropout 的思路特别巧妙——训练时随机让一部分神经元不干活。你想想看,如果每个神经元都知道自己随时可能被「开除」,它就不敢过度依赖别人,必须自己学会提取有效特征。
# 一个简单的 Dropout 实现示意
import numpy as np
def dropout_forward(x, keep_prob=0.5):
mask = np.random.binomial(1, keep_prob, size=x.shape) / keep_prob
return x * mask
注意那个 / keep_prob,这叫「缩放补偿」。训练时只保留了 keep_prob 比例的神经元,推理时所有神经元都工作,如果不缩放,输出值会翻倍。
参数经验值:
- 全连接层:keep_prob 通常设 0.5
- 卷积层:keep_prob 可以设 0.7~0.9(卷积层参数少,不需要太强正则)
- 输入层:keep_prob 设 0.8 以上,别丢太多原始信息
我做过一个对比实验:同样的 CNN 网络,不加 Dropout 时验证集准确率 83%,加 0.5 Dropout 后升到 89%。而且有意思的是,训练时间几乎没增加——Dropout 的计算量可以忽略不计。
2.4 早停法:见好就收
早停法是我在项目里用得最多的技巧,没有之一。它的逻辑很简单:训练过程中监控验证集损失,一旦连续 N 轮不下降,就停下来。
# 早停法伪代码
patience = 10
best_loss = inf
wait = 0
for epoch in range(max_epochs):
train_loss = train_one_epoch()
val_loss = validate()
if val_loss < best_loss:
best_loss = val_loss
save_checkpoint()
wait = 0
else:
wait += 1
if wait >= patience:
print("触发早停,加载最佳模型")
load_checkpoint()
break
参数怎么设?
- patience(耐心值):小数据集设 5~10,大数据集设 10~20
- min_delta(最小改善阈值):设 1e-4,避免微小波动干扰
- restore_best_weights:一定要设为 True,否则最后保存的可能是过拟合的模型
我曾经犯过一个低级错误——没保存最佳权重,早停后直接用了最后一轮的模型。结果验证集损失虽然没涨,但模型已经开始过拟合了。从那以后,我每次都在代码里加 restore_best_weights=True,成了肌肉记忆。
2.5 实战组合拳:怎么选、怎么搭
在实际项目中,我不会只用一种方法。下面是我常用的组合策略:
- 先加 L2 正则化,λ 从 1e-4 开始试
- 再加 Dropout,全连接层用 0.5,卷积层用 0.8
- 开启早停,patience 设 10,监控验证集损失
- 如果模型太大,考虑用 L1 做特征选择,压缩模型体积
边缘端部署特别提醒:
- L1 正则化能产生稀疏权重,配合剪枝可以进一步压缩模型
- Dropout 在推理时一定要关闭(大部分框架自动处理)
- 早停法在边缘端训练时尤其重要——设备资源有限,不能无限跑