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"}) # Add name for Output
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 配列としてデータを渡すことを許可します。
# Passing inputs data in form of a dictionary
infer_request.infer(inputs={0: data})
# Passing inputs data in form of a list
infer_request.infer(inputs=[data])
推論の結果はさまざまな方法で取得できます。
# Get output tensor
results = infer_request.get_output_tensor().data
# Get tensor with CompiledModel's output node
results = infer_request.get_tensor(compiled.outputs[0]).data
# Get all results with special helper property
results = list(infer_request.results.values())
同期モード - 拡張¶
Python API は、モデルを推論するさまざまな同期呼び出しを提供しますが、これによりアプリケーションの実行がブロックされます。さらに、これらの呼び出しは推論の結果を返します。
# Simple call to InferRequest
results = infer_request.infer(inputs={0: data})
# Extra feature: calling CompiledModel directly
results = compiled_model(inputs={0: data})
推論結果 - OVDict¶
同期呼び出しは、OVDict
と呼ばれる特別なデータ構造を返します。それは “frozen dictionary (凍った辞書)” に例えることができます。オブジェクトの要素にアクセスするにはさまざまな方法があります。
results = compiled_model(inputs={0: data})
# Access via string
_ = results["result_0"]
# Access via index
_ = results[0]
# Access via output port
_ = results[compiled_model.outputs[0]]
# Use iterator over keys
_ = results[next(iter(results))]
# Iterate over values
_ = next(iter(results.values()))
注
to_dict()
メソッドを使用して、OVDict
をネイティブ辞書に変換できます。
警告
to_dict()
を使用すると、文字列と整数によるアクセスが失われます。さらに、シャローコピーが実行されるため、変更は元のオブジェクトにも影響する可能性があります。
AsyncInferQueue¶
非同期モードのパイプラインは、AsyncInferQueue
と呼ばれるラッパークラスでサポートできます。このクラスは、InferRequest
オブジェクト ( “ジョブ” とも呼ばれます) のプールを自動的に生成し、パイプラインのフローを制御する同期メカニズムを提供します。
各ジョブは一意の id
によって区別できます。ID の範囲は 0 から AsyncInferQueue
コンストラクターで指定されたジョブの数までです。
start_async
関数呼び出しは同期する必要はありません。キューがビジーまたは過負荷の場合は、利用可能なジョブがあれば待機します。すべての AsyncInferQueue
コードブロックは、プール内のすべてのジョブの “グローバル” 同期を提供し、それらへのアクセスが安全であることを保証する wait_all
関数で終わる必要があります。
core = ov.Core()
# Simple model that adds two inputs together
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")
# Number of InferRequests that AsyncInferQueue holds
jobs = 4
infer_queue = ov.AsyncInferQueue(compiled, jobs)
# Create data
data = [np.array([i] * 8, dtype=np.float32) for i in range(jobs)]
# Run all 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)
# Create tensor with shape in element types
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 = []
# Processing input data will be done in a separate thread
# while compilation of the model and creation of the infer request
# is going to be executed in the main thread.
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)
# Create thread with prepare_data function as target and start it
thread = Thread(target=prepare_data, args=[model.input(), image])
thread.start()
# The GIL will be released in compile_model.
# It allows a thread above to start the job,
# while main thread is running in the background.
compiled = core.compile_model(model, "CPU")
# After returning from compile_model, the main thread acquires the GIL
# and starts create_infer_request which releases it once again.
request = compiled.create_infer_request()
# Join the thread to make sure the input_data is ready
thread.join()
# running the inference
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