Skip to content

Commit

Permalink
Added Backward CPU support for ROI Pooling Layer
Browse files Browse the repository at this point in the history
Added tests for ROI Pooling Layer

Author: Ronghang Hu
  • Loading branch information
Austriker committed Jun 9, 2016
1 parent 2e0c2fb commit dc2f501
Show file tree
Hide file tree
Showing 3 changed files with 156 additions and 30 deletions.
30 changes: 28 additions & 2 deletions include/caffe/layers/roi_pooling_layer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,34 @@

namespace caffe {

/**
* @brief ROIPoolingLayer - Region of Interest Pooling Layer.
/**
* @brief Perform max pooling on regions of interest specified by input, takes
* as input N feature maps and a list of R regions of interest.
*
* ROIPoolingLayer takes 2 inputs and produces 1 output. bottom[0] is
* [N x C x H x W] feature maps on which pooling is performed. bottom[1] is
* [R x 5] containing a list R ROI tuples with batch index and coordinates of
* regions of interest. Each row in bottom[1] is a ROI tuple in format
* [batch_index x1 y1 x2 y2], where batch_index corresponds to the index of
* instance in the first input and x1 y1 x2 y2 are 0-indexed coordinates
* of ROI rectangle (including its boundaries).
*
* For each of the R ROIs, max-pooling is performed over pooled_h x pooled_w
* output bins (specified in roi_pooling_param). The pooling bin sizes are
* adaptively set such that they tile ROI rectangle in the indexed feature
* map. The pooling region of vertical bin ph in [0, pooled_h) is computed as
*
* start_ph (included) = y1 + floor(ph * (y2 - y1 + 1) / pooled_h)
* end_ph (excluded) = y1 + ceil((ph + 1) * (y2 - y1 + 1) / pooled_h)
*
* and similar horizontal bins.
*
* @param param provides ROIPoolingParameter roi_pooling_param,
* with ROIPoolingLayer options:
* - pooled_h. The pooled output height.
* - pooled_w. The pooled output width
* - spatial_scale. Multiplicative spatial scale factor to translate ROI
* coordinates from their input scale to the scale used when pooling.
*
* Fast R-CNN
* Written by Ross Girshick
Expand Down
34 changes: 33 additions & 1 deletion src/caffe/layers/roi_pooling_layer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,39 @@ void ROIPoolingLayer<Dtype>::Forward_cpu(const vector<Blob<Dtype>*>& bottom,
template <typename Dtype>
void ROIPoolingLayer<Dtype>::Backward_cpu(const vector<Blob<Dtype>*>& top,
const vector<bool>& propagate_down, const vector<Blob<Dtype>*>& bottom) {
NOT_IMPLEMENTED;
if (propagate_down[1]) {
LOG(FATAL) << this->type()
<< " Layer cannot backpropagate to roi inputs.";
}
if (!propagate_down[0]) {
return;
}
const Dtype* bottom_rois = bottom[1]->cpu_data();
const Dtype* top_diff = top[0]->cpu_diff();
Dtype* bottom_diff = bottom[0]->mutable_cpu_diff();
caffe_set(bottom[0]->count(), Dtype(0.), bottom_diff);
const int* argmax_data = max_idx_.cpu_data();
const int num_rois = top[0]->num();

// Accumulate gradient over all ROIs
for (int roi_n = 0; roi_n < num_rois; ++roi_n) {
int roi_batch_ind = bottom_rois[roi_n * 5];
// Accumulate gradients over each bin in this ROI
for (int c = 0; c < channels_; ++c) {
for (int ph = 0; ph < pooled_height_; ++ph) {
for (int pw = 0; pw < pooled_width_; ++pw) {
int offset_top = ((roi_n * channels_ + c) * pooled_height_ + ph)
* pooled_width_ + pw;
int argmax_index = argmax_data[offset_top];
if (argmax_index >= 0) {
int offset_bottom = (roi_batch_ind * channels_ + c) * height_
* width_ + argmax_index;
bottom_diff[offset_bottom] += top_diff[offset_top];
}
}
}
}
}
}


Expand Down
122 changes: 95 additions & 27 deletions src/caffe/test/test_roi_pooling_layer.cpp
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#include <algorithm>
#include <cfloat>
#include <cmath>
#include <cstdlib>
#include <cstring>
Expand All @@ -17,77 +19,143 @@ using boost::scoped_ptr;

namespace caffe {

#ifndef CPU_ONLY
template <typename Dtype>
class ROIPoolingLayerTest : public GPUDeviceTest<Dtype> {
template <typename TypeParam>
class ROIPoolingLayerTest : public MultiDeviceTest<TypeParam> {
typedef typename TypeParam::Dtype Dtype;

protected:
ROIPoolingLayerTest()
: blob_bottom_data_(new Blob<Dtype>(4, 3, 12, 8)),
: blob_bottom_data_(new Blob<Dtype>(2, 2, 6, 8)),
blob_bottom_rois_(new Blob<Dtype>(4, 5, 1, 1)),
blob_top_data_(new Blob<Dtype>()) {
blob_top_data_(new Blob<Dtype>()),
blob_bottom_data_2_(new Blob<Dtype>(2, 3, 12, 20)),
blob_bottom_rois_2_(new Blob<Dtype>(1, 5, 1, 1)),
blob_top_data_2_(new Blob<Dtype>()) {
// fill the values
FillerParameter filler_param;
filler_param.set_std(10);
GaussianFiller<Dtype> filler(filler_param);
filler.Fill(this->blob_bottom_data_);

// for (int i = 0; i < blob_bottom_data_->count(); ++i) {
// blob_bottom_data_->mutable_cpu_data()[i] = i;
// }
blob_bottom_vec_.push_back(blob_bottom_data_);

int i = 0;
// caffe_rng_rand() % 4;
blob_bottom_rois_->mutable_cpu_data()[0 + 5*i] = 0;
blob_bottom_rois_->mutable_cpu_data()[1 + 5*i] = 1; // x1 < 8
blob_bottom_rois_->mutable_cpu_data()[2 + 5*i] = 1; // y1 < 12
blob_bottom_rois_->mutable_cpu_data()[3 + 5*i] = 6; // x2 < 8
blob_bottom_rois_->mutable_cpu_data()[4 + 5*i] = 6; // y2 < 12
blob_bottom_rois_->mutable_cpu_data()[1 + 5*i] = 0; // x1 < 8
blob_bottom_rois_->mutable_cpu_data()[2 + 5*i] = 0; // y1 < 6
blob_bottom_rois_->mutable_cpu_data()[3 + 5*i] = 7; // x2 < 8
blob_bottom_rois_->mutable_cpu_data()[4 + 5*i] = 5; // y2 < 6
i = 1;
blob_bottom_rois_->mutable_cpu_data()[0 + 5*i] = 2;
blob_bottom_rois_->mutable_cpu_data()[0 + 5*i] = 1;
blob_bottom_rois_->mutable_cpu_data()[1 + 5*i] = 6; // x1 < 8
blob_bottom_rois_->mutable_cpu_data()[2 + 5*i] = 2; // y1 < 12
blob_bottom_rois_->mutable_cpu_data()[2 + 5*i] = 2; // y1 < 6
blob_bottom_rois_->mutable_cpu_data()[3 + 5*i] = 7; // x2 < 8
blob_bottom_rois_->mutable_cpu_data()[4 + 5*i] = 11; // y2 < 12
blob_bottom_rois_->mutable_cpu_data()[4 + 5*i] = 5; // y2 < 6
i = 2;
blob_bottom_rois_->mutable_cpu_data()[0 + 5*i] = 1;
blob_bottom_rois_->mutable_cpu_data()[1 + 5*i] = 3; // x1 < 8
blob_bottom_rois_->mutable_cpu_data()[2 + 5*i] = 1; // y1 < 12
blob_bottom_rois_->mutable_cpu_data()[3 + 5*i] = 5; // x2 < 8
blob_bottom_rois_->mutable_cpu_data()[4 + 5*i] = 10; // y2 < 12
blob_bottom_rois_->mutable_cpu_data()[2 + 5*i] = 1; // y1 < 6
blob_bottom_rois_->mutable_cpu_data()[3 + 5*i] = 6; // x2 < 8
blob_bottom_rois_->mutable_cpu_data()[4 + 5*i] = 4; // y2 < 6
i = 3;
blob_bottom_rois_->mutable_cpu_data()[0 + 5*i] = 0;
blob_bottom_rois_->mutable_cpu_data()[1 + 5*i] = 3; // x1 < 8
blob_bottom_rois_->mutable_cpu_data()[2 + 5*i] = 3; // y1 < 12
blob_bottom_rois_->mutable_cpu_data()[2 + 5*i] = 3; // y1 < 6
blob_bottom_rois_->mutable_cpu_data()[3 + 5*i] = 3; // x2 < 8
blob_bottom_rois_->mutable_cpu_data()[4 + 5*i] = 3; // y2 < 12
blob_bottom_rois_->mutable_cpu_data()[4 + 5*i] = 3; // y2 < 6

blob_bottom_vec_.push_back(blob_bottom_rois_);
blob_top_vec_.push_back(blob_top_data_);

filler.Fill(this->blob_bottom_data_2_);
blob_bottom_vec_2_.push_back(blob_bottom_data_2_);

// Pool over the entire bottom of feature map 1
blob_bottom_rois_2_->mutable_cpu_data()[0] = 1;
blob_bottom_rois_2_->mutable_cpu_data()[1] = 0;
blob_bottom_rois_2_->mutable_cpu_data()[2] = 0;
blob_bottom_rois_2_->mutable_cpu_data()[3] = 19;
blob_bottom_rois_2_->mutable_cpu_data()[4] = 11;

blob_bottom_vec_2_.push_back(blob_bottom_rois_2_);
blob_top_vec_2_.push_back(blob_top_data_2_);
}
virtual ~ROIPoolingLayerTest() {
delete blob_bottom_data_;
delete blob_bottom_rois_;
delete blob_top_data_;
delete blob_bottom_data_2_;
delete blob_bottom_rois_2_;
delete blob_top_data_2_;
}
Blob<Dtype>* const blob_bottom_data_;
Blob<Dtype>* const blob_bottom_rois_;
Blob<Dtype>* const blob_top_data_;
vector<Blob<Dtype>*> blob_bottom_vec_;
vector<Blob<Dtype>*> blob_top_vec_;

Blob<Dtype>* const blob_bottom_data_2_;
Blob<Dtype>* const blob_bottom_rois_2_;
Blob<Dtype>* const blob_top_data_2_;
vector<Blob<Dtype>*> blob_bottom_vec_2_;
vector<Blob<Dtype>*> blob_top_vec_2_;
};

// Forward and Backward CPU not implemented yet
TYPED_TEST_CASE(ROIPoolingLayerTest, TestDtypes);
TYPED_TEST_CASE(ROIPoolingLayerTest, TestDtypesAndDevices);

TYPED_TEST(ROIPoolingLayerTest, TestGradient) {
TYPED_TEST(ROIPoolingLayerTest, TestForward) {
typedef typename TypeParam::Dtype Dtype;
LayerParameter layer_param;
ROIPoolingParameter* roi_pooling_param =
layer_param.mutable_roi_pooling_param();

// 12 x 20 pooling with bin_size_h == 1 && bin_size_w == 1
roi_pooling_param->set_pooled_h(12);
roi_pooling_param->set_pooled_w(20);
ROIPoolingLayer<Dtype> layer_2(layer_param);
layer_2.SetUp(this->blob_bottom_vec_2_, this->blob_top_vec_2_);
layer_2.Forward(this->blob_bottom_vec_2_, this->blob_top_vec_2_);
for (int i = 0; i < this->blob_top_data_2_->count(); ++i) {
EXPECT_EQ(this->blob_top_data_2_->cpu_data()[i],
this->blob_bottom_data_2_->cpu_data()[i+3*12*20]);
}

// 6 x 10 pooling with bin_size_h == 2 && bin_size_w == 2
roi_pooling_param->set_pooled_h(6);
roi_pooling_param->set_pooled_w(6);
ROIPoolingLayer<TypeParam> layer(layer_param);
GradientChecker<TypeParam> checker(1e-4, 1e-2);
roi_pooling_param->set_pooled_w(10);
ROIPoolingLayer<Dtype> layer(layer_param);
layer.SetUp(this->blob_bottom_vec_2_, this->blob_top_vec_2_);
layer.Forward(this->blob_bottom_vec_2_, this->blob_top_vec_2_);
int n = 1;
for (int c = 0; c < 3; ++c) {
for (int ph = 0; ph < 6; ++ph) {
for (int pw = 0; pw < 10; ++pw) {
Dtype maxval = -FLT_MAX;
for (int h = 2 * ph; h < 2 * (ph + 1); ++h) {
for (int w = 2 * pw; w < 2 * (pw + 1); ++w) {
maxval = std::max(maxval, this->blob_bottom_data_2_->cpu_data()[
((n * 3 + c) * 12 + h) * 20 + w]);
}
}
EXPECT_EQ(this->blob_top_data_2_->cpu_data()[(c * 6 + ph) * 10 + pw],
maxval);
}
}
}
}

TYPED_TEST(ROIPoolingLayerTest, TestGradient) {
typedef typename TypeParam::Dtype Dtype;
LayerParameter layer_param;
ROIPoolingParameter* roi_pooling_param =
layer_param.mutable_roi_pooling_param();
roi_pooling_param->set_pooled_h(3);
roi_pooling_param->set_pooled_w(4);
ROIPoolingLayer<Dtype> layer(layer_param);
GradientChecker<Dtype> checker(1e-4, 1e-2);
checker.CheckGradientExhaustive(&layer, this->blob_bottom_vec_,
this->blob_top_vec_, 0);
}
#endif

} // namespace caffe

0 comments on commit dc2f501

Please sign in to comment.