https://mp.weixin.qq.com/s/E5qbMsuc7UBnNmYBzq__5Q
TensorRT 系列之入門篇
原創(chuàng)?2018-01-28?子棐?子棐之GPGPU
這個(gè)世界沒有免費(fèi)的午餐,如果有---那就是TensorRT……
——子棐
Why TensorRT
訓(xùn)練對于深度學(xué)習(xí)來說是為了獲得一個(gè)性能優(yōu)異的模型,其主要的關(guān)注點(diǎn)在于模型的準(zhǔn)確度等指標(biāo)。推理則不一樣,其沒有了訓(xùn)練中的反向迭代過程驻售,是針對新的數(shù)據(jù)進(jìn)行預(yù)測,而我們?nèi)粘I钪惺褂玫腁I服務(wù)都是推理服務(wù)。相較于訓(xùn)練椭更,推理的關(guān)注點(diǎn)不一樣,從而也給現(xiàn)在有技術(shù)帶來了新的挑戰(zhàn):
需求現(xiàn)有框架的局限性影響
高吞吐率無法處理大量和高速的數(shù)據(jù)增加了單次推理的開銷
低響應(yīng)時(shí)間應(yīng)用無法提供實(shí)時(shí)的結(jié)果損害了用戶體驗(yàn)(語音識別蛾魄、個(gè)性化推薦和實(shí)時(shí)目標(biāo)檢測)
高效的功耗以及顯存消耗控制非最優(yōu)效能增加了推理的開銷甚至無法進(jìn)行推理部署
部署級別的解決方案非專用于部署使用框架復(fù)雜度和配置增加了部署難度以及生產(chǎn)率
根據(jù)上圖可知虑瀑,推理更關(guān)注的是高吞吐率湿滓、低響應(yīng)時(shí)間、低資源消耗以及簡便的部署流程舌狗,而TensorRT就是用來解決推理所帶來的挑戰(zhàn)以及影響的部署級的解決方案叽奥。
TensorRT部署流程
TensorRT的部署分為兩個(gè)部分:
[if !supportLists]1.??? [endif]優(yōu)化訓(xùn)練好的模型并生成計(jì)算流圖
[if !supportLists]2.??? [endif]使用TensorRT Runtime部署計(jì)算流圖
關(guān)于這個(gè)流程很自然我們會(huì)想到以下幾個(gè)問題:
[if !supportLists]1.??? [endif]TensorRT支持什么框架訓(xùn)練出來的網(wǎng)絡(luò)模型呢?
[if !supportLists]2.??? [endif]TensorRT支持什么網(wǎng)絡(luò)結(jié)構(gòu)呢痛侍?
[if !supportLists]3.??? [endif]TensorRT優(yōu)化器做了哪些優(yōu)化呢朝氓?
[if !supportLists]4.??? [endif]TensorRT優(yōu)化好的計(jì)算流圖可以運(yùn)行在什么設(shè)備上呢?
個(gè)中因果主届,諸位看官赵哲,稍安勿躁,待本文娓娓道來君丁。
TensorRT之大胃王
輸入篇之網(wǎng)絡(luò)框架:TensorRT3支持所有常見的深度學(xué)習(xí)框架包括Caffe枫夺、Chainer、CNTK绘闷、MXnet筷屡、PaddlePaddle、Pytorch簸喂、TensorFlow以及Theano毙死。
輸入篇之網(wǎng)絡(luò)層:TensorRT3支持的網(wǎng)絡(luò)層包括
Convolution
LSTM and GRU
Activation: ReLU, tanh,? sigmoid
Pooling: max and average
Scaling
ElementWise
LRN
Fully-connected
SoftMax
Deconvolution
Concatenation
Flatten
Padding
Plugin
RNN: RNN, GRU, LSTM
Scale
Shuffle
Softmax
Squeeze
Unary
輸入篇之接口方式:TensorRT3支持模型導(dǎo)入方式包括C++ API、Python API喻鳄、NvCaffeParser和NvUffParser
輸出篇之支持系統(tǒng)平臺:TensorRT3支持的平臺包括Linux x86扼倘、Linux aarch64、Android aarch64和QNX aarch64除呵。
輸出篇之支持硬件平臺:TensorRT3可以運(yùn)行在每一個(gè)GPU平臺再菊,從數(shù)據(jù)中心的Tesla P4/V100到自動(dòng)駕駛和嵌入式平臺的DrivePX及TX1/TX2。
TensorRT 模型導(dǎo)入流程
如上圖所示颜曾,模型的導(dǎo)入方法可以根據(jù)框架種類分成三種:Caffe纠拔、TensorFlow和其他。
Caffe
[if !supportLists]1.??? [endif]使用C++/Python API導(dǎo)入模型:通過代碼定義網(wǎng)絡(luò)結(jié)構(gòu)泛豪,并載入模型weights的方式導(dǎo)入稠诲;
[if !supportLists]2.??? [endif]使用NvCaffeParser導(dǎo)入模型:導(dǎo)入時(shí)輸入網(wǎng)絡(luò)結(jié)構(gòu)prototxt文件及caffemodel文件即可。
TensorFlow
[if !supportLists]1.??? [endif]訓(xùn)練完成后诡曙,使用uff python接口將模型轉(zhuǎn)成uff格式臀叙,之后使用NvUffParaser導(dǎo)入;
[if !supportLists]2.??? [endif]對于TensorFlow或者keras(TensorFlow后端)的价卤,利用Freeze graph來生成.pb(protobuf)文件劝萤,之后使用convert-to-uff工具將.pb文件轉(zhuǎn)化成uff格式,然后利用NvUffParaser導(dǎo)入慎璧。
其他框架
使用C++/Python API導(dǎo)入模型:通過代碼定義網(wǎng)絡(luò)結(jié)構(gòu)床嫌,載入模型weights的方式導(dǎo)入跨释。以Pytorch為例,在完成訓(xùn)練后厌处,通過stat_dict()函數(shù)獲取模型的weights煤傍,從而在定義網(wǎng)絡(luò)結(jié)構(gòu)時(shí)將weights載入。
[if !supportLineBreakNewLine]
[endif]
[if !supportLineBreakNewLine]
[endif]
注:weights文件是保存的C++ Map對象嘱蛋,該對象定義為:
std::map
其中Weights類型是在NvInfer.h中定義蚯姆,為存儲(chǔ)weights的array。
TensorRT 優(yōu)化細(xì)節(jié)
網(wǎng)絡(luò)模型在導(dǎo)入至TensorRT后會(huì)進(jìn)行一系列的優(yōu)化洒敏,主要優(yōu)化內(nèi)容如下圖所示龄恋。
Layer & Tensor Fusion
TensorRT在獲得網(wǎng)絡(luò)計(jì)算流圖后會(huì)針對計(jì)算流圖進(jìn)行優(yōu)化,這部分優(yōu)化不會(huì)改變圖中最底層計(jì)算內(nèi)容凶伙,而是會(huì)去重構(gòu)計(jì)算圖來獲得更快更高效的執(zhí)行方式郭毕,即計(jì)算不變優(yōu)化計(jì)算方法。
以下圖為例:
深度學(xué)習(xí)框架在做推理時(shí)函荣,會(huì)對每一層調(diào)用多個(gè)/次功能函數(shù)显押。而由于這樣的操作都是在GPU上運(yùn)行的,從而會(huì)帶來多次的CUDA Kernel
launch過程傻挂。相較于Kernel launch以及每層tensor data讀取來說乘碑,kernel的計(jì)算是更快更輕量的,從而使得這個(gè)程序受限于顯存帶寬并損害了GPU利用率金拒。
TensorRT通過以下三種方式來解決這個(gè)問題:
[if !supportLists]1.??? [endif]Kernel縱向融合:通過融合相同順序的操作來減少Kernel Launch的消耗以及避免層之間的顯存讀寫操作兽肤。如上圖所示,卷積绪抛、Bias和Relu層可以融合成一個(gè)Kernel资铡,這里稱之為CBR。
[if !supportLists]2.??? [endif]Kernel橫向融合:TensorRT會(huì)去挖掘輸入數(shù)據(jù)且filter大小相同但weights不同的層幢码,對于這些層不是使用三個(gè)不同的Kernel而是使用一個(gè)Kernel來提高效率笤休,如上圖中超寬的1x1 CBR所示。
[if !supportLists]3.??? [endif]消除concatenation層症副,通過預(yù)分配輸出緩存以及跳躍式的寫入方式來避免這次轉(zhuǎn)換店雅。
通過這樣的優(yōu)化,TensorRT可以獲得更小瓦糕、更快底洗、更高效的計(jì)算流圖腋么,其擁有更少層網(wǎng)絡(luò)結(jié)構(gòu)以及更少Kernel Launch次數(shù)咕娄。下表列出了常見幾個(gè)網(wǎng)絡(luò)在TensorRT優(yōu)化后的網(wǎng)絡(luò)層數(shù)量,很明顯的看到TensorRT可以有效的優(yōu)化網(wǎng)絡(luò)結(jié)構(gòu)珊擂、減少網(wǎng)絡(luò)層數(shù)從而帶來性能的提升圣勒。
NetworkLayersLayers after fusion
VGG194327
Inception V3309113
ResNet-152670159
FP16 & INT8 精度校準(zhǔn)
大多數(shù)的網(wǎng)絡(luò)都是使用FP32進(jìn)行模型訓(xùn)練费变,因此模型最后的weights也是FP32格式。但是一旦完成訓(xùn)練圣贸,所有的網(wǎng)絡(luò)參數(shù)就已經(jīng)是最優(yōu)挚歧,在推理過程中是無需進(jìn)行反向迭代的,因此可以在推理中使用FP16或者INT8精度計(jì)算從而獲得更小的模型吁峻,低的顯存占用率和延遲以及更高的吞吐率滑负。
TensorRT可以采用FP32、FP16和INT8精度部署模型用含,只需要在uff_to_trt_engine函數(shù)中指定相應(yīng)數(shù)據(jù)類型即可:
[if !supportLists]o???[endif]對于FP32, 使用 trt.infer.DataType.FLOAT.
[if !supportLists]o???[endif]對于FP16 指令 以及 Volta GPU內(nèi)的Tensor Cores, 使用trt.infer.DataType.HALF
[if !supportLists]o???[endif]對于INT8, 使用 trt.infer.DataType.INT8.
更多詳細(xì)的信息會(huì)在未來幾期公眾號解釋矮慕,如有興趣請保持關(guān)注。
Kernel Auto-Tuning
TensorRT會(huì)針對大量的Kernel進(jìn)行參數(shù)優(yōu)化和調(diào)整啄骇。例如說痴鳄,對于卷積計(jì)算有若干種算法,TensorRT會(huì)根據(jù)輸入數(shù)據(jù)大小缸夹、filter大小痪寻、tensor分布、batch大小等等參數(shù)針對目標(biāo)平臺GPU進(jìn)行選擇和優(yōu)化虽惭。
Dynamic Tensor Memory
TensorRT通過為每一個(gè)tensor在其使用期間設(shè)計(jì)分配顯存來減少顯存的占用增加顯存的復(fù)用率橡类,從而避免了顯存的過度開銷以獲得更快和高效的推理性能。
優(yōu)化結(jié)果
上圖為基于Resnet50網(wǎng)絡(luò)芽唇,分別在CPU猫态、V100+TensorFlow、V100+TensorRT上進(jìn)行推理時(shí)的性能比較披摄,縱軸為每秒處理的圖片數(shù)量亲雪。相較于CPU和TensorFlow,TensorRT可以帶來40倍和18倍的吞吐率的提升疚膊,而這部分的提升只需要在擁有GPU的前提下使用TensorRT即可免費(fèi)獲得义辕。
TensorRT 部署方法
完成TensorRT優(yōu)化后可以得到一個(gè)Runtime
inference engine,這個(gè)文件可以被系列化保存至硬盤中寓盗,而這個(gè)保存的序列化文件我們稱之為“Plan”(流圖)灌砖,之所以稱之為流圖,因此其不僅保存了計(jì)算時(shí)所需的網(wǎng)絡(luò)weights也保存了Kernel執(zhí)行的調(diào)度流程傀蚌。TensorRT提供了write_engine_to_file()函數(shù)以來保存流圖基显。
在獲得了流圖之后就可以使用TensorRT部署應(yīng)用。為了進(jìn)一步的簡化部署流程善炫,TensorRT提供了TensorRT Lite API撩幽,它是高度抽象的接口會(huì)自動(dòng)處理大量的重復(fù)的通用任務(wù)例如創(chuàng)建一個(gè)Logger,反序列化流圖并生成Runtime inference engine,處理輸入的數(shù)據(jù)窜醉。以下代碼提供了一個(gè)使用TensorRT Lite
API的范例教程宪萄,只需使用API創(chuàng)建一個(gè)Runtime Engine即可完成前文提到的通用任務(wù),之后將需要推理的數(shù)據(jù)載入并送入Engine即可進(jìn)行推理榨惰。
from tensorrt.lite import Engine
from tensorrt.infer import LogSeverity
import tensorrt
# Create a runtime engine from plan file using TensorRT Lite API?
engine_single = Engine(PLAN="keras_vgg19_b1_FP32.engine",
? ?postprocessors={"dense_2/Softmax":analyze})
images_trt, images_tf = load_and_preprocess_images()
results = []
for image in images_trt:
? ? result = engine_single.infer(image) # Single function for
inference
? ? results.append(result)
敲黑板0萦ⅰ!琅催!TensorRT3Highlight
TensorRT3帶來的三個(gè)重大更新為:
[if !supportLists]1.??? [endif]TensorFlow Model
Importer - 便捷的API用于導(dǎo)入TensorFLow訓(xùn)練的模型居凶,優(yōu)化并生成推理Engine。
[if !supportLists]2.??? [endif]Python API -
Python API用于進(jìn)一步提高開發(fā)效率藤抡。
[if !supportLists]3.??? [endif]Volta Tensor
Core支持 - 帶來相較于Tesla P100高達(dá)3.7倍的更快的推理性能排监。
以上 & 未完待續(xù)~~~