# Convert PyTorch model to TensorRT for 3-8x speedup。 將PyTorch模型轉換為TensorRT,實現3-8倍加速 ###### tags: `TensorRT` `cuda` `deployment` `YOLOv7` `Edge AI` `Nvidia` `Jetson` ![](https://i.imgur.com/S5QFcnE.png =800x) ## NVIDIA Jetson 平台部署相關筆記 ### 基本環境設定 - [Jetson AGX Xavier 系統環境設定1_在windows10環境連接與安裝](https://hackmd.io/@YungHuiHsu/HJ2lcU4Rj) - [Jetson AGX Xavier 系統環境設定2_Docker安裝或從源程式碼編譯](https://hackmd.io/k-lnDTxVQDWo_V13WEnfOg) - [NVIDIA Container Toolkit 安裝筆記](https://hackmd.io/wADvyemZRDOeEduJXA9X7g) - [Jetson 邊緣裝置查詢系統性能指令jtop](https://hackmd.io/VXXV3T5GRIKi6ap8SkR-tg) - [Jetson Network Setup 網路設定](https://hackmd.io/WiqAB7pLSpm2863N2ISGXQ) - [OpenCV turns on cuda acceleration in Nvidia Jetson platform<br>OpenCV在Nvidia Jetson平台開啟cuda加速](https://hackmd.io/6IloyiWMQ_qbIpIE_c_1GA) ### 模型部署與加速 - [[Deployment] AI模型部屬入門相關筆記](https://hackmd.io/G80HMJRmSwaaLD8W1PHUPg) - [[Object Detection_YOLO] YOLOv7 論文筆記](https://hackmd.io/xhLeIsoSToW0jL61QRWDcQ) - [Deploy YOLOv7 on Nvidia Jetson](https://hackmd.io/kZftj6AgQmWJsbXsswIwEQ) - [Convert PyTorch model to TensorRT for 3-8x speedup<br>將PyTorch模型轉換為TensorRT,實現3-8倍加速](https://hackmd.io/_oaJhYNqTvyL_h01X1Fdmw?both) - [Accelerate multi-streaming cameras with DeepStream and deploy custom (YOLO) models<br>使用DeepStream加速多串流攝影機並部署客製(YOLO)模型](https://hackmd.io/@YungHuiHsu/rJKx-tv4h) - [Use Deepstream python API to extract the model output tensor and customize model post-processing (e.g., YOLO-Pose)<br>使用Deepstream python API提取模型輸出張量並定製模型后處理(如:YOLO-Pose)](https://hackmd.io/@YungHuiHsu/rk41ISKY2) - [Model Quantization Note 模型量化筆記](https://hackmd.io/riYLcrp1RuKHpVI22oEAXA) --- ## 加速測試結果 ### 使用TensorRT推理引擎 - Pytorch > ONNX > TensorRT(include NMS) - `.trt` and `.pt` are averaged over a 1-minute video - `.engine` are tested with `/usr/src/tensorrt/bin/trtexec --loadEngine=model.engine --verbose` (trtexec FPS) - `size` : 640 | model | NMS | batch<br>tested | precision | FPS | speedup | mAP | |:-----------------------:|:--------:|:---------------:|:---------:|:-----:|:--------:| --- | | yolov7-tiny.pt | - | 1 | fp32 | 24.0 | baseline | 38.7% | | yolov7-tiny.trt | included | 1 | fp16 | 145.5 | 6.1x | to do | | yolov7-tiny.engine | - | 1 | fp16 | 166.8 | 7.0x | to do | | yolov7-tiny_qat.engine* | - | 1 | int8 | 183.1 | 7.6x | to do | | model | NMS | batch<br>tested | precision | FPS | speedup | mAP | |:------------------:|:--------:|:---------------:|:---------:|:----:|:--------:| --- | | yolov7.pt | - | 1 | fp32 | 14.8 | baseline | 51.4% | | yolov7.trt | included | 1 | fp16 | 40.2 | 2.7x | 51.2% | | yolov7.engine | - | 1 | fp16 | 40.8 | 2.7x | 51.2% | | yolov7_qat.engine* | - | 1 | int8 | 57.8 | 3.9x | 51.1% | \* qat表Quantization-aware training,見[Model Quantization Note 模型量化筆記](https://hackmd.io/riYLcrp1RuKHpVI22oEAXA) 測試環境: ``` Module: NVIDIA Jetson AGX Xavier (32 GB ram) Python: 3.8.10 Pytorch: 1.12.0a0+2c916ef.nv22.3 Libraries CUDA: 11.4.315 cuDNN: 8.6.0.166 TensorRT: 8.5.2 L4T: 35.2.1 Jetpack: 5.1 ``` --- 從系統角度分析部署過程中的優化加速方案,可分為模型優化與系統優化。本文以YOLOv7為例,記錄部署流程中,系統優化/推理引擎加速操作部分 ![](https://hackmd.io/_uploads/S18KmhlHn.png =400x) ![](https://hackmd.io/_uploads/r1-PQ2xrn.png =400x) (source:[2022/02。Law-Yao。深度学习模型压缩与优化加速(Model Compression and Acceleration Overview)](https://blog.csdn.net/nature553863/article/details/81083955)) --- # 一、系統優化 ## 0. 使用DeepStream加速資料串流的處理 見[Accelerate multi-streaming cameras with DeepStream and deploy custom (YOLO) models<br>使用DeepStream加速多串流攝影機並部署客製(YOLO)模型](https://hackmd.io/@YungHuiHsu/rJKx-tv4h) ## 1. 使用TensorRT推理引擎 ![](https://hackmd.io/_uploads/HJL0CafI3.png =800x) 根據官網TensorRT的運作流程包含: * Weight & Activation Precision Calibration 模型權重和活化層的精度校準 * 通過將模型量化到INT8,同時保持精度,使吞吐量最大化 * Layer & Tensor Fusion 層和張量融合(計算子融合) * 通過融合內核中的節點,優化GPU記憶體和帶寬的使用 * Kernel Auto-Tuning (內)核自動優化 * 根據目標GPU平台選擇最佳數據層和演算法 * Dynamic Tensor Memory 動態的GPU記憶體分配 * 最大限度地減少記憶體佔用,有效地重新使用張量的記憶體 * Multi-Stream Execution 多數據流執行 * 可擴展的設計,可並行處理多個輸入流 * Time Fusion 時間融合 * 通過動態生成的內核,在時間步驟上優化遞歸神經網絡 ### 為何要使用TensoRT :::info 將模型轉換為ONNX或TensorRT檔案有以下好處: * 跨平台部署:將模型轉換為ONNX或TensorRT檔案可以實現跨平台部署,使得模型可以在不同的硬件和軟件環境中運行。 * 優化性能:ONNX和TensorRT都可以對模型進行優化,使得模型可以在設備上運行得更快,從而提高模型的推論速度和效率。 * 壓縮模型大小:ONNX和TensorRT檔案通常比原始模型檔案更小,從而可以節省存儲空間和網絡帶寬。 * 易於整合:ONNX和TensorRT可以與不同的開發框架和編程語言進行整合,從而為開發者提供更加靈活和易於使用的工具。 然而,將模型轉換為ONNX或TensorRT檔案也有一些缺點: * 轉換過程可能會丟失某些模型細節,從而導致模型的性能下降。 * 某些轉換工具可能不支持所有的模型結構和層類型,從而限制了模型的使用範圍。 TensorRT缺點補充: * 經過infer優化後的模型只能與特定GPU綁定,例如在1080TI上生成的模型無法在2080TI上使用。 * 高版本的TensorRT需要使用高版本的CUDA和驅動程序,因此更換環境是不可避免的。 * TensorRT的推理優化工具infer是閉源的,類似於深度學習的黑盒子,使用起來可能會有些不可控,但幸運的是,TensorRT提供了許多調試工具 ::: 以YOLOv7為例,進行模型轉換。 - [x] 使用Open Neural Network Exchange (ONNX)中轉 - .pt(PyTorch model) > .onnx(ONNX Model) > .trt(TensorRT Engine) - [ ] 不使用 ONNX 格式中轉(待測試) - .pt(PyTorch model) .trt(TensorRT Engine) - 優缺點比較 | 路徑 | 優點 | 缺點 | |:------------------------ |:--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------:|:-------------------------------------------------------------------------------------------------------- | | Pytorch > TensorRT | 可以在不同平台之間實現模型的無縫轉換,例如在使用不同的硬件加速器時,如在NVidia GPU上使用TensorRT,在Intel處理器上使用OpenVINO,在Windows上使用DirectML,這樣就可以充分利用各種硬件的優勢。此外,ONNX Runtime提供了一系列的生產級優化和測試,並進行持續改進,使模型轉換更加高效。 | 由於存在ONNX的中間步驟,整個轉換路徑相比較於直接使用TensorRT而言,可能會增加一些額外的轉換時間和性能損失 | | Pytorch > ONNX >TensorRT | 可以避免額外的轉換時間和性能損失。此外,TensorRT本身具有很好的性能優化能力,可以大大加速深度學習模型的推理過程 | 由於直接使用TensorRT進行轉換,可能會存在一些平台兼容性的問題,不同平台的支持程度可能不同 | 另外要注意的是,TensorRT是針對特定硬體平台與環境做優化,所以==在轉換.trt檔(TensorRT Engine)時,必須在欲部署的環境中進行==。相對,PyTorch > ONNX則不限於特定平台 > The generated plan files are not portable across platforms or TensorRT versions. Plans are specific to the exact GPU model they were built on (in addition to platforms and the TensorRT version) and ==must be re-targeted to the specific GPU in case you want to run them on a different GPU== ### TensoRT序列化與格式(.trt vs .engine) ![](https://hackmd.io/_uploads/BkGc45hH3.png =600x) > [TensorRT学习笔记--序列化(Serialize)和反序列化(Deserialize)](https://blog.csdn.net/weixin_43863869/article/details/128632884) > - 序列化和反序列化的概念 > 將某個對象的信息轉化成可以存儲或者傳輸的信息,這個過程稱為序列化;反序列化是序列化的相反過程,將信息還原為序列化前的狀態; > > - 在Pytorch中,當序列化為torch.save()時,則反序列化可以是torch.load(); > - 在Tensor RT中,為了能夠在interface的時候不需要重覆編譯engine,傾向於將模型序列化成一個能夠永久保存的engine;當 需要interface的時候,只需要通過簡單的反序列化就能夠快速加載序列化保存好的模型engine,節省部署開發的時間。 #### 模型轉換流程與格式 - Nvidia官方指引[Using the TensorRT Runtime API](https://docs.nvidia.com/deeplearning/tensorrt/quick-start-guide/index.html#runtime) - 以pytorch > onnx > TensorRT 為例 ``` model.pt → model.onnx → model.trt → model.engine ``` - TensorRT格式有 `.engine` 與 `model.trt`兩種 - `.engine`格式 - 是TensorRT模型的序列化表示形式,它存儲了經過優化和編譯的模型和相關參數 - DeepStream 所需的讀入格式 - DeepStream在進行視頻流的處理和AI推理時,需要額外的配置和元數據信息,以適應其特定的輸入和輸出格式,以及其他自定義的要求。這些配置信息包括視頻流的解碼和編碼參數、輸入和輸出的格式和分辨率、推理引擎的配置等等 - `model.trt`格式 - 實際上與.engine文件相同,是.engine文件的另一種後綴名稱,可以互相轉換使用 - DeepStream無法直接使用 - 在python環境中可以透過import以下模組讀取二進位的.trt檔,對其序列化後使用 ``` import tensorrt as trt import pycuda.autoinit import pycuda.driver as cuda ``` ### 使用`trtexec`指令進行模型轉換 - `trtexec`是NVIDIA TensorRT的命令行工具,用於執行和測試TensorRT推理引擎。TensorRT是NVIDIA的高性能深度學習推理優化庫,可用於在GPU上加速深度學習模型的推理過程。 簡單的ONNX to TensorRT engine範例,以`yolov7-tiny`為例 ``` # use trtexec to convert ONNX to TensorRT engine /usr/src/tensorrt/bin/trtexec --onnx=yolov7-tiny.onnx --saveEngine=yolov7-tiny-nms.trt --fp16 ``` #### .onnx -> .engine with dynamic-batch。以yolov7為例 - 先從 .pt -> .onnx ``` #install onnx-simplifier not listed in general yolov7 requirements.txt pip3 install onnx-simplifier # Pytorch Yolov7 -> ONNX with grid, EfficientNMS plugin and dynamic batch size python export.py --weights ./yolov7.pt --grid --end2end --dynamic-batch --simplify --topk-all 100 --iou-thres 0.65 --conf-thres 0.35 --img-size 640 640 # ONNX -> TensorRT with trtexec and docker docker run -it --rm --gpus=all nvcr.io/nvidia/tensorrt:22.06-py3 # Copy onnx -> container: docker cp yolov7.onnx <container-id>:/workspace/ ``` - .onnx -> .engine - 這邊有設置dynamic batch ``` # Export with FP16 precision, min batch 1, opt batch 8 and max batch 8 ./tensorrt/bin/trtexec --onnx=yolov7.onnx --minShapes=images:1x3x640x640 --optShapes=images:8x3x640x640 --maxShapes=images:8x3x640x640 --fp16 --workspace=4096 --saveEngine=yolov7-fp16-1x8x8.engine --timingCacheFile=timing.cache ``` - 測試.engine的效能 ``` # Test engine ./tensorrt/bin/trtexec --loadEngine=yolov7-fp16-1x8x8.engine # Copy engine -> host: docker cp <container-id>:/workspace/yolov7-fp16-1x8x8.engine . ``` - 效能輸出結果摘要(Example output of test with RTX 3090) - 預設是以batch=1去執行(可以使用`--batch=`參數指定批次大小) - 以下列輸出結果為例 - 得到 73.5 qps x batch 1 = 73.5 fps @ ~15ms 總延遲時間(latency). - 在這段效能摘要中,Throughput表示每秒處理的輸入數量。在這裡,Throughput為40.7556 qps,表示模型每秒處理約40.76個輸入樣本。 - FPS則表示每秒處理的幀數(Frames Per Second),用於影像和視頻處理場景。在此情況下,qps和FPS是等價的,因為每個輸入樣本被視為一個幀。 ``` [I] === Performance summary === [I] Throughput: 73.4985 qps [I] Latency: min = 14.8578 ms, max = 15.8344 ms, mean = 15.07 ms, median = 15.0422 ms, percentile(99%) = 15.7443 ms ``` ## 環境 我的NVDIA Jetson部署環境 :::spoiler ``` Platform Machine: aarch64 Linux Ubuntu 20.04 focal Release: 5.10.104-tegra Python: 3.8.10 pytorch: 1.12.0a0+2c916ef.nv22.3 Libraries CUDA: 11.4.315 cuDNN: 8.6.0.166 TensorRT: 5.1 VPI: 2.2.4 Vulkan: 1.3.204 OpenCV: 4.5.4 with CUDA: NO Hardware Model: Jetson-AGX Module: NVIDIA Jetson AGX Xavier (32 GB ram) L4T: 35.2.1 Jetpack: 5.1 ``` ps: 使用` dpkg -l |grep -i tensor`檢視,我的TensoRT版本應該是8.5.2-1,`jtop`可能誤抓到`nvidia-tensorrt`的資訊 ``` ii nvidia-tensorrt 5.1-b147 arm64 NVIDIA TensorRT Meta Package ii python3-libnvinfer 8.5.2-1+cuda11.4 arm64 Python 3 bindings for TensorRT ii tensorrt 8.5.2.2-1+cuda11.4 arm64 Meta package for TensorRT ii tensorrt-libs 8.5.2.2-1+cuda11.4 arm64 Meta package for TensorRT runtime libraries ``` ::: ## 相依套件安裝 :::spoiler - Problem: JetPack5.1版預設的pythonbiding問題 在安裝python的tensorrt套件時會出問題,因為官方JetPack 的pythonbiding設定有問題 ,需要更新"nvidia-jetpack"套件 ``` $ sudo apt update $ sudo apt list --upgradable $ sudo apt upgrade $ sudo apt install nvidia-jetpack ``` 參考資訊:[How to use tensorrt in python of AGX Xavier JetPack5.1](https://forums.developer.nvidia.com/t/how-to-use-tensorrt-in-python-of-agx-xavier-jetpack5-1/245195) ![](https://hackmd.io/_uploads/SkfOBmPV3.png) ::: ### 安裝python的TensorRT套件 目前Linux環境系統安裝的是TensorRT: 5.1與CUDA: 11.4.315,Python的TensorRT套件需要與系統環境一致,使用`nvidia-pyindex` package 會自動幫你根據系統資訊抓取適合版本 :::spoiler ``` pip install --upgrade setuptools pip --user pip install nvidia-pyindex pip install onnx_graphsurgeon pip install onnx pip install onnxruntime pip install --ignore-installed PyYAML pip install --upgrade nvidia-tensorrt pip install pycuda pip install protobuf<4.21.3 pip install onnxruntime pip install onnx>=1.9.0 pip install onnx-simplifier>=0.3.6 --user ``` ::: ## 執行流程 ### 1. 依據YOLOv7TRT from Linaom1214/tensorrt-python.git 參考[https://github.com/WongKinYiu/yolov7/YOLOv7TRT.ipynb](https://colab.research.google.com/gist/AlexeyAB/fcb47ae544cf284eb24d8ad8e880d45c/yolov7trtlinaom.ipynb#scrollTo=DiwRxG8MKHiW) ``` wget https://github.com/WongKinYiu/yolov7/releases/download/v0.1/yolov7-tiny.pt python export.py --weights yolov7-tiny.pt --grid --include-nms git clone https://github.com/Linaom1214/tensorrt-python.git python ./tensorrt-python/export.py -o yolov7-tiny.onnx -e yolov7-tiny-nms.trt -p fp16 # Or use trtexec to convert ONNX to TensorRT engine # /usr/src/tensorrt/bin/trtexec --onnx=yolov7-tiny.onnx \ --saveEngine=yolov7-tiny-nms.trt --fp16 ``` ### YOLOv7TRT.ipynb ``` # Download YOLOv7 code git clone https://github.com/WongKinYiu/yolov7 cd yolov7 ! Download trained weights wget https://github.com/WongKinYiu/yolov7/releases/download/v0.1/yolov7-tiny.pt ``` #### PyTorch > ONNX - `--include-nms` - 會將NMS處裡包含進模型架構內,與開啟`--end2end`進行同樣操作 - ('--end2end', action='store_true', help='export end2end onnx') - ('--include-nms', action='store_true', help='export end2end onnx') - 降為fp16精度的操作建議在下一步驟執行,使用TensorRT進行優化 ``` python3 export.py --weights ./yolov7-tiny.pt --grid --simplify --include-nms ``` #### 切換至github.com/Linaom1214/tensorrt-python ``` cd ../ git clone https://github.com/Linaom1214/tensorrt-python.git cd tensorrt-python ``` #### ONNX > TensorRT Engine - 注意這邊是把包含有NMS層的ONNX Model轉為TensorRT Engine(.trrt) - `fp16` 指定float16的精度 ``` python3 export.py -o /content/yolov7/yolov7-tiny.onnx -e ./yolov7-tiny-nms.trt -p fp16 ``` - 或直接在terminal使用`trtexec`指令 ``` # Or use trtexec to convert ONNX to TensorRT engine /usr/src/tensorrt/bin/trtexec --onnx=yolov7-tiny.onnx --saveEngine=yolov7-tiny-nms.trt --fp16 ``` #### 用`netron`檢視模型架構 - 在terminal執行`netron yolov7-tiny.onnx` - 注意這邊模型已經納入了NMS層 - 預設的`batch` = 1,如果要加快推理速度進行批次處理,前面的步驟應該指定啟用動態批次 `--dynamic-batch` ![](https://hackmd.io/_uploads/H1Xet4wV3.png) #### 將.trt的讀入流程封裝為class 參考原程式碼加上註解 :::spoiler ```python= class BaseEngine(object): def __init__(self, engine_path, imgsz=(640,640)): """ 初始化模型引擎。 :param engine_path: 模型引擎的路徑。 :param imgsz: 影像的大小,預設為 (640, 640)。 """ self. self.imgsz = imgsz self.mean = None self.std = None # 目標類別的名稱列表。 self.class_names = [ 'person', 'bicycle', 'car', 'motorcycle', 'airplane', 'bus', 'train', 'truck', 'boat', 'traffic light', 'fire hydrant', 'stop sign', 'parking meter', 'bench', 'bird', 'cat', 'dog', 'horse', 'sheep', 'cow', 'elephant', 'bear', 'zebra', 'giraffe', 'backpack', 'umbrella', 'handbag', 'tie', 'suitcase', 'frisbee', 'skis', 'snowboard', 'sports ball', 'kite', 'baseball bat', 'baseball glove', 'skateboard', 'surfboard', 'tennis racket', 'bottle', 'wine glass', 'cup', 'fork', 'knife', 'spoon', 'bowl', 'banana', 'apple', 'sandwich', 'orange', 'broccoli', 'carrot', 'hot dog', 'pizza', 'donut', 'cake', 'chair', 'couch', 'potted plant', 'bed', 'dining table', 'toilet', 'tv', 'laptop', 'mouse', 'remote', 'keyboard', 'cell phone', 'microwave', 'oven', 'toaster', 'sink', 'refrigerator', 'book', 'clock', 'vase', 'scissors', 'teddy bear', 'hair drier', 'toothbrush' ] logger = trt.Logger(trt.Logger.WARNING) # 初始化 TensorRT 插件。 trt.init_libnvinfer_plugins(logger,'') runtime = trt.Runtime(logger) # 讀取模型引擎。 with open(engine_path, "rb") as f: serialized_engine = f.read() engine = runtime.deserialize_cuda_engine(serialized_engine) self.context = engine.create_execution_context() self.inputs, self.outputs, self.bindings = [], [], [] self.stream = cuda.Stream() # 為每個 binding 創建 host 和 device 記憶體 for binding in engine: size = trt.volume(engine.get_binding_shape(binding)) # 取得 binding 的尺寸 dtype = trt.nptype(engine.get_binding_dtype(binding)) # 取得 binding 的數據類型 host_mem = cuda.pagelocked_empty(size, dtype) # 建立用於主機記憶體的 Numpy 陣列 device_mem = cuda.mem_alloc(host_mem.nbytes) # 分配裝置記憶體 self.bindings.append(int(device_mem)) # 將裝置記憶體加入到 bindings 清單中 if engine.binding_is_input(binding): # 判斷 binding 是否是輸入 # 如果是輸入,將主機和裝置記憶體加入到 inputs 清單中 self.inputs.append({'host': host_mem, 'device': device_mem}) else: # 如果是輸出,將主機和裝置記憶體加入到 outputs 清單中 self.outputs.append({'host': host_mem, 'device': device_mem}) def infer(self, img): # 推理函式,接收一張圖片作為參數 self.inputs[0]['host'] = np.ravel(img) # 將圖片攤平後存入 inputs[0]['host'] 中 # 將資料傳輸到 GPU for inp in self.inputs: cuda.memcpy_htod_async(inp['device'], inp['host'], self.stream) # 執行推理 self.context.execute_async_v2( bindings=self.bindings, stream_handle=self.stream.handle) # 從 GPU 中取得輸出 for out in self.outputs: cuda.memcpy_dtoh_async(out['host'], out['device'], self.stream) # synchronize stream 等待傳輸完成 self.stream.synchronize() # 將輸出資料放入 data 清單中並返回** data = [out['host'] for out in self.outputs] return data def inference(self, img_path, conf=0.25): origin_img = cv2.imread(img_path) origin_img = cv2.cvtColor(origin_img, cv2.COLOR_BGR2RGB) img, ratio = preproc(origin_img, self.imgsz, self.mean, self.std) num, final_boxes, final_scores, final_cls_inds = self.infer(img) final_boxes = np.reshape(final_boxes, (-1, 4)) num = num[0] # 獲取物體個數 if num >0: # 將檢測結果還原至原圖大小,截取前 num 個物體 final_boxes, final_scores, final_cls_inds = final_boxes[:num]/ratio, final_scores[:num], final_cls_inds[:num] # 可視化檢測結果 origin_img = vis(origin_img, final_boxes, final_scores, final_cls_inds, conf=conf, class_names=self.class_names) origin_img = cv2.cvtColor(origin_img, cv2.COLOR_RGB2BGR) return origin_img def get_fps(self): # warmup import time img = np.ones((1,3,self.imgsz[0], self.imgsz[1])) img = np.ascontiguousarray(img, dtype=np.float32) for _ in range(20): _ = self.infer(img) t1 = time.perf_counter() _ = self.infer(img) print(1/(time.perf_counter() - t1), 'FPS') # 以下略 ``` ::: #### 推理 - 在ipynb執行範例程式: ``` pred = BaseEngine(engine_path='/content/tensorrt-python/yolov7-tiny-nms.trt') origin_img = pred.inference('/content/yolov7/inference/images/horses.jpg') ``` ``` import matplotlib.pyplot as plt plt.figure(figsize=(10,10)) plt.imshow(origin_img[:, :, ::-1]) ``` ![](https://hackmd.io/_uploads/HJcBVVwVh.png =400x) - 或在terminal使用`Linaom1214/tensorrt-python/trt.py` ``` python3 trt.py -e yourmodel.trt -i src/1.jpg -o 1-inference.jpg --end2end ``` - Linaom1214/TensorRT-For-YOLO-Series/blob/main/utils/utils.py 如果沒有`--end2end`指令,則需要在推理後`self.postprocess`進行NMS的後處理,理論上將NMS納入模型結構中可以加速處理 ``` class BaseEngine(object): # ... 略 ... def inference(self, img_path, conf=0.5, end2end=False): origin_img = cv2.imread(img_path) img, ratio = preproc(origin_img, self.imgsz, self.mean, self.std) data = self.infer(img) if end2end: num, final_boxes, final_scores, final_cls_inds = data final_boxes = np.reshape(final_boxes/ratio, (-1, 4)) dets = np.concatenate([final_boxes[:num[0]], np.array(final_scores)[:num[0]].reshape(-1, 1), np.array(final_cls_inds)[:num[0]].reshape(-1, 1)], axis=-1) else: predictions = np.reshape(data, (1, -1, int(5+self.n_classes)))[0] dets = self.postprocess(predictions,ratio) if dets is not None: final_boxes, final_scores, final_cls_inds = dets[:, :4], dets[:, 4], dets[:, 5] frame = vis(frame, final_boxes, final_scores, final_cls_inds, conf=conf, class_names=self.class_names) ``` --- --- ## TensorRT轉檔啟用動態批次 Dynamic_batch 在部署模型TensorRT轉檔時,可以選擇使用Fixed batch或Dynamic batch。使用Fixed batch意味著將模型的輸入形狀固定為一個特定的batch大小,因此無法處理其他batch大小的輸入,而使用Dynamic batch可以適應不同大小的batch輸入。Dynamic batch具有以下幾個優點: * 靈活性:使用Dynamic batch可以在不更改模型部署的情況下,輕鬆地支持不同大小的輸入,這對於實際應用非常有用,例如,當輸入大小不一致時,可以避免在重新編譯模型時浪費時間和資源。 * 效能:在TensorRT中,當使用Dynamic batch時,可以在編譯時動態生成更高效的計算圖,而不需要將計算圖固定為特定的batch大小。因此,可以在Dynamic batch下達到與Fixed batch相同甚至更好的效能,並在執行時更加高效。 如果輸入資料為多個攝影機的多串流MultiStream,那讓模型設定為動態批次是很有必要的,這部分也可以串接[DeepStream的PipeLine](https://hackmd.io/8_3tbvllTPGZopXnKAf6WA?both) #### 使用trtexec進行動態尺寸(dynamic shape)轉換 - 動態尺度支持NCHW中的N、H以及W,也就是batch、高以及寬 - 建議僅使用batch,動態寬、高的計算非常耗資源 在轉換動態模型的時候需要額外指定三個維度即可(最小、最優、最大)。 - 動態批次(1、8、16)轉換範例 ``` /usr/src/tensorrt/bin/trtexec --explicitBatch --onnx=demo.onnx \ --minShapes=input:1x3x256x256 \ --optShapes=input:8x3x256X256 \ --maxShapes=input:16x3x256X256\ --saveEngine=demo.trt ``` - 如果後面串接的stream數量固定,'--minShapes'、'--optShapes'、'--maxShapes'都設為一致,似乎可以開啟NV的[DLA (Deep Learning Accelerator)](https://docs.nvidia.com/deeplearning/tensorrt/developer-guide/index.html#dla_topic)加速 ### [`yolov7/export.py`](https://github.com/WongKinYiu/yolov7/blob/main/export.py) 參數解釋 #### --dynamic-batch 和 --dynamic - `--dynamic-batch` 參數用於啟用動態批處理的 ONNX 導出 - 設定動態批次 - 當這個參數被設置為 `--dynamic-batch` 時,ONNX 導出將生成一個動態批處理的模型,適用於使用 TensorRT 或 ONNX-Runtime 運行時進行推理。動態批處理意味着模型可以接受不同大小的批次作為輸入,而不需要預先指定批處理大小。導出的模型將使用一個特殊的符號 batch 來表示批處理維度。 - `--dynamic` 參數用於啟用動態軸的 ONNX 導出 - 設定動態尺寸 - 如如果同时启用了`--end2end`选项,该参数将被忽略。 - 當這個參數被設置為 `--dynamic` 時,ONNX 導出將生成一個動態軸的模型,適用於一些需要在運行時調整輸入形狀的場景。動態軸意味着模型可以處理不同大小的輸入圖像尺寸,而不需要預先指定固定的圖像尺寸。這對於一些需要運行時靈活調整輸入形狀的應用非常有用。 需要注意的是,--dynamic 參數和 --dynamic-batch 參數是互斥的,不能同時使用。如果同時指定了這兩個參數,--dynamic 參數會被忽略,僅導出動態批處理的模型。根據你的需求,可以選擇使用適合的參數來導出相應的 ONNX 模型。 ##### [`yolov7/export.py`](https://github.com/WongKinYiu/yolov7/blob/main/export.py#124) - 當`opt.dynamic_batch`啟用時 - opt.batch_size 會以字串'batch'取代 - 在輸出張量的第0維度 output_axes,也會以'batch'取代 ```python=124 dynamic_axes = None if opt.dynamic: dynamic_axes = {'images': {0: 'batch', 2: 'height', 3: 'width'}, # size(1,3,640,640) 'output': {0: 'batch', 2: 'y', 3: 'x'}} if opt.dynamic_batch: opt.batch_size = 'batch' dynamic_axes = { 'images': { 0: 'batch', }, } if opt.end2end and opt.max_wh is None: output_axes = { 'num_dets': {0: 'batch'}, 'det_boxes': {0: 'batch'}, 'det_scores': {0: 'batch'}, 'det_classes': {0: 'batch'}, } else: output_axes = { 'output': {0: 'batch'}, } dynamic_axes.update(output_axes) ``` - 使用`netron`檢視轉出的.onnx檔 | dynamic_batch | fix_batch(b=1) | | --------------------------------------------- | --------------------------------------------- | | ![](https://hackmd.io/_uploads/SyrhnGzBn.png) | ![](https://hackmd.io/_uploads/HyvpnMzrh.png) | # 二、模型加速-模型壓縮 在部署階段的模型加速方案包括:1) 模型壓縮(Model Compression),包括模型剪枝(Model Pruning)、模型量化(Model Quantization)、模型蒸餾(Model Distillation)、低秩因式分解(Low-Rank Factorization)等方法 ![](https://hackmd.io/_uploads/ByjdtdgS2.png =400x) (source:[2020。A comprehensive survey on model compression and acceleration](https://www.researchgate.net/figure/Different-types-of-compression-techniques-for-DNN-and-traditional-ML-methods-Here-the_fig1_339129502)) - 4種常見型壓縮技巧簡介:[4 Popular Model Compression Techniques Explained](https://xailient.com/blog/4-popular-model-compression-techniques-explained/#3_The_knowledge_distillation_technique) 以下針對在Nvidia平台提供的通用加速工具提供範例,包括: 1. 型轉換階段(.pt > .trt)啟用啟用動態批次 Dynamic_batch 2. 模型量化 Quantization ## 模型量化 Quantization 模型量化透過降低精度和簡化模型參數,大幅減少模型的體積和計算負擔,並提高模型在特定硬體上的效率。 ### 原理部分見[Model Quantization Note 模型量化筆記](https://hackmd.io/riYLcrp1RuKHpVI22oEAXA) ### 實作參考[NVIDIA-AI-IOT/yolo_deepstream/tree/main/yolov7_qat](https://github.com/NVIDIA-AI-IOT/yolo_deepstream/tree/main/yolov7_qat) :::info ~~訓練時,也需要採用arm64架構的平台,否則在 onnx轉TensorRT階段會報錯~~ [TRT] DLA requests all profiles have same min, max, and opt value. All dla layers are falling back to GPU ::: 採用PTQ後再接QAT的yolov7(int8)模型,在多批次(batch=16)時,處理速度(FPS)比fp16可以再提升1.82x(264/145) ![](https://hackmd.io/_uploads/SkvLaIEUn.png =500x) - 測試環境 - Jetson AGX Orin 64GB(PowerMode:MAXN + GPU-freq:1.3GHz + CPU:12-core-2.2GHz) - end-to-end performance of processing 1080p videos #### tensorrt_yolov7的 Dynamic batch設定 - 參照[NVIDIA-AI-IOT/yolo_deepstream/tensorrt_yolov7](https://github.com/NVIDIA-AI-IOT/yolo_deepstream/tree/main/tensorrt_yolov7#prepare-tensorrt-engines) 在將onnx模型格式轉換為TensorRT Engine時,如果需要設定動態批次,記得做以下更改,否則在DeepStream設定檔讀入"batch-size">1時,模型會無法載入 將以下指令(batch size預設為1) ``` # int8 QAT model, the onnx model with Q&DQ nodes /usr/src/tensorrt/bin/trtexec --onnx=yolov7qat.onnx --saveEngine=yolov7QAT.engine --fp16 --int8 ``` 替換為(明確指定批次範圍): ``` # int8 QAT model, the onnx model with Q&DQ nodes and dynamic-batch /usr/src/tensorrt/bin/trtexec --onnx=yolov7qat.onnx \ --minShapes=images:1x3x640x640 \ --optShapes=images:12x3x640x640 \ --maxShapes=images:16x3x640x640 \ --saveEngine=yolov7QAT.engine --fp16 --int8 ``` - 在TensorRT8.5.2 + AGX Xavier(32G)實測 - 測試指令:`/usr/src/tensorrt/bin/trtexec --loadEngine=model.engine --verbose ` - 使用QAT訓練後,比起fp16的TensorRT Engine加速大約1.4倍 | precision | Latency<br>(mean:ms) | trtexec FPS | $mAP^{val}_{0.5:0.95}$ | | ------------- | :--------------------: | :-----------: | --- | | FPS16 | 24.90 | 40.76 | 0.5124 | | Int8(PTQ/QAT) | 17.87 | 57.82 | 0.5113 | \* network input resolution: 3x640x640 \* $mAP^{test}_{0.5:0.95}$ for yolov7.pt : 0.514 - 測試環境 ``` TensorRT Version: 8.5.2 GPU Type: Jetson AGX Xavier(32G) 系統安裝在外接64G隨身碟 Nvidia Driver Version: CUDA Version: 11.4.315 CUDNN Version: 8.6.0.166 ``` --- # 參考資料 ## NVIDIA TensorRT官方資源與文件 #### [Developer Guide :: NVIDIA Deep Learning TensorRT Documentation](https://docs.nvidia.com/deeplearning/tensorrt/developer-guide/index.html#working-with-int8) #### [TensorRT/quickstart at main · NVIDIA/TensorRT (github.com)](https://github.com/NVIDIA/TensorRT/tree/main/quickstart) - 內有多個範例檔案可參考 #### [Nvidia官方中文教學影片](https://space.bilibili.com/1320140761/channel/collectiondetail?sid=303289) --- ## TensorRT相關 #### [2022/10。OLDPAN。TensorRT详细入门指北,如果你还不了解TensorRT,过来看看吧!](https://zhuanlan.zhihu.com/p/371239130) 目前中文最詳盡的介紹,作者有豐富完整的部署經驗、值得一讀 #### [2021/09。shouxieai。详解TensorRT的C++/Python高性能部署,实战应用到项目](https://www.bilibili.com/video/BV1Xw411f7FW/) - https://github.com/shouxieai/tensorRT_cpp :::spoiler ##### 動態batch,和動態寬高的處理方式 - 動態batch 源自`tensorRT`編譯時對batch的處理,若靜態batch則意味著無論你多少圖,都按照固定大小batch推理。耗時是固定的 1. 導出模型時,注意`view`操作不能固定batch維度數值,通常寫`-1` 2. 導出模型時,通常可以指定`dynamic_axes`,實際上不指定也沒關系 - 動態寬高 源自onnx導出時指定的寬高是固定的,trt編譯時也得到固定大小引擎,此時若你想得到一個不同大小的trt引擎時,就需要動態寬高的存在。而使用trt的動態寬高會帶來太多不必要的複雜度,這裏使用中間方案,編譯時修改onnx輸入實現相對動態,避免重回pytorch再做導出 1. 不建議使用`dynamic_axes`指定`O`以外的維度為動態,這樣帶來的複雜度太高,並且存在有的layer不支持。這種需求也不常用,性能也很差 2. 真正需要的,是onnx文件已經導出, 但是輸入shape固定了,此時希望修改這個onnx的輸入shape 3. 步驟一: 使用`TRT::compile`函數的inputsDimsSetup參數重定義輸入的shape 4. 步驟二: 使用`TRT::set_layer_hook_reshape`鉤子動態修改`reshape`的參數實現適配 ##### 如何正確的導出ONNX Model 1. 對於任何用到shape、size返回值的參數時,例如:`tensor.view(tensor.size(0), -1)`這類操作,避免直接使用tensor.size的返回值,而是加上int轉換, `tensor.view(int(tensor.size(0)),-1)` 2. 對於`nn.Upsample`或`nn.functional.interpolate`函數,使用`scale_factor`指定倍率,而不是使用size參數指定大小 3. 對於reshape、view操作時,-1的指定請放到batch維度。其他維度可以計算出來即可。batch維度禁止指定為大於-1的明確數字 4. `torch.onnx.export`指定`dynamic_axes`參數,並且只指定batch維度,不指定其他維度。我們只需要動態batch,相對動態的寬高有其他方案 ##### 高性能方案重點: ##### 單模型推理時的性能問題: 1、盡量使得GPU高密集度運行,避免出現CPU、GPU相互交換運行 2、盡可能使tensorRT運行多個batch 數據。與第一點相合 3、預處理盡量cuda化,例如圖像需要做normalize、reisze、warpaffine、 bgr2rgb等,在這裏,采用cuda核實現warpaffine+normalize等操作,集中 在一起性能好 4、後處理盡量cuda化,例如decode、nms等。在這裏用cuda核實現了 decode和nms 5、善於使用cudaStream,將操作加入流中,采用異步操作避免等待 6、內存覆用 ##### 系統級別的性能問題: 1、如何實現盡可能讓單模型使用多batch,此時future、promise就是很好 的工具 2、時序圖要盡可能優化,分析並繪制出來,不必的等待應該消除,同樣 是promise、future帶來的好處 3、尤其是圖像讀取和模型推理最常用的場景下,可以分析時序圖,緩存 一幀的結果,即可實現幀率的大幅提升 - 沒有緩存的時序圖 - time/frame = capture + inference - 讀取、推理以串行(serial)及隊列(Queue)方式執行 ![](https://hackmd.io/_uploads/H1aQPwuN3.png =450x) - 有緩存的時序圖 - time/frame = max(capture + inference) - 讀取與推理時間重疊、異步執行(asynchronous) ![](https://hackmd.io/_uploads/ByH3PDONn.png =450x) ::: ## 模型部署概論 #### [2022。周弈帆。模型部署入门教程](https://zhuanlan.zhihu.com/p/477743341) 簡單易懂,可以瞭解入門概念 #### [2020。Paul Bridger。Object Detection from 9 FPS to 650 FPS in 6 Steps](https://paulbridger.com/posts/video-analytics-pipeline-tuning/) ![](https://hackmd.io/_uploads/HypzUguwh.png =500x) #### [2020。Paul Bridger。Object Detection at 1840 FPS with TorchScript, TensorRT and DeepStream](https://paulbridger.com/posts/video-analytics-deepstream-pipeline/) ![](https://hackmd.io/_uploads/SJNbIedPh.png =700x) #### [2021。Graph Compilers for Deep Learning: Definition, Pros & Cons, and Popular Examples](https://deci.ai/blog/graph-compilers/) - 型推理的圖編譯器(Graph Compilers )原理解釋與補充 圖編譯器壓縮和優化深度神經網絡,以實現更快和更有效的內存推理。它們是深度學習推理加速堆棧中軟件組件的一部分,同時還有低級別的庫和深度學習框架。圖編譯器與計算圖一起工作,它代表了神經網絡及其張量操作。 圖編譯器使用的優化技術包括圖重寫、操作融合以及將操作分配給硬件 ![](https://hackmd.io/_uploads/HyxdT6M82.png) DL Inference Acceleration Stack