Overview
Trong bài viết lần trước, mình đã trình bày về tổng quan lý thuyết về Triton Inference Server bao gồm kiến trúc của Triton, các tính năng chính, thông tin cơ bản và một số Triton Server Tool. Ta hãy cùng sơ lược lại một chút thông tin.
Triton Inference Server là gì ?
Triton Inference Server là một "open source inference serving software", cho phép deploy các AI model từ nhiều deep-machine learning frameworks, bao gồm TensorRT, TensorFlow, PyTorch, ONNX, OpenVINO, v.v. Triton hỗ trợ inference trên cloud, data center và thiết bị nhúng trên GPU NVIDIA, x86 và CPU ARM hoặc AWS Inferentia cũng như mang lại hiệu suất được tối ưu hóa cho nhiều loại truy vấn, bao gồm real-time, batched, ensembles và audio/video streaming. Ngoài ra, Triton còn có thể dễ dàng tích hợp với các công cụ khác như Kubernetes, Kserve, Prometheus hay Grafana. Tóm lại, đây là một nền tảng phần mềm giúp tăng tốc quy trình khoa học dữ liệu và tối đa hóa hiệu suất và hiệu quả của các ứng dụng AI.
Features
- Hỗ trợ nhiều Deep Learning Frameworks
- Dynamic Batching
- Concurrent model execution
- Cung cấp Backend API cho phép thêm Triton Custom Backends
- Ensembling models
- HTTP/REST và GRPC inference protocols
- Metrics: throughput, latency, ...
Triton Basics
- Model repository: Cấu trúc
- Model Configuration: Cấu hình và tùy chọn model
- Model management: Quản lý mô hình
Triton Tool
- Triton Performance Analyzer: CLI tool có thể giúp tối ưu hóa hiệu suất inference của các mô hình chạy trên Triton Inference Server bằng cách đo lường những thay đổi về hiệu suất khi thử nghiệm các chiến lược tối ưu hóa khác nhau
- Triton Model Analyzer: tool sử dụng Performance Analyzer để gửi yêu cầu đến mô hình trong khi đo bộ nhớ GPU và mức sử dụng điện toán. Nó tự động xác định các cấu hình mô hình tùy chọn trong Triton để tối đa hóa hiệu suất
Triton Tutorial
Ở trên là các thông tin cơ bản liên quan đến Triton Inference Server mà trong bài viết lần trước mình đã đề cập. Trong bài viết này, mình sẽ trình bày cách thức cơ bản nhất để có thể cài đặt và sử dụng Triton Inference Server.
Installation
Triton Inference Server có sẵn dưới dạng buildable open-source code, nhưng cách dễ nhất để cài đặt và chạy Triton là spre-built Docker image có trên NVIDIA GPU Cloud (NGC). Việc khởi chạy và duy trì Triton Inference Server xoay quanh việc sử dụng kho lưu trữ mô hình xây dựng. Tutorial này sẽ bao gồm:
- Tạo Model repository
- Lauching Triton
- Gửi Inference request
Tạo Model repository
Model repository là thư mục nơi bạn đặt các mô hình mà bạn muốn Triton phục vụ. Trong tutorial lần này, mình sẽ sử dụng 2 model được train từ thư viện Pytorch Lightning là resnet34 và Unet cho 2 tác vụ phân loại và segment cho bài toán xác định tàu thuyển từ ảnh chẳng hạn, nên lưu ý rằng các mô hình cần được convert thành dạng TorchScript nếu platform được sử dụng là Pytorch. Dưới đây là cấu trúc của folder model_repository và một ví dụ của file config.pbtxt
cho model unet:
# model_repository structure
model_repository
├── ensemble_model
│ ├── 1
│ └── config.pbtxt
├── resnet34
│ ├── 1
│ │ └── model.pt
│ └── config.pbtxt
└── unet34 ├── 1 │ └── model.pt └── config.pbtxt
# config.pbtxt
name: "unet34"
platform: "pytorch_libtorch"
max_batch_size : 6
input [ { name: "input_image_unet34" data_type: TYPE_FP32 dims: [ 3, 768, 768 ] }
]
output [ { name: "SEGMENTATION_OUTPUT" data_type: TYPE_FP32 dims: [ 1, 768, 768 ] }
]
instance_group [ { count: 2 kind: KIND_GPU }
] dynamic_batching {}
Trong Trong ví dụ trên mình sử dụng mô hình U-Net cho bài toán segmentation với đầu vào là 1 ảnh RGB có kích thước 768x768 với loại dữ liệu và FP32 và đầu ra sẽ là 1 mask có cùng kích thước với ảnh đầu vào.
Launching Triton
Triton được tối ưu hóa để cung cấp hiệu suất suy luận tốt nhất bằng cách sử dụng GPU, nhưng nó cũng có thể hoạt động trên các hệ thống chỉ có CPU. Cách sử dụng Triton dễ nhất là thông qua Docker. Hãy đảm bảo máy bạn đã cài đặt Triton Trong cả hai trường hợp, bạn có thể sử dụng cùng một Triton Docker image. Trong tutorial lần này, mình sẽ sử dụng Triton Inference Server phiên bản 22.12, bạn có thể sử dụng phiên bản mới hơn tùy mục đích.
Để chạy hệ thống với GPUs, sử dụng câu lệnh
docker run --gpus all --rm -p8000:8000 -p8001:8001 -p8002:8002 -v ${PWD}/model_repository:/models nvcr.io/nvidia/tritonserver:22.12-py3 tritonserver --model-repository=/models
Chạy trên hệ thống chỉ có CPU, sử dụng câu lệnh dưới đây, ta sẽ bỏ flag --gpus
, còn lại các tham số thì y hệt ở trên (có thể thay thế <xx.yy> bằng version khác:
docker run --rm -p8000:8000 -p8001:8001 -p8002:8002 -v ${PWD}/model_repository:/models nvcr.io/nvidia/tritonserver:<xx.yy>-py3 tritonserver --model-repository=/models
Bởi vì flag --gpus
không được sử dụng, GPU sẽ không khả dụng và Triton do đó sẽ không thể tải bất kỳ cấu hình model nào yêu cầu GPU.
Nếu như trên máy local không có docker images của triton server, trong lần đầu chạy câu lệnh, Tritonserver image sẽ tự động được pull về local.
Xác minh Triton đã chạy
Sử dụng Triton's ready endpoint để xác minh rằng máy chủ và các mô hình đã sẵn sàng cho inference. Từ host system, hãy sử dụng curl
để truy cập HTTP endpoint cho biết trạng thái máy chủ. Nếu HTTP request trả về status 200 nghĩa là Triton đã sẵn sàng và non-200 nghĩa là chưa sẵn sàng.
$ curl -v localhost:8000/v2/health/ready ...
* Trying 127.0.0.1:8000...
* Connected to localhost (127.0.0.1) port 8000 (#0)
> GET /v2/health/ready HTTP/1.1
> Host: localhost:8000
> User-Agent: curl/7.81.0
> Accept: */*
> * Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Content-Length: 0
< Content-Type: text/plain
< * Connection #0 to host localhost left intact
Triton Logs
Sau khi sử dụng docker run
để khởi tạo và chạy Triton server , ta có thể check logs của container để kiểm tra xem các model nào đã sẵn sàng cho việc inference cũng như check các protocol sevice port, các config của triton container. Hãy cùng điểm qua một số log quan trọng và cần thiết:
I1102 04:11:21.983121 1 server.cc:633] +----------------+---------+--------+
| Model | Version | Status |
+----------------+---------+--------+
| ensemble_model | 1 | READY |
| resnet34 | 1 | READY |
| unet34 | 1 | READY |
+----------------+---------+--------+
Nếu model đã được load thành công và sẵn sàng cho inference, Triton container sẽ log ra như trên, ta có thể thấy các mô hình đã được đánh version và status READY
. Nếu không phải READY
, ta có thể check status để biết vấn đề hiện tại của model.
I1102 04:11:22.028262 1 grpc_server.cc:4819] Started GRPCInferenceService at 0.0.0.0:8001
I1102 04:11:22.028982 1 http_server.cc:3477] Started HTTPService at 0.0.0.0:8000
I1102 04:11:22.070508 1 http_server.cc:184] Started Metrics Service at 0.0.0.0:8002
W1102 04:11:23.023508 1 metrics.cc:603] Unable to get power limit for GPU 0. Status:Success, value:0.000000
Ngoài ra ta cũng có thể thấy được GRPCInferenceService
được mở port 8001
, Metrics Service
port 8002
và HTTPService
được mở port 8000
để truy cập cho inference request.
Gửi Inference Request
Vậy là ta đã đi qua bước khởi tạo là launching Triton Inference Server, bước tiếp theo sẽ là gửi inference request đến server đã được run ở trên.
Tutorial từ Triton Documentation
Để thử gửi inference request, bạn có thể follow step-by-step được cung cấp bởi Triton Documention. Sử dụng docker pull để lấy client libraries và examples images từ NGC. Trong đó, <xx.yy> là vervion của Triton được pull như mình đã đề cập ở trên.
$ docker pull nvcr.io/nvidia/tritonserver:<xx.yy>-py3-sdk
Để run client image:
$ docker run -it --rm --net=host nvcr.io/nvidia/tritonserver:<xx.yy>-py3-sdk
Từ bên trong image nvcr.io/nvidia/tritonserver:<xx.yy>-py3-sdk
, hãy chạy image-client application để thực hiện phân loại hình ảnh bằng densenet_onnx model. Để gửi request tới densenet_onnx model sử dụng hình ảnh từ đường dẫn /workspace/images
. trong trường hợp này, mình sẽ tìm top 3 lớp phân loại:
$ /workspace/install/bin/image_client -m densenet_onnx -c 3 -s INCEPTION /workspace/images/mug.jpg
Request 0, batch size 1
Image '/workspace/images/mug.jpg': 15.346230 (504) = COFFEE MUG 13.224326 (968) = CUP 10.422965 (505) = COFFEEPOT
Để gửi yêu cầu cho mô hình dennnet_onnx, hãy sử dụng hình ảnh từ thư mục /workspace/images. Trong trường hợp này, chúng tôi yêu cầu 3 phân loại hàng đầu. Phía trên là bản hướng dẫn cơ bản và dễ thao tác được Triton cung cấp, bạn có thể tìm đọc để có các bước chạy chi tiết hơn từ Documentation của họ.
Python Package Installer
Một cách khác để gửi inference request tới Triton Inference Server, ta có thể sử dụng Python code. Đảm bảo máy bạn có sẵn pip hoặc anaconda, tạo môi trường và cài đặt các thư viện cần thiết, bao gồm:
pip install tritonclient[all]
Trước tiên, hãy import các thư viện cần thiết, đặc biệt là tritonclient
để bắt đầu cách mà ta có thể gửi inference request tới triton server:
import tritonclient.http as httpclient
import tritonclient.grpc as grpcclient
from tritonclient.utils import triton_to_np_dtype import numpy as np
from torchvision import transforms
..........
Ở phần trên thì mình đã có file config.pbtxt
ví dụ cho mô hình unet để phân đoạn xem vùng nào trong ảnh là thuyền. Dưới đây là ảnh minh họa cho tác vụ phân đoạn:
Trước tiên, ta có thể tiền xử lý ảnh một chút trước khi gửi inference request tới mô hình (code có thể không clean mong mọi người đừng oán trách):
def preprocess(image_path): image= Image.open(image_path).convert("RGB") image = np.array(image) transform = Compose( [ A.Resize(768, 768), A.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]), ToTensorV2(), ] ) return transform(image=image)["image"].numpy()
Tiếp theo ta có thể gửi request tới triton server. Trong đó, ta có thể xác định batch_size
cụ thể để có thể gửi nhiều ảnh trong 1 lần request. Tùy thuộc vào GPU mà batch_size
tối đa có thể khác nhau.
client = grpcclient.InferenceServerClient(url="localhost:8001") for i in range(0, len(image_files), batch_size): if i + batch_size < len(image_files): batch_images = np.array(list((preprocess(f"{folder_path}{image_files[j]}") for j in range(i, i + batch_size)))) else: batch_images = np.array(list((preprocess(f"{folder_path}{image_files[j]}") for j in range(i, len(image_files))))) # GRPC inputs = grpcclient.InferInput("input_image_unet34", batch_images.shape, datatype="FP32") inputs.set_data_from_numpy(batch_images) outputs = grpcclient.InferRequestedOutput("SEGMENTATION_OUTPUT") # Querying the server results = client.infer(model_name="unet34", inputs=[inputs]) inference_output = results.as_numpy('SEGMENTATION_OUTPUT').squeeze()
Trong đoạn code mẫu trên, ta sẽ đọc ảnh từ folder và tiền xử lý chúng trước. Request sẽ được gửi thông qua giao thức grpcclient
(hoặc có thể chỉnh sửa thành httpclient
nếu muốn test). Trong đó, input_image_unet34
là tên đầu vào đã được xác định trong file cấu hình config.pbtxt
ở phía trên và SEGMENTATION_OUTPUT
là tên đầu ra, model_name
là tên model được inference.
Sau khi nhận được kết quả trả về, mình sẽ visualize một chút cho dễ nhìn:
Ngoài ra, trong cấu trúc folder model_repository
ở trên mình có đề cập, bạn có thể thấy mình cũng có chuẩn bị file config cho ensemble_model
. Và đây là file ví dụ cho tác vụ phân loại xem ảnh có tàu không và phân đoạn vị trí của tàu:
name: "ensemble_model"
platform: "ensemble"
max_batch_size: 4
input [ { name: "IMAGE" data_type: TYPE_FP32 dims: [ 3, 768, 768 ] }
]
output [ { name: "CLASSIFICATION" data_type: TYPE_FP32 dims: [ -1 ] }, { name: "SEGMENTATION" data_type: TYPE_FP32 dims: [ 1, 768, 768 ] }
]
ensemble_scheduling { step [ { model_name: "resnet34" model_version: -1 input_map { key: "input_image_resnet34" value: "IMAGE" } output_map { key: "CLASSIFICATION_OUTPUT" value: "CLASSIFICATION" } }, { model_name: "unet34" model_version: -1 input_map { key: "input_image_unet34" value: "IMAGE" } output_map { key: "SEGMENTATION_OUTPUT" value: "SEGMENTATION" } } ]
}
Trong đó, ảnh đầu vào INPUT
sẽ được gửi tới cả 2 model resnet34
và unet
và giá trị đầu ra của chúng sẽ tương ứng với output
.
Và đó cũng là kết thúc cho Inference Request Tutorial
mà mình có chuẩn bị. Các bạn hoàn toàn có thể dựa trên các file mẫu để tùy chỉnh dựa theo model, tác vụ của mình hoặc nếu bài viết của mình chưa đủ, bạn có thể đọc sâu hơn trong Triton Documentation và Github của họ mà mình có để link ở cuối.
Triton Model Analyzer (Optional)
Trong bài viết lần trước, mình cũng có nói đến một Triton tool là Triton Model Analyzer nhỉ. Nhắc lại một chút, đây là tool sử dụng Performance Analyzer để gửi yêu cầu đến mô hình trong khi đo bộ nhớ GPU và mức sử dụng điện toán. Nó tự động xác định các cấu hình mô hình tùy chọn trong Triton để tối đa hóa hiệu suất. Nó thể giúp bạn tìm cấu hình tối ưu hơn, trên một phần cứng nhất định, cho các mô hình single, multiple, ensemble hoặc BLS chạy trên Triton Inference Server. Model Analyzer cũng sẽ tạo báo cáo để giúp bạn hiểu rõ hơn về sự cân bằng giữa các cấu hình khác nhau cùng với các yêu cầu về điện toán và bộ nhớ của chúng. Vậy thì ta cũng thử đi qua cách sử dụng Model Analyzer cơ bản. c
Do Docker không có sẵn triton_model_analyzer
image nên ta phải tự build thôi. Trước tiên mọi người cần clone repository của model analyzer về trước:
git clone https://github.com/triton-inference-server/model_analyzer.git
cd model_analyzer/
Tiếp theo ta sẽ build image trên local:
docker build --pull -t model-analyzer .
Sau một thời gian chờ đợi, ta cần run image đó lên:
docker run -it --rm --gpus all -v $(pwd):/workspace --net=host model-analyzer
cd workspace/
Ở trong container, trước tiên ta sẽ cần tạo 1 configuration file cho biết model analyzer sẽ cấu hình và gửi output vào đâu. Mình sẽ tạo configuration file cơ bản để cho model đọc model_repository của mình. Ta sẽ tạo 1 file perf.yaml
để cấu hình:
vi perf.yaml
model_repository: /workspace/model_repository
profile_models: - unet34
Tiếp theo ta sẽ sử dụng câu lệnh tiếp theo để bắt đầu quá trình tìm các config và đo lường hiệu năng của chúng. Lưu ý rằng, tùy thuộc vào khả năng của GPU mà config cũng sẽ khác nhau, thời gian chạy quá trình analyzer cũng rất tốn thời gian. Tuy nhiên, các bạn có thể tham khảo thêm các cách Search khác của Model analyzer được đề cập trong Documentation.
model-analyzer profile --output-model-repository-path /workspace/output/ -f perf.yaml --override-output-model-repository
Trong đó, tham số --output-model-repository
sẽ là nơi các config được lưu lại khi config đó được chạy xong trong quá trình analyzer. Dưới đây là đoạn logs minh họa cho mỗi config được chạy:
[Model Analyzer] Initiliazing GPUDevice handles
[Model Analyzer] Using GPU 0 NVIDIA GeForce RTX 3050 Ti Laptop GPU with UUID GPU-a4c58681-b808-f91d-0254-f4bc9d22e81a
[Model Analyzer] WARNING: Overriding the output model repo path "/workspace/output/"
[Model Analyzer] Starting a local Triton Server
[Model Analyzer] Loaded checkpoint from file /workspace/checkpoints/5.ckpt
[Model Analyzer] Profiling server only metrics...
[Model Analyzer] [Model Analyzer] Creating model config: unet34_config_0
[Model Analyzer] Enabling dynamic_batching
[Model Analyzer] Setting instance_group to [{'count': 1, 'kind': 'KIND_GPU'}]
[Model Analyzer] Setting max_batch_size to 1
[Model Analyzer]
Sau quá trình chạy, điểm khá hay là model analyzer có khả năng export cho mình các thông số liên quan đến mức độ điện toán, latency,GPU power, ... Các bạn có thể kiểm tra trong folder results
. Trong đó sẽ có 3 file csv được export như sau:
results
├── metrics-model-gpu.csv
├── metrics-model-inference.csv
└── metrics-server-only.csv
Hơn nữa, nếu các bạn muốn export thành các file PDF
theo format dễ nhìn dễ đọc lại còn có thêm cả biểu đồ visualize và bảng nữa thì có thể sử dụng câu lệnh được recommend bởi Model Analyzer sau khi quá trình Analyze hoàn tất. Ví dụ:
model-analyzer report --report-model-configs unet34_config_5,unet34_config_default,unet34_config_6 --export-path /workspace --config-file perf.yaml
Khi đó, một folder mới reports
xuất hiện tại Pwd
(do đã mount từ khi chạy model analyzer container), cấu trúc của folder như sau:
reports/
├── detailed
│ ├── unet34_config_5
│ │ └── detailed_report.pdf
│ ├── unet34_config_6
│ │ └── detailed_report.pdf
│ └── unet34_config_default
│ └── detailed_report.pdf
└── summaries └── unet34 └── result_summary.pdf
Trong đó, folder detailed
sẽ bao gồm các thông số cụ thể và chi tiết của từng config (top 3 config cho kết quả tốt nhất) và folder summaries
sẽ bao gồm so sánh và tổng hợp của các config đã được phân tích. Dưới đây là vài ví dụ minh họa của file PDF detailed:
Và dưới đây là PDF summaries cho các config:
Nếu các bạn cần các ảnh biểu đồ phục vụ cho mục đích nào đó, Model Analyzer cũng phân tích và trả về các ảnh cho bạn trong folder plots
.
plots/
├── detailed
│ ├── unet34_config_5
│ │ └── latency_breakdown.png
│ ├── unet34_config_6
│ │ └── latency_breakdown.png
│ └── unet34_config_default
│ └── latency_breakdown.png
└── simple ├── unet34 │ ├── gpu_mem_v_latency.png │ └── throughput_v_latency.png ├── unet34_config_5 │ ├── cpu_mem_v_latency.png │ ├── gpu_mem_v_latency.png │ ├── gpu_power_v_latency.png │ └── gpu_util_v_latency.png ├── unet34_config_6 │ ├── cpu_mem_v_latency.png │ ├── gpu_mem_v_latency.png │ ├── gpu_power_v_latency.png │ └── gpu_util_v_latency.png └── unet34_config_default ├── cpu_mem_v_latency.png ├── gpu_mem_v_latency.png ├── gpu_power_v_latency.png └── gpu_util_v_latency.png
Ở trên, ta mới chạy qua các config mặc định, ta cũng có thể xác định thêm các ràng buộc khi phân tích cấu hình. Nếu ta muốn limit latency là 50 ms cho response. Điều thú vị của Model Analyzer là mình ko cần chạy lại toàn bộ parameters. Ta chỉ cần chạy lại cái analysis cuối cùng mà được sinh bởi các reported trên mà ta thấy.
Ta sẽ tạo 1 configuration file mới, xác định model cần analyze và xác định constraints với max latency:
# Analysis
vi analyze.yaml analysis_models: unet34: constraints: perf_latency_p99: max: 50
model-analyzer analyze --config-file analyze.yaml
Lưu ý
Các version của Triton Inference Server image và Model Analyzer của mình có thể đã hơi cũ do mình cũng chưa update các version mới. Khi các bạn thử trải nghiệm các version mới có thể sẽ gặp một chút xung đột bởi các câu lệnh có thể chưa được update. Nhưng nhìn chung, bài viết này cũng có mô tả cách thức cách thức cơ bản để sử dụng và trải nghiệm Triton Inference Server và Triton Tool.
Tổng kết
Qua 2 bài viết tổng quan về Triton Inference Server và Triton Tutorial mà mình đã dành thời gian tổng hợp, hy vọng các bạn đã có cái nhìn khái quát về cách Triton Inference Server hoạt động và cách sử dụng cơ bản nhất của nó. Bài viết có thể chưa được đầy đủ toàn bộ thông tin, các bạn có thể tìm đọc thêm và trải nghiệm dựa trên Triton Documentation mà mình sẽ đính kèm ở phần Reference.