diff --git a/python/paddle/fluid/tests/unittests/ir/inference/CMakeLists.txt b/python/paddle/fluid/tests/unittests/ir/inference/CMakeLists.txt index 45e392cd66e90a14a1176f0bd076391a811de53c..0ea9b8d2fc62f08fee6ee2acc335c0d6226445ed 100644 --- a/python/paddle/fluid/tests/unittests/ir/inference/CMakeLists.txt +++ b/python/paddle/fluid/tests/unittests/ir/inference/CMakeLists.txt @@ -38,4 +38,5 @@ set_tests_properties(test_trt_dynamic_shape PROPERTIES TIMEOUT 120) set_tests_properties(test_trt_pool_op PROPERTIES ENVIRONMENT FLAGS_fraction_of_gpu_memory_to_use=0.1 TIMEOUT 45) set_tests_properties(test_trt_reduce_mean_op PROPERTIES TIMEOUT 60) set_tests_properties(test_trt_tile_op PROPERTIES TIMEOUT 60) +set_tests_properties(test_trt_convert_conv2d PROPERTIES TIMEOUT 100) endif() diff --git a/python/paddle/fluid/tests/unittests/ir/inference/auto_scan_test.py b/python/paddle/fluid/tests/unittests/ir/inference/auto_scan_test.py new file mode 100644 index 0000000000000000000000000000000000000000..7d749cca5c2541ada25af6401e975220c8a9fe51 --- /dev/null +++ b/python/paddle/fluid/tests/unittests/ir/inference/auto_scan_test.py @@ -0,0 +1,92 @@ +# Copyright (c) 2021 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. + +import numpy as np +import unittest +import abc +import paddle +import paddle.fluid as fluid +from paddle.fluid.initializer import NumpyArrayInitializer +import paddle.fluid.core as core +from paddle import compat as cpt +import paddle.inference as paddle_infer +from typing import Optional, List, Callable, Dict, Any, Set +from program_config import TensorConfig, OpConfig, ProgramConfig, create_fake_model + + +class AutoScanTest(unittest.TestCase): + def __init__(self, methodName='runTest'): + paddle.enable_static() + super(AutoScanTest, self).__init__(methodName) + self.threshold = 1e-5 + + @abc.abstractmethod + def sample_program_configs(self) -> List[ProgramConfig]: + ''' + Generate all config with the combination of different Input tensor shape and + different Attr values. + ''' + raise NotImplementedError + + @abc.abstractmethod + def sample_predictor_configs(self) -> List[paddle_infer.Config]: + raise NotImplementedError + + def run_test_config(self, model, params, prog_config, pred_config, + feed_data) -> Dict[str, np.ndarray]: + ''' + Test a single case. + ''' + pred_config.set_model_buffer(model, len(model), params, len(params)) + predictor = paddle_infer.create_predictor(pred_config) + + for name, _ in prog_config.inputs.items(): + input_tensor = predictor.get_input_handle(name) + input_tensor.copy_from_cpu(feed_data[name]) + predictor.run() + result = {} + for out_name in prog_config.outputs: + result[out_name] = predictor.get_output_handle( + out_name).copy_to_cpu() + return result + + def assert_tensors_near(self, + threshold: float, + tensors: List[Dict[str, np.array]]): + assert len(tensors) > 1 + first = tensors[0] + for group in tensors[1:]: + for key, arr in group.items(): + self.assertTrue( + np.allclose( + first[key], arr, atol=threshold), + "Output has diff between GPU and TensorRT. ") + + def run_test(self): + for prog_config in self.sample_program_configs(): + model, params = create_fake_model(prog_config) + for batch_size in self.batch_size_set: + feed_data = {} + for name, tensor_config in prog_config.inputs.items(): + tensor_shape = tensor_config.shape.copy() + tensor_shape[0] = batch_size + feed_data[name] = np.random.random(tensor_shape).astype( + tensor_config.dtype) + results: List[Dict[str, Tensor]] = [] + for pred_config in self.sample_predictor_configs(): + results.append( + self.run_test_config(model, params, prog_config, + pred_config, feed_data)) + self.assert_tensors_near( + threshold=self.threshold, tensors=results) diff --git a/python/paddle/fluid/tests/unittests/ir/inference/program_config.py b/python/paddle/fluid/tests/unittests/ir/inference/program_config.py new file mode 100644 index 0000000000000000000000000000000000000000..399501618b6988d9ec63af48beec5ce39924ca80 --- /dev/null +++ b/python/paddle/fluid/tests/unittests/ir/inference/program_config.py @@ -0,0 +1,162 @@ +# Copyright (c) 2021 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 typing import Optional, List, Callable, Dict, Any, Set +import numpy as np +import paddle +import paddle.fluid as fluid +import paddle.fluid.core as core +from paddle import compat as cpt +from paddle.fluid.initializer import NumpyArrayInitializer +from paddle.fluid.framework import convert_np_dtype_to_dtype_ + + +class TensorConfig: + ''' + A config builder for a input or a weight. + + InputVar's shape can be [-1, xxx], batch_size + ''' + + def __init__(self, + shape: [List[int]], + dtype: [str]="float32", + data: Optional[np.array]=None): + ''' + shape: The shape of the tensor. + dtype: The data type of the tensor. + data: The value of WeightVar. for input, it should be None + ''' + self.shape = shape + self.dtype = dtype + self.data = data + + +class OpConfig: + ''' A config builder for generating a Op. ''' + + def __init__(self, + type: str, + inputs: Dict[str, List[str]], + outputs: Dict[str, List[str]], + attrs: Dict[str, Any]): + self.type = type + self.inputs = inputs + self.outputs = outputs + self.attrs = attrs + + +class ProgramConfig: + ''' A config builder for generating a Program. ''' + + def __init__(self, + ops: List[OpConfig], + weights: Dict[str, TensorConfig], + inputs: Dict[str, TensorConfig], + outputs: List[str]): + self.ops = ops + self.weights = weights + self.inputs = inputs + self.outputs = outputs + + +def create_fake_model(program_config): + ''' Create a Paddle model(in memory) according to the given config. ''' + paddle.enable_static() + main_program_desc = core.ProgramDesc() + util_program = fluid.Program() + main_block_desc = main_program_desc.block(0) + + var_desc = main_block_desc.var(cpt.to_bytes("feed")) + var_desc.set_type(core.VarDesc.VarType.FEED_MINIBATCH) + var_desc.set_persistable(True) + + index = 0 + for name, tensor_config in program_config.inputs.items(): + var_desc = main_block_desc.var(cpt.to_bytes(name)) + var_desc.set_type(core.VarDesc.VarType.LOD_TENSOR) + var_desc.set_dtype(convert_np_dtype_to_dtype_(tensor_config.dtype)) + var_desc.set_shape(tensor_config.shape) + var_desc.set_need_check_feed(True) + op_desc = main_block_desc._prepend_op() + op_desc.set_type("feed") + op_desc.set_input('X', ["feed"]) + op_desc.set_output('Out', [name]) + op_desc._set_attr("col", index) + index = index + 1 + + save_var_map = {} + for name, tensor_config in program_config.weights.items(): + var_desc = main_block_desc.var(cpt.to_bytes(name)) + var_desc.set_type(core.VarDesc.VarType.LOD_TENSOR) + var_desc.set_dtype(convert_np_dtype_to_dtype_(tensor_config.dtype)) + var_desc.set_shape(tensor_config.shape) + var_desc.set_persistable(True) + + save_var_map[name] = util_program.global_block().create_parameter( + dtype=tensor_config.dtype, + shape=tensor_config.shape, + type=core.VarDesc.VarType.LOD_TENSOR, + name=name, + initializer=NumpyArrayInitializer(tensor_config.data)) + in_vars = [] + for name in sorted(save_var_map.keys()): + in_vars.append(save_var_map[name]) + + out_var = util_program.global_block().create_var( + type=core.VarDesc.VarType.RAW, name="out_var_0") + out_var.desc.set_persistable(True) + util_program.global_block().append_op( + type='save_combine', + inputs={'X': in_vars}, + outputs={'Y': out_var}, + attrs={'file_path': '', + 'save_to_memory': True}) + for op_config in program_config.ops: + op_desc = main_block_desc.append_op() + op_desc.set_type(op_config.type) + for name, values in op_config.inputs.items(): + op_desc.set_input(name, values) + for name, values in op_config.attrs.items(): + op_desc._set_attr(name, values) + for name, values in op_config.outputs.items(): + op_desc.set_output(name, values) + var_desc = main_block_desc.var(cpt.to_bytes(name)) + var_desc.set_type(core.VarDesc.VarType.LOD_TENSOR) + op_desc.infer_var_type(main_block_desc) + op_desc.infer_shape(main_block_desc) + + for index, name in enumerate(program_config.outputs): + var_desc = main_block_desc.var(cpt.to_bytes("fetch")) + var_desc.set_type(core.VarDesc.VarType.FETCH_LIST) + var_desc.set_need_check_feed(True) + op_desc = main_block_desc.append_op() + op_desc.set_type("fetch") + op_desc.set_input('X', [name]) + op_desc.set_output('Out', ["fetch"]) + op_desc._set_attr("col", index) + + main_program_desc._set_version() + paddle.fluid.core.save_op_version_info(main_program_desc) + + model = main_program_desc.serialize_to_string() + + util_program._sync_with_cpp() + place = fluid.CPUPlace() + executor = fluid.Executor(place) + scope = fluid.Scope() + with fluid.scope_guard(scope): + executor.run(util_program) + params = scope.find_var("out_var_0").get_bytes() + return model, params diff --git a/python/paddle/fluid/tests/unittests/ir/inference/test_trt_convert_conv2d.py b/python/paddle/fluid/tests/unittests/ir/inference/test_trt_convert_conv2d.py new file mode 100644 index 0000000000000000000000000000000000000000..98c3367b3f2c3ba645acc8496ad00e1b5157a965 --- /dev/null +++ b/python/paddle/fluid/tests/unittests/ir/inference/test_trt_convert_conv2d.py @@ -0,0 +1,67 @@ +# Copyright (c) 2021 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 +from program_config import TensorConfig +import numpy as np + + +class TrtConvertConv2dTest(TrtLayerAutoScanTest): + def setUp(self): + self.ops_config = [{ + "op_type": "conv2d", + "op_inputs": { + "Input": ["input_data"], + "Filter": ["conv2d_weight"] + }, + "op_outputs": { + "Output": ["conv_output_data"] + }, + "op_attrs": { + "data_format": ["NCHW"], + "dilations": [[1, 1]], + "padding_algorithm": ["EXPLICIT"], + "groups": [1], + "paddings": [[0, 3], [3, 1]], + "strides": [[1, 1], [2, 2]], + } + }, { + "op_type": "relu", + "op_inputs": { + "X": ["conv_output_data"] + }, + "op_outputs": { + "Out": ["relu_output_data"] + }, + "op_attrs": {} + }] + self.batch_size_set = [1, 2, 4] + + def update_program_input_and_weight_with_attr(self, op_attr_list): + weight = np.random.randn(24, 3, 3, 3).astype("float32") + filter = TensorConfig(shape=[24, 3, 3, 3], data=weight) + if op_attr_list[0]["data_format"] == "NCHW": + input_data = TensorConfig(shape=[-1, 3, 64, 64]) + else: + input_data = TensorConfig(shape=[-1, 64, 64, 3]) + self.program_weights = {"conv2d_weight": filter} + self.program_inputs = {"input_data": input_data} + self.program_outputs = ["relu_output_data"] + + def test_check_output(self): + self.run_test() + + +if __name__ == "__main__": + unittest.main() diff --git a/python/paddle/fluid/tests/unittests/ir/inference/trt_layer_auto_scan_test.py b/python/paddle/fluid/tests/unittests/ir/inference/trt_layer_auto_scan_test.py new file mode 100644 index 0000000000000000000000000000000000000000..589916ad39003d7428b5899380f0cb8cba616886 --- /dev/null +++ b/python/paddle/fluid/tests/unittests/ir/inference/trt_layer_auto_scan_test.py @@ -0,0 +1,115 @@ +# Copyright (c) 2021 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. + +import numpy as np +import unittest +import itertools +import abc +import paddle +import paddle.fluid as fluid +import paddle.fluid.core as core +import paddle.inference as paddle_infer + +from paddle import compat as cpt +from typing import * +from program_config import TensorConfig, OpConfig, ProgramConfig +from auto_scan_test import AutoScanTest + + +class TrtLayerAutoScanTest(AutoScanTest): + class TensorRTParam: + ''' + TensorRT subgraph engine parameters. + ''' + + def __init__(self, workspace_size, max_batch_size, min_subgraph_size, + precision, use_static, use_calib_mode): + self.workspace_size = workspace_size + self.max_batch_size = max_batch_size + self.min_subgraph_size = min_subgraph_size + self.precision = precision + self.use_static = use_static + self.use_calib_mode = use_calib_mode + + def __init__(self, methodName='runTest'): + super(TrtLayerAutoScanTest, self).__init__(methodName) + self.trt_param = self.TensorRTParam( + workspace_size=0, + max_batch_size=4, + min_subgraph_size=0, + precision=paddle_infer.PrecisionType.Float32, + use_static=False, + use_calib_mode=False) + + def update_program_input_and_weight_with_attr(self, op_attr_list): + raise NotImplementedError + + @abc.abstractmethod + def sample_program_configs(self): + all_op_attrs_keys = [] + all_op_attrs_values = [] + for op_config in self.ops_config: + all_op_attrs_keys.append(list(op_config["op_attrs"].keys())) + all_op_attrs_values.extend(list(op_config["op_attrs"].values())) + if len(all_op_attrs_values) == 0: + all_op_attrs_values.append([None]) + for attrs_sample in itertools.product(*all_op_attrs_values): + op_attr_list = [] + index = 0 + ops = [] + for op_config in self.ops_config: + op_attr = dict( + zip( + list(op_config["op_attrs"].keys()), attrs_sample[ + index:index + len(op_config["op_attrs"])])) + op_attr_list.append(op_attr) + index = index + len(op_config["op_attrs"]) + ops.append( + OpConfig( + type=op_config["op_type"], + inputs=op_config["op_inputs"], + outputs=op_config["op_outputs"], + attrs=op_attr)) + + self.update_program_input_and_weight_with_attr(op_attr_list) + program_config = ProgramConfig( + ops=ops, + weights=self.program_weights, + inputs=self.program_inputs, + outputs=self.program_outputs) + yield program_config + + def create_program_config( + self, use_trt=True, + precision_mode=paddle_infer.PrecisionType.Float32): + config = paddle_infer.Config() + config.enable_use_gpu(100, 0) + if use_trt: + config.enable_tensorrt_engine( + max_batch_size=self.trt_param.max_batch_size, + workspace_size=self.trt_param.workspace_size, + min_subgraph_size=self.trt_param.min_subgraph_size, + precision_mode=precision_mode, + use_static=self.trt_param.use_static, + use_calib_mode=self.trt_param.use_calib_mode) + return config + + @abc.abstractmethod + def sample_predictor_configs(self): + yield self.create_program_config(use_trt=False) + yield self.create_program_config( + use_trt=True, precision_mode=self.trt_param.precision) + if self.trt_param.precision == paddle_infer.PrecisionType.Float32: + yield self.create_program_config( + use_trt=True, precision_mode=paddle_infer.PrecisionType.Half)