和田地区网站建设_网站建设公司_百度智能云_seo优化
2026/1/16 11:51:05 网站建设 项目流程

一、前言:为什么学这个hello案例?

这个案例是TensorRT的「入门敲门砖」,核心目标不是实现复杂功能,而是掌握TensorRT构建模型的4个核心步骤,理解builderconfignetworkengine这些核心组件的作用,为后续YOLO等复杂模型的高性能部署打基础。

简单说:这个案例就是教你「用TensorRT的C++ API,把一个极简的神经网络(输入→全连接→Sigmoid→输出)从「设计图」变成「可运行的优化模型文件」」。

二、核心概念先搞懂(盖房子比喻)

先把TensorRT的核心组件用「盖房子」的比喻讲清楚,避免一上来看代码懵:

TensorRT组件盖房子对应角色核心作用
TRTLogger(日志类)监理记录构建过程中的警告、信息、错误,出问题时能查原因
IBuilder(构建器)施工队负责把「网络设计图」变成「可运行的模型(engine)」,做底层优化
IBuilderConfig施工规范/资质规定施工的「资源上限」(比如最大临时空间、最大批处理量),TRT按此优化模型
INetworkDefinition房子的设计图定义网络的输入、输出、层结构(比如全连接层、激活层)
ICudaEngine(引擎)盖好的成品房子TRT优化后的最终模型,可直接用于推理,是「可执行文件」级别的产物
IHostMemory房子的「户型图二进制文件」把engine序列化成二进制数据,方便保存到硬盘、后续加载使用

三、hello案例代码逐段拆解

这份代码的核心逻辑是「定义组件→设计网络→构建模型→保存模型」,我们按代码执行顺序拆解,对应笔记里的4个核心步骤。

// tensorRT include#include<NvInfer.h>#include<NvInferRuntime.h>// cuda include#include<cuda_runtime.h>// system include#include<stdio.h>classTRTLogger:publicnvinfer1::ILogger{public:virtualvoidlog(Severity severity,nvinfer1::AsciiCharconst*msg)noexceptoverride{if(severity<=Severity::kVERBOSE){printf("%d: %s\n",severity,msg);}}};nvinfer1::Weightsmake_weights(float*ptr,intn){nvinfer1::Weights w;w.count=n;w.type=nvinfer1::DataType::kFLOAT;w.values=ptr;returnw;}intmain(){// 本代码主要实现一个最简单的神经网络 figure/simple_fully_connected_net.pngTRTLogger logger;// logger是必要的,用来捕捉warning和info等// ----------------------------- 1. 定义 builder, config 和network -----------------------------// 这是基本需要的组件//形象的理解是你需要一个builder去build这个网络,网络自身有结构,这个结构可以有不同的配置nvinfer1::IBuilder*builder=nvinfer1::createInferBuilder(logger);// 创建一个构建配置,指定TensorRT应该如何优化模型,tensorRT生成的模型只能在特定配置下运行nvinfer1::IBuilderConfig*config=builder->createBuilderConfig();// 创建网络定义,其中createNetworkV2(1)表示采用显性batch size,新版tensorRT(>=7.0)时,不建议采用0非显性batch size// 因此贯穿以后,请都采用createNetworkV2(1)而非createNetworkV2(0)或者createNetworknvinfer1::INetworkDefinition*network=builder->createNetworkV2(1);// 构建一个模型/* Network definition: image | linear (fully connected) input = 3, output = 2, bias = True w=[[1.0, 2.0, 0.5], [0.1, 0.2, 0.5]], b=[0.3, 0.8] | sigmoid | prob */// ----------------------------- 2. 输入,模型结构和输出的基本信息 -----------------------------constintnum_input=3;// in_channelconstintnum_output=2;// out_channelfloatlayer1_weight_values[]={1.0,2.0,0.5,0.1,0.2,0.5};// 前3个给w1的rgb,后3个给w2的rgbfloatlayer1_bias_values[]={0.3,0.8};//输入指定数据的名称、数据类型和完整维度,将输入层添加到网络nvinfer1::ITensor*input=network->addInput("image",nvinfer1::DataType::kFLOAT,nvinfer1::Dims4(1,num_input,1,1));nvinfer1::Weights layer1_weight=make_weights(layer1_weight_values,6);nvinfer1::Weights layer1_bias=make_weights(layer1_bias_values,2);//添加全连接层autolayer1=network->addFullyConnected(*input,num_output,layer1_weight,layer1_bias);// 注意对input进行了解引用//添加激活层autoprob=network->addActivation(*layer1->getOutput(0),nvinfer1::ActivationType::kSIGMOID);// 注意更严谨的写法是*(layer1->getOutput(0)) 即对getOutput返回的指针进行解引用// 将我们需要的prob标记为输出network->markOutput(*prob->getOutput(0));printf("Workspace Size = %.2f MB\n",(1<<28)/1024.0f/1024.0f);// 256Mibconfig->setMaxWorkspaceSize(1<<28);builder->setMaxBatchSize(1);// 推理时 batchSize = 1// ----------------------------- 3. 生成engine模型文件 -----------------------------//TensorRT 7.1.0版本已弃用buildCudaEngine方法,统一使用buildEngineWithConfig方法nvinfer1::ICudaEngine*engine=builder->buildEngineWithConfig(*network,*config);if(engine==nullptr){printf("Build engine failed.\n");return-1;}// ----------------------------- 4. 序列化模型文件并存储 -----------------------------// 将模型序列化,并储存为文件nvinfer1::IHostMemory*model_data=engine->serialize();FILE*f=fopen("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");return0;}

3.1 第一步:引入头文件+定义基础工具(准备施工材料)

// tensorRT核心头文件:构建模型、运行时推理#include<NvInfer.h>#include<NvInferRuntime.h>// CUDA头文件:TRT依赖CUDA运行#include<cuda_runtime.h>// 系统头文件:文件操作、打印#include<stdio.h>// 1. 定义日志类:继承TRT的ILogger,负责打印TRT的日志信息classTRTLogger:publicnvinfer1::ILogger{public:// 重写log方法:控制日志打印级别virtualvoidlog(Severity severity,nvinfer1::AsciiCharconst*msg)noexceptoverride{// 只打印级别<=VERBOSE的日志(包括INFO、WARNING、ERROR等)if(severity<=Severity::kVERBOSE){printf("%d: %s\n",severity,msg);}}};// 2. 辅助函数:把float数组封装成TRT的Weights结构体(权重/偏置专用)nvinfer1::Weightsmake_weights(float*ptr,intn){nvinfer1::Weights w;w.count=n;// 权重数量w.type=nvinfer1::DataType::kFLOAT;// 数据类型(浮点型)w.values=ptr;// 权重数据的指针returnw;}
关键解读:
  • TRTLogger必须的:TRT的所有操作都会产生日志,没有日志类会导致程序运行异常,也无法排查问题;
  • make_weights是简化代码的辅助函数:TRT的层(比如全连接层)需要权重/偏置以Weights结构体传入,这个函数把普通float数组转成对应格式。

3.2 第二步:初始化核心组件(组建施工队+制定规范+画设计图)

intmain(){// 目标:构建一个极简神经网络:输入(3维) → 全连接层(输出2维) → Sigmoid → 输出(2维)TRTLogger logger;// 创建监理(日志对象)// ----------------------------- 1. 定义 builder, config 和network -----------------------------// 1.1 创建施工队(builder):传入日志对象,让施工队的操作被监理记录nvinfer1::IBuilder*builder=nvinfer1::createInferBuilder(logger);// 1.2 创建施工规范(config):指定TRT优化模型的规则nvinfer1::IBuilderConfig*config=builder->createBuilderConfig();// 1.3 创建设计图(network):createNetworkV2(1) = 显性batch size(新版TRT≥7.0推荐,0已废弃)nvinfer1::INetworkDefinition*network=builder->createNetworkV2(1);
关键解读:
  • createNetworkV2(1)重点
    • 「显性batch size」:明确指定批处理大小(比如这里batch=1),推理时用enqueueV2接口;
    • 「隐性batch size(0)」:TRT自动处理批次,已被官方废弃,不要用;
  • 这三个组件是TRT构建模型的「基础三件套」,缺一不可。

3.3 第三步:设计网络结构(画设计图:输入→层→输出)

// ----------------------------- 2. 输入,模型结构和输出的基本信息 -----------------------------constintnum_input=3;// 输入维度(比如RGB 3通道)constintnum_output=2;// 输出维度(比如二分类)// 全连接层的权重:6个值 → 2个输出神经元 × 3个输入神经元floatlayer1_weight_values[]={1.0,2.0,0.5,0.1,0.2,0.5};// 全连接层的偏置:2个值 → 对应2个输出神经元floatlayer1_bias_values[]={0.3,0.8};// 2.1 定义输入层:addInput(名称, 数据类型, 维度)// Dims4是NCHW格式:(batch数, 通道数, 高度, 宽度) → 这里是1批、3通道、1高1宽(一维数据)nvinfer1::ITensor*input=network->addInput("image",nvinfer1::DataType::kFLOAT,nvinfer1::Dims4(1,num_input,1,1));// 2.2 封装权重和偏置nvinfer1::Weights layer1_weight=make_weights(layer1_weight_values,6);nvinfer1::Weights layer1_bias=make_weights(layer1_bias_values,2);// 2.3 添加全连接层:addFullyConnected(输入张量, 输出维度, 权重, 偏置)autolayer1=network->addFullyConnected(*input,num_output,layer1_weight,layer1_bias);// 2.4 添加Sigmoid激活层:addActivation(输入张量, 激活类型)autoprob=network->addActivation(*layer1->getOutput(0),nvinfer1::ActivationType::kSIGMOID);// 2.5 标记输出节点:告诉TRT这是网络的最终输出(推理时要取这个值)network->markOutput(*prob->getOutput(0));
关键解读:
  • 张量维度Dims4:TRT默认用NCHW格式(批数、通道数、高度、宽度),哪怕是一维数据(比如3个输入值),也要写成(1,3,1,1)
  • 层的调用规则:
    • addFullyConnected的第一个参数是「解引用的输入张量」(*input),因为addInput返回的是指针;
    • layer1->getOutput(0):全连接层的输出张量(一个层可能有多个输出,0是第一个);
  • markOutput:标记几次就有几个输出,addInput几次就有几个输入——推理时要和这个数量对应,否则会报错。

3.4 第四步:配置并生成Engine(按规范施工,盖出成品房子)

// 3.1 设置施工资源上限:最大工作空间(256MB)// 1 << 28 = 2^28 = 268435456 字节 = 256MBprintf("Workspace Size = %.2f MB\n",(1<<28)/1024.0f/1024.0f);config->setMaxWorkspaceSize(1<<28);// 3.2 设置最大批处理大小:推理时batchSize不能超过这个值(这里设为1)builder->setMaxBatchSize(1);// ----------------------------- 3. 生成engine模型文件 -----------------------------// 用「设计图+施工规范」构建engine(成品房子):buildEngineWithConfig是新版TRT推荐接口(替代废弃的buildCudaEngine)nvinfer1::ICudaEngine*engine=builder->buildEngineWithConfig(*network,*config);if(engine==nullptr){printf("Build engine failed.\n");return-1;}
关键解读:
  • setMaxWorkspaceSize:TRT优化模型时(比如层融合、精度校准)需要临时内存,这个参数指定「最大可用临时内存」,太小会导致构建失败,一般设256MB~1GB足够;
  • setMaxBatchSize:限制推理时的最大批大小,比如设1就只能单张图片推理,设8就最多一次推8张;
  • buildEngineWithConfig:核心构建接口,TRT会根据network(设计图)和config(规范),对网络做层融合、算子优化、精度优化,最终生成高效的engine

3.5 第五步:序列化并保存Engine(把房子转成文件保存)

// ----------------------------- 4. 序列化模型文件并存储 -----------------------------// 4.1 序列化engine:把二进制的模型数据存到IHostMemory中nvinfer1::IHostMemory*model_data=engine->serialize();// 4.2 写入文件:保存为engine.trtmodel(后续推理时直接加载这个文件即可)FILE*f=fopen("engine.trtmodel","wb");// 二进制写入模式fwrite(model_data->data(),1,model_data->size(),f);fclose(f);// 4.3 释放资源:按「创建顺序倒序」destroy,避免内存泄漏model_data->destroy();engine->destroy();network->destroy();config->destroy();builder->destroy();printf("Done.\n");return0;}
关键解读:
  • serialize():把内存中的engine转成二进制数据,方便保存到硬盘——这个二进制文件就是TRT的「优化模型文件」,后续推理时无需重新构建网络,直接加载即可;
  • 资源释放:TRT的所有组件(builder、config等)都需要手动destroy(),不像C++普通对象会自动析构,漏写会导致内存泄漏;
  • 保存的engine.trtmodel:是「平台相关」的——只能在「编译时的TRT版本、编译时的GPU设备」上最优运行,跨版本/跨设备可能运行缓慢甚至报错。

四、补充知识解读(笔记里的关键坑点)

  1. 显性batch size的重要性

    • 新版TRT(≥7.0)强制推荐createNetworkV2(1)(显性),0(隐性)已废弃;
    • 显性batch size影响推理接口:推理时要用enqueueV2,隐性用enqueue(已废弃)。
  2. Workspace Size的本质

    • 不是「模型占用的内存」,而是TRT优化层时的「临时工作内存」;
    • TRT为了内存复用,不会给每个层单独分配内存,而是从workspace里取,所以要保证足够大,但也不用过大(比如超过GPU显存)。
  3. 输入输出数量的对应性

    • addInput调用N次 → 推理时需要传入N个输入数据;
    • markOutput调用M次 → 推理时能获取M个输出数据;
    • 数量不匹配会导致推理时报错。
  4. Engine的兼容性

    • 「TRT版本兼容」:比如用TRT8.6编译的engine,在TRT8.2上可能加载失败;
    • 「设备兼容」:用RTX3090编译的engine,在RTX2080上能运行,但不是最优(TRT会针对编译时的GPU架构做优化)。

五、总结:核心要点回顾

  1. TensorRT构建模型的核心4步:「定义builder/config/network → 设计网络结构 → 生成engine → 序列化保存」;
  2. 关键组件:TRTLogger是必须的,createNetworkV2(1)要牢记,资源必须手动destroy()
  3. 核心产物:engine.trtmodel是TRT优化后的二进制模型文件,后续推理的核心依赖——构建模型的过程是「一次性的」,推理时直接加载这个文件即可,无需重复构建。

这个hello案例虽然简单,但涵盖了TRT构建模型的全流程,吃透它之后,你就能理解后续复杂模型(比如YOLO)的TRT部署核心逻辑——无非是把「全连接层」换成「卷积层/池化层」,把简单结构换成复杂结构而已。

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

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

立即咨询