// 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 "paddle/fluid/framework/ir/conv_affine_channel_fuse_pass.h" #include #include #include #include "paddle/fluid/framework/lod_tensor.h" #include "paddle/fluid/operators/math/cpu_vec.h" #include "paddle/fluid/platform/enforce.h" namespace paddle { namespace framework { namespace ir { #define GET_CONV_BN_NODES(pattern_name) \ /* OPERATORS */ \ GET_IR_NODE_FROM_SUBGRAPH(conv, conv, pattern_name); \ GET_IR_NODE_FROM_SUBGRAPH(affine_channel, affine_channel, pattern_name); \ /* CONV inputs */ \ GET_IR_NODE_FROM_SUBGRAPH(conv_weight, conv_weight, pattern_name); \ /* CONV outputs */ \ GET_IR_NODE_FROM_SUBGRAPH(conv_out, conv_out, pattern_name); \ /* Affine Channel inputs */ \ GET_IR_NODE_FROM_SUBGRAPH(ac_scale, ac_scale, pattern_name); \ GET_IR_NODE_FROM_SUBGRAPH(ac_bias, ac_bias, pattern_name); \ /* Affine channel outputs */ \ GET_IR_NODE_FROM_SUBGRAPH(ac_out, ac_out, pattern_name); /* Out */ void recompute_bias_and_weights(const Scope* scope, ir::Node* conv_weight, const ir::Node& ac_scale, const LoDTensor& ac_bias_tensor, LoDTensor* eltwise_y_in_tensor) { using EigenVectorArrayMap = Eigen::Map>; using ConstEigenVectorArrayMap = Eigen::Map>; using EigenMatrixArrayMap = Eigen::Map< Eigen::Array>; // Re-compute bias of conv2d from AffineChannel PADDLE_ENFORCE_EQ(eltwise_y_in_tensor->dims(), ac_bias_tensor.dims()); auto* scale_tensor = scope->FindVar(ac_scale.Name())->GetMutable(); ConstEigenVectorArrayMap scale_array(scale_tensor->data(), scale_tensor->numel(), 1); ConstEigenVectorArrayMap ac_bias_array(ac_bias_tensor.data(), ac_bias_tensor.numel(), 1); EigenVectorArrayMap eltwise_y_in_array( eltwise_y_in_tensor->mutable_data(platform::CPUPlace()), eltwise_y_in_tensor->numel(), 1); eltwise_y_in_array = (eltwise_y_in_array * scale_array) + ac_bias_array; // Re-compute weight of conv2d from AffineChannel auto* weights = scope->FindVar(conv_weight->Name())->GetMutable(); auto weights_shape = weights->dims(); auto weights_shape_2d = flatten_to_2d(weights_shape, 1); EigenMatrixArrayMap weights_array_2d( weights->mutable_data(platform::CPUPlace()), weights_shape_2d[0], weights_shape_2d[1]); weights_array_2d.colwise() *= scale_array; } std::unique_ptr ConvAffineChannelFusePass::ApplyImpl( std::unique_ptr graph) const { PADDLE_ENFORCE(graph.get()); FusePassBase::Init(name_scope_, graph.get()); auto* scope = param_scope(); PADDLE_ENFORCE(scope); GraphPatternDetector gpd; auto* conv_input = gpd.mutable_pattern() ->NewNode(patterns::PDNodeName(name_scope_, "conv_input")) ->AsInput() ->assert_is_op_input("conv2d", "Input"); patterns::ConvAffineChannel conv_ac_pattern(gpd.mutable_pattern(), name_scope_); conv_ac_pattern(conv_input, false /*with_eltwise_add*/); int found_conv_ac_count = 0; auto handler = [&](const GraphPatternDetector::subgraph_t& subgraph, Graph* g) { VLOG(4) << "handle ConvAffineChannel fuse"; GET_CONV_BN_NODES(conv_ac_pattern); // check if fuse can be done and if MKL-DNN should be used FuseOptions fuse_option = FindFuseOption(*conv, *affine_channel); if (fuse_option == DO_NOT_FUSE) { VLOG(3) << "do not perform conv+affinechannel fuse"; return; } // Create eltwise_y (conv bias) variable VarDesc eltwise_y_in_desc( patterns::PDNodeName(name_scope_, "eltwise_y_in")); eltwise_y_in_desc.SetPersistable(true); auto* eltwise_y_in_node = g->CreateVarNode(&eltwise_y_in_desc); auto* eltwise_y_in_tensor = scope->Var(eltwise_y_in_node->Name())->GetMutable(); // Get affine_channel bias auto* ac_bias_tensor = scope->FindVar(ac_bias->Name())->GetMutable(); // Initialize eltwise_y eltwise_y_in_tensor->Resize(ac_bias_tensor->dims()); std::fill_n(eltwise_y_in_tensor->mutable_data(platform::CPUPlace()), eltwise_y_in_tensor->numel(), 0.0f); // update weights and biases recompute_bias_and_weights(scope, conv_weight, *ac_scale, *ac_bias_tensor, eltwise_y_in_tensor); // create an elementwise add node. OpDesc desc; desc.SetInput("X", std::vector({conv_out->Name()})); desc.SetInput("Y", std::vector({eltwise_y_in_node->Name()})); desc.SetOutput("Out", std::vector({ac_out->Name()})); desc.SetType("elementwise_add"); desc.SetAttr("axis", 1); auto eltwise_op = g->CreateOpNode(&desc); // OpDesc will be copied. GraphSafeRemoveNodes(graph.get(), {ac_scale, ac_bias, affine_channel}); IR_NODE_LINK_TO(conv_out, eltwise_op); IR_NODE_LINK_TO(eltwise_y_in_node, eltwise_op); IR_NODE_LINK_TO(eltwise_op, ac_out); found_conv_ac_count++; }; gpd(graph.get(), handler); AddStatis(found_conv_ac_count); return graph; } std::unique_ptr ConvEltwiseAddAffineChannelFusePass::ApplyImpl( std::unique_ptr graph) const { PADDLE_ENFORCE(graph.get()); FusePassBase::Init(name_scope_, graph.get()); auto* scope = param_scope(); PADDLE_ENFORCE(scope); GraphPatternDetector gpd; auto* conv_input = gpd.mutable_pattern() ->NewNode(patterns::PDNodeName(name_scope_, "conv_input")) ->AsInput() ->assert_is_op_input("conv2d", "Input"); patterns::ConvAffineChannel conv_ac_pattern(gpd.mutable_pattern(), name_scope_); conv_ac_pattern(conv_input, true /*with_eltwise_add*/); int found_conv_ac_count = 0; auto handler = [&](const GraphPatternDetector::subgraph_t& subgraph, Graph* g) { VLOG(4) << "handle ConvBN fuse"; GET_CONV_BN_NODES(conv_ac_pattern); // OPERATORS GET_IR_NODE_FROM_SUBGRAPH(eltwise, eltwise, conv_ac_pattern); // BIAS inputs GET_IR_NODE_FROM_SUBGRAPH(eltwise_y_in, eltwise_y_in, conv_ac_pattern); // BIAS outputs GET_IR_NODE_FROM_SUBGRAPH(eltwise_out, eltwise_out, conv_ac_pattern); // Get eltwise_y (conv bias) variable auto* eltwise_y_in_tensor = scope->FindVar(eltwise_y_in->Name())->GetMutable(); // Get batch norm bias auto* ac_bias_tensor = scope->FindVar(ac_bias->Name())->GetMutable(); recompute_bias_and_weights(scope, conv_weight, *ac_scale, *ac_bias_tensor, eltwise_y_in_tensor); // Update the elementwise_add node eltwise->Op()->SetAttr("axis", 1); eltwise->Op()->SetOutput("Out", std::vector({ac_out->Name()})); GraphSafeRemoveNodes(graph.get(), {ac_scale, ac_bias, affine_channel, eltwise_out}); IR_NODE_LINK_TO(eltwise, ac_out); found_conv_ac_count++; }; gpd(graph.get(), handler); AddStatis(found_conv_ac_count); return graph; } } // namespace ir } // namespace framework } // namespace paddle REGISTER_PASS(conv_affine_channel_fuse_pass, paddle::framework::ir::ConvAffineChannelFusePass); REGISTER_PASS(conv_eltwiseadd_affine_channel_fuse_pass, paddle::framework::ir::ConvEltwiseAddAffineChannelFusePass);