翼度科技»论坛 编程开发 .net 查看内容

TensorRT C# API 项目更新 (1):支持动态Bath输入模型推理

4

主题

4

帖子

12

积分

新手上路

Rank: 1

积分
12
1. 项目介绍

     NVIDIA® TensorRT™ 是一款用于高性能深度学习推理的 SDK,包括深度学习推理优化器和运行时,可为推理应用程序提供低延迟和高吞吐量。基于 NVIDIA TensorRT 的应用程序在推理过程中的执行速度比纯 CPU 平台快 36 倍,使您能够优化在所有主要框架上训练的神经网络模型,以高精度校准低精度,并部署到超大规模数据中心、嵌入式平台或汽车产品平台。
     TensorRT 基于 NVIDIA CUDA® 并行编程模型构建,使您能够在 NVIDIA GPU 上使用量化、层和张量融合、内核调整等技术来优化推理。TensorRT 提供 INT8 使用量化感知训练和训练后量化和浮点 16 (FP16) 优化,用于部署深度学习推理应用程序,例如视频流、推荐、欺诈检测和自然语言处理。低精度推理可显著降低延迟,这是许多实时服务以及自主和嵌入式应用所必需的。TensorRT 与 PyTorch 和 TensorFlow 集成,因此只需一行代码即可实现 6 倍的推理速度。TensorRT 提供了一个 ONNX 解析器,因此您可以轻松地将 ONNX 模型从常用框架导入 TensorRT。它还与 ONNX 运行时集成,提供了一种以 ONNX 格式实现高性能推理的简单方法。
     基于这些优势,TensorRT目前在深度模型部署应用越来越广泛。但是TensorRT目前只提供了C++与Python接口,对于跨语言使用十分不便。目前C#语言已经成为当前编程语言排行榜上前五的语言,也被广泛应用工业软件开发中。为了能够实现在C#中调用TensorRT部署深度学习模型,我们在之前的开发中开发了TensorRT C# API。虽然实现了该接口,但由于数据传输存在问题,当时开发的版本在应用时存在较大的问题。
     基于此,我们开发了TensorRT C# API 2.0版本,该版本在开发时充分考虑了上一版本应用时出现的问题,并进行了改进。为了更加方便开发者使用,在本次更新中增加了对动态输入模型的支持,将在本技术文中详细介绍本次更新内容以及应用案例。

  • TensorRT C# API 项目源码:
  1. https://github.com/guojin-yan/TensorRT-CSharp-API.git
复制代码

  • TensorRT C# API 项目应用源码:
  1. https://github.com/guojin-yan/TensorRT-CSharp-API-Samples.git
复制代码
2. 更新回顾

     由于该项目目前还没有完全开发完成,为了更好的方便大家使用,因此会在最新更新后提供给大家最新的资讯。如果大家在使用时有任何疑问,可以阅读之前发布的技术博客:
3. 动态输入模型支持

     在上一版本中,支持了多Bath推理,单其实现方式是导出的推理模型是多Bath的,因此模型推理的Bath是不可更改的。但是目前TensorRT已经支持了动态模型输入,所以更新了对动态输入模型的支持。下面将对更新的API接口以及推理流程进行简单的介绍:
3.1 新增API


  • public static void OnnxToEngine(string modelPath, int memorySize, string nodeName, Dims minShapes, Dims optShapes, Dims maxShapes)

    • 模型转换接口:可以调用封装的TensorRT中的ONNX 解释器,对ONNX模型进行转换,并根据本机设备信息,编译本地模型,将模型转换为TensorRT 支持的engine格式,该接口支持动态输入模型。
    • string modelPath: 本地ONNX模型地址,只支持ONNX格式,且ONNX模型必须为确定的输入输出,暂不支持动态输入。
    • int memorySize: 模型转换时分配的内存大小。
    • string nodeName: 模型输入节点名称,该节点维度确定但是形状是动态的,一般为: [-1, 3 640, 640],某一维度或其中几个维度大小为“-1”。
    • Dims minShapes: 动态尺寸的最小允许值
    • Dims optShapes: 优化(内核选择)中使用的值、动态尺寸的最优值
    • Dims maxShapes: 动态尺寸等的最大允许值

  • public Nvinfer(string modelPath, int maxBatahSize)

    • Nvinfer 初始化接口: 初始化Nvinfer类,主要初始化封装的推理引擎,该推理引擎中封装了比较重要的一些类和指针。
    • string modelPath: engine模型路径。
    • int maxBatahSize:  推理推理支持的最大的Bath。

  • public void SetBindingDimensions(int index, Dims dims)/SetBindingDimensions(string nodeName, Dims dims)

    • 设置节点维度接口: 通过端口编号或者端口名称,获取绑定的端口的形状信息。
    • int index: 绑定端口的编号。
    • string nodeName: 绑定端口的名称。
    • Dims dims: 需要设置绑定端口的维度。

3.2 推理流程

     对于固定输入模型的推理流程,主要包括以下四个步骤:

  • Nvinfer初始化
  • 加载推理数据
  • 模型推理
  • 获取推理结果
     而当我们使用动态输入模型时,其推理流程发生了变化,如下图所示:
     当部署动态输入模型时,推理流程为:

  • Nvinfer初始化
  • 设置本次推理模型输入大小
  • 加载推理数据
  • 模型推理
  • 获取推理结果
     与常规的规定输入模型的推理流程相比,主要是增加了设置本次推理模型输入大小这一步,其他步骤并未发生较大的变化。此外,如果下一次推理输入数据形状大小发生了改变,就需要重新进行设置,如果输入形状大小并未对发生变化,则无需进行再次设置。
4. 接口应用

     关于该项目的调用方式在上一篇文章中已经进行了详细介绍,具体使用可以参考《最新发布!TensorRT C# API :基于C#与TensorRT部署深度学习模型》,下面结合Yolov8-cls模型详细介绍一下更新的接口使用方法。
4.1 创建并配置C#项目

     首先创建一个简单的C#项目,然后添加项目配置。
     首先是添加TensorRT C# API 项目引用,如下图所示,添加上文中C#项目生成的dll文件即可。
     接下来添加OpenCvSharp,此处通过NuGet Package安装即可,此处主要安装以下两个程序包即可:
     配置好项目后,项目的配置文件如下所示:
  1. <Project Sdk="Microsoft.NET.Sdk">
  2.   <PropertyGroup>
  3.     <OutputType>Exe</OutputType>
  4.     <TargetFramework>net6.0</TargetFramework>
  5.     <RootNamespace>TensorRT_CSharp_API_demo</RootNamespace>
  6.     <ImplicitUsings>enable</ImplicitUsings>
  7.     <Nullable>enable</Nullable>
  8.   </PropertyGroup>
  9.   <ItemGroup>
  10.     <PackageReference Include="OpenCvSharp4.Extensions" Version="4.9.0.20240103" />
  11.     <PackageReference Include="OpenCvSharp4.Windows" Version="4.9.0.20240103" />
  12.   </ItemGroup>
  13.   <ItemGroup>
  14.     <Reference Include="TensorRtSharp">
  15.       <HintPath>E:\GitSpace\TensorRT-CSharp-API\src\TensorRtSharp\bin\Release\net6.0\TensorRtSharp.dll</HintPath>
  16.     </Reference>
  17.   </ItemGroup>
  18. </Project>
复制代码
4.2 添加推理代码

     此处演示一个简单的图像分类项目,以Yolov8-cls项目为例:
(1) 转换engine模型

     动态输入的模型在进行格式转换时,需要指定模型推理形状至此的范围,minShapes表示模型推理支持的最小形状,optShapes表示模型推理支持的最佳形状,maxShapes表示模型推理支持的最大形状,模型转换需要消耗较多时间,最终转换成功后会在模型同级目录下生成相同名字的.engine文件。
  1. Dims minShapes = new Dims(1, 3, 224, 224);
  2. Dims optShapes = new Dims(10, 3, 224, 224);
  3. Dims maxShapes = new Dims(20, 3, 224, 224);
  4. Nvinfer.OnnxToEngine(onnxPath, 20, "images", minShapes, optShapes, maxShapes);
复制代码
(2) 定义模型预测方法

     下面代码是定义的Yolov8-cls模型的预测方法,该方法支持动态Bath输入模型推理,可以根据用户输入图片数量,自动设置输入Bath,然后进行推理。
     下面代码与上一篇文章中的代码差异主要是增加了predictor.SetBindingDimensions("images", new Dims(batchNum, 3, 224, 224));这一句代码。同时在初始化时,设置最大支持20Bath,这与上文模型转换时设置的一致。
  1. public class Yolov8Cls
  2. {
  3.     public Dims InputDims;
  4.     public int BatchNum;
  5.     private Nvinfer predictor;
  6.     public Yolov8Cls(string enginePath)
  7.     {
  8.         predictor = new Nvinfer(enginePath, 20);
  9.         InputDims = predictor.GetBindingDimensions("images");
  10.     }
  11.     public void Predict(List<Mat> images)
  12.     {
  13.         BatchNum = images.Count;
  14.         for (int begImgNo = 0; begImgNo < images.Count; begImgNo += BatchNum)
  15.         {
  16.             DateTime start = DateTime.Now;
  17.             int endImgNo = Math.Min(images.Count, begImgNo + BatchNum);
  18.             int batchNum = endImgNo - begImgNo;
  19.             List<Mat> normImgBatch = new List<Mat>();
  20.             int imageLen = 3 * 224 * 224;
  21.             float[] inputData = new float[BatchNum * imageLen];
  22.             for (int ino = begImgNo; ino < endImgNo; ino++)
  23.             {
  24.                 Mat input_mat = CvDnn.BlobFromImage(images[ino], 1.0 / 255.0, new OpenCvSharp.Size(224, 224), 0, true, false);
  25.                 float[] data = new float[imageLen];
  26.                 Marshal.Copy(input_mat.Ptr(0), data, 0, imageLen);
  27.                 Array.Copy(data, 0, inputData, ino * imageLen, imageLen);
  28.             }
  29.             predictor.SetBindingDimensions("images", new Dims(batchNum, 3, 224, 224));
  30.             predictor.LoadInferenceData("images", inputData);
  31.             DateTime end = DateTime.Now;
  32.             Console.WriteLine("[ INFO ] Input image data processing time: " + (end - start).TotalMilliseconds + " ms.");
  33.             predictor.infer();
  34.             start = DateTime.Now;
  35.             predictor.infer();
  36.             end = DateTime.Now;
  37.             Console.WriteLine("[ INFO ] Model inference time: " + (end - start).TotalMilliseconds + " ms.");
  38.             start = DateTime.Now;
  39.             float[] outputData = predictor.GetInferenceResult("output0");
  40.             for (int i = 0; i < batchNum; ++i)
  41.             {
  42.                 Console.WriteLine(string.Format("[ INFO ] Classification Top {0} result : ", 2));
  43.                 float[] data = new float[1000];
  44.                 Array.Copy(outputData, i * 1000, data, 0, 1000);
  45.                 List<int> sortResult = Argsort(new List<float>(data));
  46.                 for (int j = 0; j < 2; ++j)
  47.                 {
  48.                     string msg = "";
  49.                     msg += ("index: " + sortResult[j] + "\t");
  50.                     msg += ("score: " + data[sortResult[j]] + "\t");
  51.                     Console.WriteLine("[ INFO ] " + msg);
  52.                 }
  53.             }
  54.             end = DateTime.Now;
  55.             Console.WriteLine("[ INFO ] Inference result processing time: " + (end - start).TotalMilliseconds + " ms.\n");
  56.         }
  57.     }
  58.     public static List<int> Argsort(List<float> array)
  59.     {
  60.         int arrayLen = array.Count;
  61.         List<float[]> newArray = new List<float[]> { };
  62.         for (int i = 0; i < arrayLen; i++)
  63.         {
  64.             newArray.Add(new float[] { array[i], i });
  65.         }
  66.         newArray.Sort((a, b) => b[0].CompareTo(a[0]));
  67.         List<int> arrayIndex = new List<int>();
  68.         foreach (float[] item in newArray)
  69.         {
  70.             arrayIndex.Add((int)item[1]);
  71.         }
  72.         return arrayIndex;
  73.     }
  74. }
复制代码
(3) 预测方法调用

     下面是上述定义的预测方法,为了测试不同Bath性能,此处读取了多张图片,并分别预测不同张数图片,如下所示:
  1. Yolov8Cls yolov8Cls = new Yolov8Cls("E:\\Model\\yolov8\\yolov8s-cls_b.engine");
  2. Mat image1 = Cv2.ImRead("E:\\ModelData\\image\\demo_4.jpg");
  3. Mat image2 = Cv2.ImRead("E:\\ModelData\\image\\demo_5.jpg");
  4. Mat image3 = Cv2.ImRead("E:\\ModelData\\image\\demo_6.jpg");
  5. Mat image4 = Cv2.ImRead("E:\\ModelData\\image\\demo_7.jpg");
  6. Mat image5 = Cv2.ImRead("E:\\ModelData\\image\\demo_8.jpg");
  7. yolov8Cls.Predict(new List<Mat> { image1, image2 });
  8. yolov8Cls.Predict(new List<Mat> { image1, image2, image3 });
  9. yolov8Cls.Predict(new List<Mat> { image1, image2, image3, image4 });
  10. yolov8Cls.Predict(new List<Mat> { image1, image2, image3, image4, image5 });
复制代码
4.3 项目演示

     配置好项目并编写好代码后,运行该项目,项目输出如下所示:
  1. [ INFO ] Input image data processing time: 5.5277 ms.
  2. [ INFO ] Model inference time: 1.3685 ms.
  3. [ INFO ] Classification Top 2 result :
  4. [ INFO ] index: 386     score: 0.8754883
  5. [ INFO ] index: 385     score: 0.08013916
  6. [ INFO ] Classification Top 2 result :
  7. [ INFO ] index: 293     score: 0.89160156
  8. [ INFO ] index: 276     score: 0.05480957
  9. [ INFO ] Inference result processing time: 3.0823 ms.
  10. [ INFO ] Input image data processing time: 2.7356 ms.
  11. [ INFO ] Model inference time: 1.4435 ms.
  12. [ INFO ] Classification Top 2 result :
  13. [ INFO ] index: 386     score: 0.8754883
  14. [ INFO ] index: 385     score: 0.08013916
  15. [ INFO ] Classification Top 2 result :
  16. [ INFO ] index: 293     score: 0.89160156
  17. [ INFO ] index: 276     score: 0.05480957
  18. [ INFO ] Classification Top 2 result :
  19. [ INFO ] index: 14      score: 0.99853516
  20. [ INFO ] index: 88      score: 0.0006980896
  21. [ INFO ] Inference result processing time: 1.5137 ms.
  22. [ INFO ] Input image data processing time: 3.7277 ms.
  23. [ INFO ] Model inference time: 1.5285 ms.
  24. [ INFO ] Classification Top 2 result :
  25. [ INFO ] index: 386     score: 0.8754883
  26. [ INFO ] index: 385     score: 0.08013916
  27. [ INFO ] Classification Top 2 result :
  28. [ INFO ] index: 293     score: 0.89160156
  29. [ INFO ] index: 276     score: 0.05480957
  30. [ INFO ] Classification Top 2 result :
  31. [ INFO ] index: 14      score: 0.99853516
  32. [ INFO ] index: 88      score: 0.0006980896
  33. [ INFO ] Classification Top 2 result :
  34. [ INFO ] index: 294     score: 0.96533203
  35. [ INFO ] index: 269     score: 0.0124435425
  36. [ INFO ] Inference result processing time: 2.7328 ms.
  37. [ INFO ] Input image data processing time: 4.063 ms.
  38. [ INFO ] Model inference time: 1.6947 ms.
  39. [ INFO ] Classification Top 2 result :
  40. [ INFO ] index: 386     score: 0.8754883
  41. [ INFO ] index: 385     score: 0.08013916
  42. [ INFO ] Classification Top 2 result :
  43. [ INFO ] index: 293     score: 0.89160156
  44. [ INFO ] index: 276     score: 0.05480957
  45. [ INFO ] Classification Top 2 result :
  46. [ INFO ] index: 14      score: 0.99853516
  47. [ INFO ] index: 88      score: 0.0006980896
  48. [ INFO ] Classification Top 2 result :
  49. [ INFO ] index: 294     score: 0.96533203
  50. [ INFO ] index: 269     score: 0.0124435425
  51. [ INFO ] Classification Top 2 result :
  52. [ INFO ] index: 127     score: 0.9008789
  53. [ INFO ] index: 128     score: 0.07745361
  54. [ INFO ] Inference result processing time: 3.5664 ms.
复制代码
     通过上面输出可以看出,不同Bath模型推理时间在1.3685~1.6947ms,大大提升了模型的推理速度。
5. 总结

     在本项目中,我们扩展了TensorRT C# API 接口,使其支持动态输入模型。并结合分类模型部署流程向大家展示了TensorRT C# API 的使用方式,方便大家快速上手使用。
     为了方便各位开发者使用,此处开发了配套的演示项目,主要是基于Yolov8开发的目标检测、目标分割、人体关键点识别、图像分类以及旋转目标识别,并且支持动态输入模型,用户可以同时推理任意张图像。

  • Yolov8 Det 目标检测项目源码:
  1. https://github.com/guojin-yan/TensorRT-CSharp-API-Samples/blob/master/model_samples/yolov8_custom_dynamic/Yolov8Det.cs
复制代码

  • Yolov8  Seg 目标分割项目源码:
  1. https://github.com/guojin-yan/TensorRT-CSharp-API-Samples/blob/master/model_samples/yolov8_custom_dynamic/Yolov8Seg.cs
复制代码

  • Yolov8  Pose 人体关键点识别项目源码:
  1. https://github.com/guojin-yan/TensorRT-CSharp-API-Samples/blob/master/model_samples/yolov8_custom_dynamic/Yolov8Pose.cs
复制代码

  • Yolov8  Cls 图像分类项目源码:
  1. https://github.com/guojin-yan/TensorRT-CSharp-API-Samples/blob/master/model_samples/yolov8_custom_dynamic/Yolov8Cls.cs
复制代码

  • Yolov8  Obb 旋转目标识别项目源码:
  1. https://github.com/guojin-yan/TensorRT-CSharp-API-Samples/blob/master/model_samples/yolov8_custom_dynamic/Yolov8Obb.cs
复制代码
     同时对本项目开发的案例进行了时间测试,以下时间只是程序运行一次的时间,测试环境为:

  • CPU:i7-165G7
  • CUDA型号:12.2
  • Cudnn:8.9.3
  • TensorRT:8.6.1.6
ModelBatch数据预处理 (ms)模型推理 (ms)结果后处理 (ms)Yolov8s-Det116.64.613.1438.012.432.4870.523.080.1Yolov8s-Obb128.78.917.7481.725.967.48148.444.6153.0Yolov8s-Seg115.45.467.4437.315.5220.6878.726.9433.6Yolov8s-Pose115.15.28.7439.213.214.3867.823.127.7Yolov8s-Cls19.91.31.9414.71.52.3822.62.02.9     最后如果各位开发者在使用中有任何问题,欢迎大家与我联系。

来源:https://www.cnblogs.com/guojin-blogs/p/18125829
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x

举报 回复 使用道具