[PyOV] Add node name to autogenerated nodes by default (#23712)
### Details: - Automatically adding the node name to all autogenerated constants. Example: `NodeName/Constant_X` - Removed `apply_affix_on` approach. - Adjusted testcases to check compatibility. - The change is backward incompatible -- breaking cases unknown. - To be added: extend other operations `as_node/as_nodes` to follow this example, as this approach is not automatically applicable. ### Tickets: - CVS-133139 --------- Co-authored-by: Katarzyna Mitrus <katarzyna.mitrus@intel.com>
This commit is contained in:
parent
2700cef43f
commit
ea413a654e
|
|
@ -15,7 +15,7 @@ from openvino.runtime import Node, Shape, Type, Output
|
|||
from openvino.runtime.op import Constant, Result
|
||||
from openvino.runtime.opset1 import convert_like
|
||||
from openvino.runtime.opset_utils import _get_node_factory
|
||||
from openvino.runtime.utils.decorators import apply_affix_on, binary_op, nameable_op, unary_op
|
||||
from openvino.runtime.utils.decorators import binary_op, nameable_op, unary_op
|
||||
from openvino.runtime.utils.types import (
|
||||
NumericData,
|
||||
NodeInput,
|
||||
|
|
@ -350,7 +350,6 @@ def result(data: Union[Node, Output, NumericData], name: Optional[str] = None) -
|
|||
|
||||
|
||||
@nameable_op
|
||||
@apply_affix_on("data", "input_low", "input_high", "output_low", "output_high")
|
||||
def fake_quantize(
|
||||
data: NodeInput,
|
||||
input_low: NodeInput,
|
||||
|
|
@ -360,9 +359,6 @@ def fake_quantize(
|
|||
levels: int,
|
||||
auto_broadcast: str = "NUMPY",
|
||||
name: Optional[str] = None,
|
||||
*,
|
||||
prefix: Optional[str] = None,
|
||||
suffix: Optional[str] = None,
|
||||
) -> Node:
|
||||
r"""Perform an element-wise linear quantization on input data.
|
||||
|
||||
|
|
@ -375,10 +371,6 @@ def fake_quantize(
|
|||
:param auto_broadcast: The type of broadcasting specifies rules used for
|
||||
auto-broadcasting of input tensors.
|
||||
:param name: Optional name of the new node.
|
||||
:param prefix: Optional keyword-only string to apply before original names of
|
||||
all generated input nodes (for example: passed as numpy arrays).
|
||||
:param suffix: Optional keyword-only string to apply after original names of
|
||||
all generated input nodes (for example: passed as numpy arrays).
|
||||
:return: New node with quantized value.
|
||||
|
||||
Input floating point values are quantized into a discrete set of floating point values.
|
||||
|
|
@ -400,6 +392,6 @@ def fake_quantize(
|
|||
"""
|
||||
return _get_node_factory_opset13().create(
|
||||
"FakeQuantize",
|
||||
as_nodes(data, input_low, input_high, output_low, output_high),
|
||||
as_nodes(data, input_low, input_high, output_low, output_high, name=name),
|
||||
{"levels": levels, "auto_broadcast": auto_broadcast.upper()},
|
||||
)
|
||||
|
|
|
|||
|
|
@ -4,15 +4,21 @@
|
|||
|
||||
from functools import wraps
|
||||
from inspect import getfullargspec
|
||||
from typing import Any, Callable, List
|
||||
from typing import Any, Callable, List, Optional
|
||||
|
||||
from openvino.runtime import Node, Output
|
||||
from openvino.runtime.utils.types import NodeInput, as_node, as_nodes
|
||||
|
||||
|
||||
def _set_node_friendly_name(node: Node, /, **kwargs: Any) -> Node:
|
||||
def _get_name(**kwargs: Any) -> Node:
|
||||
if "name" in kwargs:
|
||||
node.friendly_name = kwargs["name"]
|
||||
return kwargs["name"]
|
||||
return None
|
||||
|
||||
|
||||
def _set_node_friendly_name(node: Node, *, name: Optional[str] = None) -> Node:
|
||||
if name is not None:
|
||||
node.friendly_name = name
|
||||
return node
|
||||
|
||||
|
||||
|
|
@ -22,47 +28,20 @@ def nameable_op(node_factory_function: Callable) -> Callable:
|
|||
@wraps(node_factory_function)
|
||||
def wrapper(*args: Any, **kwargs: Any) -> Node:
|
||||
node = node_factory_function(*args, **kwargs)
|
||||
node = _set_node_friendly_name(node, **kwargs)
|
||||
node = _set_node_friendly_name(node, name=_get_name(**kwargs))
|
||||
return node
|
||||
|
||||
return wrapper
|
||||
|
||||
|
||||
def _apply_affix(node: Node, prefix: str = "", suffix: str = "") -> Node:
|
||||
node.friendly_name = prefix + node.friendly_name + suffix
|
||||
return node
|
||||
|
||||
|
||||
def apply_affix_on(*node_names: Any) -> Callable:
|
||||
"""Add prefix and/or suffix to all openvino names of operators defined as arguments."""
|
||||
|
||||
def decorator(func: Callable) -> Callable:
|
||||
@wraps(func)
|
||||
def wrapper(*args: Any, **kwargs: Any) -> Node:
|
||||
arg_names = getfullargspec(func).args
|
||||
arg_mapping = dict(zip(arg_names, args))
|
||||
for node_name in node_names:
|
||||
# Apply only on auto-generated nodes. Create such node and apply affixes.
|
||||
# Any Node instance supplied by the user is keeping the name as-is.
|
||||
if node_name in arg_mapping and not isinstance(arg_mapping[node_name], (Node, Output)):
|
||||
arg_mapping[node_name] = _apply_affix(as_node(arg_mapping[node_name]),
|
||||
prefix=kwargs.get("prefix", ""),
|
||||
suffix=kwargs.get("suffix", ""),
|
||||
)
|
||||
results = func(**arg_mapping, **kwargs)
|
||||
return results
|
||||
return wrapper
|
||||
return decorator
|
||||
|
||||
|
||||
def unary_op(node_factory_function: Callable) -> Callable:
|
||||
"""Convert the first input value to a Constant Node if a numeric value is detected."""
|
||||
|
||||
@wraps(node_factory_function)
|
||||
def wrapper(input_value: NodeInput, *args: Any, **kwargs: Any) -> Node:
|
||||
input_node = as_node(input_value)
|
||||
input_node = as_node(input_value, name=_get_name(**kwargs))
|
||||
node = node_factory_function(input_node, *args, **kwargs)
|
||||
node = _set_node_friendly_name(node, **kwargs)
|
||||
node = _set_node_friendly_name(node, name=_get_name(**kwargs))
|
||||
return node
|
||||
|
||||
return wrapper
|
||||
|
|
@ -73,9 +52,9 @@ def binary_op(node_factory_function: Callable) -> Callable:
|
|||
|
||||
@wraps(node_factory_function)
|
||||
def wrapper(left: NodeInput, right: NodeInput, *args: Any, **kwargs: Any) -> Node:
|
||||
left, right = as_nodes(left, right)
|
||||
left, right = as_nodes(left, right, name=_get_name(**kwargs))
|
||||
node = node_factory_function(left, right, *args, **kwargs)
|
||||
node = _set_node_friendly_name(node, **kwargs)
|
||||
node = _set_node_friendly_name(node, name=_get_name(**kwargs))
|
||||
return node
|
||||
|
||||
return wrapper
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
"""Functions related to converting between Python and numpy types and openvino types."""
|
||||
|
||||
import logging
|
||||
from typing import List, Union
|
||||
from typing import List, Union, Optional
|
||||
|
||||
import numpy as np
|
||||
|
||||
|
|
@ -145,7 +145,7 @@ def get_shape(data: NumericData) -> TensorShape:
|
|||
return []
|
||||
|
||||
|
||||
def make_constant_node(value: NumericData, dtype: Union[NumericType, Type] = None) -> Constant:
|
||||
def make_constant_node(value: NumericData, dtype: Union[NumericType, Type] = None, *, name: Optional[str] = None) -> Constant:
|
||||
"""Return an openvino Constant node with the specified value."""
|
||||
ndarray = get_ndarray(value)
|
||||
if dtype is not None:
|
||||
|
|
@ -153,18 +153,23 @@ def make_constant_node(value: NumericData, dtype: Union[NumericType, Type] = Non
|
|||
else:
|
||||
element_type = get_element_type(ndarray.dtype)
|
||||
|
||||
return Constant(element_type, Shape(ndarray.shape), ndarray.flatten().tolist())
|
||||
const = Constant(element_type, Shape(ndarray.shape), ndarray.flatten().tolist())
|
||||
|
||||
if name:
|
||||
const.friendly_name = name + "/" + const.friendly_name
|
||||
|
||||
return const
|
||||
|
||||
|
||||
def as_node(input_value: NodeInput) -> Node:
|
||||
def as_node(input_value: NodeInput, name: Optional[str] = None) -> Node:
|
||||
"""Return input values as nodes. Scalars will be converted to Constant nodes."""
|
||||
if issubclass(type(input_value), Node):
|
||||
return input_value
|
||||
if issubclass(type(input_value), Output):
|
||||
return input_value
|
||||
return make_constant_node(input_value)
|
||||
return make_constant_node(input_value, name=name)
|
||||
|
||||
|
||||
def as_nodes(*input_values: NodeInput) -> List[Node]:
|
||||
def as_nodes(*input_values: NodeInput, name: Optional[str] = None) -> List[Node]:
|
||||
"""Return input values as nodes. Scalars will be converted to Constant nodes."""
|
||||
return [as_node(input_value) for input_value in input_values]
|
||||
return [as_node(input_value, name=name) for input_value in input_values]
|
||||
|
|
|
|||
|
|
@ -10,15 +10,13 @@ import openvino.runtime.opset13 as ov
|
|||
from openvino import Type
|
||||
|
||||
|
||||
@pytest.mark.parametrize("prefix_string", [
|
||||
@pytest.mark.parametrize("op_name", [
|
||||
"ABC",
|
||||
"custom_prefix_",
|
||||
"Fakeee",
|
||||
"123456",
|
||||
"FakeQuantize",
|
||||
])
|
||||
@pytest.mark.parametrize("suffix_string", [
|
||||
"XYZ",
|
||||
"_custom_suffix",
|
||||
])
|
||||
def test_affix_not_applied_on_nodes(prefix_string, suffix_string):
|
||||
def test_affix_not_applied_on_nodes(op_name):
|
||||
levels = np.int32(4)
|
||||
data_shape = [1, 2, 3, 4]
|
||||
bound_shape = []
|
||||
|
|
@ -45,12 +43,12 @@ def test_affix_not_applied_on_nodes(prefix_string, suffix_string):
|
|||
parameter_output_low,
|
||||
parameter_output_high,
|
||||
levels,
|
||||
prefix=prefix_string,
|
||||
suffix=suffix_string,
|
||||
name=op_name,
|
||||
)
|
||||
|
||||
# Check if node was created correctly
|
||||
assert model.get_type_name() == "FakeQuantize"
|
||||
assert model.get_friendly_name() == op_name
|
||||
assert model.get_output_size() == 1
|
||||
assert list(model.get_output_shape(0)) == [1, 2, 3, 4]
|
||||
|
||||
|
|
@ -61,15 +59,13 @@ def test_affix_not_applied_on_nodes(prefix_string, suffix_string):
|
|||
assert output_high_name == parameter_output_high.friendly_name
|
||||
|
||||
|
||||
@pytest.mark.parametrize("prefix_string", [
|
||||
@pytest.mark.parametrize("op_name", [
|
||||
"ABC",
|
||||
"custom_prefix_",
|
||||
"Fakeee",
|
||||
"123456",
|
||||
"FakeQuantize",
|
||||
])
|
||||
@pytest.mark.parametrize("suffix_string", [
|
||||
"XYZ",
|
||||
"_custom_suffix",
|
||||
])
|
||||
def test_affix_not_applied_on_output(prefix_string, suffix_string):
|
||||
def test_affix_not_applied_on_output(op_name):
|
||||
levels = np.int32(4)
|
||||
data_shape = [1, 2, 3, 4]
|
||||
bound_shape = []
|
||||
|
|
@ -100,12 +96,12 @@ def test_affix_not_applied_on_output(prefix_string, suffix_string):
|
|||
parameter_output_low,
|
||||
parameter_output_high,
|
||||
levels,
|
||||
prefix=prefix_string,
|
||||
suffix=suffix_string,
|
||||
name=op_name,
|
||||
)
|
||||
|
||||
# Check if node was created correctly
|
||||
assert model.get_type_name() == "FakeQuantize"
|
||||
assert model.get_friendly_name() == op_name
|
||||
assert model.get_output_size() == 1
|
||||
assert list(model.get_output_shape(0)) == [1, 2, 3, 4]
|
||||
|
||||
|
|
@ -116,17 +112,13 @@ def test_affix_not_applied_on_output(prefix_string, suffix_string):
|
|||
assert output_high_name == parameter_output_high.friendly_name
|
||||
|
||||
|
||||
@pytest.mark.parametrize("prefix_string", [
|
||||
"",
|
||||
@pytest.mark.parametrize("op_name", [
|
||||
"ABC",
|
||||
"custom_prefix_",
|
||||
"Fakeee",
|
||||
"123456",
|
||||
"FakeQuantize",
|
||||
])
|
||||
@pytest.mark.parametrize("suffix_string", [
|
||||
"",
|
||||
"XYZ",
|
||||
"_custom_suffix",
|
||||
])
|
||||
def test_fake_quantize_affix(prefix_string, suffix_string):
|
||||
def test_fake_quantize_prefix(op_name):
|
||||
levels = np.int32(4)
|
||||
data_shape = [1, 2, 3, 4]
|
||||
bound_shape = [1]
|
||||
|
|
@ -144,21 +136,15 @@ def test_fake_quantize_affix(prefix_string, suffix_string):
|
|||
d_arr,
|
||||
e_arr,
|
||||
levels,
|
||||
prefix=prefix_string,
|
||||
suffix=suffix_string,
|
||||
name=op_name,
|
||||
)
|
||||
|
||||
# Check if node was created correctly
|
||||
assert model.get_type_name() == "FakeQuantize"
|
||||
assert model.get_friendly_name() == op_name
|
||||
assert model.get_output_size() == 1
|
||||
assert list(model.get_output_shape(0)) == [1, 2, 3, 4]
|
||||
# Check that data parameter and node itself do not change:
|
||||
if prefix_string != "":
|
||||
assert prefix_string not in model.friendly_name
|
||||
if suffix_string != "":
|
||||
assert suffix_string not in model.friendly_name
|
||||
# Check that other parameters change:
|
||||
for node_input in model.inputs():
|
||||
generated_node = node_input.get_source_output().get_node()
|
||||
assert prefix_string in generated_node.friendly_name
|
||||
assert suffix_string in generated_node.friendly_name
|
||||
assert op_name + "/" in generated_node.friendly_name
|
||||
|
|
|
|||
Loading…
Reference in New Issue