KServe를 사용해 MLflow를 사용해 빌드한 모델을 배포하는 방법에 대해 소개한다.
1. 버전 호환성 및 시그니처 제한사항
MLflow 버전에 따라 signature에 제한사항이 있다. KServe v0.13 에서 사용하는 MLServer 이미지(index.docker.io/seldonio/mlserver)에 포함된 MLflow 버전은 2.3.1이다.
지금 설치해서 사용하고 있는 MLflow의 버전이 2.15.1라, 이 두 버전 사이의 차이를 설명한다.
MLflow 2.3.1
- feature 이름 정보를 유지하면서 model signature를 정의할 수 있다.
- ColSpec을 사용하여 각 특성의 이름과 데이터 타입을 명시적으로 지정할 수 있다.
MLflow 2.15.1
- 전체 입력을 하나의 텐서로 취급하는 TensorSpec만 사용 가능하다.
- feature 이름 정보가 signature에 포함되지 않는다.
- input schema에 tensor가 아닌 값을 지정하면 pod에서 `TypeError: __init__() got an unexpected keyword argument 'required'` 오류가 발생한다.
2. 모델 생성 및 MLflow로 저장
MLflow Tracking Server 사용이 가능한 경우는 `mlflow.sklearn.save_model()`을 사용하고, 사용이 불가능한 경우에는
`mlflow.sklearn.log_model()`를 사용한다.
MLflow 2.3.1 사용 시:
pip install mlflow[extra]==2.3.1
save_model 메서드를 사용해 모델 파일을 로컬 `./v231` 경로에 저장한다.
import mlflow
from sklearn import svm
from sklearn import datasets
from mlflow.models.signature import infer_signature
import pandas as pd
# load data
iris = datasets.load_iris()
X, y = iris.data, iris.target
# 모델 생성 및 학습
clf = svm.SVC(gamma='scale')
clf.fit(X, y)
# 예측 수행
pred = clf.predict(X)
# signature 생성
iris_df = pd.DataFrame(data=iris.data, columns=iris.feature_names)
signature = infer_signature(iris_df, y)
# 모델 저장
mlflow.sklearn.save_model(
clf,
path="./v231",
signature=signature,
)
MLflow 2.15.1 사용 시:
pip install mlflow[extra]==2.15.1
autolog 메서드를 사용해 MLflow tracking server에 모델을 저장한다.
import mlflow
from sklearn import svm, datasets
mlflow.set_tracking_uri("http://mlflow.dd.io")
mlflow.set_experiment("Default")
mlflow.sklearn.autolog()
# Load wine quality dataset
iris = datasets.load_iris()
X, y = iris.data, iris.target
# Start a run and train a model
with mlflow.start_run(run_name="mlflow-kserve"):
clf = svm.SVC(gamma='scale')
clf.fit(X, y)
3. KServe를 사용해 모델을 배포하기 위해 모델을 스토리지에 업로드한다.
MLflow 2.3.1 사용 시:
`./v231` 경로에 생성된 파일을 모두 스토리지에 업로드한다. 이 예제에서는 `s3://sandbox/iris_svm/v231`에 업로드했다.
MLflow 2.15.1 사용 시:
tracking server에 의해 `s3://mlflow/0/ab2a8bf253ac46789339e4cba0df8804/artifacts/model` 모델이 자동으로 업로드된다.
4. S3 접근을 위한 서비스 계정을 생성
저장소에 저장되어 있는 모델 파일에 접근하기 위한 Secret과 ServiceAccount을 생성한다.
AWS_ACCESS_KEY_ID와 AWS_SECRET_ACCESS_KEY는 base64로 인코딩된 값을 입력한다.
apiVersion: v1
kind: Secret
metadata:
name: s3-secret
namespace: kubeflow-user-example-com
annotations:
serving.kserve.io/s3-endpoint: http://10.0.2.3 # 실제 MinIO 엔드포인트로 변경
serving.kserve.io/s3-usehttps: "0" # MinIO가 HTTP를 사용하는 경우
type: Opaque
data:
AWS_ACCESS_KEY_ID: dkwzQW92VXBsakVqRnVmd3UzSUE=
AWS_SECRET_ACCESS_KEY: WVdMNWhzMkI4Q0xxYVhzN0tHcDBxemY2SnRrMmxmQ0NnT0pMUFQ5eA==
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: s3-sa
namespace: kubeflow-user-example-com
secrets:
- name: s3-secret
5. KServe InferenceService 정의 및 적용
MLflow 2.3.1 사용 시:
apiVersion: "serving.kserve.io/v1beta1"
kind: "InferenceService"
metadata:
name: "iris-svm-v231"
spec:
predictor:
serviceAccountName: s3-sa
model:
modelFormat:
name: mlflow
protocolVersion: v2
storageUri: "s3://sandbox/iris_svm/v231"
MLflow 2.15.1 사용 시:
apiVersion: "serving.kserve.io/v1beta1"
kind: "InferenceService"
metadata:
name: "iris-svm-v2151"
spec:
predictor:
serviceAccountName: s3-sa
model:
modelFormat:
name: mlflow
protocolVersion: v2
storageUri: "s3://mlflow/0/ab2a8bf253ac46789339e4cba0df8804/artifacts/model"
6. 모델 메타데이터 확인
signature에 따라 모델 메타데이터 및 입력 데이터 구조가 변경된다. 추론 요청을 보내기 위해 모델의 메타 데이터를 확인한다.
- MLflow 2.3.1 및 ColSpec 사용한 모델의 메타데이터 확인:
curl http://iris-svm-v231.kubeflow-user-example-com.10.0.2.6.sslip.io/v2/models/iris-svm-v231
- return 값
{
"name": "iris-svm-v231",
"versions": [],
"platform": "",
"inputs": [
{
"name": "sepal length (cm)",
"datatype": "FP64",
"shape": [
-1
],
"parameters": {
"content_type": "np"
}
},
{
"name": "sepal width (cm)",
"datatype": "FP64",
"shape": [
-1
],
"parameters": {
"content_type": "np"
}
},
{
"name": "petal length (cm)",
"datatype": "FP64",
"shape": [
-1
],
"parameters": {
"content_type": "np"
}
},
{
"name": "petal width (cm)",
"datatype": "FP64",
"shape": [
-1
],
"parameters": {
"content_type": "np"
}
}
],
"outputs": [
{
"name": "output-0",
"datatype": "INT32",
"shape": [
-1
],
"parameters": {
"content_type": "np"
}
}
],
"parameters": {
"content_type": "pd"
}
}
- MLflow 2.15.1 및 TensorSpec 사용한 모델의 메타데이터 확인:
curl http://iris-svm-v2151.kubeflow-user-example-com.10.0.2.6.sslip.io/v2/models/iris-svm-v2151
- return 값
{
"name": "iris-svm-v2151",
"versions": [],
"platform": "",
"inputs": [
{
"name": "input-0",
"datatype": "FP64",
"shape": [
-1,
4
],
"parameters": {
"content_type": "np"
}
}
],
"outputs": [
{
"name": "output-0",
"datatype": "INT32",
"shape": [
-1
],
"parameters": {
"content_type": "np"
}
}
],
"parameters": {
"content_type": "np"
}
}
7. 예측 요청 보내기
InferenceService endpoint로 추론 요청을 보낸다. signature에 따라 input data의 포맷이 달라지므로, 모델 메타데이터를 통해 관련 내용을 확인한 후 예측 요청을 보내야한다.
- MLflow 2.3.1 및 ColSpec 사용한 모델에 추론 요청:
import requests
import json
from sklearn import datasets
import pandas as pd
from pprint import pprint
# Iris 데이터셋 로드
iris = datasets.load_iris()
# 3건만 예측에 사용
iris_df = pd.DataFrame(data=iris.data, columns=iris.feature_names).head(3)
sklear_iris_input = {"inputs": []}
for column in iris_df.columns:
sklear_iris_input["inputs"].append({
"name": column,
"shape": [len(iris_df)],
"datatype": "FP64",
"data": iris_df[column].tolist()
})
resp = requests.post(
"http://iris-svm-v231.kubeflow-user-example-com.10.0.2.6.sslip.io/v2/models/iris-svm-v231/infer",
data=json.dumps(sklear_iris_input)
)
kserve_result = resp.json()
pprint(kserve_result)
- return 값
{'id': 'e137e52a-dbec-4c79-9110-2c4262611e19',
'model_name': 'iris-svm-v231',
'outputs': [{'data': [0, 0, 0],
'datatype': 'INT32',
'name': 'output-1',
'shape': [3, 1]}],
'parameters': {}}
- MLflow 2.15.1 및 TensorSpec 사용한 모델의 메타데이터 확인:
import requests
import json
import numpy as np
from sklearn import datasets
from pprint import pprint
# Iris 데이터셋 로드
iris = datasets.load_iris()
input_data = iris.data[:3]
# KServe 요청 형식에 맞게 데이터 구성 (단일 텐서로)
request_data = {
"inputs": [
{
"name": "input-0",
"shape": list(input_data.shape),
"datatype": "FP64",
"data": input_data.tolist()
}
]
}
# KServe 엔드포인트 URL
url = "http://iris-svm-v2151.kubeflow-user-example-com.10.0.2.6.sslip.io/v2/models/iris-svm-v2151/infer"
# 요청 보내기
resp = requests.post(url, json=request_data)
pprint(resp.json())
- return 값
{'id': 'e8488d10-53c6-466f-a25b-f1f601bc225c',
'model_name': 'iris-svm-v2151',
'outputs': [{'data': [0, 0, 0],
'datatype': 'INT32',
'name': 'output-1',
'shape': [3, 1]}],
'parameters': {}}
'kubenetes' 카테고리의 다른 글
KServe Transformer 개발 가이드 (1) | 2024.09.29 |
---|---|
KServe Private container registry certificates 설정 (0) | 2024.09.29 |
KServe를 이용한 scikit-learn 모델 배포 및 사용 가이드 (1) | 2024.09.26 |
kserve 사용 및 설정 가이드 (0) | 2024.09.24 |
CRI-O 환경에서 Podman, Ping, Audit 관련 이슈 해결 가이드 (0) | 2024.09.19 |
댓글