1. 問題分析
如圖,在開始訓練后, loss升高到87.3365后保持不變。這個問題是因為梯度爆炸導致的。
loss -= log(std::max(prob_data[i * dim + label_value * inner_num_ + j],
Dtype(FLT_MIN)));
在softmax_loss_layer.cpp的原碼中,loss的最大值由FLT_MIN得到,FLT_MIN定義為1.17549435E-38F,這個數字的自然對數正好就是
-87.3356,算loss時需要取負值,結果就能了87.3356。
這說明softmax計算得到概率值出現了零(由於float類型所能表示的最小數值是10−3810−38,比這個值還小的無法表示,只能是零)
而softmax是用指數函數計算的,指數函數的值都是大於零的。因此,我們有理由相信,計算過程中出現了float溢出等異常,出現了inf,nan等異常數值導致softmax輸出為零
最后我們發現,當softmax之前的feature值過大時,由於softmax先求指數,會超出float數據范圍,成為inf。inf與其他任何數值的和都是inf,softmax在做除法時任何正常范圍的數值除以inf都會變為0。然后求loss時log一下就出現了87.3356這樣的值。
2. 解決辦法
1. 降低學習率,提高batchsize,這樣就能減小權重參數的波動范圍,從而減小權重變大的可能性
2. 輸入歸一化
用均值文件歸一化
transform_param {
mirror: true
crop_size: 331
mean_file: "lmdb_data/img_test_lmdb/mean.binaryproto"
3. 查看輸入數據、標簽是否有異常,
solver.prototxt中設置,debug_info: true
標簽仔細看了,從0到60,總分類個數也改成了61
數據都是嚴格按照預處理lmdb來的。
4. 數據shuffle
已經在lmdb數據生成時進行了shuffle。
5. batch normalization的設置
如果有BN(batch normalization)層,finetune時最好不要凍結BN的參數,否則數據分布不一致時很容易使輸出值變的很大.
我的問題
我這里碰到的問題,其實是通過第6點解決的。如圖,inception_resnet_v2是我從github上找的,其中use_global_stats值為true
.應當進行修改,訓練階段是false,測試階段是true。
將batch normaliztion
的use_global_stats
由true
改成false
.
batch_norm_param {
use_global_stats: false
}