Files
2026-07-13 12:40:42 +08:00

718 lines
24 KiB
Python

# 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 logging
import numpy as np
import tensorrt as trt
from paddle.base.log_helper import get_logger
from paddle.tensorrt.converter_utils import (
add_1D_constant_layer,
fill_constant_layer,
get_input_constant_value,
get_shape_tensor_element,
get_trt_plugin,
set_layer_name,
trt_concat,
trt_div,
trt_gather,
trt_prod,
trt_shape,
trt_sub,
trt_sum,
trt_unsqueeze,
)
from paddle.tensorrt.register import converter_registry
from paddle.tensorrt.util import RefitManager
_logger = get_logger(
__name__, logging.INFO, fmt='%(asctime)s-%(levelname)s: %(message)s'
)
@converter_registry.register("pd_op.multiclass_nms3")
def multiclass_nms3_converter(network, paddle_op, inputs):
bboxes = inputs[0]
scores = inputs[1]
background_label = paddle_op.attrs().get("background_label")
score_threshold = paddle_op.attrs().get("score_threshold")
nms_top_k = paddle_op.attrs().get("nms_top_k")
nms_threshold = paddle_op.attrs().get("nms_threshold")
keep_top_k = paddle_op.attrs().get("keep_top_k")
normalized = paddle_op.attrs().get("normalized")
num_classes = scores.shape[1]
bboxes_dims = bboxes.shape
bboxes_expand_dims = [bboxes_dims[0], bboxes_dims[1], 1, bboxes_dims[2]]
bboxes_expand_layer = network.add_shuffle(bboxes)
bboxes_expand_layer.reshape_dims = trt.Dims(bboxes_expand_dims)
set_layer_name(bboxes_expand_layer, paddle_op)
scores_transpose_layer = network.add_shuffle(scores)
scores_transpose_layer.first_transpose = (0, 2, 1)
set_layer_name(scores_transpose_layer, paddle_op)
# create multiclass num3 plugin
batch_nms_inputs = [
bboxes_expand_layer.get_output(0),
scores_transpose_layer.get_output(0),
]
plugin_fields = [
trt.PluginField(
"shareLocation",
np.array([1], dtype=np.int32),
trt.PluginFieldType.INT32,
),
trt.PluginField(
"backgroundLabelId",
np.array(background_label, dtype=np.int32),
trt.PluginFieldType.INT32,
),
trt.PluginField(
"numClasses",
np.array(num_classes, dtype=np.int32),
trt.PluginFieldType.INT32,
),
trt.PluginField(
"topK",
np.array(nms_top_k, dtype=np.int32),
trt.PluginFieldType.INT32,
),
trt.PluginField(
"keepTopK",
np.array(keep_top_k, dtype=np.int32),
trt.PluginFieldType.INT32,
),
trt.PluginField(
"scoreThreshold",
np.array(score_threshold, dtype=np.float32),
trt.PluginFieldType.FLOAT32,
),
trt.PluginField(
"iouThreshold",
np.array(nms_threshold, dtype=np.float32),
trt.PluginFieldType.FLOAT32,
),
trt.PluginField(
"isNormalized",
np.array(normalized, dtype=np.int32),
trt.PluginFieldType.INT32,
),
trt.PluginField(
"clipBoxes",
np.array([0], dtype=np.int32),
trt.PluginFieldType.INT32,
),
]
plugin_field_collection = trt.PluginFieldCollection(plugin_fields)
plugin_name = "BatchedNMSDynamic_TRT"
plugin_version = "1"
plugin = get_trt_plugin(
plugin_name, plugin_field_collection, plugin_version
)
batch_nms_layer = network.add_plugin_v2(batch_nms_inputs, plugin)
set_layer_name(batch_nms_layer, paddle_op)
# dynamic shape: [bs, keep_topk, 4], [bs, keep_topk], [bs, keep_topk]
nmsed_boxes = batch_nms_layer.get_output(1)
nmsed_scores = batch_nms_layer.get_output(2)
nmsed_classes = batch_nms_layer.get_output(3)
nmsed_scores_transpose_layer = network.add_shuffle(nmsed_scores)
set_layer_name(nmsed_scores_transpose_layer, paddle_op)
nmsed_classes_reshape_layer = network.add_shuffle(nmsed_classes)
set_layer_name(nmsed_classes_reshape_layer, paddle_op)
nmsed_scores_transpose_layer.reshape_dims = trt.Dims(
[bboxes_dims[0], keep_top_k, 1]
)
nmsed_classes_reshape_layer.reshape_dims = trt.Dims(
[bboxes_dims[0], keep_top_k, 1]
)
concat_inputs = [
nmsed_classes_reshape_layer.get_output(0),
nmsed_scores_transpose_layer.get_output(0),
nmsed_boxes,
]
nms_concat_layer = network.add_concatenation(inputs=concat_inputs)
nms_concat_layer.axis = 2
set_layer_name(nms_concat_layer, paddle_op)
nms_concat_output = nms_concat_layer.get_output(0)
nms_shuffle_layer = network.add_shuffle(nms_concat_output)
nms_shuffle_layer.reshape_dims = trt.Dims(
[bboxes_dims[0], nms_concat_output.shape[-1]]
)
set_layer_name(nms_shuffle_layer, paddle_op)
# add fake index as output to be consistent with the outputs of multiclass_nms3
shape_weight = trt.Weights(np.array([0], dtype=np.int32))
constant_layer = network.add_constant([1, 1], shape_weight)
set_layer_name(constant_layer, paddle_op)
return (
nms_shuffle_layer.get_output(0),
constant_layer.get_output(0),
batch_nms_layer.get_output(0),
)
@converter_registry.register("pd_op.set_value")
@converter_registry.register("pd_op.set_value_")
@converter_registry.register("pd_op.set_value_with_tensor")
@converter_registry.register("pd_op.set_value_with_tensor_")
def set_value_converter(network, paddle_op, inputs):
x = inputs[0]
if (
paddle_op.name() == "pd_op.set_value"
or paddle_op.name() == "pd_op.set_value_"
):
starts = get_input_constant_value(paddle_op, inputs, 1)[0]
ends = get_input_constant_value(paddle_op, inputs, 2)[0]
steps = get_input_constant_value(paddle_op, inputs, 3)[0]
else:
starts = get_input_constant_value(paddle_op, inputs, 2)[0]
ends = get_input_constant_value(paddle_op, inputs, 3)[0]
steps = get_input_constant_value(paddle_op, inputs, 4)[0]
axes = paddle_op.attrs()["axes"][0]
input_dims = x.shape
# check params and refill
if axes < 0:
axes += len(input_dims)
if ends < 0:
ends += input_dims[axes]
if ends >= input_dims[axes]:
ends = input_dims[axes]
if (
paddle_op.name() == "pd_op.set_value_with_tensor"
or paddle_op.name() == "pd_op.set_value_with_tensor_"
):
updates = inputs[1]
else:
value = paddle_op.attrs().get("values")
input_shape_tensor = trt_shape(
network, x, name=[paddle_op.name(), 'input_shape_tensor']
)
vec_tensor = []
for i in range(len(input_dims)):
vec_tensor.append(
get_shape_tensor_element(
network,
input_shape_tensor,
i,
name=[paddle_op.name(), f'vec_tensor_{i}'],
)
)
axes_vec = [(ends - 1 - starts) / steps + 1]
vec_tensor[axes] = add_1D_constant_layer(
network, axes_vec, name=[paddle_op.name(), f'vec_tensor_{axes}']
)
output_shape_tensor = trt_concat(
network,
vec_tensor,
0,
name=[paddle_op.name(), 'output_shape_tensor'],
)
updates = fill_constant_layer(
network,
output_shape_tensor,
len(x.shape),
value,
x.dtype,
name=[paddle_op.name(), 'updates'],
)
_logger.info(f"Set_value_op: input's dimension is {input_dims}")
decrease_axes = paddle_op.attrs()["decrease_axes"]
if len(decrease_axes) > 0 and len(updates.shape) != len(x.shape):
updates = trt_unsqueeze(
network,
updates,
decrease_axes,
name=[paddle_op.name(), 'decrease_axes'],
)
value_rank = len(updates.shape)
input_rank = len(x.shape)
assert value_rank == input_rank, (
"value's rank is not equal to input's rank, "
'you should modify trt_config(a TensorRTConfig object) and set trt_config.disable_ops = ["{op_name}"] to forbid this op '
)
_logger.info(f"Set_value_op: updates tensor's simension is {updates.shape}")
# calculate dims
update_dims = updates.shape
assert update_dims[axes] > 0, (
"the update value shape[{axes}] must be greater than 0, but received {update_dims[axes]}"
)
assert input_dims[axes] > 0, (
"the input shape[{axes}] must be greater than 0, but received {input_dims[axes]}"
)
input_dims_rank = len(input_dims)
assert axes <= input_dims_rank, (
"The axes {axes} is larger than total axes {input_dims_rank}"
)
assert starts <= input_dims[axes], (
"The start {starts} of dim {axes} is larger than origin shape {input_dims[axes]}"
)
target_update_dim = (ends - 1 - starts) / steps + 1
assert update_dims[axes] == target_update_dim, (
"the {axes}th axis of update dim error, should be {target_update_dim}, but we got {update_dims[axes]}"
)
shape_0 = [1] * len(update_dims)
shape_weight = trt.Weights(np.array([0], dtype=np.float32))
zero_tensor = network.add_constant(shape_0, shape_weight)
set_layer_name(zero_tensor, paddle_op)
zero_tensor = zero_tensor.get_output(0)
indice_tensor = trt_prod(
network, zero_tensor, updates, name=[paddle_op.name(), 'indice_tensor']
)
cast_layer = network.add_identity(indice_tensor)
set_layer_name(cast_layer, paddle_op)
cast_layer.set_output_type(0, trt.int32)
indice_tensor = cast_layer.get_output(0)
shape_1 = [1] * len(update_dims)
shape_1[axes] = update_dims[axes]
tmp_1 = []
for i in range(starts, ends, steps):
tmp_1.append(i)
shape_weight = trt.Weights(np.array(tmp_1, dtype=np.int32))
one_tensor = network.add_constant(shape_1, shape_weight)
set_layer_name(one_tensor, paddle_op)
one_tensor = one_tensor.get_output(0)
indice_tensor = trt_sum(
network,
indice_tensor,
one_tensor,
name=[paddle_op.name(), 'indice_tensor'],
)
layer = network.add_scatter(
x, indice_tensor, updates, trt.ScatterMode.ELEMENT
)
set_layer_name(layer, paddle_op)
layer.axis = axes
return layer.get_output(0)
@converter_registry.register("pd_op.share_data")
@converter_registry.register("pd_op.share_data_")
def share_data_converter(network, paddle_op, inputs):
x = inputs[0]
identity_layer = network.add_identity(x)
set_layer_name(identity_layer, paddle_op)
return identity_layer.get_output(0)
@converter_registry.register("pd_op.temporal_shift")
def temporal_shift_converter(network, paddle_op, inputs):
input_tensor = inputs[0]
# Add a small bias to shift_ratio to mitigate floating point precision errors
shift_ratio = paddle_op.attrs()["shift_ratio"] + 1e-7
T = paddle_op.attrs()["seg_num"]
data_format = paddle_op.attrs().get("data_format", "NCHW")
if data_format == "NHWC":
# Transpose input to [N, C, H, W]
transpose_layer = network.add_shuffle(input_tensor)
transpose_layer.first_transpose = trt.Permutation([0, 3, 1, 2])
set_layer_name(transpose_layer, paddle_op)
input_tensor = transpose_layer.get_output(0)
input_dims = input_tensor.shape
C, H, W = input_dims[1], input_dims[2], input_dims[3]
# Reshape input to [N, T, C, H, W]
reshape_layer = network.add_shuffle(input_tensor)
reshape_layer.reshape_dims = trt.Dims([-1, T, C, H, W])
set_layer_name(reshape_layer, paddle_op)
input_tensor = reshape_layer.get_output(0)
# Pad input to [N, T + 2, C, H, W]
pre_pad = add_1D_constant_layer(
network, [0, 1, 0, 0, 0], name=[paddle_op.name(), 'pre_pad']
)
post_pad = add_1D_constant_layer(
network, [0, 1, 0, 0, 0], name=[paddle_op.name(), 'post_pad']
)
dims = 5
zeros = add_1D_constant_layer(
network, [0] * dims, name=[paddle_op.name(), 'zeros']
)
start = trt_sub(network, zeros, pre_pad, name=[paddle_op.name(), 'start'])
total_padding = trt_sum(
network, pre_pad, post_pad, name=[paddle_op.name(), 'total_padding']
)
input_shape = trt_shape(
network, input_tensor, name=[paddle_op.name(), 'input_shape']
)
size = trt_sum(
network, input_shape, total_padding, name=[paddle_op.name(), 'size']
)
stride = [1] * dims
dummy = stride
slice_layer = network.add_slice(input_tensor, dummy, dummy, stride)
slice_layer.set_input(1, start)
slice_layer.set_input(2, size)
set_layer_name(slice_layer, paddle_op)
trt_version = trt.__version__.split('.')
if int(trt_version[0]) > 8 or (
int(trt_version[0]) == 8 and int(trt_version[1]) >= 5
):
slice_layer.mode = trt.SampleMode.FILL
else:
slice_layer.mode = trt.SliceMode.FILL
slice_c = int(C * shift_ratio)
slice_c2 = int(C * shift_ratio * 2)
slice_start1 = zeros
slice_start2 = add_1D_constant_layer(
network, [0, 2, slice_c, 0, 0], name=[paddle_op.name(), 'slice_start2']
)
slice_start3 = add_1D_constant_layer(
network, [0, 1, slice_c2, 0, 0], name=[paddle_op.name(), 'slice_start3']
)
slice_size_base = trt_shape(
network, input_tensor, name=[paddle_op.name(), 'slice_size_base']
)
sub_size1 = add_1D_constant_layer(
network, [0, 0, C - slice_c, 0, 0], name=[paddle_op.name(), 'sub_size1']
)
sub_size2 = add_1D_constant_layer(
network,
[0, 0, C + slice_c - slice_c2, 0, 0],
name=[paddle_op.name(), 'sub_size2'],
)
sub_size3 = add_1D_constant_layer(
network, [0, 0, slice_c2, 0, 0], name=[paddle_op.name(), 'sub_size3']
)
slice_size1 = trt_sub(
network,
slice_size_base,
sub_size1,
name=[paddle_op.name(), 'slice_size1'],
)
slice_size2 = trt_sub(
network,
slice_size_base,
sub_size2,
name=[paddle_op.name(), 'slice_size2'],
)
slice_size3 = trt_sub(
network,
slice_size_base,
sub_size3,
name=[paddle_op.name(), 'slice_size3'],
)
slice1_layer = network.add_slice(
slice_layer.get_output(0), start=dummy, shape=dummy, stride=stride
)
slice1_layer.set_input(1, slice_start1)
slice1_layer.set_input(2, slice_size1)
set_layer_name(slice1_layer, paddle_op)
slice2_layer = network.add_slice(
slice_layer.get_output(0), start=dummy, shape=dummy, stride=stride
)
slice2_layer.set_input(1, slice_start2)
slice2_layer.set_input(2, slice_size2)
set_layer_name(slice2_layer, paddle_op)
slice3_layer = network.add_slice(
slice_layer.get_output(0), start=dummy, shape=dummy, stride=stride
)
slice3_layer.set_input(1, slice_start3)
slice3_layer.set_input(2, slice_size3)
set_layer_name(slice3_layer, paddle_op)
concat_inputs = [slice2_layer.get_output(0), slice3_layer.get_output(0)]
if slice_c == 0:
concat_layer = network.add_concatenation(concat_inputs)
concat_layer.axis = 2
set_layer_name(concat_layer, paddle_op)
else:
concat_inputs = [
slice1_layer.get_output(0),
slice2_layer.get_output(0),
slice3_layer.get_output(0),
]
concat_layer = network.add_concatenation(concat_inputs)
concat_layer.axis = 2
set_layer_name(concat_layer, paddle_op)
# Reshape output to [N*T,C,H,W]
reshape_layer3 = network.add_shuffle(concat_layer.get_output(0))
reshape_layer3.reshape_dims = trt.Dims([-1, C, H, W])
set_layer_name(reshape_layer3, paddle_op)
if data_format == "NHWC":
transpose_layer2 = network.add_shuffle(reshape_layer3.get_output(0))
transpose_layer2.first_transpose = trt.Permutation([0, 2, 3, 1])
set_layer_name(transpose_layer2, paddle_op)
output_tensor = transpose_layer2.get_output(0)
else:
output_tensor = reshape_layer3.get_output(0)
return output_tensor
@converter_registry.register("pd_op.anchor_generator")
def anchor_generator_converter(network, paddle_op, inputs):
inputs = inputs[0]
input_dims = inputs.shape
anchor_sizes = paddle_op.attrs().get("anchor_sizes")
aspect_ratios = paddle_op.attrs().get("aspect_ratios")
stride = paddle_op.attrs().get("stride")
variances = paddle_op.attrs().get("variances")
offset = paddle_op.attrs().get("offset")
num_anchors = len(aspect_ratios) * len(anchor_sizes)
height = input_dims[1]
width = input_dims[2]
plugin_fields = [
trt.PluginField(
"anchor_sizes",
np.array(anchor_sizes, dtype=np.float32),
trt.PluginFieldType.FLOAT32,
),
trt.PluginField(
"aspect_ratios",
np.array(aspect_ratios, dtype=np.float32),
trt.PluginFieldType.FLOAT32,
),
trt.PluginField(
"stride",
np.array(stride, dtype=np.float32),
trt.PluginFieldType.FLOAT32,
),
trt.PluginField(
"variances",
np.array(variances, dtype=np.float32),
trt.PluginFieldType.FLOAT32,
),
trt.PluginField(
"offset",
np.array(offset, dtype=np.float32),
trt.PluginFieldType.FLOAT32,
),
trt.PluginField(
"num_anchors",
np.array(num_anchors, dtype=np.int32),
trt.PluginFieldType.INT32,
),
]
plugin_field_collection = trt.PluginFieldCollection(plugin_fields)
plugin_name = "pir_anchor_generator_plugin_dynamic"
plugin_version = "1"
plugin = get_trt_plugin(
plugin_name, plugin_field_collection, plugin_version
)
anchor_generator_layer = network.add_plugin_v2([inputs], plugin)
set_layer_name(anchor_generator_layer, paddle_op)
out0 = anchor_generator_layer.get_output(0)
out1 = anchor_generator_layer.get_output(1)
return (out0, out1)
@converter_registry.register("pd_op.affine_channel")
def affine_channel_converter(network, paddle_op, inputs):
x, scale, bias = inputs
data_layout = paddle_op.attrs().get("data_layout")
if isinstance(scale, trt.ITensor):
refit_manager = RefitManager()
scale_weights = refit_manager.get_trt_weight_tensor(scale.name)
bias_weights = refit_manager.get_trt_weight_tensor(bias.name)
else:
scale_weights = scale
bias_weights = bias
if data_layout == "NCHW":
channel_axis = 1
x_input = x
elif data_layout == "NHWC":
# Permute NHWC to NCHW
shuffle_layer1 = network.add_shuffle(x)
shuffle_layer1.first_transpose = (0, 3, 1, 2)
set_layer_name(shuffle_layer1, paddle_op)
x_input = shuffle_layer1.get_output(0)
channel_axis = 1
else:
raise ValueError(f"affine_channel: Unsupported layout: {data_layout}")
if scale_weights.size != bias_weights.size:
raise ValueError(
f"affine_channel: scale.size({scale_weights.size}) != bias.size({bias_weights.size})"
)
power_array = np.ones((scale_weights.size,), dtype=np.float32)
power_weights = trt.Weights(power_array)
layer = network.add_scale_nd(
input=x_input,
mode=trt.ScaleMode.CHANNEL,
shift=bias_weights,
scale=scale_weights,
power=power_weights,
channel_axis=channel_axis,
)
set_layer_name(layer, paddle_op)
if not layer:
raise RuntimeError("affine_channel: add_scale_nd failed.")
out_tensor = layer.get_output(0)
if data_layout == "NHWC":
shuffle_layer2 = network.add_shuffle(out_tensor)
shuffle_layer2.first_transpose = (0, 2, 3, 1)
set_layer_name(shuffle_layer2, paddle_op)
out_tensor = shuffle_layer2.get_output(0)
return out_tensor
@converter_registry.register("pd_op.shuffle_channel")
def shuffle_channel_converter(network, paddle_op, inputs):
input = inputs[0]
group = paddle_op.attrs().get("group")
input_shape_tensor = trt_shape(
network, input, name=[paddle_op.name(), 'input_shape_tensor']
)
batch_shape_tensor = get_shape_tensor_element(
network,
input_shape_tensor,
0,
name=[paddle_op.name(), 'batch_shape_tensor'],
)
channel_shape_tensor = get_shape_tensor_element(
network,
input_shape_tensor,
1,
name=[paddle_op.name(), 'channel_shape_tensor'],
)
group_tensor = add_1D_constant_layer(
network, group, name=[paddle_op.name(), 'group_tensor']
)
new_channel_shape_tensor = trt_div(
network,
channel_shape_tensor,
group_tensor,
name=[paddle_op.name(), 'new_channel_shape_tensor'],
)
shape_dim2 = [2, 3]
shape_dim2_tensor = trt_gather(
network,
input_shape_tensor,
shape_dim2,
name=[paddle_op.name(), 'shape_dim2_tensor'],
)
itensors = [
batch_shape_tensor,
group_tensor,
new_channel_shape_tensor,
shape_dim2_tensor,
]
reshape_tensor = trt_concat(
network, itensors, name=[paddle_op.name(), 'reshape_tensor']
)
layer = network.add_shuffle(input)
layer.set_input(1, reshape_tensor)
transpose_embed = trt.Permutation([0, 2, 1, 3, 4])
layer.second_transpose = transpose_embed
set_layer_name(layer, paddle_op)
output = layer.get_output(0)
output_layer = network.add_shuffle(output)
output_layer.set_input(1, input_shape_tensor)
set_layer_name(output_layer, paddle_op)
return output_layer.get_output(0)
@converter_registry.register("pd_op.full_batch_size_like")
def full_batch_size_like_converter(network, paddle_op, inputs):
input = inputs[0]
input_dim_idx = paddle_op.attrs().get("input_dim_idx")
output_dim_idx = paddle_op.attrs().get("output_dim_idx")
value = paddle_op.attrs().get("value")
shape = paddle_op.attrs().get("shape")
value = float(value)
input_shape_tensor = trt_shape(
network, input, name=[paddle_op.name(), 'input_shape_tensor']
)
batch_tensor = get_shape_tensor_element(
network,
input_shape_tensor,
input_dim_idx,
name=[paddle_op.name(), 'batch_tensor'],
)
shape_attr_tensor = add_1D_constant_layer(
network, shape, name=[paddle_op.name(), 'shape_attr_tensor']
)
gather_output_shape_indices = [
len(shape) if i == output_dim_idx else i for i in range(len(shape))
]
concat_inputs = [shape_attr_tensor, batch_tensor]
concat_tensor = trt_concat(
network, concat_inputs, name=[paddle_op.name(), 'concat_tensor']
)
out_shape_tensor = trt_gather(
network,
concat_tensor,
gather_output_shape_indices,
name=[paddle_op.name(), 'out_shape_tensor'],
)
layer = network.add_fill(shape=(), op=trt.FillOperation.LINSPACE)
value_tensor = add_1D_constant_layer(
network,
[value],
is_scalar=True,
name=[paddle_op.name(), 'value_tensor'],
)
beta_vec = [0.0] * len(shape)
beta_tensor = add_1D_constant_layer(
network,
beta_vec,
is_scalar=False,
name=[paddle_op.name(), 'beta_tensor'],
)
layer.set_input(0, out_shape_tensor)
layer.set_input(1, value_tensor)
layer.set_input(2, beta_tensor)
set_layer_name(layer, paddle_op)
return layer.get_output(0)