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

TensorRT C# API 项目介绍:基于C#与TensorRT部署深度学习模型

8

主题

8

帖子

24

积分

新手上路

Rank: 1

积分
24
    [color=34,63,93]         TensorRT C# API 项目介绍:基于C#与TensorRT部署深度学习模型    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. 接口介绍

     下面简单介绍一下该项目封装的接口:

  • class Nvinfer
  • 模型推理类: 该类主要是封装了转换后的接口,用户可以直接调用该类进行初始化推理引擎。
  • **public static void OnnxToEngine(string modelPath, int memorySize) **

    • 模型转换接口:可以调用封装的TensorRT中的ONNX 解释器,对ONNX模型进行转换,并根据本机设备信息,编译本地模型,将模型转换为TensorRT 支持的engine格式。
    • string modelPath: 本地ONNX模型地址,只支持ONNX格式,且ONNX模型必须为确定的输入输出,暂不支持动态输入。
    • int memorySize: 模型转换时分配的内存大小

  • **public Nvinfer(string modelPath) **

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

  • **public Dims GetBindingDimensions(int index)/GetBindingDimensions(string nodeName) **

    • 获取节点维度接口: 通过端口编号或者端口名称,获取绑定的端口的形状信息.
    • int index: 绑定端口的编号
    • string nodeName: 绑定端口的名称
    • return Dims: 接口返回一个Dims结构体,该结构体包含了节点的维度大小以及每个维度的具体大小。

  • public void LoadInferenceData(string nodeName, float[] data)/LoadInferenceData(int nodeIndex, float[] data)

    • 加载待推理数据接口: 通过端口编号或者端口名称,将处理好的带推理数据加载到推理通道上。
    • string nodeName: 待加载推理数据端口的名称。
    • **int nodeIndex: **待加载推理数据端口的编号。
    • float[] data: 处理好的待推理数据,由于目前使用的推理数据多为float类型,因此此处目前只做了该类型接口。

  • public void infer()

    • 模型推理接口: 调用推理接口,对加载到推理通道的数据进行推理。

  • public float[] GetInferenceResult(string nodeName)/GetInferenceResult(int nodeIndex)

    • 获取推理结果: 通过端口编号或者端口名称,读取推理好的结果数据。
    • string nodeName: 推理结果数据端口的名称。
    • **int nodeIndex: **推理结果数据端口的编号。
    • return float[]: 返回值为指定节点的推理结果数据。

3. 安装流程

     下面演示一下安装方式,下文所有演示都是基于以下环境进行配置的:

  • 操作系统:Windows 11
  • 编译平台:Visual Studio 2022
  • 显卡型号:RTX 2060
  • CUDA型号:12.2
  • Cudnn:8.9.3
  • TensorRT:8.6.1.6
     对于CUDA以及Cudnn的安装此处不再作过多演示,大家可以自行安装。
3.1 TensorRT安装

     首先确定安装的CUDA版本,在命令提示符中输入nvcc -V指令,便可以获取安装的CUDA版本。
     接下来访问TensorRT Download | NVIDIA Developer下载安装包,此处安装8.x系列,安装最新的版本8.6,如下图所示,通过下载Zip文件进行安装,大家可以根据自己的CUDN版本进行下载。
     由于下载的是编译好的文件,因此在下载完成后,大家可以将其解压到常用的安装目录,然后将路径添加到环境变量即可,下面路径为本机安装的TensorRT路径,将其添加到本机的Path环境变量中。
  1. D:\Program Files\TensorRT\TensorRT-8.6.1.6\lib
复制代码
3.2 下载项目源码

     由于CUDA以及TensorRT程序集文件较大,无法打包成NuGet Package,因此需要用户自己进行编译。
     首先第一步下载项目源码,使用Git命令将源码下载到本地,如下图所示
  1. git clone https://github.com/guojin-yan/TensorRT-CSharp-API.git
复制代码
     然后使用Visual Studio 2022打开解决方案文件,如下图所示:
     该解决方案中包含两个项目,一个是C++项目,该项目是封装的TensorRT接口,将接口封装到动态链接库中;另一个是C#项目,该项目是读取动态链接库中的C++接口,然后重新封装该接口。
3.3 配置C++项目

     接下来配置C++项目,主要分为以下几步:
第一步:设置项目输出类型

     C++项目主要用于生成动态链接库文件,因此将配置类型改为动态库(.dll)。
第二部:设置包含目录

     当前C++项目主要使用两个依赖库,主要是CUDA(CUDNN)以及TensorRT,因此此处主要配置这两个依赖项,用户根据自己的安装情况进行配置即可。
     以下是本设备CUDA(CUDNN)以及TensorRT的包含目录位置,用户在使用时这需要根据自己本机安装情况进行设置即可。
  1. C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v12.2\include
  2. D:\Program Files\TensorRT\TensorRT-8.6.1.6\include
复制代码
第三步:设置库目录

     下面设置安装的CUDA(CUDNN)以及TensorRT的库目录情况,如下图所示。
     以下是本设备CUDA(CUDNN)以及TensorRT的库目录位置,用户在使用时这需要根据自己本机安装情况进行设置即可。
  1. C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v12.2\lib\x64
  2. D:\Program Files\TensorRT\TensorRT-8.6.1.6\lib
复制代码
第四步:设置附加依赖项

     下面设置附加依赖项,附加依赖项文件在上一步设置的库目录路径中,此处主要添加.lib文件。
     以下是CUDA(CUDNN)以及TensorRT的附加依赖项目录,但不同版本可能会不一致,因此用户在使用时需要根据自己的安装情况进行设置。
  1. cublas.lib
  2. cublasLt.lib
  3. cuda.lib
  4. cudadevrt.lib
  5. cudart.lib
  6. cudart_static.lib
  7. cudnn.lib
  8. cudnn64_8.lib
  9. cudnn_adv_infer.lib
  10. cudnn_adv_infer64_8.lib
  11. cudnn_adv_train.lib
  12. cudnn_adv_train64_8.lib
  13. cudnn_cnn_infer.lib
  14. cudnn_cnn_infer64_8.lib
  15. cudnn_cnn_train.lib
  16. cudnn_cnn_train64_8.lib
  17. cudnn_ops_infer.lib
  18. cudnn_ops_infer64_8.lib
  19. cudnn_ops_train.lib
  20. cudnn_ops_train64_8.lib
  21. cufft.lib
  22. cufftw.lib
  23. cufilt.lib
  24. curand.lib
  25. cusolver.lib
  26. cusolverMg.lib
  27. cusparse.lib
  28. nppc.lib
  29. nppial.lib
  30. nppicc.lib
  31. nppidei.lib
  32. nppif.lib
  33. nppig.lib
  34. nppim.lib
  35. nppist.lib
  36. nppisu.lib
  37. nppitc.lib
  38. npps.lib
  39. nvblas.lib
  40. nvJitLink.lib
  41. nvJitLink_static.lib
  42. nvjpeg.lib
  43. nvml.lib
  44. nvptxcompiler_static.lib
  45. nvrtc-builtins_static.lib
  46. nvrtc.lib
  47. nvrtc_static.lib
  48. OpenCL.lib
  49. nvinfer.lib
  50. nvinfer_dispatch.lib
  51. nvinfer_lean.lib
  52. nvinfer_plugin.lib
  53. nvinfer_vc_plugin.lib
  54. nvonnxparser.lib
  55. nvparsers.lib
复制代码
第五步:设置预处理器

     由于项目中应用了一些不安全方法,所以需要在宏定义中添加以下定义,如下图所示:
  1. _CRT_SECURE_NO_WARNINGS
复制代码
第六步:编译项目源码

     接下来生成C++项目,此处选择生成,不要选择运行,如下图所示:
     最终可以看出生成的动态链接库文件名称以及文件路径,这个路径在下一步中十分重要。
3.4 编译C#项目

     接下来编译C#项目,C#项目只需要修改一下位置即可,修改NativeMethods.cs文件中的dll文件路径,该路径及上一步中C++项目生成的动态链接库文件,如下图所示:
  1. E:\GitSpace\TensorRT-CSharp-API\x64\Release\TensorRtExtern.dll
复制代码
     接下来就可以运行C#项目,生成类库文件,如下图所示:
4. 接口调用

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. static void Main(string[] args)
  2. {
  3.     Nvinfer predictor = new Nvinfer("E:\\Model\\yolov8\\yolov8s-cls_2.engine");
  4.     Dims InputDims = predictor.GetBindingDimensions("images");
  5.     int BatchNum = InputDims.d[0];
  6.     Mat image1 = Cv2.ImRead("E:\\ModelData\\image\\demo_4.jpg");
  7.     Mat image2 = Cv2.ImRead("E:\\ModelData\\image\\demo_5.jpg");
  8.     List<Mat> images = new List<Mat>() { image1, image2 };
  9.     for (int begImgNo = 0; begImgNo < images.Count; begImgNo += BatchNum)
  10.     {
  11.         DateTime start = DateTime.Now;
  12.         int endImgNo = Math.Min(images.Count, begImgNo + BatchNum);
  13.         int batchNum = endImgNo - begImgNo;
  14.         List<Mat> normImgBatch = new List<Mat>();
  15.         int imageLen = 3 * 224 * 224;
  16.         float[] inputData = new float[2 * imageLen];
  17.         for (int ino = begImgNo; ino < endImgNo; ino++)
  18.         {
  19.             Mat input_mat = CvDnn.BlobFromImage(images[ino], 1.0 / 255.0, new OpenCvSharp.Size(224, 224), 0, true, false);
  20.             float[] data = new float[imageLen];
  21.             Marshal.Copy(input_mat.Ptr(0), data, 0, imageLen);
  22.             Array.Copy(data, 0, inputData, ino * imageLen, imageLen);
  23.         }
  24.         predictor.LoadInferenceData("images", inputData);
  25.         DateTime end = DateTime.Now;
  26.         Console.WriteLine("[ INFO ] Input image data processing time: " + (end - start).TotalMilliseconds + " ms.");
  27.         predictor.infer();
  28.         start = DateTime.Now;
  29.         predictor.infer();
  30.         end = DateTime.Now;
  31.         Console.WriteLine("[ INFO ] Model inference time: " + (end - start).TotalMilliseconds + " ms.");
  32.         start = DateTime.Now;
  33.         float[] outputData = predictor.GetInferenceResult("output0");
  34.         for (int i = 0; i < batchNum; ++i)
  35.         {
  36.             Console.WriteLine(string.Format("\n[ INFO ] Classification Top {0} result : \n", 10));
  37.             Console.WriteLine("[ INFO ] classid probability");
  38.             Console.WriteLine("[ INFO ] ------- -----------");
  39.             float[] data = new float[1000];
  40.             Array.Copy(outputData, i * 1000, data, 0, 1000);
  41.             List<int> sortResult = Argsort(new List<float>(data));
  42.             for (int j = 0; j < 10; ++j)
  43.             {
  44.                 string msg = "";
  45.                 msg += ("index: " + sortResult[j] + "\t");
  46.                 msg += ("score: " + data[sortResult[j]] + "\t");
  47.                 Console.WriteLine("[ INFO ] " + msg);
  48.             }
  49.         }
  50.         end = DateTime.Now;
  51.         Console.WriteLine("[ INFO ] Inference result processing time: " + (end - start).TotalMilliseconds + " ms.");
  52.     }
  53. }
  54. public static List<int> Argsort(List<float> array)
  55. {
  56.     int arrayLen = array.Count;
  57.     List<float[]> newArray = new List<float[]> { };
  58.     for (int i = 0; i < arrayLen; i++)
  59.     {
  60.         newArray.Add(new float[] { array[i], i });
  61.     }
  62.     newArray.Sort((a, b) => b[0].CompareTo(a[0]));
  63.     List<int> arrayIndex = new List<int>();
  64.     foreach (float[] item in newArray)
  65.     {
  66.         arrayIndex.Add((int)item[1]);
  67.     }
  68.     return arrayIndex;
  69. }
复制代码
4.3 项目演示

     配置好项目并编写好代码后,运行该项目,项目输出如下所示:
  1. [03/31/2024-22:27:44] [I] [TRT] Loaded engine size: 15 MiB
  2. [03/31/2024-22:27:44] [I] [TRT] [MemUsageChange] TensorRT-managed allocation in engine deserialization: CPU +0, GPU +12, now: CPU 0, GPU 12 (MiB)
  3. [03/31/2024-22:27:44] [I] [TRT] [MemUsageChange] TensorRT-managed allocation in IExecutionContext creation: CPU +0, GPU +4, now: CPU 0, GPU 16 (MiB)
  4. [03/31/2024-22:27:44] [W] [TRT] CUDA lazy loading is not enabled. Enabling it can significantly reduce device memory usage and speed up TensorRT initialization. See "Lazy Loading" section of CUDA documentation https://docs.nvidia.com/cuda/cuda-c-programming-guide/index.html#lazy-loading
  5. [ INFO ] Input image data processing time: 6.6193 ms.
  6. [ INFO ] Model inference time: 1.1434 ms.
  7. [ INFO ] Classification Top 10 result :
  8. [ INFO ] classid probability
  9. [ INFO ] ------- -----------
  10. [ INFO ] index: 386     score: 0.87328124
  11. [ INFO ] index: 385     score: 0.082506955
  12. [ INFO ] index: 101     score: 0.04416279
  13. [ INFO ] index: 51      score: 3.5818E-05
  14. [ INFO ] index: 48      score: 4.2115275E-06
  15. [ INFO ] index: 354     score: 3.5188648E-06
  16. [ INFO ] index: 474     score: 5.789438E-07
  17. [ INFO ] index: 490     score: 5.655325E-07
  18. [ INFO ] index: 343     score: 5.1091644E-07
  19. [ INFO ] index: 340     score: 4.837259E-07
  20. [ INFO ] Classification Top 10 result :
  21. [ INFO ] classid probability
  22. [ INFO ] ------- -----------
  23. [ INFO ] index: 293     score: 0.89423335
  24. [ INFO ] index: 276     score: 0.052870292
  25. [ INFO ] index: 288     score: 0.021361532
  26. [ INFO ] index: 290     score: 0.009259541
  27. [ INFO ] index: 275     score: 0.0066174944
  28. [ INFO ] index: 355     score: 0.0025512716
  29. [ INFO ] index: 287     score: 0.0024535337
  30. [ INFO ] index: 210     score: 0.00083151844
  31. [ INFO ] index: 184     score: 0.0006893527
  32. [ INFO ] index: 272     score: 0.00054959994
复制代码
     通过上面输出可以看出,模型推理仅需1.1434ms,大大提升了模型的推理速度。
5. 总结

     在本项目中,我们开发了TensorRT C# API 2.0版本,重新封装了推理接口。并结合分类模型部署流程向大家展示了TensorRT C# API 的使用方式,方便大家快速上手使用。
     为了方便各位开发者使用,此处开发了配套的演示项目,主要是基于Yolov8开发的目标检测、目标分割、人体关键点识别、图像分类以及旋转目标识别,由于时间原因,还未开发配套的技术文档,此处先行提供给大家项目源码,大家可以根据自己需求使用:

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

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

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

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

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

  • CPU:i7-165G7
  • CUDA型号:12.2
  • Cudnn:8.9.3
  • TensorRT:8.6.1.6
ModelBatch数据预处理模型推理结果后处理Yolov8s-Det225 ms7 ms20 msYolov8s-Obb249 ms15 ms32 msYolov8s-Seg223 ms8 ms128 msYolov8s-Pose227 ms7 ms20 msYolov8s-Cls216 ms1 ms3 ms     最后如果各位开发者在使用中有任何问题,欢迎大家与我联系。

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

本帖子中包含更多资源

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

x

举报 回复 使用道具