在训练回归网络时出现NaN损失

129

我有一个大小为260,000行35列的“one-hot编码”(全是1和0)的数据矩阵。我使用Keras训练一个简单的神经网络来预测一个连续变量。制作网络的代码如下:

model = Sequential()
model.add(Dense(1024, input_shape=(n_train,)))
model.add(Activation('relu'))
model.add(Dropout(0.1))

model.add(Dense(512))
model.add(Activation('relu'))
model.add(Dropout(0.1))

model.add(Dense(256))
model.add(Activation('relu'))
model.add(Dropout(0.1))
model.add(Dense(1))

sgd = SGD(lr=0.01, nesterov=True);
#rms = RMSprop()
#model.compile(loss='categorical_crossentropy', optimizer=rms, metrics=['accuracy'])
model.compile(loss='mean_absolute_error', optimizer=sgd)
model.fit(X_train, Y_train, batch_size=32, nb_epoch=3, verbose=1, validation_data=(X_test,Y_test), callbacks=[EarlyStopping(monitor='val_loss', patience=4)] )

然而,在训练过程中,我看到损失值很好地降低,但在第二个 epoch 的中间,它变成了 nan:

Train on 260000 samples, validate on 64905 samples
Epoch 1/3
260000/260000 [==============================] - 254s - loss: 16.2775 - val_loss:
 13.4925
Epoch 2/3
 88448/260000 [=========>....................] - ETA: 161s - loss: nan

我尝试使用RMSProp替代 SGD,使用tanh替代relu,使用和不使用Dropout,但都没有成功。我尝试使用一个更小的模型,即只有一个隐藏层,但问题依旧(它在不同的时间变为nan)。然而,当特征较少时,例如只有5个列,它会正常工作并且给出相当好的预测结果。似乎存在某种溢出问题,但我无法想象原因——损失并不是不合理的大。

运行在CPU上的Linux机器上的Python版本为2.7.11。我用最新版本的Theano进行了测试,也遇到Nan问题,所以我尝试转向Theano 0.8.2并面临同样的问题。最新版本的Keras也有同样的问题,0.3.2版本也是如此。


1
尝试使用单个隐藏层,设置 loss='mean_squared_error',optimizer='adam' - 仍然会出现 NaN 吗? - 1''
当使用Adam优化器与上述模型时,我得到了nans。只有一个层时,在三个训练时期中不会出现nans。 - The_Anomaly
对于未来的读者,这里有一个相关的Keras线程。https://github.com/keras-team/keras/issues/2134 我通过结合这里提到的所有建议取得了一些成功。例如添加批量归一化、变化学习率、优化器、添加clip_by_value、clip_by_global_norm,最后,多次检查代码中的错误也有所帮助,例如在一个卷积层后缺少批量归一化层。 :) - pangyuteng
检查NAN值,它解决了我的问题... :) - Krishna vamshi
27个回答

178

使用神经网络进行回归很难得到有效结果,因为输出没有上限,所以您特别容易遇到梯度爆炸问题(导致NaN的可能原因)。

历史上,解决梯度爆炸问题的一个关键方法是降低学习率。但是随着Adam等基于每个参数自适应学习率算法的出现,您不再需要设置学习率即可获得良好的性能。除非您是神经网络专家并知道如何调整学习计划,否则几乎没有理由再使用带有动量的SGD。

以下是您可能尝试的一些方法:

  1. 通过分位数标准化z-score标准化来规范化输出。严谨起见,请在训练数据上计算此转换,而不是在整个数据集上计算。例如,使用分位数标准化,如果一个示例位于训练集的第60个百分位,则其值为0.6。(您还可以将分位数标准化值向下移动0.5,使第0个百分位为-0.5,第100个百分位为+0.5)。

  2. 增加正则化,可以通过增加dropout率或向权重添加L1和L2惩罚项来实现。 L1正则化类似于特征选择,并且由于您表示将特征数量降到5可以得到良好的性能,因此L1可能也有效。

  3. 如果这些方法仍然没有帮助,请减小神经网络的大小。这不总是最佳方法,因为它可能会损害性能,但在您的情况下,第一层神经元(1024)相对于输入特征(35)有很多,所以这样做可能会有所帮助。

  4. 将批次大小从32增加到128。128是相当标准的,可能会增加优化的稳定性。


4
关于1.为什么不对整个输出集进行归一化?另外,我可以使用缩放吗? - Eran
14
如果在决定如何进行规范化时使用整个数据集(包括训练集和测试集),则间接地将测试集的信息纳入训练集中,这是一种训练-测试交叉污染。只要在决定如何规范化时只使用训练集,您就可以使用缩放或任何其他良好性能规范化的方法。 - 1''
我的批量大小原则是尽可能大,但最多不超过观测值数量的1%。 1%将为您提供100个随机批次,这意味着您仍然具有随机梯度下降的随机部分。 - grofte
据我所知,使用“adam”优化器时,您不需要手动设置lr作为参数。 - JeeyCi
增加批量大小为什么会解决NaN问题? - Fang WU
显示剩余3条评论

66

1"的回答相当不错,但是所有的修复似乎都是间接修复问题而非直接修复。我建议使用梯度裁剪,这将裁剪任何超过一定值的梯度。

在Keras中,您可以使用clipnorm = 1(请参见https://keras.io/optimizers/)来简单地剪辑所有范数大于1的梯度。


17
有道理!这是一个完全合法的策略,例如在循环神经网络中经常使用。但是,在采用此方法之前,始终要检查优化是否存在简单的错误。 - 1''
同样的Keras链接表明梯度裁剪不再受支持。是否有类似的解决方案? - NeStack
这对所有优化器都有效吗?而且将其设置为1.0总是一个好主意吗? - CMCDragonkai
是的,它应该可以跨优化器工作。如果您的优化问题足够简单/稳定,则不需要此功能,可能会在不产生任何好处的情况下稍微减慢训练速度。 - pir
1
根据我的经验,即使你使用clipnorm,如果不对数据进行缩放,仍然可能出现梯度爆炸的情况。如果没有看到Input(...)步骤中输入数据的形式,很难知道这是否是解决方案。我并不是说这种方法是错误的,但我可以想象有人会认为,如果他们得到了nan损失,这将解决他们的问题,但实际上可能并非如此。 - philosofool

57

我之前遇到过同样的问题。我搜索并找到了这个问题和答案。上面提到的所有技巧都对于训练深度神经网络非常重要。我尝试了它们所有的方法,但仍然得到NAN。

我也在这里找到了这个问题:https://github.com/fchollet/keras/issues/2134。我引用作者的结论如下:

我想指出这一点,以便为将来可能遇到此问题的其他人存档。当我的损失函数进入训练过程的某个时候突然返回nan时,我遇到了这个问题。我检查了relus、优化器、损失函数、根据relus进行的dropout、网络的大小和网络的形状。但我仍然得到了最终变成nan的损失,并感到相当沮丧。

然后我想起来了。我可能有一些坏的输入。事实证明,我输入给CNN的图像(并进行平均归一化)之一只是0。当我减去平均值并通过标准偏差进行归一化时,我没有检查这种情况,因此我最终得到了一个示例矩阵,其中只有nan。一旦我修复了我的归一化函数,我的网络现在可以完美地训练。

我同意上述观点:输入对于您的网络很敏感。在我的情况中,我使用密度估计的对数值作为输入。绝对值可能非常巨大,经过几步梯度后可能导致NaN。我认为检查输入是必要的。首先,您应该确保输入不包含-inf或inf,或者绝对值非常大的数字。


3
我和你遇到了同样的问题。在检查我的数据时,我发现有多个地方存在无穷大数据点。将它们删除后问题得到了解决。 - troymyname00
1
这对我解决了问题,我的嵌入矩阵中有多个NaN :) 谢谢。 - Aldo Canepa
我将输入图像(png)从0-255(uint8)缩放到0.-1.(float32),从未想过输入是罪魁祸首....在将输入传递给网络进行训练之前添加tf.clip_by_value似乎解决了我长达9个月的调试之旅... - pangyuteng
2
另外,请注意 np.isnan(np.inf) == False。为确保您的示例不包含 NaN 或 Inf,您可以执行类似于 assert np.all(np.isfinite(X)) 的操作。(这个问题曾经让我犯了好几次错:我以为我的数据没问题,因为我检查了 NaN。但我忘记了 np.isnan 无法发现 Inf!) - Jack Kelly
我遇到的问题正是这个:有时候,我们只是错过了显而易见的东西。令人惊讶的是,一个简单的 dropna() 可以实现很多。 - Shahar
显示剩余3条评论

24

我在使用LSTM时遇到了同样的问题,问题是我的数据在标准化后有一些nan值,因此,在标准化之后应该检查输入模型数据是否存在NaN值:

print(np.any(np.isnan(X_test)))
print(np.any(np.isnan(y_test)))

你可以通过像这样向Std添加一个小值(0.000001)来解决此问题:

def standardize(train, test):


    mean = np.mean(train, axis=0)
    std = np.std(train, axis=0)+0.000001

    X_train = (train - mean) / std
    X_test = (test - mean) /std
    return X_train, X_test

19

总结下面提到的不同解决方案以及来自Github讨论的内容,当然这取决于您特定的情况:

  • 添加正则化以添加l1或l2惩罚项到权重中。否则,尝试更小的l2 reg。例如l2(0.001),如果已经存在,则删除它。
  • 尝试较小的Dropout率。
  • 剪切梯度以防止其爆炸。例如,在Keras中,您可以使用clipnorm=1.或clipvalue=1.作为优化器参数。
  • 检查输入的有效性(没有NaN或有时为0)。例如df.isnull().any()
  • 将优化器替换为更易处理的Adam。有时也将sgd替换为rmsprop会有所帮助。
  • 使用RMSProp进行严格的正则化以防止梯度爆炸。
  • 尝试标准化数据,或者检查标准化过程是否引入了任何不良值。
  • 确认您正在使用正确的激活函数(例如,对于多类分类,使用softmax而不是sigmoid)。
  • 尝试增加批量大小(例如32到64或128),以增加优化稳定性。
  • 尝试降低学习率。
  • 检查最后一批的大小,可能与批量大小不同。

请注意:如果批量大小过大,您可能会陷入局部最小值。 - JeeyCi
拥有不同大小的最后一批会造成什么问题? - roygbiv
另一个原因是使用混合精度,这将强制使用float16与float32进行比较。我还遇到了mean_absolute_error的NaN问题,尝试了上面所有的解决方案,然后发现我仍然在使用混合精度,这是因为复制了一个Jupyter笔记本。 - ronaldmathies
去除L2正则化解决了我的NaN验证损失问题。但是为什么呢? - CyberPlayerOne

15

我遇到了非常类似的问题,以下是我解决它的方法。

你可以尝试将激活函数从Relu或Tanh改为LeakyReLU。原因是在许多层中,很多节点的激活值为零,由于梯度也是零,反向传播不会更新这些节点的权重。这也被称为“死亡ReLU”问题(你可以在这里阅读更多信息:https://datascience.stackexchange.com/questions/5706/what-is-the-dying-relu-problem-in-neural-networks)。

要实现这一点,你可以使用以下代码导入LeakyReLU激活函数:

from keras.layers.advanced_activations import LeakyReLU
并将其嵌入您的层中,如下所示:
model.add(Dense(800,input_shape=(num_inputs,)))
model.add(LeakyReLU(alpha=0.1))

此外,输出特征(您要预测的连续变量)可能是一个不平衡的数据集,并且有太多的0。解决这个问题的一种方法是使用平滑处理。您可以通过在此列中的所有值的分子上加1,并将此列中的每个值除以1 /(此列中所有值的平均值)来实现。

这实际上将所有值从0移动到大于0的值(可能仍然非常小)。这可以防止曲线预测0并最小化损失(最终使其变为NaN)。较小的值受到的影响比较大,但总体上,数据集的平均值保持不变。


10

我曾经遇到相同的问题,我正在使用Keras进行多元回归分析。后来我意识到我的数据集中有一些值是nan,这导致了损失值为nan。

我使用了以下命令:

df=df.dropna()

这解决了我的问题。


真的,我们提供给神经网络的数据中不应该有任何NaN值。 - Hemanth Kollipara

3

我在使用keras LSTM层的RNN时遇到了同样的问题,所以我尝试以上每种解决方案。我已经使用 sklearn.preprocessing.MinMaxScaler 缩放了我的数据,缩放后我的数据中没有 NaN 值。像使用LeakyRelU或改变学习率这样的解决方案都没有帮助。

因此,我决定将缩放器从MinMaxScaler更改为StandardScaler,即使我没有 NaN 值也觉得奇怪,但它起作用了!


3
我尝试了这个页面以及其他很多建议,但都没有成功。我们正在使用pandas导入csv文件,然后使用keras Tokenizer处理文本输入以创建词汇和词向量矩阵。突然有一天,我们发现某些CSV文件会导致NaN,而其他CSV文件却能正常工作,于是我们注意到文件的编码方式不同,ASCII文件使用keras时无法正常工作,导致损失为nan,准确度为0.0000e+00; 然而,utf-8和utf-16文件可以正常工作!这是一个重大突破。
如果您正在进行文本分析并在尝试这些建议后出现nan损失,请使用file -i {input}(Linux)或file -I {input}(OSX)来查看文件类型。如果您的文件类型为ISO-8859-1us-ascii,请尝试将其转换为utf-8utf-16le。我还没有尝试过后者,但我想它也应该可以正常工作。希望这能帮助到一些非常沮丧的人!

3

在训练开始时,我在第一个epoch中就遇到了损失为nan的问题。解决方法非常简单,只需从输入数据中删除nas即可(df.dropna())。

希望这能帮助遇到类似问题的人。


1
你是如何在第一个时期中去除NaN值的?我在开始训练之前就有NaN值。 - Supamee

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