2021/03/02 軟體版本更新至 v2.7.0

前言

隨著深度學習技術快速的成長,在應用環境中部署和運行 AI 模型的需求也與日俱增。然而開發一個推論解決方案來部署這些模型是一項艱鉅的任務,延遲、吞吐量、支援多個 AI 框架、並行多個模型、GPU 最佳化 … 等因素,皆是要考慮到的重點,因此如何進行快速部署及管理成為一件複雜卻必須做的事情。

NVIDIA Triton Inference Server 由 NVIDIA 釋出的一套開源軟體 — 模型推論解決方案,具低延遲、高吞吐等特性,可透過 HTTP 或 GRPC 端點提供客戶端推理服務及服務管理,大幅簡化 AI 模型部署的流程。

由於 Triton 系統龐大再加上篇幅限制,本章注重在基本功能的操作,較深的功能將於日後配合專案來介紹。


介紹

Triton 走的是 Client-Server 架構。
Server 端主要功能為傳接資料,模型推論及管理。
Client 端則為傳接資料,透過 Triton Client API,自行結合如網頁、手機 APP 等來實現與 Triton Server 的通訊。

特性

  • 支援多種 AI 框架
    • TensorRT (plan)
    • ONNX (onnx)
    • TorchScript (pt)
    • Tensorflow (graphdef, savedmodel)
    • OpenVINO (xml+bin)
  • 支援客製化模組
    • Python (py)
    • DALI (dali)
  • 可同時運行多個模型
  • 可將多個模型串接成一個大模型 (Ensemble Model)
  • 支援 HTTP 和 GRPC 的接口
  • 支援將模型庫放在 Google Cloud Storage 、 Amazon S3 或 Azure Storage 中
  • 具自我監控功能,可顯示 GPU 的利用率,服務器的吞吐量,以及服務器延遲等指標
  • 適用於部署框架 (Kubernetes)
  • 允許使用自定義後端 (C++ 或 Python)

版本

  • 2.7.0

Server

系統環境

  • OS:Ubuntu 20.04
  • GPU Driver:460.27.04
  • Docker:19.03.14
  • Docker Image:nvcr.io/nvidia/tritonserver:21.02-py3

安裝步驟

安裝 Docker

本篇是將系統架設在 Docker 上,可以參考此文章 將 Docker 環境建立起來。

下載映像檔

1
sudo docker pull nvcr.io/nvidia/tritonserver:21.02-py3

運行步驟

模型轉換

系統支援的模型如下,如訓練時所使用的框架非下述所示,則需進行轉換。

  • Tensorflow (graphdef, savedmodel)
  • TensorRT (plan)
  • ONNX (onnx)
  • PyTorch (pt)
  • OpenVINO (xml+bin)

為了增加系統效能,建議模型都要過 TensorRT 編譯。

建立模型庫

模型庫中保存著多個模型包。
每個模型包皆由版本及設定檔所組成,版本建議以自然數依序命名 (Ex. 1,2,3…)。

建立模型庫範例如下圖所示:
以此範例來說
模型庫為 model-repository
模型庫中共存放了4個模型包 (mnistTensorrt, mnist-tftrt, tensorflowGraphdef, tensorflowSavedmodel)
其中以 mnist-tftrt 模型包為例,mnist-tftrt 為此模型的命名,資料夾中存放了版本 1 的模型及設定檔。

編輯設定檔

每個模型包皆須有設定檔,系統會根據設定檔來運行模型,最基本的設定檔由 5 大元素組成:模型名稱、模型框架、最大批次大小、輸入及輸出層的資訊、GPU 實例資訊。

更詳盡的參數說明可以參考官方文件

編輯設定檔範例如下圖所示:

設定檔中的模型名稱需與資料夾的模型名稱一致。

最大批次大小為推論批次設定的最大值,不一定是實際推論批次大小。

運行伺服器端

在運行前需指定此服務要跑幾個 GPU、走哪些 Port 以及模型庫的路徑。

1
2
3
4
sudo docker run -d --gpus <GPU Number> --name <Conatainer Name> --shm-size=1g --ulimit memlock=-1 --ulimit stack=67108864 -p <Host Port>:8000 -p <Host Port>:8001 -p <Host Port>:8002 -v <Model Repository Path>:/models nvcr.io/nvidia/tritonserver:21.02-py3 tritonserver --model-store=/models

# Example
sudo docker run -d --gpus all --name Triton_Server --shm-size=1g --ulimit memlock=-1 --ulimit stack=67108864 -p 8000:8000 -p 8001:8001 -p 8002:8002 -v /home/user/Documents/model-repository:/models nvcr.io/nvidia/tritonserver:21.02-py3 tritonserver --model-store=/models

Port 8000 為 HTTP 協定通道
Port 8001 為 GRPC 協定通道

檢查系統狀態

檢查系統是否正常的運行中:

1
curl -v localhost:8000/v2/health/ready

檢查模型的狀態:

1
GET v2/models[/${MODEL_NAME}[/versions/${MODEL_VERSION}]]/stats

Triton 提供 Prometheus Metrics,內容主要為 GPU 狀態和請求統計資訊等:

1
http://localhost:8002/metrics

Client

系統環境

  • OS:Ubuntu 20.04
  • Docker:19.03.14
  • Docker Image:nvcr.io/nvidia/tritonserver:21.02-py3-sdk

安裝步驟

安裝 Docker

本篇是將系統架設在 Docker 上,可以參考此文章 將 Docker 環境建立起來。

下載映像檔

1
sudo docker pull nvcr.io/nvidia/tritonserver:21.02-py3-sdk

運行步驟

串接設備

目前主流要推論的資料為數據、影像、聲音以及文字,在跟 Server 端溝通之前,Client 端需先跟各設備進行串接。

如影像型推論,就需跟攝影機、機台、儲存設備、呈現平台…等串接。

撰寫程式碼

Client 端程式碼撰寫主要分成三大部分,依序為

  • 前處理
    此部分主要處裡的事項為資訊進模型前的所有處理,如影像解碼、維度轉換、訊號處理等等。
  • Client-Server 溝通
    指定將推論的模型資訊,透過 HTTP 或 GRPC 跟 Server 溝通。
    官方範例可以參考官方範例
  • 後處理
    此部分主要處裡的事項為接到 Server 回傳來推論結果後的所有處理,如儲存檔案、訊息整理、結果呈現等等。

前後處理會因不同的應用實例有所差異,此部分不是本章的重點。
本章將會著重在說明 Triton Client-Server 基本的溝通函數,其他更深入的函數將在日後介紹。

運行客戶端

在運行前需確認 Server 的 IP、走的協定 (HTTP or GRPC) 以及讀取儲存資料的位置。

1
2
3
4
sudo docker run -d --name <Container Name> -v <Data Path>:/data nvcr.io/nvidia/tritonserver:21.02-py3-sdk bash -c '<Client Code>'

# Example
sudo docker run -d --name Triton_Client -v /home/user/Documents/data:/data nvcr.io/nvidia/tritonserver:21.02-py3-sdk bash -c 'python /data/client.py'

範例

接下來將以大家耳熟能詳的 MNIST 所訓練出的模型當作範例,由於訓練模型不是本篇的重點,可以直接下載訓練好的模型進行後續的練習。

Server

訓練模型

此部分以 MNIST 資料集和簡單的 CNN 模型當作範例。
撰寫框架為 TensorFlow v2,來源為官方範本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
import tensorflow.compat.v2 as tf
import tensorflow_datasets as tfds

tf.enable_v2_behavior()

(ds_train, ds_test), ds_info = tfds.load(
'mnist',
split=['train', 'test'],
shuffle_files=True,
as_supervised=True,
with_info=True,
try_gcs=True
)

def normalize_img(image, label):
"""Normalizes images: `uint8` -> `float32`."""
return tf.cast(image, tf.float32) / 255., label

ds_train = ds_train.map(
normalize_img, num_parallel_calls=tf.data.experimental.AUTOTUNE)
ds_train = ds_train.cache()
ds_train = ds_train.shuffle(ds_info.splits['train'].num_examples)
ds_train = ds_train.batch(128)
ds_train = ds_train.prefetch(tf.data.experimental.AUTOTUNE)


ds_test = ds_test.map(
normalize_img, num_parallel_calls=tf.data.experimental.AUTOTUNE)
ds_test = ds_test.batch(128)
ds_test = ds_test.cache()
ds_test = ds_test.prefetch(tf.data.experimental.AUTOTUNE)


model = tf.keras.models.Sequential([
tf.keras.layers.Flatten(input_shape=(28, 28, 1)),
tf.keras.layers.Dense(128,activation='relu'),
tf.keras.layers.Dense(10)
])
model.compile(
optimizer=tf.keras.optimizers.Adam(0.001),
loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),
metrics=[tf.keras.metrics.SparseCategoricalAccuracy()],
)

model.fit(
ds_train,
epochs=6,
validation_data=ds_test,
)

## 模型儲存
model.save('model.savedmodel')

建立模型庫

以上面的模型為例,建立出的模型庫如下圖所示。

編輯設定檔

以下的參數為必要欄位,其他深入或客製化的參數,可以參考官網文件

使用 gpu_execution_accelerator 可在運行 Triton 時,轉成 TensorRT 來加速。
只支援 TensorFlow、ONNX。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
name: "mnist"
platform: "tensorflow_savedmodel"
max_batch_size: 32
input [
{
name: "flatten_input"
data_type: TYPE_FP32
format: FORMAT_NHWC
dims: [28, 28, 1]
}
]
output [
{
name: "dense_1"
data_type: TYPE_FP32
dims: [10]
}
]
instance_group [
{
kind: KIND_GPU
count: 2
}
]

optimization { execution_accelerators {
gpu_execution_accelerator : [ {
name : "tensorrt"
parameters { key: "precision_mode" value: "FP16" }}]
}}

version_policy { latest { num_versions: 1 } }

dynamic_batching {
preferred_batch_size: [ 4, 8 ]
max_queue_delay_microseconds: 100
}

運行伺服器端

1
sudo docker run -d --gpus all --name Triton_Server --shm-size=1g --ulimit memlock=-1 --ulimit stack=67108864 -p 8000:8000 -p 8001:8001 -p 8002:8002 -v /home/user/Documents/model-repository:/models nvcr.io/nvidia/tritonserver:21.02-py3 tritonserver --model-store=/models --backend-config=tensorflow,version=2 --backend-config=tensorflow,allow-soft-placement=0

運行伺服器端後,成功的畫面如下所示。

檢查系統狀態

1
curl -v localhost:8000/v2/health/ready
1
curl localhost:8000/v2/models/mnist/versions/1/stats
1
http://localhost:8002/metrics

部分 Metrics 畫面:

Cleint

串接設備

由於目前沒有實際的設備可以串接,此部分直接取用 MNIST 測試資料集中一張影像當作範例,最後將接到的推論結果直接顯示在螢幕上。

MNIST 測試資料如下圖所示,將圖片放置掛載的目錄下 (如範例 /home/user/data)。

撰寫程式碼

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
from PIL import Image
import numpy as np
import tritonclient.grpc as grpcclient
from tritonclient.utils import triton_to_np_dtype

## 前處理
img = Image.open('input.jpg').convert('L')
img = img.resize((28, 28))
imgArr = np.asarray(img)/255
imgArr = np.expand_dims(imgArr[:, :, np.newaxis], 0)
imgArr = imgArr.astype(triton_to_np_dtype('FP32'))

## Client-Server 溝通
triton_client = grpcclient.InferenceServerClient(url='localhost:8001', verbose=0)
inputs = []
inputs.append(grpcclient.InferInput('flatten_input', imgArr.shape, 'FP32'))
inputs[0].set_data_from_numpy(imgArr)
outputs = []
outputs.append(grpcclient.InferRequestedOutput('dense_1',class_count=0))
responses = []
responses.append(triton_client.infer('mnist',inputs,
request_id=str(1),
model_version='1',
outputs=outputs))

## 後處理
print (np.argmax(responses[0].as_numpy('dense_1')[0]))

運行客戶端

將客戶端環境運行起來後,執行撰寫好的程式碼,即可進行推論。

1
sudo docker run -it --rm --name Triton_Client -v /home/user/data:/data nvcr.io/nvidia/tritonserver:21.02-py3-sdk bash -c 'python /data/client.py'

最後 MNIST 推論結果如下圖所示,可以發現有成功的預測出數字。


其他文章

日後陸陸續續撰寫此套件各個功能的說明與範例,讓各位能快速的上手並應用在實際場域中。


參考資料

  1. Triton Inference Server
  2. Triton Inference Server Documentation