/* 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 #include #include #include "ngraph/ngraph.hpp" #include "paddle/fluid/operators/ngraph/ops/op_bridge.h" #include "paddle/fluid/platform/ngraph_helper.h" namespace paddle { namespace operators { namespace ngraphs { std::shared_ptr GroupedConvolution( const std::shared_ptr& data_batch, const std::shared_ptr& filters, const ngraph::Strides strides, const ngraph::Strides dilations, const ngraph::CoordinateDiff& paddings, size_t groups) { auto& data_shape = data_batch->get_shape(); auto& filter_shape = filters->get_shape(); ngraph::NodeVector ng_slices; for (size_t i = 0; i < groups; ++i) { size_t channel_step = filter_shape.at(1); const std::vector lower_bound{0, i * channel_step, 0, 0}; const std::vector upper_bound{data_shape.at(0), (i + 1) * channel_step, data_shape.at(2), data_shape.at(3)}; auto data_slice = std::make_shared( data_batch, lower_bound, upper_bound); size_t filter_step = filter_shape.at(0) / groups; const std::vector filter_lower_bound{i * filter_step, 0, 0, 0}; const std::vector filter_upper_bound{ (i + 1) * filter_step, filter_shape.at(1), filter_shape.at(2), filter_shape.at(3)}; auto filter_slice = std::make_shared( filters, filter_lower_bound, filter_upper_bound); auto ng_conv = std::make_shared( data_slice, filter_slice, strides, dilations, paddings, paddings); ng_slices.push_back(ng_conv); } size_t concat_axis = 1; return std::make_shared(ng_slices, concat_axis); } std::shared_ptr GroupedGradConvolutionFilter( const std::shared_ptr& data_batch, const std::shared_ptr& filters, const std::shared_ptr& doutput, const ngraph::Strides strides, const ngraph::Strides dilations, const ngraph::CoordinateDiff& paddings, size_t groups) { auto& data_shape = data_batch->get_shape(); auto& filter_shape = filters->get_shape(); auto& out_shape = doutput->get_shape(); ngraph::NodeVector ng_slices; for (size_t i = 0; i < groups; ++i) { size_t channel_step = filter_shape.at(1); const std::vector lower_bound{0, i * channel_step, 0, 0}; const std::vector upper_bound{data_shape.at(0), (i + 1) * channel_step, data_shape.at(2), data_shape.at(3)}; auto data_slice = std::make_shared( data_batch, lower_bound, upper_bound); size_t filter_step = filter_shape.at(0) / groups; const std::vector filter_lower_bound{i * filter_step, 0, 0, 0}; const std::vector filter_upper_bound{ (i + 1) * filter_step, filter_shape.at(1), filter_shape.at(2), filter_shape.at(3)}; auto filter_slice = std::make_shared( filters, filter_lower_bound, filter_upper_bound); const std::vector olower_bound{0, i * filter_step, 0, 0}; const std::vector oupper_bound{out_shape.at(0), (i + 1) * filter_step, out_shape.at(2), out_shape.at(3)}; auto out_slice = std::make_shared(doutput, olower_bound, oupper_bound); auto ng_conv = std::make_shared( data_slice, filter_slice->get_shape(), out_slice, strides, dilations, paddings, paddings, ngraph::Strides{1, 1}); ng_slices.push_back(ng_conv); } size_t concat_axis = 0; return std::make_shared(ng_slices, concat_axis); } std::shared_ptr GroupedGradConvolutionData( const std::shared_ptr& data_batch, const std::shared_ptr& filters, const std::shared_ptr& doutput, const ngraph::Strides strides, const ngraph::Strides dilations, const ngraph::CoordinateDiff& paddings, size_t groups) { auto& data_shape = data_batch->get_shape(); auto& filter_shape = filters->get_shape(); auto& out_shape = doutput->get_shape(); ngraph::NodeVector ng_slices; for (size_t i = 0; i < groups; ++i) { size_t channel_step = filter_shape.at(1); const std::vector lower_bound{0, i * channel_step, 0, 0}; const std::vector upper_bound{data_shape.at(0), (i + 1) * channel_step, data_shape.at(2), data_shape.at(3)}; auto data_slice = std::make_shared( data_batch, lower_bound, upper_bound); size_t filter_step = filter_shape.at(0) / groups; const std::vector filter_lower_bound{i * filter_step, 0, 0, 0}; const std::vector filter_upper_bound{ (i + 1) * filter_step, filter_shape.at(1), filter_shape.at(2), filter_shape.at(3)}; auto filter_slice = std::make_shared( filters, filter_lower_bound, filter_upper_bound); const std::vector olower_bound{0, i * filter_step, 0, 0}; const std::vector oupper_bound{out_shape.at(0), (i + 1) * filter_step, out_shape.at(2), out_shape.at(3)}; auto out_slice = std::make_shared(doutput, olower_bound, oupper_bound); auto ng_conv = std::make_shared( data_slice->get_shape(), filter_slice, out_slice, strides, dilations, paddings, paddings, ngraph::Strides{1, 1}); ng_slices.push_back(ng_conv); } size_t concat_axis = 1; return std::make_shared(ng_slices, concat_axis); } void BuildConv2dNode( const std::shared_ptr& op, std::shared_ptr< std::unordered_map>> ngb_node_map) { auto op_attrs = paddle::framework::AttrReader(op->Attrs()); auto filters = paddle::platform::GetInputNode(op, "Filter", ngb_node_map); auto input = paddle::platform::GetInputNode(op, "Input", ngb_node_map); std::vector strides = op_attrs.Get>("strides"); std::vector paddings = op_attrs.Get>("paddings"); std::vector dilations = op_attrs.Get>("dilations"); const ngraph::Strides ng_strides{static_cast(strides.at(0)), static_cast(strides.at(1))}; const ngraph::Strides ng_dilations{static_cast(dilations.at(0)), static_cast(dilations.at(1))}; const ngraph::CoordinateDiff ng_paddings{ static_cast(paddings.at(0)), static_cast(paddings.at(1))}; int groups = static_cast(op_attrs.Get("groups")); PADDLE_ENFORCE_GE(groups, 1, "conv groups needs be no less than 1"); std::shared_ptr result; if (groups == 1) { result = std::make_shared( input, filters, ng_strides, ng_dilations, ng_paddings, ng_paddings); } else { result = GroupedConvolution(input, filters, ng_strides, ng_dilations, ng_paddings, groups); } paddle::platform::SetOutputNode(op, "Output", result, ngb_node_map); } void BuildConv2dGradNode( const std::shared_ptr& op, std::shared_ptr< std::unordered_map>> ngb_node_map) { auto op_attrs = paddle::framework::AttrReader(op->Attrs()); auto filter = paddle::platform::GetInputNode(op, "Filter", ngb_node_map); auto input = paddle::platform::GetInputNode(op, "Input", ngb_node_map); auto doutput = paddle::platform::GetInputNode(op, "Output@GRAD", ngb_node_map); int groups = op_attrs.Get("groups"); std::vector strides = op_attrs.Get>("strides"); std::vector paddings = op_attrs.Get>("paddings"); std::vector dilations = op_attrs.Get>("dilations"); const ngraph::Strides ng_strides{static_cast(strides.at(0)), static_cast(strides.at(1))}; const ngraph::Strides ng_dilations{static_cast(dilations.at(0)), static_cast(dilations.at(1))}; const ngraph::CoordinateDiff ng_paddings{ static_cast(paddings.at(0)), static_cast(paddings.at(1))}; std::shared_ptr dfilter; std::shared_ptr dinput; if (groups == 1) { dfilter = std::make_shared( input, filter->get_shape(), doutput, ng_strides, ng_dilations, ng_paddings, ng_paddings, ngraph::Strides{1, 1}); dinput = std::make_shared( input->get_shape(), filter, doutput, ng_strides, ng_dilations, ng_paddings, ng_paddings, ngraph::Strides{1, 1}); } else { dfilter = GroupedGradConvolutionFilter(input, filter, doutput, ng_strides, ng_dilations, ng_paddings, groups); dinput = GroupedGradConvolutionData(input, filter, doutput, ng_strides, ng_dilations, ng_paddings, groups); } paddle::platform::SetOutputNode(op, "Filter@GRAD", dfilter, ngb_node_map); paddle::platform::SetOutputNode(op, "Input@GRAD", dinput, ngb_node_map); } } // namespace ngraphs } // namespace operators } // namespace paddle REGISTER_NG_OP(conv2d, BuildConv2dNode); REGISTER_NG_OP(conv2d_grad, BuildConv2dGradNode); REGISTER_NG_OP(depthwise_conv2d, BuildConv2dNode);