什么是TensorRT
TensorRT是由Nvidia推出的C++語言開發的高性能神經網絡推理庫,是一個用於生產部署的優化器和運行時引擎。其高性能計算能力依賴於Nvidia的圖形處理單元。它專注於推理任務,與常用的神經網絡學習框架形成互補,包括TensorFlow、Caffe、PyTorch、MXNet等。可以直接載入這些框架的已訓練模型文件,也提供了API接口通過編程自行構建模型。
TensorRT依賴於Nvidia的深度學習硬件環境,可以是GPU也可以是DLA,如果沒有的話則無法使用。
TensorRT支持目前大部分的神經網絡Layer的定義,同時提供了API讓開發者自己實現特殊Layer的操作,相關函數為 INetworkDefinition::addPluginV2()。
關鍵接口類型
TensorRT核心庫中,最關鍵的幾種接口類型有:
- IExecutionContext 推理引擎運行上下文
- ICudaEngine 推理引擎
- IRuntime CudaEngine反序列化
- INetWorkDefinition 網絡定義
- IParser 網絡模型解析
- IOptimizationProfile 優化配置
- IBuilderConfig CudaEngine的構造參數
- IBuilder 構造器,主要用於構造CudaEngine
- ILogger 日志接口,需要開發者實現
IExecutionContext
推理引擎運行上下文(Context),使用CudaEngine進行推理操作,是推理操作的最終執行接口。
允許一個CudaEngine具有多個運行Context,每個Context可以使用不同的Batch大小,如果網絡模型的輸入尺寸支持動態調整,那么每個上下文還可以使用各自不同的尺寸輸入。
主要功能為執行推理操作,具體函數 IExecutionContext::executeV2(bindings)
通過CudaEngine創建,ICudaEngine::createExecutionContext()
ICudaEngine
可稱為推理引擎,允許應用程序調用這個接口來執行推理,支持同步執行和異步執行,通過Cuda中的Stream和Event來實現異步。一個推理引擎可以有多個運行Context,並且支持批量輸入執行。
CudaEngine的主要作用就是通過創建Context執行推理任務,創建Context的方式為 ICudaEngine::createExecutionContext()。
CudaEngine可以序列化到內存中,然后緩存到磁盤上,下次使用的時候可以直接從磁盤文件載入到內存再序列為CudaEngine即可,可以省去很多時間和參數配置等一下操作。
CudaEngine的創建方式依賴於 INetwrorkDefinition(網絡定義接口),NetWorkDefinition 一般通過解析ONNX模型文件或TensorFlow的已訓練模型文件獲得。ONNX模型文件的解析需要用到 nvonnxparser::IParser 接口。
注意:從 NeworkDefinition 生成 CudaEngine 是一個很耗時的過程,可以將生成的 CudaEngine 緩存到磁盤文件,后續使用的時候直接載入即可使用。
另外,CudaEngine 是不可以跨平台的,不同的 GPU 型號和不同的 TensorRT 版本生成的 CudaEngine 可能都會不兼容,在使用緩存的 CudaEngine 文件的時候要注意區分,可將這些影響因素作為該文件名的一部分進行區分,例如 Win64_RTX2080TI_70011.engine 這類的文件名。
相關接口:
- IExecutionContext,生成 Context 並通過Context進行推理,相關函數:ICudaEngine::createExecutionContext()
- IRuntime,反序列化緩存文件,得到CudaEngine,相關函數:IRuntime::deserializeCudaEngine()
- IBuilder,根據 NetworkDefinision 和 BuilderConfig 構造 CudaEngine,相關函數:IBuilder::buildEngineWithConfig(INetworkDefinision,IBuilderConfig)
IRuntime
這個接口的名字起得容易讓人誤解,看名字好像很底層的一個接口,但它的實際作用,主要只有一個,就是將 CudaEngine 的序列化緩存文件反序列化回來,重新得到 CudaEngine 對象。
獲取方式:nvinfer1::createInferRuntime(void)
相關接口:
- ICudaEngine,根據 NetworkDefinision 和 BuilderConfig 構造 CudaEngine,相關函數:IBuilder::buildEngineWithConfig()
INetWorkDefinition
網絡定義接口,這個接口提供了一些列的函數讓開發者可以從頭構造一個神經網絡,包括輸入輸出張量的維度大小,每個Layer的類型和激活函數等等,還提供了接口讓開發者添加自定義的Layer,是一個很強大的接口。但是,在一般的實際使用過程中基本上不會用到它的任何一個函數。因為網絡的定義是根據已訓練網絡模型文件例如ONNX文件自動生成的。
該接口的一般使用步驟:
- 通過 IBuilder::createNetwork() 生成一個 INetWorkDefinition。
- 使用接口 NvOnnxParser::createParser(&INetWorkDefinition,...) 創建一個與該 INetWorkDefinition 綁定的 IPaser 對象。
- 調用 IPaser::parseFromFile("path.onnx"),將根據模型文件構造 INetWorkDefinition 對象。
IParser
ONNX Parser,解析已訓練模型文件,根據模型文件構造綁定的 INetWorkDefinition 對象。
獲取方式:NvOnnxParser::createParser(&INetWorkDefinition,...)
主要函數:IPaser::parseFromFile("path.onnx")
IOptimizationProfile
為動態模型指定每一個輸入輸出張量的維度,函數為 IOptimizationProfile::setDimensions() 。
在構造 CudaEngine 的時候至少要有一個 IOptimizationProfile,因為每個 ExecutionContext 在使用之前都要先指定一個 IOptimizationProfile 才可以執行推理操作。
獲取方式 IBuilder::createOptimizationProfile(void)
相關接口:
- IBuilderConfig,每個 IBuilderConfig 至少要有一個 IOptimizationProfile,IOptimizationProfile 將隨着 IBuilderConfig 被構造到 CudaEngine 中,被 ExecutionContext 指定使用。相關函數為 IBuilderConfig::addOptimizationProfile(IOptimizationProfile)
- IExecutionContext,每個 Context 被創建之后需要先指定一個 IOptimizationProfile,IExecutionContext::setOptimizationProfile(index),這個 index 是 IOptimizationProfile 在 IBuilderConfig 中的序號,順序是按照 addOptimizationProfile 的調用次序來的。
IBuilderConfig
構造 CudaEngine 的配置參數,可添加 IOptimizationProfile 配置,設置最大工作內存空間、最大Batch大小、最小可接受精度級別、半浮點精度運算等。
獲取方式 IBuilder::createBuilderConfig(void)
相關接口:
- IBuilder,根據 NetworkDefinision 和 BuilderConfig 構造 CudaEngine,函數:IBuilder::buildEngineWithConfig(INetworkDefinision,IBuilderConfig)
IBuilder
IBuilder 接口主要用來構建 CudaEngine,也用來生成 INetWorkDefinition 接口對象、IOptimizationProfile 和 IBuilderConfig 接口對象。
ILogger
日志接口,用來輸出 TensorRT 內部的一些消息、警告、錯誤等信息。
在創建 IBuilder 和 IRuntime 的時候都會需要傳入 ILogger 對象,我們需要實現這個接口並創建一個對象傳給它們。最簡單的一個接口實現如下:
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;
流程圖
總結
本文介紹了使用 TensorRT 的必備接口,掌握了這些接口之間的調用關系,也就明白了 TensorRT 的工作流程,后續將結合實際工程,詳細介紹每一步驟的具體細節。
參考
https://docs.nvidia.com/deeplearning/tensorrt/developer-guide/index.html