OpenVINO™ Python API 限定機能#
OpenVINO™ ランタイム Python API は、ユーザー・エクスペリエンスを向上させる追加機能とヘルパーを提供します。Python API の主な目標は、Python ユーザーに使いやすく、シンプルでありながら強力なツールを提供することです。
モデルのコンパイルがより簡単に#
CompiledModel
はヘルパーメソッドを使用して簡単に作成できます。Core
の作成を隠匿し、デフォルトで AUTO
推論モードを適用します。
compiled_model = ov.compile_model(model)
モデル/コンパイル済みモデルの入力と出力#
C++ API に適合した関数に加えて、一部の関数には Python 対応または拡張機能があります。例えば、Model
および CompiledModel
の入力/出力には、プロパティーを介してアクセスできます。
import openvino.runtime.opset12 as ops
core = ov.Core()
input_a = ops.parameter([8], name="input_a")
res = ops.absolute(input_a)
model = ov.Model(res, [input_a])
compiled = core.compile_model(model, "CPU")
model.outputs[0].tensor.set_names({"result_0"}) # 出力名を追加
print(model.inputs)
print(model.outputs)
print(compiled.inputs)
print(compiled.outputs)
さまざまなクラスで使用できるヘルパー関数またはプロパティーについては、Python API ドキュメントを参照してください。
テンソルの操作#
Python API では、データをテンソルとして渡すことができます。Tensor
オブジェクトは、指定された配列のデータコピーを保持します。numpy 配列の dtype
は、OpenVINO™ タイプに自動変換されます。
data_float64 = np.ones(shape=(2,8))
tensor = ov.Tensor(data_float64)
assert tensor.element_type == ov.Type.f64
data_int32 = np.ones(shape=(2,8), dtype=np.int32)
tensor = ov.Tensor(data_int32)
assert tensor.element_type == ov.Type.i32
推論の実行#
Python API は、推論の同期モードおよび非同期モードへの追加の呼び出しメソッドをサポートしています。
すべての推論メソッドは、ユーザーが Python dict またはリストに収集された一般的な numpy 配列としてデータを渡すことを許可します。
# 入力データを辞書形式で渡す
infer_request.infer(inputs={0: data})
# 入力データをリスト形式で渡す
infer_request.infer(inputs=[data])
推論の結果はさまざまな方法で取得できます:
# 出力テンソルを取得
results = infer_request.get_output_tensor().data
# CompiledModel の出力ノードでテンソルを取得
results = infer_request.get_tensor(compiled.outputs[0]).data
# 特別なヘルパー・プロパティーを使用してすべての結果を取得
results = list(infer_request.results.values())
同期モード - 拡張#
Python API は、モデルを推論するさまざまな同期呼び出しを提供しますが、これによりアプリケーションの実行がブロックされます。さらに、これらの呼び出しは推論の結果を返します:
# InferRequest の単純な呼び出し
results = infer_request.infer(inputs={0: data})
# 拡張機能: CompiledModel を直接呼び出し
results = compiled_model(inputs={0: data})
推論結果 - OVDict#
同期呼び出しは、OVDict
と呼ばれる特別なデータ構造を返します。それは “frozen dictionary (凍った辞書)” に例えることができます。オブジェクトの要素にアクセスするにはさまざまな方法があります:
results = compiled_model(inputs={0: data})
# 文字列によるアクセス
_ = results["result_0"]
# インデックスによるアクセス
_ = results[0]
# 出力ポート経由でアクセス
_ = results[compiled_model.outputs[0]]
# キーにイテレーターを使用
_ = results[next(iter(results))]
# 値を反復
_ = next(iter(results.values()))
注
to_dict()
メソッドを使用して、OVDict
をネイティブ辞書に変換できます。
警告
to_dict()
を使用すると、文字列と整数によるアクセスが失われます。さらに、シャローコピーが実行されるため、変更は元のオブジェクトにも影響する可能性があります。
AsyncInferQueue#
非同期モードのパイプラインは、AsyncInferQueue
と呼ばれるラッパークラスでサポートできます。このクラスは、InferRequest
オブジェクト ( “ジョブ” とも呼ばれます) のプールを自動的に生成し、パイプラインのフローを制御する同期メカニズムを提供します。
各ジョブは一意の id
によって区別できます。ID の範囲は 0 から AsyncInferQueue
コンストラクターで指定されたジョブの数までです。
start_async
関数呼び出しは同期する必要はありません。キューがビジーまたは過負荷の場合は、利用可能なジョブがあれば待機します。すべての AsyncInferQueue
コードブロックは、プール内のすべてのジョブの “グローバル” 同期を提供し、それらへのアクセスが安全であることを保証する wait_all
関数で終わる必要があります。
core = ov.Core()
# 2 つの入力を加算するシンプルなモデル
input_a = ops.parameter([8])
input_b = ops.parameter([8])
res = ops.add(input_a, input_b)
model = ov.Model(res, [input_a, input_b])
compiled = core.compile_model(model, "CPU")
# AsyncInferQueue が保持する InferRequest の数
jobs = 4
infer_queue = ov.AsyncInferQueue(compiled, jobs)
# データの作成
data = [np.array([i] * 8, dtype=np.float32) for i in range(jobs)]
# すべてのジョブを実行
for i in range(len(data)):
infer_queue.start_async({0: data[i], 1: data[i]})
infer_queue.wait_all()
警告
InferRequest
オブジェクトは、AsyncInferQueue
オブジェクトを反復処理するか、[id]
によって取得され、テンソルの取得などの読み取り専用メソッドで動作することが保証されています。単一要求の変更メソッド (start_async、set_callback など) は、親の AsyncInferQueue オブジェクトを無効な状態にします。
要求から結果の取得#
wait_all
を呼び出した後は、ジョブとデータに安全にアクセスできるようになります。[id]
で特定のジョブを取得すると、 InferRequest
オブジェクトが返され、出力データをシームレスに取得できます。
results = infer_queue[3].get_output_tensor().data
コールバックの設定#
AsyncInferQueue
のもう 1 つの機能は、コールバックを設定できることです。コールバックが設定されていると、推論を終了するジョブは Python 関数を呼び出します。コールバック関数には 2 つの引数が必要です。1 つはコールバックを呼び出すリクエストで、InferRequest
API 提供します。もう 1 つは “userdata” と呼ばれランタイム値を渡すことができます。これらの値は任意の Python タイプにすることができ、コールバック関数内で使用されます。
AsyncInferQueue
のコールバックは、すべてのジョブで均一です。実行時に GIL を取得し、関数内でのデータ操作の安全性を保証します。
data_done = [False for _ in range(jobs)]
def f(request, userdata):
print(f"Done! Result: {request.get_output_tensor().data}")
data_done[userdata] = True
infer_queue.set_callback(f)
for i in range(len(data)):
infer_queue.start_async({0: data[i], 1: data[i]}, userdata=i)
infer_queue.wait_all()
assert all(data_done)
u1、u4、および i4 要素タイプの操作#
OpenVINO™ は低精度の要素タイプをサポートしており、Python でそれらを処理する方法がいくつかあります。このような要素タイプを使用して入力テンソルを作成するには、バイト・サイズが元の入力サイズと一致する新しい numpy 配列にデータをパックする必要があります:
from openvino.helpers import pack_data
packed_buffer = pack_data(unt8_data, ov.Type.u4)
# 要素タイプの形状を持つテンソルを作成
t = ov.Tensor(packed_buffer, [100], ov.Type.u4)
テンソルから低精度の値を抽出して numpy 配列に格納するには、次のヘルパーを使用できます:
from openvino.helpers import unpack_data
unpacked_data = unpack_data(t.data, t.element_type, t.shape)
assert np.array_equal(unpacked_data , unt8_data)
GIL のリリース#
Python API の一部の関数は、ワーク量の多いコードの実行中に Global Lock Interpreter (GIL) を解放します。これは、Python スレッドを使用してアプリケーションでより多くの並列処理を実現するのに役立ちます。GIL の詳細については、Python API ドキュメントを参照してください。
import openvino as ov
from threading import Thread
input_data = []
# 入力データの処理は別のスレッドで行われ、
# モデルのコンパイルと推論要求の作成は
# メインスレッドで実行されます。
def prepare_data(input, image):
shape = list(input.shape)
resized_img = np.resize(image, shape)
input_data.append(resized_img)
core = ov.Core()
model = core.read_model(model_path)
# prepare_data 関数をターゲットとしてスレッドを作成し、開始
thread = Thread(target=prepare_data, args=[model.input(), image])
thread.start()
# GIL は compile_model でリリースされます。
# メインスレッドがバックグラウンドで実行されている間に、
# 上位のスレッドがジョブを開始できるようになります。
compiled = core.compile_model(model, "CPU")
# compile_model から戻った後、メインスレッドは GIL を取得し、
# create_infer_request を開始して GIL を再度解放します。
request = compiled.create_infer_request()
# スレッドに参加して input_data が準備できていることを確認
thread.join()
# 推論を実行
request.infer(input_data)
注
GIL が解放されても、関数は引き続き C++ の Python オブジェクトを変更したり操作したりできます。したがって、参照カウントは変化しません。これらのオブジェクトが別のスレッドと共有される場合に備えて、スレッドの安全性に注意を払う必要があります。Python で複数のスレッドが生成された場合にのみコードに影響する可能性があります。
GIL を解放する関数一覧#
openvino.runtime.AsyncInferQueue.start_async
openvino.runtime.AsyncInferQueue.is_ready
openvino.runtime.AsyncInferQueue.wait_all
openvino.runtime.AsyncInferQueue.get_idle_request_id
openvino.runtime.CompiledModel.create_infer_request
openvino.runtime.CompiledModel.infer_new_request
openvino.runtime.CompiledModel.__call__
openvino.runtime.CompiledModel.export
openvino.runtime.CompiledModel.get_runtime_model
openvino.runtime.Core.compile_model
openvino.runtime.Core.read_model
openvino.runtime.Core.import_model
openvino.runtime.Core.query_model
openvino.runtime.Core.get_available_devices
openvino.runtime.InferRequest.infer
openvino.runtime.InferRequest.start_async
openvino.runtime.InferRequest.wait
openvino.runtime.InferRequest.wait_for
openvino.runtime.InferRequest.get_profiling_info
openvino.runtime.InferRequest.query_state
openvino.runtime.Model.reshape
openvino.preprocess.PrePostProcessor.build