diff --git a/paddle/operators/crop_op.cc b/paddle/operators/crop_op.cc new file mode 100644 index 0000000000000000000000000000000000000000..75fa42fc18b1bde7731584e830dcbe3f7393c6f9 --- /dev/null +++ b/paddle/operators/crop_op.cc @@ -0,0 +1,81 @@ +/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve. + + 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. */ + +#include "paddle/operators/crop_op.h" + +namespace paddle { +namespace operators { + +using framework::Tensor; + +class CropOp : public framework::OperatorWithKernel { + public: + using framework::OperatorWithKernel::OperatorWithKernel; + + protected: + void InferShape(const framework::InferShapeContext &ctx) const override { + auto dim0 = ctx.Input("X")->dims(); + auto Y = ctx.Input("Y"); + if (Y == nullptr) { + auto shape = GetAttr>("shape"); + PADDLE_ENFORCE_EQ( + shape.size(), dim0.size(), + "Shape size should be equal to dimention size of input tensor."); + ctx.Output("Out")->Resize(paddle::framework::make_ddim(shape)); + } else { + ctx.Output("Out")->Resize(Y->dims()); + } + } +}; + +class CropOpMaker : public framework::OpProtoAndCheckerMaker { + public: + CropOpMaker(framework::OpProto *proto, framework::OpAttrChecker *op_checker) + : OpProtoAndCheckerMaker(proto, op_checker) { + AddInput("X", "The input of crop op"); + AddInput("Y", "The input used as reference for cropping. "); + AddOutput("Out", "The output of crop op."); + AddComment(R"DOC( +Crop Operator. +)DOC"); + AddAttr>("offsets", "The offsets for cropping."); + AddAttr>("shape", "The shape for cropping."); + } +}; + +class CropOpGrad : public framework::OperatorWithKernel { + public: + using framework::OperatorWithKernel::OperatorWithKernel; + + protected: + void InferShape(const framework::InferShapeContext &ctx) const override { + PADDLE_ENFORCE_NOT_NULL(ctx.InputVar("X"), "Input(X) should not be null"); + PADDLE_ENFORCE_NOT_NULL(ctx.InputVar(framework::GradVarName("Out")), + "Input(Out@GRAD) should not be null"); + auto x_dims = ctx.Input("X")->dims(); + auto *x_grad = ctx.Output(framework::GradVarName("X")); + + x_grad->Resize(x_dims); + } +}; + +} // namespace operators +} // namespace paddle + +namespace ops = paddle::operators; +REGISTER_OP(crop, ops::CropOp, ops::CropOpMaker, crop_grad, ops::CropOpGrad); +REGISTER_OP_CPU_KERNEL(crop, + ops::CropKernel); +REGISTER_OP_CPU_KERNEL(crop_grad, + ops::CropGradKernel); diff --git a/paddle/operators/crop_op.cu b/paddle/operators/crop_op.cu new file mode 100644 index 0000000000000000000000000000000000000000..5afed4946598b89c6a72fb5e9a921bc2b1b631da --- /dev/null +++ b/paddle/operators/crop_op.cu @@ -0,0 +1,22 @@ +/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve. + + 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. */ + +#define EIGEN_USE_GPU +#include "paddle/operators/crop_op.h" + +namespace ops = paddle::operators; +REGISTER_OP_GPU_KERNEL(crop, + ops::CropKernel); +REGISTER_OP_GPU_KERNEL(crop_grad, + ops::CropGradKernel); diff --git a/paddle/operators/crop_op.h b/paddle/operators/crop_op.h new file mode 100644 index 0000000000000000000000000000000000000000..40e05869ddde1652b590455f15b41656e8604999 --- /dev/null +++ b/paddle/operators/crop_op.h @@ -0,0 +1,138 @@ +/* Copyright (c) 2016 CropdleCropdle Authors. All Rights Reserve. + + 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 "paddle/framework/eigen.h" +#include "paddle/framework/op_registry.h" + +namespace paddle { +namespace operators { + +template +using EigenTensor = framework::EigenTensor; + +using Tensor = framework::Tensor; + +template +void CropFunction(const framework::ExecutionContext& context) { + auto* x = context.Input("X"); + auto* out = context.Output("Out"); + out->mutable_data(context.GetPlace()); + auto x_dims = x->dims(); + auto out_dims = out->dims(); + + auto offsets = context.op().GetAttr>("offsets"); + PADDLE_ENFORCE_EQ( + x_dims.size(), offsets.size(), + "Offsets size should be equal to dimension size of input tensor."); + + Eigen::array, D> paddings; + for (size_t i = 0; i < D; ++i) { + paddings[i].first = -(offsets[i]); + paddings[i].second = -(x_dims[i] - out_dims[i] - offsets[i]); + } + + auto x_tensor = EigenTensor::From(*x); + auto out_tensor = EigenTensor::From(*out); + auto place = context.GetEigenDevice(); + out_tensor.device(place) = x_tensor.pad(paddings, 0); +} + +template +class CropKernel : public framework::OpKernel { + public: + void Compute(const framework::ExecutionContext& context) const override { + int dim = context.Input("X")->dims().size(); + switch (dim) { + case 1: + CropFunction(context); + break; + case 2: + CropFunction(context); + break; + case 3: + CropFunction(context); + break; + case 4: + CropFunction(context); + break; + case 5: + CropFunction(context); + break; + case 6: + CropFunction(context); + break; + default: + LOG(ERROR) << "Only ranks up to 6 supported."; + } + } +}; + +template +void CropGradFunction(const framework::ExecutionContext& context) { + auto* d_out = context.Input(framework::GradVarName("Out")); + auto* d_x = context.Output(framework::GradVarName("X")); + d_x->mutable_data(context.GetPlace()); + auto d_x_dims = d_x->dims(); + auto d_out_dims = d_out->dims(); + + auto offsets = context.op().GetAttr>("offsets"); + + Eigen::array, D> paddings; + for (int i = 0; i < d_out_dims.size(); ++i) { + paddings[i].first = offsets[i]; + paddings[i].second = d_x_dims[i] - d_out_dims[i] - offsets[i]; + } + + auto d_x_tensor = EigenTensor::From(*d_x); + auto d_out_tensor = EigenTensor::From(*d_out); + auto place = context.GetEigenDevice(); + d_x_tensor.device(place) = d_out_tensor.pad(paddings, 0); +} + +template +class CropGradKernel : public framework::OpKernel { + public: + void Compute(const framework::ExecutionContext& context) const override { + size_t dim = + context.Input(framework::GradVarName("Out"))->dims().size(); + switch (dim) { + case 1: + CropGradFunction(context); + break; + case 2: + CropGradFunction(context); + break; + case 3: + CropGradFunction(context); + break; + case 4: + CropGradFunction(context); + break; + case 5: + CropGradFunction(context); + break; + case 6: + CropGradFunction(context); + break; + default: + LOG(ERROR) << "Only ranks up to 6 supported."; + } + } +}; + +} // namespace operators +} // namespace paddle diff --git a/paddle/pybind/pybind.cc b/paddle/pybind/pybind.cc index 6896422617be0a3c73dc7b0d7cc1113075fa2f4b..e2ea5c92af81ae26415c92e2cc9e89f3418c900f 100644 --- a/paddle/pybind/pybind.cc +++ b/paddle/pybind/pybind.cc @@ -48,6 +48,7 @@ USE_NO_KERNEL_OP(identity); USE_OP(minus); USE_CPU_ONLY_OP(gather); USE_CPU_ONLY_OP(scatter); +USE_OP(crop); namespace paddle { namespace framework { diff --git a/python/paddle/v2/framework/tests/test_crop_op.py b/python/paddle/v2/framework/tests/test_crop_op.py new file mode 100644 index 0000000000000000000000000000000000000000..27d8332acfa83b0392aafddf3b5cdff8647884d9 --- /dev/null +++ b/python/paddle/v2/framework/tests/test_crop_op.py @@ -0,0 +1,35 @@ +import unittest +import numpy as np +from paddle.v2.framework.op import Operator +from gradient_checker import GradientChecker +from op_test_util import OpTestMeta + + +class TestCropOp(unittest.TestCase): + __metaclass__ = OpTestMeta + + def setUp(self): + self.type = "crop" + self.inputs = {'X': np.random.random((16, 16)).astype("float32"), } + self.attrs = {} + self.attrs['offsets'] = [2, 3] + self.attrs['shape'] = [8, 8] + self.outputs = {'Out': self.inputs['X'][2:10, 3:11]} + + +class TestCropGradOp(GradientChecker): + def setUp(self): + self.op = Operator( + type="crop", X="X", Out="Out", offsets=[2, 3], shape=[8, 8]) + self.inputs = {'X': np.random.random((16, 16)).astype("float32"), } + + def test_normal(self): + self.check_grad( + self.op, self.inputs, set(["X"]), "Out", max_relative_error=0.5) + + def test_cpu_gpu_compare(self): + self.compare_grad(self.op, self.inputs) + + +if __name__ == '__main__': + unittest.main()