RNN梯度裁剪

RNN梯度裁剪

  最近抗疫在家蹲监狱,恰巧参加DataWhale组织的组队学习,这次的内容是深度学习,看到RNN的讲解这里,有一个“梯度裁剪”的方法不是很理解,故百度一波,整理一下,首先放上讲解中的定义(也是我百度出来的第一篇博客里的内容):
  循环神经网络中较容易出现梯度衰减或梯度爆炸,这会导致网络几乎无法训练。裁剪梯度(clip gradient)是一种应对梯度爆炸的方法。假设我们把所有模型参数的梯度拼接成一个向量 𝑔 ,并设裁剪的阈值是 𝜃 。裁剪后的梯度的 𝐿2 范数不超过 𝜃 。

             m i n ( θ ∣ ∣ g ∣ ∣ , 1 ) g min(\frac{\theta}{||g||},1)g min(gθ,1)g        (1)

  第一次看过这个定义后,我知道这方法无非就是设置一个阈值,当梯度过大时,反向传播(BP)就按设定的阈值来,至于上面的公式,看不懂。那么就从头道来,这里参考的是知乎大神,原文链接:深度炼丹之梯度裁剪

梯度爆炸问题的出现

  考虑最简单的形式,假设一个单层感知机:
Y = w X + b Y =wX+b Y=wX+b

其中Y是输出,X是输入,w是需要训练的参数,为进一步简化,设b=0,这样单层感知机变为:
Y = w X Y =wX Y=wX
  那么这个单层感知机的梯度grad是:
g r a d = d Y d w = X grad=\frac{dY}{dw}=X grad=dwdY=X
  现在带值进去看,假设w初始值 w 0 w_0 w0为1,X=120,学习率lr=0.1,现在进行BP过程:
w = w 0 − l r ∗ g r a d w=w_0-lr*grad w=w0lrgrad
w = 1 − 0.1 ∗ 120 = − 11 w=1-0.1*120=-11 w=10.1120=11
  这只是一次BP,梯度就这么大,在RNN里,有多少个时间序列,grad就会累乘多少个,当时间序列很长,每个grad都是远大于1的数值时,当BP过程传到前面时就会造成梯度爆炸。

pytorch中的梯度裁剪方法

  pytorch中使用clip_grad_value_()函数对grad进行限制,源码:

def clip_grad_value_(parameters, clip_value):
    r"""Clips gradient of an iterable of parameters at specified value.
    Gradients are modified in-place.

    Arguments:
        parameters (Iterable[Tensor] or Tensor): an iterable of Tensors or a
            single Tensor that will have gradients normalized
        clip_value (float or int): maximum allowed value of the gradients.
            The gradients are clipped in the range
    """

    if isinstance(parameters, torch.Tensor):
        parameters = [parameters]
    clip_value = float(clip_value)
    for p in filter(lambda p: p.grad is not None, parameters):
        p.grad.data.clamp_(min=-clip_value, max=clip_value)

源码中clip_value就是自己设置的阈值,从最后一行来看,这个函数的作用就是把grad限制在 ± \pm ±|clip_value|之内,貌似与公式(1)没什么关系 - -!

回看公式(1)

  通过上述理解,我再次看到公式(1)时: m i n ( θ ∣ ∣ g ∣ ∣ , 1 ) g min(\frac{\theta}{||g||},1)g min(gθ,1)g现固定一个你自己设置的阈值 θ \theta θ(为了方便说明,就设 θ \theta θ=2),由于式中g为梯度grad,当g很大时,会造成梯度爆炸问题,假设此时那那 ∣ ∣ g ∣ ∣ = 4 ||g|| =4 g=4 θ ∣ ∣ g ∣ ∣ = 0.5 < 1 \frac{\theta}{||g||}=0.5<1 gθ=0.5<1,那么 m i n ( θ ∣ ∣ g ∣ ∣ , 1 ) g = θ ∣ ∣ g ∣ ∣ g = 0.5 g min(\frac{\theta}{||g||},1)g=\frac{\theta}{||g||}g=0.5g min(gθ,1)g=gθg=0.5g
当g不是很大时,假设此时 ∣ ∣ g ∣ ∣ = 1 ||g||=1 g=1 θ ∣ ∣ g ∣ ∣ = 2 > 1 \frac{\theta}{||g||}=2>1 gθ=2>1,那么 m i n ( θ ∣ ∣ g ∣ ∣ , 1 ) g = 1 ∗ g = g min(\frac{\theta}{||g||},1)g=1*g=g min(gθ,1)g=1g=g
  通过对公式(1)的分析可以看出,梯度裁剪就是当你梯度太大时强行把梯度缩小(通过乘以一个小于1的数,即 θ ∣ ∣ g ∣ ∣ \frac{\theta}{||g||} gθ),当你梯度不是很大时,保持原样。

  至于解释中 “裁剪后的梯度的L2范数不超过 θ \theta θ ”我还不是很理解,以及为什么选择 θ ∣ ∣ g ∣ ∣ \frac{\theta}{||g||} gθ当裁剪系数,希望有大佬可以告诉我,前面内容表述如有理解不当烦请轻喷,谢谢。

### 解决RNN中的梯度爆炸问题的方法 梯度爆炸问题是循环神经网络RNN)训练过程中常见的挑战之一,其主要原因是反向传播通过时间(BPTT)时,梯度在时间步上累积并逐渐增大。如果不加以控制,这会导致权重更新过大,从而破坏模型的训练稳定性[^4]。 以下是解决RNN梯度爆炸问题的几种有效方法: 1. **梯度裁剪(Gradient Clipping)** 梯度裁剪是一种直接限制梯度大小的技术,通常通过设置一个阈值来实现。当计算出的梯度范数超过该阈值时,将其按比例缩小到阈值范围内。这种方法可以防止梯度过大对模型参数造成剧烈影响,从而稳定训练过程[^2]。 ```python import torch.nn as nn import torch.optim as optim model = nn.RNN(input_size, hidden_size, num_layers) optimizer = optim.Adam(model.parameters(), lr=0.001) # 在每个训练步骤中应用梯度裁剪 for batch in data_loader: outputs, _ = model(batch) loss = criterion(outputs, targets) loss.backward() nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0) # 裁剪梯度 optimizer.step() ``` 2. **权重初始化优化** 初始化权重不当可能导致梯度在反向传播过程中迅速增大或减小。通过采用合适的权重初始化方法(如Xavier初始化或He初始化),可以有效缓解这一问题。这些方法确保了网络初始状态下的信号传播更加平稳,从而减少梯度爆炸的可能性[^3]。 3. **使用门控机制** 长短期记忆网络(LSTM)和门控循环单元(GRU)是两种引入了门控机制的RNN变体。这些模型通过设计特定的结构(如遗忘门、输入门和输出门),能够更好地控制信息流,避免梯度在长时间步上传播时发生爆炸或消失[^4]。 4. **调整学习率** 学习率过高可能会导致权重更新过于剧烈,从而引发梯度爆炸。适当降低学习率或使用自适应优化算法(如Adam、RMSprop等),可以在一定程度上缓解这一问题[^5]。 5. **批量归一化(Batch Normalization)** 批量归一化通过规范化每一层的输入,使得网络的激活值分布更加稳定,从而有助于缓解梯度爆炸问题。此外,它还可以加速训练过程并提高模型的泛化能力[^3]。 通过结合以上方法,可以显著改善RNN模型在训练过程中遇到的梯度爆炸问题,提升模型的稳定性和性能。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值