模型服務網格(Model Service Mesh)是一種架構模式,用於在分佈式環境中部署和管理機器學習模型服務。它提供了一個可擴展的、高性能的基礎架構,用於將多個模型服務進行管理、部署和調度,以此更好地處理模型的部署、版本管理、路由和推理請求的負載均衡。
模型服務網格的核心思想是將模型部署爲可伸縮的服務,並通過網格來管理和路由這些服務, 簡化模型服務的管理和運維。它通過將模型服務抽象爲可編排的、可伸縮的單元,使得模型的部署、擴展和版本控制變得更加容易。它還提供了一些核心功能,如負載均衡、自動伸縮、故障恢復等,以確保模型服務的高可用性和可靠性。
模型可以根據實際的推理請求負載進行自動縮放和負載均衡,從而實現高效的模型推理。模型服務網格還提供了一些高級功能,如流量分割、A/B 測試、灰度發佈等,以便更好地控制和管理模型服務的流量,可以輕鬆切換和回滾不同的模型版本。它還支持動態路由,可以根據請求的屬性,如模型類型、數據格式或其他元數據,將請求路由到適當的模型服務。
阿里雲服務網格 ASM 已經提供了一個可擴展的、高性能的模型服務網格基礎能力,用於將多個模型服務進行管理、部署和調度,以此更好地處理模型的部署、版本管理、路由和推理請求的負載均衡。通過使用模型服務網格,開發人員可以更輕鬆地部署、管理和擴展機器學習模型,同時提供高可用性、彈性和靈活性,以滿足不同的業務需求。
01 使用模型服務網格進行多模型推理服務
模型服務網格基於 KServe ModelMesh 實現,針對大容量、高密度和頻繁變化的模型用例進行了優化,可以智能地將模型加載到內存中或從內存中卸載,以在響應性和計算之間取得平衡。
模型服務網格提供了以下功能:
- 緩存管理
- Pod 作爲分佈式最近最少使用 (LRU) 緩存進行管理。
- 根據使用頻率和當前請求量,加載和卸載模型的副本。
- 智能放置和加載
- 模型放置通過 Pod 之間的緩存壽命和請求負載來平衡。
- 使用隊列來處理併發模型加載,並最大限度地減少對運行時流量的影響。
- 彈性
- 失敗的模型加載會在不同的 Pod 中自動重試。
- 操作簡便性
- 自動和無縫地處理滾動模型更新。
以下是部署模型示例,使用前提可以參考 [1]。
1.1 創建存儲聲明 PVC
在 ACK 集羣中,使用如下 YAML 創建存儲聲明 my-models-pvc:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: my-models-pvc
namespace: modelmesh-serving
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 1Gi
storageClassName: alibabacloud-cnfs-nas
volumeMode: Filesystem
然後運行如下命令:
kubectl get pvc -n modelmesh-serving
將會得到如下類似的預期結果:
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
my-models-pvc Bound nas-379c32e1-c0ef-43f3-8277-9eb4606b53f8 1Gi RWX alibabacloud-cnfs-nas 2h
1.2 創建 Pod 來訪問 PVC
爲了使用新的 PVC,我們需要將其作爲卷安裝到 Kubernetes Pod。然後我們可以使用這個 pod 將模型文件上傳到持久卷。
讓我們部署一個pvc-access Pod,並要求 Kubernetes 控制器通過指定“my-models-pvc”來聲明我們之前請求的 PVC:
kubectl apply -n modelmesh-serving -f - <<EOF
---
apiVersion: v1
kind: Pod
metadata:
name: "pvc-access"
spec:
containers:
- name: main
image: ubuntu
command: ["/bin/sh", "-ec", "sleep 10000"]
volumeMounts:
- name: "my-pvc"
mountPath: "/mnt/models"
volumes:
- name: "my-pvc"
persistentVolumeClaim:
claimName: "my-models-pvc"
EOF
確認 pvc-access Pod 應該正在運行:
kubectl get pods -n modelmesh-serving | grep pvc-access
將會得到如下類似的預期結果:
pvc-access 1/1 Running
1.3 將模型存儲在持久捲上
現在,我們需要將我們的 AI 模型添加到存儲卷中,我們將使用 scikit-learn 訓練的 MNIST 手寫數字字符識別模型。可以從 kserve/modelmesh-minio-examples 倉庫[2]下載 mnist-svm.joblib 模型文件的副本。
通過以下命令,將 mnist-svm.joblib 模型文件複製到 pvc-access pod 上的 /mnt/models 文件夾中:
kubectl -n modelmesh-serving cp mnist-svm.joblib pvc-access:/mnt/models/
執行如下命令,確認 model 已經加載成功:
kubectl -n modelmesh-serving exec -it pvc-access -- ls -alr /mnt/models/
應該得到如下內容:
-rw-r--r-- 1 501 staff 344817 Oct 30 11:23 mnist-svm.joblib
1.4 部署推理服務
接下來,我們需要部署一個 sklearn-mnist 推理服務:
apiVersion: serving.kserve.io/v1beta1
kind: InferenceService
metadata:
name: sklearn-mnist
namespace: modelmesh-serving
annotations:
serving.kserve.io/deploymentMode: ModelMesh
spec:
predictor:
model:
modelFormat:
name: sklearn
storage:
parameters:
type: pvc
name: my-models-pvc
path: mnist-svm.joblib
幾十秒鐘後(取決於鏡像拉取速度),新的推理服務 sklearn-mnist 應該準備就緒。
運行如下命令:
kubectl get isvc -n modelmesh-serving
將會得到如下類似的預期結果:
NAME URL READY
sklearn-mnist grpc://modelmesh-serving.modelmesh-serving:8033 True
1.5 運行推理服務
現在我們可以使用 curl 發送推理請求到我們的 sklearn-mnist 模型。數組形式的請求數據表示待分類的數字圖像掃描中 64 個像素的灰度值。
MODEL_NAME="sklearn-mnist"
ASM_GW_IP="ASM網關IP地址"
curl -X POST -k "http://${ASM_GW_IP}:8008/v2/models/${MODEL_NAME}/infer" -d '{"inputs": [{"name": "predict", "shape": [1, 64], "datatype": "FP32", "contents": {"fp32_contents": [0.0, 0.0, 1.0, 11.0, 14.0, 15.0, 3.0, 0.0, 0.0, 1.0, 13.0, 16.0, 12.0, 16.0, 8.0, 0.0, 0.0, 8.0, 16.0, 4.0, 6.0, 16.0, 5.0, 0.0, 0.0, 5.0, 15.0, 11.0, 13.0, 14.0, 0.0, 0.0, 0.0, 0.0, 2.0, 12.0, 16.0, 13.0, 0.0, 0.0, 0.0, 0.0, 0.0, 13.0, 16.0, 16.0, 6.0, 0.0, 0.0, 0.0, 0.0, 16.0, 16.0, 16.0, 7.0, 0.0, 0.0, 0.0, 0.0, 11.0, 13.0, 12.0, 1.0, 0.0]}}]}'
JSON 響應應如下所示,推斷掃描的數字是“8”:
{
"modelName": "sklearn-mnist__isvc-3c10c62d34",
"outputs": [
{
"name": "predict",
"datatype": "INT64",
"shape": [
"1",
"1"
],
"contents": {
"int64Contents": [
"8"
]
}
}
]
}
02 使用模型服務網格自定義模型運行時
模型服務網格(Model Service Mesh,簡稱爲 ModelMesh) 針對大容量、高密度和頻繁變化的模型推理服務的部署運行進行了優化,可以智能地將模型加載到內存中或從內存中卸載,以在響應性和計算之間取得最佳的平衡。
ModelMesh 默認集成了以下模型服務器運行環境,例如
- Triton Inference Server,NVIDIA 的服務器,適用於 TensorFlow、PyTorch、TensorRT 或 ONNX 等框架。
- MLServer,Seldon 的基於 Python 的服務器,適用於 SKLearn、XGBoost 或 LightGBM 等框架。
- OpenVINO Model Server,英特爾用於英特爾 OpenVINO 或 ONNX 等框架的服務器。
- TorchServe,支持包含 eager 模式的 PyTorch 模型。
如果這些模型服務器無法滿足您的特定要求時,譬如需要處理推理的自定義邏輯,或者您的模型所需的框架還不在上述支持列表中,您可以自定義服務運行時來進行擴展支撐。
具體可以參考 [3]。
03 爲大語言模型 LLM 提供服務
大語言模型 LLM(Large Language Model)指參數數量達到億級別的神經網絡語言模型,例如:GPT-3、GPT-4、PaLM、PaLM2 等。以下介紹如何爲大語言模型 LLM 提供服務。
使用前提可以具體參考 [4]。
3.1 構建自定義運行時
構建自定義運行時,提供帶有提示調整配置的 HuggingFace LLM。此示例中的默認值設置爲我們預先構建的自定義運行時鏡像和預先構建的提示調整配置。
3.1.1 實現一個繼承自 MLServer MLModel 的類
kfp-tekton/samples/peft-modelmesh-pipeline 目錄[5]中的 peft_model_server.py 文件包含了如何提供帶有提示調整配置的 HuggingFace LLM 的所有代碼。
下面的 _load_model 函數顯示我們將選擇已訓練的 PEFT 提示調整配置的預訓練 LLM 模型。分詞器也作爲模型的一部分進行定義,因此可以用於對推理請求中的原始字符串輸入進行編碼和解碼,而無需要求用戶預處理其輸入爲張量字節。
from typing import List
from mlserver import MLModel, types
from mlserver.codecs import decode_args
from peft import PeftModel, PeftConfig
from transformers import AutoModelForCausalLM, AutoTokenizer
import torch
import os
class PeftModelServer(MLModel):
async def load(self) -> bool:
self._load_model()
self.ready = True
return self.ready
@decode_args
async def predict(self, content: List[str]) -> List[str]:
return self._predict_outputs(content)
def _load_model(self):
model_name_or_path = os.environ.get("PRETRAINED_MODEL_PATH", "bigscience/bloomz-560m")
peft_model_id = os.environ.get("PEFT_MODEL_ID", "aipipeline/bloomz-560m_PROMPT_TUNING_CAUSAL_LM")
self.tokenizer = AutoTokenizer.from_pretrained(model_name_or_path, local_files_only=True)
config = PeftConfig.from_pretrained(peft_model_id)
self.model = AutoModelForCausalLM.from_pretrained(config.base_model_name_or_path)
self.model = PeftModel.from_pretrained(self.model, peft_model_id)
self.text_column = os.environ.get("DATASET_TEXT_COLUMN_NAME", "Tweet text")
return
def _predict_outputs(self, content: List[str]) -> List[str]:
output_list = []
for input in content:
inputs = self.tokenizer(
f'{self.text_column} : {input} Label : ',
return_tensors="pt",
)
with torch.no_grad():
inputs = {k: v for k, v in inputs.items()}
outputs = self.model.generate(
input_ids=inputs["input_ids"], attention_mask=inputs["attention_mask"], max_new_tokens=10, eos_token_id=3
)
outputs = self.tokenizer.batch_decode(outputs.detach().cpu().numpy(), skip_special_tokens=True)
output_list.append(outputs[0])
return output_list
3.1.2 構建 Docker 鏡像
實現了模型類之後,我們需要將其依賴項(包括 MLServer)打包到一個支持 ServingRuntime 資源的鏡像中。參考如下 Dockerfile 進行鏡像構建。
# TODO: choose appropriate base image, install Python, MLServer, and
# dependencies of your MLModel implementation
FROM python:3.8-slim-buster
RUN pip install mlserver peft transformers datasets
# ...
# The custom `MLModel` implementation should be on the Python search path
# instead of relying on the working directory of the image. If using a
# single-file module, this can be accomplished with:
COPY --chown=${USER} ./peft_model_server.py /opt/peft_model_server.py
ENV PYTHONPATH=/opt/
# environment variables to be compatible with ModelMesh Serving
# these can also be set in the ServingRuntime, but this is recommended for
# consistency when building and testing
ENV MLSERVER_MODELS_DIR=/models/_mlserver_models \
MLSERVER_GRPC_PORT=8001 \
MLSERVER_HTTP_PORT=8002 \
MLSERVER_LOAD_MODELS_AT_STARTUP=false \
MLSERVER_MODEL_NAME=peft-model
# With this setting, the implementation field is not required in the model
# settings which eases integration by allowing the built-in adapter to generate
# a basic model settings file
ENV MLSERVER_MODEL_IMPLEMENTATION=peft_model_server.PeftModelServer
CMD mlserver start ${MLSERVER_MODELS_DIR}
3.1.3 創建新的 ServingRuntime 資源
可以使用以下代碼塊中的 YAML 模板創建一個新的 ServingRuntime 資源,並將其指向您剛創建的鏡像。
apiVersion: serving.kserve.io/v1alpha1
kind: ServingRuntime
metadata:
name: peft-model-server
namespace: modelmesh-serving
spec:
supportedModelFormats:
- name: peft-model
version: "1"
autoSelect: true
multiModel: true
grpcDataEndpoint: port:8001
grpcEndpoint: port:8085
containers:
- name: mlserver
image: registry.cn-beijing.aliyuncs.com/test/peft-model-server:latest
env:
- name: MLSERVER_MODELS_DIR
value: "/models/_mlserver_models/"
- name: MLSERVER_GRPC_PORT
value: "8001"
- name: MLSERVER_HTTP_PORT
value: "8002"
- name: MLSERVER_LOAD_MODELS_AT_STARTUP
value: "true"
- name: MLSERVER_MODEL_NAME
value: peft-model
- name: MLSERVER_HOST
value: "127.0.0.1"
- name: MLSERVER_GRPC_MAX_MESSAGE_LENGTH
value: "-1"
- name: PRETRAINED_MODEL_PATH
value: "bigscience/bloomz-560m"
- name: PEFT_MODEL_ID
value: "aipipeline/bloomz-560m_PROMPT_TUNING_CAUSAL_LM"
# - name: "TRANSFORMERS_OFFLINE"
# value: "1"
# - name: "HF_DATASETS_OFFLINE"
# value: "1"
resources:
requests:
cpu: 500m
memory: 4Gi
limits:
cpu: "5"
memory: 5Gi
builtInAdapter:
serverType: mlserver
runtimeManagementPort: 8001
memBufferBytes: 134217728
modelLoadingTimeoutMillis: 90000
然後使用 kubectl apply 命令創建 ServingRuntime 資源,您將在 ModelMesh 部署中看到您的新自定義運行時。
3.2 部署 LLM 服務
爲了使用您新創建的運行時部署模型,您需要創建一個 InferenceService 資源來提供模型服務。該資源是 KServe 和 ModelMesh 用於管理模型的主要接口,代表了模型在推理中的邏輯端點。
apiVersion: serving.kserve.io/v1beta1
kind: InferenceService
metadata:
name: peft-demo
namespace: modelmesh-serving
annotations:
serving.kserve.io/deploymentMode: ModelMesh
spec:
predictor:
model:
modelFormat:
name: peft-model
runtime: peft-model-server
storage:
key: localMinIO
path: sklearn/mnist-svm.joblib
在前面的代碼塊中,InferenceService 命名爲 peft-demo,並聲明其模型格式爲 peft-model,與之前創建的示例自定義運行時使用相同的格式。還傳遞了一個可選字段 runtime,明確告訴 ModelMesh 使用 peft-model-server運行時來部署此模型。
3.3 運行推理服務
現在我們可以使用 curl 發送推理請求到我們上面部署的 LLM 模型服務。
MODEL_NAME="peft-demo"
ASM_GW_IP="ASM網關IP地址"
curl -X POST -k http://${ASM_GW_IP}:8008/v2/models/${MODEL_NAME}/infer -d @./input.json
其中 input.json 表示請求數據:
{
"inputs": [
{
"name": "content",
"shape": [1],
"datatype": "BYTES",
"contents": {"bytes_contents": ["RXZlcnkgZGF5IGlzIGEgbmV3IGJpbm5pbmcsIGZpbGxlZCB3aXRoIG9wdGlvbnBpZW5pbmcgYW5kIGhvcGU="]}
}
]
}
bytes_contents 對應的是字符串“Every day is a new beginning, filled with opportunities and hope”的 base64 編碼。
JSON 響應應如下所示,推斷掃描的數字是“8”:
{
"modelName": "peft-demo__isvc-5c5315c302",
"outputs": [
{
"name": "output-0",
"datatype": "BYTES",
"shape": [
"1",
"1"
],
"parameters": {
"content_type": {
"stringParam": "str"
}
},
"contents": {
"bytesContents": [
"VHdlZXQgdGV4dCA6IEV2ZXJ5IGRheSBpcyBhIG5ldyBiaW5uaW5nLCBmaWxsZWQgd2l0aCBvcHRpb25waWVuaW5nIGFuZCBob3BlIExhYmVsIDogbm8gY29tcGxhaW50"
]
}
}
]
}
其中 bytesContents 進行 base64 解碼後的內容爲:
Tweet text : Every day is a new binning, filled with optionpiening and hope Label : no complaint
至此,說明上述大語言模型 LLM 的模型服務請求得到了預期的結果。
04 總結
阿里雲服務網格 ASM 已經提供了一個可擴展的、高性能的模型服務網格基礎能力,用於將多個模型服務進行管理、部署和調度,以此更好地處理模型的部署、版本管理、路由和推理請求的負載均衡。
歡迎試用: https://www.aliyun.com/product/servicemesh
相關鏈接:
[1] 以下是部署模型示例,使用前提可以參考
[2] kserve/modelmesh-minio-examples 倉庫
https://github.com/kserve/modelmesh-minio-examples/blob/main/sklearn/mnist-svm.joblib
[3] 具體可以參考
[4] 使用前提可以具體參考
[5] kfp-tekton/samples/peft-modelmesh-pipeline 目錄
https://github.com/kubeflow/kfp-tekton
作者:王夕寧
本文爲阿里雲原創內容,未經允許不得轉載。