1.自定义字段
最近在老版本的caffe上跑resnext网络的时候出现如下所示的bug,正如我们上一篇文章Caffe | 核心积木Layer层类详解中说到的,在caffe.proto文件的PoolingParameter中没有ceil_mode这个field字段。因此只有在源码中添加这个参数以及相关实现代码,并重新编译caffe。
Message type “caffe.PoolingParameter” has no field named “ceil_mode”.
(1)修改pooling_layer.hpp文件
首先在头文件中添加参数的定义,因为参数在使用之前需要先定义(C/C++语言的特性)。
int height_, width_;
int pooled_height_, pooled_width_;
bool global_pooling_;
bool ceil_mode_; //添加bug中指出的缺少的属性
Blob<Dtype> rand_idx_;
Blob<int> max_idx_;
(2)修改pooling_layer.cpp文件中对应参数
也是在上一篇文章Caffe | 核心积木Layer层类详解中说到的,每一个层(包括当前存在问题的PoolingLayer)都继承自基类LayerParameter。而基类参数中有两个重要的函数如下:
- Layersetup:读取指定层类的layer param(层参数),为后续reshape做准备。
- reshape:根据输入该层的bottom blob的形状,和改成定制化的计算策略(也就是当前层的逻辑)计算得到对应的top blob的形状,并预先分配好内存空间。
所以在这里这两个函数跟参数紧密相关,因此我们主要修改的就是pooling_layer.cpp中的这两个函数。
Layersetup函数:
|| (!pool_param.has_stride_h() && !pool_param.has_stride_w()))
<< "Stride is stride OR stride_h and stride_w are required.";
global_pooling_ = pool_param.global_pooling();
// 添加的代码-----------------------------------
ceil_mode_ = pool_param.ceil_mode(); //添加的代码,
//主要作用是从参数文件中获取ceil_mode_的参数数值。
// ------------------------------------------------------
if (global_pooling_) {
kernel_h_ = bottom[0]->height();
kernel_w_ = bottom[0]->width();
if (pad_h_ != 0 || pad_w_ != 0) {
CHECK(this->layer_param_.pooling_param().pool()
== PoolingParameter_PoolMethod_AVE
|| this->layer_param_.pooling_param().pool()
== PoolingParameter_PoolMethod_MAX)
<< "Padding implemented only for average and max pooling.";
CHECK_LT(pad_h_, kernel_h_);
CHECK_LT(pad_w_, kernel_w_);
Reshape函数:
void PoolingLayer<Dtype>::Reshape(const vector<Blob<Dtype>*>& bottom,
const vector<Blob<Dtype>*>& top) {
CHECK_EQ(4, bottom[0]->num_axes()) << "Input must have 4 axes, "
<< "corresponding to (num, channels, height, width)";
channels_ = bottom[0]->channels();
height_ = bottom[0]->height();
width_ = bottom[0]->width();
if (global_pooling_) {
kernel_h_ = bottom[0]->height();
kernel_w_ = bottom[0]->width();
}
- pooled_height_ = static_cast<int>(ceil(static_cast<float>(
- height_ + 2 * pad_h_ - kernel_h_) / stride_h_)) + 1;
- pooled_width_ = static_cast<int>(ceil(static_cast<float>(
- width_ + 2 * pad_w_ - kernel_w_) / stride_w_)) + 1;
+ // Specify the structure by ceil or floor mode
+
+ // 添加的代码-----------------------------------
+ if (ceil_mode_) {
+ pooled_height_ = static_cast<int>(ceil(static_cast<float>(
+ height_ + 2 * pad_h_ - kernel_h_) / stride_h_)) + 1;
+ pooled_width_ = static_cast<int>(ceil(static_cast<float>(
+ width_ + 2 * pad_w_ - kernel_w_) / stride_w_)) + 1;
+ } else {
+ pooled_height_ = static_cast<int>(floor(static_cast<float>(
+ height_ + 2 * pad_h_ - kernel_h_) / stride_h_)) + 1;
+ pooled_width_ = static_cast<int>(floor(static_cast<float>(
+ width_ + 2 * pad_w_ - kernel_w_) / stride_w_)) + 1;
+ }
+ // ------------------------------------------------------
+
if (pad_h_ || pad_w_) {
// If we have padding, ensure that the last pooling starts strictly
// inside the image (instead of at the padding); otherwise clip the last.
(3)修改caffe.proto文件中PoolingParameter
因为所有层的参数定义都存放在caffe.proto文件中,因此修改参数后需要将新的参数添加到该文件对应的层参数中,本例中就将参数添加到PoolingParameter中。
/ If global_pooling then it will pool over the size of the bottom by doing
// kernel_h = bottom->height and kernel_w = bottom->width
optional bool global_pooling = 12 [default = false];
// 添加的代码-----------------------------------
+ // Specify floor/ceil mode
// 为pooling层添加参数,这样可以在net.prototxt文件中为pooling层设置