Deep-Learning Nan损失原因
Deep-Learning Nan loss reasons
也许这个问题太笼统了,但是谁能解释什么会导致卷积神经网络发散?
细节:
我正在使用 Tensorflow 的 iris_training 模型和我自己的一些数据,并不断获得
ERROR:tensorflow:Model diverged with loss = NaN.
Traceback...
tensorflow.contrib.learn.python.learn.monitors.NanLossDuringTrainingError: NaN loss during training.
追溯源自行:
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")
我试过调整优化器,使用零学习率,不使用优化器。对网络层、数据大小等的任何见解表示赞赏。
我看到有很多东西会导致模型发散。
学习率过高。如果损失开始增加然后发散到无穷大,您通常可以判断是否属于这种情况。
我不熟悉 DNNClassifier,但我猜它使用分类交叉熵成本函数。这涉及取预测的对数,随着预测接近零而发散。这就是为什么人们通常会在预测中添加一个小的 epsilon 值来防止这种分歧。我猜 DNNClassifier 可能会这样做或为此使用 tensorflow opp。可能不是问题。
可能存在其他数值稳定性问题,例如被零除时添加 epsilon 会有所帮助。如果在处理有限精度数字时没有适当简化,导数的平方根可能会发散,这是另一个不太明显的问题。我再次怀疑这是 DNNClassifier 的问题。
您输入的数据可能有问题。尝试对输入数据调用 assert not np.any(np.isnan(x))
以确保您没有引入 nan。还要确保所有目标值都有效。最后,确保数据已正确规范化。您可能希望像素在 [-1, 1] 而不是 [0, 255] 范围内。
标签必须在损失函数的范围内,因此如果使用基于对数的损失函数,所有标签都必须是非负的(如 evan pu 和下面的评论所述)。
如果使用整数作为目标,请确保它们在 0 处不对称。
即,不要使用 类 -1、0、1。请改用 0、1、2。
如果你正在训练交叉熵,你想在你的输出概率中添加一个像 1e-8 这样的小数字。
因为 log(0) 是负无穷大,当你的模型训练得足够多时,输出分布会非常倾斜,例如说我正在做 4 class 输出,一开始我的概率看起来像
0.25 0.25 0.25 0.25
但最后的概率可能看起来像
1.0 0 0 0
然后你取这个分布的交叉熵,一切都会爆炸。解决方法是人为地向所有术语添加一个小数字以防止出现这种情况。
如果您想收集有关错误的更多信息,并且如果错误发生在前几次迭代中,我建议您运行在仅 CPU 模式下进行实验(无 GPU ).错误消息将更加具体。
在我的例子中,我在设置远程整数标签时得到了 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后的NANs:
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
正则化可以提供帮助。对于 classifier,activity 正则化是一个很好的案例,无论它是二进制还是多 class classifier。对于回归器,内核正则化可能更合适。
我想补充一些我经历过的(肤浅的)原因如下:
- 我们可能已经更新了字典(用于 NLP 任务),但模型和准备好的数据使用了不同的字典。
- 我们可能重新处理了我们的数据(二进制 tf_record),但我们加载了旧模型。重新处理的数据可能与之前的冲突
一。
- 我们可能应该从头开始训练模型,但我们忘记删除检查点,模型自动加载了最新参数。
希望对您有所帮助。
nan
、inf
或 -inf
的原因通常是因为 TensorFlow 中的 division by 0.0
不会导致被零除异常。它可能导致 nan
、inf
或 -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
。
虽然大部分的点都已经讨论过了。但我想再次强调缺少 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”能够解决此类问题。
我在解决这个问题时发现了一些有趣的事情,除了上面的答案,当你的数据标签像下面这样排列时,对数据应用随机播放可能会有所帮助:
y=[0,0,0,0,0,0,0....,0,0,0,1,1,1,1,1....,1,1,1,1,1,1,1,2,2,2,2,2,......,2,2,2,2,2]
from sklearn.utils import shuffle
x, y = shuffle(x, y)
我遇到了同样的问题。我的标签是享受等级 [1, 3, 5]。我阅读了所有答案,但它们对我面临的问题没有多大意义。我将标签更改为 [0 1 2] 并且有效。不知道这是怎么回事。
TensorFlow 在某些情况下使用标签作为张量中的位置,因此它们必须是 0、1、...、L-1。负数,non-integers 等会导致损失为 NaN。
也许这个问题太笼统了,但是谁能解释什么会导致卷积神经网络发散?
细节:
我正在使用 Tensorflow 的 iris_training 模型和我自己的一些数据,并不断获得
ERROR:tensorflow:Model diverged with loss = NaN.
Traceback...
tensorflow.contrib.learn.python.learn.monitors.NanLossDuringTrainingError: NaN loss during training.
追溯源自行:
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")
我试过调整优化器,使用零学习率,不使用优化器。对网络层、数据大小等的任何见解表示赞赏。
我看到有很多东西会导致模型发散。
学习率过高。如果损失开始增加然后发散到无穷大,您通常可以判断是否属于这种情况。
我不熟悉 DNNClassifier,但我猜它使用分类交叉熵成本函数。这涉及取预测的对数,随着预测接近零而发散。这就是为什么人们通常会在预测中添加一个小的 epsilon 值来防止这种分歧。我猜 DNNClassifier 可能会这样做或为此使用 tensorflow opp。可能不是问题。
可能存在其他数值稳定性问题,例如被零除时添加 epsilon 会有所帮助。如果在处理有限精度数字时没有适当简化,导数的平方根可能会发散,这是另一个不太明显的问题。我再次怀疑这是 DNNClassifier 的问题。
您输入的数据可能有问题。尝试对输入数据调用
assert not np.any(np.isnan(x))
以确保您没有引入 nan。还要确保所有目标值都有效。最后,确保数据已正确规范化。您可能希望像素在 [-1, 1] 而不是 [0, 255] 范围内。标签必须在损失函数的范围内,因此如果使用基于对数的损失函数,所有标签都必须是非负的(如 evan pu 和下面的评论所述)。
如果使用整数作为目标,请确保它们在 0 处不对称。
即,不要使用 类 -1、0、1。请改用 0、1、2。
如果你正在训练交叉熵,你想在你的输出概率中添加一个像 1e-8 这样的小数字。
因为 log(0) 是负无穷大,当你的模型训练得足够多时,输出分布会非常倾斜,例如说我正在做 4 class 输出,一开始我的概率看起来像
0.25 0.25 0.25 0.25
但最后的概率可能看起来像
1.0 0 0 0
然后你取这个分布的交叉熵,一切都会爆炸。解决方法是人为地向所有术语添加一个小数字以防止出现这种情况。
如果您想收集有关错误的更多信息,并且如果错误发生在前几次迭代中,我建议您运行在仅 CPU 模式下进行实验(无 GPU ).错误消息将更加具体。
在我的例子中,我在设置远程整数标签时得到了 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后的NANs:
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
正则化可以提供帮助。对于 classifier,activity 正则化是一个很好的案例,无论它是二进制还是多 class classifier。对于回归器,内核正则化可能更合适。
我想补充一些我经历过的(肤浅的)原因如下:
- 我们可能已经更新了字典(用于 NLP 任务),但模型和准备好的数据使用了不同的字典。
- 我们可能重新处理了我们的数据(二进制 tf_record),但我们加载了旧模型。重新处理的数据可能与之前的冲突 一。
- 我们可能应该从头开始训练模型,但我们忘记删除检查点,模型自动加载了最新参数。
希望对您有所帮助。
nan
、inf
或 -inf
的原因通常是因为 TensorFlow 中的 division by 0.0
不会导致被零除异常。它可能导致 nan
、inf
或 -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
。
虽然大部分的点都已经讨论过了。但我想再次强调缺少 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”能够解决此类问题。
我在解决这个问题时发现了一些有趣的事情,除了上面的答案,当你的数据标签像下面这样排列时,对数据应用随机播放可能会有所帮助:
y=[0,0,0,0,0,0,0....,0,0,0,1,1,1,1,1....,1,1,1,1,1,1,1,2,2,2,2,2,......,2,2,2,2,2]
from sklearn.utils import shuffle
x, y = shuffle(x, y)
我遇到了同样的问题。我的标签是享受等级 [1, 3, 5]。我阅读了所有答案,但它们对我面临的问题没有多大意义。我将标签更改为 [0 1 2] 并且有效。不知道这是怎么回事。
TensorFlow 在某些情况下使用标签作为张量中的位置,因此它们必须是 0、1、...、L-1。负数,non-integers 等会导致损失为 NaN。