前面介紹的nvinfer是用本地模型推論,而今天要介紹的nvinferserver則是使用Triton Inference Server(後續簡稱Triton Server)運行推論服務。為什麼是推論服務呢?因為Triton Server是一個模型託管服務,它可以集中管理模型,透過遠程調用(RPC)的方式執行推論請求,而模型的調度則是在伺服端內完成,客戶端不必處理調度的細節。
nvinferserver這個元件其實跟nvinfer的功能完全相同,只是如同剛才所說的是透過遠程調用完成推論,不過因為它的設定檔跟系統配置方式截然不同,所以還是得單獨出來介紹。那我會以nvinfer介紹過的設定欄位來對照nvinferserver的設定欄位。
設定檔請參照/opt/nvidia/deepstream/deepstream/sources/apps/sample_apps/deepstream-test1/dstest1_pgie_nvinferserver_config.txt
因為nvinferserver這邊把設定群組分開許多,所以我會以這邊的每個群組去對照nvinfer的欄位,順序會不一樣。
infer_config群組infer_config {
unique_id: 1
gpu_ids: [0]
max_batch_size: 30
[...]
}
| nvinfer | nvinferserver |
|---|---|
| gie-unique-id | unique_id |
| batch-size | max_batch_size |
這邊多了這一項是Triton Server特有的功能。因為推論任務抽象化,我們可以設定Triton Server使用多個GPU資源去執行推論任務,客戶端只管發請求,不必處理不同GPU之間的調度。
backend群組backend {
inputs: [ {
name: "input_1:0"
}]
outputs: [
{name: "output_cov/Sigmoid:0"},
{name: "output_bbox/BiasAdd:0"}
]
[...]
}
inputs和outputs這個兩個是用來描述輸入和輸出tensor,官方文件的描述這兩項是Optional,當backend需要明確知道這些資訊時才要寫上去,就不多作介紹囉。
triton群組triton {
model_name: "Primary_Detector"
version: -1
[...]
}
模型是以固定的資料夾結構去區分,這個model_name就等於是資料夾名稱。
模型的版本號。模型主目錄下會再以數字做為資料夾名稱,放置各個版本的模型,例如version: 1就去主目錄底下的1資料夾找模型,而version: -1則是使用數字最大的那個資料夾。
model_repo群組Triton Server有提供C-API模式,這個模式不用啟動Server,而是將建立in-memory的Server實例,所以所有的CUDA Memory都是在同一個進程內,效率幾乎等效於直接調用TensorRT模型。這個群組就是C-API模式需要的設定。
model_repo {
root: "../../../../samples/triton_model_repo"
strict_model_config: true
}
所有的模型都是以主目錄在往下分層,root就是那個主目錄。
每個模型目錄底下都要放一個設定檔,預設的設定檔是config.pbtxt。這個屬性啟用的時,如果你有指定要用其他設定檔就要使用這一項,但通常預設的那一份就夠用了,而官方是建議設定為true就好。
preprocess群組preprocess {
network_format: MEDIA_FORMAT_NONE
tensor_order: TENSOR_ORDER_LINEAR
tensor_name: "input_1:0"
maintain_aspect_ratio: 0
frame_scaling_hw: FRAME_SCALING_HW_DEFAULT
frame_scaling_filter: 1
[...]
}
| nvinfer | nvinferserver |
|---|---|
| model-color-format | network_format |
| network-input-order | tensor_order |
| maintain-aspect-ratio | maintain_aspect_ratio |
| scaling-compute-hw | frame_scaling_hw |
| scaling-filter | frame_scaling_filter |
有幾項是nvinfer設定檔沒提到的,就在這邊進一步介紹
預設都是將影像以RGB格式轉為Tensor,但是有些模型訓練時是使用BGR格式,所以這個屬性可以設定
MEDIA_FORMAT_NONE
IMAGE_FORMAT_RGB
IMAGE_FORMAT_BGR
IMAGE_FORMAT_GRAY
至於為什麼跑出一個MEDIA_FORMAT_NONE,我也不知道,我猜是直接複製不管數據排列。
預設都是使用NCHW,但有些老一點的模型是用NHWC,當你有需求時就要改這個設定
TENSOR_ORDER_NONE
TENSOR_ORDER_LINEAR (NCHW)
TENSOR_ORDER_NHWC
TENSOR_ORDER_NONE的說明如下,老實說我也看得不是很懂。總之沒意外的話都還是直接設定TENSOR_ORDER_LINEAR
It can deduce the value from backend layers info if set to TENSOR_ORDER_NONE
當模型有多個輸入時,可以用這個欄位設定你要執行前處理的tensor。如果只有一個輸入,那麼這項可以忽略不設定。
模型的輸入都會給定一個固定尺寸,DeepStream的作法是先將影格縮放到跟輸入相同尺寸的影像,例如1280x720縮放到640x640,接著再將縮放後的影像轉換為Tensor。這裡就牽涉到縮放的運算是要在哪個硬體上完成,有以下這些選項
FRAME_SCALING_HW_DEFAULT
FRAME_SCALING_HW_GPU (Default for dGPU)
FRAME_SCALING_HW_VIC (Default for Jetson)
預設是選擇FRAME_SCALING_HW_DEFAULT,元件會根據平台切換到FRAME_SCALING_HW_GPU或FRAME_SCALING_HW_VIC。GPU大家都可以理解,就是用CUDA運算,但VIC是什麼呢?
全名是Video and Image Compositor,還記不記得先前提到的硬體編碼/解碼器,VIC也是屬於為特定任務開發的硬體,專門負責影像處理。至於為什麼要有VIC這個硬體,大概是因為Jetson這種邊緣平台的GPU資源非常珍貴,而推論任務以NVIDIA的硬體來說也只能在GPU上運作,因此VIC的存在可以將影像處理的任務從GPU上卸載出來。
VIC雖然可以處理影像縮放,但是根據我的開發經歷,使用上是有一些限制的
縮放算法。直接以經驗跟網路上的資料上結論:
NvBufSurfTransformInter_Nearest(0)快速但容易產生鋸齒NvBufSurfTransformInter_Bilinear(1)慢一些但更平滑normalize群組normalize {
scale_factor: 0.00392156862745098
channel_offsets: [0, 0, 0]
}
| nvinfer | nvinferserver |
|---|---|
| net-scale-factor | scale_factor |
| offsets | channel_offsets |
postprocess群組postprocess {
labelfile_path: "../../../../samples/models/Primary_Detector/labels.txt"
[...]
}
| nvinfer | nvinferserver |
|---|---|
| labelfile-path | labelfile_path |
將模型輸出的數值對照成文字的檔案,要按照DeepStream設計的形式去描述。有提供這份檔案的話,元件會自動幫你加在輸出的Metadata上。
detection群組detection {
num_detected_classes: 4
per_class_params {
key: 0
value { pre_threshold: 0.4 }
}
nms {
confidence_threshold:0.2
topk:20
iou_threshold:0.5
}
}
這邊相較nvinfer,設定就分類的比較明確,總之你要用哪個就寫哪個,參數就從官方文件去搜尋。這部分參數比較多而且大同小異,就不多介紹了。
input_control群組input_control {
process_mode: PROCESS_MODE_FULL_FRAME
operate_on_gie_id: -1
interval: 0
}
| nvinfer | nvinferserver |
|---|---|
| process-mode | process_mode |
| operate-on-gie-id | operate_on_gie_id |
| interval | interval |
PROCESS_MODE_FULL_FRAME對應到Primary模式PROCESS_MODE_CLIP_OBJECTS對應到Secondary模式grpc群組範例的設定檔沒有,但必須要介紹,所以我們參考另外一個檔案/opt/nvidia/deepstream/deepstream/configs/deepstream-app-triton-grpc/config_infer_plan_engine_primary.txt。
grpc {
url: "localhost:8001"
enable_cuda_buffer_sharing: true
}
Triton Server以獨立的進程啟動時,有提供HTTP和gRPC兩種遠程調用的接口,而gRPC的優勢在於序列化跟反序列化是二進制數據,性能優勢更高,串流應用會建議使用gRPC接口去調用
Triton Server的通訊位置,預設通訊埠是8001。
這項特別重要,只有Triton Server運行在同一個主機上時可以使用,也可以是同主機、不同容器。因為預設的行為是Client會將整個Tensor序列化後透過網路發送,到Server再返序列化後複製到CUDA Memory,一聽就知道這個調用方式會直接拖垮性能,不可能用在即時串流。
這個屬性就是讓模型使用CUDA Shared Memory作為Tensor的儲存空間,遠程調用時的訊息只傳遞記憶體的位置,因為是共享記憶體,所以Server收到時可以直接從這個位置去操作該記憶體,完全消除了複製的成本,性能也是逼近直接調用TensorRT模型。
設定參數實在太多了,又是好長的一篇文章,希望可以幫助初次使用DeepStream的人來解少查詢這些設定用意的時間,畢竟我當時看到相同功能的元件還得分兩種設定文件看,屬實非常痛苦。跟nvinfer一樣,明天我們就單獨介紹Triton Server怎麼使用吧。