/* Copyright (c) 2019 PaddlePaddle Authors. All Rights Reserved. * * Licensed 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. */ #pragma once #include #include #include "paddle/fluid/framework/op_registry.h" #include "paddle/fluid/operators/math/im2col.h" #include "paddle/fluid/operators/math/math_function.h" namespace paddle { namespace operators { using Tensor = framework::Tensor; inline int CalcOutputSize(int input_size, int filter_size, int dilation, int padding1, int padding2, int stride) { const int dkernel = dilation * (filter_size - 1) + 1; int output_size = (input_size + padding1 + padding2 - dkernel) / stride + 1; return output_size; } template class UnfoldOpKernel : public framework::OpKernel { public: void Compute(const framework::ExecutionContext& ctx) const override { const Tensor* input = ctx.Input("X"); const int batch_size = static_cast(input->dims()[0]); Tensor* output = ctx.Output("Y"); output->mutable_data(ctx.GetPlace()); std::vector kernel_sizes = ctx.Attr>("kernel_sizes"); std::vector strides = ctx.Attr>("strides"); std::vector paddings = ctx.Attr>("paddings"); std::vector dilations = ctx.Attr>("dilations"); math::Im2ColFunctor im2col; auto& dev_ctx = ctx.template device_context(); auto input_dims = input->dims(); int output_height = CalcOutputSize(input_dims[2], kernel_sizes[0], dilations[0], paddings[0], paddings[2], strides[0]); int output_width = CalcOutputSize(input_dims[3], kernel_sizes[1], dilations[1], paddings[1], paddings[3], strides[1]); framework::DDim input_shape({input_dims[1], input_dims[2], input_dims[3]}); framework::DDim output_matrix_shape({input_dims[1], kernel_sizes[0], kernel_sizes[1], output_height, output_width}); for (int i = 0; i < batch_size; i++) { Tensor in_batch = input->Slice(i, i + 1).Resize(input_shape); Tensor out_batch = output->Slice(i, i + 1).Resize(output_matrix_shape); im2col(dev_ctx, in_batch, dilations, strides, paddings, &out_batch); } } }; template class UnfoldGradOpKernel : public framework::OpKernel { public: void Compute(const framework::ExecutionContext& ctx) const override { const Tensor* output_grad = ctx.Input(framework::GradVarName("Y")); Tensor* input_grad = ctx.Output(framework::GradVarName("X")); input_grad->mutable_data(ctx.GetPlace()); if ((!output_grad) || (!input_grad)) return; std::vector kernel_sizes = ctx.Attr>("kernel_sizes"); std::vector strides = ctx.Attr>("strides"); std::vector paddings = ctx.Attr>("paddings"); std::vector dilations = ctx.Attr>("dilations"); const int batch_size = static_cast(input_grad->dims()[0]); auto input_dims = input_grad->dims(); int output_height = CalcOutputSize(input_dims[2], kernel_sizes[0], dilations[0], paddings[0], paddings[2], strides[0]); int output_width = CalcOutputSize(input_dims[3], kernel_sizes[1], dilations[1], paddings[1], paddings[3], strides[1]); framework::DDim input_shape({input_dims[1], input_dims[2], input_dims[3]}); framework::DDim output_matrix_shape({input_dims[1], kernel_sizes[0], kernel_sizes[1], output_height, output_width}); math::Col2ImFunctor col2im; auto& dev_ctx = ctx.template device_context(); math::SetConstant set_zero; set_zero(dev_ctx, input_grad, static_cast(0)); for (int i = 0; i < batch_size; i++) { Tensor out_grad_batch = output_grad->Slice(i, i + 1).Resize(output_matrix_shape); Tensor in_grad_batch = input_grad->Slice(i, i + 1).Resize(input_shape); col2im(dev_ctx, out_grad_batch, dilations, strides, paddings, &in_grad_batch); } } }; } // namespace operators } // namespace paddle