梯度爆炸
原因:梯度變得非常大,使得學習過程難以繼續
現象:觀察log,注意每一輪迭代后的loss。loss隨着每輪迭代越來越大,最終超過了浮點型表示的范圍,就變成了NaN。
措施:
1. 減小solver.prototxt中的base_lr,至少減小一個數量級。如果有多個loss layer,需要找出哪個損失層導致了梯度爆炸,並在train_val.prototxt中減小該層的loss_weight,而非是減小通用的base_lr。
2. 設置clip gradient,用於限制過大的diff
不當的損失函數
原因:有時候損失層中loss的計算可能導致NaN的出現。比如,給InfogainLoss層(信息熵損失)輸入沒有歸一化的值,使用帶有bug的自定義損失層等等。
現象:觀測訓練產生的log時一開始並不能看到異常,loss也在逐步的降低,但突然之間NaN就出現了。
措施:看看你是否能重現這個錯誤,在loss layer中加入一些輸出以進行調試。
示例:有一次我使用的loss歸一化了batch中label錯誤的次數。如果某個label從未在batch中出現過,loss就會變成NaN。在這種情況下,可以用足夠大的batch來盡量避免這個錯誤。
不當的輸入
原因:輸入中就含有NaN。
現象:每當學習的過程中碰到這個錯誤的輸入,就會變成NaN。觀察log的時候也許不能察覺任何異常,loss逐步的降低,但突然間就變成NaN了。
措施:重整你的數據集,確保訓練集和驗證集里面沒有損壞的圖片。調試中你可以使用一個簡單的網絡來讀取輸入層,有一個缺省的loss,並過一遍所有輸入,如果其中有錯誤的輸入,這個缺省的層也會產生NaN。
案例:有一次公司需要訓練一個模型,把標注好的圖片放在了七牛上,拉下來的時候發生了dns劫持,有一張圖片被換成了淘寶的購物二維碼,且這個二維碼格式與原圖的格式不符合,因此成為了一張“損壞”圖片。每次訓練遇到這個圖片的時候就會產生NaN。良好的習慣是,你有一個檢測性的網絡,每次訓練目標網絡之前把所有的樣本在這個檢測性的網絡里面過一遍,去掉非法值。
池化層中步長比核的尺寸大
如下例所示,當池化層中stride > kernel的時候會在y中產生NaN
layer { name: "faulty_pooling" type: "Pooling" bottom: "x" top: "y" pooling_param { pool: AVE stride: 5 kernel: 3 } }