Pytorch训练模型损失Loss为Nan或者无穷大(INF)原因

目录

1、 Nan 和 INF

2、出现 Nan 和 INF 常见原因汇总

3、原因分析与解决方法

3.1、输入数据有误

3.2、学习率过高 --> 梯度爆炸进 --> Nan

3.3、损失函数有误

3.4、Pooling层的步长(stride)大于核(kernel)的尺寸 

3.5、batchNorm可能捣鬼

3.6、Shuffle设置有没有乱动

3.7、设置远距离的Label会得到NAN

4、解决方案:本质就是调整输入数据在模型运算过程中的值域

4.1、模型权重加入正则化,约束参数的大小

4.2、模型中加入 BatchNormalization ,归一化数据

4.3、使用带上限的激活函数,例如relu6函数

4.4、在 losse 函数运算前进行值域修正

4.5、进行梯度減枝

4.6、使用 ResNet 或 DenseNet 结构

5、pytorch半精度amp训练nan问题

5.1、Why?

5.2、How?

6、神经网络学不出东西怎么办?

参考文章:


另一篇文章必须看,提供了示例代码: 深度学习中nan和inf的解决

1、 Nan 和 INF

NaN 是 not a number, 

INF是infinity的简写,意义是无穷大。比如求损失函数会用到log(x),如果 x 接近0,那么结果就是 inf。

梯度消失:是指导数值特别小,导致其连乘项接近无穷小,可能是由输人数据的值域太小(导致权重 W 的导数特别小)或者是神经网络层输出数据落在在激活函数的饱和区(导致激活函数的导数特别小)

梯度爆炸:是指导数值特别大,导致其连乘项特别大,致使 W 在更新后超出了值域的表示范围。可能是输人数据没有进行归一化(数据量纲太大致使 W 的梯度值变大),只要连乘项的导数一直大于1,就会使得靠近输入层的 W 更新幅度特别大。连乘项是指链式求导法则中毎一层的导数,很明显梯度消失与梯度爆炸都受连乘项的影响(也就是网络梯度的影响)。

总结:梯度消失不会导致模型出现 nan 和 inf ,只会导致模型 loss 不会下降,精度无法在训练过程中提升。而梯度爆炸则有可能导致模型在训练过程中出现 inf 。

1.1、从理论的角度看,训练过程中出现  Nan的本质原因是是出现了下溢出 和 上溢出 的现象

上溢出:首先怀疑模型中的指数运算, 因为模型中的数值过大,做exp(x)操作的时候出现了上溢出现象,这里的解决方法是推荐做Nrom 操作,对参数进行正则化,这样在做exp操作的时候就会很好的避免出现上溢出的现象,可以做LayerNormBatchNorm 等,这里我对模型加fine-tune的时候使用LayerNorm 解决了loss为NAN 的问题。【比如不做其他处理的softmax中分子分母需要计算exp(x),值过大,最后可能为INF/INF,得到NaN,此时你要确认你使用的softmax中在计算exp(x)做了相关处理(比如减去最大值等等)】

上溢出同样可能是因为, x/0 的原因,这样就不是参数的值过大的原因,而是具体操作的原因,例如,在自己定义的softmax类似的操作中出现问题,下面是softmax解决上溢出和下溢出的解决方法:

下溢出:一般是log⁡(0) 或者exp(x)操作出现的问题。可能的情况可能是学习率设定过大,需要降低学习率,可以降低到学习率直至不出现nan为止,例如将学习率1e-4设定为1e-5即可。

除了降低学习率的方法,也可在在优化器上面加上一个eps来防止分母上出现0的现象,例如在batchnorm中就设定eps的数值为1e-5,在优化器同样推荐加入参数eps,torch.optim.adam中默认的eps 是1e-8。但是这个值属实有点小了,可以调大这个默认的eps 值,例如设定为1e-3

optimizer1 = optim.Adam(model.parameters(), lr=1e-3, eps=1e-4)
optimizer1 = optim.Adam(model.parameters(), lr=0.001, eps=1e-3)
optimizer2 = optim.RMSprop(model.parameters(), lr=0.001, eps=1e-2)

1.2、从数据的角度上看,训练中产生的 nan 、 inf ,本质上可以分为输人数据值域的问题。

  • 对于输入数据有缺陷,模型前向传播过程中值域超出界限,需要用均值进行 nan 值填充,对于图像数据通常不会出现。
  • 绝大部分情况是值域的问题,值过大或过小。这出现在模型的前向传播中,模型中的系列运行使得数据超出值域范围,如交又熵中的 log 函数, log (1e-10) 值过小, mse loss 中的 math.pow ( x .2)取平方值过大;激活函数中的 relu 函数,没有上界限制;模型的权重过大。

2、出现 Nan 和 INF 常见原因汇总

一般来说,出现NaN有以下几种情况:
相信很多人都遇到过训练一个deep model的过程中,loss突然变成了NaN。在这里对这个问题做一个总结:

  1. 脏数据:训练数据(包括label)中有无异常值(nan, inf等);
  2. 除0问题。这里实际上有两种可能,一种是被除数的值是无穷大,即 Nan,另一种就是0作为了除数(分母可以加一个eps=1e-8)。之前产生的 Nan 或者0,有可能会被传递下去,造成后面都是 Nan。请先检查一下神经网络中有可能会有除法的地方,例 softmax 层,再认真的检查一下数据。可以尝试加一些日志,把神经网络的中间结果输出出来,看看哪一步开始出现 Nan 。
  3. 可能0或者负数作为自然对数,或者 网络中有无开根号(torch.sqrt), 保证根号下>=0
  4. 初始参数值过大:也有可能出现 Nan 问题。输入和输出的值,最好也做一下归一化。
  5. 学习率设置过大:初始学习率过大,也有可能造成这个问题。如果在迭代的100轮以内,出现NaN,一般情况下的原因是因为你的学习率过高,需要降低学习率。可以不断降低学习率直至不出现NaN为止,一般来说低于现有学习率1-10倍即可。如果为了排除是不是学习率的原因,可以直接把学习率设置为0,然后观察loss是否出现Nan,如果还是出现就不是学习率的原因。需要注意的是,即使使用 adam 之类的自适应学习率算法进行训练,也有可能遇到学习率过大问题,而这类算法,一般也有一个学习率的超参,可以把这个参数改的小一些。
  6. 梯度过大,造成更新后的值为 Nan 。如果当前的网络是类似于RNN的循环神经网络的话,在序列比较长的时候,很容易出现梯度爆炸的问题,进而导致出现NaN,一个有效的方式是增加“gradient clipping”(梯度截断来解决):对梯度做梯度裁剪,限制最大梯度,
  7. 需要计算loss的数组越界(尤其是自定义了一个新的网络,可能出现这种情况)
  8. 在某些涉及指数计算,可能最后算得值为 INF(无穷)(比如不做其他处理的softmax中分子分母需要计算exp(x),值过大,最后可能为INF/INF,得到NaN,此时你要确认你使用的softmax中在计算exp(x)做了相关处理(比如减去最大值等等)

3、原因分析与解决方法

3.1、输入数据有误(脏数据)

原因: 输入数据中就含有NaN 、使用的label为空或者 training sample中出现了脏数据!脏数据的出现导致logits计算出了0 出现INF,0作为了除数即nan。

通常我们都会保证输入的数据是否正确。一般输入不正确的话可以立马观察出来。有两种情况可能并不是那么容易检测到:

现象:每当学习的过程中碰到这个错误的输入,就会变成NaN。观察loss的时候也许不能察觉任何异常,loss逐步的降低,但突然间就变成NaN了
解决方法:

1、我们要注意在训练过程中的输入和输出是否正确,是否有Nan:

利用debug寻找错误的输入

2、label缺失问题也

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值