0


pytorch训练模型时出现nan原因整合

必须看的博客:

警惕!损失Loss为Nan或者超级大的原因 - Oldpan的个人博客

深度学习中nan和inf的解决_dddeee的专栏-CSDN博客_nan和inf

解决pytorch半精度amp训练nan问题 - 知乎

模型训练 loss变成NAN的原因解决方法 - 知乎

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

1.如果在迭代的100轮以内,出现NaN,一般情况下的原因是因为你的学习率过高,需要降低学习率。可以不断降低学习率直至不出现NaN为止,一般来说低于现有学习率1-10倍即可。

如果为了排除是不是学习率的原因,可以直接把学习率设置为0,然后观察loss是否出现Nan,如果还是出现就不是学习率的原因

2.如果当前的网络是类似于RNN的循环神经网络的话,出现NaN可能是因为梯度爆炸的原因,一个有效的方式是增加“gradient clipping”(梯度截断来解决)

3.可能用0作为了除数;

4.可能0或者负数作为自然对数

5.需要计算loss的数组越界(尤其是自定义了一个新的网络,可能出现这种情况)

6.在某些涉及指数计算,可能最后算得值为INF(无穷)(比如不做其他处理的softmax中分子分母需要计算exp(x),值过大,最后可能为INF/INF,得到NaN,此时你要确认你使用的softmax中在计算exp(x)做了相关处理(比如减去最大值等等))

1 梯度爆炸

原因:学习的过程中,梯度变得非常大,使得学习的过程偏离了正常的轨迹,使得学习过程难以继续
现象:观察每次迭代的loss值,会发现loss随着每轮迭代明显增长,并且代越来越大,最后loss值太大最终超过了浮点型表示的范围,所以变成了Nan。
解决方法:

1、降低学习率
2、梯度裁剪,设置gradient clipping,用于限制过大的diff
3、数据量纲不一致,也会导致梯度爆炸,数据归一化方法(减均值,除方差,或者加入normalization,例如BN、L2 norm等)
4、如果模型中有多个loss层,就需要找到梯度爆炸的层,然后降低该层的loss weight

2 学习率过高

原因:过高的学习率乘上所有的梯度使得所有参数变成无效的值。
现象:观察输出日志,会发现学习率变成nan
解决方法:设置合适的学习速率

3 损失函数有误, data underflow

原因:损失函数的计算可能导致NaN的出现,如交叉熵损失函数的计算可能出现log(0),所以就会出现loss为Nan的情况

当网络训练到达一定程度的时候,模型对分类的判断可能会产生0这样的数值,log(0)本身是没有问题的,-inf可以安全的参与绝大部分运算,除了(-inf * 0),会产生NaN。NaN的话,一旦参与reduce运算会让结果完蛋的

**现象: **观测训练产生的loss时一开始并不能看到异常,loss也在逐步的下降,但是突然出现Nan
解决方法: 尝试重现该错误,在loss layer中加入一些输出以进行调试.
找到可能出现的错误的地方,增加一个bias

# 源代码
# match wh / prior wh
g_wh = (matched[:, 2:] - matched[:, :2]) / priors[:, 2:]
g_wh = torch.log(g_wh) / variances[1]
# return target for smooth_l1_loss
return torch.cat([g_cxcy, g_wh], 1)  # [num_priors,4]

# 修改后
eps = 1e-5
# match wh / prior wh
g_wh = (matched[:, 2:] - matched[:, :2]) / priors[:, 2:]
g_wh = torch.log(g_wh + eps) / variances[1]
# return target for smooth_l1_loss
return torch.cat([g_cxcy, g_wh], 1)  # [num_priors,4]
y_truth * log(y_predict)      
# when y_truth[i] is 0, it is likely that y_predict[i] would be 0

# 这样的表达式,要考虑对log中的变量进行clip. 比如

safe_log = tf.clip_by_value(some_tensor, 1e-10, 1e100)
bin_tensor * tf.log(safe_log)

4 输入数据有误

原因: 输入中就含有NaN 或者 trainingsample中出现了脏数据!脏数据的出现导致logits计算出了0,0传给 即nan。

**现象:**每当学习的过程中碰到这个错误的输入,就会变成NaN。观察loss的时候也许不能察觉任何异常,loss逐步的降低,但突然间就变成NaN了
解决方法:逐步去定位错误数据,然后删掉这部分数据.
通过设置batch_size = 1,shuffle = False,一步一步地将sample定位到了所有可能的脏数据,删掉
重整数据集,确保训练集和验证集里面没有损坏的图片。可以使用一个简单的网络去读取输入,如果有一个数据是错误的,这个网络的loss值也会出现Nan

5 Pooling层的步长大于核的尺寸

如下例所示,当池化层中stride > kernel的时候会在y中产生NaN

6 设置远距离的Label会得到NAN

我的理解是,模型采用了交叉熵损失函数,当标签过于分散的时候,比方说标签为8000的数据,其概率分布值就会变成了比较小的数值,也就是会出现类似于log(0)这种情况,从而模型的loss为Nan.

1、 nan 和inf产生原因

NaN 是not a number,

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

从理论的角度上看,出现NaN和Inf,本质是梯度消失与梯度爆炸所导致的。

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

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

从数据的角度上看,训练中产生的 nan 、 inf ,本质上可以分为输人数据值域的问题。输入数据有缺失,模型前向传播过程中值域超出界限。

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

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

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

1、模型权重加入正则化,约束参数的大小
2、模型中加入 BatchNormalization ,归一化数据
3、使用带上限的激活函数,例如relu6函数

pytorch 下使用 torch . nn .ReLU6,函数原型为 min ( max (0, x ),6),也就是把 relu 函数的最大值限制为6。 也就是对输出做了限制
4、在 losse 函数运算前进行值域修正
tf可以使用 clip_by_value 函数,在 loss 函数的 log , exp 前调整 y_pred 的值域,避第 -logl(0)产生的无穷大

pytorch 可以使用 torch.clip ( input , min=None , max = None ) 或 torch.clamp ( input , min=None, max = None )进行值域限制

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=2)
nn.utils.clip_grad_norm_(model.parameters(), max_norm=20, norm_type=2)
optimizer.step()

5、进行梯度減枝
对超出值域范围的梯度进行约束,避免梯度持续大于1,造成梯度爆炸。(没办法规避梯度消失)
pytorch 使用 nn.utils.clip_grad_value(parameters, clip_value).将所有的参数剪裁到[-clip_value , clip_value]。如 clip_value =1,[100,0.1]=>[1,0.1],该操作会改变梯度的方向
使用 nn.utils.clip_grad_norm_ 按照范数大小进行归一化,当参数的范数( norm_type=2 范数)大于最大值时,则会将其归约到最大值。该方法可以保证梯度的方向是完全一致的,可能会导致梯度值被缩放到特别小(如[100,0.1]=>[1,0.0001])。

6、使用 ResNet 或 DenseNet 结构
将深层的样度值通过跳跃连接传递到浅层中

在训练早期,模型参数可能不是很合适,会出现梯度消失和爆炸的情况,特别是有lstm,rnn这类网络的情况。nan 是not a number ,inf是无穷大。比如求损失函数会用到log,如果输入接近0,那么结果就是inf。


本文转载自: https://blog.csdn.net/ytusdc/article/details/122321907
版权归原作者 ytusdc 所有, 如有侵权,请联系我们删除。

“pytorch训练模型时出现nan原因整合”的评论:

还没有评论