[Docs][PyOV] Add docstrings for transformations + python examples for stateful model (#15978)

* [Docs][PyOV] Add docstrings for transformation + python examples for stateful

* add snippets + small improvements
This commit is contained in:
Anastasia Kuporosova 2023-03-03 17:07:34 +01:00 committed by GitHub
parent 190b64a0af
commit b130b73f80
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 242 additions and 26 deletions

View File

@ -0,0 +1,172 @@
# Copyright (C) 2018-2023 Intel Corporation
# SPDX-License-Identifier: Apache-2.0
import logging as log
import numpy as np
import sys
from openvino.runtime import opset10 as ops
from openvino.runtime import Core, Model, PartialShape, Tensor, Type
from openvino.runtime.passes import LowLatency2, MakeStateful, Manager
def state_network_example():
#! [ov:state_network]
input = ops.parameter([1, 1], dtype=np.float32)
read = ops.read_value(input, "variable0")
add = ops.add(read, input)
save = ops.assign(add, "variable0")
result = ops.result(add)
model = Model(results=[result], sinks=[save], parameters=[input])
#! [ov:state_network]
def low_latency_2_example():
#! [ov:low_latency_2]
# Precondition for Model.
# TensorIterator and Parameter are created in body of TensorIterator with names
tensor_iterator_name = "TI_name"
body_parameter_name = "body_parameter_name"
idx = "0" # this is a first variable in the network
# The State will be named "TI_name/param_name/variable_0"
state_name = tensor_iterator_name + "//" + body_parameter_name + "//" + "variable_" + idx # todo
#! [ov:get_ov_model]
core = Core()
ov_model = core.read_model("path_to_the_model")
#! [ov:get_ov_model]
# reshape input if needed
#! [ov:reshape_ov_model]
ov_model.reshape({"X": PartialShape([1, 1, 16])})
#! [ov:reshape_ov_model]
#! [ov:apply_low_latency_2]
manager = Manager()
manager.register_pass(LowLatency2())
manager.run_passes(ov_model)
#! [ov:apply_low_latency_2]
hd_specific_model = core.compile_model(ov_model)
# Try to find the Variable by name
infer_request = hd_specific_model.create_infer_request()
states = infer_request.query_state()
for state in states:
name = state.get_name()
if (name == state_name):
# some actions
#! [ov:low_latency_2]
#! [ov:low_latency_2_use_parameters]
manager.register_pass(LowLatency2(False))
#! [ov:low_latency_2_use_parameters]
def apply_make_stateful_tensor_names():
#! [ov:make_stateful_tensor_names]
core = Core()
ov_model = core.read_model("path_to_the_model")
tensor_names = {"tensor_name_1": "tensor_name_4",
"tensor_name_3": "tensor_name_6"}
manager = Manager()
manager.register_pass(MakeStateful(tensor_names))
manager.run_passes(ov_model)
#! [ov:make_stateful_tensor_names]
def apply_make_stateful_ov_nodes():
#! [ov:make_stateful_ov_nodes]
core = Core()
ov_model = core.read_model("path_to_the_model")
# Parameter_1, Result_1, Parameter_3, Result_3 are
# ops.parameter/ops.result in the ov_model
pairs = ["""(Parameter_1, Result_1), (Parameter_3, Result_3)"""]
manager = Manager()
manager.register_pass(MakeStateful(pairs))
manager.run_passes(ov_model)
#! [ov:make_stateful_ov_nodes]
def main():
#! [ov:state_api_usage]
# 1. Load inference engine
log.info("Loading Inference Engine")
core = Core()
# 2. Read a model
log.info("Loading network files")
model = core.read_model("path_to_the_model")
# 3. Load network to CPU
hw_specific_model = core.compile_model(model, "CPU")
# 4. Create Infer Request
infer_request = hw_specific_model.create_infer_request()
# 5. Reset memory states before starting
states = infer_request.query_state()
if (states.size() != 1):
log.error(f"Invalid queried state number. Expected 1, but got {str(states.size())}")
return -1
for state in states:
state.reset()
# 6. Inference
input_data = np.arange(start=1, stop=12, dtype=np.float32)
# This example demonstrates how to work with OpenVINO State API.
# Input_data: some array with 12 float numbers
# Part1: read the first four elements of the input_data array sequentially.
# Expected output for the first utterance:
# sum of the previously processed elements [ 1, 3, 6, 10]
# Part2: reset state value (set to 0) and read the next four elements.
# Expected output for the second utterance:
# sum of the previously processed elements [ 5, 11, 18, 26]
# Part3: set state value to 5 and read the next four elements.
# Expected output for the third utterance:
# sum of the previously processed elements + 5 [ 14, 24, 35, 47]
target_state = states[0]
# Part 1
log.info("Infer the first utterance")
for next_input in range(len(input_data)/3):
infer_request.infer({0 : input_data[next_input]})
state_buf = target_state.state.data
log.info(state_buf[0])
# Part 2
log.info("\nReset state between utterances...\n")
target_state.reset()
log.info("Infer the second utterance")
for next_input in range(len(input_data)/3, (len(input_data)/3 * 2)):
infer_request.infer({0 : input_data[next_input]})
state_buf = target_state.state.data
log.info(state_buf[0])
# Part 3
log.info("\nSet state value between utterances to 5...\n")
v = np.asarray([5], dtype=np.float32)
tensor = Tensor(v, shared_memory=True)
target_state.state = tensor
log.info("Infer the third utterance")
for next_input in range((input_data.size()/3 * 2), input_data.size()):
infer_request.infer({0 : input_data[next_input]})
state_buf = target_state.state.data
log.info(state_buf[0])
log.info("Execution successful")
#! [ov:state_api_usage]
return 0
if __name__ == '__main__':
sys.exit(main())

View File

@ -98,15 +98,41 @@ void regclass_transformations(py::module m) {
py::class_<ov::pass::MakeStateful, std::shared_ptr<ov::pass::MakeStateful>, ov::pass::ModelPass, ov::pass::PassBase>
make_stateful(m, "MakeStateful");
make_stateful.doc() = "openvino.runtime.passes.MakeStateful transformation";
// TODO: update docstrings for c-tors below
make_stateful.def(py::init<const ov::pass::MakeStateful::ParamResPairs&>(), py::arg("pairs_to_replace"));
make_stateful.def(py::init<const std::map<std::string, std::string>&>());
make_stateful.def(
py::init<const ov::pass::MakeStateful::ParamResPairs&>(),
py::arg("pairs_to_replace"),
R"( The transformation replaces the provided pairs Parameter and Result with openvino Memory operations ReadValue and Assign.
:param pairs_to_replace:
:type pairs_to_replace: List[Tuple[op.Parameter, op.Result]
)");
make_stateful.def(py::init<const std::map<std::string, std::string>&>(),
py::arg("pairs_to_replace"),
R"(
The transformation replaces the provided pairs Parameter and Result with openvino Memory operations ReadValue and Assign.
:param pairs_to_replace: a dictionary of names of the provided Parameter and Result operations.
:type pairs_to_replace: Dict[str, str]
)");
py::class_<ov::pass::LowLatency2, std::shared_ptr<ov::pass::LowLatency2>, ov::pass::ModelPass, ov::pass::PassBase>
low_latency(m, "LowLatency2");
low_latency.doc() = "openvino.runtime.passes.LowLatency2 transformation";
// TODO: update docstrings for c-tor below
low_latency.def(py::init<bool>(), py::arg("use_const_initializer") = true);
low_latency.def(py::init<bool>(),
py::arg("use_const_initializer") = true,
R"(
Create LowLatency2 pass which is used for changing the structure of the model,
which contains TensorIterator/Loop operations.
The transformation finds all TensorIterator/Loop layers in the network,
processes all back edges that describe a connection between Result and Parameter of the TensorIterator/Loop bodies,
and inserts ReadValue and Assign layers at the input and output corresponding to this back edge.
:param use_const_initializer: Changes the type of the initializing subgraph for ReadValue operations.
If "true", then the transformation inserts Constant before ReadValue operation.
If "false, then the transformation leaves existed initializing subgraph for ReadValue operation.
:type use_const_initializer: bool
)");
py::class_<ov::pass::ConvertFP32ToFP16,
std::shared_ptr<ov::pass::ConvertFP32ToFP16>,

View File

@ -30,7 +30,7 @@ def test_graph_function_api():
assert parameter_a.partial_shape == PartialShape([2, 2])
parameter_a.layout = ov.Layout("NC")
assert parameter_a.layout == ov.Layout("NC")
function = Model(model, [parameter_a, parameter_b, parameter_c], "TestFunction")
function = Model(model, [parameter_a, parameter_b, parameter_c], "TestModel")
function.get_parameters()[1].set_partial_shape(PartialShape([3, 4, 5]))
@ -56,7 +56,7 @@ def test_graph_function_api():
assert results[0].get_output_partial_shape(0) == PartialShape([2, 2])
results[0].layout = ov.Layout("NC")
assert results[0].layout.to_string() == ov.Layout("NC")
assert function.get_friendly_name() == "TestFunction"
assert function.get_friendly_name() == "TestModel"
@pytest.mark.parametrize(
@ -521,7 +521,7 @@ def test_sink_function_ctor():
add = ops.add(rv, input_data, name="MemoryAdd")
node = ops.assign(add, "var_id_667")
res = ops.result(add, "res")
function = Model(results=[res], sinks=[node], parameters=[input_data], name="TestFunction")
function = Model(results=[res], sinks=[node], parameters=[input_data], name="TestModel")
ordered_ops = function.get_ordered_ops()
op_types = [op.get_type_name() for op in ordered_ops]
@ -534,7 +534,7 @@ def test_sink_function_ctor():
assert (function.get_parameters()[0].get_partial_shape()) == PartialShape([2, 2])
assert len(function.get_parameters()) == 1
assert len(function.get_results()) == 1
assert function.get_friendly_name() == "TestFunction"
assert function.get_friendly_name() == "TestModel"
def test_node_version():

View File

@ -4,9 +4,9 @@
import os
import pytest
import numpy as np
import openvino.runtime as ov
from openvino.runtime import Model, PartialShape, Shape, opset8, Core
from openvino.runtime import Model, PartialShape, Shape, Core
from openvino.runtime import opset10
from openvino.runtime.passes import (
Manager,
ConstantFolding,
@ -20,16 +20,34 @@ from tests.test_utils.test_utils import create_filename_for_test
def get_model():
param = opset8.parameter(PartialShape([1, 3, 22, 22]), name="parameter")
param = opset10.parameter(PartialShape([1, 3, 22, 22]), name="parameter")
param.get_output_tensor(0).set_names({"parameter"})
relu = opset8.relu(param)
reshape = opset8.reshape(relu, opset8.shape_of(relu), False)
res = opset8.result(reshape, name="result")
relu = opset10.relu(param)
reshape = opset10.reshape(relu, opset10.shape_of(relu), False)
res = opset10.result(reshape, name="result")
res.get_output_tensor(0).set_names({"result"})
return Model([res], [param], "test")
def test_make_stateful():
param = opset10.parameter(PartialShape([1, 3, 22, 22]), name="parameter")
param.get_output_tensor(0).set_names({"parameter"})
relu = opset10.relu(param)
reshape = opset10.reshape(relu, opset10.shape_of(relu), False)
res = opset10.result(reshape, name="result")
res.get_output_tensor(0).set_names({"result"})
model = Model([res], [param], "test")
manager = Manager()
manager.register_pass(MakeStateful([(param, res)]))
manager.run_passes(model)
assert model is not None
assert len(model.get_parameters()) == 0
assert len(model.get_results()) == 0
def test_make_stateful_with_dict():
model = get_model()
manager = Manager()
@ -68,20 +86,20 @@ def test_convert_precision():
def test_low_latency2():
param_x = opset8.parameter(Shape([32, 40, 10]), np.float32, "X")
param_y = opset8.parameter(Shape([32, 40, 10]), np.float32, "Y")
param_m = opset8.parameter(Shape([32, 2, 10]), np.float32, "M")
param_x = opset10.parameter(Shape([32, 40, 10]), np.float32, "X")
param_y = opset10.parameter(Shape([32, 40, 10]), np.float32, "Y")
param_m = opset10.parameter(Shape([32, 2, 10]), np.float32, "M")
x_i = opset8.parameter(Shape([32, 2, 10]), np.float32, "X_i")
y_i = opset8.parameter(Shape([32, 2, 10]), np.float32, "Y_i")
m_body = opset8.parameter(Shape([32, 2, 10]), np.float32, "M_body")
x_i = opset10.parameter(Shape([32, 2, 10]), np.float32, "X_i")
y_i = opset10.parameter(Shape([32, 2, 10]), np.float32, "Y_i")
m_body = opset10.parameter(Shape([32, 2, 10]), np.float32, "M_body")
add = opset8.add(x_i, y_i)
zo = opset8.multiply(add, m_body)
add = opset10.add(x_i, y_i)
zo = opset10.multiply(add, m_body)
body = Model([zo], [x_i, y_i, m_body], "body_function")
ti = opset8.tensor_iterator()
ti = opset10.tensor_iterator()
ti.set_body(body)
ti.set_sliced_input(x_i, param_x.output(0), 0, 2, 2, 39, 1)
ti.set_sliced_input(y_i, param_y.output(0), 0, 2, 2, -1, 1)
@ -90,8 +108,8 @@ def test_low_latency2():
out0 = ti.get_iter_value(zo.output(0), -1)
out1 = ti.get_concatenated_slices(zo.output(0), 0, 2, 2, 39, 1)
result0 = opset8.result(out0)
result1 = opset8.result(out1)
result0 = opset10.result(out0)
result1 = opset10.result(out1)
model = Model([result0, result1], [param_x, param_y, param_m])