【Pytorch】梯度裁剪——torch.nn.utils.clip_grad_norm_的原理及计算过程

这篇具有很好参考价值的文章主要介绍了【Pytorch】梯度裁剪——torch.nn.utils.clip_grad_norm_的原理及计算过程。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。


众所周知,梯度裁剪是为了防止梯度爆炸。在训练FCOS算法时,因为训练过程出现了损失为NaN的情况,在github issue有很多都是这种训练过程出现loss为NaN,作者也提出要调整梯度裁剪的超参数,于是理了理梯度裁剪函数torch.nn.utils.clip_grad_norm_ 的计算过程,方便调参。


一、torch.nn.utils.clip_grad_norm_

torch.nn.utils.clip_grad_norm_(parameters, max_norm, norm_type) ,这个梯度裁剪函数一般来说只需要调整max_normnorm_type这两个参数。
parameters参数是需要进行梯度裁剪的参数列表。通常是模型的参数列表,即model.parameters()
max_norm参数可以理解为梯度(默认是L2 范数)范数的最大阈值
norm_type参数可以理解为指定范数的类型,比如norm_type=1 表示使用L1 范数,norm_type=2 表示使用L2 范数。
同时,torch.nn.utils.clip_grad_norm_torch.nn.utils.clip_grad_norm(该函数已被弃用)的区别就是前者是直接修改原Tensor,后者不会(在Pytorch中有很多这样的函数对均是如此,在函数最后多了下划线一般都是表示直接在原Tensor上进行操作)。

import torch

# 构造两个Tensor
x = torch.tensor([99.0, 108.0], requires_grad=True)
y = torch.tensor([45.0, 75.0], requires_grad=True)

# 模拟网络计算过程
z = x ** 2 + y ** 3
z = z.sum()

# 反向传播
z.backward()

# 得到梯度
print(f"gradient of x is:{x.grad}") 
print(f"gradient of y is:{y.grad}") 

# 梯度裁剪
torch.nn.utils.clip_grad_norm_([x, y], max_norm=100, norm_type=2)


# 再次打印裁剪后的梯度
# 直接修改了原x.grad的值
print("---clip_grad---")
print(f"clip_grad of x is:{x.grad}") 
print(f"clip_grad of y is:{y.grad}") 


# 输出如下
"""
gradient of x is:tensor([198., 216.])
gradient of y is:tensor([ 6075., 16875.])
---clip_grad---
clip_grad of x is:tensor([1.1038, 1.2042])
clip_grad of y is:tensor([33.8674, 94.0762])
"""

上例中可以看出,裁剪后的梯度远小于原来的梯度。一开始变量x的梯度是tensor([198., 216.]),这个很好计算,就是求zx的偏导,也就是2*x 。变量y同理。裁剪后的梯度远小于原来的梯度,所以可以缓解梯度爆炸的问题。

二、计算过程

梯度裁剪的计算过程参考源码是不难的,
SOURCE CODE FOR TORCH.NN.UTILS.CLIP_GRAD
结合代码转换成数学公式,计算过程如下:
第一步:依然以上面的代码为例,构造Tensor反向传播,得到参数xy 的梯度,也就是torch.nn.utils.clip_grad_norm_(parameters, max_norm, norm_type) 中的parameters参数。
【Pytorch】梯度裁剪——torch.nn.utils.clip_grad_norm_的原理及计算过程,PyTorch应用,pytorch,深度学习,python

import torch

# 构造两个Tensor
x = torch.tensor([99.0, 108.0], requires_grad=True)
y = torch.tensor([45.0, 75.0], requires_grad=True)

# 模拟网络计算过程
z = x ** 2 + y ** 3
z = z.sum()

# 反向传播
z.backward()

# 得到梯度
print(f"gradient of x is:{x.grad}") 
print(f"gradient of y is:{y.grad}") 

# 输出
"""
gradient of x is:tensor([198., 216.])
gradient of y is:tensor([ 6075., 16875.])
"""

第二步:计算每个变量梯度的L2 范数(以L2 范数为例)
【Pytorch】梯度裁剪——torch.nn.utils.clip_grad_norm_的原理及计算过程,PyTorch应用,pytorch,深度学习,python

这段代码的意思就是定义一个空列表norms ,用来存储每个参数梯度的L2 范数。随后利用torch.stacknorms中的所有Tensor(计算所得的L2 范数)合并成一个Tensor,最后又再求合并后Tensor的L2 范数得到total_norm(总范数)。

norm_type是inf即无穷范数时,total_norm会直接取参数梯度最大的那一个

# 相当于把x_L2norm、y_L2norm放入代码中的norms空列表

# x的梯度的L2 范数
x_L2norm = torch.sum(x.grad ** 2) ** 0.5
# y的梯度的L2 范数
y_L2norm = torch.sum(y.grad ** 2) ** 0.5

# 相当于遍历norms列表合并成一个Tensor
total_norm = torch.sum(torch.stack([x_L2norm, y_L2norm]) ** 2) ** 0.5

"""
等价过程
x_L2norm = sum([198 ** 2, 216 ** 2]) ** 0.5
y_L2norm = sum([6075 ** 2, 16875 ** 2]) ** 0.5
total_norm = sum([x_L2norm ** 2, y_L2norm ** 2]) ** 0.5
"""

第三步:计算梯度裁剪系数
【Pytorch】梯度裁剪——torch.nn.utils.clip_grad_norm_的原理及计算过程,PyTorch应用,pytorch,深度学习,python

# 1e-6防止分母为0
# clip_coef = max_norm / (total_norm + 1e-6)
max_norm = 100
clip_coef = max_norm / total_norm 

第四步:将原始梯度乘以梯度裁剪系数得到裁剪后的梯度,这与函数计算的结果是一致的。
【Pytorch】梯度裁剪——torch.nn.utils.clip_grad_norm_的原理及计算过程,PyTorch应用,pytorch,深度学习,python

print(f"clip_grad of x is: is {x.grad * clip_coef }")
print(f"clip_grad of x is: is {y.grad * clip_coef }")

# 输出
"""
clip_grad of x is: is tensor([1.1038, 1.2042])
clip_grad of x is: is tensor([33.8674, 94.0762])
"""

整合一下代码:

import torch

# 构造两个Tensor
x = torch.tensor([99.0, 108.0], requires_grad=True)
y = torch.tensor([45.0, 75.0], requires_grad=True)

# 模拟网络计算过程
z = x ** 2 + y ** 3
z = z.sum()

# 反向传播
z.backward()

# 得到梯度
print(f"gradient of x is:{x.grad}") 
print(f"gradient of y is:{y.grad}") 

x_L2norm = torch.sum(x.grad ** 2) ** 0.5
y_L2norm = torch.sum(y.grad ** 2) ** 0.5
total_norm = torch.sum(torch.stack([x_L2norm, y_L2norm]) ** 2) ** 0.5

max_norm = 100
clip_coef = max_norm / total_norm 

print(f"clip_grad of x is: is {x.grad * clip_coef }")
print(f"clip_grad of x is: is {y.grad * clip_coef }")

# 输出如下
"""
gradient of x is:tensor([198., 216.])
gradient of y is:tensor([ 6075., 16875.])
clip_grad of x is:tensor([1.1038, 1.2042])
clip_grad of y is:tensor([33.8674, 94.0762])
"""

三、确定max_norm

根据上述计算过程,梯度裁剪最主要的就是计算出裁剪系数得出裁剪后的梯度。clip_coef = max_norm / total_norm 公式中,clip_coef 越小,裁剪的梯度越大。
max_norm越小,裁剪的梯度越大,得到的梯度就越小,防止梯度爆炸的效果越明显。

在训练模型时,我们可以根据total_norm的值大概确定max_norm的一个取值范围。调用torch.nn.utils.clip_grad_norm_([x, y], max_norm=100, norm_type=2)函数时,该函数会返回total_norm的值。比如最开始参数x、y,这时的total_norm17937.5879,值非常大,那么为了防止梯度爆炸我们就可以把max_norm设置得稍微小一些。文章来源地址https://www.toymoban.com/news/detail-527162.html

# 以最开始的x、y为例
total_norm = torch.nn.utils.clip_grad_norm_([x, y], max_norm=100, norm_type=2)
print(total_norm)

#输出
# tensor(17937.5879) 

到了这里,关于【Pytorch】梯度裁剪——torch.nn.utils.clip_grad_norm_的原理及计算过程的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 积跬步至千里 || PyTorch 中的“with torch no_grad” 语句

    “with ”torch.no_grad()的使用就像一个循环,其中循环内的每个张量都将requires_grad设置为False。这意味着当前与当前计算图相连的任何具有梯度的张量现在都与当前图分离。我们不再能够计算关于这个张量的梯度。 张量从当前图中分离,直到它在循环内。一旦它离开循环,如果

    2024年02月11日
    浏览(41)
  • Pytorch学习:神经网络模块torch.nn.Module和torch.nn.Sequential

    官方文档:torch.nn.Module CLASS torch.nn.Module(*args, **kwargs) 所有神经网络模块的基类。 您的模型也应该对此类进行子类化。 模块还可以包含其他模块,允许将它们嵌套在树结构中。您可以将子模块分配为常规属性: training(bool) -布尔值表示此模块是处于训练模式还是评估模式。

    2024年02月10日
    浏览(42)
  • Pytorch:torch.nn.Module

    torch.nn.Module 是 PyTorch 中神经网络模型的基类,它提供了模型定义、参数管理和其他相关功能。 以下是关于 torch.nn.Module 的详细说明: 1. torch.nn.Module 的定义: torch.nn.Module 是 PyTorch 中所有神经网络模型的基类,它提供了模型定义和许多实用方法。自定义的神经网络模型应该继

    2024年01月16日
    浏览(46)
  • 【Pytorch】torch.nn.LeakyReLU()

    Hello! 非常感谢您阅读海轰的文章,倘若文中有错误的地方,欢迎您指出~   ଘ(੭ˊᵕˋ)੭ 昵称:海轰 标签:程序猿|C++选手|学生 简介:因C语言结识编程,随后转入计算机专业,获得过国家奖学金,有幸在竞赛中拿过一些国奖、省奖…已保研 学习经验:扎实基础 + 多做

    2024年02月02日
    浏览(35)
  • pytorch 固定部分网络参数需要使用 with torch.no_grad()吗

    在 PyTorch 中,torch.no_grad() 是一个上下文管理器,用于设置一段代码的计算图不需要梯度。具体来说,当我们在 torch.no_grad() 的上下文中执行某些操作时,PyTorch 不会为这些操作自动计算梯度,以节省计算资源。 使用 torch.no_grad() 可以有如下几种情况: 测试模型:在测试模型或

    2024年02月11日
    浏览(34)
  • Pytorch-----torch.nn.Module.modules()

    在使用pytorch构建神经网络时,定义的网络模型必须要继承自torch.nn.Module这一父类。在Module类中,有一个函数可以返回网络中所有模块的迭代器。这就是torch.nn.Module.modules() 提示:以下是本篇文章正文内容,下面案例可供参考 源码中的解释如下: 不只是返回网络中的某一层,

    2024年02月14日
    浏览(45)
  • PyTorch中的torch.nn.Parameter() 详解

    今天来聊一下PyTorch中的torch.nn.Parameter()这个函数,笔者第一次见的时候也是大概能理解函数的用途,但是具体实现原理细节也是云里雾里,在参考了几篇博文,做过几个实验之后算是清晰了,本文在记录的同时希望给后来人一个参考,欢迎留言讨论。 先看其名,parameter,中文

    2023年04月08日
    浏览(85)
  • 【学习】pytorch中with torch.no_grad():和 model.eval()的区别

    model.eval()和with torch.no_grad()的区别 先给出结论: 这两个的所起的作用是不同的。 model.eval()的作用是不启用Batch Normalization 和 Dropout. 相当于告诉网络,目前在eval模式,dropout层不会起作用(而在训练阶段,dropout会随机以一定的概率丢弃掉网络中间层的神经元,而且在实际操作

    2024年02月22日
    浏览(48)
  • PyTorch中的torch.nn.Linear函数解析

    torch.nn是包含了构筑神经网络结构基本元素的包,在这个包中,可以找到任意的神经网络层。这些神经网络层都是nn.Module这个大类的子类。torch.nn.Linear就是神经网络中的线性层,可以实现形如y=Xweight^T+b的加和功能。 nn.Linear():用于设置网络中的全连接层,需要注意的是全连接

    2024年02月16日
    浏览(39)
  • 深度学习之pytorch 中 torch.nn介绍

    pytorch 中必用的包就是 torch.nn,torch.nn 中按照功能分,主要如下有几类: 1. Layers(层):包括全连接层、卷积层、池化层等。 2. Activation Functions(激活函数):包括ReLU、Sigmoid、Tanh等。 3. Loss Functions(损失函数):包括交叉熵损失、均方误差等。 4. Optimizers(优化器):包括

    2024年02月22日
    浏览(42)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包