正则化
从过拟合的本质出发,系统引入参数正则化与结构正则化机制,并澄清其与归一化方法的根本区别
为什么需要正则化
-
什么是正则化
-
正则化就是给模型加约束,防止它过度拟合训练数据,提升泛化能力
-
过拟合:模型把训练集里的噪声、细节都记住了,换新数据就表现很差
-
正则化:惩罚过于复杂的模型,让模型学得更简单、更平滑、更通用
-
-
直观上理解
-
在原始的无约束优化问题中,给定训练数据,只要求训练损失越小越好
-
只要在训练集上预测对,不管用了什么方式,不管参数长什么样,只要损失一样小,在优化器眼里这些解完全等价
-
但在高维模型(尤其是神经网络)中,“训练损失很小” 的解不是一个点,而是一大片区域,里面可能有:
-
参数很小、变化平滑的解
-
参数巨大、极度敏感的解
-
稍微扰动输入就崩掉的解
-
非常稳定、抗噪声的解
-
-
以上这些解在训练集上,损失几乎一样
-
-
从数学角度理解
-
监督学习的目标并非最小化训练误差,而是最小化泛化误差:
-
但真实分布 不可得,只能通过有限样本 近似:
-
-
过拟合并不是指模型复杂,其本质而是:
- 参数空间中存在大量对训练集低损失、但对分布扰动极不稳定的解
- 梯度下降在高维空间中,极易滑向这些 “尖锐极小值”
-
正则化的核心目标
- 正则化不是直接提升训练精度,而是改变优化问题的几何结构,打破损失相近的不同解之间 “等价” 的假设,使 “稳定的解” 更容易被找到
- 正则化不是在约束模型,而是在约束 “哪种解值得被相信”
-
那么正则化约束如何进入优化问题
-
原始无约束优化问题:
-
正则化后的等价形式:
- :数据一致性 ——“在训练集上做得怎么样?”
- :复杂度度量 ——“这个解,看起来靠谱吗?”
- :在 “拟合” 与 “稳定” 之间的权衡系数 ——“到底有多在乎‘靠谱吗’这件事?”
-
-
通过修改优化目标,正则化直接从目标函数层面作用于可行解空间 / 参数空间,从而限制 “不稳定的使用方式”
-
在不同的解中,参数小往往意味着解更好,因为参数越大 → 输出对输入越敏感 → 一点点噪声就会放大
L2 正则化
-
L2 正则化(Ridge)的定义:
-
带正则项的目标函数:
-
对参数求梯度:
-
梯度下降更新:
-
由梯度下降公式可以看出,每次更新前,权重先乘一个小于 1 的数 ,这代表着权重被持续拉向 0】
-
而权重越小,对输入的变化越不敏感,输出变化平滑、稳定,因此 L2 就是强制让所有权重都不能太大,让模型变 “温和”
-
机制理解:
- 通过 L2 正则,参数在每一步都会被整体向 0 拉回,并优先抑制那些仅靠放大参数幅值来降低损失的方向
- 相当于持续地、温和地告诉模型:不要靠 “把某个参数拧得特别大” 来拟合数据
- 从几何视角看,L2 正则倾向于选择损失盆地中更平坦、曲率更小的解,通过连续收缩,引导优化走向平坦解
-
L2 正则化让权重整体变小、更平滑,不会极端大,最常用
L1 正则化
-
L1 正则化(Lasso)的定义:
-
正则化目标:
-
梯度(严格来说是次梯度):
-
机制理解:
- 相比于 L2 正则限制参数的大小,L1 正则不是简单地缩小参数,而是主动制造精确为 0 的维度
- 由于在 0 附近存在 “不可导拐角”,那么参数一旦足够小,就会被直接压到 0
- 通过制造几何拐角,制造结构稀疏性,而模型自动完成 “特征选择”,仅保留少数关键方向
-
让很多权重直接变成 0,实现特征选择
Dropout
-
Dropout 从参数正则化转向到结构正则化,Dropout 并不是在目标函数中显式加项,而是在训练阶段随机破坏网络结构
-
训练时:
- 其中,
- 每一次前向传播,都是一个 “子网络”
-
测试时:
-
机制理解:
-
Dropout 从参数的正则化转向结构的正则化,不允许模型依赖任何一条固定通路
-
每次训练随机砍掉一部分神经元,使网络结构不断变化
-
保证只有在 “很多不同子网络中都好用的表示”,才能存活下来
-
通过结构随机性,迫使表示具有鲁棒性
-
Dropout ≈ 对指数数量子模型的参数共享平均,是一种隐式的模型集成
-
Regularization ≠ Normalization
-
正则化(Regularization):
- 作用对象:解空间 / 参数空间
- 改变的是:什么样的解被偏好
- 目标:抑制过拟合、提升泛化
-
归一化(Normalization,如 BatchNorm / LayerNorm):
- 作用对象:中间激活分布
- 改变的是:优化路径与梯度尺度
- 目标:稳定训练、加速收敛
-
虽然:Normalization 往往带来 “正则化效应”,但本质上它们解决的是完全不同层级的问题
| 维度 | 归一化 | 正则化 |
|---|---|---|
| 作用层级 | 中间表示 | 解空间 |
| 是否进入目标函数 | 否 | 是 |
| 主要目标 | 稳定优化 | 抑制过拟合 |
| 是否改变最优解集合 | 通常不 | 明确改变 |
梯度裁剪
-
虽然像 AdamW 这样的优化器和合理的学习率调度策略能改善 Transformer 的训练稳定性,但由于模型层数深、计算路径长,仍可能出现另一个数值问题 —— 梯度爆炸
-
为什么会出现梯度爆炸
-
在反向传播过程中,梯度是逐层传递的
-
若每层梯度的范数略大于 1,这种 “链式乘积” 可能随着层数增加呈指数增长,导致梯度幅度变得非常大,从而引发:
-
参数更新剧烈跳动
-
数值溢出甚至 NaN
-
训练损失震荡或发散
-
-
虽然像层归一化等结构能缓解一部分梯度问题,但在训练初期或遇到异常输入时仍可能发生大梯度爆发
-
-
梯度裁剪(Gradient Clipping)是一种直接应对梯度爆炸的技术:
- 在优化器根据梯度更新参数之前,对梯度的大小设置上限
- 如果梯度的总范数超过阈值,则按比例缩小到该阈值,从而限制单次更新步长
-
按全局范数裁剪(Transformer 训练中最常见的梯度裁剪方式)
-
设所有可训练参数的梯度组成一个向量 , 是其 L2 范数(即欧几里得范数)
-
其中,L2 范数定义为:
-
给定一个阈值
max_norm:-
若 :梯度不变
-
若 :则整体缩放为:
-
-
这种方式保持梯度方向不变,只限制其整体幅度,有助于避免单次更新过大,同时保留优化方向不偏离
-
-
在大多数深度学习框架中,梯度裁剪已被内置支持,并应在梯度计算之后、参数更新之前调用。典型的 PyTorch 调用方式如下:
# 正常前向 + 计算损失 outputs = model(inputs) loss = compute_loss(outputs, targets) # 反向传播计算梯度 loss.backward() # 在 optimizer.step() 之前进行梯度裁剪 gradient_threshold = 1.0 torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=gradient_threshold) # 应用裁剪后的梯度更新参数 optimizer.step() # 清零梯度以供下次迭代 optimizer.zero_grad() -
选择
max_norm阈值的经验-
常见范围:约在
-
常用起点:
-
过低:可能过度限制梯度,从而减慢学习甚至阻碍模型收敛
-
过高:会无法有效抑制异常大梯度
-