| /** |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include "./pooling.h" |
| #include "singa/model/layer.h" |
| #include <cmath> |
| |
| namespace singa { |
| |
| RegisterLayerClass(singacpp_pooling, Pooling); |
| void Pooling::Setup(const Shape& in_sample, const LayerConf& conf) { |
| Layer::Setup(in_sample, conf); |
| PoolingConf pool_conf = conf.pooling_conf(); |
| if (pool_conf.has_kernel_size()) { |
| kernel_w_ = kernel_h_ = pool_conf.kernel_size(); |
| } else { |
| kernel_w_ = pool_conf.kernel_w(); |
| kernel_h_ = pool_conf.kernel_h(); |
| } |
| CHECK_GT(kernel_w_, 0u); |
| CHECK_GT(kernel_h_, 0u); |
| |
| if (pool_conf.has_pad()) { |
| pad_w_ = pad_h_ = pool_conf.pad(); |
| } else { |
| pad_w_ = pool_conf.pad_w(); |
| pad_h_ = pool_conf.pad_h(); |
| } |
| CHECK_GE(pad_w_, 0u); |
| CHECK_GE(pad_h_, 0u); |
| |
| if (pool_conf.has_stride()) { |
| stride_w_ = stride_h_ = pool_conf.stride(); |
| } else { |
| stride_w_ = pool_conf.stride_w(); |
| stride_h_ = pool_conf.stride_h(); |
| } |
| CHECK_GT(stride_w_, 0u); |
| CHECK_GE(stride_h_, 0u); // 0 for 1D pooling |
| |
| pool_ = pool_conf.pool(); |
| CHECK(pool_ == PoolingConf_PoolMethod_AVE || |
| pool_ == PoolingConf_PoolMethod_MAX || |
| pool_ == PoolingConf_PoolMethod_STOCHASTIC) |
| << "Padding implemented only for average and max pooling."; |
| |
| CHECK_EQ(in_sample.size(), 3u); |
| channels_ = in_sample.at(0); |
| height_ = in_sample.at(1); |
| width_ = in_sample.at(2); |
| pooled_height_ = 1; |
| if (pool_conf.ceil()) { |
| // TODO(wangwei): caffe also ensures the last pooling window starts strictly |
| // within the original area |
| if (stride_h_ > 0) |
| 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 { |
| if (stride_h_ > 0) |
| pooled_height_ = |
| static_cast<size_t>((height_ + 2 * pad_h_ - kernel_h_) / stride_h_) + 1; |
| pooled_width_ = |
| static_cast<size_t>((width_ + 2 * pad_w_ - kernel_w_) / stride_w_) + 1; |
| } |
| out_sample_shape_ = vector<size_t>{channels_, pooled_height_, pooled_width_}; |
| } |
| |
| const Tensor Pooling::Forward(int flag, const Tensor& input) { |
| CHECK(buf_.empty()); |
| CHECK_EQ(input.device()->lang(), kCpp); |
| CHECK_EQ(input.nDim(), 4u); |
| size_t batchsize = input.shape(0); |
| DataType dtype = input.data_type(); |
| |
| // TODO(wangwei) update the layer config if the input sample shape changes |
| CHECK(input.shape(1) == channels_ && input.shape(2) == height_ && |
| input.shape(3) == width_) << "input sample shape should not change"; |
| |
| auto dev = input.device(); |
| Shape shape{batchsize, channels_, pooled_height_, pooled_width_}; |
| Tensor output(shape, dev, dtype); |
| float* outptr = new float[output.Size()]; |
| auto inptr = input.data<float>(); |
| if (pool_ == PoolingConf_PoolMethod_MAX) { |
| Tensor mask; |
| mask.ResetLike(output); |
| float* maskptr = new float[mask.Size()]; |
| ForwardMaxPooling(inptr, batchsize, channels_, height_, width_, |
| pooled_height_, pooled_width_, kernel_h_, kernel_w_, |
| pad_h_, pad_w_, stride_h_, stride_w_, outptr, |
| maskptr); |
| mask.CopyDataFromHostPtr(maskptr, mask.Size()); |
| if (flag & kTrain) buf_.push(mask); |
| delete[] maskptr; |
| } else if (pool_ == PoolingConf_PoolMethod_AVE) |
| ForwardAvgPooling(inptr, batchsize, channels_, height_, width_, |
| pooled_height_, pooled_width_, kernel_h_, kernel_w_, |
| pad_h_, pad_w_, stride_h_, stride_w_, outptr); |
| else |
| LOG(FATAL) << "Unknown pooling method"; |
| |
| output.CopyDataFromHostPtr(outptr, output.Size()); |
| delete[] outptr; |
| return output; |
| } |
| |
| const std::pair<Tensor, vector<Tensor>> |
| Pooling::Backward(int flag, const Tensor& grad) { |
| CHECK_EQ(grad.device()->lang(), kCpp); |
| CHECK_EQ(grad.nDim(), 4u); |
| |
| vector<Tensor> param_grad; |
| |
| auto batchsize = grad.shape(0); |
| auto dtype = grad.data_type(); |
| auto dev = grad.device(); |
| Shape shape{batchsize, channels_, height_, width_}; |
| |
| Tensor dx(shape, dev, dtype); |
| auto gradptr = grad.data<float>(); |
| float* dxptr = new float[dx.Size()]; |
| |
| if (pool_ == PoolingConf_PoolMethod_MAX) { |
| CHECK(!buf_.empty()); |
| Tensor mask = buf_.top(); |
| buf_.pop(); |
| auto maskptr = mask.data<float>(); |
| BackwardMaxPooling(gradptr, maskptr, batchsize, channels_, height_, width_, |
| pooled_height_, pooled_width_, kernel_h_, kernel_w_, |
| pad_h_, pad_w_, stride_h_, stride_w_, dxptr); |
| } else if (pool_ == PoolingConf_PoolMethod_AVE) { |
| BackwardAvgPooling(gradptr, batchsize, channels_, height_, width_, |
| pooled_height_, pooled_width_, kernel_h_, kernel_w_, |
| pad_h_, pad_w_, stride_h_, stride_w_, dxptr); |
| } else { |
| LOG(FATAL) << "Unknown pooling method"; |
| } |
| |
| dx.CopyDataFromHostPtr(dxptr, dx.Size()); |
| delete[] dxptr; |
| return std::make_pair(dx, param_grad); |
| } |
| |
| void Pooling::ForwardMaxPooling(const float* bottom, const int num, |
| const int channels, |
| const int height, const int width, |
| const int pooled_h, const int pooled_w, |
| const int kernel_h, const int kernel_w, |
| const int pad_h, const int pad_w, |
| const int stride_h, const int stride_w, |
| float* top, float* mask) { |
| int top_count = num * pooled_h * pooled_w * channels; |
| for (int i = 0; i < top_count; i++) { |
| mask[i] = -1; |
| top[i] = -FLT_MAX; |
| } |
| const int bottom_offset = height * width; |
| const int top_offset = pooled_h * pooled_w; |
| // The main loop |
| for (int n = 0; n < num; ++n) { |
| for (int c = 0; c < channels; ++c) { |
| for (int ph = 0; ph < pooled_h; ++ph) { |
| for (int pw = 0; pw < pooled_w; ++pw) { |
| int hstart = ph * stride_h - pad_h; |
| int wstart = pw * stride_w - pad_w; |
| int hend = std::min(hstart + kernel_h, height); |
| int wend = std::min(wstart + kernel_w, width); |
| hstart = std::max(hstart, 0); |
| wstart = std::max(wstart, 0); |
| const int top_index = ph * pooled_w + pw; |
| for (int h = hstart; h < hend; ++h) { |
| for (int w = wstart; w < wend; ++w) { |
| const int index = h * width + w; |
| if (bottom[index] > top[top_index]) { |
| top[top_index] = bottom[index]; |
| mask[top_index] = (float) index; |
| } |
| } |
| } |
| } |
| } |
| // compute offset |
| bottom += bottom_offset; |
| top += top_offset; |
| mask += top_offset; |
| } |
| } |
| } |
| |
| void Pooling::BackwardMaxPooling(const float* top, const float* mask, |
| const int num, const int channels, |
| const int height, const int width, |
| const int pooled_h, const int pooled_w, |
| const int kernel_h, const int kernel_w, |
| const int pad_h, const int pad_w, |
| const int stride_h, const int stride_w, |
| float* bottom) { |
| const int top_offset = pooled_h * pooled_w; |
| const int bottom_offset = height * width; |
| memset(bottom, 0, sizeof(float) * num * channels * bottom_offset); |
| for (int n = 0; n < num; ++n) { |
| for (int c = 0; c < channels; ++c) { |
| for (int ph = 0; ph < pooled_h; ++ph) { |
| for (int pw = 0; pw < pooled_w; ++pw) { |
| const int top_idx = ph * pooled_w + pw; |
| const int bottom_idx = static_cast<int>(mask[top_idx]); |
| bottom[bottom_idx] += top[top_idx]; |
| } |
| } |
| top += top_offset; |
| mask += top_offset; |
| bottom += bottom_offset; |
| } |
| } |
| } |
| |
| void Pooling::ForwardAvgPooling(const float* bottom, const int num, |
| const int channels, |
| const int height, const int width, |
| const int pooled_h, const int pooled_w, |
| const int kernel_h, const int kernel_w, |
| const int pad_h, const int pad_w, |
| const int stride_h, const int stride_w, |
| float* top) { |
| int top_count = num * pooled_h * pooled_w * channels; |
| for (int i = 0; i < top_count; i++) { |
| top[i] = 0; |
| } |
| const int bottom_offset = height * width; |
| const int top_offset = pooled_h * pooled_w; |
| // The main loop |
| for (int n = 0; n < num; ++n) { |
| for (int c = 0; c < channels; ++c) { |
| for (int ph = 0; ph < pooled_h; ++ph) { |
| for (int pw = 0; pw < pooled_w; ++pw) { |
| int hstart = ph * stride_h - pad_h; |
| int wstart = pw * stride_w - pad_w; |
| int hend = std::min(hstart + kernel_h, height + pad_h); |
| int wend = std::min(wstart + kernel_w, width + pad_w); |
| int pool_size = (hend - hstart) * (wend - wstart); |
| hstart = std::max(hstart, 0); |
| wstart = std::max(wstart, 0); |
| hend = std::min(hend, height); |
| wend = std::min(wend, width); |
| const int top_index = ph * pooled_w + pw; |
| for (int h = hstart; h < hend; ++h) { |
| for (int w = wstart; w < wend; ++w) { |
| const int index = h * width + w; |
| top[top_index] += bottom[index]; |
| } |
| } |
| top[top_index] /= pool_size; |
| } |
| } |
| // compute offset |
| bottom += bottom_offset; |
| top += top_offset; |
| } |
| } |
| } |
| |
| void Pooling::BackwardAvgPooling(const float* top, const int num, |
| const int channels, |
| const int height, const int width, |
| const int pooled_h, const int pooled_w, |
| const int kernel_h, const int kernel_w, |
| const int pad_h, const int pad_w, |
| const int stride_h, const int stride_w, |
| float* bottom) { |
| const int top_offset = pooled_h * pooled_w; |
| const int bottom_offset = height * width; |
| memset(bottom, 0, sizeof(float) * num * channels * bottom_offset); |
| for (int n = 0; n < num; ++n) { |
| for (int c = 0; c < channels; ++c) { |
| for (int ph = 0; ph < pooled_h; ++ph) { |
| for (int pw = 0; pw < pooled_w; ++pw) { |
| int hstart = ph * stride_h - pad_h; |
| int wstart = pw * stride_w - pad_w; |
| int hend = std::min(hstart + kernel_h, height + pad_h); |
| int wend = std::min(wstart + kernel_w, width + pad_w); |
| int pool_size = (hend - hstart) * (wend - wstart); |
| hstart = std::max(hstart, 0); |
| wstart = std::max(wstart, 0); |
| hend = std::min(hend, height); |
| wend = std::min(wend, width); |
| const int top_index = ph * pooled_w + pw; |
| for (int h = hstart; h < hend; ++h) { |
| for (int w = wstart; w < wend; ++w) { |
| const int index = h * width + w; |
| bottom[index] += top[top_index] / pool_size; |
| } |
| } |
| } |
| } |
| top += top_offset; |
| bottom += bottom_offset; |
| } |
| } |
| } |
| } // namespace singa |