亚欧色一区w666天堂,色情一区二区三区免费看,少妇特黄A片一区二区三区,亚洲人成网站999久久久综合,国产av熟女一区二区三区

  • 發布文章
  • 消息中心
點贊
收藏
評論
分享
原創

TensorRT部署簡介

2023-06-30 07:48:51
128
0

一、TensorRT版本部署簡介

模(mo)(mo)型(xing)訓練好之后,就到(dao)了AI模(mo)(mo)型(xing)實際應用(yong)的(de)最后一環(huan) --- 模(mo)(mo)型(xing)部署(shu)(shu)。模(mo)(mo)型(xing)部署(shu)(shu)是(shi)將已經訓練好的(de)模(mo)(mo)型(xing),部署(shu)(shu)到(dao)實際的(de)應用(yong)場(chang)景中,所以(yi)模(mo)(mo)型(xing)部署(shu)(shu)更多(duo)是(shi)工程化的(de)過程,就是(shi)解決實際的(de)模(mo)(mo)型(xing)應用(yong)問題,本文采用(yong)NVIDIA推出(chu)的(de)針對GPU卡的(de)TensorRT對onnx模(mo)(mo)型(xing)轉換(huan)部署(shu)(shu)。

二、模型壓縮
模型壓縮是對已經訓練好的深度模型進行精簡,進而得到一個輕量且準確率相當的網絡,壓縮后的網絡具有更小的結構和更少的參數,可以有效降低計算和存儲開銷,便于部署在受限的硬件環境中。
訓練的(de)(de)(de)(de)時候因為要(yao)保證(zheng)前(qian)后向傳播,每次梯度(du)的(de)(de)(de)(de)更新(xin)是很(hen)微小的(de)(de)(de)(de),這個時候需(xu)要(yao)相對較高的(de)(de)(de)(de)精(jing)度(du),一(yi)般(ban)(ban)來說(shuo)需(xu)要(yao)float型(xing)(xing),如FP32,32位的(de)(de)(de)(de)浮點型(xing)(xing)來處(chu)理(li)數(shu)據,但是在推(tui)理(li)(Inference)的(de)(de)(de)(de)時候,對精(jing)度(du)的(de)(de)(de)(de)要(yao)求沒(mei)有(you)那么(me)高,很(hen)多研究表明(ming)可以(yi)用(yong)低精(jing)度(du),如半長(16)的(de)(de)(de)(de)float,即(ji)FP16,也可以(yi)用(yong)8位的(de)(de)(de)(de)整型(xing)(xing)(INT8)來做推(tui)理(li)(Inference)。所以(yi),一(yi)般(ban)(ban)來說(shuo),在模型(xing)(xing)部署時會對模型(xing)(xing)進行壓縮。模型(xing)(xing)壓縮方法有(you):蒸餾,剪枝(zhi),量(liang)化(hua)等。

三、模型格式轉換
首先研(yan)究(jiu)員通(tong)過各(ge)種(zhong)訓(xun)(xun)(xun)(xun)練(lian)(lian)(lian)框(kuang)架訓(xun)(xun)(xun)(xun)練(lian)(lian)(lian)好的(de)(de)(de)模(mo)(mo)型(xing)一(yi)般(ban)都需要進(jin)行模(mo)(mo)型(xing)格(ge)(ge)(ge)式(shi)(shi)適配(pei),模(mo)(mo)型(xing)訓(xun)(xun)(xun)(xun)練(lian)(lian)(lian)大家(jia)可以(yi)(yi)選擇各(ge)種(zhong)不(bu)(bu)同的(de)(de)(de)訓(xun)(xun)(xun)(xun)練(lian)(lian)(lian)框(kuang)架,例(li)如TensorFlow,Pytorch,PaddlePaddle,Caffe等(deng)等(deng)一(yi)系列的(de)(de)(de)開源框(kuang)架,這么多(duo)不(bu)(bu)同的(de)(de)(de)訓(xun)(xun)(xun)(xun)練(lian)(lian)(lian)框(kuang)架他們(men)訓(xun)(xun)(xun)(xun)練(lian)(lian)(lian)出來的(de)(de)(de)模(mo)(mo)型(xing)格(ge)(ge)(ge)式(shi)(shi)都有各(ge)自的(de)(de)(de)標準,各(ge)不(bu)(bu)相同,部(bu)署(shu)要解(jie)決(jue)的(de)(de)(de)第一(yi)個(ge)(ge)問題就是(shi)要適配(pei)各(ge)種(zhong)不(bu)(bu)同的(de)(de)(de)模(mo)(mo)型(xing)格(ge)(ge)(ge)式(shi)(shi)。但是(shi)如果要一(yi)個(ge)(ge)個(ge)(ge)訓(xun)(xun)(xun)(xun)練(lian)(lian)(lian)框(kuang)架去適配(pei)格(ge)(ge)(ge)式(shi)(shi),工作量太(tai)大,也不(bu)(bu)適合擴展,所以(yi)(yi)微軟聯合Facebook等(deng)大廠推出一(yi)種(zhong)中間格(ge)(ge)(ge)式(shi)(shi)ONNX,希望能解(jie)決(jue)多(duo)種(zhong)模(mo)(mo)型(xing)格(ge)(ge)(ge)式(shi)(shi)適配(pei)的(de)(de)(de)問題,就是(shi)無論(lun)是(shi)什么訓(xun)(xun)(xun)(xun)練(lian)(lian)(lian)框(kuang)架訓(xun)(xun)(xun)(xun)練(lian)(lian)(lian)出來的(de)(de)(de)模(mo)(mo)型(xing)格(ge)(ge)(ge)式(shi)(shi),最終(zhong)都是(shi)用(yong)ONNX格(ge)(ge)(ge)式(shi)(shi)來進(jin)行部(bu)署(shu)。所以(yi)(yi)一(yi)般(ban)模(mo)(mo)型(xing)部(bu)署(shu)可以(yi)(yi)跑的(de)(de)(de)第一(yi)步要解(jie)決(jue)的(de)(de)(de)問題就是(shi)模(mo)(mo)型(xing)格(ge)(ge)(ge)式(shi)(shi)轉換。

1.初步了解部署內容

在(zai)軟(ruan)件工(gong)程(cheng)中,部署(shu)指把開發完畢(bi)的(de)軟(ruan)件投入使用的(de)過程(cheng),包括環境(jing)配(pei)置、軟(ruan)件安裝(zhuang)等步(bu)驟。類(lei)似地,對(dui)于深度學(xue)習模(mo)型(xing)來說,模(mo)型(xing)部署(shu)指讓(rang)訓練好的(de)模(mo)型(xing)在(zai)特定環境(jing)中運行的(de)過程(cheng)。相比(bi)于軟(ruan)件部署(shu),模(mo)型(xing)部署(shu)會面臨更(geng)多的(de)難題:

(1)運行模(mo)型所需的環(huan)境(jing)難以配(pei)置。深度學習模(mo)型通常是(shi)由一些(xie)框架(jia)編寫,比如 PyTorch、TensorFlow。由于框架(jia)規模(mo)、依(yi)賴環(huan)境(jing)的限制,這(zhe)些(xie)框架(jia)不適合在手機、開發(fa)板等生產環(huan)境(jing)中(zhong)安(an)裝。

(2)深度學習模型(xing)(xing)的(de)(de)結(jie)構通常比較龐大(da),需要(yao)大(da)量(liang)的(de)(de)算力才能滿足實時運行(xing)的(de)(de)需求。模型(xing)(xing)的(de)(de)運行(xing)效率需要(yao)優化(hua)。

2.創建關于pytorch內容

     用 PyTorch 實現一個超(chao)分辨率模型(xing),并(bing)把模型(xing)部署到 ONNX Runtime 這(zhe)個推理引擎上(shang)。首先,需要創建一個有 PyTorch 庫的(de)(de) Python 編程(cheng)環境。如果你的(de)(de) PyTorch 環境還沒(mei)有裝(zhuang)好,可以參考官方的(de)(de)入門教程(cheng)。在(zai)這(zhe)里推薦使用 conda 來管理 Python 庫。

   在(zai)介紹 ONNX 之前,先(xian)從本質上來認識一(yi)下神經網絡的(de)(de)結構。神經網絡實際上只是描(miao)述(shu)了數據(ju)計(ji)算(suan)的(de)(de)過程,其(qi)結構可(ke)以用(yong)計(ji)算(suan)圖(tu)(tu)表示。比(bi)如(ru)(ru) a+b 可(ke),為(wei)了加(jia)速計(ji)算(suan),一(yi)些框架(jia)會使用(yong)對(dui)神經網絡“先(xian)編譯,后執行”的(de)(de)靜態圖(tu)(tu)來描(miao)述(shu)網絡。靜態圖(tu)(tu)的(de)(de)缺(que)點是難以描(miao)述(shu)控(kong)(kong)制流(liu)(比(bi)如(ru)(ru) if-else 分支(zhi)語(yu)句和 for 循(xun)(xun)環語(yu)句),直接對(dui)其(qi)引入控(kong)(kong)制語(yu)句會導致(zhi)產生(sheng)不(bu)同的(de)(de)計(ji)算(suan)圖(tu)(tu)。比(bi)如(ru)(ru)循(xun)(xun)環執行 n 次 a=a+b,對(dui)于不(bu)同的(de)(de) n,會生(sheng)成(cheng)不(bu)同的(de)(de)計(ji)算(suan)圖(tu)(tu)。

ONNX (Open Neural Network Exchange)是 Facebook 和(he)微(wei)軟在2017年共同發布的(de),用于標準描(miao)述計算圖(tu)的(de)一種(zhong)格式。目前,在數家機構的(de)共同維護下,ONNX 已(yi)經對接了(le)多種(zhong)深度學(xue)習(xi)框架和(he)多種(zhong)推(tui)理引擎。因此,ONNX 被當(dang)成了(le)深度學(xue)習(xi)框架到推(tui)理引擎的(de)橋梁,就(jiu)像編譯器的(de)中間(jian)語言一樣。由于各框架兼容(rong)性(xing)不一,通常只(zhi)用 ONNX 表(biao)示(shi)更容(rong)易部(bu)署的(de)靜態圖(tu)。

用下面的代碼來把 PyTorch 的模型轉換成 ONNX 格式的模型:

= torch.randn(1, 3, 256, 256) 

 with torch.no_grad(): 

    torch.onnx.export( 

        model, 

        x, 

        "srcnn.onnx", 

        opset_version=11, 

        input_names=['input'], 

        output_names=['output'])

其(qi)中,torch.onnx.export 是 PyTorch 自帶的(de)(de)(de)(de)(de)(de)(de)(de)把模(mo)(mo)型(xing)(xing)轉(zhuan)換(huan)(huan)成 ONNX 格(ge)式(shi)的(de)(de)(de)(de)(de)(de)(de)(de)函數(shu)(shu)(shu)。先看一(yi)(yi)下前(qian)三個必選參(can)數(shu)(shu)(shu):前(qian)三個參(can)數(shu)(shu)(shu)分別是要(yao)轉(zhuan)換(huan)(huan)的(de)(de)(de)(de)(de)(de)(de)(de)模(mo)(mo)型(xing)(xing)、模(mo)(mo)型(xing)(xing)的(de)(de)(de)(de)(de)(de)(de)(de)任意一(yi)(yi)組輸入、導出的(de)(de)(de)(de)(de)(de)(de)(de) ONNX 文件的(de)(de)(de)(de)(de)(de)(de)(de)文件名。從 PyTorch 的(de)(de)(de)(de)(de)(de)(de)(de)模(mo)(mo)型(xing)(xing)到 ONNX 的(de)(de)(de)(de)(de)(de)(de)(de)模(mo)(mo)型(xing)(xing),本質上(shang)是一(yi)(yi)種語(yu)言上(shang)的(de)(de)(de)(de)(de)(de)(de)(de)翻譯。直(zhi)覺上(shang)的(de)(de)(de)(de)(de)(de)(de)(de)想法(fa)是像編譯器一(yi)(yi)樣(yang)徹底解析(xi)原模(mo)(mo)型(xing)(xing)的(de)(de)(de)(de)(de)(de)(de)(de)代碼,記錄(lu)所有控制(zhi)流。但(dan)前(qian)面也講(jiang)到,通常只用(yong) ONNX 記錄(lu)不(bu)考慮(lv)控制(zhi)流的(de)(de)(de)(de)(de)(de)(de)(de)靜態圖(tu)(tu)。因此(ci),PyTorch 提(ti)供了(le)一(yi)(yi)種叫做追蹤(trace)的(de)(de)(de)(de)(de)(de)(de)(de)模(mo)(mo)型(xing)(xing)轉(zhuan)換(huan)(huan)方(fang)(fang)法(fa):給定一(yi)(yi)組輸入,再實(shi)際(ji)執(zhi)行一(yi)(yi)遍模(mo)(mo)型(xing)(xing),即把這組輸入對應的(de)(de)(de)(de)(de)(de)(de)(de)計(ji)算圖(tu)(tu)記錄(lu)下來(lai),保存(cun)為 ONNX 格(ge)式(shi)。export 函數(shu)(shu)(shu)用(yong)的(de)(de)(de)(de)(de)(de)(de)(de)就(jiu)是追蹤導出方(fang)(fang)法(fa),需要(yao)給任意一(yi)(yi)組輸入,讓模(mo)(mo)型(xing)(xing)跑起(qi)來(lai)。測試(shi)圖(tu)(tu)片是三通道,256x256大小(xiao)的(de)(de)(de)(de)(de)(de)(de)(de),這里也構(gou)造一(yi)(yi)個同樣(yang)形狀的(de)(de)(de)(de)(de)(de)(de)(de)隨機張量。

剩下(xia)的(de)(de)參數中,opset_version 表(biao)示 ONNX 算子(zi)(zi)(zi)集(ji)的(de)(de)版本(ben)。深度學(xue)習的(de)(de)發展會(hui)不斷誕生(sheng)新(xin)算子(zi)(zi)(zi),為(wei)了(le)支(zhi)持這些新(xin)增(zeng)的(de)(de)算子(zi)(zi)(zi),ONNX會(hui)經常發布新(xin)的(de)(de)算子(zi)(zi)(zi)集(ji),目前(qian)已經更新(xin)15個(ge)(ge)(ge)版本(ben)。假(jia)如令 opset_version = 11,即使用(yong)第11個(ge)(ge)(ge) ONNX 算子(zi)(zi)(zi)集(ji),是(shi)因為(wei) SRCNN 中的(de)(de) bicubic (雙三次插值)在 opset11 中才(cai)得到支(zhi)持。剩下(xia)的(de)(de)兩個(ge)(ge)(ge)參數 input_names, output_names 是(shi)輸入、輸出(chu) tensor 的(de)(de)名稱。

如果上述代碼(ma)運行成功,目錄下會新增(zeng)一(yi)個"model.onnx"的(de) ONNX 模型文件。就可(ke)以(yi)用下面的(de)腳本(ben)來驗(yan)證一(yi)下模型文件是否(fou)正確。

import onnx 

 onnx_model = onnx.load("model.onnx") try

    onnx.checker.check_model(onnx_model) except Exception

    print("Model incorrect") else

    print("Model correct")

其中(zhong),onnx.load 函(han)數用于讀(du)取一(yi)個(ge) ONNX 模(mo)型(xing)。onnx.checker.check_model 用于檢查(cha)模(mo)型(xing)格式是(shi)否正確,如(ru)果有錯誤的話(hua)該(gai)函(han)數會直接報錯。該(gai)模(mo)型(xing)是(shi)正確的,控制臺中(zhong)應該(gai)會打印出"Model correct"。

接下來,讓我們來看一看 ONNX 模型具(ju)體的結構是怎(zen)么樣的。然(ran)后可(ke)(ke)以(yi)使用 Netron (開源的模型可(ke)(ke)視化工具(ju))來可(ke)(ke)視化 ONNX 模型。把 srcnn.onnx 文件(jian)(jian)從本地的文件(jian)(jian)系統拖入網站,即可(ke)(ke)看到如下的可(ke)(ke)視化結果(guo)。

點擊 input 或(huo)者 output,可以查看 ONNX 模型的基本(ben)信(xin)息(xi),包(bao)括(kuo)模型的版本(ben)信(xin)息(xi),以及模型輸入、輸出的名稱和數據類型。

(1)模型(xing)部署,指把訓練好(hao)的模型(xing)在特定(ding)環境(jing)中運(yun)行的過程。模型(xing)部署要解決模型(xing)框架(jia)兼容(rong)性差和模型(xing)運(yun)行速(su)度慢這(zhe)兩大問題。

(2)模型(xing)部署的(de)常見流水線是(shi)“深(shen)度學習框(kuang)架-中間(jian)表(biao)示(shi)-推理引擎(qing)”。其中比較常用(yong)的(de)一個中間(jian)表(biao)示(shi)是(shi) ONNX。

(3)深(shen)度學習模(mo)型實際上就是一個計(ji)(ji)算(suan)圖(tu)。模(mo)型部署時(shi)通常把模(mo)型轉換成(cheng)靜態(tai)的計(ji)(ji)算(suan)圖(tu),即沒有控制流(liu)(分(fen)支(zhi)語(yu)(yu)句、循環(huan)語(yu)(yu)句)的計(ji)(ji)算(suan)圖(tu)。

PyTorch 框架自(zi)帶對 ONNX 的(de)支持,只需(xu)要構造(zao)一組隨(sui)機(ji)的(de)輸入,并對模型調用(yong)。

3、模型部署中常(chang)見的難題(ti)

模(mo)型的動態化(hua)。出于性能的考慮,各推理框架都默認模(mo)型的輸入形(xing)狀、輸出形(xing)狀、結構是靜態的。而為了讓(rang)模(mo)型的泛用性更(geng)強,部署(shu)時需要在(zai)盡可能不影響原有邏輯的前提下(xia),讓(rang)模(mo)型的輸入輸出或是結構動態化(hua)。

新算(suan)子的(de)實現。深(shen)度學(xue)習(xi)技術(shu)日新月(yue)異,提出新算(suan)子的(de)速度往往快(kuai)于 ONNX 維(wei)護者(zhe)支持的(de)速度。為了(le)部署最新的(de)模型,部署工程師(shi)往往需要自己在 ONNX 和推理引擎中支持新算(suan)子。

中(zhong)間表示與推(tui)理(li)引(yin)擎(qing)(qing)的兼容問題(ti)。由(you)于各推(tui)理(li)引(yin)擎(qing)(qing)的實現不同,對 ONNX 難以形成統一(yi)的支(zhi)持。為了確(que)保(bao)模型在不同的推(tui)理(li)引(yin)擎(qing)(qing)中(zhong)有同樣(yang)的運行效果,部(bu)署(shu)工(gong)(gong)程師往(wang)往(wang)得(de)為某(mou)個推(tui)理(li)引(yin)擎(qing)(qing)定制模型代(dai)碼,這為模型部(bu)署(shu)引(yin)入了許(xu)多工(gong)(gong)作量。

實(shi)現(xian)動態放大的超分辨率模型:

在原來的 SRCNN 中,圖片的放大比例是寫(xie)死(si)在模型(xing)里(li)的:

class SuperResolutionNet(nn.Module): 

    def __init__(self, upscale_factor): 

        super().__init__() 

        self.upscale_factor = upscale_factor 

        self.img_upsampler = nn.Upsample( 

            scale_factor=self.upscale_factor, 

            mode='bicubic', 

            align_corners=False) 

 ... 

 def init_torch_model(): 

    torch_model = SuperResolutionNet(upscale_factor=3) 

使用(yong) upscale_factor 來(lai)控(kong)制(zhi)模型的(de)放(fang)(fang)大(da)比例。初(chu)始(shi)化模型的(de)時(shi)候(hou),假(jia)如默認令(ling) upscale_factor 為 3,生(sheng)成(cheng)了一(yi)(yi)個放(fang)(fang)大(da) 3 倍(bei)的(de) PyTorch 模型。這個 PyTorch 模型最終被轉換成(cheng)了 ONNX 格(ge)式的(de)模型。這樣需(xu)要一(yi)(yi)個放(fang)(fang)大(da) 4 倍(bei)的(de)模型,需(xu)要重新生(sheng)成(cheng)一(yi)(yi)遍(bian)模型,再做一(yi)(yi)次到 ONNX 的(de)轉換。

現在(zai),假設要做一個(ge)超(chao)分(fen)辨率(lv)(lv)的(de)(de)應用。此時用戶希望圖片的(de)(de)放(fang)大倍數(shu)能(neng)夠自(zi)由(you)設置。在(zai)交給用戶的(de)(de)時候(hou),只有一個(ge) .onnx 文件和運行(xing)超(chao)分(fen)辨率(lv)(lv)模(mo)型的(de)(de)應用程(cheng)序。我們在(zai)不修改 .onnx 文件的(de)(de)前提下(xia)改變放(fang)大倍數(shu)。

因此,這時必(bi)須修改原來的(de)(de)模型(xing),令模型(xing)的(de)(de)放大(da)倍(bei)數(shu)變(bian)成推理時的(de)(de)輸(shu)入。

import torch

from torch import nn 

from torch.nn.functional import interpolate 

import torch.onnx 

import cv2 

import numpy as np 

 class SuperResolutionNet(nn.Module): 

    def __init__(self): 

        super().__init__() 

        self.conv1 = nn.Conv2d(3, 64, kernel_size=9, padding=4) 

        self.conv2 = nn.Conv2d(64, 32, kernel_size=1, padding=0) 

        self.conv3 = nn.Conv2d(32, 3, kernel_size=5, padding=2) 

        self.relu = nn.ReLU() 

    def forward(self, x, upscale_factor): 

        x = interpolate(x, 

                        scale_factor=upscale_factor, 

                        mode='bicubic', 

                        align_corners=False) 

        out = self.relu(self.conv1(x)) 

        out = self.relu(self.conv2(out)) 

        out = self.conv3(out) 

        return out 

 def init_torch_model(): 

    torch_model = SuperResolutionNet() 

    state_dict = torch.load('srcnn.pth')['state_dict'] 

    # Adapt the checkpoint

    for old_key in list(state_dict.keys()): 

        new_key = '.'.join(old_key.split('.')[1:]) 

        state_dict[new_key] = state_dict.pop(old_key) 

    torch_model.load_state_dict(state_dict) 

    torch_model. 

    return torch_model 

 model = init_torch_model() 

 input_img = cv2.imread('face.png').astype(np.float32) 

 # HWC to NCHW input_img = np.transpose(input_img, [2, 0, 1]) input_img = np.expand_dims(input_img, 0) 

 # Inference torch_output = model(torch.from_numpy(input_img), 3).detach().numpy() 

 # NCHW to HWC torch_output = np.squeeze(torch_output, 0) torch_output = np.clip(torch_output, 0, 255) torch_output = np.transpose(torch_output, [1, 2, 0]).astype(np.uint8) 

 # Show image cv2.imwrite("face_torch_2.png", torch_output) 

SuperResolutionNet 未修改(gai)之(zhi)前(qian),nn.Upsample 在(zai)初始(shi)化階段(duan)固化了放(fang)(fang)(fang)大倍(bei)數,而 PyTorch 的(de)(de) interpolate 插值(zhi)算子可以(yi)在(zai)運行階段(duan)選擇放(fang)(fang)(fang)大倍(bei)數。因此,在(zai)新腳本中使用 interpolate 代替 nn.Upsample,從而讓模型支(zhi)持(chi)動態放(fang)(fang)(fang)大倍(bei)數的(de)(de)超分。 在(zai)使用模型推理(li)時(shi),我們把放(fang)(fang)(fang)大倍(bei)數設置為 3。最后,圖片保存在(zai)文件 "face_torch_2.png" 中。一切正(zheng)常的(de)(de)話(hua),"face_torch_2.png" 和 "face_torch.png" 的(de)(de)內容一模一樣(yang)。

通過簡(jian)單(dan)的修(xiu)改,PyTorch 模(mo)型已經支(zhi)持了動態分辨率。現在來嘗試一下(xia)導出(chu)模(mo)型:

= torch.randn(1, 3, 256, 256) 

 with torch.no_grad(): 

    torch.onnx.export(model, (x, 3), 

                      "srcnn2.onnx", 

                      opset_version=11, 

                      input_names=['input', 'factor'], 

                      output_names=['output']) 

  • tensorrt模型轉換和部署

1、tensorrt模型轉(zhuan)換

(1)當batch=1時(shi)并用tensorrt:22.02-py3進行模型轉(zhuan)換:

sudo docker run --gpus all -v $(pwd):/work -it nvcr.io/nvidia/tensorrt:22.02-py3 bash

cd /work

trtexec --onnx=swin_base_new3.onnx --saveEngine=model.plan --workspace=1024 --explicitBatch

(2)當batch=-1時并用(yong)tensorrt:22.02-py3進(jin)行模型轉換(huan):

sudo docker run --gpus all -v $(pwd):/work -it registry.cn-guangzhou.aliyuncs.com/nvidia-images/tensorrt:21.06-py3-opencv bash

cd /work

trtexec --onnx=scene_0718.onnx --saveEngine=scene.plan  --workspace=1024    --minShapes=input:1x3x224x224 --optShapes=input:8x3x224x224  --maxShapes=input:16x3x224x224  --explicitBatch

(3)模(mo)型是(shi)yolov5時需要對應yolov5相(xiang)關(guan)cpp文件和對應腳本轉換:

#!/bin/bash

CURRENT_PATH=$(pwd)

MODEL_FILE=helmet_detect_0701.pt

MODEL_NAME=yolov5m  #s,m,l,x

WTS_FILE=${MODEL_NAME}.wts

ENGINE_FILE=${MODEL_NAME}.plan

#sudo docker run --gpus all  \

#    -v ${CURRENT_PATH}:/work  registry.cn-guangzhou.aliyuncs.com/nvidia-images/yolov5:4.0 \

#    python3 gen_wts.py --model=/work/${MODEL_FILE} --wts=/work/${WTS_FILE}

sudo docker run --gpus all  \

                -v ${CURRENT_PATH}:/work \                 

      -w /work \

                 -it registry.cn-guangzhou.aliyuncs.com/nvidia-images/tensorrt:21.06-py3-opencv \                 bash -c 'cd yolov5-4.0-nms-helmet && bash run.sh'

其中21.06-py3-opencv或者22.02-py3-opencv需要在tensorrt:21.06-py3和tensorrt:22.02-py3容器(qi)里增加c++版本opencv即可。

2、模型部署

HTTP_PORT=7020

MODEL_DIR=$(dirname $(pwd))/models

DOCKER_IMAGE=nvcr.io/nvidia/tritonserver:22.02-py3

LOG_VERBOSE=1

NAME="ocr-docker"

sudo docker run \

        --name ${NAME} \

        --gpus '"device=1"' \

        --rm --shm-size=1g --ulimit memlock=-1 --ulimit stack=67108864 \

        --network=host \

        -v${MODEL_DIR}:/models \

        ${DOCKER_IMAGE} \

   ;     tritonserver --model-repository=/models \

        --http-port=${HTTP_PORT} \

        --allow-grpc=false \

        --allow-metrics=false \

        --allow-sagemaker=false \

        --log-verbose=1

Docker啟動服務腳本,值得注意的是模型(xing)tensorrt版(ban)本與tritonserver服務版(ban)本需要一致。

3、模型推理和前后處理
(1)前處理:因為模型推理的輸入是Tensor(多維矩陣)數據,但是正常AI應用的輸入都是圖片,視頻,文字等數據,所以前處理就是要將業務的輸入數據(圖像,視頻,文字等)預先處理成模型推理可以接收的數據---Tensor(多維矩陣)。以圖像處理為例,前處理動作就包括但不限于:圖像格式轉換,顏色空間變換,圖像變換(resize,warpaffine,圖像濾波等操作。
OpenCV就是intel推(tui)出開源的(de)跨平臺的(de)計算機視(shi)覺庫。

在預(yu)(yu)處理(li)(li)(li)過程中為了提(ti)高(gao)處理(li)(li)(li)效率(lv),預(yu)(yu)處理(li)(li)(li)可以選擇在GPU上處理(li)(li)(li),一般選用DALI進行處理(li)(li)(li),下(xia)面是某個模型預(yu)(yu)處理(li)(li)(li)DALI腳本:

import nvidia.dali.fn as fn

import nvidia.dali as dali

import nvidia.dali.types as types

import cv2

import numpy as np

import time

def get_imgs():

    image_data = np.fromfile("5.jpg", dtype="uint8")

    data = [image_data for i in range(8)]

    return data

input_dim = [640,640]

@dali.pipeline_def(batch_size=32, num_threads=16, device_id=0)

def pipe():

    data = fn.external_source(device="cpu", name="DALI_INPUT_0", source=get_imgs)

    raw_images = fn.decoders.image(data, device="mixed", output_type=types.RGB)

    images = fn.resize(raw_images, size=input_dim, mode="not_larger", max_size=input_dim)

    images = fn.pad(images, fill_value=128, shape=[input_dim[0], input_dim[1], 3])

    images = fn.transpose(images, perm=[2, 0, 1])

    images = fn.cast(images, dtype=types.FLOAT)

    return images

def main():

    pipe().serialize(filename='model.dali')

    print("serialized!")

    pipeline = pipe()

    pipeline.build()

    o = pipeline.run()

    image = np.transpose(np.array(o[0][0].as_cpu()),[1,2,0])[:,:,::-1]

    cv2.imwrite("out0.jpg",image)

if __name__ == '__main__':

    main()

其中DALI腳本對輸入data進行解碼然后對圖像數據resize以及填充等處理并輸出處理結果,此處DALI模型一般與推理模型級聯部署處理。
(2)模型推理:模型推理應該是模型部署pipline中最核心的部分。就是需要在實際應用環境中(具體部署設備)將實際輸入的數據(轉換成Tensor數據后)在訓練好的模型中跑通,并且性能和精度等商業指標上達到預期效果。這個過程包括了對部署設備的適配(CPU/GPU/DSP/NPU),要想將模型跑在任何一種設備上,都需要提前針對設備進行適配,并且還要保證性能和精度等指標。本文采用tensorrt對onnx模型轉換和tritonserver服務部署(NVIDIA推出的針對GPU卡的TensorRT)。
市面上有非常多的開源深度學習推理框架都是在解決模型推理相關的問題。例如:國內各大廠推出的開源的推理框架:OpenPPL、NCNN、TNN、MNN、PaddleLite、Tengine等等,還有intel針對intel芯片的OpenVINO等。
后(hou)處(chu)理(li):就(jiu)是將模型推理(li)后(hou)的Tensor數據轉換成業務可(ke)以(yi)識別的特征數據(不(bu)同(tong)的業務會(hui)呈現不(bu)同(tong)的最終效果數據)。

 

0條評論
作者已關閉評論
w****n
2文章(zhang)數
0粉(fen)絲(si)數
w****n
2 文章 | 0 粉(fen)絲
w****n
2文章數
0粉絲數
w****n
2 文章 | 0 粉絲
原創

TensorRT部署簡介

2023-06-30 07:48:51
128
0

一、TensorRT版本部署簡介

模型(xing)(xing)訓練(lian)(lian)好之后(hou),就到了AI模型(xing)(xing)實際(ji)應用的(de)(de)最后(hou)一環 --- 模型(xing)(xing)部(bu)署(shu)(shu)(shu)(shu)。模型(xing)(xing)部(bu)署(shu)(shu)(shu)(shu)是(shi)將已(yi)經訓練(lian)(lian)好的(de)(de)模型(xing)(xing),部(bu)署(shu)(shu)(shu)(shu)到實際(ji)的(de)(de)應用場(chang)景中,所以(yi)模型(xing)(xing)部(bu)署(shu)(shu)(shu)(shu)更多是(shi)工程(cheng)(cheng)化(hua)的(de)(de)過程(cheng)(cheng),就是(shi)解決實際(ji)的(de)(de)模型(xing)(xing)應用問題,本文采用NVIDIA推出的(de)(de)針對(dui)GPU卡的(de)(de)TensorRT對(dui)onnx模型(xing)(xing)轉換部(bu)署(shu)(shu)(shu)(shu)。

二、模型壓縮
模型壓縮是對已經訓練好的深度模型進行精簡,進而得到一個輕量且準確率相當的網絡,壓縮后的網絡具有更小的結構和更少的參數,可以有效降低計算和存儲開銷,便于部署在受限的硬件環境中。
訓練的(de)(de)時候因為(wei)要(yao)保證(zheng)前后向(xiang)傳播,每次(ci)梯度的(de)(de)更新是(shi)很微小的(de)(de),這個時候需(xu)要(yao)相對(dui)較(jiao)高(gao)的(de)(de)精(jing)度,一般來(lai)說(shuo)需(xu)要(yao)float型(xing)(xing),如FP32,32位的(de)(de)浮點型(xing)(xing)來(lai)處理(li)數據,但是(shi)在(zai)推(tui)理(li)(Inference)的(de)(de)時候,對(dui)精(jing)度的(de)(de)要(yao)求(qiu)沒有(you)那(nei)么高(gao),很多研究表明可(ke)以(yi)用低精(jing)度,如半(ban)長(chang)(16)的(de)(de)float,即FP16,也可(ke)以(yi)用8位的(de)(de)整型(xing)(xing)(INT8)來(lai)做推(tui)理(li)(Inference)。所(suo)以(yi),一般來(lai)說(shuo),在(zai)模型(xing)(xing)部署時會對(dui)模型(xing)(xing)進行(xing)壓(ya)縮。模型(xing)(xing)壓(ya)縮方法(fa)有(you):蒸餾(liu),剪枝(zhi),量化等。

三、模型格式轉換
首先研(yan)究員通(tong)過各(ge)(ge)種(zhong)(zhong)訓(xun)(xun)(xun)(xun)練框(kuang)架(jia)(jia)訓(xun)(xun)(xun)(xun)練好的(de)(de)模(mo)型(xing)一(yi)般都需要進(jin)行(xing)模(mo)型(xing)格式(shi)(shi)適(shi)(shi)(shi)配,模(mo)型(xing)訓(xun)(xun)(xun)(xun)練大(da)家可(ke)以(yi)選擇各(ge)(ge)種(zhong)(zhong)不(bu)(bu)同(tong)(tong)的(de)(de)訓(xun)(xun)(xun)(xun)練框(kuang)架(jia)(jia),例如TensorFlow,Pytorch,PaddlePaddle,Caffe等(deng)等(deng)一(yi)系列的(de)(de)開源框(kuang)架(jia)(jia),這么多不(bu)(bu)同(tong)(tong)的(de)(de)訓(xun)(xun)(xun)(xun)練框(kuang)架(jia)(jia)他們訓(xun)(xun)(xun)(xun)練出來(lai)(lai)的(de)(de)模(mo)型(xing)格式(shi)(shi)都有各(ge)(ge)自的(de)(de)標準,各(ge)(ge)不(bu)(bu)相同(tong)(tong),部(bu)署要解決(jue)(jue)的(de)(de)第一(yi)個(ge)問(wen)題就是要適(shi)(shi)(shi)配各(ge)(ge)種(zhong)(zhong)不(bu)(bu)同(tong)(tong)的(de)(de)模(mo)型(xing)格式(shi)(shi)。但是如果要一(yi)個(ge)個(ge)訓(xun)(xun)(xun)(xun)練框(kuang)架(jia)(jia)去適(shi)(shi)(shi)配格式(shi)(shi),工作量太大(da),也不(bu)(bu)適(shi)(shi)(shi)合(he)擴展,所以(yi)微軟聯(lian)合(he)Facebook等(deng)大(da)廠推出一(yi)種(zhong)(zhong)中間(jian)格式(shi)(shi)ONNX,希望(wang)能解決(jue)(jue)多種(zhong)(zhong)模(mo)型(xing)格式(shi)(shi)適(shi)(shi)(shi)配的(de)(de)問(wen)題,就是無論(lun)是什么訓(xun)(xun)(xun)(xun)練框(kuang)架(jia)(jia)訓(xun)(xun)(xun)(xun)練出來(lai)(lai)的(de)(de)模(mo)型(xing)格式(shi)(shi),最(zui)終都是用ONNX格式(shi)(shi)來(lai)(lai)進(jin)行(xing)部(bu)署。所以(yi)一(yi)般模(mo)型(xing)部(bu)署可(ke)以(yi)跑的(de)(de)第一(yi)步要解決(jue)(jue)的(de)(de)問(wen)題就是模(mo)型(xing)格式(shi)(shi)轉(zhuan)換。

1.初步了解部(bu)署內容

在(zai)(zai)軟(ruan)(ruan)件工程(cheng)中,部署(shu)指把開發(fa)完(wan)畢的(de)軟(ruan)(ruan)件投入(ru)使用的(de)過程(cheng),包(bao)括環境(jing)配置、軟(ruan)(ruan)件安裝等步驟。類似(si)地(di),對于(yu)(yu)深度學習模型(xing)來說,模型(xing)部署(shu)指讓訓練好的(de)模型(xing)在(zai)(zai)特定環境(jing)中運行(xing)的(de)過程(cheng)。相(xiang)比于(yu)(yu)軟(ruan)(ruan)件部署(shu),模型(xing)部署(shu)會(hui)面臨更(geng)多的(de)難題:

(1)運行(xing)模型所(suo)需的(de)環境(jing)難以配(pei)置。深度學(xue)習(xi)模型通(tong)常是(shi)由一些框(kuang)(kuang)架(jia)(jia)編寫,比如 PyTorch、TensorFlow。由于框(kuang)(kuang)架(jia)(jia)規模、依賴環境(jing)的(de)限制(zhi),這些框(kuang)(kuang)架(jia)(jia)不適合在手機、開發板等(deng)生產環境(jing)中安裝(zhuang)。

(2)深度學習(xi)模型的(de)結構通常比(bi)較龐大,需(xu)要(yao)大量的(de)算力才能滿足(zu)實時(shi)運(yun)行的(de)需(xu)求。模型的(de)運(yun)行效率需(xu)要(yao)優化。

2.創(chuang)建(jian)關于pytorch內容(rong)

     用(yong) PyTorch 實現一個(ge)超分辨率模型,并把模型部署到 ONNX Runtime 這個(ge)推理(li)(li)引擎(qing)上(shang)。首(shou)先,需(xu)要創建一個(ge)有 PyTorch 庫(ku)的 Python 編(bian)程(cheng)環境。如果(guo)你的 PyTorch 環境還沒有裝好,可以參考(kao)官方(fang)的入(ru)門教程(cheng)。在(zai)這里推薦(jian)使用(yong) conda 來管理(li)(li) Python 庫(ku)。

   在介紹 ONNX 之前,先從本質上來認識一下神經(jing)網(wang)絡的(de)結構(gou)。神經(jing)網(wang)絡實際上只是(shi)描述(shu)了(le)數據(ju)計(ji)(ji)算(suan)的(de)過程,其結構(gou)可以用計(ji)(ji)算(suan)圖(tu)表示。比(bi)如 a+b 可,為了(le)加速計(ji)(ji)算(suan),一些框(kuang)架會使(shi)用對(dui)神經(jing)網(wang)絡“先編譯(yi),后執(zhi)行(xing)”的(de)靜態圖(tu)來描述(shu)網(wang)絡。靜態圖(tu)的(de)缺點(dian)是(shi)難(nan)以描述(shu)控制流(比(bi)如 if-else 分支語句(ju)(ju)和 for 循(xun)環(huan)語句(ju)(ju)),直接對(dui)其引入控制語句(ju)(ju)會導致產生不同(tong)的(de)計(ji)(ji)算(suan)圖(tu)。比(bi)如循(xun)環(huan)執(zhi)行(xing) n 次 a=a+b,對(dui)于不同(tong)的(de) n,會生成不同(tong)的(de)計(ji)(ji)算(suan)圖(tu)。

ONNX (Open Neural Network Exchange)是 Facebook 和微(wei)軟在(zai)2017年共同(tong)發(fa)布的,用于標準描(miao)述計(ji)算圖的一(yi)種(zhong)格式。目前,在(zai)數家機(ji)構的共同(tong)維護下,ONNX 已經對接(jie)了(le)多種(zhong)深度(du)學習(xi)框架和多種(zhong)推理引擎。因(yin)此,ONNX 被(bei)當成了(le)深度(du)學習(xi)框架到推理引擎的橋梁(liang),就像(xiang)編譯器(qi)的中間語言一(yi)樣。由于各(ge)框架兼容性不一(yi),通常(chang)只用 ONNX 表示更容易部署(shu)的靜(jing)態圖。

用下面的(de)(de)代碼來把 PyTorch 的(de)(de)模(mo)型(xing)(xing)轉換成 ONNX 格式的(de)(de)模(mo)型(xing)(xing):

= torch.randn(1, 3, 256, 256) 

 with torch.no_grad(): 

    torch.onnx.export( 

        model, 

        x, 

        "srcnn.onnx", 

        opset_version=11, 

        input_names=['input'], 

        output_names=['output'])

其中,torch.onnx.export 是(shi)(shi) PyTorch 自(zi)帶的(de)(de)(de)(de)把模型(xing)(xing)轉(zhuan)換成 ONNX 格(ge)式的(de)(de)(de)(de)函數(shu)。先看一(yi)(yi)下(xia)前(qian)三(san)個必選(xuan)參數(shu):前(qian)三(san)個參數(shu)分別是(shi)(shi)要(yao)(yao)轉(zhuan)換的(de)(de)(de)(de)模型(xing)(xing)、模型(xing)(xing)的(de)(de)(de)(de)任意一(yi)(yi)組(zu)輸入(ru)(ru)、導(dao)出的(de)(de)(de)(de) ONNX 文件的(de)(de)(de)(de)文件名。從 PyTorch 的(de)(de)(de)(de)模型(xing)(xing)到 ONNX 的(de)(de)(de)(de)模型(xing)(xing),本質上(shang)是(shi)(shi)一(yi)(yi)種(zhong)(zhong)語言上(shang)的(de)(de)(de)(de)翻(fan)譯(yi)。直覺(jue)上(shang)的(de)(de)(de)(de)想(xiang)法(fa)是(shi)(shi)像編譯(yi)器一(yi)(yi)樣徹底解(jie)析(xi)原模型(xing)(xing)的(de)(de)(de)(de)代碼,記(ji)錄(lu)所有控制(zhi)流(liu)。但前(qian)面也講(jiang)到,通常只用 ONNX 記(ji)錄(lu)不(bu)考慮控制(zhi)流(liu)的(de)(de)(de)(de)靜態圖。因此,PyTorch 提(ti)供了(le)一(yi)(yi)種(zhong)(zhong)叫做追蹤(zong)(trace)的(de)(de)(de)(de)模型(xing)(xing)轉(zhuan)換方法(fa):給定(ding)一(yi)(yi)組(zu)輸入(ru)(ru),再實際執行一(yi)(yi)遍(bian)模型(xing)(xing),即把這組(zu)輸入(ru)(ru)對應(ying)的(de)(de)(de)(de)計算圖記(ji)錄(lu)下(xia)來(lai),保存為 ONNX 格(ge)式。export 函數(shu)用的(de)(de)(de)(de)就(jiu)是(shi)(shi)追蹤(zong)導(dao)出方法(fa),需(xu)要(yao)(yao)給任意一(yi)(yi)組(zu)輸入(ru)(ru),讓模型(xing)(xing)跑(pao)起來(lai)。測試圖片是(shi)(shi)三(san)通道,256x256大小的(de)(de)(de)(de),這里也構造一(yi)(yi)個同樣形狀的(de)(de)(de)(de)隨機張量。

剩下的(de)(de)(de)參(can)數中,opset_version 表示 ONNX 算(suan)子集(ji)的(de)(de)(de)版本。深度學習的(de)(de)(de)發(fa)展會(hui)不斷誕生新(xin)(xin)算(suan)子,為了支持(chi)這些新(xin)(xin)增(zeng)的(de)(de)(de)算(suan)子,ONNX會(hui)經(jing)常發(fa)布新(xin)(xin)的(de)(de)(de)算(suan)子集(ji),目前已經(jing)更新(xin)(xin)15個版本。假如令 opset_version = 11,即使用第11個 ONNX 算(suan)子集(ji),是(shi)因(yin)為 SRCNN 中的(de)(de)(de) bicubic (雙三次插值)在 opset11 中才得到支持(chi)。剩下的(de)(de)(de)兩個參(can)數 input_names, output_names 是(shi)輸入(ru)、輸出(chu) tensor 的(de)(de)(de)名(ming)稱。

如果上述代碼運行成功,目錄(lu)下(xia)會新增一(yi)(yi)個"model.onnx"的 ONNX 模型文件。就(jiu)可以用下(xia)面的腳(jiao)本(ben)來驗證(zheng)一(yi)(yi)下(xia)模型文件是否正確。

import onnx 

 onnx_model = onnx.load("model.onnx") try

    onnx.checker.check_model(onnx_model) except Exception

    print("Model incorrect") else

    print("Model correct")

其中,onnx.load 函數用于(yu)讀取(qu)一個(ge) ONNX 模(mo)型。onnx.checker.check_model 用于(yu)檢查模(mo)型格式是否正(zheng)確,如(ru)果有錯誤的(de)話該(gai)函數會直接報錯。該(gai)模(mo)型是正(zheng)確的(de),控制臺中應該(gai)會打印出(chu)"Model correct"。

接下來(lai),讓我們來(lai)看(kan)(kan)一看(kan)(kan) ONNX 模(mo)型(xing)(xing)具體的(de)結(jie)構是怎么(me)樣的(de)。然(ran)后可(ke)(ke)以(yi)使用 Netron (開源的(de)模(mo)型(xing)(xing)可(ke)(ke)視(shi)化工具)來(lai)可(ke)(ke)視(shi)化 ONNX 模(mo)型(xing)(xing)。把 srcnn.onnx 文件從本(ben)地的(de)文件系統拖入網站,即可(ke)(ke)看(kan)(kan)到如下的(de)可(ke)(ke)視(shi)化結(jie)果。

點擊 input 或(huo)者 output,可以查(cha)看 ONNX 模(mo)型(xing)(xing)的基(ji)本信(xin)息,包括模(mo)型(xing)(xing)的版本信(xin)息,以及(ji)模(mo)型(xing)(xing)輸入、輸出(chu)的名稱和數據類(lei)型(xing)(xing)。

(1)模型部署,指把訓練好(hao)的(de)模型在特定(ding)環境(jing)中運(yun)(yun)行(xing)的(de)過程。模型部署要(yao)解決模型框(kuang)架兼容性差(cha)和模型運(yun)(yun)行(xing)速度慢這(zhe)兩大問(wen)題。

(2)模(mo)型(xing)部署的常見流水(shui)線(xian)是“深度(du)學習框架-中間表示-推理(li)引擎”。其中比較常用的一個中間表示是 ONNX。

(3)深度學習模型(xing)(xing)實際上就(jiu)是一個計算圖。模型(xing)(xing)部(bu)署(shu)時通常把模型(xing)(xing)轉換成(cheng)靜態的計算圖,即沒有(you)控制流(分支語句(ju)、循環語句(ju))的計算圖。

PyTorch 框架自(zi)帶對 ONNX 的支(zhi)持,只需要構造一組隨機的輸入,并對模(mo)型(xing)調用(yong)。

3、模型部署(shu)中常見(jian)的難題

模型(xing)的(de)(de)動態化。出(chu)于性(xing)能的(de)(de)考慮,各推理框(kuang)架都(dou)默認模型(xing)的(de)(de)輸(shu)(shu)入形(xing)狀(zhuang)、輸(shu)(shu)出(chu)形(xing)狀(zhuang)、結構是(shi)靜態的(de)(de)。而為了(le)讓模型(xing)的(de)(de)泛用性(xing)更強,部署時需要(yao)在盡(jin)可能不影響原(yuan)有邏輯的(de)(de)前提下,讓模型(xing)的(de)(de)輸(shu)(shu)入輸(shu)(shu)出(chu)或是(shi)結構動態化。

新算(suan)子的(de)實現。深度學習技術日新月異,提出新算(suan)子的(de)速度往(wang)往(wang)快于 ONNX 維護者支持(chi)的(de)速度。為(wei)了部署(shu)最新的(de)模型,部署(shu)工程(cheng)師往(wang)往(wang)需要自(zi)己在 ONNX 和推理引擎(qing)中支持(chi)新算(suan)子。

中間表示與推(tui)(tui)理(li)(li)(li)引擎的兼容問題。由于(yu)各推(tui)(tui)理(li)(li)(li)引擎的實現(xian)不同,對 ONNX 難以形成統(tong)一的支持。為了確(que)保模型在不同的推(tui)(tui)理(li)(li)(li)引擎中有同樣的運(yun)行效(xiao)果,部署工(gong)程師往往得(de)為某個(ge)推(tui)(tui)理(li)(li)(li)引擎定制模型代碼(ma),這為模型部署引入(ru)了許多工(gong)作量。

實(shi)現動態放大(da)的超分(fen)辨率模型(xing):

在原來的 SRCNN 中,圖片的放大比例是寫死(si)在模型里的:

class SuperResolutionNet(nn.Module): 

    def __init__(self, upscale_factor): 

        super().__init__() 

        self.upscale_factor = upscale_factor 

        self.img_upsampler = nn.Upsample( 

            scale_factor=self.upscale_factor, 

            mode='bicubic', 

            align_corners=False) 

 ... 

 def init_torch_model(): 

    torch_model = SuperResolutionNet(upscale_factor=3) 

使用 upscale_factor 來控(kong)制(zhi)模(mo)型(xing)(xing)的(de)放大(da)比例。初始(shi)化模(mo)型(xing)(xing)的(de)時候,假如默認令 upscale_factor 為 3,生成了(le)一(yi)個放大(da) 3 倍(bei)的(de) PyTorch 模(mo)型(xing)(xing)。這(zhe)個 PyTorch 模(mo)型(xing)(xing)最(zui)終被轉(zhuan)換成了(le) ONNX 格式(shi)的(de)模(mo)型(xing)(xing)。這(zhe)樣需要(yao)一(yi)個放大(da) 4 倍(bei)的(de)模(mo)型(xing)(xing),需要(yao)重新生成一(yi)遍(bian)模(mo)型(xing)(xing),再(zai)做一(yi)次(ci)到 ONNX 的(de)轉(zhuan)換。

現在(zai),假設(she)要做一個(ge)超(chao)分(fen)辨率(lv)的(de)應用。此時用戶(hu)(hu)希望圖片的(de)放大倍數能夠自由設(she)置。在(zai)交(jiao)給(gei)用戶(hu)(hu)的(de)時候,只(zhi)有一個(ge) .onnx 文件和(he)運行超(chao)分(fen)辨率(lv)模(mo)型的(de)應用程序。我們(men)在(zai)不修改 .onnx 文件的(de)前提下(xia)改變放大倍數。

因此,這時(shi)必須(xu)修改原(yuan)來的模型,令模型的放(fang)大倍數(shu)變成推理時(shi)的輸(shu)入(ru)。

import torch

from torch import nn 

from torch.nn.functional import interpolate 

import torch.onnx 

import cv2 

import numpy as np 

 class SuperResolutionNet(nn.Module): 

    def __init__(self): 

        super().__init__() 

        self.conv1 = nn.Conv2d(3, 64, kernel_size=9, padding=4) 

        self.conv2 = nn.Conv2d(64, 32, kernel_size=1, padding=0) 

        self.conv3 = nn.Conv2d(32, 3, kernel_size=5, padding=2) 

        self.relu = nn.ReLU() 

    def forward(self, x, upscale_factor): 

        x = interpolate(x, 

                        scale_factor=upscale_factor, 

                        mode='bicubic', 

                        align_corners=False) 

        out = self.relu(self.conv1(x)) 

        out = self.relu(self.conv2(out)) 

        out = self.conv3(out) 

        return out 

 def init_torch_model(): 

    torch_model = SuperResolutionNet() 

    state_dict = torch.load('srcnn.pth')['state_dict'] 

    # Adapt the checkpoint

    for old_key in list(state_dict.keys()): 

        new_key = '.'.join(old_key.split('.')[1:]) 

        state_dict[new_key] = state_dict.pop(old_key) 

    torch_model.load_state_dict(state_dict) 

    torch_model. 

    return torch_model 

 model = init_torch_model() 

 input_img = cv2.imread('face.png').astype(np.float32) 

 # HWC to NCHW input_img = np.transpose(input_img, [2, 0, 1]) input_img = np.expand_dims(input_img, 0) 

 # Inference torch_output = model(torch.from_numpy(input_img), 3).detach().numpy() 

 # NCHW to HWC torch_output = np.squeeze(torch_output, 0) torch_output = np.clip(torch_output, 0, 255) torch_output = np.transpose(torch_output, [1, 2, 0]).astype(np.uint8) 

 # Show image cv2.imwrite("face_torch_2.png", torch_output) 

SuperResolutionNet 未(wei)修(xiu)改(gai)之前,nn.Upsample 在初始化(hua)階(jie)段固化(hua)了放大倍(bei)數,而 PyTorch 的 interpolate 插值算(suan)子可以在運(yun)行(xing)階(jie)段選擇放大倍(bei)數。因(yin)此,在新腳本(ben)中(zhong)使(shi)用(yong) interpolate 代替 nn.Upsample,從而讓(rang)模(mo)型支持動(dong)態(tai)放大倍(bei)數的超分。 在使(shi)用(yong)模(mo)型推理時,我(wo)們把放大倍(bei)數設置(zhi)為 3。最(zui)后(hou),圖片保存(cun)在文件 "face_torch_2.png" 中(zhong)。一切(qie)正(zheng)常的話,"face_torch_2.png" 和 "face_torch.png" 的內容一模(mo)一樣。

通(tong)過(guo)簡單的修改,PyTorch 模型已(yi)經支持了動(dong)態(tai)分辨率。現在來嘗試一(yi)下導出模型:

= torch.randn(1, 3, 256, 256) 

 with torch.no_grad(): 

    torch.onnx.export(model, (x, 3), 

                      "srcnn2.onnx", 

                      opset_version=11, 

                      input_names=['input', 'factor'], 

                      output_names=['output']) 

  • tensorrt模型轉換和部署

1、tensorrt模型轉換

(1)當batch=1時并(bing)用tensorrt:22.02-py3進(jin)行模型轉(zhuan)換:

sudo docker run --gpus all -v $(pwd):/work -it nvcr.io/nvidia/tensorrt:22.02-py3 bash

cd /work

trtexec --onnx=swin_base_new3.onnx --saveEngine=model.plan --workspace=1024 --explicitBatch

(2)當batch=-1時并用tensorrt:22.02-py3進行模型轉換:

sudo docker run --gpus all -v $(pwd):/work -it registry.cn-guangzhou.aliyuncs.com/nvidia-images/tensorrt:21.06-py3-opencv bash

cd /work

trtexec --onnx=scene_0718.onnx --saveEngine=scene.plan  --workspace=1024    --minShapes=input:1x3x224x224 --optShapes=input:8x3x224x224  --maxShapes=input:16x3x224x224  --explicitBatch

(3)模(mo)型是yolov5時需要對應yolov5相關cpp文件(jian)和(he)對應腳本轉(zhuan)換:

#!/bin/bash

CURRENT_PATH=$(pwd)

MODEL_FILE=helmet_detect_0701.pt

MODEL_NAME=yolov5m  #s,m,l,x

WTS_FILE=${MODEL_NAME}.wts

ENGINE_FILE=${MODEL_NAME}.plan

#sudo docker run --gpus all  \

#    -v ${CURRENT_PATH}:/work  registry.cn-guangzhou.aliyuncs.com/nvidia-images/yolov5:4.0 \

#    python3 gen_wts.py --model=/work/${MODEL_FILE} --wts=/work/${WTS_FILE}

sudo docker run --gpus all  \

                -v ${CURRENT_PATH}:/work \                 

      -w /work \

                 -it registry.cn-guangzhou.aliyuncs.com/nvidia-images/tensorrt:21.06-py3-opencv \                 bash -c 'cd yolov5-4.0-nms-helmet && bash run.sh'

其中21.06-py3-opencv或者(zhe)22.02-py3-opencv需要(yao)在tensorrt:21.06-py3和(he)tensorrt:22.02-py3容器里增加c++版本(ben)opencv即可。

2、模型部署

HTTP_PORT=7020

MODEL_DIR=$(dirname $(pwd))/models

DOCKER_IMAGE=nvcr.io/nvidia/tritonserver:22.02-py3

LOG_VERBOSE=1

NAME="ocr-docker"

sudo docker run \

        --name ${NAME} \

        --gpus '"device=1"' \

        --rm --shm-size=1g --ulimit memlock=-1 --ulimit stack=67108864 \

        --network=host \

        -v${MODEL_DIR}:/models \

        ${DOCKER_IMAGE} \

 ;       tritonserver --model-repository=/models \

        --http-port=${HTTP_PORT} \

        --allow-grpc=false \

        --allow-metrics=false \

        --allow-sagemaker=false \

        --log-verbose=1

Docker啟動服務(wu)(wu)腳本,值(zhi)得注意的(de)是(shi)模(mo)型tensorrt版(ban)本與(yu)tritonserver服務(wu)(wu)版(ban)本需要一致。

3、模型推理和前后處理
(1)前處理:因為模型推理的輸入是Tensor(多維矩陣)數據,但是正常AI應用的輸入都是圖片,視頻,文字等數據,所以前處理就是要將業務的輸入數據(圖像,視頻,文字等)預先處理成模型推理可以接收的數據---Tensor(多維矩陣)。以圖像處理為例,前處理動作就包括但不限于:圖像格式轉換,顏色空間變換,圖像變換(resize,warpaffine,圖像濾波等操作。
OpenCV就是(shi)intel推出開源(yuan)的跨平臺的計算機視覺庫(ku)。

在(zai)預處理(li)(li)過程(cheng)中(zhong)為了提高處理(li)(li)效(xiao)率,預處理(li)(li)可以選擇(ze)在(zai)GPU上處理(li)(li),一般選用DALI進行(xing)處理(li)(li),下面是某個模(mo)型預處理(li)(li)DALI腳(jiao)本:

import nvidia.dali.fn as fn

import nvidia.dali as dali

import nvidia.dali.types as types

import cv2

import numpy as np

import time

def get_imgs():

    image_data = np.fromfile("5.jpg", dtype="uint8")

    data = [image_data for i in range(8)]

    return data

input_dim = [640,640]

@dali.pipeline_def(batch_size=32, num_threads=16, device_id=0)

def pipe():

    data = fn.external_source(device="cpu", name="DALI_INPUT_0", source=get_imgs)

    raw_images = fn.decoders.image(data, device="mixed", output_type=types.RGB)

    images = fn.resize(raw_images, size=input_dim, mode="not_larger", max_size=input_dim)

    images = fn.pad(images, fill_value=128, shape=[input_dim[0], input_dim[1], 3])

 ;   images = fn.transpose(images, perm=[2, 0, 1])

    images = fn.cast(images, dtype=types.FLOAT)

    return images

def main():

  ; ; pipe().serialize(filename='model.dali')

    print("serialized!")

    pipeline = pipe()

    pipeline.build()

    o = pipeline.run()

    image = np.transpose(np.array(o[0][0].as_cpu()),[1,2,0])[:,:,::-1]

    cv2.imwrite("out0.jpg",image)

if __name__ == '__main__':

    main()

其中DALI腳本對輸入data進行解碼然后對圖像數據resize以及填充等處理并輸出處理結果,此處DALI模型一般與推理模型級聯部署處理。
(2)模型推理:模型推理應該是模型部署pipline中最核心的部分。就是需要在實際應用環境中(具體部署設備)將實際輸入的數據(轉換成Tensor數據后)在訓練好的模型中跑通,并且性能和精度等商業指標上達到預期效果。這個過程包括了對部署設備的適配(CPU/GPU/DSP/NPU),要想將模型跑在任何一種設備上,都需要提前針對設備進行適配,并且還要保證性能和精度等指標。本文采用tensorrt對onnx模型轉換和tritonserver服務部署(NVIDIA推出的針對GPU卡的TensorRT)。
市面上有非常多的開源深度學習推理框架都是在解決模型推理相關的問題。例如:國內各大廠推出的開源的推理框架:OpenPPL、NCNN、TNN、MNN、PaddleLite、Tengine等等,還有intel針對intel芯片的OpenVINO等。
后處理(li):就是將模型推理(li)后的Tensor數據(ju)轉換成業務(wu)可以識別的特(te)征數據(ju)(不同(tong)(tong)的業務(wu)會呈現不同(tong)(tong)的最終效(xiao)果數據(ju))。

 

文章來自個人專欄
文章(zhang) | 訂閱
0條評論
作者已關閉評論
作者已關閉評論
0
0