OpenAI API 経由で連続バッチ処理を使用して LLM モデルを提供する方法#
このデモでは、連続バッチ処理とページド・アテンション・アルゴリズムを使用して、OpenVINO モデルサーバーに LLM モデルを展開する方法を示します。テキスト生成のユースケースは、OpenAI API chat/completions
および補完エンドポイントを介して公開されます。これにより、特にインテル® Xeon® プロセッサー上で使いやすく、効率的になります。
注: このデモは、インテル® Xeon® プロセッサー Gen4 および Gen5 でテストされています。
Docker イメージを取得#
このデモはバージョン 2024.2 以降でサポートされます。
docker pull openvino/model_server:latest
オプションですが、メインブランチから最新のコードバージョンをビルドすることを推奨します。それにより、この機能の最新の拡張機能を活用できます。
git clone https://github.com/openvinotoolkit/model_server.git
cd model_server
make release_image RUN_TESTS=0
openvino/model_server:latest
というイメージが作成されます。
注意: ビルドするホストによっては、この操作に 40 分以上かかる場合があります。
モデルの準備#
このステップでは、元の Pytorch LLM モデルとトークナイザーが IR 形式に変換され、オプションで量子化されます。これにより、初期化時間が短縮され、パフォーマンスが向上し、メモリー消費量が軽減されます。ここでは、graph.pbtxt
内の LLM エンジン・パラメーターも定義します。
変換スクリプトの Python 依存関係をインストールします:
pip3 install -r https://raw.githubusercontent.com/openvinotoolkit/model_server/releases/2024/3/demos/continuous_batching/requirements.txt
モデルをダウンロードして量子化するには、optimal-cli を実行します:
cd demos/continuous_batching
optimum-cli export openvino --disable-convert-tokenizer --model meta-llama/Meta-Llama-3-8B-Instruct --weight-format fp16 Meta-Llama-3-8B-Instruct
convert_tokenizer -o Meta-Llama-3-8B-Instruct --with-detokenizer --skip-special-tokens --streaming-detokenizer --not-add-special-tokens meta-llama/Meta-Llama-3-8B-Instruct
注意: モデルをダウンロードする前に、アクセスをリクエストする必要があります。アクセスをリクエストするには、HuggingFace モデルページの指示に従ってください。アクセスが許可されたら、HuggingFace アカウント -> 設定 -> アクセス・トークン・ページで認証トークンを作成します。次のコマンドを発行し、認証トークンを入力します。
huggingface-cli login
を介して認証します。
グラフをモデルフォルダーにコピーします。
cat graph.pbtxt
input_stream: "HTTP_REQUEST_PAYLOAD:input"
output_stream: "HTTP_RESPONSE_PAYLOAD:output"
node: {
name: "LLMExecutor"
calculator: "HttpLLMCalculator"
input_stream: "LOOPBACK:loopback"
input_stream: "HTTP_REQUEST_PAYLOAD:input"
input_side_packet: "LLM_NODE_RESOURCES:llm"
output_stream: "LOOPBACK:loopback"
output_stream: "HTTP_RESPONSE_PAYLOAD:output"
input_stream_info: {
tag_index: 'LOOPBACK:0',
back_edge: true
}
node_options: {
[type.googleapis.com / mediapipe.LLMCalculatorOptions]: {
models_path: "./",
cache_size: 50
}
}
input_stream_handler {
input_stream_handler: "SyncSetInputStreamHandler",
options {
[mediapipe.SyncSetInputStreamHandlerOptions.ext] {
sync_set {
tag_index: "LOOPBACK:0"
}
}
}
}
}
cp graph.pbtxt Meta-Llama-3-8B-Instruct/
tree Meta-Llama-3-8B-Instruct
Meta-Llama-3-8B-Instruct
├── config.json
├── generation_config.json
├── graph.pbtxt
├── openvino_detokenizer.bin
├── openvino_detokenizer.xml
├── openvino_model.bin
├── openvino_model.xml
├── openvino_tokenizer.bin
├── openvino_tokenizer.xml
├── special_tokens_map.json
├── tokenizer_config.json
├── tokenizer.json
└── tokenizer.model
LLMExecutor
のデフォルト構成はほとんどの場合機能しますが、graph.pbtxt
ファイルの node_options
セクションでパラメーターを調整できます。グラフファイル内の models_path
パラメーターは、絶対パスまたは config.json
の base_path
に対する相対パスであることに注意してください。構成オプションの詳細については、LLM 計算機のドキュメントを確認してください。
注: グラフ内の
cache_size
パラメーターは、KV キャッシュサイズ (GB 単位) を表します。ホストに十分な RAM 容量がない場合は値を減らしてください。
サーバー構成#
config.json を準備:
cat config.json
{
"model_config_list": [],
"mediapipe_config_list": [
{
"name": "meta-llama/Meta-Llama-3-8B-Instruct",
"base_path": "Meta-Llama-3-8B-Instruct"
}
]
}
開始#
docker run -d --rm -p 8000:8000 -v $(pwd)/:/workspace:ro openvino/model_server --port 9000 --rest_port 8000 --config_path /workspace/config.json
モデルのロードを待機します。次のコマンドを使用して状態をチェックします:
curl http://localhost:8000/v1/config
{
"meta-llama/Meta-Llama-3-8B-Instruct" :
{
"model_version_status": [
{
"version": "1",
"state": "AVAILABLE",
"status": {
"error_code": "OK",
"error_message": "OK"
}
}
]
}
クライアント・コード#
単一のサーバブルで、ストリーム機能の有無にかかわらず、チャット/補完と補完エンドポイントの両方を公開します。チャット・エンドポイントは、会話コンテキストをクライアントが貼り付け、モデルプロンプトが Jinja モデル・テンプレートに基づいてサーバーによって作成されるシナリオで使用されることが想定されています。補完エンドポイントは、クライアントが直接プロンプトを渡す場合や、jinja テンプレートのないモデルに使用する必要があります。
単項:#
curl http://localhost:8000/v3/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "meta-llama/Meta-Llama-3-8B-Instruct",
"max_tokens":30,
"stream":false,
"messages": [
{
"role": "system",
"content": "You are a helpful assistant."},
{
"role": "user",
"content": "What is OpenVINO?" }
]
}'| jq .
{
"choices": [
{
"finish_reason": "stop",
"index": 0,
"logprobs": null,
"message": {
"content": "\n\nOpenVINO is an open-source software library for deep learning inference that is designed to optimize and run deep learning models on a variety",
"role": "assistant"
}
}
],
"created": 1716825108,
"model": "meta-llama/Meta-Llama-3-8B-Instruct",
"object": "chat.completion"
}
補完エンドポイントでも同様の呼び出しを行うことができます:
curl http://localhost:8000/v3/completions \
-H "Content-Type: application/json" \
-d '{
"model": "meta-llama/Meta-Llama-3-8B-Instruct",
"max_tokens":30,
"stream":false,
"prompt": "<|begin_of_text|><|start_header_id|>system<|end_header_id|>\n\nYou are assistant<|eot_id|><|start_header_id|>user<|end_header_id|>\n\nWhat is OpenVINO?<|eot_id|><|start_header_id|>assistant<|end_header_id|>"
}'| jq .
{
"choices": [
{
"finish_reason": "stop",
"index": 0,
"logprobs": null,
"text": "\n\nOpenVINO is an open-source software library for deep learning inference that is designed to optimize and run deep learning models on a variety"
}
],
"created": 1716825108,
"model": "meta-llama/Meta-Llama-3-8B-Instruct",
"object": "text_completions"
}
ストリーミング:#
エンドポイントのチャット/補完は OpenAI クライアントと互換性があるため、ストリーミング・モードでも簡単にコードを生成することができます:
クライアント・ライブラリーのインストール:
pip3 install openai
from openai import OpenAI
client = OpenAI(
base_url="http://localhost:8000/v3",
api_key="unused"
)
stream = client.chat.completions.create(
model="meta-llama/Meta-Llama-3-8B-Instruct",
messages=[{"role": "user", "content": "Say this is a test"}],
stream=True,
)
for chunk in stream:
if chunk.choices[0].delta.content is not None:
print(chunk.choices[0].delta.content, end="", flush=True)
出力:
まるで私を試しているようです!
補完エンドポイントにも同様のコードを適用できます:
pip3 install openai
from openai import OpenAI
client = OpenAI(
base_url="http://localhost:8000/v3",
api_key="unused"
)
stream = client.completions.create(
model="meta-llama/Meta-Llama-3-8B-Instruct",
prompt="<|begin_of_text|><|start_header_id|>user<|end_header_id|>\n\nSay this is a test.<|eot_id|><|start_header_id|>assistant<|end_header_id|>",
stream=True,
)
for chunk in stream:
if chunk.choices[0].text is not None:
print(chunk.choices[0].text, end="", flush=True)
出力:
まるで私を試しているようです!
高い並行性を持つテキスト生成のベンチマーク#
OpenVINO モデルサーバーは、テキスト生成に効率的な並列化を採用しています。複数のクライアントで共有される環境でも、高い並行性でテキストを生成するのに使用できます。これは、vLLM リポジトリーのベンチマーク・アプリを使用して実証できます:
git clone https://github.com/vllm-project/vllm
cd vllm
pip3 install -r requirements-cpu.txt
cd benchmarks
wget https://huggingface.co/datasets/anon8231489123/ShareGPT_Vicuna_unfiltered/resolve/main/ShareGPT_V3_unfiltered_cleaned_split.json # sample dataset
python benchmark_serving.py --host localhost --port 8000 --endpoint /v3/chat/completions --backend openai-chat --model meta-llama/Meta-Llama-3-8B-Instruct --dataset ShareGPT_V3_unfiltered_cleaned_split.json --num-prompts 1000 --request-rate inf
Namespace(backend='openai-chat', version='N/A', base_url=None, host='localhost', port=8000, endpoint='/v3/chat/completions', dataset='ShareGPT_V3_unfiltered_cleaned_split.json', model='meta-llama/Meta-Llama-3-8B-Instruct', tokenizer=None, best_of=1, use_beam_search=False, num_prompts=1000, request_rate=inf.0, seed=0, trust_remote_code=False, disable_tqdm=False, save_result=False)
Traffic request rate: inf
100%|██████████████████████████████████████████████████| 1000/1000 [17:17<00:00, 1.04s/it]
============ Serving Benchmark Result ============
Successful requests: 1000
Benchmark duration (s): 447.62 Total input tokens: 215201
Total generated tokens: 198588
Request throughput (req/s): 2.23
Input token throughput (tok/s): 480.76
Output token throughput (tok/s): 443.65
---------------Time to First Token----------------
Mean TTFT (ms): 171999.94
Median TTFT (ms): 170699.21
P99 TTFT (ms): 360941.40
-----Time per Output Token (excl. 1st token)------
Mean TPOT (ms): 211.31
Median TPOT (ms): 223.79
P99 TPOT (ms): 246.48
==================================================
モデルサーバーを使用した RAG#
上記でデプロイされたサービスは、OpenAI エンドポイントを LLM エンジンとして langchain
ライブラリーを介して RAG チェーンで使用できます。
RAG ノートブックの例を確認してください