神经网络训练中出现NaN的常见原因

125

我注意到在训练过程中经常出现NAN

通常情况下,这似乎是由内积/全连接或卷积层的权重爆炸引起的。

这是由于梯度计算爆炸造成的吗?还是因为权重初始化(如果是这样,为什么会产生这种影响)?还是可能由于输入数据的性质引起的?

总体问题很简单:NAN在训练期间出现的最常见原因是什么?其次,有哪些方法可以解决这个问题(以及它们为什么有效)?

6个回答

178

我已经多次遇到这种现象,以下是我的观察:


梯度爆炸

原因:大的梯度会使学习过程偏离轨道。

你应该期望什么:查看运行时日志,你应该关注每次迭代的损失值。你会注意到损失值从一次迭代到另一次迭代开始显著增长,最终损失将变得太大而无法用浮点变量表示,并且它将变成nan

你可以做什么:base_lr(在solver.prototxt中)降低一个数量级(至少)。如果你有几个损失层,你应该检查日志以查看哪个层负责梯度爆炸,并为该特定层减少train_val.prototxt中的loss_weight,而不是通用的base_lr


糟糕的学习率策略和参数

原因:caffe无法计算有效的学习率,而是得到'inf''nan',这个无效的学习率会使所有更新失效,从而使所有参数失效。

你应该期望什么:查看运行时日志,你应该看到学习率本身变成了'nan',例如:

... sgd_solver.cpp:106] Iteration 0, lr = -nan
你能做什么: 在你的 'solver.prototxt' 文件中修复影响学习率的所有参数。例如,如果你使用 lr_policy: "poly" 并忘记定义 max_iter 参数,你最终会得到 lr = nan...有关caffe中学习率的更多信息,请参见此线程

损失函数出现问题

原因: 有时,损失层中的损失计算会导致 nan 出现。例如,使用未标准化值来提供InfogainLoss,使用带有错误的自定义损失层等等。 你应该期望: 查看运行时日志,你可能不会注意到任何异常情况:损失逐渐减小,突然出现了一个 nan你能做什么: 看看是否可以重现错误,在损失层添加打印输出并调试错误。
例如:我曾经使用一种损失,通过批次中标签出现的频率对惩罚进行规范化。恰巧,如果一个训练标签在批次中根本没有出现,计算出的损失会产生 nan。在这种情况下,使用足够大的批次(相对于集合中标签的数量)就足以避免此错误。

输入出现问题

原因:你的输入中有 nan你应该期望: 一旦学习过程“碰到”这个有问题的输入,输出就会变成 nan。查看运行时日志,你可能不会注意到任何异常情况:损失逐渐减小,突然出现了一个 nan你能做什么: 重新构建你的输入数据集(lmdb/leveldn/hdf5...),确保你的训练/验证集中没有坏的图像文件。为了调试,可以建立一个简单的网络,读取输入层,在其顶部具有虚拟损失,并通过所有输入数据:如果其中一个是有问题的,则这个虚拟网络也应该产生 nan

"Pooling" 层的步长大于内核大小

某些情况下,选择池化的 stride > kernel_size 可能导致出现 nan。例如:
layer {
  name: "faulty_pooling"
  type: "Pooling"
  bottom: "x"
  top: "y"
  pooling_param {
    pool: AVE
    stride: 5
    kernel: 3
  }
}

y 中出现 nan 值的结果。


"BatchNorm" 中的不稳定性

有报告称,在某些设置下,"BatchNorm" 层可能由于数值不稳定性而输出 nan
这个问题在 bvlc/caffe 中被提出,PR#5136 正试图解决它。


最近,我了解到 debug_info 标志:在 'solver.prototxt' 中设置 debug_info: true 将使 caffe 在训练期间记录更多调试信息(包括梯度大小和激活值)打印到日志中:这些信息可以帮助发现训练过程中的梯度爆炸和其他问题


谢谢,如何解释这些数字?这些数字是什么意思?http://pastebin.com/DLYgXK5v 为什么每个层输出只有一个数字!? 这些数字应该长什么样,以便有人知道是否存在问题或没有问题!? - Hossein
@Hossein 这正是这篇帖子的全部内容。 - Shai
谢谢你的回答。我的图像分割应用程序使用DICE损失进行训练,但仍获得了NAN损失(即使添加了小的epsilon/平滑常数)。我的数据集中包含一些图像,其对应的标签没有包含前景标签,当我从训练集中删除这些图像时,损失就稳定了。我不确定原因是什么? - samra irshad
@samrairshad 你尝试过增加DICE损失函数中的epsilon值吗? - Shai
是的,我这样做了。我打开了Stack Overflow上的帖子,并粘贴了一些时期的损失演变情况。这是参考链接:https://dev59.com/TE0GtIcB2Jgan1znxHaQ?noredirect=1#comment110115202_62259112 - samra irshad
数学直觉在这里解释:https://stats.stackexchange.com/q/326029 - HARSH NILESH PATHAK

6
在我的情况下,卷积/反卷积层中没有设置偏置是原因。
解决方案:在卷积层参数中添加以下内容。
bias_filler {
      type: "constant"
      value: 0
    }

那在MatConvNet中会是什么样子呢?我有类似于'biases'.init_bias*ones(1,4,single)的东西。 - h612

4
这篇回答不是关于导致nan的原因,而是提出了一种帮助调试的方法。 你可以使用以下Python代码层:
class checkFiniteLayer(caffe.Layer):
  def setup(self, bottom, top):
    self.prefix = self.param_str
  def reshape(self, bottom, top):
    pass
  def forward(self, bottom, top):
    for i in xrange(len(bottom)):
      isbad = np.sum(1-np.isfinite(bottom[i].data[...]))
      if isbad>0:
        raise Exception("checkFiniteLayer: %s forward pass bottom %d has %.2f%% non-finite elements" %
                        (self.prefix,i,100*float(isbad)/bottom[i].count))
  def backward(self, top, propagate_down, bottom):
    for i in xrange(len(top)):
      if not propagate_down[i]:
        continue
      isf = np.sum(1-np.isfinite(top[i].diff[...]))
        if isf>0:
          raise Exception("checkFiniteLayer: %s backward pass top %d has %.2f%% non-finite elements" %
                          (self.prefix,i,100*float(isf)/top[i].count))

在你怀疑可能会引起麻烦的某些点将此层添加到train_val.prototxt中:

layer {
  type: "Python"
  name: "check_loss"
  bottom: "fc2"
  top: "fc2"  # "in-place" layer
  python_param {
    module: "/path/to/python/file/check_finite_layer.py" # must be in $PYTHONPATH
    layer: "checkFiniteLayer"
    param_str: "prefix-check_loss" # string for printouts
  }
}

3

如果你遭遇与我一样的问题,这里有一个解决方案——

我在建立一个使用float16数据类型的网络时遇到了nan或inf的损失。在所有其他方法都失败后,我想到了切换回float32,结果nan的损失问题得到了解决!

因此,底线是:如果你将dtype切换为float16,请将其改回float32。


2

学习率过高,应该降低。 在RNN代码中,准确性为nan,选择较低的学习率可以解决这个问题。


0

我尝试构建一个稀疏自编码器,并在其中引入了多层以诱导稀疏性。在运行网络时,我遇到了NaN的问题。在删除一些层(在我的情况下,实际上必须删除1个)之后,我发现NaN消失了。因此,我想太多的稀疏性也可能会导致NaN的出现(一些0/0计算可能已被调用!?)


你能具体一些吗?您能提供有关配置的详细信息,其中包含“nan”和固定配置的信息吗?是什么类型的层?有哪些参数? - Shai
1
@shai 我使用了几个InnerProduct层(lr_mult 1,decay_mult 1,lr_mult 2,decay_mult 0,xavier,std:0.01),每个层后面都跟着ReLU(除了最后一个)。我正在处理MNIST数据集,如果我没记错的话,架构是784->1000->500->250->100->30(还有对称的解码器阶段);删除30层以及它的ReLU可以消除NaN。 - LKB

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接