効率的な LLM サービス#

これは、プレビュー機能です。

概要#

生成 AI の急速な発展に伴い、ハードウェアを最大限に活用し、最高の生成パフォーマンスを提供するため、パフォーマンスの最適化とリソースを有効活用する新しい技術とアルゴリズムが導入されています。OpenVINO は、次のような最先端のメソッドを GenAI ライブラリー に実装しています:

  • 連続バッチ処理

  • ページド・アテンション

  • ダイナミック・スプリット・ヒューズ

  • その他

現在では OpenVINO モデルサーバーに統合されており、生成ワークロードを効率的に実行できます。

この機能の使用方法を示す簡単な例は、クイックスタート・ガイドを参照してください。

LLM 計算機#

前述のクイックスタートでわかるように、構成の大部分は graph.pbtxt ファイルにあります。これは、モデルサーバーのテキスト生成サーバブルが、最新の OpenVINO GenAI ソリューションで動作する専用の LLM 計算機を備えた MediaPipe グラフとして展開されるためです。計算機はサイクルで実行され、応答のチャンクをクライアントに返すように設計されています。

入力には、モデルサーバーのフロントエンドから渡される HttpPayload 構造体が必要です:

struct HttpPayload { 
    std::string uri; 
    std::vector<std::pair<std::string, std::string>> headers; 
    std::string body; // always 
    rapidjson::Document* parsedJson; // pre-parsed body = null 
};

入力された JSON コンテンツは、チャット補完または補完 API と互換性がある必要があります。

入力には、LLM エンジンを表す共有オブジェクトである LLM_NODE_RESOURCES への参照を含むサイドパケットも含まれます。モデルをロードし、生成サイクルを実行し、生成された結果を生成ハンドラーを介して LLM 計算機に報告します。

LLM 計算機に基づくすべてのノードは、このサイドパケットの仕様を正確に満たす必要があります:

input_side_packet: "LLM_NODE_RESOURCES:llm"

変更されると、モデルサーバーはモデルにグラフを提供できなくなります

出力時に、計算​​機は json コンテンツを含む std::string を作成し、それが 1 つの応答として、またはストリーミングでチャンクとしてクライアントに返されます。

クイックスタートのグラフ設定からグラフを見てみましょう:

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: "./"       } 
  } 
  input_stream_handler { 
    input_stream_handler: "SyncSetInputStreamHandler", 
    options { 
      [mediapipe.SyncSetInputStreamHandlerOptions.ext] { 
        sync_set { 
          tag_index: "LOOPBACK:0" 
        } 
      } 
    } 
  } 
}

ユーザーがコンテンツを変更することは想定されていないため、上記のノード構成はテンプレートとして使用する必要があります。実際には、LLM エンジン・パラメーターを指定する node_options のみにユーザーは注意を払う必要があります。残りの構成は変更せずにそのままにしておきます。

計算機は、パイプライン構成を調整する次の node_options をサポートしています:

  • 必須文字列 models_path - モデル・ディレクトリーの場所 (相対でも可);

  • オプションの uint64 max_num_batched_tokens - 1 回の反復で処理されるトークンの最大数 [デフォルト = 256];

  • オプションの uint64 cache_size - KV キャッシュを保存するメモリーサイズ (GB 単位) [デフォルト = 8];

  • オプションの uint64 block_size - KV が 1 つのブロックに格納されるトークンの数 (Paged Attention 関連) [デフォルト = 32];

  • オプションの uint64 max_num_seqs - エンジンによってアクティブに処理されるシーケンスの最大数 [デフォルト = 256];

  • オプションの bool dynamic_split_fuse - Dynamic Split Fuse トークンのスケジュールを使用します [デフォルト = true];

  • オプションの文字列デバイス - モデルをロードするデバイス。サポートされる値: “CPU” [デフォルト = “CPU”]

  • オプションの文字列 plugin_config - OpenVINO デバイスプラグインの構成。通常のモデル構成と同じ形式で提供する必要があります [デフォルト = “”]

  • オプションの uint32 best_of_limit - エンドポイントで受け入れられる best_of パラメーターの最大値 [デフォルト = 20];

  • オプションの uint32 max_tokens_limit - エンドポイントで受け入れられる max_tokens パラメーターの最大値 [デフォルト = 4096];

cache_size の値はパフォーマンスと安定性に影響を与える可能性があります。LLM モデルの KV キャッシュデータを保存するために使用されます。環境機能、モデルサイズ、予想される同時実行レベルに基づいて調整します。キャッシュの実際の使用状況はサーバーログで追跡できます。ログ出力では以下のように確認できます:

[2024-07-30 14:28:02.536][624][llm_executor][info][llm_executor.hpp:65] All requests: 50; Scheduled requests: 25; Cache usage 23.9%;

ログで使用率が 100% に近づいていることが報告される場合は、cache_size パラメーターを増やすことを検討してください。キャッシュが消費されると、実行中のリクエストの一部がプリエンプトされ、他のリクエストが生成を完了できるようにキャッシュが解放される可能性があります (プリエンプトされたリクエストキャッシュは再度処理されるときに再計算する必要があるため、プリエンプトはパフォーマンスに悪影響を及ぼす可能性があります)。プリエンプションが不可能な場合、つまりキャッシュサイズが非常に小さく、それをすべて消費する単一の長時間実行リクエストがある場合、停止基準に達する前であっても、それ以上キャッシュを割り当てることができなくなると、リクエストは終了します。

LLM 計算機構成では、クライアント要求内のサンプリング・パラメーターの範囲を制限することもできます。必要に応じて、max_tokens_limitbest_of_limit のデフォルト値を変更します。これは、無効なリクエストによるメモリーの過剰消費を回避することを目的としています。

モデルのディレクトリー#

ノード構成では、LLM エンジンによってロードされたファイルを含むディレクトリーの場所を示す models_path を設定します。次のファイルを読み込みます:

├── openvino_detokenizer.bin
├── openvino_detokenizer.xml
├── openvino_model.bin
├── openvino_model.xml
├── openvino_tokenizer.bin
├── openvino_tokenizer.xml
├── tokenizer_config.json
├── template.jinja

メインモデル、トークナイザー、デトークナイザーは .xml ファイルと .bin ファイルから読み込まれ、それらはすべて必須です。チャット・テンプレートの処理に必要な情報を取得するため、tokenizer_config.jsontemplate.jinja が読み込まれます。

このモデル・ディレクトリーは、Hugging Face Hub のモデルまたはローカル・ファイル・システムに保存されている PyTorch モデルに基づいて作成できます。モデルを中間表現形式にエクスポートするのは 1 回限りの操作であり、量子化と圧縮と組み合わせると、読み込み時間を短縮し、ストレージ容量を削減できます。

Python 環境に必要な依存関係をインストールします:

pip3 install "optimum-intel[nncf,openvino]"@git+https://github.com/huggingface/optimum-intel.git@7a224c2419240d5fb58f2f75c2e29f179ed6da28 openvino-tokenizers

optimum-intel と openvino の開発は非常に活発であるため、依存関係の最新バージョンを使用することを推奨します:

export PIP_EXTRA_INDEX_URL="https://download.pytorch.org/whl/cpu https://storage.openvinotoolkit.org/simple/wheels/nightly" 
pip3 install --pre "optimum-intel[nncf,openvino]"@git+https://github.com/huggingface/optimum-intel.git openvino-tokenizers

LLM モデルは次のコマンドでエクスポートできます:

optimum-cli export openvino --disable-convert-tokenizer --model {LLM model in HF hub or Pytorch model folder} --weight-format {fp32/fp16/int8/int4/int4_sym_g128/int4_asym_g128/int4_sym_g64/int4_asym_g64} {target folder name}

精度パラメーターは重要であり、パフォーマンス、精度、メモリー使用量に影響を与える可能性があります。fp16 で開始することを推奨します。int8 精度を使用すると、精度への影響が少なくなり、メモリー消費量を削減し、レイテンシーを改善できます。メモリー使用量を最小限に抑えるため int4 を試し、最適な結果を得るためにさまざまなアルゴリズムをチェックしてください。

次のコマンドを使用してトークナイザー・モデルをエクスポートします:

convert_tokenizer -o {target folder name} --with-detokenizer --skip-special-tokens --streaming-detokenizer --not-add-special-tokens {tokenizer model in HF hub or Pytorch model folder}

テスト済みのモデルを確認します。

チャット・テンプレート#

チャット・テンプレートは、/chat/completions エンドポイントでのみ使用されます。テンプレートは /completions への呼び出しには適用されないため、/completions のみを操作する場合は、テンプレートが存在する必要はありません。

チャット・テンプレートの読み込みは次のように進行します:

  1. tokenizer.jinja が存在する場合は、そこからテンプレートをロードします。

  2. tokenizer.jinja が存在せず、tokenizer_config.json が存在する場合、その chat_template フィールドからテンプレートを読み取ります。存在しない場合は、デフォルトのテンプレートを使用します。

  3. tokenizer_config.json が存在する場合、eos_token および bos_token フィールドを読み取ります。存在しない場合、両方の値は空の文字列に設定されます。

: template.jinja ファイルと tokenizer_config.jsonchat_completion フィールドの両方が正常に読み込まれた場合、template.jinjatokenizer_config.json よりも優先されます。

ファイルまたはフィールドの読み込みでエラーが発生した場合 (存在するが誤っている場合)、テンプレートは読み込まれず、サーバブルは /chat/completions 呼び出しに応答しません。

チャット・テンプレートが指定されていない場合、デフォルトのテンプレートが適用されます。テンプレートは次のようにが表示されます:

"{% if messages|length != 1 %} {{ raise_exception('This servable accepts only single message requests') }}{% endif %}{{ messages[0]['content'] }}"

デフォルトのテンプレートがロードされると、messages リストに要素が 1 つだけ含まれている場合 (そうでない場合はエラーを返す)、サーバブルは /chat/completions 呼び出しを受け入れ、単一メッセージの content 値をモデルの入力プロンプトとして扱います。

制限事項#

LLM 計算機はプレビュー機能です。精度、安定性、パフォーマンスのテストが実行されますが、次のリリースでは製品レベルの品質が目標となります。現在、いくつかの既知の問題があります:

  • テキスト生成に関連するメトリックは、metrics エンドポイント経由では公開されません。LLM 計算機の主要メトリックは、アクティブなリクエスト、テキスト生成のスケジュール、KV キャッシュの使用に関する情報とともにサーバーログに含まれます。

  • bos_token または eos_token が文字列でない場合、tokenizer_config.json を変更する必要があります。例えば、コマンド sed -i '/"bos_token": null,/d' tokenizer_config.json および sed -i '/"eos_token": null,/d' tokenizer_config.json (これらが null の場合)。この回避策を必要とする既知のモデルは、Qwen1.5-7B-Chatallenai/OLMo-1.7-7B-hf です。次のリリースでは必要ありません。この問題は completions エンドポイントには影響しません。

  • Llama3.1 モデルでは精度の問題と応答が長すぎることが観察されており、これは調査中です。

  • まれに、モデルが有効でない utf8 シーケンスを生成することがありますが、そのシーケンスは Unicode 置換文字に置き換えられずにクライアントに返されます。クライアント側で置き換えを行うには、このコード text.decode("utf-8",errors='replace') を使用します。

  • マルチモーダル・モデルはまだサポートされていません。現時点では、画像をコンテキストとして送信することはできません。

  • 切断されたクライアントはサーバー上の生成フローを中断しません。eos トークンが生成されるか、max_tokens に到達すると終了します。

関連情報#