Yolov7的tensorRT部署与使用

本文详细介绍了TensorRT的环境配置步骤,包括安装TensorRT和LibTorch,以及如何在VisualStudio中设置编译选项。重点讲述了TensorRT的使用流程,包括自定义网络构建和外部模型导入,以及如何将Yolo模型转换为TensorRT并实现部署,结果显示TensorRT显著提高了模型运行速度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一. 环境配置

1.tensorRT安装

        TensorRT是英伟达公司出品的高性能的推断C++库,应用于边缘设备的推理,可以将训练好的模型分解再进行融合,融合后的模型具有高度的集合度,其直接利用cuda以在显卡上运行,所有的代码库仅仅包括C++和cuda,在利用此优化库运行代码时,运行速度和所占内存的大小都会大大缩减。

        在官网下载tensorRT,本文选取版本8.4.2.4;

        下载并解压后,将tensorRT/bin目录加至Path环境变量中;

D:\TensorRT-8.4.2.4\bin

        将TensorRT\lib下的lib文件拷贝至CUDA的lib\x64路径,如C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v11.2\lib\x64;将TensorRT\lib下的dll文件拷贝至C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v11.2\bin。

        Visual Studio配置

        打开任意一个项目,打开项目属性->C/C++->常规->附加包含目录,将TensorRT\include加入附加包含目录;打开项目属性->链接器->附加库目录,将TensorRT\lib加入附加库目录;打开项目属性->链接器->输入->附加依赖项,将TensorRT\lib下的所有lib文件加入附加依赖项;

nvinfer.lib
nvinfer_plugin.lib
nvonnxparser.lib
nvparsers.lib

2. LibTorch安装

        从Libtorch官网下载并解压,此后对VS进行配置。打开项目属性->C/C++->附加包含目录,将Libtorch\include和Libtorch\include\torch\csrc\api\include加入;

        打开项目属性->链接器->常规->附加库目录,将Libtorch\lib加入;打开项目属性->链接器->输入->附加依赖性,将Libtorch\lib下的lib文件名全部加入;打开项目属性->配置属性->调试->环境,将D:\libtorch\bin;%PATH%加入其中;

二.TensorRT的使用

1.TensorRT的流程

       TensorRT包含解析器parser、构建器builder、引擎engine、执行上下文context等;解析器负责加载模型框架与权重,构建器负责构建网络,引擎与执行上下文负责在网络中进行前向传播。

        一般而言,TensorRT的支持自定义构建TensorRT模型和从外界导入模型两种。

 网络自定义构建

        TensorRT支持从头创建网络,其一般流程如下。

        创建logger->创建builder->创建Network->创建网络->标记输入输出->设置运行参数config->创建engine->创建上下文并推理;

class Logger : public ILogger           
 {
     void log(Severity severity, const char* msg) override
     {
         // suppress info-level messages
         if (severity != Severity::kINFO)
             std::cout << msg << std::endl;
     }
 } gLogger;
//builder创建
IBuilder* builder = createInferBuilder(gLogger);
//network创建
INetworkDefinition* network = builder->createNetworkV2(1U << static_cast<uint32_t>(NetworkDefinitionCreationFlag::kEXPLICIT_BATCH));

        上述代码创建了builder与network,可以使用network->add函数以在模型中增加模块。亦可以在增加模块的时候对齐命名,使用getOutput->setName即可命名。

IBuilderConfig* config = builder->createBuilderConfig();
config->setMaxBatchSize(maxBatchSize);//设置批量大小
config->setMaxWorkspaceSize(1 << 30);//设置最大运行空间

//创建engine
ICudaEngine* engine = builder->buildEngineWithConfig(*network, *config);

       上述代码则实现了config的创建,以配置运行空间等参数,此后创建了引擎。此后可以使用引擎进行推理,也可以使用engine->serialize()函数以将模型序列化保存。

        当使用完毕后,使用如下函数进行析构。

serializedModel->destroy();
engine->destroy();
network->destroy();
config->destroy();
builder->destroy();

外界模型文件的导入

        tensorRT可以直接解析caffe和TensorFlow的网络模型;而caffe2,pytorch,mxnet,chainer,CNTK等框架则是需要将模型转为 ONNX(开放式神经网络交换工具) ,再对ONNX模型做解析以转化为TensorRT模型。其支持的精度包括FP32/FP16/TF32等。

        在得到导出的ONNX模型以后,应先使用onnxruntime包,在python上进行调用,以验证此模型的正确性,在得到了验证后再转化为TensorRT文件以进行后续部署。

        得到onnx文件后,在/tensorRT/bin目录下,调用trtexec.exe文件即可将onnx转为tensorRT文件。其调用指令如下。待运行完毕后即可得到tensorRT文件。

#640 批量固定为1
./trtexec.exe    --onnx=yolov7.onnx  --saveEngine=yolov7-tiny.trt  --buildOnly
# 640 动态维度 最大最小shape可更改
./trtexec.exe    --onnx=yolov7.onnx   	--saveEngine=yolov7.trt       --buildOnly --minShapes=images:1x3x640x640 --optShapes=images:2x3x640x640 --maxShapes=images:4x3x640x640
# 1280 动态维度
./trtexec.exe  --onnx=yolov7-w6.onnx    --saveEngine=yolov7-w6.trt    --buildOnly --minShapes=images:1x3x1280x1280 --optShapes=images:2x3x1280x1280 --maxShapes=images:4x3x1280x1280

        而对于调用TRT模型而言,其一般流程如下:

        读入trt文件->创建Iruntime->Iruntime反序列化trt文件以得到engine->engine创建context->为运行分配空间->将数据拷贝至分配的空间上->模型推理->后处理;

class Logger : public ILogger {
public:
	void log(Severity severity, const char* msg) noexcept override
	{
		// suppress info-level messages
		if (severity <= Severity::kWARNING)
			std::cout << msg << std::endl;
	}
}gLogger;

//假设模型已经被读入到vector<char> modeltrt中
initLibNvInferPlugins(&gLogger, "");
//模型导入
Logger m_logger;
IRuntime* runtime = createInferRuntime(gLogger);
ICudaEngine* engine = runtime->deserializeCudaEngine(modeltrt.data(), modeltrt.size(), nullptr);
IExecutionContext* context = engine->createExecutionContext();

//分配空间
void* buffers[2];
buffers[0] = buffers[1] = NULL;
cudaMallocManaged(&buffers[0], 3 * 640 * 640 * 4);
cudaMallocManaged(&buffers[1], 1 * 25200 * 85 * 4);
cudaStream_t stream;
cudaStreamCreate(&stream);

//假设输入数据已经复制到buffers上,运行
context->enqueueV2(buffers, stream, nullptr);

        TRT亦可直接解析ONNX/CAFFE模型等,其流程类似,即:

        创建Ibuilder->创建InetworkDefiniton->创建IParser->parser调用parsefromFile函数解析文件->标记网络输出->创建IbuilderConfig以配置运行环境->创建engine->序列化或进行推理;

class Logger : public ILogger {
public:
	void log(Severity severity, const char* msg) noexcept override
	{
		// suppress info-level messages
		if (severity <= Severity::kWARNING)
			std::cout << msg << std::endl;
	}
}gLogger;

IBuilder* builder = createInferBuilder(gLogger);
//创建网络
const auto explicitBatch = 1U << static_cast<uint32_t>(NetworkDefinitionCreationFlag::kEXPLICIT_BATCH);  
INetworkDefinition* network = builder->createNetworkV2(explicitBatch);

//创建解析器
nvonnxparser::IParser* parser = nvonnxparser::createParser(*network, gLogger);

//onnx_filename为onnx路径
parser->parseFromFile(onnx_filename, ILogger::Severity::kWARNING);

//设置运行参数
IBuilderConfig* config = builder->createBuilderConfig();
config->setMaxBatchSize(maxBatchSize);//设置最大batchsize
config->setMaxWorkspaceSize(1 << 30);//2^30 ,这里是1G

//创建engine
ICudaEngine* engine = builder->buildEngineWithConfig(*network, *config);
//序列化
IHostMemory *serializedModel = engine->serialize();

2.yolo的tensorRT部署

        而对于本文的yolo,首先从YOLOv7官网将项目克隆,从Release下载yolov7.pt权重后,使用以下指令导出动态维度的yolov7;

        亦可去除dynamic参数以导出不具有动态维度的onnx模型,此时批量大小为1。下文使用不带有动态维度的onnx模型。

python export.py --weights yolov7.pt  --dynamic  --grid

       通过onnxruntime调用验证其正确性后,调用上文的trtexec文件生成yolov7.engine文件。首先将trt文件读入至vector中;

string TRTpath = "your trtpath";
ifstream trtinput(TRTpath, ios_base::binary);
int trtsize = 0;
if (trtinput.is_open()) {
	trtinput.seekg(0, ios::end);
	trtsize = trtinput.tellg();
	trtinput.seekg(0, ios::beg);
}
vector<char> modeltrt(trtsize);
trtinput.read(modeltrt.data(), trtsize);
trtinput.close();

        此后创建Iruntime、IcudaEngine、IExecutionContext,ICudaEngine使用deserializeCudaEngine函数以反序列化模型;

Logger m_logger;
IRuntime* runtime = createInferRuntime(gLogger);
ICudaEngine* engine = runtime->deserializeCudaEngine(modeltrt.data(), modeltrt.size(), nullptr);
IExecutionContext* context = engine->createExecutionContext();

        此时已经创建好了对应的engine与context,接下来所需要的是分配对应的空间,由于在GPU上运行,需要使用cudaMallocManaged函数以进行分配。yolov7的输出为1*25200*85,使用float类型,故分配1*25200*85*4的空间,输入为三通道的640*640图像,故输入为3*640*640*4。

void* buffers[2];
buffers[0] = buffers[1] = NULL;
cudaMallocManaged(&buffers[0], 3 * 640 * 640 * 4);
cudaMallocManaged(&buffers[1], 1 * 25200 * 85 * 4);
cudaStream_t stream;
cudaStreamCreate(&stream);

        此后将数据复制到buffer数组上并推理。需要注意的是,yolo的输入为3*640*640,图像为640*640*3,需要调用permute函数交换维度的同时,还需要调用reshape函数,同时还需要将BGR转化为RGB格式。这是因为permute函数只是改变了张量解释的方法,并没有改变张量在内存中的存储,因而直接调用cudamemcpy函数以后仍然是没有调整维度的原张量,而reshape函数会重新分配一块内存,此时维度才能在内存上得到交换。最后将输出结果进行后处理即可。

tensorimage1 = tensorimage1.permute({ 2,0,1 }).toType(torch::kFloat).unsqueeze(0).to(at::kCUDA);
tensorimage1 = tensorimage1 / 255.0;
tensorimage1 = tensorimage1.reshape(-1);
cudaMemcpy(buffers[0], tensorimage1.data_ptr(), 4 * 3 * 640 * 640, cudaMemcpyHostToDevice);
context->enqueueV2(buffers, stream, nullptr);
torch::Tensor output = torch::ones({ 1 ,25200 ,85 }, at::kFloat);
cudaMemcpy(output.data_ptr<float>(), buffers[1], 25200 * 85 * 4, cudaMemcpyDeviceToHost);

        需要注意的是,yolo本身并不是直接将图片进行resize的,而是进行了等比例缩放,其余地方进行填充的。即letterbox函数,在yolo中,网格的大小并不是固定为20*20,40*40,80*80的,,输入也不必固定为640*640,而是可以根据图像的长宽比自动缩放的。但是导出为trt以后失去了这个功能,网格大小被固定为20/40/80,且输入也要被固定。

        本文的letterbox函数如下。

Mat latterBox(const Mat& img, Size new_Shape ,Scalar color,bool auto_size,bool scale_fill, bool scale_up,float& ratio) {
	int rows = img.rows,cols = img.cols;
	float Scale_ratio = min((float)new_Shape.height / rows, (float)new_Shape.width / cols);
	if (!scale_up)	//如果不允许扩大 仅仅允许缩小
		Scale_ratio = min(Scale_ratio,1.0f);
	int new_origin_width = cols * Scale_ratio, new_origin_height = rows * Scale_ratio;
	int pad_width = new_Shape.width - new_origin_width,pad_height = new_Shape.height - new_origin_height;
	if (auto_size) {
		pad_width = pad_width % 32;
		pad_height = pad_height % 32;

	}
	else if(scale_fill) {
		pad_width = 0;
		pad_height = 0;
		new_origin_height = new_Shape.height;
		new_origin_width = new_Shape.width;
	}
	ratio = Scale_ratio;
	Mat resize_img;
	//先转过来 对应比例转化且保证被32整除
	resize(img, resize_img, Size(new_origin_width, new_origin_height), 0, 0, cv::INTER_LINEAR);
	int top = pad_height / 2;
	int bottom = pad_height - top;
	int left = pad_width / 2;
	int right = pad_width - left;
	Mat temp;
	copyMakeBorder(resize_img, temp, top, bottom, left, right, BORDER_CONSTANT, color);

	//再进行填充 转到640*640
	Mat result(new_Shape.height, new_Shape.width, CV_8UC3,color);
	int y = (new_Shape.height - temp.rows) / 2;
	int x = (new_Shape.width - temp.cols) / 2;
	temp.copyTo(result(Rect(x,y,temp.cols,temp.rows)));
	return result;
}

        后处理包括对每一个检测框进行IOU计算与NMS抑制。yolov7的输出为1*25200*85,本质上是由3*20*20*85+3*40*40*85+3*80*80*85,其含义为将图片划分为20*20或40*40或80*80的网格,每一个网格中检测3个检测框;而最后一维的85由四个坐标+置信度+80种类别的可能性组成,其含义为检测框的左上角和右下角的坐标+置信度+检测框的物体类别,80基于COCO80数据集。

       通过设置置信度阈值,选出大于置信度的所有检测框,再对每一个检测框进行NMS抑制,即可得到最终的检测框。

        在本文中,NMS和IOU计算函数如下。

class Bbox
{
public:
	int x;
	int y;
	int h;
	int w;
	float score;
	int type_number;//类型编号
};
//iou计算
float calculateIOU(Bbox& box1, Bbox& box2) {
	int min_x = max(box1.x - box1.h / 2, box2.x - box2.h / 2);  // 找出左上角坐标哪个大
	int max_x = min(box1.x + box1.h / 2, box2.x + box2.h / 2);  // 找出右上角坐标哪个小

	int min_y = max(box1.y - box1.w / 2, box2.y - box2.w / 2);
	int max_y = min(box1.y + box1.w / 2, box2.y + box2.w / 2);

	if (min_x >= max_x || min_y >= max_y) // 如果没有重叠
		return 0;
	float over_area = (max_x - min_x) * (max_y - min_y);  // 计算重叠面积
	float area_a = box1.h * box1.w;
	float area_b = box2.h * box2.w;
	float iou = over_area / (area_a + area_b - over_area);
	return iou;
}
vector<Bbox> NMS(vector<Bbox>& boxes,  float threshold) {
	sort(boxes.begin(), boxes.end(), cmp);
	vector<Bbox> result;
	while (boxes.size() > 0) {
		result.push_back(boxes[0]);
		int index = 1;
		while (index < boxes.size())
		{
			float iou_value = calculateIOU(boxes[0], boxes[index]);
			if (iou_value > threshold)
				boxes.erase(boxes.begin() + index);
			else
				index++;
		}
		boxes.erase(boxes.begin());
	}
	return result;

}

3.部署效果

        下图为yolov7的tensorRT部署效果。

        在已有的项目中,使用TensorRT部署的yolov7,较部署前平均快了3-4倍,可见TensorRT的效率。     

<think>好的,我现在需要回答用户关于TensorRT的查询。用户提到了NVIDIA的官方文档、教程和示例,还希望了解相关信息。首先,我得回忆一下TensorRT的基本概念和主要功能。TensorRT是NVIDIA推出的深度学习推理优化器,能提高模型在GPU上的推理速度和效率。它支持模型量化、层融合等技术,这些都是关键点。 接下来,我应该整理用户提供的引用内容。引用1到5提到了TensorRT的应用场景、优化方法、部署步骤以及在不同设备上的注意事项。比如,引用2提到将PyTorch模型转为ONNX再导入TensorRT,引用4强调其在计算需求高的应用中的价值,引用5讨论了设备兼容性和资源管理的问题。 用户可能需要一个结构清晰的回答,涵盖TensorRT的核心功能、如何入门、优化策略、应用场景以及资源推荐。根据示例中的格式,需要分点说明,每个部分引用对应的文献。例如,核心功能部分可以引用1和4,入门教程引用2和3,优化策略引用5,应用场景引用4,资源推荐引用1、2、3。 同时,按照系统指令,数学公式要用$...$,独立公式用$$...$$。不过当前内容可能较少涉及数学,但如果有相关内容,比如模型量化的数学原理,可能需要用LaTeX表达,例如$INT8$量化。另外,要生成相关问题,可能需要围绕TensorRT的优化技术、部署流程、适用场景等提问。 需要确保回答准确,引用正确。比如,提到模型转换流程时引用2,设备优化引用5,性能提升引用4。避免引用错误或信息冲突。此外,用户可能希望了解官方资源,所以需要提供文档链接或示例位置,但用户提供的引用中没有直接链接,可能需要根据现有引用内容指导用户查阅官方文档和示例项目。 最后,检查是否符合中文回答,LaTeX格式正确,问题相关且合理。确保没有使用任何markdown,行内公式正确,没有遗漏关键点,如TensorRT的8位推理(引用1和4),优化策略中的层融合和内核选择(引用5),以及部署步骤中的JetPack和TensorRT安装(引用3)。</think>### TensorRT深度学习推理引擎详解 TensorRT是NVIDIA推出的高性能深度学习推理优化器,专为GPU加速设计。它通过模型优化和高效内核生成,显著提升推理速度并降低延迟。以下是关键信息整理: --- #### 1. **核心功能** - **模型优化**:支持网络层融合(如卷积激活函数合并)、精度校准(FP16/INT8量化)[^4],减少计算冗余。 - **跨框架兼容**:可将PyTorch、TensorFlow等框架的模型转换为ONNX格式,再导入TensorRT生成优化引擎[^2]。 - **动态资源管理**:根据目标设备(如不同GPU型号)自动选择高效内核,平衡计算资源性能[^5]。 --- #### 2. **快速入门步骤** 1. **环境配置**:安装JetPack(含TensorRT库)并验证版本兼容性[^3]。 2. **模型转换**:将训练好的模型导出为ONNX格式,例如PyTorch模型: ```python torch.onnx.export(model, input_sample, "model.onnx") ``` 3. **引擎构建**:使用TensorRTC++或Python API解析ONNX模型,应用优化策略生成推理引擎。 4. **部署推理**:加载引擎并执行推理任务,支持批处理异步计算。 --- #### 3. **关键优化技术** - **量化压缩**:将FP32模型压缩为INT8,减少内存占用,公式表示为: $$ x_{\text{int8}} = \text{round}\left( \frac{x_{\text{FP32}}}{s} \right) $$ 其中$s$为缩放因子[^4]。 - **显存优化**:复用中间张量内存,减少显存碎片。 - **动态形状支持**:适配可变输入尺寸,适用于实时视频流处理[^1]。 --- #### 4. **典型应用场景** - **高吞吐场景**:如数据中心的大规模图像分类。 - **低延迟场景**:自动驾驶中的实时目标检测。 - **边缘计算**:Jetson设备上的嵌入式AI应用[^3]。 --- #### 5. **官方资源推荐** - **文档教程**:NVIDIA官方文档涵盖从API详解到最佳实践。 - **示例项目**:GitHub仓库提供C++/Python代码示例(如ONNX解析、INT8校准)。 - **工具链**:使用`trtexec`命令行工具快速测试模型性能[^2]。 ---
评论 14
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值