深度学习训练中cost突然出现NaN

问题:在深度学习训练中,之前的cost是正常的,突然在某一个batch训练中出现Nan

few advises to avoid this problem

if error starts increasing then NaN appears afterwards: diverging due to too high learning rate
if NaNs appear suddenly: saturating units yielding non-differentiable gradient NaN computation due to log(0)
NaN due to floating point issues (to high weights) or activations on the output 0/0, inf/inf, inf*weight...
还可以在加一条:数据检查,送入模型的数据是否有问题。


回归到我自己的问题上,首先把数据检查了下,数据没有问题,但是NaN仍忍出现。

然后把learning rate开小了(各种变小),NaN问题依然存在。沿着learning rate这条路,把gradient都check一遍,把Clip Gradient弄的很厉害,但是NaN问题依然存在。

接着把所有可能出问题的变量依次output出来,追溯到某一个变量在cost出现的NaN那一轮产生了NaN。现在问题就来了,为什么上一轮的gradient没有问题,该变量突然变为NaN呢?

再后来,使用NanGuardMode模式,报错出现big values,更加坚信是gradient过大出现的问题,然后就是各种瞎改gradient。

后来的后来,才蓦然回首发现code中有一个逻辑上的bug,会导致NaN的出现。

简单讲就是使用到某个变量 A = [a_1, a_2, ... a_n ] 及其 Mask MA = [ma_1, ma_2,..., ma_n],需要结合其Mask对其进行softmax操作。 在直接对A进行softmax发现,A中某个值得energy太大(对于某个元素 exp^(a_j)数值非常大),会出问题。为了避免问题,找出A中最大数值max(A),对每个值进行“缩放”,A - max(A)后再计算其每个元素的energy,在结合Mask MA进行softmax计算。

step1. 元素“缩放”:  A' = A - A(max)

step2. 计算energy: exp(A') 

step3. Mask(Mask为0的地方概率要为0): exp(A') * MA

step4. 计算概率: exp(A')  / sum( exp(A') * MA)

潜在除0问题:在step1. “缩放”步骤中,Mask为0的某元素比其他元素大的多,导致在step2.计算energy步骤中Mask为1的元素energy几乎为0。接着step3.Mask步骤中使用MA把energy不为0的地方给掩盖掉,这时候留下的全都是0.....

然后step4.计算概率中sum( exp(A') * MA) 的结果为0,最后就出现除0-->NaN问题了。
--------------------- 

NaNs问题(Dealing with NaNs)
相信很多自己实现过深度学习模型或者训练过深度学习模型的人都会知道,产生NaNs或者Infs是一件特别常见的事情。但是NaNs的问题又往往很棘手,因为出现NaN的原因可能有很多,诊断起来并不容易。

这可能是因为模型中某个地方设置的不是特别合理,或者代码中出现了Bug。也有可能是出现了数值不稳定,数值不稳定问题会进行单独说明。甚至也可能是你的算法出了问题,下面将介绍几种出现NaNs的场景以及如何进行诊断并处理模型训练过程中出现的NaNs。

超参数的设置以及权重初始化(Check Superparameters and Weight Initialization)
通常情况下,尤其是你使用深度学习库训练时,原因大多是超参数设置问题。

最可能是就你的学习率设置过大。一个过大的学习率可能在一个epoch里就让你整个模型产生NaN的输出,所以首先检查学习率,最简单的办法就是设置一个小的学习率。

其次,你的模型里有正则化项吗?看正则项是否设置合理,选择一个大的正则项系数来训练几个epoch看下NaNs有没有消失。

还有一些模型可能对权重的初始化十分敏感,如果权重没有采用合理的初始化方法,或者合适的初始化范围,模型可能会产生NaNs。

关于数值稳定(Numerical Stability)
如果你最终诊断发现是在某个公式计算时产生的NaNs,可能的原因就是这个公式存在数值稳定问题,通常发生在你自己实现的某个公式,例如Softmax,因为深度学习库通常会帮助解决公式中的数值稳定问题。

举个列子:一些nodes在某些输入下产生一个很小的p(x)。

在反向传播时,正确的类别所得到的概率非常小(接近零)的话,这里会有 overflow 的危险。

更加详细的数值稳定讲解参照这篇Blog: Softmax vs. Softmax-Loss: Numerical Stability

算法相关(Algorithm Related)
如果上述方法都不适用,那可能是你的算法出了问题,回头去检查数学公式的实现来确保所有的计算和推导是正确的。
--------------------- 

在训练神经网络的过程中,经常会遇到当训练进行到一定次数后,cost会变成NaN的问题。在网上搜索相关问题时,发现大多人的观点是认为learningRate过大导致NaN,但是经过我自己摸索,我发现绝大多数情况下NaN是由以下几种情况导致的。learningRate过大只是会造成cost不收敛,在最优点附近震荡。

第一种情况:
cost的计算公式为:cost = -(y.t * log(yHat) + (1.0 - y).t * log(1.0 - yHat))。这里当yHat=0时,log(yHat)可能为NaN,当yHat=1时,log(1.0 - yHat)可能为NaN,所以为了保险起见,在log的参数中加上一个极小值,即:cost=-(y.t * log(yHat + pow(10.0, -9)) + (1.0 - y).t * log(1.0 - yHat + pow(10.0, -9)));。Scala中计算cost的代码如下:
private def calCost(res: ResultUtils.ForwardRes, label: DenseVector[Double]):
Double = {
  val yHat = res.yCurrent(::, 0)
  -(label.t * log(yHat + pow(10.0, -9)) + (1.0 - label).t * log(1.0 - yHat + pow(10.0, -9))) / label.length.toDouble
}

第二种情况:
计算输出层yHat的导数的公式为:dYHat = -(y /:/ yHat - (1.0 - y) /:/ (1.0 - yHat)),这里当yHat=0时,y /:/ yHat无穷大,可能为NaN;当yHat=1时, (1.0 - y) /:/ (1.0 - yHat)无穷大,可能为NaN。而这个问题也是我在训练带有两个隐含层的神经网络时debug了接近一天的问题,不知道为什么当隐含层只有一层时没问题,但当隐含层层数为2时,每当cost下降到0.40附近就会出现NaN。为了解决这个问题,在分母上加上一个很小的正数:dYHat = -(y /:/ (yHat + pow(10.0, -9)) - (1.0 - y) /:/ (1.0 - yHat + pow(10.0, -9))),修改完毕后即可正常训练。Scala中计算dYHat也即dYL的公式如下:
//+ pow(10.0, -9)防止出现被除数为0,NaN的情况
val dYL = -(label /:/ (yHat + pow(10.0, -9)) - (1.0 - label) /:/ (1.0 - yHat + pow(10.0, -9)))

第三种情况:
梯度爆炸?
--------------------- 

梯度爆炸了吧。

我的解决办法一般以下几条:
1、数据归一化(减均值,除方差,或者加入normalization,例如BN、L2 norm等);
2、更换参数初始化方法(对于CNN,一般用xavier或者msra的初始化方法);
3、减小学习率、减小batch size;
4、加入gradient clipping;

发布于 2016-09-04

 

仁孟

仁孟

 

 

说明训练不收敛了, 学习率太大,步子迈的太大导致梯度爆炸等都是有可能的,另外也有可能是网络的问题,网络结构设计的有问题。
我现在的采用方式是:
1. 弱化场景,将你的样本简化,各个学习率等参数采用典型配置,比如10万样本都是同一张复制的,让这个网络去拟合,如果有问题,则是网络的问题。否则则是各个参数的问题。
2. 如果是网络的问题,则通过不断加大样本的复杂度和调整网络(调整拟合能力)来改变。
3. 参数的微调,我个人感觉是在网络的拟合能力和样本的复杂度匹配的情况下,就是可以train到一定水平,然后想进行进一步优化的时候采用。
4. 参数的微调,楼上说得几个也算是一种思路吧,其他的靠自己去积累,另外将weights可视化也是一个细调起来可以用的方法,现在digits tf里面都有相关的工具.

编辑于 2016-12-13

 

Scofield

Scofield

不懂不会没听说过NLP

 

 

最近做了一组实验,每次在固定的迭代次数段,都会loss突然变nan,导致acc骤降,慢慢变0。

于是找啊找啊找bug……

很难受,在意志力的坚持下,找到海枯石烂终于知道了!

loss突然变nan的原因,很可惜并不是这里其他所有答主所说的“因为梯度爆炸”、“lr过大”、“不收敛”等等原因,而是因为training sample中出现了脏数据

脏数据的出现导致我的logits计算出了0,0传给 log(x|x=0) \rightarrow ∞, 即nan。

所以我通过设置batch_size = 1,shuffle = False,一步一步地将sample定位到了所有可能的脏数据,删掉。期间,删了好几个还依然会loss断崖为nan,不甘心,一直定位一直删。终于tm work out!

之所以会这样,是因为我的实验是实际业务上的真实数据,有实际经验的就知道的,现实的数据非常之脏,基本上数据预处理占据我80%的精力。

好怀念以前可以天真快乐的在open dataset上做task跑模型的时候,真是啥都不用管,专注模型算法……

编辑于 2018-02-03

 

匿名用户

匿名用户

 

 

在训练深度神经网络的时候,出现NaN比较大的可能是因为学习速率过大,梯度值过大,产生梯度爆炸。

During experimentation, once the gradient value grows extremely large, it causes an overflow (i.e. NaN) which is easily detectable at runtime; this issue is called the Gradient Explosion Problem.

参考斯坦福CS 224D的lecture note,我们也可以找到一些解决方法:
1. 加入Gradient clipping:每当梯度达到一定的阈值,就把他们设置回一个小一些的数字。
\frac{\partial E}{\partial W} \rightarrow g
if \left| \left| g \right| \right|  \geq thresholdthen
\frac{threshlod}{\left| \left|  g \right| \right| }g\rightarrow g
endif
2. 调整学习速率。学习速率过大会导致不能正常收敛,因此可以把学习速率适当调小一些。
3. 调整深度神经网络的结构。

To solve the problem of exploding gradients, Thomas Mikolov first introduced a simple heuristic solution that clips gradients to a small number whenever they explode. That is, whenever they reach a certain threshold, they are set back to a small number as shown in Algorithm 1. 
Figure 5 visualizes the effect of gradient clipping. It shows the decision surface of a small recurrent neural network with respect to its W matrix and its bias terms, b. The model consists of a single unit of recurrent neural network running through a small number of timesteps; the solid arrows illustrate the training progress on each gradient descent step. When the gradient descent model hits the high error wall in the objective function, the gradient is pushed off to a far-away location on the decision surface. The clipping model produces the dashed line where it instead pulls back the error gradient to somewhere close to the original gradient landscape.

编辑于 2017-02-05

 

llh

llh

 

 

用的是交叉熵cross_entropy = -tf.reduce_sum(y_*tf.log(y_conv))的话,最后softmax层输出y_conv的取值范围在[0,1]页就是说允许取0值,有log(0)出现很有可能出现nan啊,cross_entropy = -tf.reduce_mean(y_*tf.log(tf.clip_by_value(y_conv,1e-15,1.0)))在tensorflow里可以限定一下y_conv的取值范围,别的框架不清楚。

编辑于 2018-01-08

 

匿名用户

匿名用户

 

 

是不是用了tanh?

发布于 2016-12-12

 

 

相信很多人都遇到过训练一个deep model的过程中,loss突然变成了NaN。在这里对这个问题做一个总结。

一般来说,出现NaN有以下几种情况:

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

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

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

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

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

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

参考: 
http://russellsstewart.com/notes/0.html

 

tensorflow训练中出现nan问题

原创 2017年06月24日 14:16:22

 

        深度学习中对于网络的训练是参数更新的过程,需要注意一种情况就是输入数据未做归一化时,如果前向传播结果已经是[0,0,0,1,0,0,0,0]这种形式,而真实结果是[1,0,0,0,0,0,0,0,0],此时由于得出的结论不惧有概率性,而是错误的估计值,此时反向传播会使得权重和偏置值变的无穷大,导致数据溢出,也就出现了nan的问题。

解决办法:

1、对输入数据进行归一化处理,如将输入的图片数据除以255将其转化成0-1之间的数据;

2、对于层数较多的情况,各层都做batch_nomorlization;

3、对设置Weights权重使用tf.truncated_normal(0, 0.01, [3,3,1,64])生成,同时值的均值为0,方差要小一些;

4、激活函数可以使用tanh;

5、减小学习率lr。

from:http://blog.csdn.net/fireflychh/article/details/73691373

 

 

 

使用caffe训练时Loss变为nan的原因

 

梯度爆炸

原因:梯度变得非常大,使得学习过程难以继续

现象:观察log,注意每一轮迭代后的loss。loss随着每轮迭代越来越大,最终超过了浮点型表示的范围,就变成了NaN。

措施: 
1. 减小solver.prototxt中的base_lr,至少减小一个数量级。如果有多个loss layer,需要找出哪个损失层导致了梯度爆炸,并在train_val.prototxt中减小该层的loss_weight,而非是减小通用的base_lr。 
2. 设置clip gradient,用于限制过大的diff

不当的损失函数

原因:有时候损失层中loss的计算可能导致NaN的出现。比如,给InfogainLoss层(信息熵损失)输入没有归一化的值,使用带有bug的自定义损失层等等。

现象:观测训练产生的log时一开始并不能看到异常,loss也在逐步的降低,但突然之间NaN就出现了。

措施:看看你是否能重现这个错误,在loss layer中加入一些输出以进行调试。

示例:有一次我使用的loss归一化了batch中label错误的次数。如果某个label从未在batch中出现过,loss就会变成NaN。在这种情况下,可以用足够大的batch来尽量避免这个错误。

不当的输入

原因:输入中就含有NaN。

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

措施:重整你的数据集,确保训练集和验证集里面没有损坏的图片。调试中你可以使用一个简单的网络来读取输入层,有一个缺省的loss,并过一遍所有输入,如果其中有错误的输入,这个缺省的层也会产生NaN。

案例:有一次公司需要训练一个模型,把标注好的图片放在了七牛上,拉下来的时候发生了dns劫持,有一张图片被换成了淘宝的购物二维码,且这个二维码格式与原图的格式不符合,因此成为了一张“损坏”图片。每次训练遇到这个图片的时候就会产生NaN。良好的习惯是,你有一个检测性的网络,每次训练目标网络之前把所有的样本在这个检测性的网络里面过一遍,去掉非法值。

池化层中步长比核的尺寸大

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

 
  1. layer {

  2. name: "faulty_pooling"

  3. type: "Pooling"

  4. bottom: "x"

  5. top: "y"

  6. pooling_param {

  7. pool: AVE

  8. stride: 5

  9. kernel: 3

  10. }

  11. }

致谢

原文:https://blog.csdn.net/pan5431333/article/details/77606581 
原文:https://blog.csdn.net/Shingle_/article/details/79954304 

原文:https://blog.csdn.net/wangxinginnlp/article/details/77503354 

  • 10
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值