最近在研究SSD的源碼,驚訝的發現,在 https://github.com/weiliu89/caffe/tree/ssd 上給出的源碼,與論文中描述竟然不一樣!或許是我錯了。下面給出疑問和證據。
疑問:mbox_loss中到底包含沒包含loc_loss? smooth_L1到底參與運算了沒?
首先,在生成腳本:ssd_pascal.py中mining_type是:MAX_NEGATIVE
那麼在multibox_loss_layer.cpp中,計算loc_loss表明是下面完成的:
實際我們都被誤導,這個函數僅僅是完成該層的前向傳遞計算,實際計算loc_loss是在MineHardExamples這個函數中,我們打開這個函數的定義,你會發現這裡面才是論文中提及的NMS、Smooth_L1的根據地。我搜便了整個caffe-ssd工程路徑,發現實作L1計算的隻有一處,就是在bbox_util.cpp中的void ComputeLocLoss(const Blob<Dtype>& loc_pred, const Blob<Dtype>& loc_gt, const vector<map<int, vector<int> > >& all_match_indices, const int num, const int num_priors, const LocLossType loc_loss_type, vector<vector<float> >* all_loc_loss)這個函數,也就是說,隻有調用這個ComputeLocLoss,才會進行smooth L1 loss的計算。那麼問題來了,整個bbox_util.cpp中僅調用了一次ComputeLocLoss,但是這次掉用是在mining_type=MultiBoxLossParameter_MiningType_HARD_EXAMPLE 的情況下。
我查過了,MultiBoxLossParameter_MiningType_HARD_EXAMPLE=HARD_EXAMPLE,而并不是我們定義的MAX_NEGATIVE,是以按照這麼分析,上圖中的ComputeLocLoss 不執行。
證據:實際驗證
在關鍵點出,我設定了LOG輸出,例如在調用ComputeLocLoss後,以及下面的else:
還有,以防程式在其他地方調用這個計算,我索性在ComputeLocLoss函數中設定了LOG:
然後重新編譯工程,make -j8,截圖顯示是我第二次增加LOG,僅改了bbox_util.cpp,是以之編譯了這個:
然後重新運作ssd_pascal.py,訓練的LOG截圖如下:
從輸出結果看,computeLocLoss并沒有執行(下圖是摘出來的LOG輸出的邏輯,假如執行,應該輸出 "ComputeLocLoss MultiBoxLossParameter_MiningType_HARD_EXAMPLE"):
總結:通過實際驗證發現,論文中提到的,可能在實際代碼中并沒有用到。或許是本人對ssd的使用還沒了解到位,如有大神路過,還請不吝指教,為小弟解惑!
另外補充,我們使用./build/tools/caffe train進行訓練,但是在運作score_ssd_pascal.py時依然使用 train這個工具,無論怎改test_interval或者multistep的stepvalue,它都是在test。這個問題也困擾了好久,今天回過頭來仔細對比了score_ssd_pascal和ssd_pascal生成的.sh發現,前者裡面并沒有引入solverstate,而僅僅隻有一個caffemodel,而後者是兩者都載入了。可能就是這裡的原因吧,後者會根據設定的solver參數繼續進行訓練,而前者沒有snapshoot的記錄,對現存的模型進行test。