模型服務網格:雲原生下的模型服務管理

模型服務網格(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] 以下是部署模型示例,使用前提可以參考

https://help.aliyun.com/zh/asm/user-guide/multi-model-inference-service-using-model-service-mesh?spm=a2c4g.11186623.0.0.7c4e6561k1qyJV#213af6d078xu7

[2] kserve/modelmesh-minio-examples 倉庫

https://github.com/kserve/modelmesh-minio-examples/blob/main/sklearn/mnist-svm.joblib

[3] 具體可以參考

https://help.aliyun.com/zh/asm/user-guide/customizing-the-model-runtime-using-the-model-service-mesh?spm=a2c4g.11186623.0.0.1db77614Vw96Eu

[4] 使用前提可以具體參考

https://help.aliyun.com/zh/asm/user-guide/services-for-the-large-language-model-llm?spm=a2c4g.11186623.0.0.29777614EEBYWt#436fc73079euz

[5] kfp-tekton/samples/peft-modelmesh-pipeline 目錄

https://github.com/kubeflow/kfp-tekton

作者:王夕寧

原文鏈接

本文爲阿里雲原創內容,未經允許不得轉載。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章