【caffe源码研究】第三章:源码篇(11) :PoolingLayer
2017-01-06 01:21
441 查看
Pooling层也是CNN中重要的层。
主要分成max-pool和mean-pool两种方法
mean-pool的反向传播
max-pool的反向传播
主要分成max-pool和mean-pool两种方法
Forward_cpu
前向过程如下void PoolingLayer<Dtype>::Forward_cpu(const vector<Blob<Dtype>*>& bottom, const vector<Blob<Dtype>*>& top) { const Dtype* bottom_data = bottom[0]->cpu_data(); Dtype* top_data = top[0]->mutable_cpu_data(); const int top_count = top[0]->count(); //将mask信息输出到top[1],如果top大于1 const bool use_top_mask = top.size() > 1; int* mask = NULL; // suppress warnings about uninitalized variables Dtype* top_mask = NULL; switch (this->layer_param_.pooling_param().pool()) { case PoolingParameter_PoolMethod_MAX://这里的case主要是实现max pooling的方法 // Initialize if (use_top_mask) { top_mask = top[1]->mutable_cpu_data(); caffe_set(top_count, Dtype(-1), top_mask); } else { mask = max_idx_.mutable_cpu_data(); caffe_set(top_count, -1, mask); } caffe_set(top_count, Dtype(-FLT_MAX), top_data); // The main loop for (int n = 0; n < bottom[0]->num(); ++n) { for (int c = 0; c < channels_; ++c) { for (int ph = 0; ph < pooled_height_; ++ph) { for (int pw = 0; pw < pooled_width_; ++pw) { int hstart = ph * stride_h_ - pad_h_;//这里的hstart,wstart,hend,wend指的是pooling窗口在特征图中的坐标,对应左上右下即x1 y1 x2 y2 int wstart = pw * stride_w_ - pad_w_; int hend = min(hstart + kernel_h_, height_); int wend = min(wstart + kernel_w_, width_); hstart = max(hstart, 0); wstart = max(wstart, 0); const int pool_index = ph * pooled_width_ + pw; for (int h = hstart; h < hend; ++h) { for (int w = wstart; w < wend; ++w) { const int index = h * width_ + w;//记录index偏差 if (bottom_data[index] > top_data[pool_index]) {//不停迭代 top_data[pool_index] = bottom_data[index]; if (use_top_mask) { top_mask[pool_index] = static_cast<Dtype>(index);//记录当前最大值的的坐标索引 } else { mask[pool_index] = index; } } } } } } // 计算偏移量,进入下一张图的index起始地址 bottom_data += bottom[0]->offset(0, 1); top_data += top[0]->offset(0, 1); if (use_top_mask) { top_mask += top[0]->offset(0, 1); } else { mask += top[0]->offset(0, 1); } } } break; case PoolingParameter_PoolMethod_AVE://average_pooling for (int i = 0; i < top_count; ++i) { top_data[i] = 0; } // The main loop for (int n = 0; n < bottom[0]->num(); ++n) {//同样是主循环 for (int c = 0; c < channels_; ++c) { for (int ph = 0; ph < pooled_height_; ++ph) { for (int pw = 0; pw < pooled_width_; ++pw) { int hstart = ph * stride_h_ - pad_h_; int wstart = pw * stride_w_ - pad_w_; int hend = min(hstart + kernel_h_, height_ + pad_h_); int wend = min(wstart + kernel_w_, width_ + pad_w_); int pool_size = (hend - hstart) * (wend - wstart); hstart = max(hstart, 0); wstart = max(wstart, 0); hend = min(hend, height_); wend = min(wend, width_); for (int h = hstart; h < hend; ++h) { for (int w = wstart; w < wend; ++w) { top_data[ph * pooled_width_ + pw] += bottom_data[h * width_ + w]; } } top_data[ph * pooled_width_ + pw] /= pool_size;//获得相应的平均值 } } // compute offset同理计算下一个图的起始地址 bottom_data += bottom[0]->offset(0, 1); top_data += top[0]->offset(0, 1); } } break; case PoolingParameter_PoolMethod_STOCHASTIC: NOT_IMPLEMENTED; break; default: LOG(FATAL) << "Unknown pooling method."; }
Backward_cpu
反向计算的原理如下mean-pool的反向传播
max-pool的反向传播
template <typename Dtype> void PoolingLayer<Dtype>::Backward_cpu(const vector<Blob<Dtype>*>& top, const vector<bool>& propagate_down, const vector<Blob<Dtype>*>& bottom) { if (!propagate_down[0]) { return; } const Dtype* top_diff = top[0]->cpu_diff();//首先获得上层top_blob的diff Dtype* bottom_diff = bottom[0]->mutable_cpu_diff(); caffe_set(bottom[0]->count(), Dtype(0), bottom_diff); // We'll output the mask to top[1] if it's of size >1. const bool use_top_mask = top.size() > 1; const int* mask = NULL; // suppress warnings about uninitialized variables const Dtype* top_mask = NULL; switch (this->layer_param_.pooling_param().pool()) { case PoolingParameter_PoolMethod_MAX: // The main loop if (use_top_mask) { top_mask = top[1]->cpu_data(); } else { mask = max_idx_.cpu_data(); } for (int n = 0; n < top[0]->num(); ++n) { for (int c = 0; c < channels_; ++c) { for (int ph = 0; ph < pooled_height_; ++ph) { for (int pw = 0; pw < pooled_width_; ++pw) { const int index = ph * pooled_width_ + pw; const int bottom_index = use_top_mask ? top_mask[index] : mask[index];//根据max pooling记录的mask位置,进行误差反转 bottom_diff[bottom_index] += top_diff[index]; } } bottom_diff += bottom[0]->offset(0, 1); top_diff += top[0]->offset(0, 1); if (use_top_mask) { top_mask += top[0]->offset(0, 1); } else { mask += top[0]->offset(0, 1); } } } break; case PoolingParameter_PoolMethod_AVE: // The main loop for (int n = 0; n < top[0]->num(); ++n) { for (int c = 0; c < channels_; ++c) { for (int ph = 0; ph < pooled_height_; ++ph) { for (int pw = 0; pw < pooled_width_; ++pw) { int hstart = ph * stride_h_ - pad_h_; int wstart = pw * stride_w_ - pad_w_; int hend = min(hstart + kernel_h_, height_ + pad_h_); int wend = min(wstart + kernel_w_, width_ + pad_w_); int pool_size = (hend - hstart) * (wend - wstart); hstart = max(hstart, 0); wstart = max(wstart, 0); hend = min(hend, height_); wend = min(wend, width_); for (int h = hstart; h < hend; ++h) { for (int w = wstart; w < wend; ++w) { bottom_diff[h * width_ + w] += top_diff[ph * pooled_width_ + pw] / pool_size;//mean_pooling中,bottom的误差值按pooling窗口中的大小计算,从上一层进行填充后,再除窗口大小 } } } } // offset bottom_diff += bottom[0]->offset(0, 1); top_diff += top[0]->offset(0, 1); } } break; case PoolingParameter_PoolMethod_STOCHASTIC: NOT_IMPLEMENTED; break; default: LOG(FATAL) << "Unknown pooling method."; } }
相关文章推荐
- 【caffe源码研究】第三章:源码篇(13) :损失层
- 【caffe源码研究】第三章:源码篇(4) :Solver
- 【caffe源码研究】第三章:源码篇(5) :Net
- 【caffe源码研究】第三章:源码篇(3) :工厂模式
- 【caffe源码研究】第三章:源码篇(7) :Layer种类
- 【caffe源码研究】第三章:源码篇(6) :caffe.proto
- 【caffe源码研究】第三章:源码篇(2) :Blob 和 SyncedMemory
- 【caffe源码研究】第三章:源码篇(1) :caffe整体架构
- 【caffe源码研究】第三章:源码篇(9) :DataLayer
- 【caffe源码研究】第三章:源码篇(10) :ConvolutionLayer
- 【caffe源码研究】第三章:源码篇(8) :Layer代码
- 【caffe源码研究】第三章:源码篇(12) :激活函数层
- 【caffe源码研究】第四章:完整案例源码篇(1) :LeNetSolver初始化
- 【caffe源码研究】第四章:完整案例源码篇(4) :LeNet前向过程
- 【caffe源码研究】第四章:完整案例源码篇(5) :LeNet反向过程
- 【caffe源码研究】第四章:完整案例源码篇(2) :LeNet初始化训练网络
- 【caffe源码研究】第四章:完整案例源码篇(3) :LeNet初始化测试网络
- 【caffe源码研究】番外篇:(1) 使用Visual Studio 调试caffe代码
- jQuery源码研究分析学习笔记-回调函数(11)
- 【caffe源码研究】第二章:使用篇(1): 制作数据集