梯度裁剪以避免梯度爆炸

梯度裁剪

梯度用数学式子表达即为偏导数,梯度爆炸就等价于偏导数很大。

梯度裁剪应用的前提

网络在训练过程中出现了梯度爆炸(loss激增or直接NaN)的现象。梯度爆炸问题一般会随着网络层数的增加而变得越来越明显。

根据weight更新的计算公式: w i = w i − α ∂ N ( Θ ) ∂ w i w_i = w_i - \alpha \frac{\partial N(\Theta)}{\partial w_i} wi=wiαwiN(Θ), 其中 ∂ N ( Θ ) ∂ w i \frac{\partial N(\Theta)}{\partial w_i} wiN(Θ)为复合函数求导。若每一层的偏导数都大于1,且层数又多,则容易发生梯度爆炸现象。

解决梯度爆炸问题的办法

  • 将学习率 α \alpha α设置得小一点。但是如果使用重启余弦退火算法,就不可避免地会在重启时学习率跳到很大,学习率调到太小也不利于继续寻找最优解。而
  • 使用梯度裁剪,即控制 ∂ N ( Θ ) ∂ w i \frac{\partial N(\Theta)}{\partial w_i} wiN(Θ)不能超过某一阈值。

PyTorch使用梯度裁剪

梯度裁剪的使用位置在loss.backward()得到梯度值之后,在使用optimizer.step()进行权重值更新之前。

固定阈值裁剪(对象:for gradient of each parameter)

约束每个权重参数 w i w_i wi的梯度值 ∂ N ( Θ ) ∂ w i ∈ [ − x , x ] \frac{\partial N(\Theta)}{\partial w_i}\in [-x, x] wiN(Θ)[x,x].

例子:

import torch.nn as nn
outputs = model(data)
loss= loss_fn(outputs, target)
optimizer.zero_grad()
loss.backward()
nn.utils.clip_grad_value_(model.parameters(), clip_value=x)
optimizer.step()

优点:简单粗暴;

缺点:因为是element-wise的操作,很难找到合适的阈值。

根据参数的范数来衡量(对象:for all gradients)

对所有权重参数 { w i } i = 1 K \{w_i\}_{i=1}^{K} {wi}i=1K的总体范数进行约束。所有权重参数的总体范数为 ∥ g ∥ n = [ ( ∂ N ( Θ ) ∂ w 1 ) n + ⋯ + ( ∂ N ( Θ ) ∂ w K ) n ] 1 n \Vert \mathbf{g}\Vert_n = {[(\frac{\partial N(\Theta)}{\partial w_1})^n + \cdots + (\frac{\partial N(\Theta)}{\partial w_K})^n]}^{\frac{1}{n}} gn=[(w1N(Θ))n++(wKN(Θ))n]n1,设置阈值 c c c

∥ g ∥ n ≤ c \Vert \mathbf{g}\Vert_n\leq c gnc时,不做clip的操作。

∥ g ∥ n > c \Vert \mathbf{g}\Vert_n > c gn>c,执行操作 g : = c ∥ g ∥ n ⋅ g \mathbf{g} := \frac{c}{\Vert \mathbf{g}\Vert_n} \cdot \mathbf{g} g:=gncg,最终 ∥ g ∥ n = c \Vert \mathbf{g}\Vert_n = c gn=c

所以,通过对范数进行约束,以达到 ∥ g ∥ n ≤ c \Vert \mathbf{g}\Vert_n\leq c gnc的目的,这种对于梯度值整体做缩放的操作,比较容易调参。

例子:

import torch.nn as nn
outputs = model(data)
loss= loss_fn(outputs, target)
optimizer.zero_grad()
loss.backward()
nn.utils.clip_grad_norm_(model.parameters(), max_norm=c, norm_type=2)
optimizer.step()

根据经验, c c c值的选取应由大到小,依次为10, 5, 1, 0.1。如果都不行的话,则需观察梯度更新时的值来进行具体设置。

关于实时梯度值的观测

此处举例观察各层梯度值的二范数(应该写在loss.backward()之后)中的最大值

max_grad_norm = 0
for param in model.parameters():
	if param.grad is not None:
		max_grad_norm = max(param.grad.data.norm(2), max_grad_norm)
print("the max value of gradient's L2 norm is {}".format(max_grad_norm))
TensorFlow中的梯度裁剪是一种常用的技术,用于限制学习算法中的梯度值大小,以避免梯度爆炸或消失的问题。 在深度学习中,通过反向传播算法计算梯度,并使用其来更新模型参数。然而,梯度可能会变得非常大,导致参数更新过大,这称为梯度爆炸。另一方面,梯度也可能变得非常小,导致参数更新缓慢,这称为梯度消失。这两个问题都会影响模型的训练和性能。 为了解决这些问题,可以使用梯度裁剪梯度裁剪通过设置一个阈值来限制梯度的大小,如果梯度的范数超过了这个阈值,就对梯度进行缩放,从而将其限制在合理的范围内。梯度裁剪可以在反向传播之前或之后应用,具体取决于具体的实现方式。 在TensorFlow中,可以使用tf.clip_by_value或tf.clip_by_norm等函数来实现梯度裁剪。tf.clip_by_value函数通过限制梯度的数值范围来进行裁剪。tf.clip_by_norm函数将梯度视为向量,并通过将其缩放到指定的范数来进行裁剪。 例如,可以使用以下代码在TensorFlow中对模型的梯度进行裁剪: ``` optimizer = tf.train.GradientDescentOptimizer(learning_rate) gradients, variables = zip(*optimizer.compute_gradients(loss)) clipped_gradients, _ = tf.clip_by_value(gradients, -threshold, threshold) train_op = optimizer.apply_gradients(zip(clipped_gradients, variables)) ``` 这里,首先使用optimizer.compute_gradients函数计算梯度和变量,然后使用tf.clip_by_value函数对梯度进行裁剪,最后使用optimizer.apply_gradients函数将裁剪后的梯度应用到变量上。 梯度裁剪是一种常用的技术,可以有效地处理梯度爆炸梯度消失问题,提高模型训练的稳定性和收敛性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值