From 8d568f0326c5b6bdf938f7aa6ab73e6beb13bb96 Mon Sep 17 00:00:00 2001 From: Kai Li Date: Thu, 3 Jul 2014 10:30:26 +0800 Subject: [PATCH] Add the MultiScaleOrderlessPoolingLayer --- .../multi_scale_orderless_pooling_layer.cpp | 269 ++++++++++++++++++ 1 file changed, 269 insertions(+) create mode 100644 src/caffe/layers/multi_scale_orderless_pooling_layer.cpp diff --git a/src/caffe/layers/multi_scale_orderless_pooling_layer.cpp b/src/caffe/layers/multi_scale_orderless_pooling_layer.cpp new file mode 100644 index 00000000000..bc002078814 --- /dev/null +++ b/src/caffe/layers/multi_scale_orderless_pooling_layer.cpp @@ -0,0 +1,269 @@ +// Copyright 2014 BVLC and contributors. + +#include +#include +#include + +#include "caffe/common.hpp" +#include "caffe/layer.hpp" +#include "caffe/vision_layers.hpp" +#include "caffe/syncedmem.hpp" +#include "caffe/util/math_functions.hpp" + +using std::max; +using std::min; + +namespace caffe { + +template +void PoolingLayer::SetUp(const vector*>& bottom, + vector*>* top) { + // Set the max number of top blobs before calling base Layer::SetUp. + // If doing MAX pooling, we can optionally output an extra top Blob + // for the mask. Otherwise, we only have one top Blob. + if (this->layer_param_.pooling_param().pool() == + PoolingParameter_PoolMethod_MAX) { + max_top_blobs_ = 2; + } else { + max_top_blobs_ = 1; + } + Layer::SetUp(bottom, top); + kernel_size_ = this->layer_param_.pooling_param().kernel_size(); + stride_ = this->layer_param_.pooling_param().stride(); + pad_ = this->layer_param_.pooling_param().pad(); + if (pad_ != 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_, kernel_size_); + } + channels_ = bottom[0]->channels(); + height_ = bottom[0]->height(); + width_ = bottom[0]->width(); + pooled_height_ = static_cast(ceil(static_cast( + height_ + 2 * pad_ - kernel_size_) / stride_)) + 1; + pooled_width_ = static_cast(ceil(static_cast( + width_ + 2 * pad_ - kernel_size_) / stride_)) + 1; + if (pad_) { + // If we have padding, ensure that the last pooling starts strictly + // inside the image (instead of at the padding); otherwise clip the last. + if ((pooled_height_ - 1) * stride_ >= height_ + pad_) { + --pooled_height_; + } + if ((pooled_width_ - 1) * stride_ >= width_ + pad_) { + --pooled_width_; + } + CHECK_LT((pooled_height_ - 1) * stride_, height_ + pad_); + CHECK_LT((pooled_width_ - 1) * stride_, width_ + pad_); + } + (*top)[0]->Reshape(bottom[0]->num(), channels_, pooled_height_, + pooled_width_); + if (top->size() > 1) { + (*top)[1]->ReshapeLike(*(*top)[0]); + } + // If max pooling, we will initialize the vector index part. + if (this->layer_param_.pooling_param().pool() == + PoolingParameter_PoolMethod_MAX && top->size() == 1) { + max_idx_.reset(new Blob(bottom[0]->num(), channels_, + pooled_height_, pooled_width_)); + } + // If stochastic pooling, we will initialize the random index part. + if (this->layer_param_.pooling_param().pool() == + PoolingParameter_PoolMethod_STOCHASTIC) { + rand_idx_.Reshape(bottom[0]->num(), channels_, pooled_height_, + pooled_width_); + } +} + +// TODO(Yangqing): Is there a faster way to do pooling in the channel-first +// case? +template +Dtype PoolingLayer::Forward_cpu(const vector*>& bottom, + vector*>* top) { + const Dtype* bottom_data = bottom[0]->cpu_data(); + Dtype* top_data = (*top)[0]->mutable_cpu_data(); + const int top_count = (*top)[0]->count(); + // We'll output the mask to top[1] if it's of size >1. + const bool use_top_mask = top->size() > 1; + int* mask = NULL; // suppress warnings about uninitalized variables + Dtype* top_mask = NULL; + // Different pooling methods. We explicitly do the switch outside the for + // loop to save time, although this results in more code. + switch (this->layer_param_.pooling_param().pool()) { + case PoolingParameter_PoolMethod_MAX: + // 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_ - pad_; + int wstart = pw * stride_ - pad_; + int hend = min(hstart + kernel_size_, height_); + int wend = min(wstart + kernel_size_, 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; + 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(index); + } else { + mask[pool_index] = index; + } + } + } + } + } + } + // compute offset + 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: + 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_ - pad_; + int wstart = pw * stride_ - pad_; + int hend = min(hstart + kernel_size_, height_ + pad_); + int wend = min(wstart + kernel_size_, width_ + pad_); + 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."; + } + return Dtype(0.); +} + +template +void PoolingLayer::Backward_cpu(const vector*>& top, + const vector& propagate_down, vector*>* bottom) { + if (!propagate_down[0]) { + return; + } + const Dtype* top_diff = top[0]->cpu_diff(); + Dtype* bottom_diff = (*bottom)[0]->mutable_cpu_diff(); + // Different pooling methods. We explicitly do the switch outside the for + // loop to save time, although this results in more codes. + 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]; + 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_ - pad_; + int wstart = pw * stride_ - pad_; + int hend = min(hstart + kernel_size_, height_ + pad_); + int wend = min(wstart + kernel_size_, width_ + pad_); + 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; + } + } + } + } + // 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."; + } +} + + +INSTANTIATE_CLASS(PoolingLayer); + + +} // namespace caffe