📑 本页目录(点开跳转)
10 / 先看直觉,再看细节
先确认是过拟合,
再决定加哪种约束。
正则化是限制模型记住训练数据的方式,不是加得越多越好。
先抓住一个具体结果
训练更差,不代表验证更好
加大 Dropout 后,训练和验证都变差,可能是约束过强。每次只改一项,比较固定验证集上的结果;别用测试集选择强度。
接下来,按需要选一项
你现在想看什么?
这些入口是选择,不是必须按顺序完成的任务。
可以停在这里
正则化是限制模型记住训练数据的方式,不是加得越多越好。
需要更多细节时,继续看完整正文 →原有正文、图解和例子都在下方。按需跳转,不必一次读完。
10 · 正则化全家桶
⏱ 36 分钟 | ⭐ 核心
🎯 一句话
正则化 = 一切「故意让模型别学得太顺」的手段。 反直觉但成立:给模型制造困难,它反而学得更好。
🤔 先想明白:为什么"制造困难"会有用
关键信息
正则化就是在堵这些捷径。
三种堵法,对应后面七件武器的三个族:
| 堵法 | 做什么 | 武器 |
|---|---|---|
| 让捷径不可靠 | 每次训练时随机破坏一部分信息 | Dropout、数据增强 |
| 让捷径变贵 | 给"复杂的解"加罚款 | 权重衰减、L1 |
| 不给记忆的时间 | 在模型开始死记硬背之前停下 | 早停 |
🔑 数学原理第 2 章会告诉你: 权重衰减的真身是「给参数加一个均值为 0 的高斯先验」—— 也就是"在没有强证据时,我倾向于相信参数应该接近 0"。 正则化不是 hack,是把你的先验信念写进目标函数。
🧰 七件武器(按性价比排序)
① 加数据 —— 永远的第一名
第 5 章说过:唯一能同时降方差又不增偏差的手段。 有数据就别折腾别的。
对照
其他所有正则化:降方差,但【会抬高偏差】(模型被限制了)
加数据: 降方差,偏差【不变】 ⭐ 白赚
② 数据增强 —— 免费造数据
对照
图像:翻转、裁剪、旋转、颜色抖动、Mixup、CutMix
文本:同义词替换、回译、随机删词
音频:变速、变调、加背景噪声
通用:给输入加轻微噪声
💡 本质:告诉模型「这些变化下答案不变」——把先验知识注入进去。
⚠️ 增强必须"语义保持",这是最容易翻车的地方:
❌ 错误的增强 为什么错 手写数字做水平翻转 6 会变成镜像,2 会变成不认识的字符 医学影像做左右翻转 "病灶在左肺"是关键信息,翻转后标签就错了 文本做随机打乱词序 "狗咬人"和"人咬狗"不是一回事 时间序列做随机重排 直接破坏了因果顺序 判断标准一句话:增强后,一个人类专家会不会改变他的答案?会 → 别用。
🔗 Kaggle 第 10 章和第 8 章有大量实战增强策略。
③ 早停 Early Stopping —— 零成本,必用
# 🧩 骨架:`train_one_epoch` 来自你自己的代码,这一段只看写法
import torch
best, patience, wait = float('inf'), 10, 0
for epoch in range(200):
train_one_epoch()
val = evaluate()
if val < best:
best, wait = val, 0
torch.save(model.state_dict(), "best.pt") # ⭐ 保存最佳
else:
wait += 1
if wait >= patience:
model.load_state_dict(torch.load("best.pt")) # ⭐ 回滚
break
⚠️ 两个必坑:
| 坑 | 后果 |
|---|---|
| 忘了回滚(只 break 不 load) | 你保留的是"已经开始过拟合的那个模型",白停了 ⭐ |
| patience 太小(比如 2) | 学习率调度会让 loss 暂时上升,你会在它还能变好的时候停掉 |
💡 patience 怎么定:至少要大于学习率调度的一个周期。 用
ReduceLROnPlateau时,patience 要比它的 patience 大 2~3 倍。
④ 权重衰减 L2 / AdamW
第 3 章讲过原理。深度学习里通过 weight_decay=0.01 实现。
⚠️ 两个必须知道的细节:
流程图
# 🧩 骨架:`model` 来自你自己的代码,这一段只看写法
# ⭐ 正确的分组写法(几乎所有大模型训练脚本都这么写)
import torch
decay, no_decay = [], []
for n, p in model.named_parameters():
if p.ndim <= 1 or "bias" in n: # 1 维的都是 bias/norm 参数
no_decay.append(p)
else:
decay.append(p)
opt = torch.optim.AdamW([
{"params": decay, "weight_decay": 0.01},
{"params": no_decay, "weight_decay": 0.0}, # ⭐
], lr=3e-4)
⑤ Dropout —— 训练时随机"停工"
流程图
💡 "隐式集成"有多大规模:n 个神经元 → 2ⁿ 个可能的子网络。 一个 1000 神经元的层,Dropout 相当于在集成 2¹⁰⁰⁰ 个网络的平均。 🔗 这就是数学原理第 8 章说的"集成降方差"在起作用。
经验值:
| 位置 | p |
|---|---|
| 全连接层 | 0.2 – 0.5 |
| 卷积层 | 通常不用或 ≤0.1(BN 已经起了正则作用,且卷积参数本来就少) |
| Transformer | 0.1(attention 和 FFN 后各一个) |
| 输入层 | 很少用,用也 ≤0.1 |
⚠️ 最常见的两个 bug:
流程图
💡 一个自检技巧:同样的输入跑两次,如果输出不同 → 你忘了
eval()。⭐ 上面那句「框架自动处理缩放」,缩放到底放在哪一边? 这是 Dropout 唯一的考点,而这一节故意跳过了它:丢掉 30% 之后这一层输出的期望只剩 70%, 补法有两种(训练时除 $1-p$ 的 inverted dropout,还是推理时整层乘 $1-p$),现代框架为什么全选前者。 🔗 附录 C 第 6 题把这 8 行写出来了,含实测输出(训练均值 1.0018 vs 推理 1.0000)。
⑥ BatchNorm / LayerNorm —— 主要作用是稳定训练
信息关系
信息关系
为什么 Transformer 用 LayerNorm 不用 BatchNorm:
| 原因 | 说明 |
|---|---|
| 序列长度可变 | batch 内不同样本长度不同,padding 位会污染统计量 |
| 推理时 batch=1 | BN 只能用训练时存的 running mean/var,一旦分布有偏移就崩 ⭐ |
| LN 不跨样本 | 每个样本独立归一化,batch=1 和 batch=1000 结果完全一样 ⭐ |
⚠️ BN 的三个坑:
| 坑 | 解法 |
|---|---|
| 小 batch(<16)统计不准,效果会崩 | 改用 GroupNorm 或 LayerNorm |
| BN 层之前的卷积不需要 bias | BN 会减去均值,bias 直接被抵消 → 白算(bias=False) |
| 微调时 BN 的 running stats 还在更新 | 冻结 backbone 时记得 bn.eval(),否则统计量被新数据带跑 ⭐ |
⭐ 「BN 竖着算、LN 横着算」是结论,白板上要写的是那 15 行: 🔗 附录 C 第 4 题(LayerNorm)和第 5 题(BatchNorm)。 那边补了这一节没说的两件事:方差要用有偏(分母 n),但
running_var存的是无偏(× n/(n−1))—— 这一行是手写实现和 PyTorch 对不齐的头号原因;以及else分支漏了会怎样(batch=1 时方差恒为 0,输出直接爆掉)。
⑦ 标签平滑 Label Smoothing
结果对照
💡 为什么硬标签有害:要让 softmax 输出精确的 1,那个 logit 必须趋于 +∞—— 模型会把权重越推越大来追求这个永远达不到的目标,这本身就是一种过拟合。
⚠️ 两个不该用的场景:
- 需要精确概率的任务(如风控打分、竞价)——平滑会系统性压低置信度
- 知识蒸馏的教师模型——你需要它输出真实的概率分布
🧭 怎么组合(可以照抄的配方)
| 场景 | 配方 |
|---|---|
| 表格数据 MLP | 早停 + weight_decay(0.01) + Dropout(0.2~0.3) |
| 树模型 / GBDT | max_depth + min_child_weight + subsample + colsample + 小 learning_rate + 早停 ⭐ |
| CNN 图像 | 早停 + 数据增强 ⭐ + BN + weight_decay(1e-4) |
| Transformer/NLP | 早停 + LayerNorm + Dropout(0.1) + AdamW + warmup + 标签平滑 |
| 微调预训练模型 | 小学习率 + 早停 + 冻结前几层(正则化基本靠预训练本身)⭐ |
| 数据极少 | 加数据 > 强增强 > 强正则 > 换更小的模型 / 用预训练 |
🔑 调试顺序:先确认模型能过拟合(在小数据上跑到训练 loss ≈ 0), 再加正则往回收。一上来就堆正则,你分不清是欠拟合还是模型有 bug。 —— 这条是第 11 章的核心方法论。
⚠️ 一个重要提醒:正则化不是越多越好
信息关系
每次只加一种,看验证集怎么变。
🩺 一张诊断表
| 训练 loss | 验证 loss | 诊断 | 该动哪件武器 |
|---|---|---|---|
| 低 | 高 | 过拟合 | 加正则(按性价比顺序:数据→增强→早停→衰减→Dropout) |
| 高 | 高 | 欠拟合 | 减正则 / 加大模型 / 训久一点 / 调学习率 |
| 低 | 低 | ✅ 健康 | 可以试着加大模型再来一轮 |
| 高 | 低 | ⚠️ 异常 | 多半是 bug:验证集太简单、泄漏、或 Dropout 没关(训练 loss 被 Dropout 抬高了)⭐ |
💡 最后一行值得记住:验证 loss 比训练 loss 还低时,先怀疑 Dropout 和 BN—— 训练时它们在给模型"加难度",所以训练 loss 天然偏高。轻微的这种现象是正常的。
🔗 和站内其他章的关系
| 相关的地方 | 这里的对应 |
|---|---|
| 第 3 章 L1/L2 正则 | ④ 权重衰减的线性模型版 |
第 4 章 GBDT 的 learning_rate、树深限制 |
树模型的正则化手段 |
| 第 5 章"加数据只降方差" | 为什么加数据排第一 |
| 数学原理第 2 章 | 正则化 = 先验(λ = σ²/τ²)⭐ |
| 数学原理第 9 章 | 正则化 = 缩小假设空间 = 降低样本需求 |
| Kaggle 第 2 章 | 竞赛实战版(含 FGM/R-Drop 等进阶手段) |
| 推荐算法第 5 章 MF 的 λ | L2 正则 |
| 附录 C 第 4、5、6 题 ⭐ | 这一章讲的是这三个东西在干什么;面试要的是它们长什么样——LayerNorm / BatchNorm / Dropout 各 10~20 行手写实现,三个最容易写错的点都标出来了 |
✅ 检查点
- 正则化在"堵"什么?三种堵法各对应哪些武器?
- 七件武器里性价比最高的是哪个?为什么它特殊?
- 数据增强翻车的判断标准是什么?举两个错误增强的例子。
- 早停有哪两个必踩的坑?
- 为什么 Adam + L2 ≠ AdamW?哪些参数不该加 weight decay?
- Dropout 为什么有效?测试时怎么处理?怎么自检忘没忘
eval()? - 为什么 Transformer 用 LayerNorm 而不是 BatchNorm?(说出两条)
- 为什么 BN 层前的卷积不需要 bias?
- 标签平滑在哪两个场景不该用?
- 训练 loss 高但验证 loss 低,该怀疑什么?
👀 答案
- 堵捷径(模型记住训练集特有的东西)。三种:让捷径不可靠(Dropout、增强)、让捷径变贵(权重衰减、L1)、不给记忆时间(早停)。
- 加数据——唯一同时降方差又不抬高偏差的手段;其他所有正则化都是拿偏差换方差。
- 标准:增强后一个人类专家会不会改变答案。错误例子:手写数字水平翻转(6 变镜像)、医学影像左右翻转("病灶在左肺"是关键信息)、文本打乱词序、时间序列重排。
- ①忘了回滚最佳权重(只 break 不 load,保留的是已过拟合的模型)②patience 太小,学习率调度让 loss 暂时上升时被误停。
- Adam 里 L2 加进梯度会被自适应学习率缩放,导致大梯度参数罚得少;AdamW 把衰减直接作用在权重上绕开缩放。偏置和 LayerNorm/BatchNorm 参数不加(p.ndim <= 1 的都是)。
- 逼迫网络不依赖特定神经元、学到冗余表示;且相当于隐式集成 2ⁿ 个子网络。测试时全部保留、框架自动缩放,但必须调
model.eval()。自检:同一输入跑两次,输出不同就是忘了 eval()。 - ①序列长度可变,padding 会污染 batch 统计 ②推理时 batch=1,BN 只能用 running stats,分布一偏移就崩;LN 不跨样本,batch=1 和 1000 结果完全一样。
- BN 会减去均值,bias 直接被抵消,白算。设
bias=False。 - ①需要精确概率的任务(风控、竞价)——会系统性压低置信度 ②知识蒸馏的教师模型——需要真实概率分布。
- 多半是 bug 或正常的 Dropout/BN 效应:训练时 Dropout/BN 在加难度,训练 loss 天然偏高(轻微正常)。严重时怀疑验证集太简单或数据泄漏。
🛑 可以停在这里
⚡ 走神救援
先记住这几件事
- 正则化的目标是改善泛化,不是让训练分数更漂亮。
- 权重约束、Dropout、数据增强和早停作用在不同环节,不能混成一种方法。
- 根据验证曲线逐项尝试,记录收益;训练和推理模式要正确切换。
下一节 👉 11-训练调试手册.md ⭐