【TensorRT】使用記錄 —— TensorRTv6

TensorRT使用記錄

版本號 TensorRT 6
萬惡之源——TensorRT Release Notes,有不同版本的文檔供你閱讀

TensorRT Developer Guide
官方手冊:Python user guide
官方手冊:C++ TensorRT Documentation

不想看英文版的可以看看中文博客的介紹:高性能深度學習支持引擎實戰——TensorRT

安裝教程:推薦

框架介紹

TensorRT的流程:輸入是一個預先訓練好的FP32的模型和網絡,將模型通過parser等方式輸入到TensorRT中,TensorRT可以生成一個Serialization,也就是說將輸入串流到內存或文件中,形成一個優化好的engine,執行的時候可以調取它來執行推斷(Inference)。只要理解框架的運作方式,就很容易利用官方給的samples和手冊進行代碼的魔改了。
在這裏插入圖片描述

插件支持Plugin: 首先TensorRT是支持插件(Plugin)的,或者前面提到的Customer layer的形式,也就是說我們在某些層TensorRT不支持的情況下,最主要是做一些檢測的操作的時候,很多層是該網絡專門定義的,TensorRT沒有支持,需要通過Plugin的形式自己去實現。

推薦閱讀:TensorRT優化原理和TensorRT Plguin總結

Python API

pycuda: tensorRT並沒有像pytorch一樣把cuda的也集成在裏面,所以在使用tensorRT的時候,還要結合cuda的API進行顯存的管理和分配,兩者往往一起使用。
cuda的python的API叫pycuda,看完下面的官方手冊之後,就可以對整個框架有更加深入的理解。
pycuda英文官方手冊
pycuda中文手冊

下面是tensorRT框架的一些關鍵步驟

import tensorflow as tf
import tensorrt as trt
import pycuda.autoinit
import pycuda.driver as cuda

#tensorRT框架使用流程,在導入不同類型的深度學習框架模型的時候,某些步驟會稍有不用,但是主要是集中在builder這一步
#更多的詳細情況以官方sample/python/裏面的例子爲準,樓主也是引用別人的博客並進行了稍微版本的修改
iGpu = 0
cuda.Device(iGpu).make_context()                # 設備上下文
logger = trt.Logger(trt.Logger.WARNING)         # 創建 logger

trtFilePath = "./densenetEngine.engine"    # 讀取現成的 engine 序列文件,否則現場生成一個 engine 並序列化保存爲文件,現在6的版本模型後綴爲engine。
with trt.Builder(TRT_LOGGER) as builder, builder.create_network() as network, trt.CaffeParser() as parser:
        builder.max_batch_size = batch_size #1/2 parament for builder
        builder.max_workspace_size = common.GiB(1) #2/2 parament for builder
        model_tensors = parser.parse(deploy=deploy_file, model=model_file, network=network, dtype=ModelData.DTYPE)
        #in caffe version, wo need to set the mark for output-end
        network.mark_output(model_tensors.find(ModelData.OUTPUT_NAME_1))
        network.mark_output(model_tensors.find(ModelData.OUTPUT_NAME_2))
        network.mark_output(model_tensors.find(ModelData.OUTPUT_NAME_3))
        engine = builder.build_cuda_engine(network)
        print('We had builded an engine...')
        return engine
    
def get_engine(deploy_file, model_file, engine_path): 
	# 利用運行時環境讀取序列化的 engine(現場創建 engine 的可以跳過這步)
    #build an engine and save the serializing model
    try:
        with open(engine_path, "rb") as f, trt.Runtime(TRT_LOGGER) as runtime:
            #If we had builder an engine ,we can deserialize an engine built.(Here you can add an IPlugin or IPluginExt).
            print('We had read an engine.')
            return runtime.deserialize_cuda_engine(f.read())
    except:
        # Fallback to building an engine if the engine cannot be loaded.
        engine = build_engine(deploy_file, model_file)
        with open(engine_path, "wb") as f:
            f.write(engine.serialize())
        return engine
#以上大致步驟已經完成


context = engine.create_execution_context()                                 # 創建內核上下文(區別於設備上下文)
stream  = cuda.Stream()                                                     # 創建流(可選)

hIn = cuda.pagelocked_empty(engine.get_binding_shape(0), dtype=np.float32)  # 使用無初始化的頁鎖定內存,指定尺寸(隱式轉換爲 trt.volume)和數據類型,也可用 np.empty 等來申請一般內存
hOut = cuda.pagelocked_empty(engine.get_binding_shape(1), dtype=np.float32) # engine.get_binding_shape 的 (0) 和 (1) 分別等於network 的輸入和輸出節點尺寸
dIn = cuda.mem_alloc(h_input.nbytes)                                        # 申請設備內存,使用主機內存的大小
dOut = cuda.mem_alloc(h_output.nbytes)

cuda.memcpy_htod_async(d_input, h_input, stream)                            # 異步數據拷貝
#cuda.memcpy_htod(d_input, data)                                            # 非異步數據拷貝
context.execute_async(batchSize, bindings=[int(dIn), int(dOut)], stream_handle=stream.handle)  # 異步執行內核
context.execute(batchSize, bindings=[int(dIn), int(dOut)])                  # 非異步執行內核
cuda.memcpy_dtoh_async(hOut, dOut, stream)

stream.synchronize()                                                        # 同步

context = None                                                              # 清空內核上下文和 engine
engine  = None
cuda.Context.pop()                                                          # 關閉設備上下文

Python API 報錯一覽:

  • [TensorRT] ERROR: Network must have at least one output: 這個報錯最常見的原因就是通過不同方式轉換過來的模型中摻雜了奇奇怪怪的非神經網絡操作,就會導致tensorrt解析不了這個模型。正確的做法是先檢查tensorrt要加載的模型及其參數和沒轉換過來之前的模型是不是完全一致,然後再針對出錯的地方進行修改。官方論壇中關於對這一報錯的建議 也是如此,不過也有可能是其他原因的而導致的問題,如果實在找不到問題的出處,可以先用network.mark_output(network.get_layer(network.num_layers-1).get_output(0))這條語句先強行輸出結果查看,與原來的網絡結果進行對比(建議先打印出network.num_layers你的engine裏面的網絡層數正不正確,有的模型連層都檢測不到,那肯定就是你在轉換模型的時候出了問題)
  • tensorrt不支持分支型輸出?: 這一點在官方的論壇得到了確定,當你的模型文件不完整或者是輸出沒有定義好,的確會出現這個。那麼應該怎麼辦,官方在yolo3轉換的sample裏面給出了答案,可以把結果extend或者concat起來,先把結果輸出出來再進行分開提出所要的結果。樓主親自檢驗過,效果ok。PS:有的如果tensorrt可以檢測到你的網絡有兩個輸出的話,他會把結果變成一個一維的向量來輸出,然後你就分不清哪一個是哪一個了,還是先自己concat一下比較好~
  • Caffe Parser: Invalid reshape param. TensorRT does not support reshape in N (batch) dimension 將所有Reshape層的第一個維度參數dim由1變成0就可以啦

C++ API

推薦閱讀:
C++ API:利用TensorRT實現神經網絡提速(讀取ONNX模型並運行)

使用心得

  • pytorch: 對於pytorch的模型,使用onnx當做轉換中介,你會方便很多

  • onnx(中介): 對於通過轉換爲onnx的模型,因爲不同框架下onnx神經層的支持性和兼容性也是不一樣的,要以對應框架下onnx的api爲準。 同時要注意的是!!! 如果轉換過來的模型中夾雜着着太多的非神經網絡的操作,tensorrt就有可能解析不了,因爲tensorrt對於某些python的操作是不支持,比如求和,平均,這樣即使你的onnx轉換沒有報錯,最後得到的tensorrt的engine就會檢測不到輸出(當前tensorrt版本爲6)。

  • 解析模型: 很多人會卡在tensorrt的解析模型這一步,確實樓主也栽跟頭了,確實tensorrt對模型的要求比較太嚴格,原因是他可以進行加速的層的種類是有限的(支持的層在官網的開發者手冊中可以查詢得到,不然你只能通過tensorrt提供的plugin來自定義你想要的層了,其中包括cuda,tensorrt的結合編程,有點麻煩)

  • 出現報錯多去tensorrt的官網論壇看看 : )

參考資料
https://blog.csdn.net/haima1998/article/details/83245155
https://www.cnblogs.com/cuancuancuanhao/p/11725715.html

發佈了35 篇原創文章 · 獲贊 26 · 訪問量 8504
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章