潘多拉固件建设网站,在线教育类网站模板,中山网站改版,小程序搭建是什么意思文章目录 1、固定输入尺寸逻辑2、动态输入尺寸2.1、模型导出2.2、推理测试2.3、显存分配问题2.4、完整代码 这里主要说明使用TensorRT进行加载编译优化后的模型engine进行推理测试#xff0c;与前面进行目标识别、目标分类的模型的网络输入是固定大小不同#xff0c;导致输入… 文章目录 1、固定输入尺寸逻辑2、动态输入尺寸2.1、模型导出2.2、推理测试2.3、显存分配问题2.4、完整代码 这里主要说明使用TensorRT进行加载编译优化后的模型engine进行推理测试与前面进行目标识别、目标分类的模型的网络输入是固定大小不同导致输入维度不能直接获取需要自己手动调整的问题。 1、固定输入尺寸逻辑
基本逻辑如下
读取engine文件到内存使用TensorRT运行时IRuntime序列化一个引擎ICudaEngine在创建一个上下文对象IExecutionContext从引擎中ICudaEngine获取输入、输出的纬度和数据类型并分配显存将输入从host内存中拷贝到输入device显存中利用创建的上下文对象IExecutionContext执行推理将推理结果从输出device显存拷贝到host内存中
至于显存分配根据engine是可以获取网络输入输出的尺寸的。以前面 【TensorRt3mnist示例中的C API】 博客中的简要代码为例说推理代码路程
int simple2()
{/// 2.1 加载engine到内存// .... 省略std::vectorchar buf(buflen);// .... /// 2.2 反序列化std::unique_ptrIRuntime runtime{ createInferRuntime(sample::gLogger.getTRTLogger()) };auto mEngine std::shared_ptrnvinfer1::ICudaEngine(runtime-deserializeCudaEngine(buf.data(),buf.size()),[](nvinfer1::ICudaEngine* p) {delete p; });// inference上下文auto context std::unique_ptrnvinfer1::IExecutionContext(mEngine-createExecutionContext());// 网络输入、输出信息 auto mInputDims mEngine-getBindingDimensions(0); // 部署使用 [1,1,28,28]auto mOutputDims mEngine-getBindingDimensions(1); // 部署使用 [1,10]int inputH mInputDims.d[2];int inputW mInputDims.d[3];//---------- 整个网络输入只有1个输出只有1个且均为float类型分配cuda显存std::vectorvoid* bindings(mEngine-getNbBindings());for (int i 0; i bindings.size(); i) {nvinfer1::DataType type mEngine-getBindingDataType(i);// 明确为 floatsize_t volume sizeof(float) * std::accumulate(dims.d,dims.d dims.nbDims,1,std::multipliessize_t());CHECK(cudaMalloc(bindings[i],volume));}// 加载一个random imagesrand(unsigned(time(nullptr)));std::vectoruint8_t fileData(inputH * inputW);// 省略.... //---------- 输入host数据类型从uint8_t转换为float, 这里明确知道 1*1*28*28std::vectorfloat hostDataBuffer(1 * 1 * 28 * 28); for (int i 0; i inputH * inputW; i) {hostDataBuffer[i] 1.0 - float(fileData[i] / 255.0);}//---------- 将图像数据从host空间拷贝到device空间CHECK(cudaMemcpy(bindings[0],static_castconst void*(hostDataBuffer.data()),hostDataBuffer.size() * sizeof(float), cudaMemcpyHostToDevice));//---------- excution执行推理bool status context-executeV2(bindings.data());/// 2.3 处理推理结果数据//---------- 将推理结果从device空间拷贝到host空间std::vectorfloat pred(1 * 10); // 这里明确知道 1*10)CHECK(cudaMemcpy(static_castvoid*(pred.data()),bindings[1], pred.size() * sizeof(float),cudaMemcpyDeviceToHost));// .... 省略
}2、动态输入尺寸
以paddle中的语义语义分割模型 OCRNet backbone HRNet_w18 为例进行说明测试。
2.1、模型导出
默认训练模型导出推理模型是不带softmax和argmax层的为避免后续实现效率降低添加这两层之再导出推理模型使用paddle2onnx导出onnx模型
paddle2onnx --model_dir saved_inference_model \--model_filename model.pdmodel \--params_filename model.pdiparams \--save_file model.onnx \--enable_dev_version True # --opset_version 12 # default 9使用netron工具查看输入和输出的尺寸信息截图如下 输入尺寸为 [n, 3, h, w]原始输出为[n,c,h,w]由于使用soft和argmax输出直接为[1, h, w]。输出和输入图像宽高一致输出的每个像素位置就是像素分类的类别数。
之后使用 trtexec 将onnx模型编译优化导出为engine类型由于是动态输入因此指定了输入尺寸范围和最优尺寸。
trtexec.exe --onnxmodel.onnx --explicitBatch --fp16 --minShapesx:1x3x540x960 --optShapesx:1x3x720x1280 --maxShapesx:1x3x1080x1920 --saveEnginemodel.trt2.2、推理测试
0基本类型数据准备 我们仅使用#include NvInfer.h 来使用TensorRT sdk定义几个需要的宏和对象
#define CHECK(status) \do \{ \auto ret (status); \if (ret ! 0) \{ \std::cerr Cuda failure: ret std::endl; \abort(); \} \} while (0)class Logger : public nvinfer1::ILogger
{
public:Logger(Severity severity Severity::kWARNING) : severity_(severity) {}virtual void log(Severity severity, const char* msg) noexcept override{// suppress info-level messagesif(severity severity_)std::cout msg std::endl;}nvinfer1::ILogger getTRTLogger() noexcept{return *this;}
private:Severity severity_;
};struct InferDeleter
{template typename Tvoid operator()(T* obj) const{delete obj;}
};template typename T
using SampleUniquePtr std::unique_ptrT, InferDeleter;1加载模型
Logger logger(nvinfer1::ILogger::Severity::kVERBOSE);/*trtexec.exe --onnxinference_model\model.onnx --explicitBatch --fp16 --minShapesx:1x3x540x960 --optShapesx:1x3x720x1280 --maxShapesx:1x3x1080x1920 --saveEnginemodel.trt*/std::string trtFile R(C:\Users\wanggao\Desktop\123\inference_model_0\model.trt);//std::string trtFile model.test.trt;std::ifstream ifs(trtFile, std::ifstream::binary);if(!ifs) {return false;}ifs.seekg(0, std::ios_base::end);int size ifs.tellg();ifs.seekg(0, std::ios_base::beg);std::unique_ptrchar pData(new char[size]);ifs.read(pData.get(), size);ifs.close();// engine模型//SampleUniquePtrnvinfer1::IRuntime runtime{nvinfer1::createInferRuntime(logger.getTRTLogger())};//auto mEngine std::shared_ptrnvinfer1::ICudaEngine(// runtime-deserializeCudaEngine(pData.get(), size), InferDeleter());//auto context SampleUniquePtrnvinfer1::IExecutionContext(mEngine-createExecutionContext());std::shared_ptrnvinfer1::ICudaEngine mEngine;{SampleUniquePtrnvinfer1::IRuntime runtime{nvinfer1::createInferRuntime(logger.getTRTLogger())};mEngine std::shared_ptrnvinfer1::ICudaEngine(runtime-deserializeCudaEngine(pData.get(), size), InferDeleter());}auto context SampleUniquePtrnvinfer1::IExecutionContext(mEngine-createExecutionContext());2输入 将RGB三通道图像转换为NCHW格式数据数据类型为float。 // 输入 前处理//cv::Mat img cv::imread(dog.jpg);cv::Mat img cv::imread(R(C:\Users\wanggao\Desktop\123\data\test\t.jpg));cv::Mat blob cv::dnn::blobFromImage(img, 1 / 255., img.size(), {0,0,0}, true, false);//blob blob * 2 - 1; // 测试使用可以不归一化3显存分配 不同于固定输入通过engine获取尺寸并分配显存大小 // 固定输入尺寸的显存分配方式std::vectorvoid* bindings(mEngine-getNbBindings());for (int i 0; i bindings.size(); i) {nvinfer1::DataType type mEngine-getBindingDataType(i);nvinfer1::Dims dims mEngine-getBindingDimensions(i);//size_t volume // sizeof(float) * std::accumulate(dims.d,dims.d dims.nbDims,1,std::multipliessize_t());size_t volume std::accumulate(dims.d,dims.d dims.nbDims,1,std::multipliessize_t());switch (type){case nvinfer1::DataType::kINT32:case nvinfer1::DataType::kFLOAT: volume * 4; break; // 明确为类型 floatcase nvinfer1::DataType::kHALF: volume * 2; break;case nvinfer1::DataType::kBOOL:case nvinfer1::DataType::kINT8:default:break;}CHECK(cudaMalloc(bindings[i],volume));}这里通过模型获取的输入类型为float尺寸为[-1,3,1,1]、输出类型为int32尺寸为[-1,-1,-1]即获取不到尺寸信息。所以只能根据输入的尺寸来分配显存大小。(文后说明在实际推理中应该如何分配) // 设置网络的输入尺寸context-setBindingDimensions(0, nvinfer1::Dims4{1, 3 , img.rows, img.cols});// 分配显存std::vectorvoid* bindings(mEngine-getNbBindings()); //auto type1 mEngine-getBindingDataType(0); // kFLOAT float//auto type2 mEngine-getBindingDataType(1); // kINT32 intCHECK(cudaMalloc(bindings[0], sizeof(float) * 1 * 3 * img.rows * img.cols*3)); // n*3*h*wCHECK(cudaMalloc(bindings[1], sizeof(int) * 1 * 1 * img.rows * img.cols*3)); // n*1*h*w注意必须通过context-setBindingDimensions()设置网络的输入尺寸否则网络在推理时报错即输入维度未指定导出网络输出无结果。 3: [executionContext.cpp::nvinfer1::rt::ShapeMachineContext::resolveSlots::1541] Error Code 3: API Usage Error (Parameter check failed at: executionContext.cpp::nvinfer1::rt::ShapeMachineContext::resolveSlots::1541, condition: allInputDimensionsSpecified(routine) ) 9: [executionContext.cpp::nvinfer1::rt::ExecutionContext::executeInternal::564] Error Code 9: Internal Error (Could not resolve slots: ) 4推理 将前处理后的图片数据拷贝到显存中之后进行推理之后将推理结果数据从显存拷贝到内存中 cv::Mat pred(img.size(), CV_32SC1, {255,255,255}); // 用于输出//cv::reduceArgMax() //opencv 4.8.0//buffers.copyInputToDevice();CHECK(cudaMemcpy(bindings[0], static_castconst void*(blob.data), 3 * img.rows * img.cols * sizeof(float), cudaMemcpyHostToDevice));context-executeV2(bindings.data());// buffers.copyOutputToHost()CHECK(cudaMemcpy(static_castvoid*(pred.data), bindings[1], pred.total() * sizeof(int), cudaMemcpyDeviceToHost));5结果数据展示后处理 这里仅显示运行截图如下
2.3、显存分配问题
在示例中我们根据图片大小来分配显存实际应用将进行多次推理有多种方案
1、根据实际输入大小每次进行动态分配使用完后释放原已分配显存2、在1基础上如果显存不够再分配分配前释放原已分配显存3、预分配一块较大的显存程序退出时释放显存
为提高效率我们可以选择第三种已知我们动态输入最大尺寸为 --maxShapesx:1x3x1080x1920因此我们直接根据网络输入输出类型分配显存 std::vectorvoid* bindings(mEngine-getNbBindings()); // 1个输入1个输出CHECK(cudaMalloc(bindings[0], sizeof(float) * 1 * 3 * 1280 * 1920)); // n*3*h*wCHECK(cudaMalloc(bindings[1], sizeof(int) * 1 * 1 * * 1280 * 1920)); // n*1*h*w当输入尺寸超过我们设置的 maxShapes 时context-setBindingDimensions()将报异常提示这一种情况就不应该继续执行分配显存属于输出错误。
2.4、完整代码
#include opencv2\opencv.hpp#include NvInfer.h
#include cuda_runtime_api.h
#include random//using namespace nvinfer1;
//using samplesCommon::SampleUniquePtr;#include fstream
#include string#define CHECK(status) \do \{ \auto ret (status); \if (ret ! 0) \{ \std::cerr Cuda failure: ret std::endl; \abort(); \} \} while (0)class Logger : public nvinfer1::ILogger
{
public:Logger(Severity severity Severity::kWARNING) : severity_(severity) {}virtual void log(Severity severity, const char* msg) noexcept override {// suppress info-level messagesif(severity severity_)std::cout msg std::endl;}nvinfer1::ILogger getTRTLogger() noexcept{return *this;}
private:Severity severity_;
};struct InferDeleter
{template typename Tvoid operator()(T* obj) const{delete obj;}
};template typename T
using SampleUniquePtr std::unique_ptrT, InferDeleter;int inference();int main(int argc, char** argv)
{return inference();
}int inference()
{Logger logger(nvinfer1::ILogger::Severity::kVERBOSE);/*trtexec.exe --onnxinference_model\model.onnx --explicitBatch --fp16 --minShapesx:1x3x540x960 --optShapesx:1x3x720x1280 --maxShapesx:1x3x1080x1920 --saveEnginemodel.trt*/std::string trtFile R(C:\Users\wanggao\Desktop\123\inference_model_0\model.trt);//std::string trtFile model.test.trt;std::ifstream ifs(trtFile, std::ifstream::binary);if(!ifs) {return false;}ifs.seekg(0, std::ios_base::end);int size ifs.tellg();ifs.seekg(0, std::ios_base::beg);std::unique_ptrchar pData(new char[size]);ifs.read(pData.get(), size);ifs.close();// engine模型//SampleUniquePtrnvinfer1::IRuntime runtime{nvinfer1::createInferRuntime(logger.getTRTLogger())};//auto mEngine std::shared_ptrnvinfer1::ICudaEngine(// runtime-deserializeCudaEngine(pData.get(), size), InferDeleter());//auto context SampleUniquePtrnvinfer1::IExecutionContext(mEngine-createExecutionContext());std::shared_ptrnvinfer1::ICudaEngine mEngine;{SampleUniquePtrnvinfer1::IRuntime runtime{nvinfer1::createInferRuntime(logger.getTRTLogger())};mEngine std::shared_ptrnvinfer1::ICudaEngine(runtime-deserializeCudaEngine(pData.get(), size), InferDeleter());}auto context SampleUniquePtrnvinfer1::IExecutionContext(mEngine-createExecutionContext());// 输入//cv::Mat img cv::imread(dog.jpg);cv::Mat img cv::imread(R(C:\Users\wanggao\Desktop\123\data\test\t.jpg));cv::Mat blob cv::dnn::blobFromImage(img, 1 / 255., img.size(), {0,0,0}, true, false);//blob blob * 2 - 1;cv::Mat pred(img.size(), CV_32SC1, {255,255,255});//cv::reduceArgMax() //4.8.0context-setBindingDimensions(0, nvinfer1::Dims4{1, 3 , img.rows, img.cols});// 分配显存std::vectorvoid* bindings(mEngine-getNbBindings()); //auto type1 mEngine-getBindingDataType(0); // kFLOAT float//auto type2 mEngine-getBindingDataType(1); // kINT32 intCHECK(cudaMalloc(bindings[0], sizeof(float) * 1 * 3 * img.rows * img.cols*3)); // n*3*h*wCHECK(cudaMalloc(bindings[1], sizeof(int) * 1 * 1 * img.rows * img.cols*3)); // n*1*h*w// 推理// warmingup ...CHECK(cudaMemcpy(bindings[0], static_castconst void*(blob.data), 1 * 3 * 640 * 640 * sizeof(float), cudaMemcpyHostToDevice));context-executeV2(bindings.data());context-executeV2(bindings.data());context-executeV2(bindings.data());context-executeV2(bindings.data());CHECK(cudaMemcpy(static_castvoid*(pred.data), bindings[1], 1 * 84 * 8400 * sizeof(int), cudaMemcpyDeviceToHost));auto t1 cv::getTickCount();//buffers.copyInputToDevice();CHECK(cudaMemcpy(bindings[0], static_castconst void*(blob.data), 3 * img.rows * img.cols * sizeof(float), cudaMemcpyHostToDevice));context-executeV2(bindings.data());// buffers.copyOutputToHost()CHECK(cudaMemcpy(static_castvoid*(pred.data), bindings[1], pred.total() * sizeof(int), cudaMemcpyDeviceToHost));auto t2 cv::getTickCount();std::cout (t2-t1) / cv::getTickFrequency() std::endl;// 资源释放cudaFree(bindings[0]);cudaFree(bindings[1]);return 0;
}