ChatGPT解决这个技术问题 Extra ChatGPT

如何在pytorch中进行渐变剪裁?

在 pytorch 中执行渐变裁剪的正确方法是什么?

我有一个爆炸梯度问题。

@pierrom 谢谢。我自己找到了那个线程。认为在这里提问可以节省所有追随我和谷歌的人快速回答阅读所有讨论的麻烦(我自己还没有完成),并且只是得到一个快速的答案,stackoverflow风格。去论坛找答案让我想起了1990年。如果没有其他人在我之前发布答案,那么我一旦找到它。

M
Mateen Ulhaq

here 中的更完整示例:

optimizer.zero_grad()        
loss, hidden = model(data, hidden, targets)
loss.backward()

torch.nn.utils.clip_grad_norm_(model.parameters(), args.clip)
optimizer.step()

为什么这个更完整?我看到更多的选票,但不明白为什么这更好。你能解释一下吗?
这只是遵循一种流行的模式,可以在 loss.backward() 和 optimizer.step() 之间插入 torch.nn.utils.clip_grad_norm_(model.parameters(), args.clip)
什么是 args.clip?
是否在前向传递之前调用 opt.zero_grad() 是否重要?我的猜测是,它越早归零,也许 MEM 释放越早发生?
@FarhangAmaji 来自 args(可能来自 argparse 模块)的 max_norm(裁剪阈值)值
I
Ivan

clip_grad_norm(实际上不推荐使用 clip_grad_norm_,在执行就地修改时遵循尾随 _ 的更一致的语法)通过连接所有传递给函数的参数,从the documentation可以看出:

范数是在所有梯度上一起计算的,就好像它们被连接成一个向量一样。渐变被原地修改。

从您的示例看来,您希望 clip_grad_value_ 具有类似的语法,并且还可以就地修改渐变:

clip_grad_value_(model.parameters(), clip_value)

另一种选择是注册一个 backward hook。这将当前梯度作为输入,并可能返回一个张量,该张量将用于代替先前的梯度,即修改它。每次计算梯度后都会调用这个钩子,即一旦钩子被注册就不需要手动裁剪:

for p in model.parameters():
    p.register_hook(lambda grad: torch.clamp(grad, -clip_value, clip_value))

值得一提的是,这两种方法并不等同。后一种注册钩子的方法绝对是大多数人想要的。这两种方法之间的区别在于,后一种方法在反向传播期间剪裁梯度,而第一种方法在整个反向传播发生后剪裁梯度。
N
Nikita

通读 the forum discussion 给出了这个:

clipping_value = 1 # arbitrary value of your choosing
torch.nn.utils.clip_grad_norm(model.parameters(), clipping_value)

我确信它比仅此代码片段更深入。


h
hkchengrex

如果您使用的是自动混合精度 (AMP),则需要在裁剪之前做更多工作:

optimizer.zero_grad()
loss, hidden = model(data, hidden, targets)
scaler.scale(loss).backward()

# Unscales the gradients of optimizer's assigned params in-place
scaler.unscale_(optimizer)

# Since the gradients of optimizer's assigned params are unscaled, clips as usual:
torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm)

# optimizer's gradients are already unscaled, so scaler.step does not unscale them,
# although it still skips optimizer.step() if the gradients contain infs or NaNs.
scaler.step(optimizer)

# Updates the scale for next iteration.
scaler.update()

参考:https://pytorch.org/docs/stable/notes/amp_examples.html#gradient-clipping


C
Charles Xu

好吧,我遇到了同样的错误。我尝试使用剪辑规范,但它不起作用。

我不想更改网络或添加正则化器。所以我将优化器更改为 Adam,它可以工作。

然后我使用 Adam 的预训练模型开始训练并使用 SGD + 动量进行微调。它现在正在工作。


“不起作用”是什么意思?
仍然给出一个'nan'