ステートフル・モデルの提供¶
ステートレスとステートフル・モデル¶
ステートレス・モデル¶
ステートレス・モデルは、すべての推論要求を独立して処理し、連続する推論要求間の依存関係を認識しません。したがって、推論要求間の状態は維持されません。ステートレス・モデルの例としては、画像分類や物体検出の畳み込みニューラル・ネットワーク (CNN) が挙げられます。
ステートフル・モデル¶
ステートフル・モデルは、連続する推論要求間の依存関係を認識します。次の推論が前の推論の結果に依存するように、推論要求間の状態を維持します。ステートフル・モデルの例としては、Long Short Term Memory (LSTM : 長短期記憶) などのオンライン音声認識モデルが挙げられます。
注: モデルサーバーのコンテキストでは、推論要求間でモデルが状態を維持する場合、モデルはステートフルであると見なされます。
一部のモデルは、データのシーケンス全体を入力として受け取り、内部でそのシーケンスの要素を反復処理し、反復間の状態を維持します。このようなモデルは、1 回の推論要求でシーケンス全体の推論を実行するため、ステートレスと見なされます。
ステートフル・モデルのロードと提供¶
ステートフル・モデルを使用してモデルサーバーを実行¶
OpenVINO モデルサーバーでのステートフル・モデルの提供は、ステートレス・モデルの提供と非常に似ています。唯一の違いは、ステートフル・モデルの場合、モデル設定でステートフル・フラグを設定する必要があることです。
rm_lstm4f からサンプルモデルをダウンロードして準備します。
mkdir models && cd models
wget https://storage.openvinotoolkit.org/models_contrib/speech/2021.2/rm_lstm4f/rm_lstm4f.counts
wget https://storage.openvinotoolkit.org/models_contrib/speech/2021.2/rm_lstm4f/rm_lstm4f.nnet
wget https://storage.openvinotoolkit.org/models_contrib/speech/2021.2/rm_lstm4f/rm_lstm4f.mapping
docker run -u $(id -u):$(id -g) -v $(pwd):/models:rw openvino/ubuntu20_dev:latest mo --framework kaldi --input_model /models/rm_lstm4f.nnet --counts /models/rm_lstm4f.counts --remove_output_softmax --output_dir /models/rm_lstm4f/1
コマンドラインからステートフル・モデルを使用して OVMS を起動します。
docker run -d -u $(id -u):$(id -g) -v $(pwd)/rm_lstm4f:/models/stateful_model -p 9000:9000 openvino/model_server:latest \
--port 9000 --model_path /models/stateful_model --model_name rm_lstm4f --stateful
構成ファイル経由でステートフル・モデルを使用して OVMS を開始します。
echo '{
"model_config_list":[
{
"config": {
"name":"rm_lstm4f",
"base_path":"/models/stateful_model",
"stateful": true
}
}
]
}' >> config.json
docker run -d -u $(id -u):$(id -g) -v $(pwd)/rm_lstm4f:/models/stateful_model -v $(pwd)/config.json:/models/config.json -p 9000:9000 openvino/model_server:latest \
--port 9000 --config_path /models/config.json
オプションで、ステートフル・モデルに固有の追加パラメーターを設定することもできます。
ステートフル・モデルの構成オプション¶
モデル構成:
オプション |
値形式 |
説明 |
デフォルト値 |
---|---|---|---|
|
|
true に設定すると、モデルはステートフルとしてロードされます。 |
false |
|
|
true に設定すると、モデルは定期的なシーケンス・クリーナー・スキャンの対象になります。 |
true |
|
|
モデルのインスタンスによって同時に処理できるシーケンスの数を決定します。 |
500 |
|
|
true に設定すると、モデルサーバーはモデルの読み込み時に低レイテンシーの変換を適用します。 |
false |
注: idle_sequence_cleanup
、max_sequence_number
、および low_latency_transformation
変換を設定するには、stateful
を true に設定する必要があります。
サーバー構成:
オプション |
値形式 |
説明 |
デフォルト値 |
---|---|---|---|
|
|
次のシーケンス・クリーナー・スキャンの時間間隔 (分単位)。アイドルシーケンスのクリーンアップの対象となっており、最後のスキャン以降非アクティブなモデルのシーケンスは削除されます。値をゼロにすると、シーケンスクリーナーは無効になります。 |
5 |
完全なセットアップを行うには、すべてのサーバーおよびモデルの構成オプションも参照してください。
ステートフル・モデルで推論を実行¶
シーケンス処理のための特別な入力¶
ステートフル・モデルは、相互に関連付けられ、一連の要求を形成する連続した推論要求に対して機能します。単一のステートフル・モデルは、一度に複数の独立したシーケンスを処理できます。モデルサーバーは、ステートフル・モデルに対する要求を受信すると、各要求を適切なシーケンスとそのメモリー状態にマップします。OVMS は、シーケンスの開始と終了も追跡して、システムリソースを適切に管理します。
ステートフル・モデルへの要求には、予測用のデータ以外に追加の入力を含める必要があります。
sequence_id
- これは、シーケンスを識別する 64 ビットの符号なし整数です (モデル・インスタンスのスコープ内で一意です)。値 0 は、この入力を全く提供しないことと同じです。-
sequence_control_input
- これは、シーケンスの開始と終了を示す 32 ビットの符号なし整数です。受け入れられる値は次のとおりです。0 - 制御入力なし (効果はありません - この入力を全く提供しないのと同じです)
1 - シーケンスの始まりを示します
2 - シーケンスの終わりを示します
注: モデルサーバーは、すべての応答に sequence_id
も追加します。sequence_id
出力の名前と形式は、sequence_id
入力の場合と同じです。
sequence_id
と sequence_control_input
は両方とも、1 つの要素配列 (shape:[1]) と適切な精度を持つテンソルとして提供されます。
以下の gRPC と HTTP の例を参照してください。
シーケンスを正常に推測するには、次のアクションを実行します。
-
シーケンスの最初の要求を送信し、シーケンスの開始を通知します。
シーケンスを開始するには、値 1 を持つ
sequence_control_input
を要求の入力に追加する必要があります。次のことも可能です。選択した値を使用して
sequence_id
を追加する。sequence_id
を 0 で追加するか、sequence_id
を追加しません。この場合、モデルサーバーはシーケンスに一意の ID を提供し、それが出力に追加されるため、それを読み取って次の要求で使用することができます。
指定された
sequence_id
がすでに占有されている場合、OVMS は競合を避けるためエラーを返します。 -
最後の要求を除く残りの要求を送信します。
シーケンスの途中で要求を送信するには、シーケンスの
sequence_id
を追加する必要があります。この場合、sequence_id
は必須であり、この入力を提供しないこと、またはその値を 0 に設定することは許可されません。この場合、
sequence_control_input
は空または 0 でなければなりません。 -
シーケンス内の最後の要求を送信し、シーケンスの終了を通知します。
シーケンスを終了するには、値 2 を持つ
sequence_control_input
を要求の入力に追加する必要があります。シーケンスのsequence_id
も追加する必要があります。この場合、sequence_id
は必須であり、この入力を提供しないこと、またはその値を 0 に設定することは許可されません。
gRPC による推論¶
gRPC を介したステートフル・モデルの推論は、ステートレス・モデルの推論と非常に似ています (gRPC API を参照)。違いは、ステートフル・モデルへの要求には、適切なシーケンス処理に必要な情報を含む追加の入力が含まれている必要があることです。
sequence_id
と sequence_control_input
を TensorProtos として gRPC リクエスト入力に追加する必要があります。
sequence_id
モデルの場合、サーバーは tensor proto uint64_val フィールドに 1 つの値を期待します。sequence_control_input
モデルの場合、サーバーは tensor proto uint32_val フィールドに 1 つの値を期待します。
両方の入力で TensorShape
が [1] に設定され、適切な DataType
が必要です。
DT_UINT64
はsequence_id
DT_UINT32
はsequence_control_input
例 (Python tensorflow および tensorflow-serving-api パッケージを使用):
...
import grpc
from tensorflow_serving.apis import prediction_service_pb2_grpc
from tensorflow import make_tensor_proto, make_ndarray, expand_dims
from tensorflow_serving.apis import predict_pb2
...
SEQUENCE_START = 1
SEQUENCE_END = 2
sequence_id = 10
channel = grpc.insecure_channel("localhost:9000")
stub = prediction_service_pb2_grpc.PredictionServiceStub(channel)
request = predict_pb2.PredictRequest()
request.model_spec.name = "stateful_model"
"""
Add inputs with data to infer
"""
################ Add stateful specific inputs #################
################ Starting sequence with custom ID #################
request.inputs['sequence_control_input'].CopyFrom(
make_tensor_proto([SEQUENCE_START], dtype="uint32"))
request.inputs['sequence_id'].CopyFrom(
make_tensor_proto([sequence_id], dtype="uint64"))
################ Starting sequence without ID #################
request.inputs['sequence_control_input'].CopyFrom(
make_tensor_proto([SEQUENCE_START], dtype="uint32"))
################ Non control requests #################
request.inputs['sequence_id'].CopyFrom(
make_tensor_proto([sequence_id], dtype="uint64"))
################ Ending sequence #################
request.inputs['sequence_control_input'].CopyFrom(
make_tensor_proto([SEQUENCE_END], dtype="uint32"))
request.inputs['sequence_id'].CopyFrom(
make_tensor_proto([sequence_id], dtype="uint64"))
###################################################################
# Send request to OVMS and get response
response = stub.Predict(request, 10.0)
# response variable now contains model outputs (inference results) as well as sequence_id in response.outputs
# Fetch sequence id from the response
sequence_id = response.outputs['sequence_id'].uint64_val[0]
HTTP による推論¶
HTTP を介したステートフル・モデルの推論は、ステートレス・モデルの推論と非常に似ています (REST API を参照)。違いは、ステートフル・モデルへの要求には、適切なシーケンス処理に必要な情報を含む追加の入力が含まれている必要があることです。
sequence_id
および sequence_control_input
は、JSON 本文の inputs
フィールドに新しい key:value
のペアを追加することによって、HTTP リクエストに追加する必要があります。
どちらの入力でも、値は 1 次元配列内の単一の数値である必要があります。
例 (Python 要求パッケージを使用):
...
import json
import requests
...
SEQUENCE_START = 1
SEQUENCE_END = 2
sequence_id = 10
inputs = {}
"""
Add inputs with data to infer
"""
################ Add stateful specific inputs #################
################ Starting sequence with custom ID #################
inputs['sequence_control_input'] = [int(SEQUENCE_START)]
inputs['sequence_id'] = [int(sequence_id)]
################ Starting sequence without ID #################
inputs['sequence_control_input'] = [int(SEQUENCE_START)]
################ Non control requests #################
inputs['sequence_id'] = [int(sequence_id)]
################ Ending sequence #################
inputs['sequence_control_input'] = [int(SEQUENCE_END)]
inputs['sequence_id'] = [int(sequence_id)]
###################################################################
# Prepare request
signature = "serving_default"
request_body = json.dumps({"signature_name": signature,'inputs': inputs})
# Send request to OVMS and get response
response = requests.post("localhost:5555/v1/models/stateful_model:predict", data=request_body)
# Parse response
response_body = json.loads(response.text)
# response_body variable now contains model outputs (inference results) as well as sequence_id in response_body["outputs"]
# Fetch sequence id from the response
sequence_id = response_body["outputs"]["sequence_id"]
エラーコード¶
要求が無効であるか、処理できなかった場合、ステートフル・モデルでの推論特有のエラーが発生することが予想されます。
説明 |
gRPC |
HTTP |
---|---|---|
指定された ID を持つシーケンスは存在しません。 |
NOT_FOUND |
404 NOT FOUND |
指定された ID を持つシーケンスはすでに存在します。 |
ALREADY_EXISTS |
409 CONFLICT |
サーバーは終了用に設定されたシーケンスの ID を持つ SEQUENCE START リクエストを受信しましたが、そのシーケンスの最後のリクエストはまだ処理中です。 |
FAILED_PRECONDITION |
412 PRECONDITION FAILED |
最大シーケンス数に達しました。新しいシーケンスを作成できませんでした。 |
UNAVAILABLE |
503 SERVICE UNAVAILABLE |
リクエスト入力にシーケンス ID が指定されていません。 |
INVALID_ARGUMENT |
400 BAD REQUEST |
シーケンス制御入力の予期しない値です。 |
INVALID_ARGUMENT |
400 BAD REQUEST |
予期されたテンソル・プロト・フィールド uint64_val でシーケンス ID が見つかりませんでした。 |
INVALID_ARGUMENT |
N/A |
予期されたテンソル・プロト・フィールド uint32_val でシーケンス ID が見つかりませんでした。 |
INVALID_ARGUMENT |
N/A |
特殊な入力プロトにはテンソル形状情報が含まれません。 |
INVALID_ARGUMENT |
N/A |
アイドルシーケンスのクリーンアップ¶
一度開始されたシーケンスは、接続の切断などの何らかの理由でドロップされる可能性があります。この場合、モデルサーバーは SEQUENCE_END 信号を受信せず、シーケンスリソースを解放しません。アイドル状態のシーケンスが無期限に保持されるのを防ぐために、モデルサーバーは、ステートフル・モデルを定期的にスキャンし、そのシーケンスが最近有効な推論要求を受信したかどうかをチェックするシーケンス・クリーナー・スレッドを起動します。そうでない場合、そのようなシーケンスは削除され、そのリソースが解放され、ID が再利用できるようになります。
2 つのパラメーターはシーケンスのクリーンアップを制御します。1 つは、次のスキャン間の時間間隔の値を保持する sequence_cleaner_poll_wait_minutes
です。2 つの連続したチェックの間に、特定のシーケンス ID を持つ有効な要求が 1 つも存在しなかった場合、シーケンスはアイドル状態と見なされ、削除されます。
sequence_cleaner_poll_wait_minutes
はサーバー・パラメーターであり、すべてのモデルに共通です。デフォルトでは、2 回の連続したクリーナースキャン間の時間は 5 分に設定されています。この値を 0 に設定すると、シーケンスクリーナーが無効になります。
ステートフル・モデルは、アイドルシーケンスのクリーンアップの対象となる場合と、対象とならない場合があります。これは、idle_sequence_cleanup
パラメーターを使用してモデルごとに設定できます。true
に設定すると、シーケンスクリーナーがそのモデルをチェックします。それ以外の場合、シーケンスクリーナーはそのモデルをスキップし、アクティブでないシーケンスは削除されません。デフォルトでは、この値は true
に設定されます。
既知の問題¶
OVMS のステートフル・モデルの使用には制限があります。
ターゲットデバイスとして CPU のみを使用した推論実行をサポートします。
メモリーレイヤーを備えた Kaldi モデルとテンソル・イテレーターを備えた非 Kaldi モデルをサポートします。OpenVINO でのステートフル・ネットワークの表現については、ステートフル・ネットワークに関するドキュメントを参照してください。
自動バッチサイズと形状は、ステートフル・モデルでは使用できません。
ステートフル・モデルのインスタンスは DAG では使用できません。
要求の順序は、単一のクライアントが後続の要求を同期的に送信する場合にのみ保証されます。同じシーケンスを同時に操作すると、結果の精度に悪影響を及ぼす可能性があります。
モデル構成の変更によりステートフル・モデルのインスタンスがリロードされると、進行中のシーケンスがすべて削除されます。
モデルタイプは実行時に変更できません。ステートフル・フラグの切り替えは拒否されます。