keras训练出现nan的解决流程

 

 

 

出现nan可能是在train的loss阶段,也可能是train的metric阶段,还可能是validation阶段,反正都一样。
在写毕设的过程里面,用学弟提取的特征做DNN的训练,loss没有出现nan,但是反而是metric(MSE)里面出现了nan,predict的结果也全是nan。尝试了很多方法来调bug,最后采用的方法是将训练集二分的方法来调试。

先将训练集划成(0,x),x是训练集的大小,假设x=1000
首先尝试train_df = train_df[0:1000],出现了nan,然后缩减为[0,500],仍然出现问题,然后尝试[0,250],没有出现问题,然后尝试[250,500],以此类推
最后缩减到训练集只有10个样本,保存在csv中,用pandas打开看数据,发现某个特征有inf值
到学弟的代码里面查看该特征编写方法,发现除了一个值A,这个值A有极小概率为0,修改了他的计算方法。
最后总结下出现nan时候的调试方法:

1.第一步:检查训练集里面的特征是否出现Nan值或者Inf值。
2.第二步:如果是自己定义的函数(loss或者metric),检查有没有可能除0或者干脆都在分母加上一个极小值。
3.第三步:换一个优化器,如果没出现问题,可能是网络参数有问题,改小一下lr试试,或者调节下其他参数。

1.训练过程中,测试数据引起NAN:

    如果更换测试集后问题解决,则说明是测试数据有问题。

test_day_n=7
        for test_start_day in [1,]:#range(len(all_days))[::test_day_n][0:1]:#[::-1]:#range(len(all_days)-test_day_n+1)[::-1]:

更改为:
test_day_n=7
        for test_start_day in [10,]:#range(len(all_days))[::test_day_n][0:1]:#[::-1]:#range(len(all_days)-test_day_n+1)[::-1]:

对数据的处理:

            if 'count' in c or 'gmv' in c or 'num' in c or 'sales' in c:
                thresh=100*std
                print(c,'thresh',thresh)
                pos_thresh_v=np.sort(vs)[-pos_thresh]
                thresh=min([thresh,pos_thresh_v])
                print(c,'pos_thresh_v',pos_thresh_v)
                condition=((vs>thresh)|(vs<0)).values
            elif c=='add_time':
                condition=(vs<0).values

将如上代码中剔除100倍标准差更改成剔除10倍标准差,经过验证,解决问题。

总结:数据偏离太大可能引起NAN

===========================================================================

相关参考:

参考1:

前言

训练或者预测过程中经常会遇到训练损失值或者验证损失值不正常、无穷大、或者直接nan的情况:

《警惕!损失Loss为Nan或者超级大的原因》

遇到这样的现象,通常有以下几个原因导致:

梯度爆炸造成Loss爆炸

learningRate过大不仅可能会导致模型在最优点附近震荡,还有可能造成NAN

原因很简单,学习率较高的情况下,直接影响到每次更新值的程度比较大,走的步伐因此也会大起来。如下图,过大的学习率会导致无法顺利地到达最低点,稍有不慎就会跳出可控制区域,此时我们将要面对的就是损失成倍增大(跨量级)。

《警惕!损失Loss为Nan或者超级大的原因》

另外,这种情况很容易在网络层数比较深的时候出现,借用gluon的一段话:
《警惕!损失Loss为Nan或者超级大的原因》

这就是典型的梯度爆炸,解决方法也很简单,降低初始的学习率,并设置学习率衰减。

检查输入数据和输出数据

通常我们都会保证输入的数据是否正确(这个要是不能保证那么后续也就没必要继续了..)。一般输入不正确的话可以立马观察出来。

有两种情况可能并不是那么容易检测到:

  • 数据比较多,99%的数据是对的,但有1%的数据不正常,或者损坏,在训练过程中这些数据往往会造成nan或者inf,这时候需要仔细挑选自己的数据,关于如何挑选数据(https://oldpan.me/archives/how-to-use-tricks-to-train-network)。
  • 训练过程中跳出了错误的数据,这是需要在IDE或者通过其他途径对运行中的程序进行分析。

这时我们要注意的是在训练过程中的输入和输出是否正确:

《警惕!损失Loss为Nan或者超级大的原因》
(利用debug寻找错误的输入)

神经网络中,很有可能在前几层的输入是正确的,但是到了某一层的时候输出就会变成nan或者inf(其中-inf代表负无穷,而nan代表不存在的数),这个时候就需要通过debug去一一检查。

当然我们可以在自己代码中添加检测函数。

例如在Pytorch框架中我们可以使用torch.autograd.tect_anomaly类来监测训练或者预测过程中遇到的隐晦的问题:

>>> import torch
>>> from torch import autograd
>>> class MyFunc(autograd.Function):
...     @staticmethod
...     def forward(ctx, inp):
...         return inp.clone()
...     @staticmethod
...     def backward(ctx, gO):
...         # Error during the backward pass
...         raise RuntimeError("Some error in backward")
...         return gO.clone()
>>> def run_fn(a):
...     out = MyFunc.apply(a)
...     return out.sum()
>>> inp = torch.rand(10, 10, requires_grad=True)
>>> out = run_fn(inp)
>>> out.backward()
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "/your/pytorch/install/torch/tensor.py", line 93, in backward
        torch.autograd.backward(self, gradient, retain_graph, create_graph)
      File "/your/pytorch/install/torch/autograd/__init__.py", line 90, in backward
        allow_unreachable=True)  # allow_unreachable flag
      File "/your/pytorch/install/torch/autograd/function.py", line 76, in apply
        return self._forward_cls.backward(self, *args)
      File "<stdin>", line 8, in backward
    RuntimeError: Some error in backward
>>> with autograd.detect_anomaly():
...     inp = torch.rand(10, 10, requires_grad=True)
...     out = run_fn(inp)
...     out.backward()
    Traceback of forward call that caused the error:
      File "tmp.py", line 53, in <module>
        out = run_fn(inp)
      File "tmp.py", line 44, in run_fn
        out = MyFunc.apply(a)
    Traceback (most recent call last):
      File "<stdin>", line 4, in <module>
      File "/your/pytorch/install/torch/tensor.py", line 93, in backward
        torch.autograd.backward(self, gradient, retain_graph, create_graph)
      File "/your/pytorch/install/torch/autograd/__init__.py", line 90, in backward
        allow_unreachable=True)  # allow_unreachable flag
      File "/your/pytorch/install/torch/autograd/function.py", line 76, in apply
        return self._forward_cls.backward(self, *args)
      File "<stdin>", line 8, in backward
    RuntimeError: Some error in backward

损失函数可能不正确

损失函数也是有可能导致输出nan,尤其是在我们自己设计损失函数的时候。

损失函数应该考虑到是否可以正常地backward

其次对输入的Tensor是否进行了类型转化,保证计算中保持同一类型。

最后考虑在除数中加入微小的常数保证计算稳定性。

batchNorm可能捣鬼

如果你的网络中batchNorm层很多,而且充当比较重要的角色,那么可以适当地检查一下Tensor在输入Batchnorm层后有没有可能变为nan,如果恰好发生这种情况,batchNorm层中的移动均值(running_mean)和移动方差(running_var)也很有可能都是nan,而且这种情况很有可能发生在预测阶段。

这种情况通过发生在训练集和验证集是两个截然不同的分布的时候,这是在训练集中学习到的均值和方法在验证集中是没有作用反而会捣乱。或者在一个神经网络中存在两种结构不同的阵营:典型的是Unet,当在自定义Unet的时候,编码网络和解码网络如果是两个结构存在较大差异的网络,那么在编码阶段学习到的分布在解码阶段就会出现问题。

举个真实的例子:Unet + resnet34 表现正常,但是使用Unet + resnext50 则造成损失爆炸(将解码阶段的batchnorm层失效后表现正常)。

当然上述现象出现的原因大部分在当我们使用model.eval()(Pytorch)之后发生。如果你在预测阶段也将模型model设置为model.train(True),那么问题可能就不会出现:

《警惕!损失Loss为Nan或者超级大的原因》

解决方式:

《警惕!损失Loss为Nan或者超级大的原因》

或者设置Batchnorm中的参数track_running_stats=False使移动均值和移动方差不起作用:

《警惕!损失Loss为Nan或者超级大的原因》

相关的问题:
https://github.com/BVLC/caffe/pull/5136

采用stride大于kernel size的池化层

在卷积层的卷积步伐大于卷积核大小的时候,有可能产生nan

比如:

layer {
  name: "faulty_pooling"
  type: "Pooling"
  bottom: "x"
  top: "y"
  pooling_param {
    pool: AVE
    stride: 5
    kernel: 3
  }
}

你的Shuffle设置有没有乱动

Suffle即洗牌的意思,如果我们在数据加载阶段将Shuffle参数设置在True,那么在神经网络的读取数据的时候,将会打乱顺序去读取,也就是不按照数据的排列顺序去读取。

一般我们是在训练阶段开启shuffle而在预测阶段关闭shuffle,训练阶段在进行每个epoch的时候开启shuffle可以使数据充分地被随机过一遍(类似于我们烤鱼,我们选择是频繁将鱼上下翻面(shuffle)或者只翻一次面,每次烤很长时间),这样训练的鲁棒性比不shuffle稍高一些。

但是假如我们使用了batch_norm层,并且数据的分布极不规律(使用shuflle和不使用shuffle读取的数据顺序的信息分布完全不同),那么在训练阶段训练好的模型(使用shuffle),在预测阶段使用的时候(不使用shuffle),由于数据分布的不同,也是有可能导致batch_norm层出现nan,从而导致不正常的损失函数出现。

在GPU上和CPU上表现并不同

还有种可能的情况,也是没办法的情况,该深度学习框架存在Bug –> 在GPU上运行损失爆炸,但移动到CPU上可能就没有问题,这时候该怎么办么?查找关键原因,赶紧去提个Issue吧~

参考资料:
https://discuss.pytorch.org/t/model-breaks-in-evaluation-mode/2190
https://discuss.pytorch.org/t/model-eval-gives-incorrect-loss-for-model-with-batchnorm-layers/7561/19
https://stackoverflow.com/questions/33962226/common-causes-of-NaNs-during-training

 

 

参考2 :https://blog.csdn.net/pan5431333/article/details/77606581

第一种情况:
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)))

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值