目录
3.4、Pooling层的步长(stride)大于核(kernel)的尺寸
4.2、模型中加入 BatchNormalization ,归一化数据
另一篇文章必须看,提供了示例代码: 深度学习中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。在这里对这个问题做一个总结:
- 脏数据:训练数据(包括label)中有无异常值(nan, inf等);
- 除0问题。这里实际上有两种可能,一种是被除数的值是无穷大,即 Nan,另一种就是0作为了除数(分母可以加一个eps=1e-8)。之前产生的 Nan 或者0,有可能会被传递下去,造成后面都是 Nan。请先检查一下神经网络中有可能会有除法的地方,例 softmax 层,再认真的检查一下数据。可以尝试加一些日志,把神经网络的中间结果输出出来,看看哪一步开始出现 Nan 。
- 可能0或者负数作为自然对数,或者 网络中有无开根号(torch.sqrt), 保证根号下>=0
- 初始参数值过大:也有可能出现 Nan 问题。输入和输出的值,最好也做一下归一化。
- 学习率设置过大:初始学习率过大,也有可能造成这个问题。如果在迭代的100轮以内,出现NaN,一般情况下的原因是因为你的学习率过高,需要降低学习率。可以不断降低学习率直至不出现NaN为止,一般来说低于现有学习率1-10倍即可。如果为了排除是不是学习率的原因,可以直接把学习率设置为0,然后观察loss是否出现Nan,如果还是出现就不是学习率的原因。需要注意的是,即使使用 adam 之类的自适应学习率算法进行训练,也有可能遇到学习率过大问题,而这类算法,一般也有一个学习率的超参,可以把这个参数改的小一些。
- 梯度过大,造成更新后的值为 Nan 。如果当前的网络是类似于RNN的循环神经网络的话,在序列比较长的时候,很容易出现梯度爆炸的问题,进而导致出现NaN,一个有效的方式是增加“gradient clipping”(梯度截断来解决):对梯度做梯度裁剪,限制最大梯度,
- 需要计算loss的数组越界(尤其是自定义了一个新的网络,可能出现这种情况)
- 在某些涉及指数计算,可能最后算得值为 INF(无穷)(比如不做其他处理的softmax中分子分母需要计算exp(x),值过大,最后可能为INF/INF,得到NaN,此时你要确认你使用的softmax中在计算exp(x)做了相关处理(比如减去最大值等等)
3、原因分析与解决方法
3.1、输入数据有误(脏数据)
原因: 输入数据中就含有NaN 、使用的label为空或者 training sample中出现了脏数据!脏数据的出现导致logits计算出了0 出现INF,0作为了除数即nan。
通常我们都会保证输入的数据是否正确。一般输入不正确的话可以立马观察出来。有两种情况可能并不是那么容易检测到:
- 数据比较多,99%的数据是对的,但有1%的数据不正常,或者损坏,在训练过程中这些数据往往会造成
nan或者inf,这时候需要仔细挑选自己的数据,关于如何挑选数据(关于训练神经网路的诸多技巧Tricks(完全总结版) - Oldpan的个人博客)。 - 训练过程中跳出了错误的数据,这是需要在IDE或者通过其他途径对运行中的程序进行分析。
现象:每当学习的过程中碰到这个错误的输入,就会变成NaN。观察loss的时候也许不能察觉任何异常,loss逐步的降低,但突然间就变成NaN了
解决方法:
1、我们要注意在训练过程中的输入和输出是否正确,是否有Nan:
2、label缺失问题也

最低0.47元/天 解锁文章
5802

被折叠的 条评论
为什么被折叠?



