TensorRT-安裝-使用
一.安裝
這里 是英偉達提供的安裝指導,如果有仔細認真看官方指導,基本上按照官方的指導肯定能安裝成功。
問題是肯定有很多人不願意認真看英文指導,比如說我就是,我看那個指導都是直接找到命令行所在,直接敲命令,然后就出了很多問題,然后搜索好長時間,最后才發現,原來官方install guide里是有說明的。
這里使用的是 deb 包安裝的方式,以下是安裝過程,我是cuda 8.0 ,cuda9.0也是類似的。
進行下面三步時最好先將后面記錄的遇到的問題仔細看看,然后回過頭來按照 一二三 步來安裝。
第一步:
$ sudo dpkg -i nv-tensorrt-repo-ubuntu1604-ga-cuda8.0-trt3.0-20171128_1-1_amd64.deb
$ sudo apt-get update
$ sudo apt-get install tensorrt
其中的deb包要換成與自己 cuda和系統 對應的版本。
第二步:
使用python2則安裝如下依賴
$ sudo apt-get install python-libnvinfer-doc
這個是為了安裝一些依賴的:比如 python-libnvinfer python-libnvinfer-dev swig3.0
如果是python3則安裝如下依賴
$ sudo apt-get install python3-libnvinfer-doc
第三步:
$ sudo apt-get install uff-converter-tf
這個是安裝通用文件格式轉換器,主要用在 TensorRT 與TensorFlow 交互使用的時候。
不過我安裝的時候還是出問題了:
- 安裝tensorRT之前要將cuda的兩個deb包添加上,因為TensorRT依賴好多cuda的一些東西比如 cuda-cublas-8-0 ,我之前cuda是用runfile安裝的,所以TensorRT安裝時有些依賴庫找不到導致出錯,如下圖:
上面提示缺少依賴包,但是實際上 libnvinfer4 的包是tensorRT安裝了之后才有的,那現在反而成了依賴包了,不管他,缺什么安裝什么,但是還是出錯,如下:
還是缺少依賴包,這次是缺 cuda-cublas-8-0 ,現在知道了,缺的是cuda的相關組件。
后來把 cuda 的兩個deb包安裝之后就沒問題了,cuda 8.0 的deb包 在這里 ,如下圖,下載紅框里的兩個deb包。
如果用的是 runfile 的方式安裝的cuda的話,很容易出錯,因為網上大部分cuda安裝教程都是用runfile的方式安裝的。所以如果cuda就是用deb包安裝的話,就沒有這個問題,如果使用runfile安裝的話,安裝tensorRT之前要把這兩個deb包安裝上,安裝方式如下:
$ sudo dpkg -i cuda-repo-ubuntu1604-8-0-local-cublas-performance-update_8.0.61-1_amd64.deb
$ sudo dpkg -i cuda-repo-ubuntu1604-8-0-local-ga2_8.0.61-1_amd64.deb
以上是自己摸索出來的,折騰了一番之后才發現原來官方的 install guide已經說明了,如下:
The debian installation automatically installs any dependencies, but:
- requires sudo root privileges to install
- provides no flexibility as to which location TensorRT is installed into
- requires that the CUDA Toolkit has also been installed with a debian package.
注意最后一條,意思是如果用deb包安裝TensorRT,那么前提是 你的CUDA也是用deb包安裝的。
怪自己沒有認真看,要是多花個5分鍾仔細看一下,就不用折騰這么久了,由此深有感觸,文檔還是官方英文原版的最好,而且要認真看。
不過不知道用 runfile cuda+Tar File Installation tensorRT的組合安裝方式是怎么樣的,沒試過。
- tensorRT 3 支持CUDA 8 和 CUDA 9,但是只支持 cuDNN 7,我第一次安裝的時候cuDNN是5.1的,結果總是出錯,錯誤是啥忘記了,反正換成cuDNN 7就好了,這個官方指導也有說明,不過比較隱蔽,他是放在 4.2 Tar File Installation 一節說明的:
- Install the following dependencies, if not already present:
‣ Install the CUDA Toolkit v8.0, 9.0 or 9.2
‣ cuDNN 7.1.3
‣ Python 2 or Python 3
我試過只要大版本是 cudnn7就可以。這個也容易忽略。
安裝好后,使用 $ dpkg -l | grep TensorRT 命令檢測是否成功,輸出如下所示即為成功
安裝后會在 /usr/src 目錄下生成一個 tensorrt 文件夾,里面包含 bin , data , python , samples 四個文件夾, samples 文件夾中是官方例程的源碼; data , python 文件中存放官方例程用到的資源文件,比如caffemodel文件,TensorFlow模型文件,一些圖片等;bin 文件夾用於存放編譯后的二進制文件。
可以把 tensorrt 文件夾拷貝到用戶目錄下,方便自己修改測試例程中的代碼。
進入 samples 文件夾直接 make,會在 bin 目錄中生成可執行文件,可以一一進行測試學習。
另外tensorRT是不開源的, 它的頭文件位於 /usr/include/x86_64-linux-gnu 目錄下,共有七個,分別為:
1 2 3 4 5 6 7 |
/usr/include/x86_64-linux-gnu/NvCaffeParser.h /usr/include/x86_64-linux-gnu/NvInfer.h /usr/include/x86_64-linux-gnu/NvInferPlugin.h /usr/include/x86_64-linux-gnu/NvOnnxConfig.h /usr/include/x86_64-linux-gnu/NvOnnxParser.h /usr/include/x86_64-linux-gnu/NvUffParser.h /usr/include/x86_64-linux-gnu/NvUtils.h |
TensorRT4.0相比於3.0新增了對ONNX的支持。
tensorRT的庫文件位於 /usr/lib/x86_64-linux-gnu 目錄下,如下(篩選出來的,摻雜了一些其他nvidia庫):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
/usr/lib/x86_64-linux-gnu/libnvinfer.so /usr/lib/x86_64-linux-gnu/libnvToolsExt.so /usr/lib/x86_64-linux-gnu/libnvinfer_plugin.a /usr/lib/x86_64-linux-gnu/libnvinfer_plugin.so.4 /usr/lib/x86_64-linux-gnu/libnvcaffe_parser.so /usr/lib/x86_64-linux-gnu/libnvparsers.so.4.1.2 /usr/lib/x86_64-linux-gnu/stubs/libnvrtc.so /usr/lib/x86_64-linux-gnu/libnvcaffe_parser.a /usr/lib/x86_64-linux-gnu/libnvidia-opencl.so.1 /usr/lib/x86_64-linux-gnu/libnvvm.so /usr/lib/x86_64-linux-gnu/libnvinfer.a /usr/lib/x86_64-linux-gnu/libnvvm.so.3 /usr/lib/x86_64-linux-gnu/libnvToolsExt.so.1 /usr/lib/x86_64-linux-gnu/libnvrtc.so.7.5 /usr/lib/x86_64-linux-gnu/libnvparsers.a /usr/lib/x86_64-linux-gnu/libnvblas.so.7.5 /usr/lib/x86_64-linux-gnu/libnvToolsExt.so.1.0.0 /usr/lib/x86_64-linux-gnu/libnvcaffe_parser.so.4.1.2 /usr/lib/x86_64-linux-gnu/libnvinfer_plugin.so /usr/lib/x86_64-linux-gnu/libnvrtc-builtins.so /usr/lib/x86_64-linux-gnu/libnvparsers.so /usr/lib/x86_64-linux-gnu/libnvrtc-builtins.so.7.5.18 /usr/lib/x86_64-linux-gnu/libnvblas.so.7.5.18 /usr/lib/x86_64-linux-gnu/libnvvm.so.3.0.0 /usr/lib/x86_64-linux-gnu/libnvrtc.so /usr/lib/x86_64-linux-gnu/libnvrtc-builtins.so.7.5 /usr/lib/x86_64-linux-gnu/libnvinfer.so.4.1.2 /usr/lib/x86_64-linux-gnu/libnvidia-opencl.so.390.30 /usr/lib/x86_64-linux-gnu/libnvrtc.so.7.5.17 /usr/lib/x86_64-linux-gnu/libnvblas.so /usr/lib/x86_64-linux-gnu/libnvinfer.so.4 /usr/lib/x86_64-linux-gnu/libnvparsers.so.4 /usr/lib/x86_64-linux-gnu/libnvinfer_plugin.so.4.1.2 /usr/lib/x86_64-linux-gnu/libnvcaffe_parser.so.4 |
編譯
將 /usr/src/tensorrt 文件夾拷貝到用戶目錄下,假設路徑為 <tensorrt_srcpath> 。
第一個問題:
在 <tensorrt_srcpath>/tensorrt/samples 文件夾中有個 Makefile.config 文件,里面第4行:
CUDA_VER?=cuda-$(shell dpkg-query -f '$${version}\n' -W 'cuda-cudart-[0-9]*' | cut -d . -f 1,2 | sort -n | tail -n 1)
這一句是為了獲取cuda版本的,我的機器是 CUDA 8.0 。我記得我第一次安裝時,后面dpkg命令 輸出的不是8.0,是一個很奇怪的數字,導致我不能編譯 tensorRT 例程。 后來我直接在這句后面添加了一句: CUDA_VER=cuda-8.0 ,簡單粗暴解決問題了。
這個問題好像是還是因為我之前安裝 cuda 時是用 runfile 的方式安裝的,用這種方式安裝的cuda不會安裝cuda的deb包,所以上面語句輸出的是不對的,導致找不到cuda庫目錄,編譯不能進行。
可以使用命令sudo dpkg -i cuda-repo-ubuntu1604-8-0-local-ga2_8.0.61-1_amd64.deb ,安裝deb包,就可以了。或者像我那樣添加 CUDA_VER=cuda-8.0 也可以。
如果安裝cuda就是使用deb包安裝的話,就不會出現這個問題。
第二個問題:
如果機器上安裝了多個cuda版本,像我這個機器上 cuda8.0,9.0,9.1都裝上了,上面語句得到的就只是 CUDA_VER=9.1,如果安裝的是其他版本cuda的TensorRT的話肯定是不對的。
可以直接在第4行下面添加:
CUDA_INSTALL_DIR=/usr/local/cuda-9.0
二.TensorRT 使用流程
這是個很簡單的流程,先簡單了解一下,以后會深入研究更高級的用法。
在使用tensorRT的過程中需要提供以下文件(以caffe為例):
- A network architecture file (deploy.prototxt), 模型文件
- Trained weights (net.caffemodel), 權值文件
- A label file to provide a name for each output class. 標簽文件
前兩個是為了解析模型時使用,最后一個是推理輸出時將數字映射為有意義的文字標簽。
tensorRT的使用包括兩個階段, build and deployment:
- build:Import and optimize trained models to generate inference engines
build階段主要完成模型轉換(從caffe或TensorFlow到TensorRT),在模型轉換時會完成前述優化過程中的層間融合,精度校准。這一步的輸出是一個針對特定GPU平台和網絡模型的優化過的TensorRT模型,這個TensorRT模型可以序列化存儲到磁盤或內存中。存儲到磁盤中的文件稱之為 plan file。
下面代碼是一個簡單的build過程:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
//創建一個builder IBuilder* builder = createInferBuilder(gLogger); // parse the caffe model to populate the network, then set the outputs // 創建一個network對象,不過這時network對象只是一個空架子 INetworkDefinition* network = builder->createNetwork(); //tensorRT提供一個高級別的API:CaffeParser,用於解析Caffe模型 //parser.parse函數接受的參數就是上面提到的文件,和network對象 //這一步之后network對象里面的參數才被填充,才具有實際的意義 CaffeParser parser; auto blob_name_to_tensor = parser.parse(“deploy.prototxt”, trained_file.c_str(), *network, DataType::kFLOAT);
// 標記輸出 tensors // specify which tensors are outputs network->markOutput(*blob_name_to_tensor->find("prob")); // Build the engine // 設置batchsize和工作空間,然后創建inference engine builder->setMaxBatchSize(1); builder->setMaxWorkspaceSize(1 << 30); //調用buildCudaEngine時才會進行前述的層間融合或精度校准優化方式 ICudaEngine* engine = builder->buildCudaEngine(*network); |
上面的過程使用了一個高級別的API:CaffeParser,直接讀取 caffe的模型文件,就可以解析,也就是填充network對象。解析的過程也可以直接使用一些低級別的C++API,比如:
1 2 |
ITensor* in = network->addInput(“input”, DataType::kFloat, Dims3{…}); IPoolingLayer* pool = network->addPooling(in, PoolingType::kMAX, …); |
解析caffe模型之后,必須要指定輸出tensor,設置batchsize,和設置工作空間。設置batchsize就跟使用caffe測試是一樣的,設置工作空間是進行前述層間融合和張量融合的必要措施。層間融合和張量融合的過程是在調用builder->buildCudaEngine時才進行的。
- deploy:Generate runtime inference engine for inference
deploy階段主要完成推理過程,Kernel Auto-Tuning 和 Dynamic Tensor Memory 應該是在這里完成的。將上面一個步驟中的plan文件首先反序列化,並創建一個 runtime engine,然后就可以輸入數據(比如測試集或數據集之外的圖片),然后輸出分類向量結果或檢測結果。
tensorRT的好處就是不需要安裝其他深度學習框架,就可以實現部署和推理。
以下是一個簡單的deploy代碼:這里面沒有包含反序列化過程和測試時的batch流獲取
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
// The execution context is responsible for launching the // compute kernels 創建上下文環境 context,用於啟動kernel IExecutionContext *context = engine->createExecutionContext(); // In order to bind the buffers, we need to know the names of the // input and output tensors. //獲取輸入,輸出tensor索引 int inputIndex = engine->getBindingIndex(INPUT_LAYER_NAME), int outputIndex = engine->getBindingIndex(OUTPUT_LAYER_NAME); //申請GPU顯存 // Allocate GPU memory for Input / Output data void* buffers = malloc(engine->getNbBindings() * sizeof(void*)); cudaMalloc(&buffers[inputIndex], batchSize * size_of_single_input); cudaMalloc(&buffers[outputIndex], batchSize * size_of_single_output); //使用cuda 流來管理並行計算 // Use CUDA streams to manage the concurrency of copying and executing cudaStream_t stream; cudaStreamCreate(&stream); //從內存到顯存,input是讀入內存中的數據;buffers[inputIndex]是顯存上的存儲區域,用於存放輸入數據 // Copy Input Data to the GPU cudaMemcpyAsync(buffers[inputIndex], input, batchSize * size_of_single_input, cudaMemcpyHostToDevice, stream); //啟動cuda核計算 // Launch an instance of the GIE compute kernel context.enqueue(batchSize, buffers, stream, nullptr); //從顯存到內存,buffers[outputIndex]是顯存中的存儲區,存放模型輸出;output是內存中的數據 // Copy Output Data to the Host cudaMemcpyAsync(output, buffers[outputIndex], batchSize * size_of_single_output, cudaMemcpyDeviceToHost, stream)); //如果使用了多個cuda流,需要同步 // It is possible to have multiple instances of the code above // in flight on the GPU in different streams. // The host can then sync on a given stream and use the results cudaStreamSynchronize(stream); |
可見使用了挺多的CUDA 編程,所以要想用好tensorRT還是要熟練 GPU編程。
4 Performance Results
來看一看使用以上優化方式之后,能獲得怎樣的加速效果:
可見使用tensorRT與使用CPU相比,獲得了40倍的加速,與使用TensorFlow在GPU上推理相比,獲得了18倍的加速。效果還是很明顯的。
以下兩圖,是使用了INT8低精度模式進行推理的結果展示:包括精度和速度。
可見精度損失很少,速度提高很多。
上面還是17年 TensorRT2.1的性能,這里 是一個最新的TensorRT4.0.1的性能表現,有很詳細的數據展示來說明TensorRT在inference時的強勁性能。
后面的博客中會進一步學習 tensorRT,包括官方例程和做一些實用的優化。