今天要來介紹DeepStream中第一個跟模型相關的元件nvinfer,nvinfer是使用NVIDIA的推論框架TensorRT來完成最終的推論工作,元件會讀取設定檔將ONNX模型編譯成TensorRT模型,並且元件還有實作前後處理的功能,降低開發成本。
config-file-path每個模型需要擁有自己的一份設定檔,設定config-file-path後,nvinfer就會處理前處理、推論、後處理等事情。設定檔的某些欄位會跟元件屬性重複,可以在runtime的時候覆蓋設定檔的值,但通常用不上。
詳細可以看完整的設定檔文件連結,我這邊就挑幾個比較重要的出來說明
Mode (primary or secondary) in which the element is to operate on (ignored if input-tensor-meta enabled)Integer 1=Primary 2=Secondary
DeepStream設計了兩個影像推論的方式,Primary和Secondary,物件偵測模型歸類在Primary,而物件分類模型歸類在Secondary。Primary模式就是從整個影像中找出物件的位置,例如人、車;Secondary模式再從Metadata中找出Primary元件輸出的物件位置進一步將某個種類的物件進行分類,例如把車子分類成不同廠牌或型號。
Primary元件的batch-size要設定到你能支援的最大串流數,而Secondary元件不太一樣,它會依據Primary模型輸出的Bounding Box把影像裁切出來再做批次推論。舉例來說,如果Primary元件找出10輛車,那Secondary元件就會以batch-size=10去推論。
gie-unique-idUnique ID to be assigned to the GIE to enable the application and other elements to identify detected bounding boxes and labelsInteger, >0
operate-on-gie-idUnique ID of the GIE on whose metadata (bounding boxes) this GIE is to operate onInteger, >0
剛才提到Secondary元件必須仰賴Primary元件的輸出,而nvinfer就是透過gie-unique-id和operate-on-gie-id這兩個欄位去作為Secondary元件搜尋Metadata的依據。DeepStream中的每一個nvinfer及nvinferserver(後續會提到)都可以設定gie-unique-id,元件會在輸出的Metadata加上這個ID,後續Secondary元件就會從Metadata中ID是operate-on-gie-id的這些數據,根據數據夾帶的Bounding Box去裁切輸入影像。
Class IDs of the parent GIE on which this GIE is to operate onSemicolon delimited integer arrayoperate-on-class-ids=1;2Operates on objects with class IDs 1, 2generated by parent GIEIf operate-on-class-ids is set to -1,it will operate on all class-ids
Primary元件會給每一個物件一個類別ID,取決於模型的輸出,如果這個屬性有設定非空字串,Secondary元件就會只針對設定的類別ID做推論。舉例Primary元件輸出Person的ID是0、Car的ID是1,那Secondary元件可以設定operate-on-class-ids=1,那麼元件就只會找出類別是Car的Bounding Box去推論。
Specifies the number of consecutive batches to be skipped for inferenceInteger, >0
如果模型不需要每一幀都推論,那麼可以設定這個欄位,例如設定3就是每3幀推論一次。
net-scale-factorPixel normalization factor (ignored if input-tensor-meta enabled)Float, >0.0
offsetsArray of mean values of color components to be subtracted from each pixel. Array length must equal the number of color components in the frame. The plugin multiplies mean values by net-scale-factor.(ignored if input-tensor-meta enabled)Semicolon delimited float array, all values ≥0
DeepStream在Tensor前處理運算方式有點不同,所謂前處理其實就是Normalize,我們先介紹Pytorch是怎麼做的。
在Pytorch中Normalize要提供mean和std兩個參數,各自有三個數值對應到RGB三個通道,運算方式如下:
output[channel] = input[channel] - mean[channel] / std[channel]
要注意上述公式的input已經轉為[0.0,1.0]區間
而DeepStream這邊則是用一個net-scale-factor去乘上每個像素值,再根據RGB三個通道加上offsets,運算方式如下
output[channel] = input[channel] x net-scale-factor + offset[channel]
net-scale-factor負責處理像素轉Tensor,通常是
1.0 / 255.0 = 0.0039215686274509
但如果你對數學夠敏銳可以發現,offsets不論三個數值怎麼設定,都沒辦法完全等效PyTorch的算法,如果只有mean/std是沒問題的,但是還有input/std,這部分在DeepStream就只能選一個近似值讓差距不要太大。
Pathname of the ONNX model fileString
因為TensorRT模型是綁定硬體的,所以為了方便佈署程式,DeepStream允許只提供ONNX模型,Runtime階段會再用本機硬體編譯成TensorRT模型,就像Day 5跟大家示範的情況相同。另外這個值可以是絕對路徑或是相對於設定檔的路徑。
Pathname of the serialized model engine fileString
元件會先嘗試從這個路徑去找編譯好的TensorRT模型,如果找到了就直接開始推論,如果沒找到或是載入失敗(無效檔案)就會用onnx-file重新編譯TensorRT模型,並將編譯完成的模型寫入這個欄位設定的路徑。
network-modeData format to be used by inferenceInteger 0: FP32 1: INT8 2: FP16 3: BEST
模型運算用的精度,注意是運算用,模型還是維持ONNX中的精度,只是運算用更低的精度換取速度,只要模型輸出沒有受精度影響太多,都建議至少用FP16作為運算精度。INT8則必須在編譯時提供int8-calib-file,讓TensorRT做量化數值比對,這個檔案是用IInt8EntropyCalibrator2產生的,通常NVIDIA的模型如果有做INT8量化都會提供這個檔案。BEST則是可以混用INT8和FP16,沒有量化的節點就轉為FP16運算,否則預設是FP32。
這邊提醒大家一件事情,目前DeepStream9.1使用的TensorRT版本(10.16)仍然可以調整這些精度參數,但是在TensorRT 11.x的版本已經棄用了這個做法,之後都必須採用已經降精度的ONNX模型(FP16、INT8)去編譯TensorRT模型,編譯器會直接參照ONNX內的Tensor精度去運算。
TensorRT 11.x removes all precision flags (--fp16, --int8, --bf16, --fp8, --int4, --best) because all networks are strongly typed. Use ModelOpt AutoCast to produce a mixed-precision model before building.原文連結
Number of frames or objects to be inferred together in a batchInteger, >0
模型的最大批次數。這是TensorRT模型需要邊界,在處理動態維度時,我們需要提供編譯器最大的維度,編譯器用會這個數值去作為推論上限,模型在推論前可以指定1~batch-size作為推論要使用的維度。
這衍生另外一個個問題,如果實際要推論的批次數大於這個數字怎麼辦?那就是把剩下的數量作為batch-size再推論一次,一直重複到整個批次推論完。
Number of classes detected by the networkInteger, >0
設定模型輸出有幾個類別。
maintain-aspect-ratioIndicates whether to maintain aspect ratio while scaling input.Boolean
前處理縮放原始影像時是否要保持長寬比,這取決於你的模型訓練時的前處理方式。維持長寬比時,通常長、寬會有一邊是小於輸入大小的,nvinfer會把剩下的像素填上0。
symmetric-paddingIndicates whether to pad image symmetrically while scaling input. DeepStream pads the images asymmetrically by default.
簡單來說就是你要把縮放後的影像用左上對齊還是置中對齊,這個通常是影響到模型輸出的後處理,只要跟你的處理方式跟設定是匹配的就好。
network-typeType of networkInteger0: Detector1: Classifier2: Segmentation3: Instance Segmentation100: Other (沒寫在官方文件上)
DeepStream只支援Detector、Classifier、Segmentation、Instance Segmentation這幾種模型,不再上述的就使用Other搭配output-tensor-meta
output-tensor-metaGst-nvinfer attaches raw tensor output as Gst Buffer metadata.Boolean
另外Segmentation模型在官方宣傳時看似美好,但我從DeepStream 7.1一路使用到9.1,都沒有辦法正常渲染模型的輸出,渲染的位置會跑掉,而且是錯的。如果有人知道原因是什麼的也歡迎告訴我哦,至少在這邊我的建議是把output-tensor-meta打開,自己實作程式碼去處理。
Absolute pathname of a library containing custom method implementations for custom modelsString
如果你不是用官方宣傳DeepStream用的那幾個模型,基本上一定要用上這幾個欄位。每個模型都有自己的後處理方式,DeepStream有定義後處理的function prototype,我們只要照著prototype實作function並編譯成動態庫(Shared Library)。接著config-file-path設定為動態庫的路徑,parse-<type>-func-name則是根據network-type去選擇要設定哪一個,例如network-type=0 (Detector)就要設定parse-bbox-func-name,把它設定為你實作的function name。
我們拿官方預先寫好的後處理動態庫作為例子,檔案在/opt/nvidia/deepstream/deepstream/sources/libs/nvdsinfer_customparser/nvdsinfer_custombboxparser.cpp,實作的部分因為是根據模型而有不同,就不做介紹了,請大家直接去看範例來瞭解每個參數的功能吧。以這個範例來說,要設定parse-bbox-func-name=NvDsInferParseCustomResnet
bool NvDsInferParseCustomResnet (std::vector<NvDsInferLayerInfo> const &outputLayersInfo,
NvDsInferNetworkInfo const &networkInfo,
NvDsInferParseDetectionParams const &detectionParams,
std::vector<NvDsInferObjectDetectionInfo> &objectList)
{
/* Implementation */
}
Clustering algorithm to use.Integer0: OpenCV groupRectangles()1: DBSCAN2: Non Maximum Suppression3: DBSCAN + NMS Hybrid4: No clustering
Detector模型的原始輸出通常包含數千到上萬個Bounding Box,這個欄位設定的算法可以用來消除重複的Bounding Box,根據自己的需求去選擇算法即可,常用的如NMS算法。官方文件有對於算法的詳細說明,請參考這裡
這個欄位群組則是設定你後處理完的數據該怎麼保留,保留下來的數據才會附加到Metadata上。
post-cluster-thresholdDetection threshold to be applied prior to clustering operationFloat, ≥0
在做cluster算法前先篩掉信心度低於閥值的Bounding Box,可以減少計算的負擔。
post-cluster-thresholdDetection threshold to be applied post clustering operationFloat, ≥0
cluster運算結束後,從剩下的Bounding Box篩去低於閥值的。
Maximum IOU score between two proposals after which the proposal with the lower confidence will be rejected.Float, ≥0
用NMS算法時的IOU閥值,如果你對這個名詞不熟,簡單來說就是兩個Bounding Box的重疊率。
Keep only top K objects with highest detection scores.Integer, ≥0. -1 to disable
最後要附加到Metadata的Bounding Box上限。
上述這些是比較有機會用到的欄位,如果有需要更細節的設定就再去細讀官方文件囉。
終於結束今天的介紹了,好多欄位要設定啊。希望今天的內容有幫助到對DeepStream還很陌生的你,關於模型的部分我只簡單帶過,內容還是以框架為主。