【神经网络】梯度消失与梯度爆炸问题

梯度消失与梯度爆炸问题

Glorot 和 He 初始化

  • 我们需要信号在两个方向上正确流动:进行预测时,信号为正向;在反向传播梯度时,信号为反向。我们需要每层输出的方差等于输入的方差,并且在反方向流过某层之前和之后的梯度具有相同的方差。Glorot初始化(使用逻辑激活函数时)按照下列公式随机初始化每层的连接权重,其中 f a n a v g = ( f a n i n + f a n o u t ) / 2 fan_{avg}=(fan_{in}+fan_{out})/2 fanavg=(fanin+fanout)/2
    在这里插入图片描述
    使用Glorot初始化可以大大加快训练速度。
    在这里插入图片描述
  • 默认情况下,Keras使用具有均匀分布的Glorot初始化。创建层时,可以通过设置 kernel_initializer="he_uniform"或kernel_initializer="he_normal"来将其更改为He初始化。
keras.layers.Dense(10, activation="relu", kernel_initializer="he_normal")
  • 如果要使用均匀分布,但基于fanavg而不是fanin进行He初始化,则可以使用Variance Scaling:
he_avg_init = keras.initializers.VarianceScaling(scale=2., mode='fan_avg', distribution='uniform')
keras.layers.Dense(10, activation="sigmoid", kenel_initializer=he_avg_init)

非饱和激活函数

  • ReLU函数的变体,例如leaky ReLU。该函数定义为 L e a k y R e L U α = m a x ( α z , z ) LeakyReLU_{\alpha}=max(\alpha_{z},z) LeakyReLUα=max(αz,z).
    在这里插入图片描述
    超参数 α \alpha α 定义函数“泄漏”的程度,通常设置为0.01,实际上,设置 α = 0.2 \alpha = 0.2 α=0.2(大泄漏)似乎比 α = 0.01 \alpha = 0.01 α=0.01(小泄漏)会产生更好的性能。
  • 2015年提出了一种新的激活函数,称为指数线性单元(Exponential Linear Unit,ELU)。
    在这里插入图片描述
    超参数 α \alpha α定义一个值,该值为当 z z z为较大负数时ELU函数逼近的值。通常将其设置为1.
    对于 z < 0 z<0 z<0,它具有非零梯度,从而避免了神经元死亡的问题。
  • 产生自归一化的条件:
    • 输入特征必须是标准化的(平均值为0,标准差为1);
    • 每个隐藏层的权重必须使用LeCun正态初始化。在Keras中,这意味着设置 kernel_initializer=“lecun_normal”。
    • 网络的架构必须是顺序的。
  • 使用激活函数,通常 SELU>ELU>Leaky ReLU(及其变体)>ReLU>tanh>logistic。
  • 使用leaky ReLU 激活函数,创建一个 LeakyReLU 层,并将其添加到想要应用它的层之后的模型中:
model = keras.models.Sequential([
    [...]
    keras.layers.Dense(10, kernel_initializer="he_normal"),
    keras.layers.LeakyReLU(alpha=0.2),
    [...]
])
  • 对于SELU激活,在创建层时设置activation="selu"和 kernel_initializer=“lecun_normal”:
layer = keras.layers.Dense(10, activation="selu",
                          kernel_initializer="lecun_normal")

批量归一化

  • 2015年提出一种称为批量归一化(BN)的技术来解决梯度消失/梯度爆炸问题。该操作可以使模型学习各层输入的最佳缩放和均值。
  • 为了使输入零中心并归一化,该算法需要估计每个输入的均值和标准差。
  • 批量归一化算法如下:
    1. η B = 1 m B ∑ i = 1 m B x ( i ) 2. δ 2 = 1 m B ∑ i = 1 m B ( x ( i ) − η B ) 2 3. x ^ ( i ) = x ( i ) − η B δ 2 + ε 4. z ( i ) = γ ⊗ x ^ ( i ) + β \begin{array}{ll} 1.&\eta _{B} = \frac{1}{m_B} {\sum_{i = 1}^{m_B} x^{(i)} }\\ 2.&\delta ^2 = \frac{1}{m_B} {\sum_{i = 1}^{m_B} (x^{(i)}-\eta_{B})^2 }\\ 3.&\hat{x}^{(i)} = \frac{x^{(i)}-\eta_{B}}{\sqrt{\delta ^2+\varepsilon } }\\ 4.&z^{(i)}=\gamma \otimes \hat{x}^{(i)}+\beta \end{array} 1.2.3.4.ηB=mB1i=1mBx(i)δ2=mB1i=1mB(x(i)ηB)2x^(i)=δ2+ε x(i)ηBz(i)=γx^(i)+β
    m B m_B mB 是小批量中的实例数量, γ \gamma γ 是该层的输出缩放向量, ⊗ \otimes 表示逐元素乘法(每个输入乘以其相应的输出缩放参数), β \beta β 是层的输出移动(偏移)参数向量, z ( i ) z^{(i)} z(i) 是BN操作的输出,它是输入的缩放和偏移版本。
  • 在训练期间,BN会归一化其输入,那在测试期间呢?我们需要对单个实例,而不是成批次的实例做出预测,此时计算统计量是不可靠的。
  • 大多数批量归一化的实现都是通过使用该层输入的 均值和标准差 的移动平均值 来估计训练期间的最终统计信息。这是Keras在使用BatchNormalization层时自动执行的操作。
  • 综上所述,在每个批归一化层学习了四个参数向量:
    • 通过常规反向传播学习 γ \gamma γ(输出缩放向量)和 β \beta β(输出偏移向量)
    • 使用指数移动平均值估计的 η \eta η(最终的输入均值向量)和 δ \delta δ(最终输入标准差向量)。

用Keras实现批量归一化

  • 实现批量归一化,只需在每个隐藏层的激活函数之前或之后添加一个BatchNormalization层,然后可选地在模型的第一层后添加一个BN层。
import tensorflow as tf
from tensorflow import keras

model = keras.models.Sequential([
    keras.layers.Flatten(input_shape=[28, 28]),
    keras.layers.BatchNormalization(),
    keras.layers.Dense(300, activation="elu", kernel_initializer="he_normal"),
    keras.layers.BatchNormalization(),
    keras.layers.Dense(100, activation="elu", kernel_initializer="he_normal"),
    keras.layers.BatchNormalization(),
    keras.layers.Dense(10, activation="softmax")
])

在这里插入图片描述

  • 看一下第一个BN层的参数。两个是可训练的(通过反向传播),两个不是:
    在这里插入图片描述
  • 在Keras中创建BN层时,还会创建两个操作,在训练期间的每次迭代中,Keras都会调用这两个操作。这些操作会更新移动平均值。
  • BN论文的作者主张在激活函数之前添加BN层,要在激活函数之前添加BN层,必须从隐藏层中删除激活函数,并将其作为单独的层添加到BN层之后。此外,由于批量归一化层的每个输入都包含一个偏移参数,因此可以从上一层中删除偏置项(创建时只需传递use_bias=False即可):
model = keras.models.Sequential([
    keras.layers.Flatten(input_shape=[28, 28]),
    keras.layers.BatchNormalization(),
    keras.layers.Dense(300, kernel_initializer="he_normal", use_bias=False),
    keras.layers.BatchNormalization(),
    keras.layers.Activation("elu"),
    keras.layers.Dense(100, kernel_initializer="he_normal", use_bias=False),
    keras.layers.BatchNormalization(),
    keras.layers.Activation("elu"),
    keras.layers.Dense(10, activation="softmax")
])
  • BatchNormalization类具有许多可以调整的超参数,偶尔可能需要调整momentum。BatchNormalization层在更新指数移动平均值时使用此超参数。给定一个新值(即在当前批次中计算的输入均值或标准差的新向量),该层使用以下公式来更新运行时平均 v ^ \hat{v} v^
\hat{v}\gets \hat{v}\times momentum+v\times(1-momentum)

一个良好的动量值通常接近于1,例如0.9、0.99。

  • 另一个重要的超参数是 axis:它确定哪个轴该被归一化。默认为-1,对最后一个轴进行归一化。

梯度裁剪

  • 缓解梯度爆炸问题的另一个流行技术是:在反向传播期间裁剪梯度,使它们永远不会超过某个阈值,称为梯度裁剪。这种技术最常用于循环神经网络,因为在RNN难以使用批量归一化。
  • 在Keras中实现梯度裁剪,即是在创建优化器时设置 clipvalue 或 clipnorm 参数的问题:
optimizer = keras.optimizers.SGD(clipvalue=1.0)
model.compile(loss="mse", optimizer=optimizer)
  • 如果要确保“梯度裁剪”不更改梯度向量的方向,应该设置clipnorm按照范数来裁剪。
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值