From dd0f9b96d841b174776efeb5df6489ba73f27340 Mon Sep 17 00:00:00 2001 From: LielinJiang <50691816+LielinJiang@users.noreply.github.com> Date: Tue, 6 Sep 2022 10:47:49 +0800 Subject: [PATCH] [TRT] Add silu converter (#45588) * add silu converter --- .../ir_passes/tensorrt_subgraph_pass.cc | 1 + .../fluid/inference/api/analysis_predictor.cc | 1 + .../inference/tensorrt/convert/CMakeLists.txt | 1 + .../inference/tensorrt/convert/silu_op.cc | 80 +++++++++++ .../tensorrt/convert/test_silu_op.cc | 46 ++++++ paddle/fluid/inference/tensorrt/op_teller.cc | 5 +- .../ir/inference/test_trt_convert_silu.py | 135 ++++++++++++++++++ 7 files changed, 268 insertions(+), 1 deletion(-) create mode 100644 paddle/fluid/inference/tensorrt/convert/silu_op.cc create mode 100644 paddle/fluid/inference/tensorrt/convert/test_silu_op.cc create mode 100755 python/paddle/fluid/tests/unittests/ir/inference/test_trt_convert_silu.py diff --git a/paddle/fluid/inference/analysis/ir_passes/tensorrt_subgraph_pass.cc b/paddle/fluid/inference/analysis/ir_passes/tensorrt_subgraph_pass.cc index 30697e6686..cd79b3fcde 100644 --- a/paddle/fluid/inference/analysis/ir_passes/tensorrt_subgraph_pass.cc +++ b/paddle/fluid/inference/analysis/ir_passes/tensorrt_subgraph_pass.cc @@ -600,6 +600,7 @@ REGISTER_PASS_CAPABILITY(tensorrt_subgraph_pass) .EQ("fc", 0) .EQ("shuffle_channel", 0) .EQ("swish", 0) + .EQ("silu", 0) .EQ("split", 0) .LE("instance_norm", 1) .EQ("gelu", 0) diff --git a/paddle/fluid/inference/api/analysis_predictor.cc b/paddle/fluid/inference/api/analysis_predictor.cc index 2fa14d867d..9feca9620f 100755 --- a/paddle/fluid/inference/api/analysis_predictor.cc +++ b/paddle/fluid/inference/api/analysis_predictor.cc @@ -2132,6 +2132,7 @@ USE_TRT_CONVERTER(conv2d_transpose); USE_TRT_CONVERTER(leaky_relu); USE_TRT_CONVERTER(shuffle_channel); USE_TRT_CONVERTER(swish); +USE_TRT_CONVERTER(silu); USE_TRT_CONVERTER(group_norm); USE_TRT_CONVERTER(instance_norm); USE_TRT_CONVERTER(layer_norm); diff --git a/paddle/fluid/inference/tensorrt/convert/CMakeLists.txt b/paddle/fluid/inference/tensorrt/convert/CMakeLists.txt index 4f563c2df8..1bb7d3c5b1 100644 --- a/paddle/fluid/inference/tensorrt/convert/CMakeLists.txt +++ b/paddle/fluid/inference/tensorrt/convert/CMakeLists.txt @@ -23,6 +23,7 @@ list( multihead_matmul_op.cc shuffle_channel_op.cc swish_op.cc + silu_op.cc instance_norm_op.cc stack_op.cc transpose_op.cc diff --git a/paddle/fluid/inference/tensorrt/convert/silu_op.cc b/paddle/fluid/inference/tensorrt/convert/silu_op.cc new file mode 100644 index 0000000000..3a7b593574 --- /dev/null +++ b/paddle/fluid/inference/tensorrt/convert/silu_op.cc @@ -0,0 +1,80 @@ +/* Copyright (c) 2022 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/inference/tensorrt/convert/op_converter.h" + +namespace nvinfer1 { +class ILayer; +} // namespace nvinfer1 +namespace paddle { +namespace framework { +class Scope; + +namespace proto { +class OpDesc; +} // namespace proto +} // namespace framework +} // namespace paddle + +namespace paddle { +namespace inference { +namespace tensorrt { + +class SiluOpConverter : public OpConverter { + public: + void operator()(const framework::proto::OpDesc& op, + const framework::Scope& scope, + bool test_mode) override { + VLOG(4) << "convert fluid silu op to tensorrt layer"; + + framework::OpDesc op_desc(op, nullptr); + // Declare inputs + int input_num = op_desc.Input("X").size(); + PADDLE_ENFORCE_EQ(input_num, + 1, + platform::errors::InvalidArgument( + "The input X's size must equal to 1 in TRT silu op." + " But received X's size %d.", + input_num)); + auto* input = engine_->GetITensor(op_desc.Input("X")[0]); + // Get output + size_t output_num = op_desc.Output("Out").size(); + PADDLE_ENFORCE_EQ( + output_num, + 1UL, + platform::errors::InvalidArgument( + "The output Out's size must equal to 1 in TRT silu op. " + "But received Out's size %u.", + output_num)); + + nvinfer1::ILayer* layer = nullptr; + + auto* sigmoid = TRT_ENGINE_ADD_LAYER( + engine_, Activation, *input, nvinfer1::ActivationType::kSIGMOID); + layer = TRT_ENGINE_ADD_LAYER(engine_, + ElementWise, + *input, + *(sigmoid->getOutput(0)), + nvinfer1::ElementWiseOperation::kPROD); + + auto output_name = op_desc.Output("Out")[0]; + RreplenishLayerAndOutput(layer, "silu", {output_name}, test_mode); + } +}; + +} // namespace tensorrt +} // namespace inference +} // namespace paddle + +REGISTER_TRT_OP_CONVERTER(silu, SiluOpConverter); diff --git a/paddle/fluid/inference/tensorrt/convert/test_silu_op.cc b/paddle/fluid/inference/tensorrt/convert/test_silu_op.cc new file mode 100644 index 0000000000..33b04b4e65 --- /dev/null +++ b/paddle/fluid/inference/tensorrt/convert/test_silu_op.cc @@ -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 + +#include "paddle/fluid/inference/tensorrt/convert/op_converter.h" +#include "paddle/fluid/inference/tensorrt/convert/ut_helper.h" + +namespace paddle { +namespace inference { +namespace tensorrt { + +TEST(silu_op, test_silu) { + std::unordered_set parameters; + framework::Scope scope; + TRTConvertValidation validator(10, parameters, scope, 1000); + validator.DeclInputVar("silu_input", nvinfer1::Dims3(3, 2, 2)); + validator.DeclOutputVar("silu_out", nvinfer1::Dims3(3, 2, 2)); + + // Prepare Op description + framework::OpDesc desc; + desc.SetType("silu"); + desc.SetInput("X", {"silu_input"}); + desc.SetOutput("Out", {"silu_out"}); + + validator.SetOp(*desc.Proto()); + + validator.Execute(1); +} + +} // namespace tensorrt +} // namespace inference +} // namespace paddle + +USE_OP(silu); diff --git a/paddle/fluid/inference/tensorrt/op_teller.cc b/paddle/fluid/inference/tensorrt/op_teller.cc index 32e999b879..8243bb9620 100644 --- a/paddle/fluid/inference/tensorrt/op_teller.cc +++ b/paddle/fluid/inference/tensorrt/op_teller.cc @@ -121,6 +121,7 @@ struct SimpleOpTypeSetTeller : public Teller { "fc", "shuffle_channel", "swish", + "silu", "split", "instance_norm", "gelu", @@ -228,6 +229,7 @@ struct SimpleOpTypeSetTeller : public Teller { "fc", "shuffle_channel", "swish", + "silu", "split", "instance_norm", "gelu", @@ -309,7 +311,8 @@ bool OpTeller::Tell(const framework::ir::Node* node, "tan", "tanh", "sinh", "cosh", "asin", "acos", "atan", "asinh", "atanh", - "ceil", "floor", "erf"}; + "ceil", "floor", "erf", + "silu"}; if (act_op_list.find(op_type) != act_op_list.end()) { auto* block = desc.Block(); if (block == nullptr) { diff --git a/python/paddle/fluid/tests/unittests/ir/inference/test_trt_convert_silu.py b/python/paddle/fluid/tests/unittests/ir/inference/test_trt_convert_silu.py new file mode 100755 index 0000000000..2b5ac81e30 --- /dev/null +++ b/python/paddle/fluid/tests/unittests/ir/inference/test_trt_convert_silu.py @@ -0,0 +1,135 @@ +# Copyright (c) 2022 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. + +from trt_layer_auto_scan_test import TrtLayerAutoScanTest, SkipReasons +from program_config import TensorConfig, ProgramConfig +import numpy as np +import paddle.inference as paddle_infer +from functools import partial +from typing import Optional, List, Callable, Dict, Any, Set +import unittest + + +class TrtConvertSwishTest(TrtLayerAutoScanTest): + + def is_program_valid(self, program_config: ProgramConfig) -> bool: + return True + + def sample_program_configs(self): + + def generate_input1(dims, attrs: List[Dict[str, Any]]): + if dims == 1: + return np.ones([3]).astype(np.float32) + elif dims == 2: + return np.ones([3, 64]).astype(np.float32) + elif dims == 3: + return np.ones([3, 64, 64]).astype(np.float32) + else: + return np.ones([1, 3, 64, 64]).astype(np.float32) + + for dims in [1, 2, 3, 4]: + for beta in [1.0, 2.0, 3.0]: + self.dims = dims + + ops_config = [{ + "op_type": "silu", + "op_inputs": { + "X": ["input_data"], + }, + "op_outputs": { + "Out": ["output_data"] + }, + "op_attrs": {} + }] + ops = self.generate_op_config(ops_config) + + program_config = ProgramConfig( + ops=ops, + weights={}, + inputs={ + "input_data": + TensorConfig( + data_gen=partial(generate_input1, dims, {})) + }, + outputs=["output_data"]) + + yield program_config + + def sample_predictor_configs( + self, program_config) -> (paddle_infer.Config, List[int], float): + + def generate_dynamic_shape(attrs): + if self.dims == 1: + self.dynamic_shape.min_input_shape = {"input_data": [1]} + self.dynamic_shape.max_input_shape = {"input_data": [128]} + self.dynamic_shape.opt_input_shape = {"input_data": [64]} + elif self.dims == 2: + self.dynamic_shape.min_input_shape = {"input_data": [1, 32]} + self.dynamic_shape.max_input_shape = {"input_data": [4, 64]} + self.dynamic_shape.opt_input_shape = {"input_data": [3, 64]} + elif self.dims == 3: + self.dynamic_shape.min_input_shape = {"input_data": [1, 32, 32]} + self.dynamic_shape.max_input_shape = { + "input_data": [10, 64, 64] + } + self.dynamic_shape.opt_input_shape = {"input_data": [3, 64, 64]} + else: + self.dynamic_shape.min_input_shape = { + "input_data": [1, 3, 32, 32] + } + self.dynamic_shape.max_input_shape = { + "input_data": [4, 3, 64, 64] + } + self.dynamic_shape.opt_input_shape = { + "input_data": [1, 3, 64, 64] + } + + def clear_dynamic_shape(): + self.dynamic_shape.min_input_shape = {} + self.dynamic_shape.max_input_shape = {} + self.dynamic_shape.opt_input_shape = {} + + def generate_trt_nodes_num(attrs, dynamic_shape): + if self.dims == 1: + return 0, 3 + return 1, 2 + + attrs = [ + program_config.ops[i].attrs for i in range(len(program_config.ops)) + ] + + # for static_shape + clear_dynamic_shape() + self.trt_param.precision = paddle_infer.PrecisionType.Float32 + yield self.create_inference_config(), generate_trt_nodes_num( + attrs, False), 1e-5 + self.trt_param.precision = paddle_infer.PrecisionType.Half + yield self.create_inference_config(), generate_trt_nodes_num( + attrs, False), (1e-3, 1e-3) + + # for dynamic_shape + generate_dynamic_shape(attrs) + self.trt_param.precision = paddle_infer.PrecisionType.Float32 + yield self.create_inference_config(), generate_trt_nodes_num( + attrs, True), 1e-5 + self.trt_param.precision = paddle_infer.PrecisionType.Half + yield self.create_inference_config(), generate_trt_nodes_num( + attrs, True), (1e-3, 1e-3) + + def test(self): + self.run_test() + + +if __name__ == "__main__": + unittest.main() -- GitLab