OpenVINO™ ランタイム Python API 高度な推論#

警告

ここで説明するすべての方法は、特定のハードウェアとソフトウェアの設定に大きく依存します。さまざまなモデルと入出力サイズを使用して独自の検証を行うことを検討してください。ここで紹介する方法は普遍的なものではなく、特定のパイプラインでは適用される場合とされない場合があります。すべてのトレードオフを考慮し、早まった最適化を避けてください。

CompiledModel による直接推論#

CompiledModel クラスは、モデルを指定して単一の同期推論を実行する __call__ メソッドを提供します。コンパクトなコードに加えて、オブジェクトはすでに作成された InferRequest を再利用するため、以降の CompiledModel.__call__ への呼び出しはオーバーヘッドが少なくなります。

# CompiledModel を呼び出すと InferRequest オブジェクトが作成され保存されます 
results_0 = compiled_model({"input_0": data_0, "input_1": data_1}) 
# 2 回目の呼び出しでは、以前に作成された InferRequest オブジェクトを再利用します 
results_1 = compiled_model({"input_0": data_2, "input_1": data_3})

入力と出力の共有メモリー#

CompiledModelInferRequest および AsyncInferQueue を使用する際、OpenVINO™ ランタイム Python API は追加モードとして “共有メモリー” を提供します。この機能を有効または無効にするには、share_inputsshare_outputs フラグを設定します。“共有メモリー” モードは、入力または出力が多く、データコピーのコストが高いと考えられる場合に有益です。

この機能では、 “ゼロコピー” アプローチで共有 Tensor インスタンスを作成し、入力設定のオーバーヘッドを最小限に抑えます。出力の場合、この機能はデータに numpy ビューを作成します。使用例:

# データは入力時にのみ共有可能 
_ = compiled_model({"input_0": data_0, "input_1": data_1}, share_inputs=True) 
_ = request.infer({"input_0": data_0, "input_1": data_1}, share_inputs=True) 
# データは出力時にのみ共有可能 
_ = request.infer({"input_0": data_0, "input_1": data_1}, share_outputs=True) 
# または、両方のフラグを組み合わせて、望ましい動作を実現することもできます 
_ = compiled_model({"input_0": data_0, "input_1": data_1}, share_inputs=False, share_outputs=True)

CompiledModel.__call__ では、入力の “共有メモリー” がデフォルトで有効になります。InferRequest.inferInferRequest.start_async などの他のメソッドでは、フラグを手動で True に設定する必要があります。出力の “共有メモリー” は、すべての逐次推論メソッド (CompiledModel.__call__ および InferRequest.infer) でデフォルトで無効になります。フラグを手動で True に設定する必要があります。

警告

データが共有されている場合、すべての更新 (後続の推論呼び出しを含む) が推論の入力と出力に影響を与える可能性があります。この機能は、特に関数の制御フロー外でデータが変更される可能性がある、マルチスレッド/並列コードでは注意して使用してください。

非同期呼び出しによるレイテンシーの隠蔽#

非同期呼び出しを使用すると、レイテンシーを隠匿して、コード全体的の実行時間を最適化できます。例えば、InferRequest.start_async は GIL を解放し、ノンブロッキング呼び出しを提供します。計算集約型の推論完了まで待機する間に、他の呼び出しを処理すると有益です。使用例:

import time 

# 長時間実行される関数 
def run(time_in_sec): 
    time.sleep(time_in_sec) 

# レイテンシーを隠匿しない 
results = request.infer({"input_0": data_0, "input_1": data_1})[0] 
run(time_in_sec) 

# レイテンシーを隠匿 
request.start_async({"input_0": data_0, "input_1": data_1}) 
run(time_in_sec) 
request.wait() 
results = request.get_output_tensor(0).data # Gather data from InferRequest

潜在的な並列化のメリットを得るためコード内の実行フローを最適化するかどうかは、ユーザー/開発者の責任です。

非同期呼び出しによる “リターンの遅延”#

“リターンの延期” は、同期呼び出しから常に返される OVDict のオーバーヘッドを排除する手法です。“リターンの延期” は以下の場合に適用されます:

  • 出力データの一部のみが必要な場合。例えば、特定のパイプライン・ステップで特定の出力 1 つのみが重要であり、すべての出力は大きいため、コピーにコストがかかる場合。

  • データを “すぐには” 必要としない場合。例えば、レイテンシー隠匿の一部としてパイプライン内部で後で抽出することができる場合があります。

  • データをリターンする必要がない場合。例えば、モデルは純粋な Tensor インターフェイスでチェーンされている場合があります。

# 標準的なアプローチ 
results = request.infer({"input_0": data_0, "input_1": data_1})[0] 

# “リターンの延期” アプローチ 
request.start_async({"input_0": data_0, "input_1": data_1}) 
request.wait() 
results = request.get_output_tensor(0).data # Gather data "on demand" from InferRequest