TensorRT 动态batch推理

  跑 官方示例 发现不是动态batch,想改成动态batch过程中碰到了一些问题,以下为整个流程。

1. 将模型文件导出为onnx文件,需要添加dynamic_axes选项,我这里是动态batch,第0维度设置为动态,命名为bs。此外输入输出也需要命名,我这里是输入为input,输出为output,后面要用。

# input
BATCH_SIZE=32
dummy_input=torch.randn(BATCH_SIZE, 3, 224, 224)

# export the model to ONNX
input_name = 'input'
output_names= "output"

torch.onnx.export(resnet50, dummy_input, "resnet50_pytorch.onnx", input_names=[input_name], output_names=[output_names], dynamic_axes = {input_name:{0: "bs"}, output_names:{0:"bs"}} )

2. 导出后可通过可视化平台查看是否有误,可视化平台右侧菜单如下

在这里插入图片描述

3. 将onnx转成trt模型,指令如下

trtexec --onnx=resnet50_pytorch.onnx --saveEngine=resnet_engine_pytorch.trt --inputIOFormats=fp16:chw --outputIOFormats=fp16:chw --minShapes=input:1x3x224x224 --optShapes=input:32x3x224x224 --maxShapes=input:64x3x224x224  --fp16

参数说明:

  • minShapes、optShapes、maxShapes: 分别是最小、推理常用、最大的尺寸,使用动态batch就意味着是显式批处理,并且高版本TensorRT(10.0之后)隐式batch已经被删除,其中的input就是步骤2中设置的名称
  • inputIOFormats、outputIOFormats: 输入与输出的数据类型,这里均为fp16,要与自己的输入类型匹配,否则结果会出错,如我输入的是fp16的numpy array,但是没有加这个选项,结果就会出错,默认为fp32,如果输入也是默认的类型,则不用加这个选项
  • fp16: 推理时模型的精度

4. 推理阶段

  • (1)创建引擎与上下文
import tensorrt as trt
import pycuda.driver as cuda
import pycuda.autoinit

f = open("resnet_engine_pytorch.trt", "rb")
runtime = trt.Runtime(trt.Logger(trt.Logger.WARNING)) 

engine = runtime.deserialize_cuda_engine(f.read())
context = engine.create_execution_context()

创建引擎后可通过下面的查看是否为动态batch:

context.get_tensor_shape("input")

结果如下:

在这里插入图片描述

  • (2)分配显存
# 最大batch 与 数据类型
MAX_BATCH_SIZE = 32
target_dtype = np.float16

# 计算最大batch对应的显存大小
dynamic_input_dim = np.array(engine.get_tensor_shape("input"))
dynamic_output_dim = np.array(engine.get_tensor_shape("output"))
dynamic_input_dim[0] = MAX_BATCH_SIZE
dynamic_output_dim[0] = MAX_BATCH_SIZE
max_input = np.empty(dynamic_input_dim, dtype=target_dtype)
max_output = np.empty(dynamic_output_dim, dtype=target_dtype)

max_input_memory = max_input.nbytes
max_output_memory = max_output.nbytes


# 根据最大的BATCH_SIZE分配显存
d_input = cuda.mem_alloc(1 * max_input_memory)
d_output = cuda.mem_alloc(1 * max_output_memory)

# 将分配的显存地址分配给输入输出buffer
context.set_tensor_address(engine.get_tensor_name(0), int(d_input)) 
context.set_tensor_address(engine.get_tensor_name(1), int(d_output)) 
stream = cuda.Stream()

del max_input, max_output
  • (3) 推理部分

  推理部分要根据输入数据动态的设定上下文推理形状

def predict(batch): 

    # 根据输入batch设置输出大小
    N, _, _, _ = batch.shape[0]
    output = np.empty([N, 1000], dtype = target_dtype) 

    # 设置上下文输入形状 
    tensor_name = engine.get_tensor_name(0) # input tensor
    context.set_input_shape(tensor_name, batch.shape)

    # 将数据从内存移动到显存中
    cuda.memcpy_htod_async(d_input, batch, stream)

    # 异步执行模型
    context.execute_async_v3(stream.handle)

    # 将输出从显存中移动到内存中
    cuda.memcpy_dtoh_async(output, d_output, stream)

    # 同步线程
    stream.synchronize()
    
    return output

pred = predict(preprocessed_images)

5. 参考资料

  • https://blog.youkuaiyun.com/hjxu2016/article/details/119796206
  • https://docs.nvidia.com/deeplearning/tensorrt/api/python_api/infer/Core/Engine.html#tensorrt.ICudaEngine.get_tensor_vectorized_dim
  • https://blog.youkuaiyun.com/HW140701/article/details/120360642
### TensorRT 中实现动态批次大小的方法 在 TensorRT 中支持动态批量大小的能力对于优化不同场景下的性能至关重要。通过使用 Python API 或者 C++ API,可以灵活配置网络以适应不同的输入尺寸。 #### 使用 Python API 配置动态批处理 当构建引擎时,允许指定最小、最优以及最大维度来定义形状范围: ```python import tensorrt as trt def create_engine(onnx_file_path, engine_file_path=""): TRT_LOGGER = trt.Logger(trt.Logger.WARNING) with trt.Builder(TRT_LOGGER) as builder, \ builder.create_network(1 << int(trt.NetworkDefinitionCreationFlag.EXPLICIT_BATCH)) as network, \ trt.OnnxParser(network, TRT_LOGGER) as parser: config = builder.create_builder_config() profile = builder.create_optimization_profile() # 定义输入张量的形状范围 (min, opt, max) input_shape_min = (1, 3, 224, 224) input_shape_optimal = (8, 3, 224, 224) input_shape_max = (64, 3, 224, 224) profile.set_shape( "input", min=input_shape_min, opt=input_shape_optimal, max=input_shape_max ) config.add_optimization_profile(profile) ... ``` 此代码片段展示了如何设置 `OptimizationProfile` 来描述可能遇到的不同批次大小的情况[^4]。 #### 后端执行阶段调整批次数目 一旦创建好了具有灵活性的序列化引擎文件,在实际运行期间可以根据具体需求改变批次数量而无需重新编译整个模型。这通常涉及到修改输入数据的形式并确保它匹配当前使用的批次参数。 ```python with open(engine_file_path, 'rb') as f, trt.Runtime(TRT_LOGGER) as runtime: engine = runtime.deserialize_cuda_engine(f.read()) context = engine.create_execution_context() # 假设我们想要用新的批次大小推断 new_batch_size = 16 if not context.can_execute_v2(): raise RuntimeError("This sample only works on devices that support can_execute_v2.") for binding in range(engine.num_bindings): if engine.binding_is_input(binding): dims = list(context.get_binding_shape(binding)) dims[0] = new_batch_size context.set_binding_shape(binding, tuple(dims)) output = np.empty([new_batch_size, output_channels], dtype=np.float32) ... ``` 这段脚本说明了怎样安全地更改上下文中绑定对象的形状属性以便于容纳更多或更少的数据样本。 #### 处理输出结果 完成推理之后还需要考虑如何有效地解析返回的结果数组。如果原始设计是以固定长度的一维向量形式给出,则可以通过 NumPy 的 reshape 函数轻松转换成多维结构用于进一步分析或者可视化展示[^5]: ```python h_outputs = ... # 推理后的输出指针指向的内容被复制到这里面 shape_of_output = (-1, num_classes) # 这里的-1表示自动计算合适的第一个轴上的元素数目 reshaped_outputs = h_outputs.reshape(shape_of_output) ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值