在深度學習中Loss出現Nan的可能原因有哪些?
也許這個問題太籠統,但是誰能大致解釋下導致卷積神經網絡發散的原因呢?
問題細節:
我正在使用Tensorflow的iris_training模型以及一些我自己的數據,老是出現以下報錯:
ERROR:tensorflow:Model diverged with loss = NaN.
Traceback…
tensorflow.contrib.learn.python.learn.monitors.NanLossDuringTrainingError: NaN loss during training.
Traceback問題源於以下行:
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")
我嘗試過調整優化器,將學習率設置為零,並且不使用優化器,然並卵。
最佳回答
常見的導致模型發散(Nan問題)原因:
- 學習率太高。損失是否開始增加然後發散到無窮大。
- 我對DNNClassifier不熟悉,但是我猜想它使用了分類交叉熵代價函數。這涉及獲取預測的對數,該對數隨著預測接近零而發散。這就是為什麽人們通常在預測中添加較小的ε值以防止這種差異。我猜測DNNClassifier可能會這樣做或使用tensorflow opp。
- 也可能存在其他數值穩定性問題,例如零除,在其中添加epsilon可能會有所幫助。一種不那麽明顯的情況是,如果在處理有限精度數時未適當簡化,則導數的平方根可能發散。我再次懷疑這是DNNClassifier的問題。
- 還可能是輸入數據有問題。嘗試對輸入數據調用
assert not np.any(np.isnan(x))
,以確保您沒有引入nan。還要確保所有目標值(樣本Label)均有效。最後,確保數據正確歸一化。您可能希望像素在[-1,1]而不是[0,255]範圍內。 - 另外,標簽(樣本Label)必須在損失函數值的域(範圍)中。例如,如果使用基於對數的損失函數,則所有標簽都必須是非負的。
次佳回答
如果您正在訓練交叉熵,則需要在輸出概率中添加一個小數,例如1e-8。
因為log(0)是負無窮大,所以當您的模型經過足夠的訓練後,輸出分布將非常偏斜,例如說我正在執行4類輸出,一開始我的概率類似下麵這樣:
0.25 0.25 0.25 0.25
但到最後,可能看起來像
1.0 0 0 0
如果
對該分布進行交叉熵分解,問題就來了。解決方法是人為地在所有項中添加很小的數字,以防止出現這種情況。
第三種回答
調試NaN可能很棘手,尤其是在您擁有大型網絡的情況下。可以用 tf.add_check_numerics_ops()
將ops添加到圖中,以聲明圖中的每個浮點張量不包含任何NaN值(默認情況下是不運行這些檢查的)。參考:tensorflow Nan調試
train_op = ...
check_op = tf.add_check_numerics_ops()
sess = tf.Session()
sess.run([train_op, check_op]) # Runs training and checks for NaNs
第四種回答
就我而言,設置遠距離整數LABEL時得到了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
第五種回答
如果使用整數作為目標,請確保它們不對稱於0。
即,請勿使用-1、0、1類。而應使用0、1、2類。
參考資料