最近在研究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。