Regularization in Machine Learning
在机器学习训练过程中,如果梯度/权重变得过大,甚至变成 nan, 或者 loss 过大、异常跳跃,甚至变成 nan, 便出现了所谓的梯度爆炸问题. 梯度爆炸问题是指在训练过程中梯度范数的大幅度增加,从而导致网络无法从训练数据中学习,梯度下降无法执行,学不到最优解.
梯度爆炸的识别:
准确度上不去,
- 输出 loss 值, 看 loss 是否过大、有异常跳跃情况
- 输出梯度的范数, 看是否呈指数增长
- 调试看权重/梯度的大小
Solution to exploding gradients
梯度爆炸的基本解决方法有 3 种:
L1 Regularization
L2 Regularization
L2 Regularization 相当于对网络中的损失函数应用 weight decay
Gradient clipping
对于模型权重系数 求解是通过最小化目标函数实现的。正则化方法通过在 Loss 函数中加惩罚项来防止 overfitting。L1 正则化和 L2 正则化,二者分别相当于在 Loss 函数中增加权重的 L1 范数和 L2 范数。
\[ \begin{aligned} &l_1: \Omega(w)=\|w\|_1=\sum_i\left|w_i\right| \\ &l_2: \Omega(w)=\|w\|_2^2=\sum_i w_i^2 \end{aligned} \]
加了之后的权重优化公式如下:
\[ \begin{aligned} \mathbf{w}^* &=\arg \min _{\mathbf{w}} \sum_j\left(t\left(\mathbf{x}_j\right)-\sum_i w_i h_i\left(\mathbf{x}_j\right)\right)^2+\lambda \sum_{i=1}^k\left|w_i\right| \\ \mathbf{w}^* &=\arg \min _{\mathbf{w}} \sum_j\left(t\left(\mathbf{x}_j\right)-\sum_i w_i h_i\left(\mathbf{x}_j\right)\right)^2+\lambda \sum_{i=1}^k w_i^2 \end{aligned} \]
两个正则项区别:
L1 norm:对输入特征进行过滤(一部分特征保留,一部分特征移除),会把不重要的特征直接置零,产生稀疏性效果
L2 norm:
- 控制特征值的范围,使其不要过大,产生平滑效果
- L2 计算方便,而 L1 在特别是非稀疏向量上的计算效率就很低;
- L2 Norm 对大数的惩罚更大,对 outlier 更敏感,因为使用 L2 Norm 求出来的解是比较均匀的,而 L1 Norm 常常产生稀疏解。
实际应用过程中,L1 norm 几乎没有比 L2 norm 表现好的时候,优先使用 L2 norm 是比较好的选择。
Gradient clipping
而对权重/梯度进行约束裁剪,避免模型越过最优点,是解决梯度爆炸的有效方法之一.
梯度裁剪是通过在网络反向传播过程中,将梯度裁剪到一个阈值, 通过裁剪的梯度更新权重,实现对权重更新的缩放,从而降低溢出的可能。
梯度裁剪主要分为两种: Clipping-by-value, Clipping-by-norm
Gradient clipping-by-value
按照值裁剪: 定义 a minimum clip value 和 a maximum clip value, 将超出这个范围的梯度, 裁剪为对应阈值.(‖gradient‖是梯度的范数)
伪代码:
1 | gradient ← ∂C/∂W |
Gradient clipping-by-norm
clipping-by-norm 的思想类似于 clipping-by-value。不同之处在于, 我们通过将梯度的单位向量与阈值相乘来裁剪梯度。因为 g/‖g‖ 是一个单位向量, 在重新缩放之后, 使得梯度的 L2 norm 小于等于预设的 clip_norm.
伪代码:
1 | g ← ∂C/∂W |
深度学习框架
通常在 backward 得到梯度之后,step() 更新之前,使用梯度剪裁, 继而进行网络更新的过程。
tensorflow
在 tf1.0 中:
1 | # Compute gradients. |
在 tf2 中:
1 | from tensorflow.keras import optimizers |
clipnorm
: float or None. If set, clips gradients to a maximum norm.clipvalue
: float or None. If set, clips gradients to a maximum value.
PyTorch
因为 PyTorch 是将梯度存储在参数中,所以这里直接将模型参数传到裁剪函数中.
1 | loss.backward() |
weight constraint
更加直接地,还可以约束权重的大小.
torch.clamp 可实现 tensor 一定范围约束
ealy stopping 缓解过拟合
early stopping,每个神经元激活函数在不同数值区间的性能是不同的,值较小时为线性区,适当增大后为非线性区,过度增大则为饱合区(梯度消失)。初始化时,神经元一般工作在线性区(拟合能力有限),训练时间增大时,部分值会增大进入非线性区(拟合能力提高),但是训练时间过大时,就会进入饱合区,神经元就“死掉”。所以应该在适当时间内就 stopping 训练。
推荐文章
A Gentle Introduction to Weight Constraints in Deep Learning