站长网站大全网站网络
2026/6/26 21:21:00 网站建设 项目流程
站长网站大全,网站网络,余姚网站建设公司,湖南省建设厅官方网站一、前言#xff1a;为什么需要动态Shape#xff1f; 你复习的TensorRT课程里讲动态Shape#xff0c;核心解决的是「模型推理时输入尺寸不固定」的问题#xff1a; 静态Shape#xff1a;模型编译后输入尺寸固定#xff08;比如只能处理3x3的图片#xff09;#xff0c;…一、前言为什么需要动态Shape你复习的TensorRT课程里讲动态Shape核心解决的是「模型推理时输入尺寸不固定」的问题静态Shape模型编译后输入尺寸固定比如只能处理3x3的图片要换尺寸就得重新编译像“只能生产固定尺寸产品的流水线”动态Shape编译时指定尺寸范围比如3x3~5x5推理时可在范围内任意换尺寸像“可调节的流水线能适配不同尺寸原料”全卷积网络比如图像分割、目标检测特别需要这个功能——推理时输入图片的宽高不用固定死。二、核心概念动态Shape是什么动态Shape的本质是模型编译时指定输入维度的「合法范围」[最小-最大]推理时可在范围内任意设置具体值。笔记里的定义很直白推理时可以允许 LshapeHL是最小尺寸H是最大尺寸。三、案例代码拆解动态Shape的实现步骤笔记里的代码分「模型构建build_model」和「模型推理inference」两大核心部分我们逐段拆解重点讲动态Shape的关键操作。先看代码整体逻辑整个流程是定义网络结构卷积ReLU→ 配置动态Shape范围 → 编译生成engine文件 → 加载engine → 设置推理时的具体Shape → 执行推理 → 输出结果3.1 辅助函数基础工具不用纠结重点看核心逻辑// 日志类打印TensorRT的日志信息classTRTLogger:publicnvinfer1::ILogger{public:virtualvoidlog(Severity severity,nvinfer1::AsciiCharconst*msg)noexceptoverride{if(severitySeverity::kINFO){// 只打印INFO及以下级别日志printf(%d: %s\n,severity,msg);}}}logger;// 封装权重把float数组转成TensorRT的Weights格式nvinfer1::Weightsmake_weights(float*ptr,intn){nvinfer1::Weights w;w.countn;// 权重数量w.typenvinfer1::DataType::kFLOAT;// 数据类型w.valuesptr;// 权重数据指针returnw;}// 加载文件把engine文件读成二进制数组用于反序列化vectorunsignedcharload_file(conststringfile){ifstreamin(file,ios::in|ios::binary);if(!in.is_open())return{};in.seekg(0,ios::end);size_t lengthin.tellg();std::vectoruint8_tdata;if(length0){in.seekg(0,ios::beg);data.resize(length);in.read((char*)data[0],length);}in.close();returndata;}3.2 核心1模型构建build_model—— 动态Shape的关键配置这部分是动态Shape的核心重点是「定义动态维度」和「配置Optimization Profile」。boolbuild_model(){TRTLogger logger;// 1. 创建基础组件builder构建器、config配置、network网络nvinfer1::IBuilder*buildernvinfer1::createInferBuilder(logger);nvinfer1::IBuilderConfig*configbuilder-createBuilderConfig();nvinfer1::INetworkDefinition*networkbuilder-createNetworkV2(1);// 1表示启用动态Shape// 2. 定义网络结构卷积(3x3, pad1) ReLUconstintnum_input1;// 输入通道数固定constintnum_output1;// 输出通道数固定// 卷积核权重3x3行优先floatlayer1_weight_values[]{1.0,2.0,3.1,0.1,0.1,0.1,0.2,0.2,0.2};floatlayer1_bias_values[]{0.0};// 偏置// -------------------------- 动态Shape关键1定义动态维度 --------------------------// Dims4(-1, num_input, -1, -1)四个维度分别是batch、通道、高、宽// -1 表示该维度是动态的通道数固定为1所以写num_inputnvinfer1::ITensor*inputnetwork-addInput(image,nvinfer1::DataType::kFLOAT,nvinfer1::Dims4(-1,num_input,-1,-1));// 构建卷积层nvinfer1::Weights layer1_weightmake_weights(layer1_weight_values,9);nvinfer1::Weights layer1_biasmake_weights(layer1_bias_values,1);autolayer1network-addConvolution(*input,num_output,nvinfer1::DimsHW(3,3),layer1_weight,layer1_bias);layer1-setPadding(nvinfer1::DimsHW(1,1));// 卷积padding1保证输入输出尺寸一致// 构建ReLU激活层autoprobnetwork-addActivation(*layer1-getOutput(0),nvinfer1::ActivationType::kRELU);network-markOutput(*prob-getOutput(0));// 标记输出// 配置工作空间TensorRT需要的临时内存128256MBconfig-setMaxWorkspaceSize(128);// -------------------------- 动态Shape关键2配置Optimization Profile --------------------------// Profile是“尺寸配置文件”定义动态维度的最小/最优/最大范围autoprofilebuilder-createOptimizationProfile();// 设置输入image的尺寸范围// kMIN最小允许尺寸1,1,3,3→ batch1通道1高3宽3profile-setDimensions(input-getName(),nvinfer1::OptProfileSelector::kMIN,nvinfer1::Dims4(1,num_input,3,3));// kOPT最优尺寸推理时优先用这个尺寸性能最好profile-setDimensions(input-getName(),nvinfer1::OptProfileSelector::kOPT,nvinfer1::Dims4(1,num_input,3,3));// kMAX最大允许尺寸10,1,5,5→ batch最大10高宽最大5profile-setDimensions(input-getName(),nvinfer1::OptProfileSelector::kMAX,nvinfer1::Dims4(10,num_input,5,5));// 把profile添加到配置中没有这步动态Shape不生效config-addOptimizationProfile(profile);// 编译生成engine核心产物包含模型的推理逻辑nvinfer1::ICudaEngine*enginebuilder-buildEngineWithConfig(*network,*config);if(enginenullptr){printf(Build engine failed.\n);returnfalse;}// 序列化engine把engine转成二进制文件保存方便后续加载nvinfer1::IHostMemory*model_dataengine-serialize();FILE*ffopen(engine.trtmodel,wb);fwrite(model_data-data(),1,model_data-size(),f);fclose(f);// 释放资源倒序释放model_data-destroy();engine-destroy();network-destroy();config-destroy();builder-destroy();printf(Done.\n);returntrue;}动态Shape关键1定义动态维度-1的含义nvinfer1::ITensor*inputnetwork-addInput(image,...,nvinfer1::Dims4(-1,num_input,-1,-1));Dims4(batch, channel, height, width)TensorRT中输入维度默认是「NCHW」格式-1标记该维度为「动态维度」推理时可在profile的范围里改注意如果维度不是-1比如channel1则min/opt/max必须和这个值一致不能改。动态Shape关键2配置Profile尺寸范围Profile是动态Shape的「规则手册」告诉TensorRTkMIN输入尺寸不能小于这个值比如不能处理2x2的图片kMAX输入尺寸不能大于这个值比如不能处理6x6的图片kOPTTensorRT优化时优先针对这个尺寸做性能优化 笔记里的重点不要被“Optimization”迷惑Profile的核心是“指定尺寸范围”不是“优化性能”。3.3 核心2模型推理inference—— 动态Shape的使用推理阶段的关键是「设置本次推理的具体尺寸」代码如下voidinference(){// 1. 加载engine文件并反序列化TRTLogger logger;autoengine_dataload_file(engine.trtmodel);nvinfer1::IRuntime*runtimenvinfer1::createInferRuntime(logger);nvinfer1::ICudaEngine*engineruntime-deserializeCudaEngine(engine_data.data(),engine_data.size());if(enginenullptr){printf(Deserialize cuda engine failed.\n);runtime-destroy();return;}// 创建执行上下文每个推理可以有独立的上下文支持并行推理nvinfer1::IExecutionContext*execution_contextengine-createExecutionContext();cudaStream_t streamnullptr;cudaStreamCreate(stream);// 创建CUDA流异步推理用// 2. 准备输入输出数据// 输入数据2个batch每个batch是3x3的矩阵floatinput_data_host[]{// batch 0全1矩阵1,1,1,1,1,1,1,1,1,// batch 1混合正负值-1,1,1,1,0,1,1,1,-1};float*input_data_devicenullptr;float*output_data_devicenullptr;// 分配GPU内存cudaMalloc(input_data_device,sizeof(input_data_host));cudaMalloc(output_data_device,sizeof(input_data_host));// 输出尺寸和输入一致卷积padding1// 把主机数据拷贝到GPUcudaMemcpyAsync(input_data_device,input_data_host,sizeof(input_data_host),cudaMemcpyHostToDevice,stream);// -------------------------- 动态Shape关键3设置本次推理的具体尺寸 --------------------------intib2;// batch2intiw3;// 宽3intih3;// 高3// 设置绑定维度第0个绑定点输入的尺寸为(2,1,3,3)execution_context-setBindingDimensions(0,nvinfer1::Dims4(ib,1,ih,iw));// 3. 执行推理float*bindings[]{input_data_device,output_data_device};// 绑定输入输出的GPU指针// enqueueV2异步推理用CUDA流boolsuccessexecution_context-enqueueV2((void**)bindings,stream,nullptr);// 把GPU输出拷贝回主机floatoutput_data_host[ib*iw*ih];cudaMemcpyAsync(output_data_host,output_data_device,sizeof(output_data_host),cudaMemcpyDeviceToHost,stream);cudaStreamSynchronize(stream);// 等待流执行完成// 4. 打印输出结果for(intb0;bib;b){printf(batch %d. output_data_host \n,b);for(inti0;iiw*ih;i){printf(%f, ,output_data_host[b*iw*ihi]);if((i1)%iw0)printf(\n);}}// 释放资源cudaStreamDestroy(stream);cudaFree(input_data_device);cudaFree(output_data_device);execution_context-destroy();engine-destroy();runtime-destroy();}动态Shape关键3推理时设置具体尺寸execution_context-setBindingDimensions(0,nvinfer1::Dims4(ib,1,ih,iw));0输入的「绑定索引」因为模型只有一个输入所以是0Dims4(2,1,3,3)本次推理用的具体尺寸batch2通道1高3宽3注意这个尺寸必须在profile的min~max范围内比如不能设成batch11或高6否则推理会失败。3.4 主函数串联构建和推理intmain(){if(!build_model()){// 先构建模型生成engine文件return-1;}inference();// 再执行推理return0;}四、补充知识静态Batch vs 动态Batch笔记里重点提了“大部分时候只考虑batch维度的动态”我们用表格对比特性静态Batch动态Batch维度定义所有维度都是固定数字比如Dims4(1,1,3,3)batch维度设为-1比如Dims4(-1,1,3,3)编译后只能处理固定batch的输入可处理min~max范围内的任意batch推理性能固定尺寸优化性能略高适配不同尺寸性能稍低但灵活适用场景批量推理比如离线处理大量图片服务端推理比如用户请求的batch大小不固定代码复杂度低不用配置profile稍高需要配置profile、推理前设维度五、关键避坑点笔记里的重点Reshape层的坑如果模型里有Reshape操作Reshape的参数必须「动态计算」比如基于输入尺寸否则动态Shape会失效除非是全卷积模型否则尽量只把batch设为动态宽高固定。ONNX动态维度如果从ONNX导入模型ONNX里的动态维度也要标为-1否则TensorRT里设-1也没用。维度一致性非动态维度比如通道的min/opt/max必须和网络定义的维度一致否则编译失败。正确性校验可以用PyTorch跑相同的模型对比输出结果笔记里的图1-1和1-2确保TensorRT的动态Shape推理正确。六、总结动态Shape核心要点定义阶段网络输入维度用-1标记动态维度通常只标batch配置Profile指定min/opt/max范围推理阶段每次推理前用setBindingDimensions设置具体的输入尺寸必须在Profile范围内适用场景静态Batch适合离线批量处理动态Batch适合服务端不均匀请求核心本质动态Shape是TensorRT为“可变输入尺寸”设计的适配机制核心是「提前约定范围推理时指定具体值」。吃透这些知识点你就能在TensorRT中灵活处理不同尺寸的输入满足高性能部署中“灵活推理”的需求。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询