Caffe框架源码剖析(6)—池化层PoolingLayer

卷积层ConvolutionLayer正向传导的目标层往往是池化层PoolingLayer。池化层通过降采样来降低卷积层输出的特征向量,同时改善结果,不易出现过拟合。最常用的降采样方法有均值采样(取区域平均值作为降采样值)、最大值采样(取区域最大值作为降采样值)和随机采样(取区域内随机一个像素)等。

    PoolingLayer类从Layer基类单一继承而来,没有派生其它子类。具体定义在pooling_layer.hpp中,

  1. template <typename Dtype>  
  2. class PoolingLayer : public Layer<Dtype> {  
  3.  public:  
  4.   explicit PoolingLayer(const LayerParameter& param)  
  5.       : Layer<Dtype>(param) {}  
  6.   virtual void LayerSetUp(const vector<Blob<Dtype>*>& bottom,  
  7.       const vector<Blob<Dtype>*>& top);  
  8.   virtual void Reshape(const vector<Blob<Dtype>*>& bottom,  
  9.       const vector<Blob<Dtype>*>& top);  
  10.   
  11.   virtual inline const char* type() const { return "Pooling"; }  
  12.   virtual inline int ExactNumBottomBlobs() const { return 1; }  
  13.   virtual inline int MinTopBlobs() const { return 1; }  
  14.   // 最大值采样可以额外输出一个Blob,所以MaxTopBlobs返回2  
  15.   virtual inline int MaxTopBlobs() const {  
  16.     return (this->layer_param_.pooling_param().pool() ==  
  17.             PoolingParameter_PoolMethod_MAX) ? 2 : 1;  
  18.   }  
  19.   
  20.  protected:  
  21.   virtual void Forward_cpu(const vector<Blob<Dtype>*>& bottom,  
  22.       const vector<Blob<Dtype>*>& top);  
  23.   virtual void Forward_gpu(const vector<Blob<Dtype>*>& bottom,  
  24.       const vector<Blob<Dtype>*>& top);  
  25.   virtual void Backward_cpu(const vector<Blob<Dtype>*>& top,  
  26.       const vector<bool>& propagate_down, const vector<Blob<Dtype>*>& bottom);  
  27.   virtual void Backward_gpu(const vector<Blob<Dtype>*>& top,  
  28.       const vector<bool>& propagate_down, const vector<Blob<Dtype>*>& bottom);  
  29.   
  30.   // 卷积区域尺寸  
  31.   int kernel_h_, kernel_w_;  
  32.   // 卷积平移步幅  
  33.   int stride_h_, stride_w_;  
  34.   // 图像补齐像素数  
  35.   int pad_h_, pad_w_;  
  36.   // 通道  
  37.   int channels_;  
  38.   // 输入图像尺寸  
  39.   int height_, width_;  
  40.   // 池化后尺寸  
  41.   int pooled_height_, pooled_width_;  
  42.   // 是否全区域池化(将整幅图像降采样为1x1)  
  43.   bool global_pooling_;  
  44.   // 随机采样点索引  
  45.   Blob<Dtype> rand_idx_;  
  46.   // 最大值采样点索引  
  47.   Blob<int> max_idx_;  
  48. };  

具体实现在pooling_layer.cpp中,

  1. template <typename Dtype>  
  2. void PoolingLayer<Dtype>::LayerSetUp(const vector<Blob<Dtype>*>& bottom,  
  3.       const vector<Blob<Dtype>*>& top) {  
  4.   PoolingParameter pool_param = this->layer_param_.pooling_param();  
  5.   if (pool_param.global_pooling()) {  
  6.     CHECK(!(pool_param.has_kernel_size() ||  
  7.       pool_param.has_kernel_h() || pool_param.has_kernel_w()))  
  8.       << "With Global_pooling: true Filter size cannot specified";  
  9.   } else {  
  10.     CHECK(!pool_param.has_kernel_size() !=  
  11.       !(pool_param.has_kernel_h() && pool_param.has_kernel_w()))  
  12.       << "Filter size is kernel_size OR kernel_h and kernel_w; not both";  
  13.     CHECK(pool_param.has_kernel_size() ||  
  14.       (pool_param.has_kernel_h() && pool_param.has_kernel_w()))  
  15.       << "For non-square filters both kernel_h and kernel_w are required.";  
  16.   }  
  17.   CHECK((!pool_param.has_pad() && pool_param.has_pad_h()  
  18.       && pool_param.has_pad_w())  
  19.       || (!pool_param.has_pad_h() && !pool_param.has_pad_w()))  
  20.       << "pad is pad OR pad_h and pad_w are required.";  
  21.   CHECK((!pool_param.has_stride() && pool_param.has_stride_h()  
  22.       && pool_param.has_stride_w())  
  23.       || (!pool_param.has_stride_h() && !pool_param.has_stride_w()))  
  24.       << "Stride is stride OR stride_h and stride_w are required.";  
  25.   global_pooling_ = pool_param.global_pooling();  
  26.   // 设置卷积区域尺寸  
  27.   if (global_pooling_) {  
  28.     // 如果全区域池化,则区域尺寸等于输入图像尺寸  
  29.     kernel_h_ = bottom[0]->height();  
  30.     kernel_w_ = bottom[0]->width();  
  31.   } else {  
  32.     if (pool_param.has_kernel_size()) {  
  33.       kernel_h_ = kernel_w_ = pool_param.kernel_size();  
  34.     } else {  
  35.       kernel_h_ = pool_param.kernel_h();  
  36.       kernel_w_ = pool_param.kernel_w();  
  37.     }  
  38.   }  
  39.   CHECK_GT(kernel_h_, 0) << "Filter dimensions cannot be zero.";  
  40.   CHECK_GT(kernel_w_, 0) << "Filter dimensions cannot be zero.";  
  41.   // 设置图像补齐像素  
  42.   if (!pool_param.has_pad_h()) {  
  43.     pad_h_ = pad_w_ = pool_param.pad();  
  44.   } else {  
  45.     pad_h_ = pool_param.pad_h();  
  46.     pad_w_ = pool_param.pad_w();  
  47.   }  
  48.   // 设置卷积平移步幅  
  49.   if (!pool_param.has_stride_h()) {  
  50.     stride_h_ = stride_w_ = pool_param.stride();  
  51.   } else {  
  52.     stride_h_ = pool_param.stride_h();  
  53.     stride_w_ = pool_param.stride_w();  
  54.   }  
  55.   if (global_pooling_) {  
  56.     CHECK(pad_h_ == 0 && pad_w_ == 0 && stride_h_ == 1 && stride_w_ == 1)  
  57.       << "With Global_pooling: true; only pad = 0 and stride = 1";  
  58.   }  
  59.   if (pad_h_ != 0 || pad_w_ != 0) {  
  60.     CHECK(this->layer_param_.pooling_param().pool()  
  61.         == PoolingParameter_PoolMethod_AVE  
  62.         || this->layer_param_.pooling_param().pool()  
  63.         == PoolingParameter_PoolMethod_MAX)  
  64.         << "Padding implemented only for average and max pooling.";  
  65.     CHECK_LT(pad_h_, kernel_h_);  
  66.     CHECK_LT(pad_w_, kernel_w_);  
  67.   }  
  68. }  
  69.   
  70. template <typename Dtype>  
  71. void PoolingLayer<Dtype>::Reshape(const vector<Blob<Dtype>*>& bottom,  
  72.       const vector<Blob<Dtype>*>& top) {  
  73.   CHECK_EQ(4, bottom[0]->num_axes()) << "Input must have 4 axes, "  
  74.       << "corresponding to (num, channels, height, width)";  
  75.   channels_ = bottom[0]->channels();  
  76.   height_ = bottom[0]->height();  
  77.   width_ = bottom[0]->width();  
  78.   if (global_pooling_) {  
  79.     kernel_h_ = bottom[0]->height();  
  80.     kernel_w_ = bottom[0]->width();  
  81.   }  
  82.   // 计算降采样后图像尺寸  
  83.   pooled_height_ = static_cast<int>(ceil(static_cast<float>(  
  84.       height_ + 2 * pad_h_ - kernel_h_) / stride_h_)) + 1;  
  85.   pooled_width_ = static_cast<int>(ceil(static_cast<float>(  
  86.       width_ + 2 * pad_w_ - kernel_w_) / stride_w_)) + 1;  
  87.   if (pad_h_ || pad_w_) {  
  88.     // 如果有图像补齐,则需要确保不发生越界,否则不做最后一个采样点  
  89.     if ((pooled_height_ - 1) * stride_h_ >= height_ + pad_h_) {  
  90.       --pooled_height_;  
  91.     }  
  92.     if ((pooled_width_ - 1) * stride_w_ >= width_ + pad_w_) {  
  93.       --pooled_width_;  
  94.     }  
  95.     CHECK_LT((pooled_height_ - 1) * stride_h_, height_ + pad_h_);  
  96.     CHECK_LT((pooled_width_ - 1) * stride_w_, width_ + pad_w_);  
  97.   }  
  98.   top[0]->Reshape(bottom[0]->num(), channels_, pooled_height_,  
  99.       pooled_width_);  
  100.   if (top.size() > 1) {  
  101.     top[1]->ReshapeLike(*top[0]);  
  102.   }  
  103.   // 如果是最大值采样,则初始化最大值采样点索引  
  104.   if (this->layer_param_.pooling_param().pool() ==  
  105.       PoolingParameter_PoolMethod_MAX && top.size() == 1) {  
  106.     max_idx_.Reshape(bottom[0]->num(), channels_, pooled_height_,  
  107.         pooled_width_);  
  108.   }  
  109.   // 如果是随机采样,则初始化随机采样点索引  
  110.   if (this->layer_param_.pooling_param().pool() ==  
  111.       PoolingParameter_PoolMethod_STOCHASTIC) {  
  112.     rand_idx_.Reshape(bottom[0]->num(), channels_, pooled_height_,  
  113.       pooled_width_);  
  114.   }  
  115. }  
  116.   
  117. // CPU正向传导  
  118. // TODO(Yangqing): 池化操作还可以更快吗?  
  119. template <typename Dtype>  
  120. void PoolingLayer<Dtype>::Forward_cpu(const vector<Blob<Dtype>*>& bottom,  
  121.       const vector<Blob<Dtype>*>& top) {  
  122.   const Dtype* bottom_data = bottom[0]->cpu_data();  
  123.   Dtype* top_data = top[0]->mutable_cpu_data();  
  124.   const int top_count = top[0]->count();  
  125.   // 如果top.size() > 1,则额外输出一个Blob到top[1]  
  126.   const bool use_top_mask = top.size() > 1;  
  127.   int* mask = NULL;  // suppress warnings about uninitalized variables  
  128.   Dtype* top_mask = NULL;  
  129.   // switch不同的降采样方法  
  130.   // 将swtich放在for循环外用来提高运行速度,虽然这样会增加代码量  
  131.   switch (this->layer_param_.pooling_param().pool()) {  
  132.   // 最大值采样  
  133.   case PoolingParameter_PoolMethod_MAX:  
  134.     // 查找区域最大值前,将数组值初始化为-1  
  135.     if (use_top_mask) {  
  136.       top_mask = top[1]->mutable_cpu_data();  
  137.       caffe_set(top_count, Dtype(-1), top_mask);  
  138.     } else {  
  139.       mask = max_idx_.mutable_cpu_data();  
  140.       caffe_set(top_count, -1, mask);  
  141.     }  
  142.     caffe_set(top_count, Dtype(-FLT_MAX), top_data);  
  143.     // 循环遍历区域最大值  
  144.     for (int n = 0; n < bottom[0]->num(); ++n) {  
  145.       for (int c = 0; c < channels_; ++c) {  
  146.         for (int ph = 0; ph < pooled_height_; ++ph) {  
  147.           for (int pw = 0; pw < pooled_width_; ++pw) {  
  148.             int hstart = ph * stride_h_ - pad_h_;  
  149.             int wstart = pw * stride_w_ - pad_w_;  
  150.             int hend = min(hstart + kernel_h_, height_);  
  151.             int wend = min(wstart + kernel_w_, width_);  
  152.             hstart = max(hstart, 0);  
  153.             wstart = max(wstart, 0);  
  154.             const int pool_index = ph * pooled_width_ + pw;  
  155.             for (int h = hstart; h < hend; ++h) {  
  156.               for (int w = wstart; w < wend; ++w) {  
  157.                 const int index = h * width_ + w;  
  158.                 if (bottom_data[index] > top_data[pool_index]) {  
  159.                   top_data[pool_index] = bottom_data[index];  
  160.                   if (use_top_mask) {  
  161.                     top_mask[pool_index] = static_cast<Dtype>(index);  
  162.                   } else {  
  163.                     // 位置记录在max_idx_索引中  
  164.                     mask[pool_index] = index;  
  165.                   }  
  166.                 }  
  167.               }  
  168.             }  
  169.           }  
  170.         }  
  171.         // 加上偏移,跳转到下一幅图像  
  172.         bottom_data += bottom[0]->offset(0, 1);  
  173.         top_data += top[0]->offset(0, 1);  
  174.         if (use_top_mask) {  
  175.           top_mask += top[0]->offset(0, 1);  
  176.         } else {  
  177.           mask += top[0]->offset(0, 1);  
  178.         }  
  179.       }  
  180.     }  
  181.     break;  
  182.   // 平均值采样  
  183.   case PoolingParameter_PoolMethod_AVE:  
  184.     for (int i = 0; i < top_count; ++i) {  
  185.       top_data[i] = 0;  
  186.     }  
  187.     // 循环遍历计算区域平均值  
  188.     for (int n = 0; n < bottom[0]->num(); ++n) {  
  189.       for (int c = 0; c < channels_; ++c) {  
  190.         for (int ph = 0; ph < pooled_height_; ++ph) {  
  191.           for (int pw = 0; pw < pooled_width_; ++pw) {  
  192.             int hstart = ph * stride_h_ - pad_h_;  
  193.             int wstart = pw * stride_w_ - pad_w_;  
  194.             int hend = min(hstart + kernel_h_, height_ + pad_h_);  
  195.             int wend = min(wstart + kernel_w_, width_ + pad_w_);  
  196.             int pool_size = (hend - hstart) * (wend - wstart);  
  197.             hstart = max(hstart, 0);  
  198.             wstart = max(wstart, 0);  
  199.             hend = min(hend, height_);  
  200.             wend = min(wend, width_);  
  201.             for (int h = hstart; h < hend; ++h) {  
  202.               for (int w = wstart; w < wend; ++w) {  
  203.                 top_data[ph * pooled_width_ + pw] +=  
  204.                     bottom_data[h * width_ + w];  
  205.               }  
  206.             }  
  207.             top_data[ph * pooled_width_ + pw] /= pool_size;  
  208.           }  
  209.         }  
  210.         // 加上偏移,跳转到下一幅图像  
  211.         bottom_data += bottom[0]->offset(0, 1);  
  212.         top_data += top[0]->offset(0, 1);  
  213.       }  
  214.     }  
  215.     break;  
  216.   // 随机采样尚未在CPU端实现  
  217.   case PoolingParameter_PoolMethod_STOCHASTIC:  
  218.     NOT_IMPLEMENTED;  
  219.     break;  
  220.   default:  
  221.     LOG(FATAL) << "Unknown pooling method.";  
  222.   }  
  223. }  
  224.   
  225. // CPU反向传导  
  226. template <typename Dtype>  
  227. void PoolingLayer<Dtype>::Backward_cpu(const vector<Blob<Dtype>*>& top,  
  228.       const vector<bool>& propagate_down, const vector<Blob<Dtype>*>& bottom) {  
  229.   if (!propagate_down[0]) {  
  230.     return;  
  231.   }  
  232.   const Dtype* top_diff = top[0]->cpu_diff();  
  233.   Dtype* bottom_diff = bottom[0]->mutable_cpu_diff();  
  234.   // 和正向传导代码类似,将switch放在for循环外部  
  235.   caffe_set(bottom[0]->count(), Dtype(0), bottom_diff);  
  236.   // 如果top.size() > 1,则额外输出一个Blob到top[1]  
  237.   const bool use_top_mask = top.size() > 1;  
  238.   const int* mask = NULL;  // suppress warnings about uninitialized variables  
  239.   const Dtype* top_mask = NULL;  
  240.   switch (this->layer_param_.pooling_param().pool()) {  
  241.   // 最大值采样  
  242.   case PoolingParameter_PoolMethod_MAX:  
  243.     // 开始循环  
  244.     if (use_top_mask) {  
  245.       top_mask = top[1]->cpu_data();  
  246.     } else {  
  247.       mask = max_idx_.cpu_data();  
  248.     }  
  249.     for (int n = 0; n < top[0]->num(); ++n) {  
  250.       for (int c = 0; c < channels_; ++c) {  
  251.         for (int ph = 0; ph < pooled_height_; ++ph) {  
  252.           for (int pw = 0; pw < pooled_width_; ++pw) {  
  253.             const int index = ph * pooled_width_ + pw;  
  254.             // 从采样点索引数组中取出反向传导的目的索引  
  255.             const int bottom_index =  
  256.                 use_top_mask ? top_mask[index] : mask[index];  
  257.             bottom_diff[bottom_index] += top_diff[index];  
  258.           }  
  259.         }  
  260.         bottom_diff += bottom[0]->offset(0, 1);  
  261.         top_diff += top[0]->offset(0, 1);  
  262.         if (use_top_mask) {  
  263.           top_mask += top[0]->offset(0, 1);  
  264.         } else {  
  265.           mask += top[0]->offset(0, 1);  
  266.         }  
  267.       }  
  268.     }  
  269.     break;  
  270.   // 平均值采样  
  271.   case PoolingParameter_PoolMethod_AVE:  
  272.     // 开始循环  
  273.     for (int n = 0; n < top[0]->num(); ++n) {  
  274.       for (int c = 0; c < channels_; ++c) {  
  275.         for (int ph = 0; ph < pooled_height_; ++ph) {  
  276.           for (int pw = 0; pw < pooled_width_; ++pw) {  
  277.             int hstart = ph * stride_h_ - pad_h_;  
  278.             int wstart = pw * stride_w_ - pad_w_;  
  279.             int hend = min(hstart + kernel_h_, height_ + pad_h_);  
  280.             int wend = min(wstart + kernel_w_, width_ + pad_w_);  
  281.             int pool_size = (hend - hstart) * (wend - wstart);  
  282.             hstart = max(hstart, 0);  
  283.             wstart = max(wstart, 0);  
  284.             hend = min(hend, height_);  
  285.             wend = min(wend, width_);  
  286.             for (int h = hstart; h < hend; ++h) {  
  287.               for (int w = wstart; w < wend; ++w) {  
  288.                 // 将top偏导平均分配到bottom各点上  
  289.                 bottom_diff[h * width_ + w] +=  
  290.                   top_diff[ph * pooled_width_ + pw] / pool_size;  
  291.               }  
  292.             }  
  293.           }  
  294.         }  
  295.         // 加上偏移,跳转到下一幅图像  
  296.         bottom_diff += bottom[0]->offset(0, 1);  
  297.         top_diff += top[0]->offset(0, 1);  
  298.       }  
  299.     }  
  300.     break;  
  301.   // 随机采样尚未在CPU端实现  
  302.   case PoolingParameter_PoolMethod_STOCHASTIC:  
  303.     NOT_IMPLEMENTED;  
  304.     break;  
  305.   default:  
  306.     LOG(FATAL) << "Unknown pooling method.";  
  307.   }  
  308. }  
  309.   
  310. // 如果CPU_ONLY模式则禁止Forward_gpu和Backward_gpu函数  
  311. #ifdef CPU_ONLY  
  312. STUB_GPU(PoolingLayer);  
  313. #endif 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值