// 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. #include "lite/backends/xpu/builder.h" #include "lite/kernels/xpu/bridges/registry.h" namespace paddle { namespace lite { namespace kernels { namespace xpu { namespace bridges { node_map_type ConvConverter(const std::shared_ptr op, graph_ctx_type* graph_ctx, const node_map_type& input_nodes) { auto scope = op->scope(); auto op_info = op->op_info(); auto op_type = op_info->Type(); auto unique_op_type = lite::xpu::UniqueName(op_type); LOG(INFO) << "[XPU] Converting " << op_type << "... "; // get input, filter and op attributes auto input_var_name = op_info->Input("Input").front(); auto input = scope->FindVar(input_var_name)->GetMutable(); auto input_dims = input->dims(); auto filter_var_name = op_info->Input("Filter").front(); auto filter = scope->FindVar(filter_var_name)->GetMutable(); auto filter_dims = filter->dims(); auto bs = input_dims[0]; auto oc = filter_dims[0]; CHECK_EQ(input_dims.size(), 4); CHECK_EQ(filter_dims.size(), 4); auto strides = op_info->GetAttr>("strides"); auto paddings = op_info->GetAttr>("paddings"); auto groups = op_info->GetAttr("groups"); auto dilations = op_info->GetAttr>("dilations"); auto fuse_relu = op_info->GetAttr("fuse_relu"); CHECK_EQ(strides.size(), 2L); CHECK_EQ(paddings.size(), 4L); CHECK_EQ(dilations.size(), 2L); std::vector output_shape({bs, oc}); for (size_t i = 0; i < 2; i++) { const int dkernel = dilations[i] * (filter_dims[2 + i] - 1) + 1; output_shape.push_back( (input_dims[i + 2] + paddings[2 * i] + paddings[2 * i + 1] - dkernel) / strides[i] + 1); } DDim output_dims(output_shape); bool pads_equal = (paddings[0] == paddings[1]) && (paddings[2] == paddings[3]); if (!pads_equal) { LOG(FATAL) << "Padding requies pad_top==pad_bottom and pad_lef==pad_right."; } // check context CHECK(graph_ctx != nullptr); CHECK(graph_ctx->builder != nullptr); CHECK(graph_ctx->params != nullptr); // create filter node CHECK(!input_nodes.count(filter_var_name)); auto filter_const_node = std::make_shared( graph_ctx->builder->CreateTensor(filter_var_name, lite::xpu::CvtShape(filter_dims), ::xtcl::Float(32))); auto filter_const_tensor = lite::xpu::CvtTensor(filter); graph_ctx->params->emplace( std::make_pair(filter_var_name, *filter_const_tensor)); // create conv node and set input, filter, bias nodes and attributes auto conv_attrs = xtcl::make_node(); conv_attrs->strides = std::move(lite::xpu::CvtShape(strides)); conv_attrs->padding = std::move(lite::xpu::CvtShape(paddings)); conv_attrs->dilation = std::move(lite::xpu::CvtShape(dilations)); conv_attrs->groups = groups; // conv_attrs->channels = nullptr; conv_attrs->kernel_size = std::move(xtcl::Array(nullptr)); conv_attrs->data_layout = "NCHW"; conv_attrs->kernel_layout = "OIHW"; conv_attrs->out_layout = ""; // conv_attrs->out_dtype = ""; CHECK(input_nodes.count(input_var_name)); auto conv_node = std::make_shared(graph_ctx->builder->CreateConv2D( *input_nodes.at(input_var_name), *filter_const_node, conv_attrs)); graph_ctx->builder->SetLayer(unique_op_type); // create bias node if has bias // supports the bias nodes with the following dimensions // 0: {oc} // 1: {1, oc, oh, ow} // 2: {n, oc, oh, ow} if (lite::xpu::HasInputArg(op_info, scope, "Bias")) { auto bias_var_name = op_info->Input("Bias").front(); auto* bias = scope->FindVar(bias_var_name)->GetMutable(); auto bias_dims = bias->dims(); auto bias_data_size = bias_dims.production(); auto output_data_size = output_dims.production(); std::vector bias_shape; bool is_channel_bias = false; if (bias_data_size == oc) { // 0: {oc} bias_shape = {oc}; is_channel_bias = true; } else if (bias_data_size == output_data_size / bs) { // 1: {1, oc, oh, ow} bias_shape = {1, output_dims[1], output_dims[2], output_dims[3]}; } else if (bias_data_size == output_data_size) { // 2: {n, oc, oh, ow} bias_shape = output_dims.Vectorize(); } else { LOG(ERROR) << "bias dimension " << bias_dims << " isn't supported in conv2d Op when output dimension is " << output_dims; } std::shared_ptr bias_node = nullptr; if (input_nodes.count(bias_var_name)) { // bias node from input node bias_node = input_nodes.at(bias_var_name); } else { // bias node with const tensor auto bias_const_node = std::make_shared( graph_ctx->builder->CreateTensor(bias_var_name, lite::xpu::CvtShape(bias_shape), ::xtcl::Float(32))); auto bias_const_tensor = lite::xpu::CvtTensor(bias, bias_shape); graph_ctx->params->emplace( std::make_pair(bias_var_name, *bias_const_tensor)); bias_node = bias_const_node; } std::shared_ptr add_node = nullptr; if (is_channel_bias) { add_node = std::make_shared( graph_ctx->builder->CreateBiasAdd(*conv_node, 1, *bias_node)); } else { add_node = std::make_shared( graph_ctx->builder->CreateBinaryOp("add", *conv_node, *bias_node)); } graph_ctx->builder->SetLayer(unique_op_type + "/add"); conv_node = add_node; } // output converted nodes node_map_type output_nodes; if (fuse_relu) { // append relu node if fuse_relu is true auto relu_node = std::make_shared( graph_ctx->builder->CreateRelu(*conv_node)); graph_ctx->builder->SetLayer(unique_op_type + "/relu"); output_nodes[op_info->Output("Output").front()] = relu_node; } else { output_nodes[op_info->Output("Output").front()] = conv_node; } return output_nodes; } } // namespace bridges } // namespace xpu } // namespace kernels } // namespace lite } // namespace paddle REGISTER_XPU_BRIDGE(conv2d, paddle::lite::kernels::xpu::bridges::ConvConverter); REGISTER_XPU_BRIDGE(depthwise_conv2d, paddle::lite::kernels::xpu::bridges::ConvConverter);