IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 人工智能 -> PyTorch的gradcheck()报错问题RuntimeError: Jacobian mismatch for output 0 with respect to input 1的解决 -> 正文阅读

[人工智能]PyTorch的gradcheck()报错问题RuntimeError: Jacobian mismatch for output 0 with respect to input 1的解决

有时遇到有的模型训练或测试脚本执行时遇到torch.autograd.gradcheck()抛出类似如下的错误:

?有时报的是Jacobian mismatch for output 0 with respect to input 0,这个出错的原因都是一个:

torch.autograd.gradcheck()要求参数计算的PyTorch Tensor数据都是torch.DoubleTensor类型的(torch.float64),也就是双精度数据,而不是默认的torch.FloatTensor类型(torch.float32),参见torch.autograd.gradcheck — PyTorch master documentation:

一般人在实现网络和写训练或测试脚本时没注意到这个问题,使用的数值类的Tensor都是没有指定类型的,默认是float32而不是float64,精度不够参与运算的话很容易造成numerical gradients和analytical gradients的误差超出设定的允许范围而抛出异常导致程序终止。

? ?另外要注意的是,当inputs里的Tensor数据是使用torch.rand()产生的随机数时,有时执行可能能正常完成,多数时候会报错。有的人提出的解决办法就是针对单精度数据将参数eps的默认值1e-6改为更大的1e-3或者1e-4,实际上这样做并不能完全保证触发抛出异常,只是减少了出错机会而已,但是异常还是会发生的,有时还是频繁抛出异常,所以根本不算好的解决办法。

? ?另外,我看了一下源码/usr/local/lib/python3.6/dist-packages/torch/autograd/gradcheck.py:

for j, (a, n) in enumerate(zip(analytical, numerical)):
   if a.numel() != 0 or n.numel() != 0:
      if not torch.allclose(a, n, rtol, atol):
         return fail_test('Jacobian mismatch for output %d with respect to input %d,\n'
                                     'numerical:%s\nanalytical:%s\n' % (i, j, n, a))

实际上是在使用torch.allclose()比较analytical gradients和numerical gradients的误差是否在由atol和rtol设置的允许范围内,torch.allclose()实现的比较a和n是否接近的公式为
? ? ? ? ? ? ? |a-n| <= atol+rtol*|n|

那这么看来,似乎调大atol的参数值似乎也能避免抛出异常啊,试了一下,还是只能保证部分时候能正常执行完,有时会报Backward is not reentrant,i.e., running backward with same input and grad_output multiple times gives different values, although analytical gradient matches numerical gradient的错误:

?

? ? 因为gradcheck()只是检查校验方向传播时使用链式法则算出来的analytical gradients和直接使用数值微分方式算出来的numerical gradients是否误差在允许的范围内,如果只是使用人家的模型,对这个训练或测试时的梯度检查结果不在意,完全可以把调用gradcheck()的语句给注释掉,或者设置raise_exception=False即可。

? ?如果要根本解决这个问题,传入gradcheck()的inputs里tensor数据都必须转换成double类型,另外对于func(可以是继承自torch.nn.model或者torch.autograd.Function,前者是实现了后者的)的内部,也必须是使用的DoubleTensor类型tensor参与运算,对于使用pytorch实现的代码,很好修改,有的内部实现都是使用的参入参数inputs里的tensor,自己并没有创建tensor变量之类的,那就更好办根本不用修改,如果是为了性能考虑使用C++实现的网络再python包装实现的,那就得C++代码里要修改保证数据都是double类型的。

? ? ?看个pytorch官网上别报告问题的简单例子以及怎么修改:Why is nn.Linear failing gradcheck? Is there a bug in gradcheck? - PyTorch Forums

x = torch.rand(256, 2, requires_grad=True)
y = torch.randint(0, 10, (256, ), requires_grad=True)
custom_op = nn.Linear(2, 10) 
res = torch.autograd.gradcheck(custom_op, (x, ))
print(res)

上面这个代码执行时会抛出Jacobian mismatch的异常,那么改成下面的样子就不会抛出异常了:

x = torch.rand(256, 2, requires_grad=True).double()
y = torch.randint(0, 10, (256, ), requires_grad=True)
custom_op = nn.Linear(2, 10).double() 
res = torch.autograd.gradcheck(custom_op, (x, ))
print(res)

自己弄着玩弄个完全没干啥的空壳func,这个只是演示gradcheck()的func大致该是什么样的:

class MF(Function):
   @staticmethod
   def forward(ctx, input):
      return input
   @staticmethod
   def backward(ctx, grad):
      return grad
mfa = MF.apply

x = dcn_v2_conv (input, offset, mask, weight, bias,
                    stride, padding, dilation, deformable_groups)

print('check_gradient_dconv: ',
          gradcheck(mfa, (x.double(),), eps=1e-3, atol=1e-4, rtol=1e-2))

其实gradcheck()一般是用来校验模型网络的某层或者整个网络的反向传播链式梯度计算是否有误,校验的标准就是对照正常的数值微分计算出来的对应梯度值,既然是这样,一般传入gradcheck()的func就是model或者model里的某个layer就行了。

最后,再次说说什么是analytical gradients和numerical gradients。大学时学高数和数值分析等科目时我们都学了导数是什么东西、求导是怎么弄出来的,以及怎么使用小微量值求近似导数,正常的近似计算办法就是数值梯度法,这种办法计算得到的是numerical gradients,原理就是:

? ? ? ? 当delta->0时, df(x)/dx = lim( f(x + delta) - f(x) )/delta

数值分析中一般稍加改动,使用好看一点的中心差值公式(centered difference formula):

? ? ? ? delta是个很小的数值,df(x)/dx = lim( f(x + delta) - f(x - delta))/(2*delta)

这里的delta其实就是gradcheck()中的eps参数,eps默认值是1e-06,所以gradcheck()里在使用数值微分方式计算x tensor的第i个分量对应的梯度值时应该是这样算的:

? ? ? ? ?gradient[i] = (f(x + eps) - f( x -eps))/(2*eps)

从上面公式看出,eps的值设置会对致梯度值影响很大。按理说,计算数值梯度时,eps 越小,求得的值应该更接近于真实的梯度,可是实际中可能当eps值设置为 1e-3 时,func可以通过梯度检查,但 eps调小为1e-4 时,func反而都无法通过了!原因就是input tensor的默认类型为torch.float32, 在 eps 过小的情况下,与真实精度间产生了较大的精度误差(因为计算数值梯度时eps 作为被除数嘛)。可以以PyTorch内置的激活函数Sigmoid为例实验一下:

input = torch.randn(4, requires_grad=True)
torch.autograd.gradcheck(torch.sigmoid, (input,), eps=1e-3)    # succeeded
torch.autograd.gradcheck(torch.sigmoid, (input,), eps=1e-4)    # failed

将input的类型指定为torch.float64后,即使调小eps到1e-06都能通过校验成功:

input = torch.randn(4, requires_grad=True, dtype=torch.float64)
torch.autograd.gradcheck(torch.sigmoid, (input,), eps=1e-4)    # succeeded
torch.autograd.gradcheck(torch.sigmoid, (input,), eps=1e-6)    # succeeded

对于analytical gradients,其实就是反向传播(BP)时根据求导的链式法则进行从网络尾端向前端逐层反向计算各参数的梯度得到的,所谓求导的链式法则,举个简单例子:?

? ? ? ? ? ? ?df(g(z(x)))/dx = (df/dg)*(dg/dz)*(dz/dx)

链式法则求导非常好理解,网上也有很多解释就不多说了。

  人工智能 最新文章
2022吴恩达机器学习课程——第二课(神经网
第十五章 规则学习
FixMatch: Simplifying Semi-Supervised Le
数据挖掘Java——Kmeans算法的实现
大脑皮层的分割方法
【翻译】GPT-3是如何工作的
论文笔记:TEACHTEXT: CrossModal Generaliz
python从零学(六)
详解Python 3.x 导入(import)
【答读者问27】backtrader不支持最新版本的
上一篇文章      下一篇文章      查看所有文章
加:2021-09-22 14:41:05  更:2021-09-22 14:43:19 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/21 18:15:36-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码