# Copyright (c) 2024 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 inspect import logging import os import sys import numpy as np import tensorrt as trt from paddle.tensorrt.util import TensorRTConfigManager, TensorRTConstantManager current_dir = os.path.dirname(os.path.abspath(__file__)) parent_dir = os.path.abspath(os.path.join(current_dir, os.pardir)) if parent_dir not in sys.path: sys.path.append(parent_dir) from tensorrt import INetworkDefinition, ITensor from paddle.base.log_helper import get_logger _logger = get_logger( __name__, logging.INFO, fmt='%(asctime)s-%(levelname)s: %(message)s' ) from paddle.base.libpaddle.pir import ( get_attrs_map_json, get_inputs_type_json, get_outputs_type_json, ) version = trt.__version__ version_list = list(map(int, version.split('.'))) def has_dynamic_shape(shape): return any(s == -1 for s in shape) def append_ones(network, input, name, num_prepend_ones): layer = network.add_shuffle(input) if has_dynamic_shape(input.shape): input_shape_layer = network.add_shape(input) prepend_shape_layer = network.add_constant( (num_prepend_ones,), np.ones((num_prepend_ones,), dtype=np.int32) ) reshape_dim_layer = network.add_concatenation( [prepend_shape_layer.get_output(0), input_shape_layer.get_output(0)] ) reshape_dim_layer.axis = 0 layer.set_input(1, reshape_dim_layer.get_output(0)) if name is not None: set_layer_name(input_shape_layer, [name[0], "input_shape_layer"]) set_layer_name( prepend_shape_layer, [name[0], "prepend_shape_layer"] ) set_layer_name(reshape_dim_layer, [name[0], "reshape_dim_layer"]) else: layer.reshape_dims = (1,) * num_prepend_ones + tuple(input.shape) if name is not None: set_layer_name(layer, name) return layer.get_output(0) def broadcast(network, a, b, a_name, b_name, paddle_op, preset_diff=0): a_shape = tuple(a.shape) b_shape = tuple(b.shape) diff = len(a_shape) - len(b_shape) - preset_diff if diff > 0: b = append_ones(network, b, [paddle_op.name(), b_name], diff) elif diff < 0: a = append_ones(network, a, [paddle_op.name(), a_name], -diff) return a, b def get_axes_for_reduce_op( dim, has_implicit_batch_dimension=False, ): if isinstance(dim, int): dim = (dim,) if has_implicit_batch_dimension: assert 0 not in dim, ( "Can't reduce over batch dimension when it's implicit." ) axes = 0 for d in dim: axes |= 1 << (d - (1 if has_implicit_batch_dimension else 0)) return axes def get_dynamic_dims(shape): """ This function finds the dynamic dimensions in the given shape. A dimension is dynamic if it's -1. Args: shape (Shape): A sequence of integer that represents the shape of a tensor. Returns: A list of integers contains all the dynamic dimensions in the given shape """ dynamic_dims = [] for i, s in enumerate(shape): if s == -1: dynamic_dims.append(i) return dynamic_dims def get_trt_plugin(plugin_name, field_collection, version, plugin_namespace=""): plugin_registry = trt.get_plugin_registry() plugin_creator = plugin_registry.get_plugin_creator( plugin_name, version, plugin_namespace ) assert plugin_creator, ( f"Unable to found plugin creator with name {plugin_name}" ) plugin = plugin_creator.create_plugin( name=plugin_name, field_collection=field_collection ) assert plugin is not None, f"Plugin:{plugin_name} could not be fetched" return plugin def get_positive_dim(dim, dim_size): if dim < 0: return dim % dim_size return dim def add_elementwise_layer(network, paddle_op, inputs, op_type): from paddle.tensorrt.util import support_fp32_mix_precision weight_shape = paddle_op.operands()[1].source().shape input_shape = paddle_op.operands()[0].source().shape weight_tensor = inputs[1] input_tensor = inputs[0] if type(inputs[1]) == trt.Weights: weight_tensor = network.add_constant(weight_shape, inputs[1]) set_layer_name(weight_tensor, paddle_op) weight_tensor = weight_tensor.get_output(0) if type(inputs[0]) == trt.Weights: input_tensor = network.add_constant(input_shape, inputs[0]) set_layer_name(input_tensor, paddle_op) input_tensor = input_tensor.get_output(0) lhs_val, rhs_val = broadcast( network, input_tensor, weight_tensor, "input_tensor_broadcast", "weight_tensor_broadcast", paddle_op, ) layer = network.add_elementwise(lhs_val, rhs_val, op_type) set_layer_name(layer, paddle_op) support_fp32_mix_precision(paddle_op.name(), layer) return layer.get_output(0) # Create and add 1D constant layer def add_1D_constant_layer( network, data, dtype=np.int32, is_scalar=False, name=None ): if not isinstance(data, list): data = [data] shape = () if is_scalar else (len(data),) constant_data = np.array(data, dtype=dtype) constant_layer = network.add_constant(shape, constant_data) set_layer_name(constant_layer, name) return constant_layer.get_output(0) # Create and add ND constant layer def add_constant_layer(network, data, shape, dtype=np.int32, name=None): constant_data = np.array(data, dtype=dtype) constant_data = np.resize(constant_data, shape) constant_layer = network.add_constant(shape, constant_data) set_layer_name(constant_layer, name) return constant_layer.get_output(0) # Create an constant layer with shape_tensor and value def fill_constant_layer( network, shape_tensor, tensor_rank, data, trt_dtype, name=None ): fill_layer = network.add_fill( trt.Dims([tensor_rank]), trt.FillOperation.LINSPACE ) np_dtype = map_trt_dtype(trt_dtype) fill_layer.set_input(0, shape_tensor) fill_layer.set_input( 1, add_1D_constant_layer(network, data, np_dtype, is_scalar=True) ) beta = [0] * tensor_rank fill_layer.set_input( 2, add_1D_constant_layer(network, beta, np_dtype, is_scalar=False) ) set_layer_name(fill_layer, name) return fill_layer.get_output(0) def trt_expand(network, input, rank, shape_tensor, shape_rank, name=None): def process_names(name, layer_name): if name is not None: return [name[0], layer_name] else: return None if rank < shape_rank: one_rank_tensor = add_1D_constant_layer( network, [1] * (shape_rank - rank), name=process_names(name, "one_rank_tensor"), ) in_shape_tensor = trt_shape( network, input, name=process_names(name, "in_shape_tensor") ) itensors = [one_rank_tensor, in_shape_tensor] input_shape_tensor = trt_concat( network, itensors, name=process_names(name, "input_shape_tensor") ) else: input_shape_tensor = trt_shape( network, input, name=process_names(name, "input_shape_tensor") ) new_input_tensor = trt_reshape( network, input, input_shape_tensor, process_names(name, "new_input_tensor"), True, ) start = [0] * shape_rank starts_tensor = add_1D_constant_layer( network, start, name=process_names(name, "starts_tensor") ) one_tensor = add_1D_constant_layer( network, 1, name=process_names(name, "one_tensor") ) sizes_tensor = trt_max( network, input_shape_tensor, shape_tensor, name=process_names(name, "sizes_tensor"), ) input_sub_tensor = trt_sub( network, input_shape_tensor, one_tensor, name=process_names(name, "input_sub_tensor"), ) strides_tensor = trt_min( network, one_tensor, input_sub_tensor, name=process_names(name, "strides_tensor"), ) slice_layer = network.add_slice( new_input_tensor, start, [0] * len(start), [0] * len(start) ) slice_layer.set_input(1, starts_tensor) slice_layer.set_input(2, sizes_tensor) slice_layer.set_input(3, strides_tensor) set_layer_name(slice_layer, name) return slice_layer.get_output(0) # Concat not make rank changed def trt_concat(network, inputs, axis=0, name=None): concat_layer = network.add_concatenation(inputs=inputs) if axis != 0: concat_layer.axis = axis set_layer_name(concat_layer, name) return concat_layer.get_output(0) def trt_cast(network, input, dtype, name=None): identity_layer = network.add_identity(input) identity_layer.set_output_type(0, dtype) identity_layer.get_output(0).dtype = dtype set_layer_name(identity_layer, name) return identity_layer.get_output(0) def trt_shape( network: INetworkDefinition, input: ITensor, name=None ) -> ITensor: """ Add a IShapeLayer to get the shape of `input` ITensor. This includes a workaround that casting the shape result(int64) from TRT10 back to int32. Many existing paddle op kernels only support input shape tensor as int32 , to make TRT op more compatible with other paddle op, we cast back to int32. NOTE: please remove this workaround when all paddle op supports shape tensor in int64 """ shape_layer = network.add_shape(input) set_layer_name(shape_layer, name) if version_list[0] >= 10: # trt_version >=10 # workaround if name is not None: name = [name[0], "trt_cast"] return trt_cast( network, shape_layer.get_output(0), trt.int32, name=name ) return shape_layer.get_output(0) def trt_reshape(network, input, new_shape, name=None, is_shape_tensor=False): reshape_layer = network.add_shuffle(input) if is_shape_tensor: reshape_layer.set_input(1, new_shape) else: reshape_layer.reshape_dims = new_shape if name is not None: if isinstance(name, list): set_layer_name(reshape_layer, name) else: reshape_layer.name = name return reshape_layer.get_output(0) # resize shape tensor's shape to 1dim def resize_to_1d(network, shape_tensor, name=None): if shape_tensor is None: return shape_tensor if len(shape_tensor.shape) > 1: # shape_tensor need 1-dim in trt shape_tensor_layer = network.add_shuffle(shape_tensor) numel = 1 for ele in shape_tensor.shape: numel *= ele shape_tensor_layer.reshape_dims = [numel] set_layer_name(shape_tensor_layer, name) shape_tensor = shape_tensor_layer.get_output(0) return shape_tensor # Get element tensor of 1D shape tensor def get_shape_tensor_element(network, x, index, is_scalar=False, name=None): assert index >= 0, ( f"The index should be greater or equal than 0, but got {index}" ) index_tensor_name = [name[0], "index_tensor"] if name is not None else None index_tensor = add_1D_constant_layer( network, index, is_scalar=is_scalar, name=index_tensor_name ) gather_layer = network.add_gather(input=x, indices=index_tensor, axis=0) if name is not None: set_layer_name(gather_layer, [name[0], "gather_layer"]) shape_tensor = resize_to_1d(network, gather_layer.get_output(0), name=name) return shape_tensor def trt_less(network, a, b, name=None): layer = network.add_elementwise(a, b, trt.ElementWiseOperation.LESS) set_layer_name(layer, name) return layer.get_output(0) def trt_sum(network, a, b, name=None): layer = network.add_elementwise(a, b, trt.ElementWiseOperation.SUM) set_layer_name(layer, name) return layer.get_output(0) def trt_max(network, a, b, name=None): layer = network.add_elementwise(a, b, trt.ElementWiseOperation.MAX) set_layer_name(layer, name) return layer.get_output(0) def trt_sub(network, a, b, name=None): layer = network.add_elementwise(a, b, trt.ElementWiseOperation.SUB) set_layer_name(layer, name) return layer.get_output(0) def trt_min(network, a, b, name=None): layer = network.add_elementwise(a, b, trt.ElementWiseOperation.MIN) set_layer_name(layer, name) return layer.get_output(0) def trt_div(network, a, b, name=None): layer = network.add_elementwise(a, b, trt.ElementWiseOperation.DIV) set_layer_name(layer, name) return layer.get_output(0) def trt_floor_div(network, a, b, name=None): layer = network.add_elementwise(a, b, trt.ElementWiseOperation.FLOOR_DIV) set_layer_name(layer, name) return layer.get_output(0) def trt_equal(network, a, b, name=None): layer = network.add_elementwise(a, b, trt.ElementWiseOperation.EQUAL) set_layer_name(layer, name) return layer.get_output(0) def trt_gather(network, input, indices, axis=0, name=None): if name is not None: name = [name[0], "indices_tensor"] indices_tensor = add_1D_constant_layer(network, indices, name=name) gather_layer = network.add_gather(input, indices_tensor, axis) set_layer_name(gather_layer, name) result = gather_layer.get_output(0) return result def trt_prod(network, a, b, name=None): layer = network.add_elementwise(a, b, trt.ElementWiseOperation.PROD) set_layer_name(layer, name) return layer.get_output(0) def trt_pow(network, a, b, name=None): layer = network.add_elementwise(a, b, trt.ElementWiseOperation.POW) set_layer_name(layer, name) return layer.get_output(0) def cast_tensor(network, input_tensor, dtype, name=None): layer = network.add_identity(input_tensor) layer.set_output_type(0, dtype) set_layer_name(layer, name) return layer.get_output(0) def build_start_tensor(network, rank, axis_tensor, offset, name=None): # Create indices_tensor [0, 1, ..., rank-1] indices = np.arange(rank, dtype=np.int32) indices_name = [name[0], "indices_tensor"] if name is not None else None indices_tensor = network.add_constant([rank], indices) set_layer_name(indices_tensor, indices_name) indices_tensor = indices_tensor.get_output(0) # Create mask: mask = (indices == axis_tensor) mask_name = [name[0], "mask"] if name is not None else None mask = network.add_elementwise( indices_tensor, axis_tensor, trt.ElementWiseOperation.EQUAL ) set_layer_name(mask, mask_name) mask = mask.get_output(0) mask_int = cast_tensor( network, mask, trt.int32, name=[name[0], "mask_int"] if name is not None else None, ) # Calculate start_tensor = mask_int * offset start_tensor = network.add_elementwise( mask_int, offset, trt.ElementWiseOperation.PROD ) set_layer_name(start_tensor, name) start_tensor = start_tensor.get_output(0) return start_tensor def build_size_tensor( network, rank, axis_tensor, size_value, input_shape_tensor, name=None ): # Create indices_tensor [0, 1, ..., rank-1] indices = np.arange(rank, dtype=np.int32) indices_name = [name[0], 'indices_tensor'] if name is not None else None indices_tensor = network.add_constant([rank], indices) set_layer_name(indices_tensor, indices_name) indices_tensor = indices_tensor.get_output(0) # Create mask: mask = (indices == axis_tensor) mask_name = [name[0], 'mask'] if name is not None else None mask = network.add_elementwise( indices_tensor, axis_tensor, trt.ElementWiseOperation.EQUAL ) set_layer_name(mask, mask_name) mask = mask.get_output(0) mask_int = cast_tensor( network, mask, trt.int32, name=[name[0], "mask_int"] if name is not None else None, ) # Create ones_tensor ones_name = [name[0], 'ones_tensor'] if name is not None else None ones_tensor = network.add_constant([rank], np.ones([rank], dtype=np.int32)) set_layer_name(ones_tensor, ones_name) ones_tensor = ones_tensor.get_output(0) # Calculate inverse_mask = ones_tensor - mask_int inverse_mask_name = [name[0], 'inverse_mask'] if name is not None else None inverse_mask = network.add_elementwise( ones_tensor, mask_int, trt.ElementWiseOperation.SUB ) set_layer_name(inverse_mask, inverse_mask_name) inverse_mask = inverse_mask.get_output(0) # Calculate size_tensor = mask_int * size_value + inverse_mask * input_shape_tensor size_value_broadcast_name = ( [name[0], 'size_value_broadcast'] if name is not None else None ) size_value_broadcast = network.add_elementwise( mask_int, size_value, trt.ElementWiseOperation.PROD ) set_layer_name(size_value_broadcast, size_value_broadcast_name) size_value_broadcast = size_value_broadcast.get_output(0) input_shape_broadcast_name = ( [name[0], 'input_shape_broadcast'] if name is not None else None ) input_shape_broadcast = network.add_elementwise( inverse_mask, input_shape_tensor, trt.ElementWiseOperation.PROD ) set_layer_name(input_shape_broadcast, input_shape_broadcast_name) input_shape_broadcast = input_shape_broadcast.get_output(0) size_tensor = network.add_elementwise( size_value_broadcast, input_shape_broadcast, trt.ElementWiseOperation.SUM, ) set_layer_name(size_tensor, name) size_tensor = size_tensor.get_output(0) return size_tensor # convert trt_dtype to numpy dtype def map_trt_dtype(trt_dtype): dtype_map = { trt.DataType.FLOAT: np.float32, trt.DataType.HALF: np.float16, trt.DataType.INT32: np.int32, trt.DataType.INT8: np.int8, trt.DataType.BOOL: bool, } if trt_dtype in dtype_map: return dtype_map[trt_dtype] else: raise TypeError(f"Unsupported trt_dtype: {trt_dtype}") # Reduce the given tensor in the TensorRT network to a scalar def trt_reduce_to_scalar(network, tensor, dtype=trt.int32, name=None): if len(tensor.shape) == 0: return tensor axes = 0 for i in range(len(tensor.shape)): axes |= 1 << i reduce_layer = network.add_reduce( tensor, trt.ReduceOperation.SUM, axes, keep_dims=False ) if name is not None: set_layer_name(reduce_layer, [name[0], 'reduce_layer']) scalar_name = name scalar = trt_cast( network, reduce_layer.get_output(0), dtype, name=scalar_name ) return scalar def convert_conv2d(network, paddle_op, inputs): from paddle.tensorrt.util import ( RefitManager, RefitRole, support_fp32_mix_precision, ) bias = None if ( paddle_op.name() == "pd_op.conv2d" or paddle_op.name() == "pd_op.depthwise_conv2d" ): input_tensor, filter = inputs elif ( paddle_op.name() == "pd_op.conv2d_transpose" or paddle_op.name() == "pd_op.depthwise_conv2d_transpose" ): if len(inputs) == 3: input_tensor, filter, output_size = inputs elif len(inputs) == 2: input_tensor, filter = inputs output_size = None else: raise ValueError("Invalid number of inputs for conv2d_transpose") if paddle_op.name() == "pd_op.fused_conv2d_add_act": input_tensor, filter, bias, _ = inputs input_shape = paddle_op.operands()[0].source().shape filter_shape = paddle_op.operands()[1].source().shape if len(filter_shape) != 4: raise ValueError( f"filter's dims size should be 4, but got {len(filter_shape)}" ) n_output = filter_shape[0] n_input = filter_shape[1] filter_h = filter_shape[2] filter_w = filter_shape[3] paddings = paddle_op.attrs().get("paddings", [0, 0]) stride = paddle_op.attrs().get("strides", [1, 1]) dilation = paddle_op.attrs().get("dilations", [1, 1]) groups = paddle_op.attrs().get("groups", 1) if has_dynamic_shape(input_shape): assert input_shape[1] != -1, ( "Channel dim can't be dynamic for transpose convolution." ) output_padding = paddle_op.attrs().get("output_padding", [0, 0]) padding_algorithm = paddle_op.attrs().get("padding_algorithm", "EXPLICIT") if padding_algorithm == "VALID": paddings = [0] * len(paddings) nv_ksize = trt.DimsHW(filter_h, filter_w) nv_dilations = trt.DimsHW(dilation[0], dilation[1]) nv_strides = trt.DimsHW(stride[0], stride[1]) pre_paddings = [0, 0] post_paddings = [0, 0] if isinstance(filter, trt.Weights): weight_filter = filter else: weight_filter = trt.Weights() if len(paddings) == 2: pre_paddings[0] = paddings[0] pre_paddings[1] = paddings[1] post_paddings[0] = paddings[0] post_paddings[1] = paddings[1] elif len(paddings) == 4: pre_paddings[0] = paddings[0] pre_paddings[1] = paddings[2] post_paddings[0] = paddings[1] post_paddings[1] = paddings[3] else: raise ValueError(f"Unsupported paddings size: {len(paddings)}") if paddle_op.name() == "pd_op.fused_conv2d_add_act": constant_manager = TensorRTConstantManager() bias_source_op = paddle_op.operands()[2].source().get_defining_op() if bias_source_op.name() == "builtin.parameter": bias_name = bias_source_op.attrs()['parameter_name'] elif bias_source_op.name() == "builtin.constant": bias_np = bias_source_op.attrs()['value'] else: raise ValueError( f"Unsupported bias source op: {bias_source_op.name()}" ) bias_np = constant_manager.get_constant_value(bias_name) bias_weights = trt.Weights(bias_np) layer = network.add_convolution_nd( input=input_tensor, num_output_maps=n_output, kernel_shape=nv_ksize, kernel=weight_filter, bias=bias_weights, ) elif ( paddle_op.name() == "pd_op.conv2d" or paddle_op.name() == "pd_op.depthwise_conv2d" ): layer = network.add_convolution_nd( input=input_tensor, num_output_maps=n_output, kernel_shape=nv_ksize, kernel=weight_filter, bias=None, ) elif ( paddle_op.name() == "pd_op.conv2d_transpose" or paddle_op.name() == "pd_op.depthwise_conv2d_transpose" ): layer = network.add_deconvolution_nd( input=input_tensor, num_output_maps=n_input * groups, kernel_shape=nv_ksize, kernel=weight_filter, bias=None, ) if isinstance(filter, trt.ITensor): layer.set_input(1, filter) layer.stride_nd = nv_strides layer.pre_padding = pre_paddings if output_padding: post_paddings[0] -= output_padding[0] post_paddings[1] -= output_padding[1] if post_paddings[0] < 0 or post_paddings[1] < 0: raise ValueError("The value PostPadding should be >= 0.") layer.post_padding = post_paddings layer.num_groups = groups if padding_algorithm == "SAME": layer.padding_mode = trt.PaddingMode.SAME_UPPER nv_dilations = trt.DimsHW(1, 1) layer.dilation_nd = nv_dilations set_layer_name(layer, paddle_op) support_fp32_mix_precision(paddle_op.name(), layer) trt_manager = TensorRTConfigManager() if trt_manager.get_refit_params_path(): filter_param = paddle_op.operands()[1].source() filter_name = filter_param.get_defining_op().attrs()['parameter_name'] refit_manager = RefitManager() refit_manager.set_mapping(filter_name, filter_name, RefitRole.CONSTANT) return layer.get_output(0) def convert_conv3d(network, paddle_op, inputs): from paddle.tensorrt.util import ( RefitManager, RefitRole, ) input_tensor, filter = inputs filter_shape = paddle_op.operands()[1].source().shape n_output = filter_shape[0] n_input = filter_shape[1] filter_d = filter_shape[2] filter_h = filter_shape[3] filter_w = filter_shape[4] if isinstance(filter, trt.Weights): weight_filter = filter else: weight_filter = trt.Weights() groups = paddle_op.attrs().get("groups", 1) dilations = paddle_op.attrs().get("dilations", [1, 1, 1]) strides = paddle_op.attrs().get("strides", [1, 1, 1]) paddings = paddle_op.attrs().get("paddings", [0, 0, 0]) padding_algorithm = paddle_op.attrs().get("padding_algorithm", "EXPLICIT") # for conv3d_transpose output_padding = paddle_op.attrs().get("output_padding", []) nv_ksize = trt.Dims3(filter_d, filter_h, filter_w) nv_dilations = trt.Dims3(dilations[0], dilations[1], dilations[2]) nv_strides = trt.Dims3(strides[0], strides[1], strides[2]) nv_pre_paddings = trt.Dims3(paddings[0], paddings[1], paddings[2]) if paddle_op.name() == "pd_op.conv3d": layer = network.add_convolution_nd( input=input_tensor, num_output_maps=n_output, kernel_shape=nv_ksize, kernel=weight_filter, bias=None, ) elif paddle_op.name() == "pd_op.conv3d_transpose": layer = network.add_deconvolution_nd( input=input_tensor, num_output_maps=n_input * groups, kernel_shape=nv_ksize, kernel=weight_filter, bias=None, ) layer.stride_nd = nv_strides layer.pre_padding = nv_pre_paddings nv_post_paddings = trt.Dims3(paddings[0], paddings[1], paddings[2]) if output_padding: nv_post_paddings[0] -= output_padding[0] nv_post_paddings[1] -= output_padding[1] nv_post_paddings[2] -= output_padding[2] if ( nv_post_paddings[0] < 0 or nv_post_paddings[1] < 0 or nv_post_paddings[2] < 0 ): raise ValueError( "The value in conv3d_transpose's PostPadding should be >= 0." ) if isinstance(filter, trt.ITensor): layer.set_input(1, filter) layer.post_padding = nv_post_paddings layer.num_groups = groups if padding_algorithm == "SAME": layer.padding_mode = trt.PaddingMode.SAME_UPPER layer.dilation_nd = nv_dilations set_layer_name(layer, paddle_op) trt_manager = TensorRTConfigManager() if trt_manager.get_refit_params_path(): filter_param = paddle_op.operands()[1].source() filter_name = filter_param.get_defining_op().attrs()['parameter_name'] refit_manager = RefitManager() refit_manager.set_mapping(filter_name, filter_name, RefitRole.CONSTANT) return layer.get_output(0) def get_input_constant_value(paddle_op, inputs, input_index): input_op = paddle_op.operands()[input_index].source().get_defining_op() constant_manager = TensorRTConstantManager() if input_op.name() == "builtin.constant": return constant_manager.get_constant_value( input_op.attrs()["value"] ).tolist() elif input_op.name() == "pd_op.full_int_array": return input_op.attrs()["value"] elif input_op.name() == "pd_op.full": return [input_op.attrs()["value"]] else: return None def add_reduce_layer(network, paddle_op, inputs, op_type): input_tensor = inputs[0] axis = get_input_constant_value(paddle_op, inputs, 1) input_shape = paddle_op.operands()[0].source().shape keepdim = paddle_op.attrs()["keepdim"] if network.has_implicit_batch_dimension: assert axis != 0, ( "can't reduce on axis == 0 when network has implicit batch dimension" ) output_shape = [] if len(axis) == 0: axis = list(range(len(input_shape))) for i in range(len(axis)): if axis[i] < 0: axis[i] = len(input_shape) + axis[i] layer = network.add_reduce( input_tensor, op_type, axes=get_axes_for_reduce_op(axis), keep_dims=keepdim, ) set_layer_name(layer, paddle_op) layer.get_output(0).dtype = layer.get_input(0).dtype return layer.get_output(0) def add_cast_reduce_layer(network, paddle_op, inputs, op_type): input_tensor = inputs[0] cast_layer = network.add_identity(input_tensor) set_layer_name(cast_layer, paddle_op) cast_layer.set_output_type(0, trt.int32) cast_layer.get_output(0).dtype = trt.int32 axis = paddle_op.attrs().get("axis") input_shape = paddle_op.operands()[0].source().shape input_dims = len(input_shape) keepdim = paddle_op.attrs().get("keepdim") if len(axis) == 0: axes = 0 for i in range(input_dims): axes |= 1 << i else: for i in range(len(axis)): if axis[i] < 0: axis[i] += input_dims axes = get_axes_for_reduce_op(axis) reduce_layer = network.add_reduce( cast_layer.get_output(0), op_type, axes=axes, keep_dims=keepdim, ) set_layer_name(reduce_layer, paddle_op) bool_layer = network.add_identity(reduce_layer.get_output(0)) set_layer_name(bool_layer, paddle_op) bool_layer.set_output_type(0, trt.bool) bool_layer.get_output(0).dtype = trt.bool return bool_layer.get_output(0) def fix_negative_indices(network, input_shape, indices, name=None): rank = len(input_shape.shape) zero_tensor_name = [name[0], 'zero_tensor'] if name else None zero_tensor = add_1D_constant_layer( network, [0] * rank, name=zero_tensor_name ) minus_one_tensor_name = [name[0], 'minus_one_tensor'] if name else None minus_one_tensor = add_1D_constant_layer( network, [-1] * rank, name=minus_one_tensor_name ) min_indices_zero_name = [name[0], 'min_indices_zero'] if name else None min_indices_zero = trt_min( network, indices, zero_tensor, name=min_indices_zero_name ) sign_name = [name[0], 'sign'] if name else None sign = trt_max(network, min_indices_zero, minus_one_tensor, name=sign_name) sub_name = [name[0], 'sub'] if name else None sub = trt_prod(network, sign, input_shape, name=sub_name) fixed_indices = trt_sub(network, indices, sub, name=name) return fixed_indices def trt_unsqueeze(network, input_tensor, axes, name=None): input_shape_name = [name[0], 'input_shape'] if name else None input_shape = network.add_shape(input_tensor) set_layer_name(input_shape, input_shape_name) input_shape = input_shape.get_output(0) axis_set = set(axes) subscripts = list(range(len(input_tensor.shape))) for axis in sorted(axis_set): subscripts.insert(axis, len(input_tensor.shape)) one_tensor_name = [name[0], 'one_tensor'] if name else None one_tensor = network.add_constant((1,), np.array([1], dtype=np.int32)) set_layer_name(one_tensor, one_tensor_name) one_tensor = one_tensor.get_output(0) extended_shape_name = [name[0], 'extended_shape'] if name else None extended_shape = network.add_concatenation( [input_shape, one_tensor], ) set_layer_name(extended_shape, extended_shape_name) extended_shape = extended_shape.get_output(0) gather_layer_name = [name[0], 'gather_layer'] if name else None gather_layer = network.add_gather( extended_shape, network.add_constant( (len(subscripts),), np.array(subscripts, dtype=np.int32) ).get_output(0), axis=0, ) set_layer_name(gather_layer, gather_layer_name) new_shape_tensor = gather_layer.get_output(0) reshaped_tensor = network.add_shuffle(input_tensor) reshaped_tensor.set_input(1, new_shape_tensor) set_layer_name(reshaped_tensor, name) return reshaped_tensor.get_output(0) def squeeze_trt(network, input_tensor, axes, name=None): input_shape_name = [name[0], 'input_shape'] if name else None input_shape = network.add_shape(input_tensor) set_layer_name(input_shape, input_shape_name) input_shape = input_shape.get_output(0) input_shape = input_tensor.shape all_dims = list(range(len(input_shape))) remaining_dims = [dim for dim in all_dims if dim not in axes] input_shape_tensor_name = [name[0], 'input_shape_tensor'] if name else None input_shape_tensor = network.add_shape(input_tensor) set_layer_name(input_shape_tensor, input_shape_tensor_name) input_shape_tensor = input_shape_tensor.get_output(0) remaining_dims_tensor_name = ( [name[0], 'remaining_dims_tensor'] if name else None ) remaining_dims_tensor = network.add_constant( (len(remaining_dims),), np.array(remaining_dims, dtype=np.int32) ) set_layer_name(remaining_dims_tensor, remaining_dims_tensor_name) remaining_dims_tensor = remaining_dims_tensor.get_output(0) new_shape_tensor_name = [name[0], 'new_shape_tensor'] if name else None new_shape_tensor = network.add_gather( input_shape_tensor, remaining_dims_tensor, axis=0 ) set_layer_name(new_shape_tensor, new_shape_tensor_name) new_shape_tensor = new_shape_tensor.get_output(0) reshape_layer = network.add_shuffle(input_tensor) reshape_layer.set_input(1, new_shape_tensor) set_layer_name(reshape_layer, name) return reshape_layer.get_output(0) def unary_op_converter(network, paddle_op, inputs): from paddle.tensorrt import PrecisionMode ops_type_map = { "pd_op.sqrt": [trt.UnaryOperation.SQRT], "pd_op.sqrt_": [trt.UnaryOperation.SQRT], "pd_op.floor": [trt.UnaryOperation.FLOOR], "pd_op.exp": [trt.UnaryOperation.EXP], "pd_op.abs": [trt.UnaryOperation.ABS], "pd_op.abs_": [trt.UnaryOperation.ABS], "pd_op.sin": [trt.UnaryOperation.SIN], "pd_op.cos": [trt.UnaryOperation.COS], "pd_op.sinh": [trt.UnaryOperation.SINH], "pd_op.cosh": [trt.UnaryOperation.COSH], "pd_op.asinh": [trt.UnaryOperation.ASINH], "pd_op.acosh": [trt.UnaryOperation.ACOSH], "pd_op.atanh": [trt.UnaryOperation.ATANH], "pd_op.ceil": [trt.UnaryOperation.CEIL], "pd_op.reciprocal": [trt.UnaryOperation.RECIP], "pd_op.erf": [trt.UnaryOperation.ERF], "pd_op.sign": [trt.UnaryOperation.SIGN], "pd_op.round": [trt.UnaryOperation.ROUND], "pd_op.logical_not": [trt.UnaryOperation.NOT], "pd_op.rsqrt": [trt.UnaryOperation.SQRT, trt.UnaryOperation.RECIP], "pd_op.tan": [trt.UnaryOperation.TAN], "pd_op.asin": [trt.UnaryOperation.ASIN], "pd_op.acos": [trt.UnaryOperation.ACOS], "pd_op.atan": [trt.UnaryOperation.ATAN], } input_tensor = inputs[0] layer = None org_type = input_tensor.dtype trt_type_mapping = { trt.DataType.INT8: trt.int8, trt.DataType.INT32: trt.int32, } trt_manager = TensorRTConfigManager() precision_mode = trt_manager.get_precision_mode() need_cast = org_type in [trt.DataType.INT8, trt.DataType.INT32] if need_cast: identity_layer = network.add_identity(input_tensor) if precision_mode == PrecisionMode.FP32: identity_layer.set_output_type(0, trt.float32) else: identity_layer.set_output_type(0, trt.float16) set_layer_name(identity_layer, paddle_op) input_tensor = identity_layer.get_output(0) if paddle_op.name() in ops_type_map: for trt_op in ops_type_map[paddle_op.name()]: layer = network.add_unary(input_tensor, trt_op) set_layer_name(layer, paddle_op) input_tensor = layer.get_output(0) else: raise NotImplementedError( f"Unsupported unary operation: {paddle_op.name()}" ) if need_cast: restore_layer = network.add_identity(input_tensor) restore_layer.set_output_type(0, trt_type_mapping[org_type]) set_layer_name(restore_layer, paddle_op) input_tensor = restore_layer.get_output(0) return input_tensor # get the length of the specified axis for input_tensor def get_axis_length(network, input_tensor, axis, is_scalar=False, name=None): input_shape = input_tensor.shape if input_shape[axis] >= 0: output_tensor = add_1D_constant_layer( network, input_shape[axis], is_scalar=is_scalar, name=name ) else: shape_name = [name[0], 'dynamic_shape'] if name else None dynamic_shape = trt_shape(network, input_tensor, name=shape_name) output_tensor = get_shape_tensor_element( network, dynamic_shape, axis, is_scalar, name=name ) return output_tensor def WithFp16(): from paddle.tensorrt import PrecisionMode trt_manager = TensorRTConfigManager() precision_mode = trt_manager.get_precision_mode() enable_fp16 = False if precision_mode == PrecisionMode.FP16: enable_fp16 = True # TODO(lizexu123) WithInt8() and use_dla are not yet implemented return enable_fp16 def set_layer_name(layer, second_param): """ Sets standardized names for converter output layers following the format: `_->()` Naming Rule: Format: _->() Components: - sequence_number: Output tensor's unique ID from layer - paddle_op_name: Name of source Paddle operator - layer_variable_name: Variable name referencing the layer in code - input_ids: Input tensor IDs from preceding layers Args: layer (ILayer): Target layer to name second_param: Context-dependent parameter: - For non-public functions: paddle_op (op object) - For public functions: [paddle_op_name (str), layer_var_name (str)] list - When name=None in public functions: Enables nested handling """ if second_param is not None: if isinstance(second_param, list): # Handling for public function layer op_name, layer_var_name = second_param else: # Handling for layer op_name = second_param.name() layer_var_name = None if op_name is not None: # Retrieve the name of the variable that refers to the layer for ( var_name, var_val, ) in inspect.currentframe().f_back.f_locals.items(): if var_val is layer: layer_var_name = var_name break # Retrieve the input id of the layer if op_name is not None and layer_var_name is not None: input_ids = [] i = 0 while (input_tensor := layer.get_input(i)) is not None: input_name = input_tensor.name if "Unnamed Layer" in input_name: input_id = input_name.split("*")[1].split(")")[0].strip() else: input_id = input_name input_ids.append(input_id) i += 1 # Retrieve the output id of the layer output_name = layer.get_output(0).name if "Unnamed Layer" in output_name: sequence_number = ( output_name.split("*")[1].split(")")[0].strip() ) else: sequence_number = output_name formatted_name = ( f"{sequence_number}_" f"{op_name}->" f"{layer_var_name}" f"({', '.join(input_ids)})" ) layer.name = formatted_name def generic_plugin_converter(network, paddle_op, inputs, extra_attrs=None): op_name = paddle_op.name() if extra_attrs is not None: attrs_map_info = get_attrs_map_json(extra_attrs) else: attrs_map_info = get_attrs_map_json(paddle_op) input_type_info = get_inputs_type_json(paddle_op) output_type_info = get_outputs_type_json(paddle_op) plugin_fields = [ trt.PluginField( "op_name", np.array(list(op_name), dtype=np.bytes_), trt.PluginFieldType.CHAR, ), trt.PluginField( "attrs_map_info", np.array(list(attrs_map_info), dtype=np.bytes_), trt.PluginFieldType.CHAR, ), trt.PluginField( "inputs_type_info", np.array(list(input_type_info), dtype=np.bytes_), trt.PluginFieldType.CHAR, ), trt.PluginField( "outputs_type_info", np.array(list(output_type_info), dtype=np.bytes_), trt.PluginFieldType.CHAR, ), ] plugin_field_collection = trt.PluginFieldCollection(plugin_fields) plugin_name = "pir_generic_plugin" plugin_version = "1" plugin = get_trt_plugin( plugin_name, plugin_field_collection, plugin_version ) layer = network.add_plugin_v2(inputs, plugin) return layer