因为需要用C++的代码跑测试,因而需要将prototxt中一些原来用python写的层改成c++格式。
之前跑faster rcnn的时候,将rpn层参考博客修改过,但是因为对源码不了解,这次改三个层无从下手,下面记录一下过程。需要修改的三个层分别为rpn-data,roi-data和proposal层。
rpn-data:
layer {
name: 'rpn-data'
type: 'Module'
bottom: 'rpn_cls_score/p2'
bottom: 'rpn_cls_score/p3'
bottom: 'rpn_cls_score/p4'
bottom: 'rpn_cls_score/p5'
bottom: 'rpn_cls_score/p6'
bottom: 'gt_boxes'
bottom: 'im_info'
top: 'rpn_labels'
top: 'rpn_bbox_targets'
top: 'rpn_bbox_inside_weights'
top: 'rpn_bbox_outside_weights'
module_param {
module: "modules"
type: "FPNAnchorTarget"
param_str: "{ 'feat_strides': [4,8,16,32,64] }"
}
propagate_down: 0
propagate_down: 0
propagate_down: 0
propagate_down: 0
propagate_down: 0
propagate_down: 0
propagate_down: 0
}
roi-data:
layer {
name: 'roi-data'
type: 'Module'
bottom: 'rpn_rois'
bottom: 'gt_boxes'
top: 'rois/h2'
top: 'rois/h3'
top: 'rois/h4'
top: 'rois/h5'
# top: 'rois/h6'
top: 'labels'
top: 'bbox_targets'
top: 'bbox_inside_weights'
top: 'bbox_outside_weights'
propagate_down: 0
propagate_down: 0
module_param {
module: 'modules'
type: 'FPNProposalTarget'
}
}
proposal:
layer {
name: 'proposal'
type: 'Module'
bottom: 'fpn_out_reshape/p2'
bottom: 'rpn_bbox_pred/p2'
bottom: 'fpn_out_reshape/p3'
bottom: 'rpn_bbox_pred/p3'
bottom: 'fpn_out_reshape/p4'
bottom: 'rpn_bbox_pred/p4'
bottom: 'fpn_out_reshape/p5'
bottom: 'rpn_bbox_pred/p5'
bottom: 'fpn_out_reshape/p6'
bottom: 'rpn_bbox_pred/p6'
bottom: 'im_info'
top: 'rpn_rois'
module_param {
module: 'modules'
type: 'FPNProposal'
param_str: "{'feat_strides': [4,8,16,32,64]}"
}
propagate_down: 0
propagate_down: 0
propagate_down: 0
propagate_down: 0
propagate_down: 0
propagate_down: 0
propagate_down: 0
propagate_down: 0
propagate_down: 0
propagate_down: 0
propagate_down: 0
include { phase: TRAIN }
}
注意到其中的module_param和model的'modules'都是需要根据实际情况改掉的。在调用module_param的地方,调用方法为this->layer_param_.module_param(),而直接编译caffe的时候是无法通过编译的,会报找不到module_param()的error。分析了源码,发现代码中使用YAML解析结构(在faster rcnn中为用LayerParameter直接定义解析参数,之后也可以尝试用这种方法),因此需要在caffe.proto里添加Moduleparameter。
(虽然yaml-cpp安装成功了,但是还是报错undefined yaml,所以打算尝试上面的方法:
- (备份后)将train和test的pt里的module_param更改成fpn_param
- 在caffe.proto里添加parameter,FPNParameter fpn_param;然后发现,根据caffe.proto中,参数的类型不同,会在caafe.pb.h和.cc中自动生成不同的参数。对于optional变量,会生成has_xx,对于repeated变量,会生成xx.size(),可以对比dummy_data_layer.cpp和pooling_layer.cpp两个文件,而生成参数的主要作用是用来在.cpp中取值的时候做判断)
事实上尝试失败了,因为没有搞清楚层与层之间的关系
该prototxt在roi_pooling层只写了一个feat_strides,而实际上在Faster Rcnn中,还需要anchor_scales和anchor_ratios。因此查看了fpn_proposal_layer.cpp,发现其中的结构是,如果pt中为空,则自动设置。(可以在后期加在prototxt中修改试一试)
另外可以看到,在roi-data层中,c++版本是没有另外传入参数的,而python版本是添加了num_class参数。然后发现该n_class参数是在frcnn_param里传入的,而里面的参数是从脚本中parse出来的,因此不需要在这里设置了。
另外,在fpn_proposal_layer.cu里,有cub/cub.cuh,找不到文件,所以先注释掉了。
================分界线======================
11.07 我又回来了,并搞明白了几个问题,为:
-
cub.cub.cuh为cuda的一个头文件。
-
c++版本中的另外添加参数,是在输入层用window_data这个层传入进来的。而我之前的思路是要把train的pt文件中python层也改成c++,于是我一开始的思路是把所需要的参数全部传进来,而现在思路更改为只需要改test的pt文件中的python层为c++格式,这种情况下只需要把cfg中在此调用的参数重新传进来就行了。
那么,在此重新分析一下逻辑:
1.首先要找到需要更改的python层,并简要分析
test的prototxt里,Python层为「input-data」,「rpn-data」,「proposal」,「roi-data」
test的prototxt里,Python层为「proposal」,「as-rois」
在这里的proposal层在python中调用的是(rpn.proposal_layer),roi-data是(rpn.proposal_target_layer),as-rois是(rpn.as_rois)。
自此可以看出,需要更改的只有propsal层和as-rois层。那么我们去看python文件。
2.分析as-rois层
打开python的这个文件,可以看到该层调用的外部函数只有config.py函数,并只用了其中的「cfg.TEST.RPN_POST_NMS_TOP_N」这一个参数变量,而该层的作用是将proposal和gt对比,并分类,那么该层应该还是挺好改的,因为只涉及了自己一个脚本文件。
3.分析propsal层
这个文件调用的函数比较多,而python中把调用的函数写在了多个文件中,画个表表示:
fast_rcnn.config | cfg |
generate_anchors | generate_anchors |
fast_rcnn.bbox_transform | bbox_transform_inv , clip_boxes |
fast_rcnn.nms_wrapper | nms |
参照之前修改的rpn的c++文件,其将这些函数写在了一个c++文件中。
4.分析Faster Rcnn和FPN的python脚本文件
主要去看了proposal_layer这个文件,其中区别最大的是FPN是有多个feat_stride,而Faster Rcnn只有一个,其他的函数基本一样。
分析完需要修改的文件后,整理一下需要了解的背景知识:
1.Python层的逻辑
Python中每个class需要有四个层,分别为『setup』、『forward』、『backward』、『reshape』。如果有其他需要的函数,可以在class外面定义。
其中backward的参数为(self,top,propagate_down,bottom),其他三个为(self,top,bottom)。
bottom和top的参数顺序根据prototxt中顺序决定。
2.C++层的逻辑
c++中逻辑比较麻烦,因为要头文件定义很多参数。
3.Python层的一些参数解释
im_info:bottom[0].data[0,:],存放的是(height , width , scale)
4.caffe层的一些参数解释
caffe_set:用参数alpha对参数Y进行初始化
template <typename Dtype>
void caffe_set(const int N, const Dtype alpha, Dtype* Y) {
if (alpha == 0) {
memset(Y, 0, sizeof(Dtype) * N); // NOLINT(caffe/alt_fn)
return;
}
for (int i = 0; i < N; ++i) {
Y[i] = alpha;
}
}
caffe_axpy:Y=alpha*X+Y ,N为X和Y中element个数
template <>
void caffe_axpy<float>(const int N, const float alpha, const float* X,
float* Y) { cblas_saxpy(N, alpha, X, 1, Y, 1); }
CopyFrom()
template <typename Dtype>
void Blob<Dtype>::CopyFrom(const Blob& source, bool copy_diff, bool reshape) {
}
//从source 拷贝数据 , copy_diff控制是拷贝diff还是data
_axpy:
================分界线======================
11.08 我又回来了,出现了一些问题:
我测试了之前该rpn的faster rcnn,发现直接跑测试是无法跑的,但是直接detect是可以的,问题出在blob的尺寸问题上,目前还没有解决。
猜测原因是config脚本设置的问题
解决思路
1.查看rpn的top格式
2.查看roipooling的bottom格式
================分界线======================
11.09 我又回来了,解决问题了!!!哭了哭了
昨天确定出错的位置在于,c++版本用blob传递信息(虽然python也是),但是很坑很关键的一点是,python中很多地方是用numpy计算的,而as we all know,blob是四维的,于是在我使用的环境下,就是在test.py脚本中,根据rois得到的boxes,调用了bbox_transform_inv函数,由于用到了其本身的数据类型和blob的类型,四维和二维不匹配于是出错了。
在test脚本中,net.blob['rois'].data.shape可以查看rois的维度,查到是四维。而实际上没改C++版本之前,传递的该参数维度是二维,很巧和的是,前者shape为[1,5,1,1],后者为[1,5],于是我就分别去看c++和python的rpn层文件,发现c++层在最后reshape成了4维。于是经我不缜密的猜测,最后两位数据其实只是为了对其的,其实没有用,也就是说,只需要在python文件中需要rois的地方,把原数据的前两维拷贝就好了
解决方法也很简单,在test.py中用到rois的地方(boxes)的shape改掉:
rois = net.blobs['rois'].data.copy()
boxes = rois[:,1:5] / im_scales[0]
box_min_resize = min(cfg.TEST.RPN_POST_NMS_TOP_N,boxes.shape[0])
boxes.resize([box_min_resize,4])
接下来就开始搞as_rois.py了,github上没有对应的层真的哭了!!本来计划周日稍微改改就能跑的c++模型,没想到搞到了今天,低估了工作量但是对caffe 的这几个层的了解更深刻了。。。
================分界线======================
11.13 我又回来了,全部改好并且能跑了,问题就是结果不对- -
记录一些问题
1.一开始把vector的变量定义在.hpp中,然后在cpp中操作,这造成一个问题,就是运行速度会越来越慢,可能造成了内存泄漏没有清理空间。于是我用了一个简单的方法就是把hpp中定义的变量放cpp中定义了,这样就相当于他在程序运行结束后就自动销毁了。
2.如果添加cpp层需要make clean,如果只是修改cpp层,不需要make clean