梯度消失和梯度爆炸,常见解决方法

前向传播

虚线框为 神经元 block(相当于VGG19、ResNet50的block),该block中有 4个 神经元,每个神经元后都有激活函数(下图中未显示)

假设输入x为10,权重w1=0.1,w2=0.1,w3=0.1,w4=0.1,bias为0不参加运算,那么每个神经元对输入的操作均为相乘

首先x与w1相乘,得到1;1与w2相乘,得到0.1,以此类推,如下图:

后向传播

假设从下一层网络传回来的梯度 小于1(最右边的数字,图中是错的,应小于1),后向传播的梯度数值如下图:

神经网络的反向传播用到 链式法则,是 逐层对函数偏导相乘

梯度消失

现象:模型无法从训练数据中获得更新,损失几乎保持不变。

原因1

层数多,梯度小于1

那么这里可以看到,本来从上一层传过来的梯度 小于1 时(图中的1是错的),由于 神经网络的反向传播是 逐层对函数偏导相乘 ,若干个小数相乘,得到的梯度就会越来越小

模型的层数越深,梯度越可能变为0(即:梯度消失),导致浅层的网络权重得不到训练

这就是在Resnet出现之前,CNN网络都不超过20层的原因

https://www.malaoshi.top/show_1EF4IWSucFTE.html

原因2

使用不合适的激活函数

比如使用 sigmoid 作为激活函数,由 sigmoid 导数公式:f′(x)=s(x) * (1−s(x)) 可知,当 x 为小数时,结果更小,由于上面原因,会导致梯度消失

从下图图像可知:

  • sigmoid 的 导数 在接近 0 或 1 时几乎为 0
  • 一旦输入很大或很小,激活值就 “压死” 在饱和区
  • 梯度经过链式法则连乘后 → 越来越小 → 梯度消失

解决

  • 重新调整网络结构
  • 使用残差模块,ResNet、DenseNet
  • 使用 relu、leakrelu 替代 sigmoid激活函数
  • 使用batch normalization( 将 数据分布 拉回到 均值为0、方差为1 的标准正态分布)

梯度爆炸

现象:

  1. 模型型不稳定,更新过程中的损失出现显著变化。
  2. 训练过程中,模型损失变成 NaN。

原因1

层数多,梯度大于1
与 梯度消失类似,从上一层传过来的梯度 大于1 时,由于 神经网络的反向传播是 逐层对函数偏导相乘 ,若干个 大于1的数 相乘,得到的梯度就会越来越大

模型的层数越深,梯度越可能变为无限大,导致梯度爆炸

原因2

权值初始过大

当权值初始过大,且 梯度 大于1 时,容易梯度爆炸

解决

  • 重新调整网络结构
  • l1、l2正则化
  • 减小学习率、减小batch

解决梯度消失、爆炸的常用方法归一化

对数据进行归一化(均值 0、方差 1)

需要掌握:正态分布(Normal Distribution)和标准正态分布(均值为0,方差为1)

为什么归一化(均值 0、方差 1)能解决这些问题?

归一化的目标:
让每一层的输入都回到一个稳定的分布(均值 0、方差 1),避免激活值进入饱和区或被无限放大。

(1)避免激活值进入饱和区

例如 sigmoid:

  • 输入如果很大(比如 10、20)→ sigmoid 输出接近 1 → 导数接近 0 → 梯度消失
  • 输入如果很小(比如 -10、-20)→ sigmoid 输出接近 0 → 导数接近 0 → 梯度消失

归一化后输入集中在 0 附近,激活值不会被压到饱和区,导数较大,梯度自然稳定。


(2)避免方差不断放大或缩小

如果不做归一化:

  • 某些层的输出方差可能变得很大 → 下一层输入很大 → ReLU 输出更大 → 梯度爆炸
  • 或方差变得很小 → 激活值越来越小 → 梯度消失

归一化强制让输入的方差为 1:

每一层的输入尺度都被拉回同一个范围,不会出现“越往后越爆炸/越消失”的情况。


(3)加速训练(让优化更平滑)

当输入分布稳定时:

  • 损失函数的曲面变得更“圆”
  • 梯度方向更稳定
  • 可以使用更大的学习率
  • 训练更快收敛

这也是为什么 Batch Normalization 能让训练速度提升很多。


(4)减少对初始化的依赖

如果不做归一化,初始化稍微不好就可能导致:

  • 网络一开始就进入饱和区
  • 或者梯度爆炸导致训练直接崩溃

归一化让输入分布稳定,因此:

初始化不再那么敏感,训练更鲁棒。


为什么必须是“均值为 0、方差为 1”?

这是最关键的问题。

均值为 0 的原因

  • 让激活函数的输入落在对称区域(尤其是 tanh、sigmoid)
  • 避免所有值都偏向一侧导致梯度偏向一侧
  • 让梯度更新更均匀

方差为 1 的原因

  • 把输入尺度拉回“合理范围”,避免过大或过小
  • 避免激活函数输入过大导致饱和
  • 让每一层的输出尺度一致,不会出现“越往后越大/越小”的情况

换句话说:

均值 0 保证对称性,方差 1 保证尺度合适。


感谢:
https://zhuanlan.zhihu.com/p/28124810
https://blog.csdn.net/vivian_ll/article/details/100919715


原文出处:/show_1EF5UETXH6tN.html