【问题标题】:Deep-Learning Nan loss reasons深度学习难的原因
【发布时间】:2017-02-24 07:30:33
【问题描述】:

也许这个问题太笼统了,但谁能解释一下什么会导致卷积神经网络发散?

具体说明:

我正在使用 Tensorflow 的 iris_training 模型和我自己的一些数据并不断获得

ERROR:tensorflow:Model 与 loss = NaN 发生分歧。

追溯...

tensorflow.contrib.learn.python.learn.monitors.NanLossDuringTrainingError:训练期间的 NaN 损失。

追溯起源于以下行:

 tf.contrib.learn.DNNClassifier(feature_columns=feature_columns,
                                        hidden_units=[300, 300, 300],
                                        #optimizer=tf.train.ProximalAdagradOptimizer(learning_rate=0.001, l1_regularization_strength=0.00001),                                                          
                                        n_classes=11,
                                        model_dir="/tmp/iris_model")

我尝试调整优化器,使用零作为学习率,并且不使用优化器。感谢您对网络层、数据大小等的任何见解。

【问题讨论】:

  • 这是随机梯度下降的自然属性,如果学习率太大,SGD会发散到无穷大
  • @YaroslavBulatov 我已经尝试过学习率约为 1E-15 的 AdagradOptiizer。也许我的数据不适合 SGD,你能推荐另一种算法吗?对 Tensorflow 和深度学习来说还是新手。
  • 在我的情况下,标准化有帮助
  • 我的解决方案是使用tf.losses.sparse_softmax_cross_entropy(y, logits),而不是我自己使用tf.nn.Softmax实现的Safe Softmax

标签: python tensorflow machine-learning keras theano


【解决方案1】:

我见过很多事情使模型出现分歧。

  1. 学习率太高。如果损失开始增加然后发散到无穷大,您通常可以判断是否是这种情况。

  2. 我不熟悉 DNNClassifier,但我猜它使用分类交叉熵成本函数。这涉及获取随着预测接近零而发散的预测的对数。这就是为什么人们通常在预测中添加一个小的 epsilon 值来防止这种分歧。我猜 DNNClassifier 可能会这样做或使用 tensorflow opp。可能不是问题。

  3. 可能存在其他数值稳定性问题,例如除以零,添加 epsilon 会有所帮助。另一个不太明显的问题是,在处理有限精度数时,如果没有适当简化,其导数的平方根可能会发散。我再次怀疑这是 DNNClassifier 的问题。

  4. 您的输入数据可能有问题。尝试在输入数据上调用 assert not np.any(np.isnan(x)) 以确保您没有引入 nan。还要确保所有目标值都有效。最后,确保数据正确标准化。您可能希望像素在 [-1, 1] 而不是 [0, 255] 范围内。

  5. 标签必须在损失函数的域中,因此如果使用基于对数的损失函数,所有标签都必须是非负的(如 evan pu 和下面的 cmets 所述)。

【讨论】:

  • 感谢您的分析。我的问题是我的标签围绕零对称(即 [-5,...,5])。换班解决了问题。
  • 标签应该是二进制的。 1 或 0。否则分类交叉熵成本函数将没有意义。
  • tf.keras.utils.normalize(data) 有助于规范化数据。
  • “二进制”表示它们应该是单热编码的,即第一类示例的向量 (1,0,0,....,0),(0, 1,0,....0) 用于第二类的示例, (0,....,0,1) 用于最后一类的示例。输出节点的数量应该与你拥有的类的数量相同。
  • 你是我的英雄!当我用另一个数据集尝试线性回归示例 (toptal.com/machine-learning/…) 时,比如从摄氏度到华氏度,我得到了 W, b, loss all 'nan'。但是按照您的回答后,我将 learning_rate = 0.01 更改为 learning_rate = 0.001,然后一切正常!
【解决方案2】:

如果使用整数作为目标,请确保它们在 0 处不对称。

即,不要使用类 -1、0、1。改用 0、1、2。

【讨论】:

  • 您愿意评论一下原因或引用完成的参考吗?
  • @gsimard 老实说,我不记得了,因为我之前曾使用过这个。
  • @gsimard,这是因为接受答案中的原因 5。基于逻辑的回归函数通常使用对数,对数只定义在非负数上
  • @Zroach 不,在我的情况下支持负数,但它不起作用的原因是在 0 处特别对称。
【解决方案3】:

如果您正在训练交叉熵,您希望在输出概率中添加一个小数字,例如 1e-8。

因为 log(0) 是负无穷大,当你的模型训练得足够多时,输出分布会非常偏斜,例如说我正在做一个 4 类输出,一开始我的概率看起来像

0.25 0.25 0.25 0.25

但到最后,概率可能看起来像

1.0 0 0 0

如果你取这个分布的交叉熵,一切都会爆炸。解决方法是人为地在所有条款中添加一个小数字以防止这种情况发生。

【讨论】:

  • 我使用了 keras 的 categorical_crossentropy 损失函数,它已经实现了吗?
  • @StayFoolish 我不确定,逃避的答案是查看他们的源代码,但我敢打赌他们已经在他们的代码中处理了这一点。我会试试看,很可能你没事。
  • 而且我假设 16 位精度将比 32 位精度面临这个问题?
【解决方案4】:

如果您想收集有关错误的更多信息,并且如果错误发生在最初的几次迭代中,我建议您在仅 CPU 模式(无 GPU)下运行实验。错误消息将更加具体。

来源:https://github.com/tensorflow/tensor2tensor/issues/574

【讨论】:

    【解决方案5】:

    在我的情况下,我在设置远距离整数标签时得到了 NAN。即:

    • 标签 [0..100] 训练正常,
    • 标签 [0..100] 加上一个额外的标签 8000,然后我得到了 NAN。

    所以,不要使用距离很远的标签。

    编辑 如下简单代码即可看到效果:

    from keras.models import Sequential
    from keras.layers import Dense, Activation
    import numpy as np
    
    X=np.random.random(size=(20,5))
    y=np.random.randint(0,high=5, size=(20,1))
    
    model = Sequential([
                Dense(10, input_dim=X.shape[1]),
                Activation('relu'),
                Dense(5),
                Activation('softmax')
                ])
    model.compile(optimizer = "Adam", loss = "sparse_categorical_crossentropy", metrics = ["accuracy"] )
    
    print('fit model with labels in range 0..5')
    history = model.fit(X, y, epochs= 5 )
    
    X = np.vstack( (X, np.random.random(size=(1,5))))
    y = np.vstack( ( y, [[8000]]))
    print('fit model with labels in range 0..5 plus 8000')
    history = model.fit(X, y, epochs= 5 )
    

    结果显示添加标签 8000 后的 NAN:

    fit model with labels in range 0..5
    Epoch 1/5
    20/20 [==============================] - 0s 25ms/step - loss: 1.8345 - acc: 0.1500
    Epoch 2/5
    20/20 [==============================] - 0s 150us/step - loss: 1.8312 - acc: 0.1500
    Epoch 3/5
    20/20 [==============================] - 0s 151us/step - loss: 1.8273 - acc: 0.1500
    Epoch 4/5
    20/20 [==============================] - 0s 198us/step - loss: 1.8233 - acc: 0.1500
    Epoch 5/5
    20/20 [==============================] - 0s 151us/step - loss: 1.8192 - acc: 0.1500
    fit model with labels in range 0..5 plus 8000
    Epoch 1/5
    21/21 [==============================] - 0s 142us/step - loss: nan - acc: 0.1429
    Epoch 2/5
    21/21 [==============================] - 0s 238us/step - loss: nan - acc: 0.2381
    Epoch 3/5
    21/21 [==============================] - 0s 191us/step - loss: nan - acc: 0.2381
    Epoch 4/5
    21/21 [==============================] - 0s 191us/step - loss: nan - acc: 0.2381
    Epoch 5/5
    21/21 [==============================] - 0s 188us/step - loss: nan - acc: 0.2381
    

    【讨论】:

    • 有趣。我认为这取决于您的损失函数。您能否具体说明您是如何衡量损失的?
    • 我使用的是'sparse_categorical_crossentropy'
    • 这也是我的问题的解决方案,谢谢!
    【解决方案6】:

    正则化可以提供帮助。对于分类器,活动正则化是一个很好的案例,无论是二元分类器还是多类分类器。对于回归器,核正则化可能更合适。

    【讨论】:

      【解决方案7】:

      我想补充一些我经历过的(肤浅的)原因如下:

      1. 我们可能已经更新了我们的字典(用于 NLP 任务),但模型和准备好的数据使用了不同的字典。
      2. 我们可能已经重新处理了我们的数据(二进制 tf_record),但我们加载了旧模型。重新处理的数据可能与之前的数据冲突 一。
      3. 我们可能应该从头开始训练模型,但我们忘记删除检查点,模型会自动加载最新的参数。

      希望对您有所帮助。

      【讨论】:

        【解决方案8】:

        naninf-inf 的原因通常是因为 TensorFlow 中的division by 0.0 不会导致除以零异常。它可能导致naninf-inf“值”。在您的训练数据中,您可能有0.0,因此在您的损失函数中,您可能会执行division by 0.0

        a = tf.constant([2., 0., -2.])
        b = tf.constant([0., 0., 0.])
        c = tf.constant([1., 1., 1.])
        print((a / b) + c)
        

        输出是以下张量:

        tf.Tensor([ inf  nan -inf], shape=(3,), dtype=float32)
        

        添加一个小的eplison(例如1e-5)通常可以解决问题。此外,从 TensorFlow 2 开始,定义了 tf.math.division_no_nan 选项。

        【讨论】:

          【解决方案9】:

          虽然大部分要点已经讨论过了。但我想再次强调 NaN 缺失的另一个原因。

          tf.estimator.DNNClassifier(
              hidden_units, feature_columns, model_dir=None, n_classes=2, weight_column=None,
              label_vocabulary=None, optimizer='Adagrad', activation_fn=tf.nn.relu,
              dropout=None, config=None, warm_start_from=None,
              loss_reduction=losses_utils.ReductionV2.SUM_OVER_BATCH_SIZE, batch_norm=False
          )
          

          默认激活函数是“Relu”。有可能中间层生成负值,“Relu”将其转换为 0。这会逐渐停止训练。

          我观察到“LeakyRelu”能够解决这些问题。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2018-11-05
            • 1970-01-01
            • 2021-02-25
            • 2020-06-30
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多