一、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 格式的模型:
x = 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)型:
x = 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)的最終效果數據)。