OpenVINO™ 推論要求

OpenVINO™ ランタイムは、非同期または同期してさまざまなデバイス上でモデルを実行する推論要求メカニズムを使用します。ov::InferRequest クラスは、OpenVINO™ ランタイム内でこの目的に使用されます。このクラスを使用すると、モデルの入力、出力データを設定および取得し、モデルの推論を実行できます。

推論要求の作成

ov::InferRequestov::CompiledModel から作成できます。

infer_request = compiled_model.create_infer_request()
auto infer_request = compiled_model.create_infer_request();

推論の実行

ov::InferRequest は、推論の同期モードと非同期モードをサポートします。

同期モード

アプリケーションの実行をブロックする ov::InferRequest::infer を使用して、同期モードでモデルを推論できます。

infer_request.infer()
infer_request.infer();

非同期モード

非同期モードは、推論が完了するのを待機することなく、アクセラレーターがビジーな間にホスト上でアプリケーションを動作させることで、アプリケーション全体のフレームレートを向上できます。非同期モードでモデルを推論するには、ov::InferRequest::start_async を使用します。

infer_request.start_async()
infer_request.start_async();

非同期モードでは、アプリケーションが推論結果を待機する 2 つの方法がサポートされています。

  • ov::InferRequest::wait_for - メソッドをブロックする最大期間をミリ秒単位で指定します。メソッドは、指定された時間が経過するか、結果が利用可能になるまで、どちらか早い方までブロックされます。

    infer_request.wait_for(10)
    
    infer_request.wait_for(std::chrono::milliseconds(10));
    
  • ov::InferRequest::wait - 推論結果が利用可能になるまで待機します。

    infer_request.wait()
    
    infer_request.wait();
    

どちらのメソッドもスレッドセーフです。

複数の推論要求を並行して実行する場合、デバイスはそれらを同時に処理できますが、完了順序は保証されません。これにより、ov::InferRequest::wait に基づくロジックが複雑になる可能性があります (コードがすべての要求を待機する必要がある場合を除く)。複数の要求のシナリオでは、ov::InferRequest::set_callback メソッドを使用して、要求の完了時に呼び出されるコールバックを設定することを検討してください。

def callback(request, _):
    request.start_async()

callbacks_info = {}
callbacks_info["finished"] = 0
infer_request.set_callback([&](std::exception_ptr ex_ptr) { 
    if (!ex_ptr) {
        // all done. Output data can be processed.
        // You can fill the input data and run inference one more time:
        infer_request.start_async();
    } else {
        // Something wrong, you can analyze exception_ptr
    }
});

コールバックで infer_request の弱い参照 (ov::InferRequest*ov::InferRequest&std::weal_ptr<ov::InferRequest> など) を使用します。循環参照を避ける必要があります。

詳細は、分類の非同期サンプルをご覧ください。

現在の推論要求の実行を中止したい場合は、ov::InferRequest::cancel メソッドを使用します。

infer_request.cancel()
infer_request.cancel();

入力テンソルと出力テンソルの操作

ov::InferRequest を使用すると、モデルに入力または出力が 1 つしかない場合、テンソル名、インデックス、ポートによって、引数なしで入力/出力テンソルを取得できます。

  • ov::InferRequest::get_input_tensorov::InferRequest::set_input_tensorov::InferRequest::get_output_tensorov::InferRequest::set_output_tensor 引数なしのメソッドを使用して、入出力が 1 つだけあるモデルの入出力テンソルを取得または設定できます。

    input_tensor = infer_request.get_input_tensor()
    output_tensor = infer_request.get_output_tensor()
    
    auto input_tensor = infer_request.get_input_tensor();
    auto output_tensor = infer_request.get_output_tensor();
    
  • ov::InferRequest::get_input_tensorov::InferRequest::set_input_tensorov::InferRequest::get_output_tensorov::InferRequest::set_output_tensor 引数付きのメソッドを使用して、入出力インデックスによって入出力テンソルを取得または設定できます。

    input_tensor = infer_request.get_input_tensor(0)
    output_tensor = infer_request.get_output_tensor(0)
    
    auto input_tensor = infer_request.get_input_tensor(0);
    auto output_tensor = infer_request.get_output_tensor(1);
    
  • ov::InferRequest::get_tensorov::InferRequest::set_tensor メソッドを使用して、テンソル名によって入出力テンソルを取得または設定できます。

    tensor1 = infer_request.get_tensor("result")
    tensor2 = ov.Tensor(ov.Type.f32, [1, 3, 32, 32])
    infer_request.set_tensor(input_tensor_name, tensor2)
    
    auto tensor1 = infer_request.get_tensor("tensor_name1");
    ov::Tensor tensor2;
    infer_request.set_tensor("tensor_name2", tensor2);
    
  • ov::InferRequest::get_tensorov::InferRequest::set_tensor メソッドを使用して、ポートごとに入出力テンソルを取得または設定できます。

    input_port = model.input(0)
    output_port = model.input(input_tensor_name)
    input_tensor = ov.Tensor(ov.Type.f32, [1, 3, 32, 32])
    infer_request.set_tensor(input_port, input_tensor)
    output_tensor = infer_request.get_tensor(output_port)
    
    auto input_port = model->input(0);
    auto output_port = model->output("tensor_name");
    ov::Tensor input_tensor;
    infer_request.set_tensor(input_port, input_tensor);
    auto output_tensor = infer_request.get_tensor(output_port);
    

推論要求の使用例

以下に、推論要求の使用例を示します。

モデルのカスケード

ov::InferRequest を使用して、モデルのカスケードを編成できます。推論要求はモデルごとに必要です。この場合、ov::InferRequest::get_tensor を使用して最初の要求から出力テンソルを取得し、ov::InferRequest::set_tensor を使用してそれを 2 番目の要求の入力として設定できます。2 番目のモデルが開始される前に最初の推論要求が再度実行されると、コンパイルされたモデル間で共有されるテンソルが最初のモデルによって書き換えられる可能性があることに注意してください。

output = infer_request1.get_output_tensor(0)
infer_request2.set_input_tensor(0, output)
auto output = infer_request1.get_output_tensor(0);
infer_request2.set_input_tensor(0, output);

ROI テンソルの使用

共有入力を複数のモデルで再利用することもできます。以前のモデルの割り当て済み入力内にある ROI オブジェクトを処理する場合、モデルに別の入力テンソルを割り当てる必要はありません。例えば、最初のモデルがビデオフレーム内のオブジェクト (入力テンソルとして保存) を検出し、2 番目のモデルが検出された境界ボックス (フレーム内の ROI) を入力として受け入れる場合。この場合、ov::Tensorov::Coordinate をパラメーターとして渡し、ov::Tensor を使用することで、(最初のモデルで使用された) あらかじめ割り当てられた入力テンソルを 2 番目のモデルで再利用し、新しいメモリーを割り当てずに ROI をクロップすることができます。

# input_tensor points to input of a previous network and
# cropROI contains coordinates of output bounding box **/
input_tensor = ov.Tensor(type=ov.Type.f32, shape=ov.Shape([1, 3, 100, 100]))
begin = [0, 0, 0, 0]
end = [1, 3, 32, 32]
# ...

/** input_tensor points to input of a previous network and
    cropROI contains coordinates of output bounding box **/
ov::Tensor input_tensor(ov::element::f32, ov::Shape({1, 3, 20, 20}));
ov::Coordinate begin({0, 0, 0, 0});
ov::Coordinate end({1, 2, 3, 3});
//...

リモートテンソルの使用

ov::RemoteContext を使用すると、リモート・デバイス・メモリーを操作するリモートテンソルを作成できます。

# NOT SUPPORTED
ov::RemoteContext context = core.get_default_context("GPU");
auto input_port = compiled_model.input("tensor_name");