大規模言語モデル(LLM)とは、Generative Pre-Trained Transformer 3(GPT-3)、GPT-4、Pathways Language Model(PaLM)、PaLM 2 など、数億のパラメーターを持つニューラルネットワーク言語モデルを指します。大量の自然言語データを処理する必要がある場合、または複雑な言語理解システムを構築する場合、LLM を推論サービスとして使用し、API を呼び出すことで、テキスト分類、感情分析、機械翻訳などの高度な自然言語処理(NLP)機能をアプリケーションに簡単に統合できます。 LLM-as-a-service モードでは、高額なインフラストラクチャコストを支払う必要がなく、市場の変化に迅速に対応できます。さらに、LLM はクラウド上で実行されるため、ユーザーリクエストの急増に対応するためにいつでもサービスを拡張し、運用効率を向上させることができます。
ステップ 1:カスタムランタイムを構築する
プロンプトチューニング構成で Hugging Face LLM を提供するカスタムランタイムを構築します。この例では、デフォルト値は、事前に構築されたカスタムランタイムイメージと事前に構築されたプロンプトチューニング構成に設定されています。
MLServer の MLModel クラスを継承するクラスを実装します。
peft_model_server.py ファイルには、プロンプトチューニング構成の Hugging Face LLM がどのように提供されるかについてのすべてのコードが含まれています。ファイル内の _load_model
関数は、トレーニングされた PEFT プロンプトチューニング構成で事前トレーニング済み LLM モデルを選択するために使用されます。 _load_model
関数は、ユーザーに推論リクエストからの生の文字列入力をテンソルバイトに前処理するように要求することなく、エンコードおよびデコードできるトークナイザーも定義します。
peft_model_server.py ファイルを表示する
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):
# 事前トレーニング済みモデルのパスを取得します。デフォルトは "bigscience/bloomz-560m" です。
model_name_or_path = os.environ.get("PRETRAINED_MODEL_PATH", "bigscience/bloomz-560m")
# PEFT モデル ID を取得します。デフォルトは "aipipeline/bloomz-560m_PROMPT_TUNING_CAUSAL_LM" です。
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)
# PEFT 構成をロードします
config = PeftConfig.from_pretrained(peft_model_id)
# 事前トレーニング済みモデルをロードします
self.model = AutoModelForCausalLM.from_pretrained(config.base_model_name_or_path)
# PEFT モデルをロードします
self.model = PeftModel.from_pretrained(self.model, peft_model_id)
# データセットのテキスト列名を取得します。デフォルトは "Tweet text" です。
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
Docker イメージをビルドします。
モデルクラスを実装した後、MLServer を含むその依存関係を、ServingRuntime リソースとしてサポートされているイメージにパッケージ化する必要があります。次の Dockerfile を参照してイメージをビルドできます。
Dockerfile を表示する
# TODO: 適切なベースイメージを選択し、Python、MLServer、および
# MLModel 実装の依存関係をインストールします
FROM python:3.8-slim-buster
RUN pip install mlserver peft transformers datasets
# ...
# カスタム `MLModel` 実装は、イメージの作業ディレクトリに依存するのではなく、Python 検索パス上にある必要があります。
# 単一ファイルモジュールを使用する場合、これは次のように実現できます。
COPY --chown=${USER} ./peft_model_server.py /opt/peft_model_server.py
ENV PYTHONPATH=/opt/
# ModelMesh Serving と互換性のある環境変数
# これらは ServingRuntime でも設定できますが、ビルド時とテスト時の一貫性のためにこれが推奨されます
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
# この設定では、実装フィールドはモデル設定で必須ではなくなり、組み込みアダプターが基本的なモデル設定ファイルを生成できるようにすることで統合が容易になります
ENV MLSERVER_MODEL_IMPLEMENTATION=peft_model_server.PeftModelServer
CMD mlserver start ${MLSERVER_MODELS_DIR}
新しい ServingRuntime リソースを作成します。
次のコンテンツを使用して新しい ServingRuntime リソースを作成し、作成したイメージを指します。
YAML ファイルを表示する
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
次のコマンドを実行して、ServingRuntime リソースをデプロイします。
kubectl apply -f sample-runtime.yaml
ServingRuntime リソースを作成した後、ModelMesh デプロイメントで新しいカスタムランタイムを確認できます。
ステップ 2:LLM サービスをデプロイする
新しく作成したランタイムを使用してモデルをデプロイするには、モデルを提供する InferenceService リソースを作成する必要があります。このリソースは、KServe および ModelMesh がモデルを管理するために使用するメインインターフェースです。これは、推論を提供するためのモデルの論理エンドポイントを表します。
次のコンテンツを使用して、モデルを提供する InferenceService リソースを作成します。
YAML ファイルを表示する
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
YAML ファイルでは、InferenceService リソースの名前は peft-demo
で、そのモデル形式は peft-model
として宣言されています。これは、前のステップで作成したカスタムランタイムの例と同じ形式です。オプションのフィールド runtime
も渡され、ModelMesh に peft-model-server
ランタイムを使用してこのモデルをデプロイするように明示的に指示します。
次のコマンドを実行して、InferenceService リソースをデプロイします。
kubectl apply -f ${YAML ファイルの名前}.yaml
ステップ 3:推論を実行する
curl
コマンドを実行して、前のステップでデプロイされた LLM サービスに推論リクエストを送信します。
MODEL_NAME="peft-demo"
ASM_GW_IP="イングレスゲートウェイの IP アドレス"
curl -X POST -k http://${ASM_GW_IP}:8008/v2/models/${MODEL_NAME}/infer -d @./input.json
curl
コマンドの 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 レスポンスを示しています。
{
"modelName": "peft-demo__isvc-5c5315c302",
"outputs": [
{
"name": "output-0",
"datatype": "BYTES",
"shape": [
"1",
"1"
],
"parameters": {
"content_type": {
"stringParam": "str"
}
},
"contents": {
"bytesContents": [
"VHdlZXQgdGV4dCA6IEV2ZXJ5IGRheSBpcyBhIG5ldyBiaW5uaW5nLCBmaWxsZWQgd2l0aCBvcHRpb25waWVuaW5nIGFuZCBob3BlIExhYmVsIDogbm8gY29tcGxhaW50"
]
}
}
]
}
次のコードブロックは、bytesContents
の Base64 デコードされたコンテンツを示しています。これは、推論リクエストが期待どおりに LLM サービスで実行されたことを示しています。
Tweet text : Every day is a new binning, filled with optionpiening and hope Label : no complaint