diff --git a/src/operators/conv_op.cpp b/src/operators/conv_op.cpp index 148b0f69f9633f1d82979ab324c5997fb6fcb1c1..bfddcf14acbba016c4e4333e05fcc7dd6eebc509 100644 --- a/src/operators/conv_op.cpp +++ b/src/operators/conv_op.cpp @@ -21,13 +21,6 @@ limitations under the License. */ namespace paddle_mobile { namespace operators { -int ConvOutputSize(int input_size, int filter_size, int dilation, int padding, - int stride) { - const int dkernel = dilation * (filter_size - 1) + 1; - int output_size = (input_size + 2 * padding - dkernel) / stride + 1; - return output_size; -} - template void ConvOp::InferShape() const { // std::cout << " begin get dims: " << std::endl; diff --git a/src/operators/conv_op.h b/src/operators/conv_op.h index 1557f2f06eed8237f7b7e9ff44adc233129a49a3..f15f286b606db1403b0e0e609bfc38caac2c5105 100644 --- a/src/operators/conv_op.h +++ b/src/operators/conv_op.h @@ -44,5 +44,12 @@ class ConvOp : public framework::OperatorWithKernel { ConvParam param_; }; +inline int ConvOutputSize(int input_size, int filter_size, int dilation, + int padding, int stride) { + const int dkernel = dilation * (filter_size - 1) + 1; + int output_size = (input_size + 2 * padding - dkernel) / stride + 1; + return output_size; +} + } // namespace operators } // namespace paddle_mobile diff --git a/src/operators/depthwise_conv_op.cpp b/src/operators/depthwise_conv_op.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2538298175c5ea40d7e44338caee853a73c089c4 --- /dev/null +++ b/src/operators/depthwise_conv_op.cpp @@ -0,0 +1,57 @@ +/* Copyright (c) 2018 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. */ + +#include "operators/depthwise_conv_op.h" +#include +#include "framework/data_type.h" +#include "framework/op_proto_maker.h" +#include "framework/op_registry.h" +#include "operators/conv_op.h" + +namespace paddle_mobile { +namespace operators { + +template +void DepthwiseConvOp::InferShape() const { + auto in_dims = param_.Input()->dims(); + auto filter_dims = param_.Filter()->dims(); + const std::vector &strides = param_.Strides(); + std::vector paddings = param_.Paddings(); + int groups = param_.Groups(); + std::vector dilations = param_.Dilations(); + + PADDLE_MOBILE_ENFORCE((in_dims.size() == filter_dims.size() && + dilations.size() == paddings.size() && + paddings.size() == strides.size()), + "ConvParam is not suitable"); + + std::vector output_shape({in_dims[0], filter_dims[0]}); + for (size_t i = 0; i < strides.size(); ++i) { + output_shape.push_back(ConvOutputSize(in_dims[i + 2], filter_dims[i + 2], + dilations[i], paddings[i], + strides[i])); + } + + framework::DDim ddim = framework::make_ddim(output_shape); + param_.Output()->Resize(ddim); +} + +template class DepthwiseConvOp; + +} // namespace operators +} // namespace paddle_mobile + +namespace ops = paddle_mobile::operators; +USE_OP(depthwise_conv2d); +REGISTER_OPERATOR(depthwise_conv2d, ops::DepthwiseConvOp); diff --git a/src/operators/depthwise_conv_op.h b/src/operators/depthwise_conv_op.h new file mode 100644 index 0000000000000000000000000000000000000000..c47fa0ffcacd54a5ddf7280419ca1170173bde1b --- /dev/null +++ b/src/operators/depthwise_conv_op.h @@ -0,0 +1,49 @@ +/* Copyright (c) 2018 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 "framework/operator.h" +#include "operators/kernel/depthwise_conv_kernel.h" + +namespace paddle_mobile { +namespace operators { + +template +class DepthwiseConvOp : public framework::OperatorWithKernel { + public: + DepthwiseConvOp(const std::string &type, const VariableNameMap &inputs, + const VariableNameMap &outputs, + const framework::AttributeMap &attrs, + std::shared_ptr scope) + : framework::OperatorWithKernel(type, inputs, outputs, attrs, + scope), + param_(inputs, outputs, attrs, *scope) {} + + using framework::OperatorWithKernel::OperatorWithKernel; + void InferShape() const override; + + void RunImpl() const { + operators::DepthwiseConvKernel kernel; + kernel.Compute(param_); + this->ClearVariables({"Filter", "Input"}); + } + + private: + ConvParam param_; +}; + +} // namespace operators +} // namespace paddle_mobile diff --git a/src/operators/kernel/arm/conv_kernel.cpp b/src/operators/kernel/arm/conv_kernel.cpp index 1e2572b984734dcd88be7c1c750fc0f07448e66d..f04b8156c9d3c88520b1c74b60a20f41e7fedc98 100644 --- a/src/operators/kernel/arm/conv_kernel.cpp +++ b/src/operators/kernel/arm/conv_kernel.cpp @@ -17,19 +17,6 @@ limitations under the License. */ namespace paddle_mobile { namespace operators { -bool IsExpand(const std::vector &filter_dim, - const std::vector &strides, const std::vector &paddings, - const std::vector &dilations) { - bool filter_1 = true, strides_1 = true, padding_0 = true, dilation_1 = true; - for (size_t j = 0; j < strides.size(); ++j) { - filter_1 = filter_1 && (static_cast(filter_dim[j + 2]) == 1); - strides_1 = strides_1 && (strides[j] == 1); - padding_0 = padding_0 && (paddings[j] == 0); - dilation_1 = dilation_1 && (dilations[j] == 1); - } - return !(filter_1 && strides_1 && padding_0 && dilation_1); -} - template <> void ConvKernel::Compute(const ConvParam ¶m) const { LOG(kLOG_DEBUG) << param; diff --git a/src/operators/kernel/arm/depthwise_conv_kernel.cpp b/src/operators/kernel/arm/depthwise_conv_kernel.cpp new file mode 100644 index 0000000000000000000000000000000000000000..73aa9953cfcbc8efe0ed9d3bf094455cfbb4fe6c --- /dev/null +++ b/src/operators/kernel/arm/depthwise_conv_kernel.cpp @@ -0,0 +1,126 @@ +/* Copyright (c) 2018 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. */ + +#include "operators/kernel/depthwise_conv_kernel.h" +#include "operators/kernel/conv_kernel.h" + +namespace paddle_mobile { +namespace operators { + +template <> +void DepthwiseConvKernel::Compute(const ConvParam ¶m) const { + LOG(kLOG_DEBUG) << param; + + const Tensor *input = param.Input(); + Tensor filter = *param.Filter(); + Tensor *output = param.Output(); + output->mutable_data(); + + int groups = param.Groups(); + std::vector strides = param.Strides(); + std::vector paddings = param.Paddings(); + std::vector dilations = param.Dilations(); + + DLOG << " compute end get Attrs " << strides[0]; + + const int batch_size = static_cast(input->dims()[0]); + + std::vector filter_shape_vec(framework::vectorize(filter.dims())); + std::vector output_shape_vec(framework::vectorize(output->dims())); + + size_t data_dim = filter_shape_vec.size() - 2; + std::vector col_shape_vec(1 + 2 * data_dim); + col_shape_vec[0] = input->dims()[1] / groups; + for (size_t j = 0; j < data_dim; ++j) { + col_shape_vec[j + 1] = filter_shape_vec[j + 2]; + col_shape_vec[j + 1 + data_dim] = output_shape_vec[j + 2]; + } + framework::DDim col_shape(framework::make_ddim(col_shape_vec)); + + framework::DDim col_matrix_shape = + framework::flatten_to_2d(col_shape, data_dim + 1); + + bool is_expand = IsExpand(filter_shape_vec, strides, paddings, dilations); + Tensor col; + Tensor col_matrix; + if (is_expand) { + col.mutable_data(col_shape); + col_matrix.ShareDataWith(col); + col_matrix.Resize(col_matrix_shape); + } + DLOG << " col_shape = " << col_shape; + DLOG << " col_matrix_shape = " << col_matrix_shape; + + framework::DDim input_shape = framework::slice_ddim( + input->dims(), 1, static_cast(input->dims().size())); + DLOG << " input_shape = " << input_shape; + + framework::DDim filter_matrix_shape = {filter.dims()[0], + filter.numel() / filter.dims()[0]}; + filter.Resize(filter_matrix_shape); + DLOG << " filter.dims() = " << filter.dims(); + + framework::DDim output_matrix_shape = { + output->dims()[1], + output->numel() / (output->dims()[0] * output->dims()[1])}; + + // convolution operator: im2col(or vol2col) + gemm + int in_step = static_cast(input->dims()[1]) / groups; + int out_step = static_cast(output->dims()[1]) / groups; + + math::Vol2ColFunctor vol2col; + math::Im2ColFunctor im2col; + + 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); + DLOG << " in_batch.dims() = " << in_batch.dims(); + DLOG << " out_batch.dims() = " << out_batch.dims(); + + for (int g = 0; g < groups; g++) { + Tensor in_slice = in_batch.Slice(g * in_step, (g + 1) * in_step); + + if (!is_expand) { + col.ShareDataWith(in_slice); + col_matrix.ShareDataWith(col); + col_matrix.Resize(col_matrix_shape); + } else if (data_dim == 2U) { + // im2col + im2col(in_slice, dilations, strides, + std::vector{paddings[0], paddings[1], paddings[0], + paddings[1]}, + &col); + } else if (data_dim == 3U) { + // vol2col + vol2col(in_slice, dilations, strides, paddings, &col); + } + + // gemm + Tensor out_slice = out_batch.Slice(g * out_step, (g + 1) * out_step); + Tensor filter_slice = filter.Slice(g * out_step, (g + 1) * out_step); + DLOG << " out_slice " << out_slice.dims(); + DLOG << " filter_slice " << filter_slice.dims(); + DLOG << " col_matrix " << col_matrix.dims(); + math::matmul(filter_slice, false, col_matrix, false, + static_cast(1), &out_slice, + static_cast(0)); + auto filter_ptr = filter_slice.data(); + } + } +} + +template class DepthwiseConvKernel; + +} // namespace operators +} // namespace paddle_mobile diff --git a/src/operators/kernel/conv_kernel.h b/src/operators/kernel/conv_kernel.h index a756e2d2417cc147cb0559f946a6a70085860ecb..d43a174ffdbf0ca6dbb39e463b8e97652c7b0daf 100644 --- a/src/operators/kernel/conv_kernel.h +++ b/src/operators/kernel/conv_kernel.h @@ -12,6 +12,7 @@ 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 #include "framework/operator.h" #include "operators/math/im2col.h" #include "operators/math/math_function.h" @@ -23,12 +24,28 @@ limitations under the License. */ namespace paddle_mobile { namespace operators { -using namespace framework; +using framework::OpKernelBase; template -class ConvKernel : public framework::OpKernelBase { +class ConvKernel : public OpKernelBase { public: void Compute(const ConvParam ¶m) const; }; + +inline bool IsExpand(const std::vector &filter_dim, + const std::vector &strides, + const std::vector &paddings, + const std::vector &dilations) { + bool filter_1 = true, strides_1 = true, padding_0 = true, dilation_1 = true; + for (size_t j = 0; j < strides.size(); ++j) { + filter_1 = filter_1 && (static_cast(filter_dim[j + 2]) == 1); + strides_1 = strides_1 && (strides[j] == 1); + padding_0 = padding_0 && (paddings[j] == 0); + dilation_1 = dilation_1 && (dilations[j] == 1); + } + + return !(filter_1 && strides_1 && padding_0 && dilation_1); +} + } // namespace operators } // namespace paddle_mobile diff --git a/src/operators/kernel/depthwise_conv_kernel.h b/src/operators/kernel/depthwise_conv_kernel.h new file mode 100644 index 0000000000000000000000000000000000000000..43ddfb25cd859a7e937577221215d8352b846bff --- /dev/null +++ b/src/operators/kernel/depthwise_conv_kernel.h @@ -0,0 +1,34 @@ +/* Copyright (c) 2018 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. */ + +#include "framework/operator.h" +#include "operators/math/im2col.h" +#include "operators/math/math_function.h" +#include "operators/math/vol2col.h" +#include "operators/op_param.h" + +#pragma once; + +namespace paddle_mobile { +namespace operators { + +using framework::OpKernelBase; + +template +class DepthwiseConvKernel : public OpKernelBase { + public: + void Compute(const ConvParam ¶m) const; +}; +} // namespace operators +} // namespace paddle_mobile diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index f464c3bd94f92e8cbec1509c4e82df18658a7b1f..2bb313342e21aac9dabced37039de8225c0a64ec 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -91,3 +91,7 @@ target_link_libraries(test-googlenet paddle-mobile) # gen test ADD_EXECUTABLE(test-sigmoid operators/test_sigmoid_op.cpp test_include.h) target_link_libraries(test-sigmoid paddle-mobile) + +# gen test +ADD_EXECUTABLE(test-depthwise-conv-op operators/test_depthwise_conv_op.cpp test_helper.h test_include.h executor_for_test.h) +target_link_libraries(test-depthwise-conv-op paddle-mobile) diff --git a/test/operators/test_depthwise_conv_op.cpp b/test/operators/test_depthwise_conv_op.cpp new file mode 100644 index 0000000000000000000000000000000000000000..648b4c5db9970804a2ca140eef13e2560e36f935 --- /dev/null +++ b/test/operators/test_depthwise_conv_op.cpp @@ -0,0 +1,46 @@ +/* Copyright (c) 2018 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. */ + +#include "../executor_for_test.h" +#include "../test_include.h" +#include "operators/depthwise_conv_op.h" + +int main() { + paddle_mobile::Loader loader; + // ../models/image_classification_resnet.inference.model + auto program = loader.Load(g_mobilenet_ssd); + + PADDLE_MOBILE_ENFORCE(program.originProgram != nullptr, + "program file read fail"); + + Executor4Test> + executor(program, "depthwise_conv2d"); + + paddle_mobile::framework::LoDTensor input; + // GetInput(g_test_image_1x3x224x224, &input, {1, 3, 224, 224}); + // use SetupTensor if not has local input image . + SetupTensor(&input, {1, 32, 150, 150}, static_cast(0), + static_cast(1)); + auto input_ptr = input.data(); + auto out_ddim = paddle_mobile::framework::make_ddim({1, 32, 150, 150}); + auto output = executor.Predict(input, "batch_norm_0.tmp_3", + "depthwise_conv2d_0.tmp_0", out_ddim); + + auto output_ptr = output->data(); + for (int j = 0; j < output->numel(); ++j) { + DLOG << " value of output: " << output_ptr[j]; + } + return 0; +}