Advanced Model Serving sử dụng Seldon Core (P.1)
2022-01-16 17:21:20

Như đề cập ở bài blog trước, mặc dù là một framework đơn giản và mạnh mẽ, song BentoML chưa hỗ trợ một số features hay ho như horizontal scaling, hay blue-green deployment. Seldon Core có thể được sử dụng như một giải pháp thay thế, tuy nhiên yêu cầu nhiều kiến thức hơn về Devops cũng như Kubernetes.

Bentoml vs Seldon meme

1. Seldon Core

Seldon Core là một framework giúp đóng gói và deploy các model trên Kubernetes một cách dễ dàng. Công cụ này cũng hỗ trợ nhiều features nâng cao như: monitoring, logging, explainers, AB test, vân vân và mây mây.

2. Quick start

Seldon Core cung cấp sẵn prepackaged server, cho phép user chỉ cần định nghĩa đường dẫn tới model weight (Line 12) và deploy. Đường dẫn này cần chứa 2 files sau: model.joblib (model weight), và metadata.yaml (lưu metadata của model, optional)

1
2
3
4
5
6
7
8
9
10
11
12
# metadata.yaml
name: iris
versions: [iris/v1]
platform: sklearn
inputs:
- datatype: BYTES
name: input
shape: [ 4 ]
outputs:
- datatype: BYTES
name: output
shape: [ 3 ]

Hãy thử deploy service đầu tiên với Seldon Core bằng cách apply file dưới đây nào các bác

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# seldon.yaml
apiVersion: machinelearning.seldon.io/v1alpha2
kind: SeldonDeployment
metadata:
name: sklearn
namespace: models
spec:
name: iris
predictors:
- graph:
children: []
implementation: SKLEARN_SERVER
modelUri: gs://seldon-models/v1.13.0-dev/sklearn/iris
name: classifier
name: default
replicas: 1

Lưu ý một số thông tin quan trọng ở file trên:

  • Line 43: Chúng ta chọn SKLEARN_SERVER, do model.joblib là model sklearn
  • Line 47: Số lượng pod cho service trên là 1 (chúng ta sẽ học cách tự động thay đổi số lượng replica trong những phần sau)

Bây giờ hãy thử port-forward service và test API nào

1
kubectl port-forward svc/sklearn-default-classifier -n models 9000:9000
1
2
3
4
$ curl -d '{"data": {"ndarray":[[1.0, 2.0, 5.0, 6.0]]}}'\
-X POST http://localhost:9000/api/v1.0/predictions\
-H "Content-Type: application/json"
{"data":{"names":["t:0","t:1","t:2"],"ndarray":[[9.912315378486697e-07,0.0007015931307746079,0.9992974156376876]]},"meta":{"requestPath":{"classifier":"sklearnserver:1.9.1"}}}
1
2
$ curl http://localhost:9000/api/v1.0/metadata
{"custom":{},"inputs":[{"datatype":"BYTES","name":"input","shape":[4]}],"name":"iris","outputs":[{"datatype":"BYTES","name":"output","shape":[3]}],"platform":"sklearn","versions":["iris/v1"]}

3. Custom model server

Sử dụng prepackaged model server giúp chúng ta chỉ cần quan tâm tới training model để ra được weight, mà không cần quan tâm tới việc code phần API cho model inference. Tuy nhiên, trong thực tế, đôi khi model inference không chỉ đơn giản là clf.predict(), và khi đó chúng ta cần phải customize model server như ví dụ bên dưới.

Cấu trúc thư mục của ví dụ này như sau:

1
2
3
4
5
6
7
8
9
10
11
12
custom-server/
|____requirements.txt
|____Dockerfile
|____deploy
| |____seldon.yaml # file yaml để deploy
| |____deploy.sh # script để apply file yaml
|____constants.py # các constants dùng trong code inference
|____push.sh # script để build và push docker image
|____utils
| |______init__.py
| |____gcs_utils.py # chứa các utils script, ví dụ download file từ GCS
|____online_score_api.py # code inference

Trước hết chúng ta sẽ chuẩn bị phần inference code để xử lý các request tới API

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# online_score_api.py
class online_score_api:
def __init__(self):
self.root_path = "/mnt/model"
self.load()

def load(self):
local_model_path = os.path.join(self.root_path, constants.MODEL_FILE)
logging.info("downloading model file")
gcs_utils.download_file_from_gcs(gcs_bucket_name=constants.GCS_BUCKET_NAME,
gcs_blob_object=os.path.join(constants.GCS_BLOB_OBJECT, constants.MODEL_FILE),
local_model_path=local_model_path)
self._joblib = joblib.load(local_model_path)

def predict(self, X, names=[], meta=[]):
logging.info(f"model features: {X}")
logging.info(f"model names: {names}")
logging.info(f"model meta: {meta}")
try:
result = self._joblib.predict(X)
return result
except Exception as ex:
logging.exception("Exception during predict")

Seldon Core hỗ trợ build 2 loại model server: reusablenon-reusable. Ở ví dụ trên, Line 6 load từ GCS (remote storage) nên là reusable server, nếu load từ file trong image thì sẽ là non-reusable. Hàm load() được đặt trong hàm __init__() do chúng ta muốn load model một lần, trong khi khởi tạo pod.

Cuối cùng chúng ta sẽ viết hàm predict() như Line 16 để mỗi khi có request tới thì hàm này sẽ xử lý. Các bác có thể thấy hàm này nhận 3 params là:

  • X: (prediction data đã được Seldon Core tự động chuyển đổi qua dạng numpy array)
  • names: tên của các cột tương ứng trong X
  • meta: các thông tin khác

Một lưu ý cho các bác nếu không muốn bị Seldon tự động chuyển input sang dạng numpy array (hoặc đôi khi Seldon không thể chuyển qua dạng numpy array), thì các bác chỉ cần thay hàm predict() bằng predict_raw() là được nha.

Tiếp theo chúng ta sẽ chuẩn bị một file Dockerfile đơn giản, giúp package đoạn code inference trên thành Docker image để đem đi deploy.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# Dockerfile
FROM python:3.7

RUN mkdir -p /mnt/model/
RUN chmod -R a+rw /mnt/model/

COPY . /app

WORKDIR /app

RUN pip install -r requirements.txt

# Port for REST
EXPOSE 9000

# Define environment variable
ENV MODEL_NAME online_score_api
ENV SERVICE_TYPE MODEL

CMD exec seldon-core-microservice $MODEL_NAME --service-type $SERVICE_TYPE

Note: Các bác chú ý cấp quyền rw tới thư mục /mnt/model (Line 5) để có thể lưu model nha, nếu không thì sẽ bị lỗi permission. OK! Giờ thì sửa file YAML để đem đi apply, tương tự như prepackaged server nào.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# seldon.yaml
apiVersion: machinelearning.seldon.io/v1alpha2
kind: SeldonDeployment
metadata:
name: sklearn
namespace: models
spec:
name: iris
predictors:
- componentSpecs:
- spec:
containers:
- name: classifier
image: yourdockerusername/demo-seldon:0.0.1
serviceAccountName: yourserviceaccount
graph:
children: []
endpoint:
type: REST
name: classifier
type: MODEL
name: default
replicas: 1

Line 15 các bác điền service account phù hợp để service có quyền kéo model từ GCS về nha. Tận hưởng thành quả thôi nào!

1
2
3
4
5
6
$ curl -d '{"data": {"names": ["a", "b", "c", "d"],
"ndarray": [[1.0, 2.0, 5.0, 6.0]]},
"meta": {"anotherParam": "anotherParamValue"}}'\
-X POST http://localhost:9000/api/v1.0/predictions\
-H "Content-Type: application/json"
{"data":{"names":[],"ndarray":[2]},"meta":{"requestPath":{"classifier":"yourdockerusername/demo-seldon:0.0.1"}}}

Bây giờ chúng ta sẽ chuẩn bị phần reponse cho /metadata endpoint bằng cách thêm hàm init_metadata trong code inference

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class online_score_api:
...

def init_metadata(self):
logging.info("metadata method called")
meta = {
"name": "iris-sklearn",
"versions": ["v0.0.1"],
"platform": "seldon",
"inputs": [
{
"messagetype": "tensor",
"schema": {"names": ["a", "b", "c", "d"], "shape": [4]},
}
],
"outputs": [{"messagetype": "tensor", "schema": {"shape": [1]}}],
"custom": {"author": "quandv", "extra": "information"},
}
return meta

...

Khi đó response của route /metadata sẽ tương tự như sau

1
{"custom":{"author":"seldon-dev","extra":"information"},"inputs":[{"messagetype":"tensor","schema":{"names":["a","b","c","d"],"shape":[4]}}],"name":"iris-sklearn","outputs":[{"messagetype":"tensor","schema":{"shape":[1]}}],"platform":"seldon","versions":["v0.0.1"]}

Trên đây là các bước cơ bản để các bác có thể deploy một model lên cụm Kubernetes. Ở các phần sau chúng ta sẽ cùng nhau tìm hiểu thêm một số features thú vị khác mà Seldon cung cấp nha. Ví dụ cái monitoring dashboard như sau:

Seldon monitoring dashboard

Code về tutorial các bác có thể xem ở đây: https://github.com/quan-dang/seldon-tutorials

4. Tài liệu tham khảo

[1] https://github.com/SeldonIO/seldon-core

Prev
2022-01-16 17:21:20
Next