From 8e103bcf39f2fd7847c6a58e6ee7027fa389355d Mon Sep 17 00:00:00 2001 From: Shuangji Yang Date: Fri, 11 Aug 2023 02:25:41 +0800 Subject: [PATCH 1/4] fix bug on conversion of gather to sequeeze (#19012) Co-authored-by: Ivan Tikhonov --- .../common_optimizations/nop_elimination.cpp | 4 ++- .../common_optimizations/nop_elimination.cpp | 28 +++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/src/common/transformations/src/transformations/common_optimizations/nop_elimination.cpp b/src/common/transformations/src/transformations/common_optimizations/nop_elimination.cpp index 0806a8bc55ee7c..1293e38fe31066 100644 --- a/src/common/transformations/src/transformations/common_optimizations/nop_elimination.cpp +++ b/src/common/transformations/src/transformations/common_optimizations/nop_elimination.cpp @@ -66,8 +66,10 @@ static bool simplify_gather(shared_ptr node) { if (!constant_indices) return false; // case_3: if input_shape is (1,3,5,5) and axis = 0, indices = 0, then gather is just a Squeeze + const auto constant_indices_size = constant_indices->get_output_shape(0).size(); const auto const_indices = constant_indices->cast_vector(); - if (data.get_shape()[axis] == 1 && const_indices.size() == 1 && const_indices[0] == 0) { + if (data.get_shape()[axis] == 1 && (constant_indices_size == 0 || constant_indices_size == 1) && + const_indices[0] == 0) { auto squeeze = std::make_shared(gather->input_value(0), gather->input_value(2)); squeeze->set_friendly_name(gather->get_friendly_name()); ov::copy_runtime_info(gather, squeeze); diff --git a/src/common/transformations/tests/common_optimizations/nop_elimination.cpp b/src/common/transformations/tests/common_optimizations/nop_elimination.cpp index 3f234a37b1925a..c2cf6b37e05840 100644 --- a/src/common/transformations/tests/common_optimizations/nop_elimination.cpp +++ b/src/common/transformations/tests/common_optimizations/nop_elimination.cpp @@ -1339,6 +1339,34 @@ TEST(nop_elimination, gather_to_squeeze) { run_and_check(func_axis_3); } +TEST(nop_elimination, not_gather_to_squeeze_with_vector_indices) { + auto generate_func = [](int64_t gather_axis) { + ov::Shape shape{3, 3, 4, 4}; + shape[gather_axis] = 1; + auto arg = std::make_shared(element::f32, shape); + auto indices = op::Constant::create(element::i64, Shape{1, 1}, vector{0}); + auto axis = op::Constant::create(element::i64, Shape{}, vector{gather_axis}); + auto gather = std::make_shared(arg, indices, axis); + return std::make_shared(NodeVector{gather}, ParameterVector{arg}); + }; + + auto func_axis_0 = generate_func(0); + auto func_axis_1 = generate_func(1); + auto func_axis_2 = generate_func(2); + auto func_axis_3 = generate_func(3); + pass::Manager pass_manager; + pass_manager.register_pass(); + auto run_and_check = [&](std::shared_ptr& func) { + pass_manager.run_passes(func); + EXPECT_EQ(count_ops_of_type(func), 1); + EXPECT_EQ(count_ops_of_type(func), 0); + }; + run_and_check(func_axis_0); + run_and_check(func_axis_1); + run_and_check(func_axis_2); + run_and_check(func_axis_3); +} + TEST_F(TransformationTestsF, Nopv1Broadcast) { { auto data = std::make_shared(element::f32, PartialShape{-1, -1, -1, -1}); From 726abefbaa619c55a37445a506b6478abeddee3d Mon Sep 17 00:00:00 2001 From: Anastasiia Pnevskaia Date: Thu, 10 Aug 2023 21:14:13 +0200 Subject: [PATCH 2/4] Update reminder message in MO and OVC. (#19132) * Updated reminder. * Updated reminder. --- tools/mo/openvino/tools/mo/utils/get_ov_update_message.py | 4 ++-- tools/ovc/openvino/tools/ovc/get_ov_update_message.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tools/mo/openvino/tools/mo/utils/get_ov_update_message.py b/tools/mo/openvino/tools/mo/utils/get_ov_update_message.py index ca767b1c3f05f8..0bb1ac74bd2c15 100644 --- a/tools/mo/openvino/tools/mo/utils/get_ov_update_message.py +++ b/tools/mo/openvino/tools/mo/utils/get_ov_update_message.py @@ -9,10 +9,10 @@ def get_ov_update_message(): - expected_update_date = datetime.date(year=2023, month=12, day=1) + expected_update_date = datetime.date(year=2024, month=12, day=1) current_date = datetime.date.today() - link = 'https://software.intel.com/content/www/us/en/develop/tools/openvino-toolkit/download.html?cid=other&source=prod&campid=ww_2023_bu_IOTG_OpenVINO-2023-0&content=upg_all&medium=organic' + link = 'https://software.intel.com/content/www/us/en/develop/tools/openvino-toolkit/download.html?cid=other&source=prod&campid=ww_2023_bu_IOTG_OpenVINO-2023-1&content=upg_all&medium=organic' return msg_fmt.format(link) if current_date >= expected_update_date else None diff --git a/tools/ovc/openvino/tools/ovc/get_ov_update_message.py b/tools/ovc/openvino/tools/ovc/get_ov_update_message.py index eb94f4e5f7e3b2..fe3f296f87ca85 100644 --- a/tools/ovc/openvino/tools/ovc/get_ov_update_message.py +++ b/tools/ovc/openvino/tools/ovc/get_ov_update_message.py @@ -8,10 +8,10 @@ def get_ov_update_message(): - expected_update_date = datetime.date(year=2023, month=12, day=1) + expected_update_date = datetime.date(year=2024, month=12, day=1) current_date = datetime.date.today() - link = 'https://software.intel.com/content/www/us/en/develop/tools/openvino-toolkit/download.html?cid=other&source=prod&campid=ww_2023_bu_IOTG_OpenVINO-2023-0&content=upg_all&medium=organic' + link = 'https://software.intel.com/content/www/us/en/develop/tools/openvino-toolkit/download.html?cid=other&source=prod&campid=ww_2023_bu_IOTG_OpenVINO-2023-1&content=upg_all&medium=organic' return msg_fmt.format(link) if current_date >= expected_update_date else None From 5f71679fb9ec77a9b26ee965c0bdb55ef938937c Mon Sep 17 00:00:00 2001 From: Maxim Vafin Date: Thu, 10 Aug 2023 21:28:38 +0200 Subject: [PATCH 3/4] [PT FE] Use weight share switch in frontend (#18993) * [PT FE] Use weight share switch in frontend * Return static for function * Update src/bindings/python/src/openvino/frontend/pytorch/ts_decoder.py * Fix issue with quantized constants * Add tests for shared --- .../openvino/frontend/pytorch/ts_decoder.py | 42 ++++++++------ .../src/openvino/frontend/pytorch/utils.py | 38 ++++++------ .../test_mo_convert_pytorch.py | 58 +++++++++++++++++++ .../mo/moc_frontend/pytorch_frontend_utils.py | 2 +- .../moc_frontend/pytorch_frontend_utils.py | 2 +- 5 files changed, 105 insertions(+), 37 deletions(-) diff --git a/src/bindings/python/src/openvino/frontend/pytorch/ts_decoder.py b/src/bindings/python/src/openvino/frontend/pytorch/ts_decoder.py index e07a23dc5cccc1..1090dce0163ced 100644 --- a/src/bindings/python/src/openvino/frontend/pytorch/ts_decoder.py +++ b/src/bindings/python/src/openvino/frontend/pytorch/ts_decoder.py @@ -7,7 +7,7 @@ from openvino.frontend.pytorch.py_pytorch_frontend import _FrontEndPytorchDecoder as Decoder from openvino.frontend.pytorch.py_pytorch_frontend import _Type as DecoderType from openvino.runtime import op, PartialShape, Type as OVType, OVAny -from openvino.frontend.pytorch.utils import ivalue_to_constant, get_value_from_getattr, pt_to_ov_type_map +from openvino.frontend.pytorch.utils import ivalue_to_constant, get_value_from_getattr, pt_to_ov_type_map, torch_tensor_to_ov_const from openvino.runtime import opset11 as ops import typing @@ -29,11 +29,12 @@ def forward(self, {input_sign}): class TorchScriptPythonDecoder (Decoder): - def __init__(self, pt_module, graph_element=None, example_input=None, alias_db=None): + def __init__(self, pt_module, graph_element=None, example_input=None, alias_db=None, shared_memory=True): Decoder.__init__(self) # We store every decoder created by this decoder so that all them are not deleted until the first decoder is deleted self.m_decoders = [] self._input_signature = None + self._shared_memory = shared_memory if graph_element is None: try: pt_module = self._get_scripted_model(pt_module, example_input) @@ -43,10 +44,9 @@ def __init__(self, pt_module, graph_element=None, example_input=None, alias_db=N help_msg = "" else: msg = "scripting" - help_msg = "Tracing sometimes provide better results, " - "please provide valid 'example_input' argument. " + help_msg = "\nTracing sometimes provide better results, please provide valid 'example_input' argument. " raise RuntimeError( - f"Couldn't get TorchScript module by {msg}. {help_msg}" + f"Couldn't get TorchScript module by {msg}. With exception:\n{e}\n {help_msg}" "You can also provide TorchScript module that you obtained" " yourself, please refer to PyTorch documentation: " "https://pytorch.org/tutorials/beginner/Intro_to_TorchScript_tutorial.html.") @@ -160,8 +160,11 @@ def prepare_example_inputs_and_model(inputs, input_params, model): except Exception: try: scripted = torch.jit.script(pt_module) - except Exception: - scripted = torch.jit.trace(pt_module, **input_parameters, strict=False) + except Exception as se: + try: + scripted = torch.jit.trace(pt_module, **input_parameters, strict=False) + except Exception as te: + raise f"Tracing failed with exception {te}\nScripting failed with exception: {se}" skip_freeze = False for n in scripted.inlined_graph.nodes(): # TODO: switch off freezing for all traced models @@ -283,7 +286,7 @@ def get_subgraph_size(self) -> int: def visit_subgraph(self, node_visitor) -> None: # make sure topological order is satisfied for node in self.graph_element.nodes(): - decoder = TorchScriptPythonDecoder(self.pt_module, node, alias_db=self.alias_db) + decoder = TorchScriptPythonDecoder(self.pt_module, node, alias_db=self.alias_db, shared_memory=self._shared_memory) self.m_decoders.append(decoder) node_visitor(decoder) @@ -299,7 +302,7 @@ def get_subgraphs(self) -> list: return list(self.graph_element.blocks()) def get_subgraph_decoder(self, index: int): - decoder = TorchScriptPythonDecoder(self.pt_module, self.get_subgraphs()[index], alias_db=self.alias_db) + decoder = TorchScriptPythonDecoder(self.pt_module, self.get_subgraphs()[index], alias_db=self.alias_db, shared_memory=self._shared_memory) self.m_decoders.append(decoder) return decoder @@ -336,7 +339,7 @@ def mark_node(self, node): return node @staticmethod - def convert_quantized_tensor(qtensor: torch.Tensor): + def convert_quantized_tensor(qtensor: torch.Tensor, shared_memory: bool): # need to represent as Constant(u8) -> Convert(f32) -> Subtract(zero_point) -> Multiply (scale) qscheme = qtensor.qscheme() # torch.per_channel_affine (per_tensor) if qscheme == torch.per_channel_affine: @@ -349,8 +352,8 @@ def convert_quantized_tensor(qtensor: torch.Tensor): new_shape[axis] = -1 zero_point_bc = np.reshape(zero_point, new_shape) scale_bc = np.reshape(scale, new_shape) - - int8_const = op.Constant(int8_tensor.numpy()) + + int8_const = torch_tensor_to_ov_const(int8_tensor, shared_memory=shared_memory) convert = ops.convert(int8_const, np.float32) sub = ops.subtract(convert, zero_point_bc) return ops.multiply(sub, scale_bc).outputs() @@ -359,7 +362,7 @@ def convert_quantized_tensor(qtensor: torch.Tensor): scale = np.float32(qtensor.q_scale()) zero_point = np.float32(qtensor.q_zero_point()) - int8_const = op.Constant(int8_tensor.numpy()) + int8_const = torch_tensor_to_ov_const(int8_tensor, shared_memory=shared_memory) convert = ops.convert(int8_const, np.float32) sub = ops.subtract(convert, zero_point) return ops.multiply(sub, scale).outputs() @@ -372,7 +375,7 @@ def try_decode_get_attr(self): # We assume this is __torch__.torch.classes.quantized.Conv2dPackedParamsBase or __torch__.torch.classes.quantized.LinearPackedParamsBase # TODO: but can be anything. Figure a better way to distinguish weight, bias = pt_value.unpack() - res = self.convert_quantized_tensor(weight) + res = self.convert_quantized_tensor(weight, self._shared_memory) if isinstance(bias, torch.Tensor): res += ivalue_to_constant(bias) else: @@ -383,12 +386,15 @@ def try_decode_get_attr(self): padding = pt_value.padding() dilation = pt_value.dilation() groups = pt_value.groups() - res += ivalue_to_constant(stride) + ivalue_to_constant(padding) + ivalue_to_constant(dilation) + ivalue_to_constant(groups) + res += ivalue_to_constant(stride, shared_memory=self._shared_memory) + res += ivalue_to_constant(padding, shared_memory=self._shared_memory) + res += ivalue_to_constant(dilation, shared_memory=self._shared_memory) + res += ivalue_to_constant(groups, shared_memory=self._shared_memory) except: pass return res elif not isinstance(pt_value, (torch.jit.ScriptModule, torch.jit.TracedModule)): - return ivalue_to_constant(pt_value) + return ivalue_to_constant(pt_value, shared_memory=self._shared_memory) else: return [] @@ -400,10 +406,10 @@ def as_constant(self): pt_value = self._raw_output(0) pt_type = pt_value.type() if isinstance(pt_type, torch.TensorType): - return ivalue_to_constant(pt_value.toIValue()) + return ivalue_to_constant(pt_value.toIValue(), shared_memory=self._shared_memory) if isinstance(pt_type, torch.ListType): return self._as_constant_list(pt_value) - return ivalue_to_constant(pt_value.toIValue()) + return ivalue_to_constant(pt_value.toIValue(), shared_memory=self._shared_memory) def as_string(self): if self.get_op_type() == "prim::Constant": diff --git a/src/bindings/python/src/openvino/frontend/pytorch/utils.py b/src/bindings/python/src/openvino/frontend/pytorch/utils.py index 7c491dc8aa23c9..0e7ffd66780c61 100644 --- a/src/bindings/python/src/openvino/frontend/pytorch/utils.py +++ b/src/bindings/python/src/openvino/frontend/pytorch/utils.py @@ -55,7 +55,26 @@ def get_type_from_py_type(value): return OVType.dynamic -def ivalue_to_constant(ivalue): +def torch_tensor_to_ov_const(torch_t: torch.Tensor, shared_memory=True): + torch_t = torch_t.to(memory_format=torch.contiguous_format) + if torch_t.dtype == torch.bfloat16: + # reinterpret bfloat16 data as float16 to allow conversion to numpy + torch_t = torch_t.view(torch.float16) + narr = torch_t.numpy(force=True) + if not narr.flags['C_CONTIGUOUS']: + narr = np.ascontiguousarray(narr) + # TODO: this tensor doesn't share memory with initial tensor + tensor = Tensor(narr, torch_t.shape, OVType.bf16) + ov_const = op.Constant(tensor, shared_memory=shared_memory) + else: + narr = torch_t.numpy(force=True) + if not narr.flags['C_CONTIGUOUS']: + narr = np.ascontiguousarray(narr) + ov_const = op.Constant(narr, shared_memory=shared_memory) + return ov_const + + +def ivalue_to_constant(ivalue, shared_memory=True): ov_type = get_type_from_py_type(ivalue) if ov_type.is_static(): return op.Constant(ov_type, Shape([]), [ivalue]).outputs() @@ -67,22 +86,7 @@ def ivalue_to_constant(ivalue): return op.Constant(ov_type, Shape([len(ivalue)]), ivalue).outputs() if isinstance(ivalue, torch.Tensor): - ivalue = ivalue.to(memory_format=torch.contiguous_format) - if ivalue.dtype == torch.bfloat16: - # reinterpret bfloat16 data as float16 to allow conversion to numpy - ivalue = ivalue.view(torch.float16) - narr = ivalue.numpy(force=True) - if not narr.flags['C_CONTIGUOUS']: - narr = np.ascontiguousarray(narr) - # TODO: this tensor doesn't share memory with initial tensor - tensor = Tensor(narr, ivalue.shape, OVType.bf16) - ov_const = op.Constant(tensor, shared_memory=True) - else: - narr = ivalue.numpy(force=True) - if not narr.flags['C_CONTIGUOUS']: - narr = np.ascontiguousarray(narr) - ov_const = op.Constant(narr, shared_memory=True) - return ov_const.outputs() + return torch_tensor_to_ov_const(ivalue, shared_memory=shared_memory).outputs() return None def get_value_from_getattr(getattr_node, self_module): diff --git a/tests/layer_tests/mo_python_api_tests/test_mo_convert_pytorch.py b/tests/layer_tests/mo_python_api_tests/test_mo_convert_pytorch.py index ae48f1ce1ae7f2..307a1dcde3f271 100644 --- a/tests/layer_tests/mo_python_api_tests/test_mo_convert_pytorch.py +++ b/tests/layer_tests/mo_python_api_tests/test_mo_convert_pytorch.py @@ -1011,6 +1011,64 @@ def test_mo_import_from_memory(self, create_model, ie_device, precision, ir_vers self._test_by_ref_graph(temp_dir, test_params, graph_ref, compare_tensor_names=False) + @ pytest.mark.precommit + def test_sharing_memory_switched_off(self, ie_device, precision, ir_version, temp_dir): + from openvino.tools.ovc import convert_model + from openvino.runtime import Core + + class DataModel(torch.nn.Module): + def __init__(self): + super(DataModel, self).__init__() + self.data = torch.tensor([1, 2, 3, 4]) + + def forward(self, x): + return self.data, x + + data_model = DataModel() + test_input = np.array([0, 0, 0, 0]) + + # Convert model to OV + ov_model = convert_model(data_model, input=([4], Type.i32), share_weights=False) + + # Change value of variables in original model + data_model.data[0] *= 2 + + # Check model inference + core = Core() + cmp_model = core.compile_model(ov_model, ie_device) + ov_infer1 = cmp_model(test_input) + + assert np.array_equal(ov_infer1[0], [1, 2, 3, 4]) + + @ pytest.mark.precommit + def test_sharing_memory_switched_on(self, ie_device, precision, ir_version, temp_dir): + from openvino.tools.ovc import convert_model + from openvino.runtime import Core + + class DataModel(torch.nn.Module): + def __init__(self): + super(DataModel, self).__init__() + self.data = torch.tensor([1, 2, 3, 4]) + + def forward(self, x): + return self.data, x + + data_model = DataModel() + test_input = np.array([0, 0, 0, 0]) + + # Convert model to OV + ov_model = convert_model(data_model, input=([4], Type.i32), share_weights=True) + + # Change value of variables in original model + data_model.data[0] *= 2 + + # Check model inference + core = Core() + cmp_model = core.compile_model(ov_model, ie_device) + ov_infer1 = cmp_model(test_input) + + assert np.array_equal(ov_infer1[0], [2, 2, 3, 4]) + def create_pt_model_with_custom_op(): # diff --git a/tools/mo/openvino/tools/mo/moc_frontend/pytorch_frontend_utils.py b/tools/mo/openvino/tools/mo/moc_frontend/pytorch_frontend_utils.py index 7cb46d92300640..8c489dc412770c 100644 --- a/tools/mo/openvino/tools/mo/moc_frontend/pytorch_frontend_utils.py +++ b/tools/mo/openvino/tools/mo/moc_frontend/pytorch_frontend_utils.py @@ -31,7 +31,7 @@ def get_pytorch_decoder(model, input_shape, example_inputs, args): except: pass inputs = prepare_torch_inputs(example_inputs) - decoder = TorchScriptPythonDecoder(model, example_input=inputs) + decoder = TorchScriptPythonDecoder(model, example_input=inputs, shared_memory=args.get("share_weights", True)) args['input_model'] = decoder args["framework"] = "pytorch" args["example_input"] = inputs diff --git a/tools/ovc/openvino/tools/ovc/moc_frontend/pytorch_frontend_utils.py b/tools/ovc/openvino/tools/ovc/moc_frontend/pytorch_frontend_utils.py index 89c5ce11ae520b..da2abdb21f3b55 100644 --- a/tools/ovc/openvino/tools/ovc/moc_frontend/pytorch_frontend_utils.py +++ b/tools/ovc/openvino/tools/ovc/moc_frontend/pytorch_frontend_utils.py @@ -31,7 +31,7 @@ def get_pytorch_decoder(model, example_inputs, args): except: pass inputs = prepare_torch_inputs(example_inputs) - decoder = TorchScriptPythonDecoder(model, example_input=inputs) + decoder = TorchScriptPythonDecoder(model, example_input=inputs, shared_memory=args.get("share_weights", True)) args['input_model'] = decoder args["example_input"] = inputs From 59872ee072a297120edf240dc50a0c904f51a9d0 Mon Sep 17 00:00:00 2001 From: Anastasiia Pnevskaia Date: Thu, 10 Aug 2023 21:47:27 +0200 Subject: [PATCH 4/4] Removed InputCutInfo, disabled input cut in OVC. (#18927) * Fixed output_model logic. * Removed InputCutInfo, disabled input cut in ovc. * Disabled output cut, added tests for setting shapes or types for not all inputs. * Returned support of numpy type. * Separated MO and OVC python API tests. * Small corrections. * Added output dir test, exceptions test. * Tests fixed. * Corrected extension param description. * Corrected input description, minor code corrections. --- .ci/azure/linux.yml | 9 + src/bindings/python/src/openvino/__init__.py | 2 +- .../common/mo_convert_test_class.py | 2 +- .../test_mo_convert_complex_params.py | 63 +- .../test_mo_convert_extensions.py | 5 +- .../test_mo_convert_onnx.py | 6 +- .../test_mo_convert_paddle.py | 1 + .../test_mo_convert_pytorch.py | 121 +- .../mo_python_api_tests/test_mo_convert_tf.py | 70 +- .../mo_python_api_tests/test_mo_help.py | 11 +- .../ovc_python_api_tests/conftest.py | 13 + .../test_complex_params.py | 167 +++ .../ovc_python_api_tests/test_extensions.py | 121 ++ .../ovc_python_api_tests/test_onnx.py | 83 ++ .../test_ovc_cli_tool.py | 15 + .../test_ovc_tool_help.py | 30 + .../ovc_python_api_tests/test_paddle.py | 103 ++ .../ovc_python_api_tests/test_pytorch.py | 1073 +++++++++++++++++ .../ovc_python_api_tests/test_tf.py | 859 +++++++++++++ tools/benchmark_tool/openvino/__init__.py | 2 +- tools/mo/openvino/__init__.py | 2 +- tools/openvino_dev/src/openvino/__init__.py | 2 +- tools/ovc/openvino/__init__.py | 2 +- tools/ovc/openvino/tools/ovc/__init__.py | 2 +- tools/ovc/openvino/tools/ovc/cli_parser.py | 220 ++-- tools/ovc/openvino/tools/ovc/convert.py | 48 +- tools/ovc/openvino/tools/ovc/convert_impl.py | 36 +- tools/ovc/openvino/tools/ovc/help.py | 73 +- tools/ovc/openvino/tools/ovc/main.py | 6 +- .../tools/ovc/moc_frontend/check_config.py | 5 - .../tools/ovc/moc_frontend/extractor.py | 26 +- .../tools/ovc/moc_frontend/pipeline.py | 34 +- .../moc_tf_fe/check_info_messages_test.py | 1 - .../moc_tf_fe/conversion_basic_models_test.py | 194 +-- .../ovc/convert/import_from_mo_test.py | 4 +- .../unit_tests/ovc/convert/meta_data_test.py | 2 +- .../ovc/convert/meta_data_test_actual.py | 2 +- .../unit_tests/ovc/utils/cli_parser_test.py | 302 ++--- tools/pot/openvino/__init__.py | 2 +- 39 files changed, 2883 insertions(+), 836 deletions(-) create mode 100644 tests/layer_tests/ovc_python_api_tests/conftest.py create mode 100644 tests/layer_tests/ovc_python_api_tests/test_complex_params.py create mode 100644 tests/layer_tests/ovc_python_api_tests/test_extensions.py create mode 100644 tests/layer_tests/ovc_python_api_tests/test_onnx.py rename tests/layer_tests/{mo_python_api_tests => ovc_python_api_tests}/test_ovc_cli_tool.py (85%) create mode 100644 tests/layer_tests/ovc_python_api_tests/test_ovc_tool_help.py create mode 100644 tests/layer_tests/ovc_python_api_tests/test_paddle.py create mode 100644 tests/layer_tests/ovc_python_api_tests/test_pytorch.py create mode 100644 tests/layer_tests/ovc_python_api_tests/test_tf.py diff --git a/.ci/azure/linux.yml b/.ci/azure/linux.yml index 7d7454f8794849..f07f048001f475 100644 --- a/.ci/azure/linux.yml +++ b/.ci/azure/linux.yml @@ -551,6 +551,15 @@ jobs: TEST_DEVICE: CPU displayName: 'TensorFlow Lite Layer Tests - TFL FE' + - script: | + set -e + python3 -m pip install -r $(LAYER_TESTS_DIR)/requirements.txt + $(RUN_PREFIX) python3 -m pytest $(LAYER_TESTS_DIR)/ovc_python_api_tests/ --junitxml=./TEST-test_ovc_convert.xmlTEST + env: + PYTHONPATH: $(LAYER_TESTS_DIR) + TEST_DEVICE: CPU + displayName: 'OVC Python API Tests' + - script: | set -e python3 -m pip install -r $(LAYER_TESTS_DIR)/requirements.txt diff --git a/src/bindings/python/src/openvino/__init__.py b/src/bindings/python/src/openvino/__init__.py index 8f0113d5bcaf6c..90552e0befed68 100644 --- a/src/bindings/python/src/openvino/__init__.py +++ b/src/bindings/python/src/openvino/__init__.py @@ -57,6 +57,6 @@ # Tools try: # Model Conversion API - ovc should reside in the main namespace - from openvino.tools.ovc import convert_model, InputCutInfo + from openvino.tools.ovc import convert_model except ImportError: pass diff --git a/tests/layer_tests/common/mo_convert_test_class.py b/tests/layer_tests/common/mo_convert_test_class.py index 8b251c9399e51d..ccd0c389d6739f 100644 --- a/tests/layer_tests/common/mo_convert_test_class.py +++ b/tests/layer_tests/common/mo_convert_test_class.py @@ -53,7 +53,7 @@ def _test(self, temp_dir, test_params, ref_params): ir_ref = core.read_model(Path(temp_dir, 'model_ref.xml')) flag, msg = compare_functions(ir_test, ir_ref) - assert flag, '\n'.join(msg) + assert flag, msg def _test_by_ref_graph(self, temp_dir, test_params, ref_graph, compare_tensor_names=True, compare_layout=True): """ diff --git a/tests/layer_tests/mo_python_api_tests/test_mo_convert_complex_params.py b/tests/layer_tests/mo_python_api_tests/test_mo_convert_complex_params.py index 37543013a3c258..a9f04a702249cc 100644 --- a/tests/layer_tests/mo_python_api_tests/test_mo_convert_complex_params.py +++ b/tests/layer_tests/mo_python_api_tests/test_mo_convert_complex_params.py @@ -5,9 +5,8 @@ import os import pytest from openvino.runtime import Model, Layout, PartialShape, Shape, layout_helpers, Type, Dimension -from openvino.tools.ovc import InputCutInfo -from openvino.tools.mo import LayoutMap - +from openvino.tools.mo import LayoutMap, InputCutInfo +import openvino.runtime as ov from common.mo_convert_test_class import CommonMOConvertTest from common.tf_layer_test_class import save_to_pb @@ -134,11 +133,11 @@ def create_tf_param_res_model(self, tmp_dir): {'params_test': {'input_shape': [PartialShape([2, 3, 4]), [2, 3, 4], [Dimension(2), Dimension(3), Dimension(4)]], - 'input':['Input1', 'Input2', 'Relu3'], 'use_convert_model_from_mo': True, 'compress_to_fp16': True}, + 'input':['Input1', 'Input2', 'Relu3'], 'compress_to_fp16': True}, 'params_ref': {'input_shape': "[2,3,4],[2,3,4],[2,3,4]", 'input': 'Input1,Input2,Relu3'}}, {'params_test': {'input_shape': [PartialShape([Dimension(), Dimension(1, 3), Dimension(4, -1), Dimension(-1, 5)]), [Dimension(), Dimension(1, 3), 4, Dimension(-1, 5)], - [Dimension(), 3, Dimension(4, -1), Dimension(-1, 5)]], 'use_convert_model_from_mo': True, + [Dimension(), 3, Dimension(4, -1), Dimension(-1, 5)]], 'compress_to_fp16': True, 'input':['Input1', 'Input2', 'Relu3']}, 'params_ref': {'input_shape': "[?,1..3,4..,..5],[?,1..3,4,..5],[?,3,4..,..5]", 'input': 'Input1,Input2,Relu3'}}, @@ -153,32 +152,27 @@ def create_tf_param_res_model(self, tmp_dir): {'params_test': {'output': ["Sigmoid_0", "Sigmoid_2"]}, 'params_ref': {'output': "Sigmoid_0,Sigmoid_2"}}, {'params_test': {'mean_values': {'Input1': [0.5,1.3,0.67], 'Input2':[4.2, 6.7, 3.15], 'Input3':[0.757, 4.6, 7.3]}, - 'use_convert_model_from_mo': True, 'compress_to_fp16': True}, + 'compress_to_fp16': True}, 'params_ref': {'mean_values': "Input1[0.5,1.3,0.67],Input2[4.2,6.7,3.15],Input3[0.757,4.6,7.3]"}}, {'params_test': { - 'mean_values': [[0.5, 1.3, 0.67], [4.2, 6.7, 3.15], [0.757, 4.6, 7.3]], 'use_convert_model_from_mo': True, - 'compress_to_fp16': True}, + 'mean_values': [[0.5, 1.3, 0.67], [4.2, 6.7, 3.15], [0.757, 4.6, 7.3]], 'compress_to_fp16': True}, 'params_ref': {'mean_values': "[0.5,1.3,0.67],[4.2,6.7,3.15],[0.757,4.6,7.3]"}}, {'params_test': {'scale_values': {'Input1': [0.5,1.3,0.67], 'Input2':[4.2, 6.7, 3.15], 'Input3':[0.757, 4.6, 7.3]}, - 'use_convert_model_from_mo': True, 'compress_to_fp16': True}, + 'compress_to_fp16': True}, 'params_ref': {'scale_values': "Input1[0.5,1.3,0.67],Input2[4.2,6.7,3.15],Input3[0.757,4.6,7.3]"}}, {'params_test': { - 'scale_values': [[0.5, 1.3, 0.67], [4.2, 6.7, 3.15], [0.757, 4.6, 7.3]], 'use_convert_model_from_mo': True, - 'compress_to_fp16': True}, + 'scale_values': [[0.5, 1.3, 0.67], [4.2, 6.7, 3.15], [0.757, 4.6, 7.3]], 'compress_to_fp16': True}, 'params_ref': {'scale_values': "[0.5,1.3,0.67],[4.2,6.7,3.15],[0.757,4.6,7.3]"}}, {'params_test': { - 'source_layout': {'Input1': Layout("nchw"), 'Input2': "nchw", 'Input3': "nc??"}, 'use_convert_model_from_mo': True, - 'compress_to_fp16': True}, + 'source_layout': {'Input1': Layout("nchw"), 'Input2': "nchw", 'Input3': "nc??"}, 'compress_to_fp16': True}, 'params_ref': {'source_layout': "Input1(nchw),Input2(nchw),Input3(nc??)"}}, {'params_test': { - 'target_layout': {'Input1': Layout("nhwc"), 'Input2': "nhwc", 'Input3': "n??c"}, 'use_convert_model_from_mo': True, - 'compress_to_fp16': True}, + 'target_layout': {'Input1': Layout("nhwc"), 'Input2': "nhwc", 'Input3': "n??c"}, 'compress_to_fp16': True}, 'params_ref': {'target_layout': "Input1(nhwc),Input2(nhwc),Input3(n??c)"}}, {'params_test': { 'layout': {'Input1': LayoutMap(source_layout=Layout("nchw"), target_layout="nhwc"), 'Input2': LayoutMap(source_layout="nc??", target_layout=Layout("n??c")), - 'Input3': LayoutMap(source_layout="abcd", target_layout="acdb")}, 'use_convert_model_from_mo': True, - 'compress_to_fp16': True}, + 'Input3': LayoutMap(source_layout="abcd", target_layout="acdb")}, 'compress_to_fp16': True}, 'params_ref': {'layout': "Input1(nchw->nhwc),Input2(nc??->n??c),Input3(abcd->acdb)"}}, {'params_test': {'input': [PartialShape([2, 3, 4]), [2, 3, 4], [Dimension(2), Dimension(3), Dimension(4)]]}, 'params_ref': {'input_shape': "[2,3,4],[2,3,4],[2,3,4]", 'input': 'Input1,Input2,Input3'}}, @@ -198,6 +192,7 @@ def test_mo_convert_tf_model(self, params, ie_device, precision, ir_version, test_params = params['params_test'] ref_params = params['params_ref'] + test_params.update({'use_convert_model_from_mo': True}) test_params.update({'input_model': tf_net_path}) ref_params.update({'input_model': tf_net_path}) self._test(temp_dir, test_params, ref_params) @@ -241,16 +236,13 @@ def test_mo_convert_tf_model_no_concat(self, params, ie_device, precision, ir_ve # By default compress_to_fp16 in Python API is False but for mo cli tool (used for params_ref) it's True. # compress_to_fp16 should be specified explicitly either in 'param_test' or 'params_ref' (or in both) # Check all args combinations. - {'params_test': {'input_shape': PartialShape([2, 3, 4]), 'use_convert_model_from_mo': True, - 'compress_to_fp16': True}, + {'params_test': {'input_shape': PartialShape([2, 3, 4]), 'compress_to_fp16': True}, 'params_ref': {'input_shape': "[2,3,4]"}}, - {'params_test': {'input_shape': PartialShape([2, 3, 4]), 'use_convert_model_from_mo': True}, + {'params_test': {'input_shape': PartialShape([2, 3, 4])}, 'params_ref': {'input_shape': "[2,3,4]", 'compress_to_fp16': False}}, - {'params_test': {'input_shape': PartialShape([2, 3, 4]), 'use_convert_model_from_mo': True, - 'compress_to_fp16': True}, + {'params_test': {'input_shape': PartialShape([2, 3, 4]), 'compress_to_fp16': True}, 'params_ref': {'input_shape': "[2,3,4]", 'compress_to_fp16': True}}, - {'params_test': {'input_shape': PartialShape([2, 3, 4]), 'use_convert_model_from_mo': True, - 'compress_to_fp16': False}, + {'params_test': {'input_shape': PartialShape([2, 3, 4]), 'compress_to_fp16': False}, 'params_ref': {'input_shape': "[2,3,4]", 'compress_to_fp16': False}}, # ovc.convert_model with save_model are used, by default save_model compresses to fp16 same as cli tool. @@ -266,8 +258,7 @@ def test_mo_convert_tf_model_no_concat(self, params, ie_device, precision, ir_ve {'params_test': {'input': InputCutInfo("Relu", [3, 2], Type(np.int32), [1, 2, 3, 4, 5, 6]), 'compress_to_fp16': False}, 'params_ref': {'input': "Relu[3 2]{i32}->[1 2 3 4 5 6]", 'compress_to_fp16': False}}, - {'params_test': {'input_shape': [Dimension(), Dimension(1, 3), 4, Dimension(-1, 5)], 'use_convert_model_from_mo': True, - 'compress_to_fp16': True}, + {'params_test': {'input_shape': [Dimension(), Dimension(1, 3), 4, Dimension(-1, 5)], 'compress_to_fp16': True}, 'params_ref': {'input_shape': "[?,1..3,4,..5]"}}, {'params_test': {'input': InputCutInfo("Relu", [3, 2], Type(np.int32), [1, 2, 3, 4, 5, 6])}, 'params_ref': {'input': "Relu[3 2]{i32}->[1 2 3 4 5 6]"}}, @@ -279,18 +270,17 @@ def test_mo_convert_tf_model_no_concat(self, params, ie_device, precision, ir_ve 'params_ref': {'input': "Relu[3 2]"}}, {'params_test': {'input': ("Relu")}, 'params_ref': {'input': "Relu"}}, - {'params_test': {'mean_values': [0.5, 1.3, 0.67], 'use_convert_model_from_mo': True, 'compress_to_fp16': True}, + {'params_test': {'mean_values': [0.5, 1.3, 0.67], 'compress_to_fp16': True}, 'params_ref': {'mean_values': "[0.5,1.3,0.67]"}}, - {'params_test': {'scale_values': [0.5, 1.3, 0.67], 'use_convert_model_from_mo': True, 'compress_to_fp16': True}, + {'params_test': {'scale_values': [0.5, 1.3, 0.67], 'compress_to_fp16': True}, 'params_ref': {'scale_values': "[0.5,1.3,0.67]"}}, - {'params_test': {'source_layout': Layout("nchw"), 'use_convert_model_from_mo': True, 'compress_to_fp16': True}, + {'params_test': {'source_layout': Layout("nchw"), 'compress_to_fp16': True}, 'params_ref': {'source_layout': "nchw"}}, - {'params_test': {'target_layout': Layout("nchw"), 'use_convert_model_from_mo': True, 'compress_to_fp16': True}, + {'params_test': {'target_layout': Layout("nchw"), 'compress_to_fp16': True}, 'params_ref': {'target_layout': "nchw"}}, - {'params_test': {'layout': LayoutMap(source_layout=Layout("nchw"), target_layout="nhwc"), - 'use_convert_model_from_mo': True, 'compress_to_fp16': True}, + {'params_test': {'layout': LayoutMap(source_layout=Layout("nchw"), target_layout="nhwc"), 'compress_to_fp16': True}, 'params_ref': {'layout': "nchw->nhwc"}}, - {'params_test': {'layout': Layout("nchw"), 'use_convert_model_from_mo': True, 'compress_to_fp16': True}, + {'params_test': {'layout': Layout("nchw"), 'compress_to_fp16': True}, 'params_ref': {'layout': "nchw"}}, {'params_test': {'input': [3, 2]}, 'params_ref': {'input': "Input[3 2]"}}, @@ -306,13 +296,13 @@ def test_mo_convert_tf_model_no_concat(self, params, ie_device, precision, ir_ve 'params_ref': {'input': "Input[1]{i32}->[10]"}}, {'params_test': {'input': (np.int32, [1, 2, 3])}, 'params_ref': {'input': "Input[1,2,3]{i32}"}}, - {'params_test': {'input_shape': [Dimension(3, 10), 10, -1], 'use_convert_model_from_mo': True, 'compress_to_fp16': True}, + {'params_test': {'input_shape': [Dimension(3, 10), 10, -1], 'compress_to_fp16': True}, 'params_ref': {'input_shape': '[3..10,10,?]'}}, {'params_test': {'input': [Dimension(3, 10), 10, -1]}, 'params_ref': {'input': 'Input[3..10,10,?]'}}, - {'params_test': {'input': PartialShape([1, 100, 100, 3]), 'mean_values': [0.5, 1.3, 0.67], 'use_convert_model_from_mo': True, 'compress_to_fp16': True}, + {'params_test': {'input': PartialShape([1, 100, 100, 3]), 'mean_values': [0.5, 1.3, 0.67], 'compress_to_fp16': True}, 'params_ref': {'input': "Input[1,100,100,3]", 'mean_values': "[0.5,1.3,0.67]"}}, - {'params_test': {'input': [1, 100, 100, 3], 'scale_values': [0.5, 1.3, 0.67], 'use_convert_model_from_mo': True, 'compress_to_fp16': True}, + {'params_test': {'input': [1, 100, 100, 3], 'scale_values': [0.5, 1.3, 0.67], 'compress_to_fp16': True}, 'params_ref': {'input': "Input[1,100,100,3]", 'scale_values': "[0.5,1.3,0.67]"}}, ] @@ -325,6 +315,7 @@ def test_mo_convert_tf_model_single_input_output(self, params, ie_device, precis test_params = params['params_test'] ref_params = params['params_ref'] + test_params.update({'use_convert_model_from_mo': True}) test_params.update({'input_model': tf_net_path}) ref_params.update({'input_model': tf_net_path}) self._test(temp_dir, test_params, ref_params) diff --git a/tests/layer_tests/mo_python_api_tests/test_mo_convert_extensions.py b/tests/layer_tests/mo_python_api_tests/test_mo_convert_extensions.py index 5cc02f76f54416..b510104e9e2235 100644 --- a/tests/layer_tests/mo_python_api_tests/test_mo_convert_extensions.py +++ b/tests/layer_tests/mo_python_api_tests/test_mo_convert_extensions.py @@ -102,9 +102,9 @@ def create_ref_graph2(): return Model([sigmoid], [param], "test") test_data = [ - {'params_test': {'extension': create_custom_extension_leaky_relu_to_relu()}, + {'params_test': {'extensions': create_custom_extension_leaky_relu_to_relu()}, 'ref_graph': create_ref_graph1()}, - {'params_test': {'extension': [create_custom_extension_leaky_relu_to_relu(), + {'params_test': {'extensions': [create_custom_extension_leaky_relu_to_relu(), create_custom_extension_elu_to_sigmoid()]}, 'ref_graph': create_ref_graph2()} ] @@ -118,4 +118,5 @@ def test_mo_convert_extensions(self, params, ie_device, precision, ir_version, test_params = params['params_test'] test_params.update({'input_model': onnx_net_path}) + test_params.update({'use_convert_model_from_mo': True}) self._test_by_ref_graph(temp_dir, test_params, params['ref_graph']) diff --git a/tests/layer_tests/mo_python_api_tests/test_mo_convert_onnx.py b/tests/layer_tests/mo_python_api_tests/test_mo_convert_onnx.py index 58fd799a507dc9..f671ef07b3c0da 100644 --- a/tests/layer_tests/mo_python_api_tests/test_mo_convert_onnx.py +++ b/tests/layer_tests/mo_python_api_tests/test_mo_convert_onnx.py @@ -49,9 +49,8 @@ def make_graph_proto_model(): def create_ref_model(shape): param1 = ov.opset8.parameter(shape, dtype=np.float32) - slope_const = ov.opset8.constant([0.1], dtype=np.float16) - decompress_slope = ov.opset8.convert(slope_const, np.float32) - prelu = ov.opset8.prelu(param1, slope=decompress_slope) + slope_const = ov.opset8.constant([0.1], dtype=np.float32) + prelu = ov.opset8.prelu(param1, slope=slope_const) relu = ov.opset8.elu(prelu, alpha=np.float32(0.1)) parameter_list = [param1] return Model([relu], parameter_list, "test") @@ -79,5 +78,6 @@ def test_mo_convert_onnx(self, create_model, ie_device, precision, ir_version, test_params = {'input_model': fw_model} if mo_params is not None: test_params.update(mo_params) + test_params.update({'use_convert_model_from_mo': True}) self._test_by_ref_graph(temp_dir, test_params, graph_ref, compare_tensor_names=False) diff --git a/tests/layer_tests/mo_python_api_tests/test_mo_convert_paddle.py b/tests/layer_tests/mo_python_api_tests/test_mo_convert_paddle.py index de4fe954eff204..dd403facdce277 100644 --- a/tests/layer_tests/mo_python_api_tests/test_mo_convert_paddle.py +++ b/tests/layer_tests/mo_python_api_tests/test_mo_convert_paddle.py @@ -100,4 +100,5 @@ def test_mo_import_from_memory_paddle_fe(self, create_model, ie_device, precisio test_params = {'input_model': fw_model, 'use_new_frontend': True} if mo_params is not None: test_params.update(mo_params) + test_params.update({'use_convert_model_from_mo': True}) self._test_by_ref_graph(temp_dir, test_params, graph_ref, compare_tensor_names=False) diff --git a/tests/layer_tests/mo_python_api_tests/test_mo_convert_pytorch.py b/tests/layer_tests/mo_python_api_tests/test_mo_convert_pytorch.py index 307a1dcde3f271..ea29e9c94cc1cb 100644 --- a/tests/layer_tests/mo_python_api_tests/test_mo_convert_pytorch.py +++ b/tests/layer_tests/mo_python_api_tests/test_mo_convert_pytorch.py @@ -11,7 +11,7 @@ import torch import unittest from openvino.runtime import PartialShape, Dimension, Model, Type -from openvino.tools.ovc import InputCutInfo +from openvino.tools.mo import InputCutInfo from common.mo_convert_test_class import CommonMOConvertTest @@ -176,7 +176,7 @@ def create_pytorch_nn_module_with_scalar_input(tmp_dir): sample_input2 = torch.zeros(1, 3, 10, 10) sample_input = sample_input1, sample_input2 - return pt_model, ref_model, {'input': ["[]", PartialShape([-1, 3, -1, -1])], + return pt_model, ref_model, {'input_shape': [PartialShape("[]"), PartialShape([-1, 3, -1, -1])], 'example_input': sample_input} @@ -188,7 +188,7 @@ def create_pytorch_nn_module_case3(tmp_dir): sample_input2 = torch.zeros(1, 3, 10, 10) sample_input = tuple([sample_input1, sample_input2]) - return pt_model, ref_model, {'input': "[?,3,?,?],[?,3,?,?]", + return pt_model, ref_model, {'input_shape': "[?,3,?,?],[?,3,?,?]", 'example_input': sample_input} @@ -297,7 +297,8 @@ def create_pytorch_nn_module_layout_list(tmp_dir): ref_model.inputs[1].node.layout = Layout('nhwc') return pt_model, ref_model, { - 'input': [(shape, np.float32), (shape, np.float32)], 'layout': ['nchw', Layout('nhwc')], 'use_convert_model_from_mo': True + 'input': [(shape, np.float32), (shape, np.float32)], 'layout': ['nchw', Layout('nhwc')], + 'use_convert_model_from_mo': True } @@ -312,7 +313,8 @@ def create_pytorch_nn_module_layout_list_case2(tmp_dir): ref_model.inputs[1].node.layout = Layout('nhwc') return pt_model, ref_model, { - 'input': [(shape, np.float32), (shape, np.float32)], 'layout': ('nchw', Layout('nhwc')), 'use_convert_model_from_mo': True} + 'input': [(shape, np.float32), (shape, np.float32)], 'layout': ('nchw', Layout('nhwc')), + 'use_convert_model_from_mo': True} def create_pytorch_nn_module_mean_list_compression_disabled(tmp_dir): @@ -333,7 +335,8 @@ def create_pytorch_nn_module_mean_list_compression_disabled(tmp_dir): parameter_list = [param1, param2] ref_model = Model([sigm], parameter_list, "test") - return pt_model, ref_model, {'input': [(shape, np.float32), (shape, np.float32)], 'mean_values': [[0, 0, 0], [0, 0, 0]], + return pt_model, ref_model, {'input': [(shape, np.float32), (shape, np.float32)], + 'mean_values': [[0, 0, 0], [0, 0, 0]], 'compress_to_fp16': False, 'use_convert_model_from_mo': True} @@ -357,7 +360,8 @@ def create_pytorch_nn_module_mean_list_compression_default(tmp_dir): parameter_list = [param1, param2] ref_model = Model([sigm], parameter_list, "test") - return pt_model, ref_model, {'input': [(shape, np.float32), (shape, np.float32)], 'mean_values': [[0, 0, 0], [0, 0, 0]], + return pt_model, ref_model, {'input': [(shape, np.float32), (shape, np.float32)], + 'mean_values': [[0, 0, 0], [0, 0, 0]], 'use_convert_model_from_mo': True} @@ -607,7 +611,8 @@ def create_pytorch_nn_module_convert_pytorch_frontend4(tmp_dir): parameter_list = [param1, param2] ref_model = Model([sigm], parameter_list, "test") return pt_model, ref_model, { - "example_input": {"x": torch.zeros((1, 3, 10, 10), dtype=torch.float32), "y": torch.ones((1, 3, 10, 10), dtype=torch.float32)}, + "example_input": {"x": torch.zeros((1, 3, 10, 10), dtype=torch.float32), + "y": torch.ones((1, 3, 10, 10), dtype=torch.float32)}, 'input': [InputCutInfo(shape=[-1, -1, -1, -1], type="f32"), InputCutInfo(shape=[-1, -1, -1, -1], type="f32")] } @@ -626,7 +631,7 @@ def create_pytorch_jit_script_module_convert_pytorch_frontend(tmp_dir): sigm = ov.opset10.sigmoid(relu) parameter_list = [param1, param2] ref_model = Model([sigm], parameter_list, "test") - return scripted_model, ref_model, { + return scripted_model, ref_model, { "example_input": [torch.zeros((1, 3, 10, 10)), torch.ones((1, 3, 10, 10))]} @@ -645,7 +650,7 @@ def create_pytorch_jit_trace_module_convert_pytorch_frontend(tmp_dir): sigm = ov.opset10.sigmoid(relu) parameter_list = [param1, param2] ref_model = Model([sigm], parameter_list, "test") - return scripted_model, ref_model, {"example_input": example_input} + return scripted_model, ref_model, {"example_input": example_input} def create_pytorch_module_convert_pytorch_frontend_oob(tmp_dir): @@ -663,9 +668,8 @@ def forward(self, x): net = ConvModel() shape = PartialShape([-1, 3, -1, -1]) param1 = ov.opset10.parameter(shape, dtype=np.float32) - weights = ov.opset10.constant(net.weights.numpy(force=True), dtype=np.float16) - decompress_weights = ov.opset10.convert(weights, np.float32) - conv = ov.opset10.convolution(param1, decompress_weights, strides=[1, 1], + weights = ov.opset10.constant(net.weights.numpy(force=True), dtype=np.float32) + conv = ov.opset10.convolution(param1, weights, strides=[1, 1], pads_begin=[0, 0], pads_end=[0, 0], dilations=[1, 1]) parameter_list = [param1] @@ -723,17 +727,15 @@ def forward(self, x): param1 = ov.opset10.parameter(shape, dtype=np.float32) weights = ov.opset10.constant(net.weights.numpy(force=True)) cast1 = ov.opset10.convert(weights, np.float32) - sub1_const = np.float16(0.5).reshape(1, 1, 1, 1) - mul1_const = np.float16(0.02).reshape(1, 1, 1, 1) - sub1_const_decompress = ov.opset10.convert(sub1_const, np.float32) - mul1_const_decompress = ov.opset10.convert(mul1_const, np.float32) - sub1 = ov.opset10.subtract(cast1, sub1_const_decompress) - mul1 = ov.opset10.multiply(sub1, mul1_const_decompress) + sub1_const = np.float32(0.5).reshape(1, 1, 1, 1) + mul1_const = np.float32(0.02).reshape(1, 1, 1, 1) + sub1 = ov.opset10.subtract(cast1, sub1_const) + mul1 = ov.opset10.multiply(sub1, mul1_const) conv = ov.opset10.convolution(param1, mul1, strides=[1, 1], pads_begin=[0, 0], pads_end=[0, 0], dilations=[1, 1]) ref_model = Model([conv], [param1], "test") - return traced_model, ref_model, {"example_input": example_input} + return traced_model, ref_model, {"example_input": example_input} def create_pytorch_module_with_compressed_int8_constant(tmp_dir): @@ -766,17 +768,17 @@ def forward(self, x): pads_begin=[0, 0], pads_end=[0, 0], dilations=[1, 1]) ref_model = Model([conv], [param1], "test") - return traced_model, ref_model, {"example_input": example_input, "compress_to_fp16": False} + return traced_model, ref_model, {"example_input": example_input, "compress_to_fp16": False} def create_pytorch_module_with_nested_inputs(tmp_dir): class PTModel(torch.nn.Module): - - def forward(self, z:Tuple[torch.Tensor, torch.Tensor]): + + def forward(self, z: Tuple[torch.Tensor, torch.Tensor]): z1, z2 = z zeros1 = torch.zeros((1, 1)) zeros2 = torch.zeros((1, 5, 1)) - return torch.cat([z1, zeros1], 1), torch.cat([z2, zeros2], 2) + return torch.cat([z1, zeros1], 1), torch.cat([z2, zeros2], 2) net = PTModel() constant_zeros1 = ov.opset10.constant(np.zeros((1, 1), dtype=np.float32), dtype=np.float32) @@ -795,7 +797,7 @@ def forward(self, z:Tuple[torch.Tensor, torch.Tensor]): def create_pytorch_module_with_nested_inputs_compress_to_fp16_default(tmp_dir): class PTModel(torch.nn.Module): - def forward(self, z:Tuple[torch.Tensor, torch.Tensor]): + def forward(self, z: Tuple[torch.Tensor, torch.Tensor]): z1, z2 = z zeros1 = torch.zeros((1, 1)) zeros2 = torch.zeros((1, 5, 1)) @@ -818,12 +820,12 @@ def forward(self, z:Tuple[torch.Tensor, torch.Tensor]): def create_pytorch_module_with_nested_inputs2(tmp_dir): class PTModel(torch.nn.Module): - - def forward(self, x:torch.Tensor, z:Tuple[torch.Tensor, torch.Tensor]): + + def forward(self, x: torch.Tensor, z: Tuple[torch.Tensor, torch.Tensor]): z1, z2 = z zeros1 = torch.zeros((1, 1)) zeros2 = torch.zeros((1, 5, 1)) - return torch.cat([z1, zeros1], 1) + x, torch.cat([z2, zeros2], 2) + return torch.cat([z1, zeros1], 1) + x, torch.cat([z2, zeros2], 2) net = PTModel() constant_zeros1 = ov.opset10.constant(np.zeros((1, 1), dtype=np.float32), dtype=np.float32) @@ -837,18 +839,19 @@ def forward(self, x:torch.Tensor, z:Tuple[torch.Tensor, torch.Tensor]): concat2 = ov.opset10.concat([param2, constant_zeros2], 2) add = ov.opset10.add(concat1, param0) ref_model = Model([concat2, add], [param0, param1, param2], "test") - return net, ref_model, {"example_input": {"x": torch.ones((1, 10)), "z": (torch.zeros((1, 10)), torch.ones((1, 5, 5)))}, - "compress_to_fp16": False} + return net, ref_model, { + "example_input": {"x": torch.ones((1, 10)), "z": (torch.zeros((1, 10)), torch.ones((1, 5, 5)))}, + "compress_to_fp16": False} def create_pytorch_module_with_nested_inputs3(tmp_dir): class PTModel(torch.nn.Module): - - def forward(self, z:Tuple[torch.Tensor, torch.Tensor], x:torch.Tensor): + + def forward(self, z: Tuple[torch.Tensor, torch.Tensor], x: torch.Tensor): z1, z2 = z zeros1 = torch.zeros((1, 1)) zeros2 = torch.zeros((1, 5, 1)) - return torch.cat([z1, zeros1], 1) + x, torch.cat([z2, zeros2], 2) + return torch.cat([z1, zeros1], 1) + x, torch.cat([z2, zeros2], 2) net = PTModel() shape1 = PartialShape([1, -1]) @@ -862,18 +865,19 @@ def forward(self, z:Tuple[torch.Tensor, torch.Tensor], x:torch.Tensor): concat2 = ov.opset10.concat([param2, constant_zeros2], 2) add = ov.opset10.add(concat1, param3) ref_model = Model([concat2, add], [param1, param2, param3], "test") - return net, ref_model, {"example_input": {"x": torch.ones((1, 10)), "z": (torch.zeros((1, 10)), torch.ones((1, 5, 3)))}, - "compress_to_fp16": False} + return net, ref_model, { + "example_input": {"x": torch.ones((1, 10)), "z": (torch.zeros((1, 10)), torch.ones((1, 5, 3)))}, + "compress_to_fp16": False} def create_pytorch_module_with_nested_inputs4(tmp_dir): class PTModel(torch.nn.Module): - - def forward(self, x:torch.Tensor, z:Tuple[torch.Tensor, torch.Tensor], y:torch.Tensor): + + def forward(self, x: torch.Tensor, z: Tuple[torch.Tensor, torch.Tensor], y: torch.Tensor): z1, z2 = z zeros1 = torch.zeros((1, 1)) zeros2 = torch.zeros((1, 5, 1)) - return torch.cat([z1, zeros1], 1) + x, torch.cat([z2, zeros2], 2) * y + return torch.cat([z1, zeros1], 1) + x, torch.cat([z2, zeros2], 2) * y net = PTModel() constant_zeros1 = ov.opset10.constant(np.zeros((1, 1), dtype=np.float32), dtype=np.float32) @@ -889,18 +893,20 @@ def forward(self, x:torch.Tensor, z:Tuple[torch.Tensor, torch.Tensor], y:torch.T add = ov.opset10.add(concat1, param3) mul = ov.opset10.multiply(concat2, param4) ref_model = Model([mul, add], [param3, param1, param2, param4], "test") - return net, ref_model, {"example_input": {"x": torch.ones((1, 10)), "z": (torch.zeros((1, 10)), torch.ones((1, 5, 10))), "y": torch.ones((1,))}, - "compress_to_fp16": False} + return net, ref_model, { + "example_input": {"x": torch.ones((1, 10)), "z": (torch.zeros((1, 10)), torch.ones((1, 5, 10))), + "y": torch.ones((1,))}, + "compress_to_fp16": False} def create_pytorch_module_with_nested_inputs5(tmp_dir): class PTModel(torch.nn.Module): - - def forward(self, x:torch.Tensor, z:Tuple[torch.Tensor, torch.Tensor], y:torch.Tensor): + + def forward(self, x: torch.Tensor, z: Tuple[torch.Tensor, torch.Tensor], y: torch.Tensor): z1, z2 = z zeros1 = torch.zeros((1, 1)) zeros2 = torch.zeros((1, 5, 1)) - return torch.cat([z1, zeros1], 1) + x, torch.cat([z2, zeros2], 2) * y + return torch.cat([z1, zeros1], 1) + x, torch.cat([z2, zeros2], 2) * y net = PTModel() constant_zeros1 = ov.opset10.constant(np.zeros((1, 1), dtype=np.float32), dtype=np.float32) @@ -916,19 +922,20 @@ def forward(self, x:torch.Tensor, z:Tuple[torch.Tensor, torch.Tensor], y:torch.T add = ov.opset10.add(concat1, param0) mul = ov.opset10.multiply(concat2, param4) ref_model = Model([mul, add], [param0, param1, param2, param4], "test") - return net, ref_model, {"example_input": [torch.ones((1, 10)), (torch.zeros((1, 10)), torch.ones((1, 5, 10))), torch.ones((1,))], - "compress_to_fp16": False} + return net, ref_model, { + "example_input": [torch.ones((1, 10)), (torch.zeros((1, 10)), torch.ones((1, 5, 10))), torch.ones((1,))], + "compress_to_fp16": False} def create_pytorch_module_with_nested_inputs6(tmp_dir): class PTModel(torch.nn.Module): - - def forward(self, x:torch.Tensor, y:torch.Tensor=None, z:Tuple[torch.Tensor, torch.Tensor]=None): + + def forward(self, x: torch.Tensor, y: torch.Tensor = None, z: Tuple[torch.Tensor, torch.Tensor] = None): z1, z2 = z zeros1 = torch.zeros((1, 1)) zeros2 = torch.zeros((1, 5, 1)) if y is not None: - return torch.cat([z1, zeros1], 1) * y, torch.cat([z2, zeros2], 2) * y + return torch.cat([z1, zeros1], 1) * y, torch.cat([z2, zeros2], 2) * y return torch.cat([z1, zeros1], 1) + x, torch.cat([z2, zeros2], 2) net = PTModel() @@ -943,8 +950,9 @@ def forward(self, x:torch.Tensor, y:torch.Tensor=None, z:Tuple[torch.Tensor, tor concat2 = ov.opset10.concat([param2, constant_zeros2], 2) add1 = ov.opset10.add(concat1, param0) ref_model = Model([concat2, add1], [param0, param1, param2], "test") - return net, ref_model, {"example_input": {"x": torch.ones((1, 11)), "z": (torch.zeros((1, 10)), torch.ones((1, 5, 10)))}, - "compress_to_fp16": False} + return net, ref_model, { + "example_input": {"x": torch.ones((1, 11)), "z": (torch.zeros((1, 10)), torch.ones((1, 5, 10)))}, + "compress_to_fp16": False} class TestMoConvertPyTorch(CommonMOConvertTest): @@ -998,14 +1006,15 @@ class TestMoConvertPyTorch(CommonMOConvertTest): create_pytorch_module_with_nested_inputs6 ] - @ pytest.mark.parametrize("create_model", test_data) - @ pytest.mark.nightly - @ pytest.mark.precommit + @pytest.mark.parametrize("create_model", test_data) + @pytest.mark.nightly + @pytest.mark.precommit def test_mo_import_from_memory(self, create_model, ie_device, precision, ir_version, temp_dir, use_new_frontend, use_old_api): fw_model, graph_ref, mo_params = create_model(temp_dir) test_params = {'input_model': fw_model} + test_params.update({'use_convert_model_from_mo': True}) if mo_params is not None: test_params.update(mo_params) self._test_by_ref_graph(temp_dir, test_params, @@ -1089,11 +1098,11 @@ def forward(self, x): class ConvertRaises(unittest.TestCase): def test_example_inputs(self): - from openvino.tools.ovc import convert_model + from openvino.tools.mo import convert_model pytorch_model = create_pt_model_with_custom_op() # Check that mo raises error message of wrong argument. - with self.assertRaisesRegex(TypeError, ".*got an unexpected keyword argument 'example_inputs'.*"): + with self.assertRaisesRegex(AssertionError, ".*'example_inputs' argument is not recognized.*"): convert_model(pytorch_model, example_inputs=(torch.tensor(1),)) def test_failed_extension(self): @@ -1109,7 +1118,7 @@ def relu_bad(n): # Check that mo raises error message of wrong argument. with self.assertRaisesRegex(Exception, ".*Conversion is failed for: aten::relu.*"): convert_model(pt_model, input=(inp_shapes, np.float32), extensions=[ - ConversionExtension("aten::relu", relu_bad)]) + ConversionExtension("aten::relu", relu_bad)]) def test_failed_extension(self): import tempfile diff --git a/tests/layer_tests/mo_python_api_tests/test_mo_convert_tf.py b/tests/layer_tests/mo_python_api_tests/test_mo_convert_tf.py index e86a075baea62f..b7b2c85032b58c 100644 --- a/tests/layer_tests/mo_python_api_tests/test_mo_convert_tf.py +++ b/tests/layer_tests/mo_python_api_tests/test_mo_convert_tf.py @@ -377,8 +377,8 @@ def first_func(input, filter): param1 = ov.opset8.parameter(data_shape, dtype=np.float32) param2 = ov.opset8.parameter(filters_shape, dtype=np.float32) - reshape = ov.opset8.reshape(param2, np.array([1, 1, 3, 3], dtype=np.int64), True) - conv = ov.opset11.convolution(param1, reshape, strides, pads_begin, pads_end, dilations, auto_pad="same_upper") + transpose2 = ov.opset8.transpose(param2, np.array([3, 2, 0, 1], dtype=np.int64)) + conv = ov.opset11.convolution(param1, transpose2, strides, pads_begin, pads_end, dilations, auto_pad="same_upper") parameter_list = [param1, param2] model_ref = Model([conv], parameter_list, "test") @@ -577,7 +577,7 @@ def __call__(self, input1, input2): sigm = tf.nn.sigmoid(input1) + input2 return sigm * self.var1 model = LayerModel() - model_ref = two_params_function_reference_fp16_compressed([[1, 2], [1, 2]], [[5.0]]) + model_ref = two_params_function_reference([[1, 2], [1, 2]], [[5.0]]) return model, model_ref, {} @@ -656,9 +656,10 @@ def shape_of_const_fold_test(temp_dir): # Ref model param1 = ov.opset8.parameter(PartialShape([1, 4, 10, 10])) - mul_const = ov.opset8.constant([[[[4]]]], dtype=np.float16) - cast = ov.opset8.convert(mul_const, np.float32) - mul = ov.opset8.multiply(cast, param1) + shape_const = ov.opset8.constant([1, 4, 10, 10], dtype=np.int32) + reshape = ov.opset8.reshape(param1, shape_const, False) + mul_const = ov.opset8.constant([[[[4]]]], dtype=np.float32) + mul = ov.opset8.multiply(mul_const, reshape) parameter_list = [param1] model_ref = Model([mul], parameter_list, "test") @@ -781,6 +782,7 @@ def test_mo_import_from_memory_tf_fe(self, create_model, ie_device, precision, i fw_model, graph_ref, mo_params = create_model(temp_dir) test_params = {'input_model': fw_model} + test_params.update({'use_convert_model_from_mo': True}) if mo_params is not None: test_params.update(mo_params) self._test_by_ref_graph(temp_dir, test_params, graph_ref, compare_tensor_names=False) @@ -791,6 +793,7 @@ def test_unnamed_saved_model_dir(self, ie_device, precision, ir_version, temp_di saved_model_dir, graph_ref = create_tf_saved_model_dir(temp_dir) test_params = {'input_model': saved_model_dir} + test_params.update({'use_convert_model_from_mo': True}) self._test_by_ref_graph(temp_dir, test_params, graph_ref, compare_tensor_names=False) test_params = {'input_model': saved_model_dir} @@ -798,7 +801,7 @@ def test_unnamed_saved_model_dir(self, ie_device, precision, ir_version, temp_di def test_zero_copy(self, ie_device, precision, ir_version, temp_dir): import tensorflow as tf - from openvino.tools.ovc import convert_model + from openvino.tools.mo import convert_model from openvino.runtime import compile_model class LayerModel(tf.Module): def __init__(self): @@ -845,62 +848,13 @@ def __call__(self, input): assert np.array_equal(ov_infer2['Identity:0'], fw_infer2) assert np.array_equal(ov_infer2['Identity:0'], [ 0., 8., 16.]) - def test_turn_off_sharing(self, ie_device, precision, ir_version, temp_dir): - import tensorflow as tf - from openvino.tools.ovc import convert_model - from openvino.runtime import compile_model - class LayerModel(tf.Module): - def __init__(self): - super(LayerModel, self).__init__() - self.var1 = tf.Variable([7., 5., 6.], name='var1') - self.var2 = tf.Variable([5., 7., 3.], name='var2') - - - @tf.function - def sub_function(self, input): - return input * self.var1 + self.var2 - - @tf.function() - def __call__(self, input): - return self.sub_function(input) - - # Create TF model with variables - keras_model = LayerModel() - test_input = np.array(7.).astype(np.float32) - - # Convert model to OV - ov_model = convert_model(keras_model, input=[1], share_weights=False) - cmp_model = compile_model(ov_model) - - # Check model inference - ov_infer1 = cmp_model(test_input, ie_device) - fw_infer1 = keras_model(test_input).numpy() - - assert np.array_equal(ov_infer1['Identity:0'], fw_infer1) - assert np.array_equal(ov_infer1['Identity:0'], [54., 42., 45.]) - - # Change value of variables in original model - for val in keras_model.variables: - arr = val.value().__array__() - arr[0] = 0 - arr[1] = 1 - arr[2] = 2 - - # Check model inference - ov_infer2 = cmp_model(test_input) - fw_infer2 = keras_model(test_input).numpy() - - # Check model inference calculated with old constant values - assert not np.array_equal(ov_infer2['Identity:0'], fw_infer2) - assert np.array_equal(ov_infer2['Identity:0'], [54., 42., 45.]) - def test_memory_loss(self, ie_device, precision, ir_version, temp_dir): # This test checks that the memory allocated for constants # is not lost after returning the model from convert_model() method. import tensorflow as tf tf.compat.v1.reset_default_graph() - from openvino.tools.ovc import convert_model + from openvino.tools.mo import convert_model from openvino.runtime import compile_model import gc @@ -955,7 +909,7 @@ class TFConvertTest(unittest.TestCase): @pytest.mark.precommit def test_tf_function_no_signature(self): import tensorflow as tf - from openvino.tools.ovc import convert_model + from openvino.tools.mo import convert_model @tf.function() def function(x1, x2): diff --git a/tests/layer_tests/mo_python_api_tests/test_mo_help.py b/tests/layer_tests/mo_python_api_tests/test_mo_help.py index 33219093c3831e..7a836a26e1b8b9 100644 --- a/tests/layer_tests/mo_python_api_tests/test_mo_help.py +++ b/tests/layer_tests/mo_python_api_tests/test_mo_help.py @@ -5,9 +5,8 @@ import os import sys import unittest -from openvino.tools.ovc import ovc -from openvino.tools.ovc.cli_parser import get_mo_convert_params -from openvino.tools.mo.utils.cli_parser import get_mo_convert_params as legacy_mo_params +from openvino.tools.mo import mo +from openvino.tools.mo.utils.cli_parser import get_mo_convert_params from pathlib import Path from common.utils.common_utils import shell @@ -17,8 +16,8 @@ class TestSubprocessMoConvert(unittest.TestCase): def test_mo_convert(self): mo_convert_params = get_mo_convert_params() - # Test ovc tool help - mo_path = Path(ovc.__file__).parent + # Test mo tool help + mo_path = Path(mo.__file__).parent mo_runner = mo_path.joinpath('main.py').as_posix() params = [sys.executable, mo_runner, "--help"] _, mo_output, _ = shell(params) @@ -35,7 +34,7 @@ def test_mo_convert(self): params = [sys.executable, mo_help_file] _, mo_output, _ = shell(params) - legacy_params = legacy_mo_params() + legacy_params = get_mo_convert_params() for group in legacy_params: for param_name in group: assert param_name in mo_output diff --git a/tests/layer_tests/ovc_python_api_tests/conftest.py b/tests/layer_tests/ovc_python_api_tests/conftest.py new file mode 100644 index 00000000000000..7a32373605e452 --- /dev/null +++ b/tests/layer_tests/ovc_python_api_tests/conftest.py @@ -0,0 +1,13 @@ +# Copyright (C) 2018-2023 Intel Corporation +# SPDX-License-Identifier: Apache-2.0 + +import inspect + +from common.layer_test_class import get_params + + +def pytest_generate_tests(metafunc): + test_gen_attrs_names = list(inspect.signature(get_params).parameters) + params = get_params() + + metafunc.parametrize(test_gen_attrs_names, params, scope="function") diff --git a/tests/layer_tests/ovc_python_api_tests/test_complex_params.py b/tests/layer_tests/ovc_python_api_tests/test_complex_params.py new file mode 100644 index 00000000000000..228db2f2cb0356 --- /dev/null +++ b/tests/layer_tests/ovc_python_api_tests/test_complex_params.py @@ -0,0 +1,167 @@ +# Copyright (C) 2018-2023 Intel Corporation +# SPDX-License-Identifier: Apache-2.0 + +import numpy as np +import openvino.runtime as ov +import os +import pytest +import tempfile +import unittest +from openvino.runtime import Model, Layout, PartialShape, Shape, layout_helpers, Type, Dimension + +from common.mo_convert_test_class import CommonMOConvertTest +from common.tf_layer_test_class import save_to_pb + + +class TestComplexParams(CommonMOConvertTest): + @staticmethod + def create_tf_model(tmp_dir): + # + # Create Tensorflow model with multiple inputs/outputs + # + + import tensorflow as tf + + tf.compat.v1.reset_default_graph() + + with tf.compat.v1.Session() as sess: + inp1 = tf.compat.v1.placeholder(tf.float32, [1, 3, 2, 2], 'Input1') + inp2 = tf.compat.v1.placeholder(tf.float32, [1, 3, 2, 2], 'Input2') + inp3 = tf.compat.v1.placeholder(tf.float32, [1, 3, 2, 2], 'Input3') + + relu1 = tf.nn.relu(inp1, name='Relu1') + relu2 = tf.nn.relu(inp2, name='Relu2') + relu3 = tf.nn.relu(inp3, name='Relu3') + + concat = tf.concat([relu1, relu2, relu3], axis=0, name='Concat') + + outputs = tf.split(concat, 3) + outputs_list = [] + for i, output in enumerate(outputs): + outputs_list.append(tf.nn.sigmoid(output, name='Sigmoid_{}'.format(i))) + + tf.compat.v1.global_variables_initializer() + tf_net = sess.graph_def + + # save model to .pb and return path to the model + return save_to_pb(tf_net, tmp_dir) + + @staticmethod + def create_tf_model_single_input_output(tmp_dir): + # + # Create Tensorflow model with single input/output + # + + import tensorflow as tf + + tf.compat.v1.reset_default_graph() + + with tf.compat.v1.Session() as sess: + inp = tf.compat.v1.placeholder(tf.float32, [1, 3, 2, 2], 'Input') + + relu = tf.nn.relu(inp, name='Relu') + + output = tf.nn.sigmoid(relu, name='Sigmoid') + + tf.compat.v1.global_variables_initializer() + tf_net = sess.graph_def + + # save model to .pb and return path to the model + return save_to_pb(tf_net, tmp_dir) + + test_data = [ + {'params_test': {'output': ["Sigmoid_0", "Sigmoid_2"]}, + 'params_ref': {'output': "Sigmoid_0,Sigmoid_2"}}, + {'params_test': {'output': ["Sigmoid_0"]}, + 'params_ref': {'output': "Sigmoid_0"}}, + {'params_test': {'input': [PartialShape([2, 3, 4]), [2, 3, 4], [Dimension(2), Dimension(3), Dimension(4)]]}, + 'params_ref': {'input_shape': "[2,3,4],[2,3,4],[2,3,4]", 'input': 'Input1,Input2,Input3'}}, + {'params_test': {'input': [PartialShape([1, 3, -1, -1]), [1, 3, -1, -1]]}, + 'params_ref': {'input_shape': "[1,3,?,?],[1,3,?,?]", 'input': 'Input1,Input2'}}, + {'params_test': {'input': [(2, 3, 4), [2, 3, 4], (Dimension(2), Dimension(3), Dimension(4))]}, + 'params_ref': {'input_shape': "[2,3,4],[2,3,4],[2,3,4]", 'input': 'Input1,Input2,Input3'}}, + {'params_test': {'input': {"Input1": PartialShape([2, 3, 4]), "Input2": [2, 3, 4], + "Input3": [Dimension(2), Dimension(3), Dimension(4)]}}, + 'params_ref': {'input_shape': "[2,3,4],[2,3,4],[2,3,4]", 'input': 'Input1,Input2,Input3'}}, + {'params_test': {'input': {"Input2": [1, -1, -1, -1], + "Input3": [Dimension(1), Dimension(-1), Dimension(-1), Dimension(-1)]}}, + 'params_ref': {'input_shape': "[1,?,?,?],[1,?,?,?]", 'input': 'Input2,Input3'}}, + {'params_test': {'input': [np.int32, Type(np.int32), np.int32]}, + 'params_ref': {'input': 'Input1{i32},Input2{i32},Input3{i32}'}}, + {'params_test': {'input': [ov.Type.f32, ov.Type.f32]}, + 'params_ref': {'input': 'Input1{f32},Input2{f32}'}}, + {'params_test': {'input': [([1, 3, -1, -1], ov.Type.i32), ov.Type.i32, ov.Type.i32]}, + 'params_ref': {'input': 'Input1[1,3,?,?]{i32},Input2{i32},Input3{i32}'}} + ] + + @pytest.mark.parametrize("params", test_data) + @pytest.mark.nightly + def test_mo_convert_tf_model(self, params, ie_device, precision, ir_version, + temp_dir, use_new_frontend, use_old_api): + tf_net_path = self.create_tf_model(temp_dir) + + test_params = params['params_test'] + ref_params = params['params_ref'] + test_params.update({'input_model': tf_net_path}) + ref_params.update({'input_model': tf_net_path}) + self._test(temp_dir, test_params, ref_params) + + test_data = [ + {'params_test': {'input': {"Input": ([3, 2], ov.Type.i32)}}, + 'params_ref': {'input': "Input[3,2]{i32}"}}, + {'params_test': {'input': {"Input": ov.Type.i32}}, + 'params_ref': {'input': "Input{i32}"}}, + {'params_test': {'input': {"Input": [3, 2]}}, + 'params_ref': {'input': "Input[3,2]"}}, + {'params_test': {'input': (3, 2)}, + 'params_ref': {'input': "Input[3,2]"}}, + {'params_test': {'input': (3, Dimension(2))}, + 'params_ref': {'input': "Input[3,2]"}}, + {'params_test': {'input': [3, 2]}, + 'params_ref': {'input': "Input[3 2]"}}, + {'params_test': {'input': [Dimension(3, 10), 2]}, + 'params_ref': {'input': "Input[3..10 2]"}}, + {'params_test': {'input': (-1, 10)}, + 'params_ref': {'input': "Input[?,10]"}}, + {'params_test': {'input': PartialShape([-1, 10])}, + 'params_ref': {'input': "Input[?,10]"}}, + {'params_test': {'input': np.int32}, + 'params_ref': {'input': "Input{i32}"}}, + {'params_test': {'input': (np.int32, [1, 2, 3])}, + 'params_ref': {'input': "Input[1,2,3]{i32}"}}, + {'params_test': {'input': [Dimension(3, 10), 10, -1]}, + 'params_ref': {'input': 'Input[3..10,10,?]'}}, + ] + + @pytest.mark.parametrize("params", test_data) + @pytest.mark.nightly + @pytest.mark.precommit + def test_mo_convert_tf_model_single_input_output(self, params, ie_device, precision, ir_version, + temp_dir, use_new_frontend, use_old_api): + tf_net_path = self.create_tf_model_single_input_output(temp_dir) + + test_params = params['params_test'] + ref_params = params['params_ref'] + test_params.update({'input_model': tf_net_path}) + ref_params.update({'input_model': tf_net_path}) + self._test(temp_dir, test_params, ref_params) + +class NegativeCases(unittest.TestCase): + test_directory = os.path.dirname(os.path.realpath(__file__)) + + def test_input_output_cut_exceptions(self): + from openvino.tools.ovc import convert_model + with tempfile.TemporaryDirectory(dir=self.test_directory) as temp_dir: + tf_net_path = TestComplexParams.create_tf_model_single_input_output(temp_dir) + + with self.assertRaisesRegex(Exception, ".*Name Relu is not found among model inputs.*"): + convert_model(tf_net_path, input='Relu') + with self.assertRaisesRegex(Exception, ".*Name Relu is not found among model outputs.*"): + convert_model(tf_net_path, output='Relu') + + tf_net_path = TestComplexParams.create_tf_model(temp_dir) + + with self.assertRaisesRegex(Exception, ".*Name Relu2 is not found among model inputs.*"): + convert_model(tf_net_path, input='Relu2') + with self.assertRaisesRegex(Exception, ".*Name Relu2 is not found among model outputs.*"): + convert_model(tf_net_path, output='Relu2') \ No newline at end of file diff --git a/tests/layer_tests/ovc_python_api_tests/test_extensions.py b/tests/layer_tests/ovc_python_api_tests/test_extensions.py new file mode 100644 index 00000000000000..5cc02f76f54416 --- /dev/null +++ b/tests/layer_tests/ovc_python_api_tests/test_extensions.py @@ -0,0 +1,121 @@ +# Copyright (C) 2018-2023 Intel Corporation +# SPDX-License-Identifier: Apache-2.0 + +import pytest +import numpy as np + +from common.mo_convert_test_class import CommonMOConvertTest +from common.onnx_layer_test_class import save_to_onnx + +import openvino.runtime as ov +from openvino.runtime import PartialShape, Model + + +class TestExtensions(CommonMOConvertTest): + def create_onnx_model(self, tmp_dir): + # + # Create ONNX model + # + + import onnx + from onnx import helper + from onnx import TensorProto + + shape = [2, 3, 4] + + input = helper.make_tensor_value_info('input', TensorProto.FLOAT, shape) + output = helper.make_tensor_value_info('output', TensorProto.FLOAT, shape) + + node_def = onnx.helper.make_node( + 'LeakyRelu', + inputs=['input'], + outputs=['LeakyRelu_data'], + alpha=0.1 + ) + node_def2 = onnx.helper.make_node( + 'Elu', + inputs=['LeakyRelu_data'], + outputs=['output'], + alpha=0.1 + ) + + # Create the graph (GraphProto) + graph_def = helper.make_graph( + [node_def, node_def2], + 'test_model', + [input], + [output], + ) + + # Create the model (ModelProto) + onnx_net = helper.make_model(graph_def, producer_name='test_model') + + # save model to .onnx and return path to the model + return save_to_onnx(onnx_net, tmp_dir) + + def create_custom_extension_leaky_relu_to_relu(): + # replaces LeakyRelu with Relu + from openvino.frontend import ConversionExtension + from openvino.frontend import NodeContext + import openvino.runtime.opset8 as ops + + def custom_converter(node: NodeContext): + input = node.get_input(0) + relu = ops.relu(input) + return [relu.output(0)] + + return ConversionExtension("LeakyRelu", custom_converter) + + def create_custom_extension_elu_to_sigmoid(): + # replaces Elu with Sigmoid + from openvino.frontend import ConversionExtension + from openvino.frontend import NodeContext + import openvino.runtime.opset8 as ops + + def custom_converter(node: NodeContext): + input = node.get_input(0) + sigm = ops.sigmoid(input) + return [sigm.output(0)] + + return ConversionExtension("Elu", custom_converter) + + def create_ref_graph1(): + shape = PartialShape([2, 3, 4]) + param = ov.opset8.parameter(shape, dtype=np.float32) + param.get_output_tensor(0).set_names({"input"}) + relu = ov.opset8.relu(param) + relu.get_output_tensor(0).set_names({"LeakyRelu_data"}) + elu = ov.opset8.elu(relu, alpha=0.1) + elu.get_output_tensor(0).set_names({"output"}) + + return Model([elu], [param], "test") + + def create_ref_graph2(): + shape = PartialShape([2, 3, 4]) + param = ov.opset8.parameter(shape, dtype=np.float32) + param.get_output_tensor(0).set_names({"input"}) + relu = ov.opset8.relu(param) + relu.get_output_tensor(0).set_names({"LeakyRelu_data"}) + sigmoid = ov.opset8.sigmoid(relu) + sigmoid.get_output_tensor(0).set_names({"output"}) + + return Model([sigmoid], [param], "test") + + test_data = [ + {'params_test': {'extension': create_custom_extension_leaky_relu_to_relu()}, + 'ref_graph': create_ref_graph1()}, + {'params_test': {'extension': [create_custom_extension_leaky_relu_to_relu(), + create_custom_extension_elu_to_sigmoid()]}, + 'ref_graph': create_ref_graph2()} + ] + + @pytest.mark.parametrize("params", test_data) + @pytest.mark.nightly + @pytest.mark.precommit + def test_mo_convert_extensions(self, params, ie_device, precision, ir_version, + temp_dir, use_new_frontend, use_old_api): + onnx_net_path = self.create_onnx_model(temp_dir) + + test_params = params['params_test'] + test_params.update({'input_model': onnx_net_path}) + self._test_by_ref_graph(temp_dir, test_params, params['ref_graph']) diff --git a/tests/layer_tests/ovc_python_api_tests/test_onnx.py b/tests/layer_tests/ovc_python_api_tests/test_onnx.py new file mode 100644 index 00000000000000..58fd799a507dc9 --- /dev/null +++ b/tests/layer_tests/ovc_python_api_tests/test_onnx.py @@ -0,0 +1,83 @@ +# Copyright (C) 2018-2023 Intel Corporation +# SPDX-License-Identifier: Apache-2.0 + +import io + +import numpy as np +import openvino.runtime as ov +import pytest +from openvino.runtime import Model + +from common.mo_convert_test_class import CommonMOConvertTest + + +def make_graph_proto_model(): + import onnx + from onnx import helper + from onnx import TensorProto + + shape = [2, 3, 4] + + input = helper.make_tensor_value_info('input', TensorProto.FLOAT, shape) + output = helper.make_tensor_value_info('output', TensorProto.FLOAT, shape) + + node_def = onnx.helper.make_node( + 'LeakyRelu', + inputs=['input'], + outputs=['LeakyRelu_data'], + alpha=0.1 + ) + node_def2 = onnx.helper.make_node( + 'Elu', + inputs=['LeakyRelu_data'], + outputs=['output'], + alpha=0.1 + ) + + # Create the graph (GraphProto) + graph_def = helper.make_graph( + [node_def, node_def2], + 'test_model', + [input], + [output], + ) + + # Create the model (ModelProto) + onnx_net = helper.make_model(graph_def, producer_name='test_model') + + return onnx_net + +def create_ref_model(shape): + param1 = ov.opset8.parameter(shape, dtype=np.float32) + slope_const = ov.opset8.constant([0.1], dtype=np.float16) + decompress_slope = ov.opset8.convert(slope_const, np.float32) + prelu = ov.opset8.prelu(param1, slope=decompress_slope) + relu = ov.opset8.elu(prelu, alpha=np.float32(0.1)) + parameter_list = [param1] + return Model([relu], parameter_list, "test") + +def create_bytes_io(): + import onnx + onnx_model = make_graph_proto_model() + + file_like_object = io.BytesIO() + onnx.save(onnx_model, file_like_object) + + ref_model = create_ref_model([2,3,4]) + return file_like_object, ref_model, {} + + +class TestMoConvertONNX(CommonMOConvertTest): + test_data = [create_bytes_io] + @pytest.mark.parametrize("create_model", test_data) + @pytest.mark.nightly + @pytest.mark.precommit + def test_mo_convert_onnx(self, create_model, ie_device, precision, ir_version, + temp_dir): + fw_model, graph_ref, mo_params = create_model() + + test_params = {'input_model': fw_model} + if mo_params is not None: + test_params.update(mo_params) + self._test_by_ref_graph(temp_dir, test_params, graph_ref, compare_tensor_names=False) + diff --git a/tests/layer_tests/mo_python_api_tests/test_ovc_cli_tool.py b/tests/layer_tests/ovc_python_api_tests/test_ovc_cli_tool.py similarity index 85% rename from tests/layer_tests/mo_python_api_tests/test_ovc_cli_tool.py rename to tests/layer_tests/ovc_python_api_tests/test_ovc_cli_tool.py index 0ef3b08a4ba6b8..a1ccdf96ee6353 100644 --- a/tests/layer_tests/mo_python_api_tests/test_ovc_cli_tool.py +++ b/tests/layer_tests/ovc_python_api_tests/test_ovc_cli_tool.py @@ -89,3 +89,18 @@ def test_ovc_tool(self, ie_device, precision, ir_version, temp_dir, use_new_fron ov_model = core.read_model(os.path.join(temp_dir, "model.xml")) flag, msg = compare_functions(ov_model, create_ref_graph(), False) assert flag, msg + + def test_ovc_tool_output_dir(self, ie_device, precision, ir_version, temp_dir, use_new_frontend, use_old_api): + from openvino.runtime import Core + + model_path = self.create_tf_model(temp_dir) + + core = Core() + + # tests for MO cli tool + exit_code, stderr = generate_ir_ovc(coverage=False, **{"input_model": model_path, "output_model": temp_dir}) + assert not exit_code + + ov_model = core.read_model(os.path.join(temp_dir, "model.xml")) + flag, msg = compare_functions(ov_model, create_ref_graph(), False) + assert flag, msg \ No newline at end of file diff --git a/tests/layer_tests/ovc_python_api_tests/test_ovc_tool_help.py b/tests/layer_tests/ovc_python_api_tests/test_ovc_tool_help.py new file mode 100644 index 00000000000000..74ce9ead5717b9 --- /dev/null +++ b/tests/layer_tests/ovc_python_api_tests/test_ovc_tool_help.py @@ -0,0 +1,30 @@ +# Copyright (C) 2018-2023 Intel Corporation +# SPDX-License-Identifier: Apache-2.0 + + +import os +import sys +import unittest +from openvino.tools.ovc import ovc +from openvino.tools.ovc.cli_parser import get_mo_convert_params +from pathlib import Path + +from common.utils.common_utils import shell + + +class TestSubprocessMoConvert(unittest.TestCase): + def test_mo_convert(self): + mo_convert_params = get_mo_convert_params() + + # Test ovc tool help + mo_path = Path(ovc.__file__).parent + mo_runner = mo_path.joinpath('main.py').as_posix() + params = [sys.executable, mo_runner, "--help"] + _, mo_output, _ = shell(params) + + # We don't expect PyTorch specific parameters to be in help message of the MO tool. + for group in mo_convert_params: + if group == 'Pytorch-specific parameters:' or group == 'PaddlePaddle-specific parameters:': + continue + for param_name in group: + assert param_name in mo_output diff --git a/tests/layer_tests/ovc_python_api_tests/test_paddle.py b/tests/layer_tests/ovc_python_api_tests/test_paddle.py new file mode 100644 index 00000000000000..de4fe954eff204 --- /dev/null +++ b/tests/layer_tests/ovc_python_api_tests/test_paddle.py @@ -0,0 +1,103 @@ +# Copyright (C) 2018-2023 Intel Corporation +# SPDX-License-Identifier: Apache-2.0 + +import numpy as np +import pytest +from common.mo_convert_test_class import CommonMOConvertTest + +import openvino.runtime as ov +from openvino.runtime import PartialShape, Model + +def make_pd_dynamic_graph_model(): + import paddle + paddle.disable_static() + class NeuralNetwork(paddle.nn.Layer): + def __init__(self): + super(NeuralNetwork, self).__init__() + self.relu_sigmoid_stack = paddle.nn.Sequential( + paddle.nn.ReLU(), + paddle.nn.Sigmoid()) + def forward(self, input): + return self.relu_sigmoid_stack(input) + return NeuralNetwork() + +def make_pd_static_graph_model(shape): + import paddle + import paddle.nn + + paddle.enable_static() + + x = paddle.static.data(name="x", shape=shape) + y = paddle.static.data(name="y", shape=shape) + relu = paddle.nn.ReLU() + sigmoid = paddle.nn.Sigmoid() + y = sigmoid(relu(x)) + + exe = paddle.static.Executor(paddle.CPUPlace()) + exe.run(paddle.static.default_startup_program()) + return exe, x, y + +def make_pd_hapi_graph_model(shape): + import paddle + paddle.disable_static() + from paddle.static import InputSpec + net = paddle.nn.Sequential( + paddle.nn.ReLU(), + paddle.nn.Sigmoid()) + input = InputSpec(shape, 'float32', 'x') + label = InputSpec(shape, 'float32', 'label') + + model = paddle.Model(net, input, label) + optim = paddle.optimizer.SGD(learning_rate=1e-3, + parameters=model.parameters()) + model.prepare(optim, paddle.nn.CrossEntropyLoss(), paddle.metric.Accuracy()) + return model + +def make_ref_graph_model(shape, dtype=np.float32): + shape = PartialShape(shape) + param = ov.opset8.parameter(shape, name="x", dtype=dtype) + + relu = ov.opset8.relu(param) + sigm = ov.opset8.sigmoid(relu) + + model = Model([sigm], [param], "test") + return model + +def create_paddle_dynamic_module(tmp_dir): + import paddle + shape = [2,3,4] + pd_model = make_pd_dynamic_graph_model() + ref_model = make_ref_graph_model(shape) + + x = paddle.static.InputSpec(shape=shape, dtype='float32', name='x') + return pd_model, ref_model, {"example_input": [x]} + +def create_paddle_static_module(tmp_dir): + shape = [2,3,4] + pd_model, x, y = make_pd_static_graph_model(shape) + ref_model = make_ref_graph_model(shape) + + return pd_model, ref_model, {"example_input": [x], "example_output": [y]} + +def create_paddle_hapi_module(tmp_dir): + shape = [2,3,4] + pd_model = make_pd_hapi_graph_model(shape) + ref_model = make_ref_graph_model(shape) + + return pd_model, ref_model, {} + +class TestMoConvertPaddle(CommonMOConvertTest): + test_data = [ + create_paddle_dynamic_module, + create_paddle_static_module, + create_paddle_hapi_module + ] + @pytest.mark.skip(reason="Paddlepaddle has incompatible protobuf. Ticket: 95904") + @pytest.mark.parametrize("create_model", test_data) + def test_mo_import_from_memory_paddle_fe(self, create_model, ie_device, precision, ir_version, + temp_dir): + fw_model, graph_ref, mo_params = create_model(temp_dir) + test_params = {'input_model': fw_model, 'use_new_frontend': True} + if mo_params is not None: + test_params.update(mo_params) + self._test_by_ref_graph(temp_dir, test_params, graph_ref, compare_tensor_names=False) diff --git a/tests/layer_tests/ovc_python_api_tests/test_pytorch.py b/tests/layer_tests/ovc_python_api_tests/test_pytorch.py new file mode 100644 index 00000000000000..19c7f215e48440 --- /dev/null +++ b/tests/layer_tests/ovc_python_api_tests/test_pytorch.py @@ -0,0 +1,1073 @@ +# Copyright (C) 2018-2023 Intel Corporation +# SPDX-License-Identifier: Apache-2.0 + +import os + +from typing import Tuple +import numpy +import numpy as np +import openvino.runtime as ov +import pytest +import torch +import unittest +from openvino.runtime import PartialShape, Dimension, Model, Type +from openvino.tools.mo import InputCutInfo +from common.mo_convert_test_class import CommonMOConvertTest + + +class MyTorchOp(torch.autograd.Function): + @staticmethod + def symbolic(g, in_positions): + return g.op("MyTorchOp", in_positions) + + @staticmethod + def forward(self, in_positions): + out_pos = in_positions.reshape(-1) + return out_pos + 0.5 + + +def make_pt_model_one_input(): + from torch import nn + + class NeuralNetwork(nn.Module): + def __init__(self): + super(NeuralNetwork, self).__init__() + self.linear_relu_stack = nn.Sequential( + nn.ReLU(), + nn.Sigmoid(), + ) + + def forward(self, x): + logits = self.linear_relu_stack(x) + return logits + + return NeuralNetwork() + + +def make_pt_model_two_inputs(): + from torch import nn + + class NeuralNetwork(nn.Module): + def __init__(self): + super(NeuralNetwork, self).__init__() + self.linear_relu_stack = nn.Sequential( + nn.ReLU(), + nn.Sigmoid(), + ) + + def forward(self, x, y): + logits = self.linear_relu_stack(x * y) + return logits + + return NeuralNetwork() + + +def make_pt_model_with_optional_input(): + from torch import nn + + class NeuralNetwork(nn.Module): + def __init__(self): + super(NeuralNetwork, self).__init__() + self.linear_relu_stack = nn.Sequential( + nn.ReLU(), + nn.Sigmoid(), + ) + + def forward(self, x, y=None, z=None): + logits = None + if y is None: + logits = self.linear_relu_stack(x + z) + if z is None: + logits = self.linear_relu_stack(x * y) + return logits + + return NeuralNetwork() + + +def make_ref_pt_model_one_input(shape, dtype=np.float32): + shape = PartialShape(shape) + param1 = ov.opset8.parameter(shape, name="input_0", dtype=dtype) + relu = ov.opset8.relu(param1) + if dtype not in [np.float32, Type.dynamic]: + relu = ov.opset8.convert(relu, np.float32) + sigm = ov.opset8.sigmoid(relu) + + parameter_list = [param1] + model = Model([sigm], parameter_list, "test") + return model + + +def make_ref_pt_model_two_inputs(shape, dtype=np.float32): + if len(shape) == 2: + param1 = ov.opset8.parameter(PartialShape( + shape[0]), name="input_0", dtype=dtype) + param2 = ov.opset8.parameter(PartialShape( + shape[1]), name="input_1", dtype=dtype) + else: + shape = PartialShape(shape) + param1 = ov.opset8.parameter(shape, name="input_0", dtype=dtype) + param2 = ov.opset8.parameter(shape, name="input_1", dtype=dtype) + if dtype == Type.dynamic: + cl = ov.opset8.convert_like(param2, param1) + mul = ov.opset8.multiply(param1, cl) + else: + mul = ov.opset8.multiply(param1, param2) + relu = ov.opset8.relu(mul) + if dtype not in [np.float32, Type.dynamic]: + relu = ov.opset8.convert(relu, np.float32) + sigm = ov.opset8.sigmoid(relu) + + parameter_list = [param1, param2] + model = Model([sigm], parameter_list, "test") + return model + + +def make_ref_pt_model_with_optional_inputs(shape, dtype=np.float32, z_exist=False): + if len(shape) == 2: + param1 = ov.opset8.parameter(PartialShape( + shape[0]), name="input_0", dtype=dtype) + param2 = ov.opset8.parameter(PartialShape( + shape[1]), name="input_1", dtype=dtype) + else: + shape = PartialShape(shape) + param1 = ov.opset8.parameter(shape, name="input_0", dtype=dtype) + param2 = ov.opset8.parameter(shape, name="input_1", dtype=dtype) + + op = ov.opset8.multiply( + param1, param2) if not z_exist else ov.opset8.add(param1, param2) + relu = ov.opset8.relu(op) + if dtype != np.float32: + relu = ov.opset8.convert(relu, np.float32) + sigm = ov.opset8.sigmoid(relu) + + parameter_list = [param1, param2] + model = Model([sigm], parameter_list, "test") + return model + + +def create_pytorch_nn_module_case1(tmp_dir): + pt_model = make_pt_model_two_inputs() + ref_model = make_ref_pt_model_two_inputs([-1, -1, -1, -1]) + + sample_input1 = torch.zeros(1, 3, 10, 10) + sample_input2 = torch.zeros(1, 3, 10, 10) + sample_input = sample_input1, sample_input2 + + return pt_model, ref_model, {'example_input': sample_input} + + +def create_pytorch_nn_module_case2(tmp_dir): + pt_model = make_pt_model_two_inputs() + ref_model = make_ref_pt_model_two_inputs([-1, 3, -1, -1]) + + sample_input1 = torch.zeros(1, 3, 10, 10) + sample_input2 = torch.zeros(1, 3, 10, 10) + sample_input = sample_input1, sample_input2 + + return pt_model, ref_model, {'input': [PartialShape("[?,3,?,?]"), PartialShape([-1, 3, -1, -1])], + 'example_input': sample_input} + + +def create_pytorch_nn_module_with_scalar_input(tmp_dir): + pt_model = make_pt_model_two_inputs() + ref_model = make_ref_pt_model_two_inputs([[], [-1, 3, -1, -1]]) + + sample_input1 = torch.tensor(0.66) + sample_input2 = torch.zeros(1, 3, 10, 10) + sample_input = sample_input1, sample_input2 + + return pt_model, ref_model, {'input': ["[]", PartialShape([-1, 3, -1, -1])], + 'example_input': sample_input} + + +def create_pytorch_nn_module_case3(tmp_dir): + pt_model = make_pt_model_two_inputs() + ref_model = make_ref_pt_model_two_inputs([-1, 3, -1, -1]) + + sample_input1 = torch.zeros(1, 3, 10, 10) + sample_input2 = torch.zeros(1, 3, 10, 10) + sample_input = tuple([sample_input1, sample_input2]) + + return pt_model, ref_model, {'input': "[?,3,?,?],[?,3,?,?]", + 'example_input': sample_input} + + +def create_pytorch_nn_module_case4(tmp_dir): + pt_model = make_pt_model_one_input() + + sample_input = torch.zeros(1, 3, 10, 10) + + ref_model = make_ref_pt_model_one_input(PartialShape([1, 3, 20, 20])) + + return pt_model, ref_model, {'example_input': sample_input, "input": [1, 3, 20, 20]} + + +def create_pytorch_nn_module_case5(tmp_dir): + pt_model = make_pt_model_one_input() + inp_shape = PartialShape([-1, 3, Dimension(2, -1), Dimension(-1, 10)]) + ref_model = make_ref_pt_model_one_input(inp_shape) + + sample_input = torch.zeros(3, 3, 10, 10) + return pt_model, ref_model, {'example_input': sample_input, + 'input': (inp_shape, np.float32)} + + +def create_pytorch_nn_module_case6(tmp_dir): + pt_model = make_pt_model_one_input() + shape = PartialShape([1, 3, Dimension(2, -1), Dimension(-1, 10)]) + ref_model = make_ref_pt_model_one_input(shape) + + return pt_model, ref_model, {'input': (shape, np.float32)} + + +def create_pytorch_nn_module_case7(tmp_dir): + pt_model = make_pt_model_one_input() + + sample_input = torch.zeros(1, 3, 10, 10, dtype=torch.int32) + + ref_model = make_ref_pt_model_one_input( + PartialShape([1, 3, 20, 20]), dtype=np.int32) + + return pt_model, ref_model, {'example_input': sample_input, "input": ([1, 3, 20, 20], np.int32)} + + +def create_pytorch_nn_module_torch_size(tmp_dir): + pt_model = make_pt_model_one_input() + ref_model = make_ref_pt_model_one_input([1, 3, 2, 10]) + + return pt_model, ref_model, {'input': (torch.Size([1, 3, 2, 10]), np.float32)} + + +def create_pytorch_nn_module_sample_input_int32(tmp_dir): + pt_model = make_pt_model_one_input() + shape = PartialShape([-1, 3, Dimension(2, -1), Dimension(-1, 10)]) + + sample_input = torch.zeros(1, 3, 10, 10, dtype=torch.int32) + + ref_model = make_ref_pt_model_one_input(shape, dtype=np.int32) + + return pt_model, ref_model, {'example_input': sample_input, + 'input': (shape, np.int32)} + + +def create_pytorch_nn_module_sample_input_int32_two_inputs(tmp_dir): + pt_model = make_pt_model_two_inputs() + inp_shapes = [PartialShape("[?,3,?,?]"), PartialShape([-1, 3, -1, -1])] + + sample_input1 = torch.zeros(1, 3, 10, 10, dtype=torch.int32) + sample_input2 = torch.zeros(1, 3, 10, 10, dtype=torch.int32) + sample_input = sample_input1, sample_input2 + ref_model = make_ref_pt_model_two_inputs( + [PartialShape([-1, 3, -1, -1]), inp_shapes[1]], dtype=np.int32) + + return pt_model, ref_model, {'input': [(np.int32, inp_shapes[0]), (np.int32, inp_shapes[1])], + 'example_input': sample_input} + + +def create_pytorch_jit_script_module(tmp_dir): + import torch + + net = make_pt_model_two_inputs() + scripted_model = torch.jit.script(net) + + model_ref = make_ref_pt_model_two_inputs([1, 3, 5, 5]) + return scripted_model, model_ref, {'input': [([1, 3, 5, 5], np.float32), ([1, 3, 5, 5], np.float32)]} + + +def create_pytorch_jit_script_function(tmp_dir): + import torch + + @torch.jit.script + def scripted_fn(x: torch.Tensor, y: torch.Tensor): + return torch.sigmoid(torch.relu(x * y)) + + inp_shape = PartialShape([Dimension(1, -1), Dimension(-1, 5), 10]) + ref_model = make_ref_pt_model_two_inputs(inp_shape, dtype=Type.dynamic) + return scripted_fn, ref_model, {'input': [(inp_shape), (inp_shape)]} + + +def create_pytorch_nn_module_layout_list(tmp_dir): + from openvino.runtime import Layout + pt_model = make_pt_model_two_inputs() + shape = [1, 3, 10, 10] + + shape = PartialShape(shape) + ref_model = make_ref_pt_model_two_inputs(shape) + ref_model.inputs[0].node.layout = Layout('nchw') + ref_model.inputs[1].node.layout = Layout('nhwc') + + return pt_model, ref_model, { + 'input': [(shape, np.float32), (shape, np.float32)], 'layout': ['nchw', Layout('nhwc')], + 'use_convert_model_from_mo': True + } + + +def create_pytorch_nn_module_layout_list_case2(tmp_dir): + from openvino.runtime import Layout + pt_model = make_pt_model_two_inputs() + shape = [1, 3, 10, 10] + + shape = PartialShape(shape) + ref_model = make_ref_pt_model_two_inputs(shape) + ref_model.inputs[0].node.layout = Layout('nchw') + ref_model.inputs[1].node.layout = Layout('nhwc') + + return pt_model, ref_model, { + 'input': [(shape, np.float32), (shape, np.float32)], 'layout': ('nchw', Layout('nhwc')), + 'use_convert_model_from_mo': True} + + +def create_pytorch_nn_module_mean_list_compression_disabled(tmp_dir): + pt_model = make_pt_model_two_inputs() + shape = [1, 10, 10, 3] + + shape = PartialShape(shape) + param1 = ov.opset8.parameter(shape) + param2 = ov.opset8.parameter(shape) + const1 = ov.opset8.constant([[[[-0.0, -0.0, -0.0]]]], dtype=np.float32) + const2 = ov.opset8.constant([[[[-0.0, -0.0, -0.0]]]], dtype=np.float32) + add1 = ov.opset8.add(param1, const1) + add2 = ov.opset8.add(param2, const2) + mul = ov.opset8.multiply(add1, add2) + relu = ov.opset8.relu(mul) + sigm = ov.opset8.sigmoid(relu) + + parameter_list = [param1, param2] + ref_model = Model([sigm], parameter_list, "test") + + return pt_model, ref_model, {'input': [(shape, np.float32), (shape, np.float32)], + 'mean_values': [[0, 0, 0], [0, 0, 0]], + 'compress_to_fp16': False, 'use_convert_model_from_mo': True} + + +def create_pytorch_nn_module_mean_list_compression_default(tmp_dir): + # when 'use_convert_model_from_mo': True by default compression in convert_model is disabled + # therefore decompression Converts will not be present + pt_model = make_pt_model_two_inputs() + shape = [1, 10, 10, 3] + + shape = PartialShape(shape) + param1 = ov.opset8.parameter(shape) + param2 = ov.opset8.parameter(shape) + const1 = ov.opset8.constant([[[[-0.0, -0.0, -0.0]]]], dtype=np.float32) + const2 = ov.opset8.constant([[[[-0.0, -0.0, -0.0]]]], dtype=np.float32) + add1 = ov.opset8.add(param1, const1) + add2 = ov.opset8.add(param2, const2) + mul = ov.opset8.multiply(add1, add2) + relu = ov.opset8.relu(mul) + sigm = ov.opset8.sigmoid(relu) + + parameter_list = [param1, param2] + ref_model = Model([sigm], parameter_list, "test") + + return pt_model, ref_model, {'input': [(shape, np.float32), (shape, np.float32)], + 'mean_values': [[0, 0, 0], [0, 0, 0]], + 'use_convert_model_from_mo': True} + + +def create_pytorch_nn_module_mean_list_compression_enabled(tmp_dir): + pt_model = make_pt_model_two_inputs() + shape = [1, 10, 10, 3] + + shape = PartialShape(shape) + param1 = ov.opset8.parameter(shape) + param2 = ov.opset8.parameter(shape) + const1 = ov.opset8.constant([[[[-0.0, -0.0, -0.0]]]], dtype=np.float16) + const2 = ov.opset8.constant([[[[-0.0, -0.0, -0.0]]]], dtype=np.float16) + const1_decompressed = ov.opset8.convert( + const1, destination_type=np.float32) + const2_decompressed = ov.opset8.convert( + const2, destination_type=np.float32) + + add1 = ov.opset8.add(param1, const1_decompressed) + add2 = ov.opset8.add(param2, const2_decompressed) + mul = ov.opset8.multiply(add1, add2) + relu = ov.opset8.relu(mul) + sigm = ov.opset8.sigmoid(relu) + + parameter_list = [param1, param2] + ref_model = Model([sigm], parameter_list, "test") + + return pt_model, ref_model, { + 'input': [(shape, np.float32), (shape, np.float32)], 'mean_values': [[0, 0, 0], [0, 0, 0]], + 'compress_to_fp16': True, 'use_convert_model_from_mo': True} + + +def create_pytorch_nn_module_scale_list_compression_disabled(tmp_dir): + pt_model = make_pt_model_two_inputs() + shape = [1, 10, 10, 3] + + shape = PartialShape(shape) + param1 = ov.opset8.parameter(shape) + param2 = ov.opset8.parameter(shape) + const1 = ov.opset8.constant([[[[1, 1, 1]]]], dtype=np.float32) + const2 = ov.opset8.constant([[[[1, 1, 1]]]], dtype=np.float32) + sub1 = ov.opset8.multiply(param1, const1) + sub2 = ov.opset8.multiply(param2, const2) + mul = ov.opset8.multiply(sub1, sub2) + relu = ov.opset8.relu(mul) + sigm = ov.opset8.sigmoid(relu) + + parameter_list = [param1, param2] + ref_model = Model([sigm], parameter_list, "test") + + return pt_model, ref_model, {'input': [(shape, np.float32), (shape, np.float32)], + 'scale_values': [[1, 1, 1], [1, 1, 1]], + 'compress_to_fp16': False, 'use_convert_model_from_mo': True} + + +def create_pytorch_nn_module_scale_list_compression_default(tmp_dir): + # when 'use_convert_model_from_mo': True by default compression in convert_model is disabled + # therefore decompression Converts will not be present + pt_model = make_pt_model_two_inputs() + shape = [1, 10, 10, 3] + + shape = PartialShape(shape) + param1 = ov.opset8.parameter(shape) + param2 = ov.opset8.parameter(shape) + const1 = ov.opset8.constant([[[[1, 1, 1]]]], dtype=np.float32) + const2 = ov.opset8.constant([[[[1, 1, 1]]]], dtype=np.float32) + sub1 = ov.opset8.multiply(param1, const1) + sub2 = ov.opset8.multiply(param2, const2) + mul = ov.opset8.multiply(sub1, sub2) + relu = ov.opset8.relu(mul) + sigm = ov.opset8.sigmoid(relu) + + parameter_list = [param1, param2] + ref_model = Model([sigm], parameter_list, "test") + + return pt_model, ref_model, {'input': [(shape, np.float32), (shape, np.float32)], + 'scale_values': [[1, 1, 1], [1, 1, 1]], + 'use_convert_model_from_mo': True} + + +def create_pytorch_nn_module_scale_list_compression_enabled(tmp_dir): + pt_model = make_pt_model_two_inputs() + shape = [1, 10, 10, 3] + + shape = PartialShape(shape) + param1 = ov.opset8.parameter(shape) + param2 = ov.opset8.parameter(shape) + const1 = ov.opset8.constant([[[[1, 1, 1]]]], dtype=np.float16) + const1_decompressed = ov.opset8.convert( + const1, destination_type=np.float32) + const2 = ov.opset8.constant([[[[1, 1, 1]]]], dtype=np.float16) + const2_decompressed = ov.opset8.convert( + const2, destination_type=np.float32) + mul1 = ov.opset8.multiply(param1, const1_decompressed) + mul2 = ov.opset8.multiply(param2, const2_decompressed) + mul3 = ov.opset8.multiply(mul1, mul2) + relu = ov.opset8.relu(mul3) + sigm = ov.opset8.sigmoid(relu) + + parameter_list = [param1, param2] + ref_model = Model([sigm], parameter_list, "test") + + return pt_model, ref_model, {'input': [(shape, np.float32), (shape, np.float32)], + 'scale_values': [[1, 1, 1], [1, 1, 1]], + 'compress_to_fp16': True, 'use_convert_model_from_mo': True} + + +def create_pytorch_nn_module_shapes_list_static(tmp_dir): + pt_model = make_pt_model_two_inputs() + ref_model = make_ref_pt_model_two_inputs([1, 3, 20, 20], dtype=Type.dynamic) + + return pt_model, ref_model, {'input': [[1, 3, 20, 20], [1, 3, 20, 20]]} + + +def create_pytorch_nn_module_shapes_list_static_via_input(tmp_dir): + pt_model = make_pt_model_two_inputs() + ref_model = make_ref_pt_model_two_inputs([1, 3, 20, 20]) + + return pt_model, ref_model, {'input': [([1, 3, 20, 20], np.float32), ([1, 3, 20, 20], np.float32)]} + + +def create_pytorch_nn_module_shapes_list_dynamic(tmp_dir): + pt_model = make_pt_model_two_inputs() + inp_shapes = [[Dimension(-1), 3, 20, Dimension(20, -1)], + [-1, 3, 20, Dimension(-1, 20)]] + + param1 = ov.opset8.parameter(PartialShape( + inp_shapes[0]), name="x", dtype=Type.dynamic) + param2 = ov.opset8.parameter(PartialShape( + inp_shapes[1]), name="y", dtype=Type.dynamic) + cl = ov.opset8.convert_like(param2, param1) + mul = ov.opset8.multiply(param1, cl) + relu = ov.opset8.relu(mul) + sigm = ov.opset8.sigmoid(relu) + + parameter_list = [param1, param2] + ref_model = Model([sigm], parameter_list, "test") + return pt_model, ref_model, {'input': inp_shapes} + + +def create_pytorch_nn_module_shapes_list_dynamic_via_input(tmp_dir): + pt_model = make_pt_model_two_inputs() + inp_shapes = [[Dimension(-1), 3, 20, Dimension(20, -1)], + [-1, 3, 20, Dimension(-1, 20)]] + + param1 = ov.opset8.parameter(PartialShape( + inp_shapes[0]), name="x", dtype=np.float32) + param2 = ov.opset8.parameter(PartialShape( + inp_shapes[1]), name="y", dtype=np.float32) + mul = ov.opset8.multiply(param1, param2) + relu = ov.opset8.relu(mul) + sigm = ov.opset8.sigmoid(relu) + + parameter_list = [param1, param2] + ref_model = Model([sigm], parameter_list, "test") + return pt_model, ref_model, {'input': [(inp_shapes[0], Type.f32), (inp_shapes[1], Type.f32)]} + + +def create_pytorch_nn_module_shapes_list_dynamic_single_input(tmp_dir): + pt_model = make_pt_model_one_input() + inp_shapes = [[Dimension(-1), 3, 20, Dimension(20, -1)]] + ref_model = make_ref_pt_model_one_input(inp_shapes[0], dtype=Type.dynamic) + return pt_model, ref_model, {'input': inp_shapes} + + +def create_pytorch_nn_module_shapes_list_dynamic_single_input_via_input(tmp_dir): + pt_model = make_pt_model_one_input() + inp_shapes = [Dimension(-1), 3, 20, Dimension(20, -1)] + ref_model = make_ref_pt_model_one_input(inp_shapes) + return pt_model, ref_model, {'input': (inp_shapes, np.float32)} + + +def create_pytorch_nn_module_shapes_list_static_single_input(tmp_dir): + pt_model = make_pt_model_one_input() + inp_shapes = [[1, 3, 20, 20]] + ref_model = make_ref_pt_model_one_input(inp_shapes[0], dtype=Type.dynamic) + return pt_model, ref_model, {'input': inp_shapes} + + +def create_pytorch_nn_module_shapes_list_static_single_input_via_input(tmp_dir): + pt_model = make_pt_model_one_input() + inp_shapes = [1, 3, 20, 20] + ref_model = make_ref_pt_model_one_input(inp_shapes) + return pt_model, ref_model, {'input': (inp_shapes, np.float32)} + + +def create_pytorch_nn_module_convert_pytorch_frontend1(tmp_dir): + pt_model = make_pt_model_one_input() + shape = [-1, -1, -1, -1] + shape = PartialShape(shape) + param = ov.opset10.parameter(shape) + relu = ov.opset10.relu(param) + sigm = ov.opset10.sigmoid(relu) + + parameter_list = [param] + ref_model = Model([sigm], parameter_list, "test") + return pt_model, ref_model, { + "example_input": torch.zeros((1, 3, 10, 10)), + 'input': [([-1, -1, -1, -1], np.float32)] + } + + +def create_pytorch_nn_module_convert_pytorch_frontend2(tmp_dir): + pt_model = make_pt_model_one_input() + shape = [-1, -1, -1, -1] + shape = PartialShape(shape) + param = ov.opset10.parameter(shape, Type.i32) + relu = ov.opset10.relu(param) + convt = ov.opset10.convert(relu, "f32") + sigm = ov.opset10.sigmoid(convt) + + parameter_list = [param] + ref_model = Model([sigm], parameter_list, "test") + return pt_model, ref_model, { + "example_input": torch.zeros((1, 3, 10, 10), dtype=torch.int32), + 'input': [([-1, -1, -1, -1], np.int32)] + } + + +def create_pytorch_nn_module_convert_pytorch_frontend3(tmp_dir): + pt_model = make_pt_model_two_inputs() + shape = [-1, -1, -1, -1] + shape = PartialShape(shape) + param1 = ov.opset10.parameter(shape, dtype=np.float32) + param2 = ov.opset10.parameter(shape, dtype=np.float32) + mul = ov.opset10.multiply(param1, param2) + relu = ov.opset10.relu(mul) + sigm = ov.opset10.sigmoid(relu) + + parameter_list = [param1, param2] + ref_model = Model([sigm], parameter_list, "test") + return pt_model, ref_model, { + "example_input": [torch.zeros((1, 3, 10, 10)), torch.ones((1, 3, 10, 10))], + 'input': [([-1, -1, -1, -1], np.float32), ([-1, -1, -1, -1], np.float32)] + } + + +def create_pytorch_nn_module_convert_pytorch_frontend4(tmp_dir): + pt_model = make_pt_model_two_inputs() + shape = [-1, -1, -1, -1] + shape = PartialShape(shape) + param1 = ov.opset10.parameter(shape, dtype=np.float32) + param2 = ov.opset10.parameter(shape, dtype=np.float32) + mul = ov.opset10.multiply(param1, param2) + relu = ov.opset10.relu(mul) + sigm = ov.opset10.sigmoid(relu) + + parameter_list = [param1, param2] + ref_model = Model([sigm], parameter_list, "test") + return pt_model, ref_model, { + "example_input": {"x": torch.zeros((1, 3, 10, 10), dtype=torch.float32), + "y": torch.ones((1, 3, 10, 10), dtype=torch.float32)}, + 'input': [([-1, -1, -1, -1], np.float32), ([-1, -1, -1, -1], np.float32)] + } + + +def create_pytorch_jit_script_module_convert_pytorch_frontend(tmp_dir): + import torch + + net = make_pt_model_two_inputs() + scripted_model = torch.jit.script(net) + shape = [-1, -1, -1, -1] + shape = PartialShape(shape) + param1 = ov.opset10.parameter(shape, dtype=np.float32) + param2 = ov.opset10.parameter(shape, dtype=np.float32) + mul = ov.opset10.multiply(param1, param2) + relu = ov.opset10.relu(mul) + sigm = ov.opset10.sigmoid(relu) + parameter_list = [param1, param2] + ref_model = Model([sigm], parameter_list, "test") + return scripted_model, ref_model, { + "example_input": [torch.zeros((1, 3, 10, 10)), torch.ones((1, 3, 10, 10))]} + + +def create_pytorch_jit_trace_module_convert_pytorch_frontend(tmp_dir): + import torch + + net = make_pt_model_two_inputs() + example_input = [torch.zeros((1, 3, 10, 10)), torch.ones((1, 3, 10, 10))] + scripted_model = torch.jit.trace(net, example_input) + shape = [-1, -1, -1, -1] + shape = PartialShape(shape) + param1 = ov.opset10.parameter(shape, dtype=np.float32) + param2 = ov.opset10.parameter(shape, dtype=np.float32) + mul = ov.opset10.multiply(param1, param2) + relu = ov.opset10.relu(mul) + sigm = ov.opset10.sigmoid(relu) + parameter_list = [param1, param2] + ref_model = Model([sigm], parameter_list, "test") + return scripted_model, ref_model, {"example_input": example_input} + + +def create_pytorch_module_convert_pytorch_frontend_oob(tmp_dir): + import torch + import torch.nn.functional as F + + class ConvModel(torch.nn.Module): + def __init__(self): + super(ConvModel, self).__init__() + self.weights = torch.rand([1, 3, 3, 3]) + + def forward(self, x): + return F.conv2d(x, self.weights) + + net = ConvModel() + shape = PartialShape([-1, 3, -1, -1]) + param1 = ov.opset10.parameter(shape, dtype=np.float32) + weights = ov.opset10.constant(net.weights.numpy(force=True), dtype=np.float16) + decompress_weights = ov.opset10.convert(weights, np.float32) + conv = ov.opset10.convolution(param1, decompress_weights, strides=[1, 1], + pads_begin=[0, 0], pads_end=[0, 0], + dilations=[1, 1]) + parameter_list = [param1] + ref_model = Model([conv], parameter_list, "test") + return net, ref_model, {} + + +def create_pytorch_module_with_optional_inputs_case1(tmp_dir): + net = make_pt_model_with_optional_input() + example_input = {"x": torch.zeros( + (1, 3, 10, 10)), "y": torch.ones((1, 3, 10, 10))} + ref_model = make_ref_pt_model_with_optional_inputs([-1, -1, -1, -1]) + return net, ref_model, {"example_input": example_input} + + +def create_pytorch_module_with_optional_inputs_case2(tmp_dir): + net = make_pt_model_with_optional_input() + example_input = {"x": torch.zeros( + (1, 3, 10, 10)), "z": torch.ones((1, 3, 10, 10))} + ref_model = make_ref_pt_model_with_optional_inputs( + [-1, -1, -1, -1], z_exist=True) + return net, ref_model, {"example_input": example_input} + + +def create_pytorch_module_with_optional_inputs_case3(tmp_dir): + net = make_pt_model_with_optional_input() + example_input = {"x": torch.zeros( + (1, 3, 10, 10)), "z": torch.ones((1, 3, 10, 10))} + ref_model = make_ref_pt_model_with_optional_inputs( + [3, 3, 3, 3], z_exist=True) + return net, ref_model, {"example_input": example_input, "input": [[3, 3, 3, 3], [3, 3, 3, 3]]} + + +def create_pytorch_module_with_compressed_int8_constant_compress_to_fp16_default(tmp_dir): + import torch + import torch.nn.functional as F + + class Int8Model(torch.nn.Module): + def __init__(self): + super(Int8Model, self).__init__() + self.weights = torch.randint(-127, 128, + [1, 3, 3, 3], dtype=torch.int8) + + def forward(self, x): + cast = self.weights.to(torch.float32) + sub = cast - 0.5 + mul = sub * 0.02 + return F.conv2d(x, mul) + + net = Int8Model() + example_input = (torch.rand((1, 3, 10, 10)),) + traced_model = torch.jit.trace(net, example_input) + shape = [-1, -1, -1, -1] + shape = PartialShape(shape) + param1 = ov.opset10.parameter(shape, dtype=np.float32) + weights = ov.opset10.constant(net.weights.numpy(force=True)) + cast1 = ov.opset10.convert(weights, np.float32) + sub1_const = np.float16(0.5).reshape(1, 1, 1, 1) + mul1_const = np.float16(0.02).reshape(1, 1, 1, 1) + sub1_const_decompress = ov.opset10.convert(sub1_const, np.float32) + mul1_const_decompress = ov.opset10.convert(mul1_const, np.float32) + sub1 = ov.opset10.subtract(cast1, sub1_const_decompress) + mul1 = ov.opset10.multiply(sub1, mul1_const_decompress) + conv = ov.opset10.convolution(param1, mul1, strides=[1, 1], + pads_begin=[0, 0], pads_end=[0, 0], + dilations=[1, 1]) + ref_model = Model([conv], [param1], "test") + return traced_model, ref_model, {"example_input": example_input} + + +def create_pytorch_module_with_compressed_int8_constant(tmp_dir): + import torch + import torch.nn.functional as F + + class Int8Model(torch.nn.Module): + def __init__(self): + super(Int8Model, self).__init__() + self.weights = torch.randint(-127, 128, + [1, 3, 3, 3], dtype=torch.int8) + + def forward(self, x): + cast = self.weights.to(torch.float32) + sub = cast - 0.5 + mul = sub * 0.02 + return F.conv2d(x, mul) + + net = Int8Model() + example_input = (torch.rand((1, 3, 10, 10)),) + traced_model = torch.jit.trace(net, example_input) + shape = [-1, -1, -1, -1] + shape = PartialShape(shape) + param1 = ov.opset10.parameter(shape, dtype=np.float32) + weights = ov.opset10.constant(net.weights.numpy(force=True)) + cast1 = ov.opset10.convert(weights, np.float32) + sub1 = ov.opset10.subtract(cast1, np.float32(0.5).reshape(1, 1, 1, 1)) + mul1 = ov.opset10.multiply(sub1, np.float32(0.02).reshape(1, 1, 1, 1)) + conv = ov.opset10.convolution(param1, mul1, strides=[1, 1], + pads_begin=[0, 0], pads_end=[0, 0], + dilations=[1, 1]) + ref_model = Model([conv], [param1], "test") + return traced_model, ref_model, {"example_input": example_input, "compress_to_fp16": False} + + +def create_pytorch_module_with_nested_inputs(tmp_dir): + class PTModel(torch.nn.Module): + + def forward(self, z: Tuple[torch.Tensor, torch.Tensor]): + z1, z2 = z + zeros1 = torch.zeros((1, 1)) + zeros2 = torch.zeros((1, 5, 1)) + return torch.cat([z1, zeros1], 1), torch.cat([z2, zeros2], 2) + + net = PTModel() + constant_zeros1 = ov.opset10.constant(np.zeros((1, 1), dtype=np.float32), dtype=np.float32) + constant_zeros2 = ov.opset10.constant(np.zeros((1, 5, 1), dtype=np.float32), dtype=np.float32) + shape1 = PartialShape([1, -1]) + shape2 = PartialShape([1, 5, -1]) + param1 = ov.opset10.parameter(shape1, dtype=np.float32) + param2 = ov.opset10.parameter(shape2, dtype=np.float32) + concat1 = ov.opset10.concat([param1, constant_zeros1], 1) + concat2 = ov.opset10.concat([param2, constant_zeros2], 2) + ref_model = Model([concat2, concat1], [param1, param2], "test") + return net, ref_model, {"example_input": {"z": (torch.zeros((1, 10)), torch.ones((1, 5, 2)))}, + "compress_to_fp16": False} + + +def create_pytorch_module_with_nested_inputs_compress_to_fp16_default(tmp_dir): + class PTModel(torch.nn.Module): + + def forward(self, z: Tuple[torch.Tensor, torch.Tensor]): + z1, z2 = z + zeros1 = torch.zeros((1, 1)) + zeros2 = torch.zeros((1, 5, 1)) + return torch.cat([z1, zeros1], 1), torch.cat([z2, zeros2], 2) + + net = PTModel() + constant_zeros1 = ov.opset10.constant(np.zeros((1, 1), dtype=np.float32), dtype=np.float16) + constant_zeros2 = ov.opset10.constant(np.zeros((1, 5, 1), dtype=np.float32), dtype=np.float16) + const1_decompress = ov.opset10.convert(constant_zeros1, np.float32) + const2_decompress = ov.opset10.convert(constant_zeros2, np.float32) + shape1 = PartialShape([1, -1]) + shape2 = PartialShape([1, 5, -1]) + param1 = ov.opset10.parameter(shape1, dtype=np.float32) + param2 = ov.opset10.parameter(shape2, dtype=np.float32) + concat1 = ov.opset10.concat([param1, const1_decompress], 1) + concat2 = ov.opset10.concat([param2, const2_decompress], 2) + ref_model = Model([concat2, concat1], [param1, param2], "test") + return net, ref_model, {"example_input": {"z": (torch.zeros((1, 10)), torch.ones((1, 5, 2)))}} + + +def create_pytorch_module_with_nested_inputs2(tmp_dir): + class PTModel(torch.nn.Module): + + def forward(self, x: torch.Tensor, z: Tuple[torch.Tensor, torch.Tensor]): + z1, z2 = z + zeros1 = torch.zeros((1, 1)) + zeros2 = torch.zeros((1, 5, 1)) + return torch.cat([z1, zeros1], 1) + x, torch.cat([z2, zeros2], 2) + + net = PTModel() + constant_zeros1 = ov.opset10.constant(np.zeros((1, 1), dtype=np.float32), dtype=np.float32) + constant_zeros2 = ov.opset10.constant(np.zeros((1, 5, 1), dtype=np.float32), dtype=np.float32) + shape1 = PartialShape([1, -1]) + shape2 = PartialShape([1, 5, -1]) + param0 = ov.opset10.parameter(PartialShape([-1, -1]), dtype=np.float32) + param1 = ov.opset10.parameter(shape1, dtype=np.float32) + param2 = ov.opset10.parameter(shape2, dtype=np.float32) + concat1 = ov.opset10.concat([param1, constant_zeros1], 1) + concat2 = ov.opset10.concat([param2, constant_zeros2], 2) + add = ov.opset10.add(concat1, param0) + ref_model = Model([concat2, add], [param0, param1, param2], "test") + return net, ref_model, { + "example_input": {"x": torch.ones((1, 10)), "z": (torch.zeros((1, 10)), torch.ones((1, 5, 5)))}, + "compress_to_fp16": False} + + +def create_pytorch_module_with_nested_inputs3(tmp_dir): + class PTModel(torch.nn.Module): + + def forward(self, z: Tuple[torch.Tensor, torch.Tensor], x: torch.Tensor): + z1, z2 = z + zeros1 = torch.zeros((1, 1)) + zeros2 = torch.zeros((1, 5, 1)) + return torch.cat([z1, zeros1], 1) + x, torch.cat([z2, zeros2], 2) + + net = PTModel() + shape1 = PartialShape([1, -1]) + shape2 = PartialShape([1, 5, -1]) + constant_zeros1 = ov.opset10.constant(np.zeros((1, 1), dtype=np.float32), dtype=np.float32) + constant_zeros2 = ov.opset10.constant(np.zeros((1, 5, 1), dtype=np.float32), dtype=np.float32) + param1 = ov.opset10.parameter(shape1, dtype=np.float32) + param2 = ov.opset10.parameter(shape2, dtype=np.float32) + param3 = ov.opset10.parameter(PartialShape([-1, -1]), dtype=np.float32) + concat1 = ov.opset10.concat([param1, constant_zeros1], 1) + concat2 = ov.opset10.concat([param2, constant_zeros2], 2) + add = ov.opset10.add(concat1, param3) + ref_model = Model([concat2, add], [param1, param2, param3], "test") + return net, ref_model, { + "example_input": {"x": torch.ones((1, 10)), "z": (torch.zeros((1, 10)), torch.ones((1, 5, 3)))}, + "compress_to_fp16": False} + + +def create_pytorch_module_with_nested_inputs4(tmp_dir): + class PTModel(torch.nn.Module): + + def forward(self, x: torch.Tensor, z: Tuple[torch.Tensor, torch.Tensor], y: torch.Tensor): + z1, z2 = z + zeros1 = torch.zeros((1, 1)) + zeros2 = torch.zeros((1, 5, 1)) + return torch.cat([z1, zeros1], 1) + x, torch.cat([z2, zeros2], 2) * y + + net = PTModel() + constant_zeros1 = ov.opset10.constant(np.zeros((1, 1), dtype=np.float32), dtype=np.float32) + constant_zeros2 = ov.opset10.constant(np.zeros((1, 5, 1), dtype=np.float32), dtype=np.float32) + shape1 = PartialShape([1, -1]) + shape2 = PartialShape([1, 5, -1]) + param1 = ov.opset10.parameter(shape1, dtype=np.float32) + param2 = ov.opset10.parameter(shape2, dtype=np.float32) + param3 = ov.opset10.parameter(PartialShape([-1, -1]), dtype=np.float32) + param4 = ov.opset10.parameter(PartialShape([-1]), dtype=np.float32) + concat1 = ov.opset10.concat([param1, constant_zeros1], 1) + concat2 = ov.opset10.concat([param2, constant_zeros2], 2) + add = ov.opset10.add(concat1, param3) + mul = ov.opset10.multiply(concat2, param4) + ref_model = Model([mul, add], [param3, param1, param2, param4], "test") + return net, ref_model, { + "example_input": {"x": torch.ones((1, 10)), "z": (torch.zeros((1, 10)), torch.ones((1, 5, 10))), + "y": torch.ones((1,))}, + "compress_to_fp16": False} + + +def create_pytorch_module_with_nested_inputs5(tmp_dir): + class PTModel(torch.nn.Module): + + def forward(self, x: torch.Tensor, z: Tuple[torch.Tensor, torch.Tensor], y: torch.Tensor): + z1, z2 = z + zeros1 = torch.zeros((1, 1)) + zeros2 = torch.zeros((1, 5, 1)) + return torch.cat([z1, zeros1], 1) + x, torch.cat([z2, zeros2], 2) * y + + net = PTModel() + constant_zeros1 = ov.opset10.constant(np.zeros((1, 1), dtype=np.float32), dtype=np.float32) + constant_zeros2 = ov.opset10.constant(np.zeros((1, 5, 1), dtype=np.float32), dtype=np.float32) + shape1 = PartialShape([1, -1]) + shape2 = PartialShape([1, 5, -1]) + param0 = ov.opset10.parameter(PartialShape([-1, -1]), dtype=np.float32) + param1 = ov.opset10.parameter(shape1, dtype=np.float32) + param2 = ov.opset10.parameter(shape2, dtype=np.float32) + param4 = ov.opset10.parameter(PartialShape([-1]), dtype=np.float32) + concat1 = ov.opset10.concat([param1, constant_zeros1], 1) + concat2 = ov.opset10.concat([param2, constant_zeros2], 2) + add = ov.opset10.add(concat1, param0) + mul = ov.opset10.multiply(concat2, param4) + ref_model = Model([mul, add], [param0, param1, param2, param4], "test") + return net, ref_model, { + "example_input": [torch.ones((1, 10)), (torch.zeros((1, 10)), torch.ones((1, 5, 10))), torch.ones((1,))], + "compress_to_fp16": False} + + +def create_pytorch_module_with_nested_inputs6(tmp_dir): + class PTModel(torch.nn.Module): + + def forward(self, x: torch.Tensor, y: torch.Tensor = None, z: Tuple[torch.Tensor, torch.Tensor] = None): + z1, z2 = z + zeros1 = torch.zeros((1, 1)) + zeros2 = torch.zeros((1, 5, 1)) + if y is not None: + return torch.cat([z1, zeros1], 1) * y, torch.cat([z2, zeros2], 2) * y + return torch.cat([z1, zeros1], 1) + x, torch.cat([z2, zeros2], 2) + + net = PTModel() + constant_zeros1 = ov.opset10.constant(np.zeros((1, 1), dtype=np.float32), dtype=np.float32) + constant_zeros2 = ov.opset10.constant(np.zeros((1, 5, 1), dtype=np.float32), dtype=np.float32) + shape1 = PartialShape([1, -1]) + shape2 = PartialShape([1, 5, -1]) + param0 = ov.opset10.parameter(PartialShape([-1, -1]), dtype=np.float32) + param1 = ov.opset10.parameter(shape1, dtype=np.float32) + param2 = ov.opset10.parameter(shape2, dtype=np.float32) + concat1 = ov.opset10.concat([param1, constant_zeros1], 1) + concat2 = ov.opset10.concat([param2, constant_zeros2], 2) + add1 = ov.opset10.add(concat1, param0) + ref_model = Model([concat2, add1], [param0, param1, param2], "test") + return net, ref_model, { + "example_input": {"x": torch.ones((1, 11)), "z": (torch.zeros((1, 10)), torch.ones((1, 5, 10)))}, + "compress_to_fp16": False} + + +class TestMoConvertPyTorch(CommonMOConvertTest): + test_data = [ + create_pytorch_nn_module_case1, + create_pytorch_nn_module_case2, + create_pytorch_nn_module_case3, + create_pytorch_nn_module_case4, + create_pytorch_nn_module_case5, + create_pytorch_nn_module_case6, + create_pytorch_nn_module_case7, + create_pytorch_nn_module_torch_size, + create_pytorch_nn_module_sample_input_int32, + create_pytorch_nn_module_sample_input_int32_two_inputs, + create_pytorch_jit_script_module, + create_pytorch_jit_script_function, + create_pytorch_nn_module_layout_list, + create_pytorch_nn_module_layout_list_case2, + create_pytorch_nn_module_mean_list_compression_default, + create_pytorch_nn_module_mean_list_compression_disabled, + create_pytorch_nn_module_mean_list_compression_enabled, + create_pytorch_nn_module_scale_list_compression_default, + create_pytorch_nn_module_scale_list_compression_disabled, + create_pytorch_nn_module_scale_list_compression_enabled, + create_pytorch_nn_module_shapes_list_static, + create_pytorch_nn_module_shapes_list_static_via_input, + create_pytorch_nn_module_shapes_list_dynamic, + create_pytorch_nn_module_shapes_list_dynamic_via_input, + create_pytorch_nn_module_shapes_list_dynamic_single_input, + create_pytorch_nn_module_shapes_list_static_single_input, + create_pytorch_nn_module_shapes_list_dynamic_single_input_via_input, + create_pytorch_nn_module_shapes_list_static_single_input_via_input, + create_pytorch_nn_module_convert_pytorch_frontend1, + create_pytorch_nn_module_convert_pytorch_frontend2, + create_pytorch_nn_module_convert_pytorch_frontend3, + create_pytorch_nn_module_convert_pytorch_frontend4, + create_pytorch_jit_script_module_convert_pytorch_frontend, + create_pytorch_jit_trace_module_convert_pytorch_frontend, + create_pytorch_module_convert_pytorch_frontend_oob, + create_pytorch_module_with_optional_inputs_case1, + create_pytorch_module_with_optional_inputs_case2, + create_pytorch_module_with_optional_inputs_case3, + create_pytorch_nn_module_with_scalar_input, + create_pytorch_module_with_compressed_int8_constant, + create_pytorch_module_with_compressed_int8_constant_compress_to_fp16_default, + create_pytorch_module_with_nested_inputs, + create_pytorch_module_with_nested_inputs2, + create_pytorch_module_with_nested_inputs3, + create_pytorch_module_with_nested_inputs4, + create_pytorch_module_with_nested_inputs5, + create_pytorch_module_with_nested_inputs6 + ] + + @pytest.mark.parametrize("create_model", test_data) + @pytest.mark.nightly + @pytest.mark.precommit + def test_mo_import_from_memory(self, create_model, ie_device, precision, ir_version, + temp_dir, use_new_frontend, use_old_api): + fw_model, graph_ref, mo_params = create_model(temp_dir) + + test_params = {'input_model': fw_model} + if mo_params is not None: + test_params.update(mo_params) + self._test_by_ref_graph(temp_dir, test_params, + graph_ref, compare_tensor_names=False) + + +def create_pt_model_with_custom_op(): + # + # Create PyTorch model with custom operation + # + import torch.nn as nn + + class MyModel(nn.Module): + def __init__(self): + super(MyModel, self).__init__() + self.my_op = MyTorchOp() + + def forward(self, x): + return self.my_op.apply(x) + + return MyModel() + + +class ConvertRaises(unittest.TestCase): + def test_example_inputs(self): + from openvino.tools.ovc import convert_model + pytorch_model = create_pt_model_with_custom_op() + + # Check that mo raises error message of wrong argument. + with self.assertRaisesRegex(TypeError, ".*got an unexpected keyword argument 'example_inputs'.*"): + convert_model(pytorch_model, example_inputs=(torch.tensor(1),)) + + def test_failed_extension(self): + from openvino.tools.ovc import convert_model + from openvino.frontend.pytorch import ConversionExtension + + inp_shapes = [1, 3, 20, 20] + pt_model = make_pt_model_one_input() + + def relu_bad(n): + assert False, "Something happened" + + # Check that mo raises error message of wrong argument. + with self.assertRaisesRegex(Exception, ".*Conversion is failed for: aten::relu.*"): + convert_model(pt_model, input=(inp_shapes, np.float32), extensions=[ + ConversionExtension("aten::relu", relu_bad)]) + + def test_failed_extension(self): + import tempfile + from openvino.tools.ovc import convert_model + + with self.assertRaisesRegex(Exception, ".*Cannot recognize input model.*"): + with tempfile.NamedTemporaryFile() as tmpfile: + convert_model(tmpfile.name) diff --git a/tests/layer_tests/ovc_python_api_tests/test_tf.py b/tests/layer_tests/ovc_python_api_tests/test_tf.py new file mode 100644 index 00000000000000..d2cd65602e67c6 --- /dev/null +++ b/tests/layer_tests/ovc_python_api_tests/test_tf.py @@ -0,0 +1,859 @@ +# Copyright (C) 2018-2023 Intel Corporation +# SPDX-License-Identifier: Apache-2.0 + +import unittest + +import numpy as np +import openvino.runtime as ov +import pytest +from openvino.runtime import PartialShape, Model, Dimension + +from common.mo_convert_test_class import CommonMOConvertTest +from common.layer_test_class import CommonLayerTest + + +def create_tf_graph_def(tmp_dir): + import tensorflow as tf + + tf.compat.v1.reset_default_graph() + + with tf.compat.v1.Session() as sess: + inp1 = tf.compat.v1.placeholder(tf.float32, [1, 2, 3], 'Input') + inp2 = tf.compat.v1.placeholder(tf.float32, [1, 2, 3], 'Input') + relu = tf.nn.relu(inp1 + inp2, name='Relu') + + output = tf.nn.sigmoid(relu, name='Sigmoid') + + tf.compat.v1.global_variables_initializer() + tf_net = sess.graph_def + + shape = PartialShape([1, 2, 3]) + param1 = ov.opset8.parameter(shape, dtype=np.float32) + param2 = ov.opset8.parameter(shape, dtype=np.float32) + add = ov.opset8.add(param1, param2) + relu = ov.opset8.relu(add) + sigm = ov.opset8.sigmoid(relu) + + parameter_list = [param1, param2] + model_ref = Model([sigm], parameter_list, "test") + + return tf_net, model_ref, None + + +def create_keras_model(temp_dir): + import tensorflow as tf + + tf.keras.backend.clear_session() + tf.compat.v1.reset_default_graph() + + input_names = ["Input1", "Input2"] + input_shape = [1, 2, 3] + + x1 = tf.keras.Input(shape=input_shape, name=input_names[0]) + x2 = tf.keras.Input(shape=input_shape, name=input_names[1]) + y = tf.nn.sigmoid(tf.nn.relu(x1 + x2)) + keras_net = tf.keras.Model(inputs=[x1, x2], outputs=[y]) + + shape = PartialShape([-1, 1, 2, 3]) + param1 = ov.opset8.parameter(shape, dtype=np.float32) + param2 = ov.opset8.parameter(shape, dtype=np.float32) + add = ov.opset8.add(param1, param2) + relu = ov.opset8.relu(add) + sigm = ov.opset8.sigmoid(relu) + + parameter_list = [param1, param2] + model_ref = Model([sigm], parameter_list, "test") + tf.keras.backend.clear_session() + + return keras_net, model_ref, None + + +def create_tf1_wrap_function(tmp_dir): + import tensorflow as tf + + def f(x, y): + return tf.nn.sigmoid(tf.nn.relu(x + y)) + + func = tf.compat.v1.wrap_function(f, [tf.TensorSpec((1, 2, 3), tf.float32), + tf.TensorSpec((1, 2, 3), tf.float32)]) + + shape = PartialShape([1, 2, 3]) + param1 = ov.opset8.parameter(shape, dtype=np.float32) + param2 = ov.opset8.parameter(shape, dtype=np.float32) + add = ov.opset8.add(param1, param2) + relu = ov.opset8.relu(add) + sigm = ov.opset8.sigmoid(relu) + + parameter_list = [param1, param2] + model_ref = Model([sigm], parameter_list, "test") + + return func, model_ref, None + + +def create_tf_session(tmp_dir): + import tensorflow as tf + from tensorflow.python.eager.context import graph_mode + + with graph_mode(): + tf.compat.v1.reset_default_graph() + sess = tf.compat.v1.Session() + inp1 = tf.compat.v1.placeholder(tf.float32, [1, 2, 3], 'Input1') + inp2 = tf.compat.v1.placeholder(tf.float32, [1, 2, 3], 'Input2') + relu = tf.nn.relu(inp1 + inp2, name='Relu') + + output = tf.nn.sigmoid(relu, name='Sigmoid') + + tf.compat.v1.global_variables_initializer() + + shape = PartialShape([1, 2, 3]) + param1 = ov.opset8.parameter(shape, dtype=np.float32) + param2 = ov.opset8.parameter(shape, dtype=np.float32) + add = ov.opset8.add(param1, param2) + relu = ov.opset8.relu(add) + sigm = ov.opset8.sigmoid(relu) + + parameter_list = [param1, param2] + model_ref = Model([sigm], parameter_list, "test") + + return sess, model_ref, None + + +def create_tf_module(tmp_dir): + import tensorflow as tf + + class Net(tf.Module): + def __init__(self, name=None): + super(Net, self).__init__(name=name) + + def __call__(self, x, y): + return tf.nn.sigmoid(tf.nn.relu(x + y)) + + shape = PartialShape([1, 2, 3]) + param1 = ov.opset8.parameter(shape, dtype=np.float32) + param2 = ov.opset8.parameter(shape, dtype=np.float32) + add = ov.opset8.add(param1, param2) + relu = ov.opset8.relu(add) + sigm = ov.opset8.sigmoid(relu) + + parameter_list = [param1, param2] + model_ref = Model([sigm], parameter_list, "test") + + net = Net() + return net, model_ref, {'input': [PartialShape([1, 2, 3]), PartialShape([1, 2, 3])]} + + +def create_tf_module_dynamic(tmp_dir): + import tensorflow as tf + + class Net(tf.Module): + def __init__(self, name=None): + super(Net, self).__init__(name=name) + + def __call__(self, x, y): + return tf.nn.sigmoid(tf.nn.relu(x + y)) + + input_shapes = [PartialShape([-1, Dimension(3, -1), Dimension(4)]), + PartialShape([-1, Dimension(3), Dimension(4, -1)])] + + param1 = ov.opset8.parameter(input_shapes[0], dtype=np.float32) + param2 = ov.opset8.parameter(input_shapes[1], dtype=np.float32) + add = ov.opset8.add(param1, param2) + relu = ov.opset8.relu(add) + sigm = ov.opset8.sigmoid(relu) + + parameter_list = [param1, param2] + model_ref = Model([sigm], parameter_list, "test") + + net = Net() + return net, model_ref, {'input': input_shapes} + + +def create_keras_layer(tmp_dir): + import tensorflow as tf + class LayerModel(tf.keras.layers.Layer): + + def __init__(self): + super(LayerModel, self).__init__() + + def call(self, x, y): + return tf.sigmoid(tf.nn.relu(x + y)) + + shape = PartialShape([1, 2, 3]) + param1 = ov.opset8.parameter(shape, dtype=np.float32) + param2 = ov.opset8.parameter(shape, dtype=np.float32) + add = ov.opset8.add(param1, param2) + relu = ov.opset8.relu(add) + sigm = ov.opset8.sigmoid(relu) + + parameter_list = [param1, param2] + model_ref = Model([sigm], parameter_list, "test") + + net = LayerModel() + return net, model_ref, {'input': [PartialShape([1, 2, 3]), PartialShape([1, 2, 3])]} + + +def create_keras_layer_dynamic(tmp_dir): + import tensorflow as tf + class LayerModel(tf.keras.layers.Layer): + + def __init__(self): + super(LayerModel, self).__init__() + + def call(self, x, y): + return tf.sigmoid(tf.nn.relu(x + y)) + + input_shapes = [PartialShape([-1, Dimension(3, -1), Dimension(4)]), + PartialShape([-1, Dimension(3), Dimension(4, -1)])] + + param1 = ov.opset8.parameter(input_shapes[0], dtype=np.float32) + param2 = ov.opset8.parameter(input_shapes[1], dtype=np.float32) + add = ov.opset8.add(param1, param2) + relu = ov.opset8.relu(add) + sigm = ov.opset8.sigmoid(relu) + + parameter_list = [param1, param2] + model_ref = Model([sigm], parameter_list, "test") + + net = LayerModel() + return net, model_ref, {'input': input_shapes} + + +def create_tf_checkpoint(tmp_dir): + import tensorflow as tf + + input_names = ["Input1", "Input2"] + input_shape = [1, 2, 3] + + x1 = tf.keras.Input(shape=input_shape, name=input_names[0]) + x2 = tf.keras.Input(shape=input_shape, name=input_names[1]) + y = tf.nn.sigmoid(tf.nn.relu(x1 + x2)) + + model = tf.keras.Model(inputs=[x1, x2], outputs=[y]) + checkpoint = tf.train.Checkpoint(model) + + shape = PartialShape([-1, 1, 2, 3]) + param1 = ov.opset8.parameter(shape, dtype=np.float32) + param2 = ov.opset8.parameter(shape, dtype=np.float32) + add = ov.opset8.add(param1, param2) + relu = ov.opset8.relu(add) + sigm = ov.opset8.sigmoid(relu) + + parameter_list = [param1, param2] + model_ref = Model([sigm], parameter_list, "test") + + return checkpoint, model_ref, None + + +def create_tf_function(temp_dir): + import tensorflow as tf + + @tf.function( + input_signature=[tf.TensorSpec(shape=[1, 2, 3], dtype=tf.float32), + tf.TensorSpec(shape=[1, 2, 3], dtype=tf.float32)]) + def f(x1, x2): + y = tf.nn.sigmoid(tf.nn.relu(x1 + x2)) + return y + + shape = PartialShape([1, 2, 3]) + param1 = ov.opset8.parameter(shape, dtype=np.float32) + param2 = ov.opset8.parameter(shape, dtype=np.float32) + add = ov.opset8.add(param1, param2) + relu = ov.opset8.relu(add) + sigm = ov.opset8.sigmoid(relu) + + parameter_list = [param1, param2] + model_ref = Model([sigm], parameter_list, "test") + + return f, model_ref, None + + +def create_tf_graph(temp_dir): + import tensorflow as tf + + tf.compat.v1.reset_default_graph() + + with tf.compat.v1.Session() as sess: + inp1 = tf.compat.v1.placeholder(tf.float32, [1, 2, 3], 'Input') + inp2 = tf.compat.v1.placeholder(tf.float32, [1, 2, 3], 'Input') + relu = tf.nn.relu(inp1 + inp2, name='Relu') + + output = tf.nn.sigmoid(relu, name='Sigmoid') + + tf.compat.v1.global_variables_initializer() + tf_net = sess.graph + + shape = PartialShape([1, 2, 3]) + param1 = ov.opset8.parameter(shape, dtype=np.float32) + param2 = ov.opset8.parameter(shape, dtype=np.float32) + add = ov.opset8.add(param1, param2) + relu = ov.opset8.relu(add) + sigm = ov.opset8.sigmoid(relu) + + parameter_list = [param1, param2] + model_ref = Model([sigm], parameter_list, "test") + + return tf_net, model_ref, None + + +def create_tf_saved_model_dir(temp_dir): + import tensorflow as tf + + input_names = ["Input1", "Input2"] + input_shape = [1, 2, 3] + + x1 = tf.keras.Input(shape=input_shape, name=input_names[0]) + x2 = tf.keras.Input(shape=input_shape, name=input_names[1]) + y = tf.nn.sigmoid(tf.nn.relu(x1 + x2)) + keras_net = tf.keras.Model(inputs=[x1, x2], outputs=[y]) + + tf.saved_model.save(keras_net, temp_dir + "/model") + + shape = PartialShape([-1, 1, 2, 3]) + param1 = ov.opset8.parameter(shape, name="Input1:0", dtype=np.float32) + param2 = ov.opset8.parameter(shape, name="Input2:0", dtype=np.float32) + add = ov.opset8.add(param1, param2) + relu = ov.opset8.relu(add) + sigm = ov.opset8.sigmoid(relu) + + parameter_list = [param1, param2] + model_ref = Model([sigm], parameter_list, "test") + + return temp_dir + "/model", model_ref + + +def create_tf_stateful_partioned_call_net(temp_dir): + import tensorflow as tf + tf.compat.v1.reset_default_graph() + + data_shape = [1, 1, 10, 10] + filters_shape = [3, 3, 1, 1] + + strides = [1, 1] + pads_begin = [0, 0] + pads_end = [0, 0] + dilations = [1, 1] + + @tf.function + def second_func(input, filter): + conv = tf.raw_ops.Conv2D(input=input, filter=filter, strides=[1, 1, 1, 1], padding='SAME', data_format='NCHW') + return conv + + @tf.function( + input_signature=[tf.TensorSpec(shape=data_shape, dtype=tf.float32), + tf.TensorSpec(shape=filters_shape, dtype=tf.float32)]) + def first_func(input, filter): + conv = second_func(input, filter) + return conv + + tf_model = first_func + + param1 = ov.opset8.parameter(data_shape, dtype=np.float32) + param2 = ov.opset8.parameter(filters_shape, dtype=np.float32) + reshape = ov.opset8.reshape(param2, np.array([1, 1, 3, 3], dtype=np.int64), True) + conv = ov.opset11.convolution(param1, reshape, strides, pads_begin, pads_end, dilations, auto_pad="same_upper") + + parameter_list = [param1, param2] + model_ref = Model([conv], parameter_list, "test") + + return tf_model, model_ref, {} + + +def create_keras_layer_input_list(): + import tensorflow as tf + class LayerModel(tf.keras.layers.Layer): + + def __init__(self): + super(LayerModel, self).__init__() + + def call(self, x, y): + res_list = [tf.sigmoid(tf.nn.relu(x + y)), tf.nn.relu(x), tf.sigmoid(y)] + return res_list + + input_shapes = [PartialShape([1, 2, 3]), + PartialShape([1, 2, 3])] + + param1 = ov.opset8.parameter(input_shapes[0], dtype=np.float32) + param2 = ov.opset8.parameter(input_shapes[1], dtype=np.float32) + add = ov.opset8.add(param1, param2) + relu1 = ov.opset8.relu(add) + sigm1 = ov.opset8.sigmoid(relu1) + relu2 = ov.opset8.relu(param1) + sigm2 = ov.opset8.sigmoid(param2) + + parameter_list = [param1, param2] + model_ref = Model([sigm1, relu2, sigm2], parameter_list, "test") + return LayerModel(), model_ref + + +def create_keras_layer_input_list_one_inp(): + import tensorflow as tf + class LayerModel(tf.keras.layers.Layer): + + def __init__(self): + super(LayerModel, self).__init__() + + def call(self, x): + res_list = [tf.sigmoid(tf.nn.relu(x)), tf.nn.relu(x)] + return res_list + + input_shapes = [PartialShape([1,2,3])] + + param1 = ov.opset8.parameter(input_shapes[0], dtype=np.float32) + relu1 = ov.opset8.relu(param1) + sigm1 = ov.opset8.sigmoid(relu1) + parameter_list = [param1] + model_ref = Model([sigm1, relu1], parameter_list, "test") + + return LayerModel(), model_ref + + +def create_keras_layer_input_dict(): + import tensorflow as tf + class LayerModel(tf.keras.layers.Layer): + + def __init__(self): + super(LayerModel, self).__init__() + + def call(self, args): + res = {} + res['result'] = tf.sigmoid(tf.nn.relu(args['a'] + args['b'])) + return res + + input_shapes = [PartialShape([1, 2, 3]), + PartialShape([1, 2, 3])] + + param1 = ov.opset8.parameter(input_shapes[0], dtype=np.float32) + param2 = ov.opset8.parameter(input_shapes[1], dtype=np.float32) + add = ov.opset8.add(param1, param2) + relu1 = ov.opset8.relu(add) + sigm1 = ov.opset8.sigmoid(relu1) + + parameter_list = [param1, param2] + model_ref = Model([sigm1], parameter_list, "test") + return LayerModel(), model_ref + + +def create_keras_layer_input_dict_one_inp(): + import tensorflow as tf + class LayerModel(tf.keras.layers.Layer): + + def __init__(self): + super(LayerModel, self).__init__() + + def call(self, args): + res = {} + res['result'] = tf.sigmoid(tf.nn.relu(args['args'])) + return res + + input_shapes = [PartialShape([1, 2, 3]), + PartialShape([1, 2, 3])] + + param1 = ov.opset8.parameter(input_shapes[0], dtype=np.float32) + param2 = ov.opset8.parameter(input_shapes[1], dtype=np.float32) + relu1 = ov.opset8.relu(param1) + sigm1 = ov.opset8.sigmoid(relu1) + parameter_list = [param1, param2] + model_ref = Model([sigm1], parameter_list, "test") + return LayerModel(), model_ref + + +def single_param_function_reference(shape, const_value): + param1 = ov.opset8.parameter(shape, dtype=np.float32) + const = ov.opset8.constant(const_value, dtype=np.float32) + sigm = ov.opset8.sigmoid(param1) + mul = ov.opset8.multiply(sigm, const) + parameter_list = [param1] + return Model([mul], parameter_list, "test") + + +def two_params_function_reference(shapes, const_value): + param1 = ov.opset8.parameter(shapes[0], dtype=np.float32) + param2 = ov.opset8.parameter(shapes[1], dtype=np.float32) + const = ov.opset8.constant(const_value, dtype=np.float32) + sigm = ov.opset8.sigmoid(param1) + add = ov.opset8.add(sigm, param2) + mul = ov.opset8.multiply(add, const) + parameter_list = [param1, param2] + return Model([mul], parameter_list, "test") + + +def two_params_function_reference_fp16_compressed(shapes, const_value): + param1 = ov.opset8.parameter(shapes[0], dtype=np.float32) + param2 = ov.opset8.parameter(shapes[1], dtype=np.float32) + const_value = ov.opset8.constant(const_value, dtype=np.float16) + const_decompress = ov.opset8.convert(const_value, np.float32) + sigm = ov.opset8.sigmoid(param1) + add = ov.opset8.add(sigm, param2) + mul = ov.opset8.multiply(add, const_decompress) + parameter_list = [param1, param2] + return Model([mul], parameter_list, "test") + + +def create_keras_layer_with_example_input_1(tmp_dir): + model, model_ref = create_keras_layer_input_list() + example_input = (np.random.rand(1,2,3).astype(np.float32), np.random.rand(1,2,3).astype(np.float32)) + return model, model_ref, {'example_input': example_input} + + +def create_keras_layer_with_example_input_2(tmp_dir): + model, model_ref = create_keras_layer_input_dict() + example_input = {'a': np.random.rand(1,2,3).astype(np.float32), 'b': np.random.rand(1,2,3).astype(np.float32)} + return model, model_ref, {'example_input': example_input} + + +def create_keras_layer_with_input_shapes_case1(tmp_dir): + model, model_ref = create_keras_layer_input_list() + return model, model_ref, {'input': [[1, 2, 3], [1, 2, 3]]} + + +def create_keras_layer_with_input_shapes_case2(tmp_dir): + model, model_ref = create_keras_layer_input_list() + return model, model_ref, {'input': [([1, 2, 3], np.float32), ([1, 2, 3], np.float32)]} + + +def create_keras_layer_with_input_shapes_case3(tmp_dir): + model, model_ref = create_keras_layer_input_dict_one_inp() + return model, model_ref, {'input': [('args', [1, 2, 3])]} + + +def create_keras_layer_with_input_shapes_case4(tmp_dir): + model, model_ref = create_keras_layer_input_list_one_inp() + return model, model_ref, {'input': [1, 2, 3]} + + +def create_keras_layer_with_tf_function_call(tmp_dir): + import tensorflow as tf + class LayerModel(tf.Module): + def __init__(self): + super(LayerModel, self).__init__() + self.var1 = tf.Variable(5.0) + + @tf.function(input_signature=[tf.TensorSpec([1, 2], tf.float32), tf.TensorSpec([1, 2], tf.float32)]) + def __call__(self, input1, input2): + sigm = tf.nn.sigmoid(input1) + input2 + return sigm * self.var1 + model = LayerModel() + model_ref = two_params_function_reference([[1, 2], [1, 2]], [[5.0]]) + return model, model_ref, {'compress_to_fp16': False} + + +def create_keras_layer_with_tf_function_call_default_compressed_to_fp16(tmp_dir): + import tensorflow as tf + class LayerModel(tf.Module): + def __init__(self): + super(LayerModel, self).__init__() + self.var1 = tf.Variable(5.0) + + @tf.function(input_signature=[tf.TensorSpec([1, 2], tf.float32), tf.TensorSpec([1, 2], tf.float32)]) + def __call__(self, input1, input2): + sigm = tf.nn.sigmoid(input1) + input2 + return sigm * self.var1 + model = LayerModel() + model_ref = two_params_function_reference_fp16_compressed([[1, 2], [1, 2]], [[5.0]]) + return model, model_ref, {} + + +def create_keras_layer_with_tf_function_call_no_signature(tmp_dir): + import tensorflow as tf + class LayerModel(tf.Module): + def __init__(self): + super(LayerModel, self).__init__() + self.var1 = tf.Variable(5.0) + + @tf.function() + def __call__(self, input1, input2): + sigm = tf.nn.sigmoid(input1) + input2 + return sigm * self.var1 + model = LayerModel() + example_input = [np.random.rand(2, 3).astype(np.float32), np.random.rand(2, 3).astype(np.float32)] + + model_ref = two_params_function_reference([[2, 3], [2, 3]], [[5.0]]) + return model, model_ref, {'example_input': example_input, 'compress_to_fp16': False} + + +def create_keras_layer_with_tf_function_call_no_signature_single_input(tmp_dir): + import tensorflow as tf + class LayerModel(tf.Module): + def __init__(self): + super(LayerModel, self).__init__() + self.var1 = tf.Variable(5.0) + + @tf.function() + def __call__(self, input1): + sigm = tf.nn.sigmoid(input1) + return sigm * self.var1 + model = LayerModel() + example_input = np.random.rand(2, 3).astype(np.float32) + + model_ref = single_param_function_reference([2, 3], [[5.0]]) + return model, model_ref, {'example_input': example_input, 'compress_to_fp16': False} + + +def create_keras_layer_with_string_tensor(tmp_dir): + import tensorflow as tf + class LayerModel(tf.Module): + def __init__(self): + super(LayerModel, self).__init__() + self.var = tf.Variable("Text_1", dtype=tf.string) + self.const = tf.constant("Text_2", dtype=tf.string) + + @tf.function(input_signature=[tf.TensorSpec([1], tf.float32), tf.TensorSpec([1], tf.float32)]) + def __call__(self, input1, input2): + return input1 + input2, self.var, self.const + + model = LayerModel() + + param1 = ov.opset8.parameter([1], dtype=np.float32) + param2 = ov.opset8.parameter([1], dtype=np.float32) + add = ov.opset8.add(param1, param2) + parameter_list = [param1, param2] + model_ref = Model([add], parameter_list, "test") + + return model, model_ref, {} + + +class TestMoConvertTF(CommonMOConvertTest): + test_data = [ + # TF2 + create_keras_model, + create_keras_layer, + create_tf_function, + create_tf_module, + create_tf_checkpoint, + create_keras_layer_dynamic, + create_tf_module_dynamic, + create_tf_stateful_partioned_call_net, + create_keras_layer_with_example_input_1, + create_keras_layer_with_example_input_2, + create_keras_layer_with_input_shapes_case1, + create_keras_layer_with_input_shapes_case2, + create_keras_layer_with_input_shapes_case3, + create_keras_layer_with_input_shapes_case4, + create_keras_layer_with_tf_function_call, + create_keras_layer_with_tf_function_call_default_compressed_to_fp16, + create_keras_layer_with_tf_function_call_no_signature, + create_keras_layer_with_tf_function_call_no_signature_single_input, + create_keras_layer_with_string_tensor, + + # TF1 + create_tf_graph, + create_tf_graph_def, + create_tf1_wrap_function, + create_tf_session, + ] + + @pytest.mark.parametrize("create_model", test_data) + @pytest.mark.nightly + @pytest.mark.precommit_tf_fe + @pytest.mark.precommit + def test_mo_import_from_memory_tf_fe(self, create_model, ie_device, precision, ir_version, + temp_dir): + fw_model, graph_ref, mo_params = create_model(temp_dir) + + test_params = {'input_model': fw_model} + if mo_params is not None: + test_params.update(mo_params) + self._test_by_ref_graph(temp_dir, test_params, graph_ref, compare_tensor_names=False) + + @pytest.mark.nightly + @pytest.mark.precommit + def test_unnamed_saved_model_dir(self, ie_device, precision, ir_version, temp_dir): + saved_model_dir, graph_ref = create_tf_saved_model_dir(temp_dir) + + test_params = {'input_model': saved_model_dir} + self._test_by_ref_graph(temp_dir, test_params, graph_ref, compare_tensor_names=False) + + test_params = {'input_model': saved_model_dir} + self._test_by_ref_graph(temp_dir, test_params, graph_ref, compare_tensor_names=False) + + def test_zero_copy(self, ie_device, precision, ir_version, temp_dir): + import tensorflow as tf + from openvino.tools.ovc import convert_model + from openvino.runtime import compile_model + class LayerModel(tf.Module): + def __init__(self): + super(LayerModel, self).__init__() + self.var1 = tf.Variable([7., 5., 6.], name='var1') + self.var2 = tf.Variable([5., 7., 3.], name='var2') + + + @tf.function + def sub_function(self, input): + return input * self.var1 + self.var2 + + @tf.function() + def __call__(self, input): + return self.sub_function(input) + + # Create TF model with variables + keras_model = LayerModel() + test_input = np.array(7.).astype(np.float32) + + # Convert model to OV + ov_model = convert_model(keras_model, input=[1], share_weights=True) + cmp_model = compile_model(ov_model) + + # Check model inference + ov_infer1 = cmp_model(test_input, ie_device) + fw_infer1 = keras_model(test_input).numpy() + + assert np.array_equal(ov_infer1['Identity:0'], fw_infer1) + assert np.array_equal(ov_infer1['Identity:0'], [54., 42., 45.]) + + # Change value of variables in original model + for val in keras_model.variables: + arr = val.value().__array__() + arr[0] = 0 + arr[1] = 1 + arr[2] = 2 + + # Check model inference + cmp_model = compile_model(ov_model) + ov_infer2 = cmp_model(test_input) + fw_infer2 = keras_model(test_input).numpy() + + assert np.array_equal(ov_infer2['Identity:0'], fw_infer2) + assert np.array_equal(ov_infer2['Identity:0'], [ 0., 8., 16.]) + + def test_turn_off_sharing(self, ie_device, precision, ir_version, temp_dir): + import tensorflow as tf + from openvino.tools.ovc import convert_model + from openvino.runtime import compile_model + class LayerModel(tf.Module): + def __init__(self): + super(LayerModel, self).__init__() + self.var1 = tf.Variable([7., 5., 6.], name='var1') + self.var2 = tf.Variable([5., 7., 3.], name='var2') + + + @tf.function + def sub_function(self, input): + return input * self.var1 + self.var2 + + @tf.function() + def __call__(self, input): + return self.sub_function(input) + + # Create TF model with variables + keras_model = LayerModel() + test_input = np.array(7.).astype(np.float32) + + # Convert model to OV + ov_model = convert_model(keras_model, input=[1], share_weights=False) + cmp_model = compile_model(ov_model) + + # Check model inference + ov_infer1 = cmp_model(test_input, ie_device) + fw_infer1 = keras_model(test_input).numpy() + + assert np.array_equal(ov_infer1['Identity:0'], fw_infer1) + assert np.array_equal(ov_infer1['Identity:0'], [54., 42., 45.]) + + # Change value of variables in original model + for val in keras_model.variables: + arr = val.value().__array__() + arr[0] = 0 + arr[1] = 1 + arr[2] = 2 + + # Check model inference + ov_infer2 = cmp_model(test_input) + fw_infer2 = keras_model(test_input).numpy() + + # Check model inference calculated with old constant values + assert not np.array_equal(ov_infer2['Identity:0'], fw_infer2) + assert np.array_equal(ov_infer2['Identity:0'], [54., 42., 45.]) + + def test_memory_loss(self, ie_device, precision, ir_version, temp_dir): + # This test checks that the memory allocated for constants + # is not lost after returning the model from convert_model() method. + import tensorflow as tf + tf.compat.v1.reset_default_graph() + + from openvino.tools.ovc import convert_model + from openvino.runtime import compile_model + import gc + + with tf.compat.v1.Session() as sess: + inp1 = tf.compat.v1.placeholder(tf.float32, [3], 'Input') + const = tf.constant([0.5, 2.3, 7.8], dtype=tf.float32) + res = inp1 + const + + tf.compat.v1.global_variables_initializer() + tf_graph = sess.graph # tf.Graph + + if precision == 'FP32': + eps = 1e-4 + else: + eps = 5e-2 + + + test_input = np.array([2.1, 7.3, 4.6]).astype(np.float32) + + # Convert model to OV + ov_model = convert_model(tf_graph) + cmp_model = compile_model(ov_model) + + # Check model inference + ov_infer1 = cmp_model(test_input, ie_device) + + feed_dict = {"Input:0": test_input} + with tf.compat.v1.Session(graph=tf_graph) as sess: + fw_infer1 = sess.run('add:0', feed_dict=feed_dict) + + assert CommonLayerTest().compare_ie_results_with_framework(ov_infer1, {"add:0": fw_infer1}, eps) + assert CommonLayerTest().compare_ie_results_with_framework(ov_infer1, {"add:0": [2.6, 9.6, 12.4]}, eps) + + # run Garbage collector to ensure, that values from tf.constant are copied to ov.Const and + # we do not lose allocated memory. + gc.collect() + + # Check model inference + cmp_model = compile_model(ov_model) + ov_infer2 = cmp_model(test_input, ie_device) + + feed_dict = {"Input:0": test_input} + with tf.compat.v1.Session(graph=tf_graph) as sess: + fw_infer2 = sess.run('add:0', feed_dict=feed_dict) + + assert CommonLayerTest().compare_ie_results_with_framework(ov_infer2, {"add:0": fw_infer2}, eps) + assert CommonLayerTest().compare_ie_results_with_framework(ov_infer1, {"add:0": [2.6, 9.6, 12.4]}, eps) + + +class TFConvertTest(unittest.TestCase): + @pytest.mark.nightly + @pytest.mark.precommit + def test_tf_function_no_signature(self): + import tensorflow as tf + from openvino.tools.ovc import convert_model + + @tf.function() + def function(x1, x2): + y = tf.nn.sigmoid(tf.nn.relu(x1 + x2)) + return y + + with self.assertRaisesRegex(Exception, ".*Please provide 'example_input'.*"): + convert_model(function) + + +class TestTFLoadByModel(unittest.TestCase): + def test_load_by_model_tf_graph_iterator(self): + def simple_tf_model(): + import tensorflow as tf + + tf.compat.v1.reset_default_graph() + + with tf.compat.v1.Session() as sess: + inp = tf.compat.v1.placeholder(tf.float32, [1, 2, 3], "Input") + _ = tf.nn.sigmoid(inp, name="Sigmoid") + + tf.compat.v1.global_variables_initializer() + tf_net = sess.graph + return tf_net + from openvino.frontend.tensorflow.graph_iterator import GraphIteratorTFGraph + from openvino.frontend import FrontEndManager + model = GraphIteratorTFGraph(simple_tf_model(), True) + fem = FrontEndManager() + fe = fem.load_by_model(model) + assert fe is not None + assert fe.get_name() == "tf" diff --git a/tools/benchmark_tool/openvino/__init__.py b/tools/benchmark_tool/openvino/__init__.py index 8f0113d5bcaf6c..90552e0befed68 100644 --- a/tools/benchmark_tool/openvino/__init__.py +++ b/tools/benchmark_tool/openvino/__init__.py @@ -57,6 +57,6 @@ # Tools try: # Model Conversion API - ovc should reside in the main namespace - from openvino.tools.ovc import convert_model, InputCutInfo + from openvino.tools.ovc import convert_model except ImportError: pass diff --git a/tools/mo/openvino/__init__.py b/tools/mo/openvino/__init__.py index 8f0113d5bcaf6c..90552e0befed68 100644 --- a/tools/mo/openvino/__init__.py +++ b/tools/mo/openvino/__init__.py @@ -57,6 +57,6 @@ # Tools try: # Model Conversion API - ovc should reside in the main namespace - from openvino.tools.ovc import convert_model, InputCutInfo + from openvino.tools.ovc import convert_model except ImportError: pass diff --git a/tools/openvino_dev/src/openvino/__init__.py b/tools/openvino_dev/src/openvino/__init__.py index 8f0113d5bcaf6c..90552e0befed68 100644 --- a/tools/openvino_dev/src/openvino/__init__.py +++ b/tools/openvino_dev/src/openvino/__init__.py @@ -57,6 +57,6 @@ # Tools try: # Model Conversion API - ovc should reside in the main namespace - from openvino.tools.ovc import convert_model, InputCutInfo + from openvino.tools.ovc import convert_model except ImportError: pass diff --git a/tools/ovc/openvino/__init__.py b/tools/ovc/openvino/__init__.py index 8f0113d5bcaf6c..90552e0befed68 100644 --- a/tools/ovc/openvino/__init__.py +++ b/tools/ovc/openvino/__init__.py @@ -57,6 +57,6 @@ # Tools try: # Model Conversion API - ovc should reside in the main namespace - from openvino.tools.ovc import convert_model, InputCutInfo + from openvino.tools.ovc import convert_model except ImportError: pass diff --git a/tools/ovc/openvino/tools/ovc/__init__.py b/tools/ovc/openvino/tools/ovc/__init__.py index c55c861e448318..7d462d71b205b9 100644 --- a/tools/ovc/openvino/tools/ovc/__init__.py +++ b/tools/ovc/openvino/tools/ovc/__init__.py @@ -1,4 +1,4 @@ # Copyright (C) 2018-2023 Intel Corporation # SPDX-License-Identifier: Apache-2.0 -from openvino.tools.ovc.convert import convert_model, InputCutInfo \ No newline at end of file +from openvino.tools.ovc.convert import convert_model \ No newline at end of file diff --git a/tools/ovc/openvino/tools/ovc/cli_parser.py b/tools/ovc/openvino/tools/ovc/cli_parser.py index e8dcb63fa0e184..e4f2b99cccb021 100644 --- a/tools/ovc/openvino/tools/ovc/cli_parser.py +++ b/tools/ovc/openvino/tools/ovc/cli_parser.py @@ -3,27 +3,27 @@ import argparse import ast +import inspect import logging as log import os import pathlib import re from collections import OrderedDict, namedtuple from distutils.util import strtobool -from itertools import zip_longest -from pathlib import Path from operator import xor from typing import List, Union -import numbers -import inspect import numpy as np -from openvino.runtime import Layout, PartialShape, Dimension, Shape, Type # pylint: disable=no-name-in-module,import-error +from openvino.runtime import PartialShape, Dimension, Shape, Type # pylint: disable=no-name-in-module,import-error import openvino from openvino.tools.ovc.convert_data_type import destination_type_to_np_data_type from openvino.tools.ovc.error import Error -from openvino.tools.ovc.utils import get_mo_root_dir from openvino.tools.ovc.help import get_convert_model_help_specifics +from openvino.tools.ovc.utils import get_mo_root_dir + +# Helper class for storing input cut information +_InputCutInfo = namedtuple("InputCutInfo", ["name", "shape", "type", "value"], defaults=[None, None, None, None]) def is_shape_type(value): if isinstance(value, PartialShape): @@ -45,20 +45,12 @@ def single_input_to_input_cut_info(input: [str, tuple, list, PartialShape, Type, """ if isinstance(input, str): # Parse params from string - node_name, shape, value, data_type = parse_input_value(input) - # pylint: disable=no-member - return openvino.tools.ovc.InputCutInfo(node_name, - PartialShape(shape) if shape is not None else None, - data_type, - value) - if isinstance(input, openvino.tools.ovc.InputCutInfo): # pylint: disable=no-member - # Wrap input.shape to PartialShape if possible and wrap to InputCutInfo + node_name, shape, data_type = parse_input_value(input) # pylint: disable=no-member - return openvino.tools.ovc.InputCutInfo(input.name, - PartialShape(input.shape) if input.shape is not None else None, - input.type, - input.value) - if isinstance(input, (tuple, list, PartialShape)): + return _InputCutInfo(node_name, + PartialShape(shape) if shape is not None else None, + data_type) + if isinstance(input, (tuple, list)) or is_shape_type(input): # If input represents list with shape, wrap it to list. Single PartialShape also goes to this condition. # Check of all dimensions will be in is_shape_type(val) method below if len(input) > 0 and isinstance(input[0], (int, Dimension)): @@ -85,19 +77,18 @@ def single_input_to_input_cut_info(input: [str, tuple, list, PartialShape, Type, raise Exception("Incorrect input parameters provided. Expected tuple with input name, " "input type or input shape. Got unknown object: {}".format(val)) # pylint: disable=no-member - return openvino.tools.ovc.InputCutInfo(name, - PartialShape(shape) if shape is not None else None, - inp_type, - None) + return _InputCutInfo(name, + PartialShape(shape) if shape is not None else None, + inp_type, + None) # Case when only type is set if isinstance(input, (type, Type)): - return openvino.tools.ovc.InputCutInfo(None, None, input, None) # pylint: disable=no-member + return _InputCutInfo(None, None, input, None) # pylint: disable=no-member # We don't expect here single unnamed value. If list of int is set it is considered as shape. # Setting of value is expected only using InputCutInfo or string analog. - raise Exception("Unexpected object provided for input. Expected openvino.tools.ovc.InputCutInfo " - "or tuple or str. Got {}".format(type(input))) + raise Exception("Unexpected object provided for input. Expected tuple, Shape, PartialShape, Type or str. Got {}".format(type(input))) def input_to_input_cut_info(input: [str, tuple, list]): @@ -114,17 +105,12 @@ def input_to_input_cut_info(input: [str, tuple, list]): for input_value in split_inputs(input): # Parse string with parameters for single input - node_name, shape, value, data_type = parse_input_value(input_value) + node_name, shape, data_type = parse_input_value(input_value) # pylint: disable=no-member - inputs.append(openvino.tools.ovc.InputCutInfo(node_name, - PartialShape(shape) if shape is not None else None, - data_type, - value)) + inputs.append(_InputCutInfo(node_name, + PartialShape(shape) if shape is not None else None, + data_type)) return inputs - # pylint: disable=no-member - if isinstance(input, openvino.tools.ovc.InputCutInfo): - # Wrap to list and return - return [input] if isinstance(input, tuple): # Case when input is single shape set in tuple if len(input) > 0 and isinstance(input[0], (int, Dimension)): @@ -140,6 +126,17 @@ def input_to_input_cut_info(input: [str, tuple, list]): for inp in input: inputs.append(single_input_to_input_cut_info(inp)) return inputs + if isinstance(input, dict): + res_list = [] + for name, value in input.items(): + if not isinstance(name, str): + raise Exception("Incorrect operation name type. Expected string, got {}".format(type(name))) + info = single_input_to_input_cut_info(value) + if info.name is not None and info.name != name: + raise Exception("Incorrect \"input\" dictionary, got different names in key and value. " + "Got operation name {} for key {}".format(info.name, name)) + res_list.append(_InputCutInfo(name, info.shape, info.type)) + return res_list # Case when single type or value is set, or unknown object return [single_input_to_input_cut_info(input)] @@ -443,7 +440,7 @@ def _format_usage(self, usage, actions, groups, prefix): usage = argparse.HelpFormatter._format_usage(self, usage, actions, groups, prefix) usage = usage[0:usage.find('INPUT_MODEL')].rstrip() + '\n' insert_idx = usage.find(self._prog) + len(self._prog) - usage = usage[0: insert_idx] + ' INPUT_MODEL ' + usage[insert_idx + 1:] + usage = usage[0: insert_idx] + ' INPUT_MODEL... ' + usage[insert_idx + 1:] return usage def _get_default_metavar_for_optional(self, action): @@ -473,12 +470,20 @@ def get_common_cli_parser(parser: argparse.ArgumentParser = None): add_args_by_description(parser, mo_convert_params_common) return parser +def input_model_details(model): + if isinstance(model, (list, tuple)) and len(model) == 1: + model = model[0] + if isinstance(model, (str, pathlib.Path)): + return model + return type(model) + -def get_common_cli_options(model_name): +def get_common_cli_options(argv, is_python_api_used): d = OrderedDict() - d['input_model'] = '- Path to the Input Model' - d['output_dir'] = ['- Path for generated IR', lambda x: x if x != '.' else os.getcwd()] # TODO: Consider removing - d['output_model'] = ['- IR output name', lambda x: x if x else model_name] + d['input_model'] = ['- Path to the Input Model', input_model_details] + if not is_python_api_used: + model_name = get_model_name_from_args(argv) + d['output_model'] = ['- IR output name', lambda _: model_name] d['log_level'] = '- Log level' d['input'] = ['- Input layers', lambda x: x if x else 'Not specified, inherited from the model'] d['output'] = ['- Output layers', lambda x: x if x else 'Not specified, inherited from the model'] @@ -530,7 +535,8 @@ def remove_shape_from_input_value(input_value: str): :param input_value: string passed as input to the "input" command line parameter :return: string without shape specification """ - assert '->' not in input_value, 'The function should not be called for input_value with constant value specified' + if '->' in input_value: + raise Error('Incorrect format of input. Got {}'.format(input_value)) return re.sub(r'[(\[]([0-9\.?, -]*)[)\]]', '', input_value) @@ -540,8 +546,6 @@ def get_shape_from_input_value(input_value: str): :param input_value: string passed as input to the "input" command line parameter :return: the corresponding shape and None if the shape is not specified in the input value """ - # remove the tensor value from the input_value first - input_value = input_value.split('->')[0] # parse shape shape = re.findall(r'[(\[]([0-9\.\?, -]*)[)\]]', input_value) @@ -556,7 +560,7 @@ def get_shape_from_input_value(input_value: str): shape = PartialShape([Dimension(dim) for dim in dims]) else: raise Error("Wrong syntax to specify shape. Use \"input\" " - "\"node_name[shape]->value\"") + "\"node_name[shape]\"") return shape @@ -566,33 +570,7 @@ def get_node_name_with_port_from_input_value(input_value: str): :param input_value: string passed as input to the "input" command line parameter :return: the corresponding node name with input/output port """ - return remove_shape_from_input_value(remove_data_type_from_input_value(input_value.split('->')[0])) - - -def get_value_from_input_value(input_value: str): - """ - Returns the value from the input value string - :param input_value: string passed as input to the "input" command line parameter - :return: the corresponding value or None if it is not specified - """ - parts = input_value.split('->') - value = None - if len(parts) == 2: - value = parts[1] - if value[0] == '[' and value[-1] != ']' or value[0] != '[' and value[-1] == ']': - raise Error("Wrong syntax to specify value. Use \"input\"=\"node_name[shape]->value\"") - if '[' in value.strip(' '): - value = value.replace('[', '').replace(']', '') - if ',' in value: - value = value.replace(' ', '') - value = value.split(',') - else: - value = value.split(' ') - if not isinstance(value, list): - value = ast.literal_eval(value) - elif len(parts) > 2: - raise Error("Wrong syntax to specify value. Use \"input\"=\"node_name[shape]->value\"") - return value + return remove_shape_from_input_value(remove_data_type_from_input_value(input_value)) def partial_shape_prod(shape: [PartialShape, tuple]): @@ -614,7 +592,7 @@ def parse_input_value(input_value: str): ---------- input_value string with a specified node name, shape, value and data_type. - E.g. 'node_name:0[4]{fp32}->[1.0 2.0 3.0 4.0]' + E.g. 'node_name:0[4]{fp32}' Returns ------- @@ -623,19 +601,9 @@ def parse_input_value(input_value: str): """ data_type = get_data_type_from_input_value(input_value) node_name = get_node_name_with_port_from_input_value(input_value) - value = get_value_from_input_value(input_value) shape = get_shape_from_input_value(input_value) - value_size = np.prod(len(value)) if isinstance(value, list) else 1 - - if value is not None and shape is not None: - for dim in shape: - if isinstance(dim, Dimension) and dim.is_dynamic: - raise Error("Cannot freeze input with dynamic shape: {}".format(shape)) - if shape is not None and value is not None and partial_shape_prod(shape) != value_size: - raise Error("The shape '{}' of the input node '{}' does not correspond to the number of elements '{}' in the " - "value: {}".format(shape, node_name, value_size, value)) - return node_name if node_name else None, shape, value, data_type + return node_name if node_name else None, shape, data_type def split_str_avoiding_square_brackets(s: str) -> list: @@ -818,65 +786,6 @@ def get_layout_values(argv_layout: str = '', argv_source_layout: str = '', argv_ return res_list -#TODO: Should be removed? -def parse_freeze_placeholder_values(argv_freeze_placeholder_with_value: str): - """ - Parses parse_freeze_placeholder_values string. - :param argv_freeze_placeholder_with_value: string information on freezing placeholders - :return: dictionary where key is node name, value is node value. - """ - placeholder_values = {} - if argv_freeze_placeholder_with_value is not None: - for plh_with_value in argv_freeze_placeholder_with_value.split(','): - plh_with_value = plh_with_value.split('->') - if len(plh_with_value) != 2: - raise Error("Wrong replacement syntax. Use \"freeze_placeholder_with_value\" " - "\"node1_name->value1,node2_name->value2\"") - node_name = plh_with_value[0] - value = plh_with_value[1] - if node_name in placeholder_values and placeholder_values[node_name] != value: - raise Error("Overriding replacement value of the placeholder with name '{}': old value = {}, new value = {}" - ".".format(node_name, placeholder_values[node_name], value)) - if '[' in value.strip(' '): - value = value.replace('[', '').replace(']', '').split(' ') - placeholder_values[node_name] = value - return placeholder_values - - -def get_freeze_placeholder_values(argv_input: str): - """ - Parses values for placeholder freezing and input node names - - Parameters - ---------- - argv_input - string with a list of input layers: either an empty string, or strings separated with comma. - 'node_name1[shape1]->value1,node_name2[shape2]->value2,...' - - Returns - ------- - parsed placeholders with values for freezing - input nodes cleaned from shape info - """ - placeholder_values = {} - input_node_names = None - - if argv_input is not None: - input_node_names = '' - # walkthrough all input values and save values for freezing - for input_value in split_inputs(argv_input): - node_name, _, value, _ = parse_input_value(input_value) - input_node_names = input_node_names + ',' + node_name if input_node_names != '' else node_name - if value is None: # no value is specified for freezing - continue - if node_name in placeholder_values and placeholder_values[node_name] != value: - raise Error("Overriding replacement value of the placeholder with name '{}': old value = {}, new value = {}" - ".".format(node_name, placeholder_values[node_name], value)) - placeholder_values[node_name] = value - - return placeholder_values, input_node_names - - def split_inputs(input_str): brakets_count = 0 inputs = [] @@ -942,13 +851,28 @@ def get_model_name(path_input_model: str) -> str: def get_model_name_from_args(argv: argparse.Namespace): + output_dir = os.getcwd() if hasattr(argv, 'output_model') and argv.output_model: model_name = argv.output_model - else: - model_name = argv.input_model - if isinstance(model_name, (tuple, list)) and len(model_name) > 0: - model_name = model_name[0] - return model_name + + if not os.path.isdir(argv.output_model): + if not model_name.endswith('.xml'): + model_name += '.xml' + return model_name + else: + if not os.access(argv.output_model, os.W_OK): + raise Error('The directory "{}" is not writable'.format(argv.output_model)) + output_dir = argv.output_model + + input_model = argv.input_model + if isinstance(input_model, (tuple, list)) and len(input_model) > 0: + input_model = input_model[0] + + if not isinstance(input_model, (str, pathlib.Path)): + return output_dir + + input_model_name = os.path.splitext(os.path.split(input_model)[1])[0] + return os.path.join(output_dir, input_model_name + ".xml") def get_absolute_path(path_to_file: str) -> str: @@ -1033,7 +957,7 @@ def check_bool(value): def depersonalize(value: str, key: str): dir_keys = [ - 'output_dir', 'extension', 'saved_model_dir', 'tensorboard_logdir', 'caffe_parser_path' + 'extension' ] if isinstance(value, list): updated_value = [] diff --git a/tools/ovc/openvino/tools/ovc/convert.py b/tools/ovc/openvino/tools/ovc/convert.py index ebeb37abd04b4b..393ca2977cc260 100644 --- a/tools/ovc/openvino/tools/ovc/convert.py +++ b/tools/ovc/openvino/tools/ovc/convert.py @@ -1,26 +1,21 @@ # Copyright (C) 2018-2023 Intel Corporation # SPDX-License-Identifier: Apache-2.0 -import os import pathlib -from collections import namedtuple from typing import Any -from openvino.runtime import PartialShape, Shape, Model # pylint: disable=no-name-in-module,import-error +from openvino.runtime import Model # pylint: disable=no-name-in-module,import-error +from openvino.tools.ovc.cli_parser import get_all_cli_parser from openvino.tools.ovc.convert_impl import _convert from openvino.tools.ovc.logger import get_logger_state, restore_logger_state -from openvino.tools.ovc.cli_parser import get_all_cli_parser - -#TODO: Why names InputCutInfo and InputInfo are different -InputCutInfo = namedtuple("InputInfo", ["name", "shape", "type", "value"], defaults=[None, None, None, None]) def convert_model( input_model: [str, pathlib.Path, Any, list], # TODO: Instead of list just accept arbitrary number of positional arguments # Framework-agnostic parameters - input: [str, list, tuple, InputCutInfo] = None, + input: [list, dict] = None, output: [str, list] = None, example_input: Any = None, extension: [str, pathlib.Path, list, Any] = None, @@ -63,27 +58,18 @@ def convert_model( paddle.fluid.executor.Executor :param input: - Input can be set by passing a list of InputCutInfo objects or by a list - of tuples. Each tuple can contain optionally input name, input - type or input shape. Example: input=("op_name", PartialShape([-1, - 3, 100, 100]), Type(np.float32)). Alternatively input can be set by - a string or list of strings of the following format. Quoted list of comma-separated - input nodes names with shapes, data types, and values for freezing. - If operation names are specified, the order of inputs in converted - model will be the same as order of specified operation names (applicable for TF2, ONNX, MxNet). - The shape and value are specified as comma-separated lists. The data type of input node is specified - in braces and can have one of the values: f64 (float64), f32 (float32), f16 (float16), i64 - (int64), i32 (int32), u8 (uint8), boolean (bool). Data type is optional. - If it's not specified explicitly then there are two options: if input - node is a parameter, data type is taken from the original node dtype, - if input node is not a parameter, data type is set to f32. Example, to set - `input_1` with shape [1,100], and Parameter node `sequence_len` with - scalar input with value `150`, and boolean input `is_training` with - `False` value use the following format: "input_1[1,100],sequence_len->150,is_training->False". - Another example, use the following format to set input port 0 of the node - `node_name1` with the shape [3,4] as an input node and freeze output - port 1 of the node `node_name2` with the value [20,15] of the int32 type - and shape [2]: "0:node_name1[3,4],node_name2:1[2]{i32}->[20,15]". + Information of model input required for model conversion. + Input can be set by a list of tuples or a dictionary. Each tuple can contain optionally input name (string), + input type (ov.Type, numpy.dtype) or input shape (ov.Shape, ov.PartialShape, list, tuple). + Example: input=("op_name", PartialShape([-1, 3, 100, 100]), ov.Type.f32). + Alternatively input can be set by a dictionary, where key - input name, + value - tuple with input parameters (shape or type). + Example 1: input={"op_name_1": ([1, 2, 3], ov.Type.f32), "op_name_2": ov.Type.i32} + Example 2: input=[("op_name_1", [1, 2, 3], ov.Type.f32), ("op_name_2", ov.Type.i32)] + Example 3: input=[([1, 2, 3], ov.Type.f32), ov.Type.i32] + The order of inputs in converted model will match the order of specified inputs. + If data type is not specified explicitly data type is taken from the original node data type. + :param output: The name of the output operation of the model or list of names. For TensorFlow*, do not add :0 to this name.The order of outputs in converted model is the @@ -96,9 +82,7 @@ def convert_model( :param extension: Paths to libraries (.so or .dll) with extensions, comma-separated list of paths, objects derived from BaseExtension class or lists of - objects. For the legacy MO path (if "use_legacy_frontend" is used), - a directory or a comma-separated list of directories with extensions - are supported. To disable all extensions including those that are placed + objects. To disable all extensions including those that are placed at the default location, pass an empty string. :param verbose: Print detailed information about conversion. diff --git a/tools/ovc/openvino/tools/ovc/convert_impl.py b/tools/ovc/openvino/tools/ovc/convert_impl.py index 7fd0e683f638a8..8c222cf4f4c1c5 100644 --- a/tools/ovc/openvino/tools/ovc/convert_impl.py +++ b/tools/ovc/openvino/tools/ovc/convert_impl.py @@ -21,9 +21,8 @@ from openvino.tools.ovc.moc_frontend.pipeline import moc_pipeline from openvino.tools.ovc.moc_frontend.moc_emit_ir import moc_emit_ir from openvino.tools.ovc.convert_data_type import destination_type_to_np_data_type -from openvino.tools.ovc.cli_parser import get_available_front_ends, \ - get_common_cli_options, get_model_name_from_args, depersonalize, get_mo_convert_params, \ - input_to_input_cut_info, freeze_placeholder_to_input_cut_info +from openvino.tools.ovc.cli_parser import get_available_front_ends, get_common_cli_options, depersonalize, \ + get_mo_convert_params, input_to_input_cut_info from openvino.tools.ovc.help import get_convert_model_help_specifics from openvino.tools.ovc.error import Error, FrameworkError @@ -57,10 +56,10 @@ def replace_ext(name: str, old: str, new: str): return base + new -def print_argv(argv: argparse.Namespace, model_name: str): +def print_argv(argv: argparse.Namespace): print('Model Conversion arguments:') props = OrderedDict() - props['common_args'] = get_common_cli_options(model_name) + props['common_args'] = get_common_cli_options(argv, argv.is_python_api_used) framework_specifics_map = { 'common_args': 'Common parameters:' @@ -80,10 +79,11 @@ def print_argv(argv: argparse.Namespace, model_name: str): def arguments_post_parsing(argv: argparse.Namespace): # TODO: This function looks similar to another one. Check for code duplicates. log.debug("Model Conversion API started") - log.debug('Output model name would be {}{{.xml, .bin}}'.format(argv.output_model)) + if not argv.is_python_api_used: + log.debug('Output model name would be {}{{.xml, .bin}}'.format(argv.output_model)) if argv.verbose: - print_argv(argv, argv.output_model) + print_argv(argv) params_parsing(argv) argv.output = argv.output.split(',') if isinstance(argv.output, (str, pathlib.Path)) else argv.output @@ -287,10 +287,6 @@ def params_parsing(argv: argparse.Namespace): argv.placeholder_data_types - dictionary where key is node name, value is node np.type, or list of np.types if node names were not set. - argv.freeze_placeholder_with_value - dictionary where key is node name, value is np.ndarray - - argv.unnamed_freeze_placeholder_with_value - list with np.ndarray - :param argv: MO arguments """ # Parse input to list of InputCutInfo @@ -308,13 +304,6 @@ def params_parsing(argv: argparse.Namespace): argv.inputs_list = input_names_list argv.input = ','.join(input_names_list) - # Parse freeze_placeholder_with_value. - # values for freezing can be set both by named and unnamed approach if - # 'input' was used without names and 'freeze_placeholder_with_value' was used with names. - # So named and unnamed values are stored separately. - argv.freeze_placeholder_with_value, argv.unnamed_freeze_placeholder_with_value = \ - freeze_placeholder_to_input_cut_info(inputs) - if len(input_names_list) > 0: # Named inputs case shape_dict = {} @@ -401,10 +390,6 @@ def is_verbose(argv: argparse.Namespace): def _convert(cli_parser: argparse.ArgumentParser, args, python_api_used): - # FIXME: It doesn't work when -h is passed - if 'help' in args and args['help']: - show_mo_convert_help() - return None, None simplified_ie_version = VersionChecker().get_ie_simplified_version() telemetry = init_mo_telemetry() telemetry.start_session('ovc') @@ -460,11 +445,6 @@ def _convert(cli_parser: argparse.ArgumentParser, args, python_api_used): non_default_params = get_non_default_params(argv, cli_parser) argv.is_python_api_used = python_api_used - if inp_model_is_object: - argv.output_model = "model" # TODO: Consider removing - if not hasattr(argv, "output_model") or argv.output_model is None: - argv.output_model = get_model_name_from_args(argv) - argv.framework = model_framework ov_model = driver(argv, {"conversion_parameters": non_default_params}) @@ -494,7 +474,7 @@ def _convert(cli_parser: argparse.ArgumentParser, args, python_api_used): if isinstance(e, (FileNotFoundError, NotADirectoryError)): log.error('File {} was not found'.format(str(e).split('No such file or directory:')[1])) log.debug(traceback.format_exc()) - elif isinstance(e, Error): + elif isinstance(e, (Error, OpConversionFailure)): log.error(e) log.debug(traceback.format_exc()) elif isinstance(e, FrameworkError): diff --git a/tools/ovc/openvino/tools/ovc/help.py b/tools/ovc/openvino/tools/ovc/help.py index 1921fddf94b407..53bf2171f6fdf1 100644 --- a/tools/ovc/openvino/tools/ovc/help.py +++ b/tools/ovc/openvino/tools/ovc/help.py @@ -8,77 +8,28 @@ def get_convert_model_help_specifics(): 'input_model': {'description': 'Input model file(s) from TensorFlow, ONNX, PaddlePaddle. ' - 'Use openvino.convert_model in Python to convert models from Pytorch.' + 'Use openvino.convert_model in Python to convert models from PyTorch.' '', 'action': CanonicalizePathCheckExistenceAction, 'type': readable_dirs_or_files_or_empty, 'aliases': {}}, - 'input_shape': - {'description': - 'Input shape(s) that should be fed to an input node(s) ' - 'of the model. Shape is defined as a comma-separated ' - 'list of integer numbers enclosed in parentheses or ' - 'square brackets, for example [1,3,227,227] or ' - '(1,227,227,3), where the order of dimensions depends ' - 'on the framework input layout of the model. For ' - 'example, [N,C,H,W] is used for ONNX* models and ' - '[N,H,W,C] for TensorFlow* models. The shape can ' - 'contain undefined dimensions (? or -1) and should fit ' - 'the dimensions defined in the input operation of the ' - 'graph. Boundaries of undefined dimension can be ' - 'specified with ellipsis, for example ' - '[1,1..10,128,128]. One boundary can be undefined, for ' - 'example [1,..100] or [1,3,1..,1..]. If there are ' - 'multiple inputs in the model, --input_shape should ' - 'contain definition of shape for each input separated ' - 'by a comma, for example: [1,3,227,227],[2,4] for a ' - 'model with two inputs with 4D and 2D shapes. ' - 'Alternatively, specify shapes with the --input option.'}, 'input': {'description': - 'Information for model input required for model conversion. ' + 'Information of model input required for model conversion. ' 'This is a comma separated list with optional ' - 'input names, shapes, data types, and values for freezing. ' - 'The order of inputs in converted model will match the order of ' - 'specified inputs. The shape and value are ' - 'specified as comma-separated lists. The data type of ' - 'input node is specified in braces and can have one of ' - 'the values: f64 (float64), f32 (float32), f16 ' - '(float16), i64 (int64), i32 (int32), u8 (uint8), ' - 'boolean (bool). Data type is optional. If it\'s not ' - 'specified explicitly then there are two options: if ' - 'input node is a parameter, data type is taken from the ' - 'original node dtype, if input node is not a parameter, ' - 'data type is set to f32. Example, to set `input_1` ' - 'with shape [1,100], and Parameter node `sequence_len` ' - 'with scalar input with value `150`, and boolean input ' - '`is_training` with `False` value use the following ' - 'format: \n ' - '\"input_1[1,100],sequence_len->150,is_training->False\". ' - 'Another example, use the following format to set input ' - 'port 0 of the node `node_name1` with the shape [3,4] ' - 'as an input node and freeze output port 1 of the node ' - '\"node_name2\" with the value [20,15] of the int32 type ' - 'and shape [2]: \n ' - '\"0:node_name1[3,4],node_name2:1[2]{i32}->[20,15]\".'}, - 'transform': - {'description': - 'Apply additional transformations. Usage: \"--transform ' - 'transformation_name1[args],transformation_name2...\" ' - 'where [args] is key=value pairs separated by ' - 'semicolon. Examples: \"--transform LowLatency2\" or \"--' - 'transform Pruning" or "--transform ' - 'LowLatency2[use_const_initializer=False]" or "--' - 'transform "MakeStateful[param_res_names= {\'input_name_1\':' - '\'output_name_1\',\'input_name_2\':\'output_name_2\'}]\" \n' - 'Available transformations: "LowLatency2", "MakeStateful", "Pruning"'}, + 'input names, shapes and data types. The order of inputs ' + 'in converted model will match the order of ' + 'specified inputs. The shape is specified as comma-separated list. ' + 'The data type of input node is specified in braces and can have one of ' + 'the values: f64, f32, f16, i64, i32, u8, boolean. If data type is not ' + 'specified explicitly then data type is taken from the ' + 'original node data type. Example, to set `input_1` input ' + 'with shape [1,100] and float32 type, and `sequence_len` input ' + 'with int32 type \"input_1[1,100]{f32},sequence_len{i32}\".'}, 'extension': {'description': 'Paths or a comma-separated list of paths to libraries ' - '(.so or .dll) with extensions. For the legacy MO path ' - '(if `--use_legacy_frontend` is used), a directory or a ' - 'comma-separated list of directories with extensions ' - 'are supported. To disable all extensions including ' + '(.so or .dll) with extensions. To disable all extensions including ' 'those that are placed at the default location, pass an empty string.', 'action': CanonicalizePathCheckExistenceAction, 'type': readable_files_or_empty}, diff --git a/tools/ovc/openvino/tools/ovc/main.py b/tools/ovc/openvino/tools/ovc/main.py index e5afa079b0ff48..45f26c210c0609 100644 --- a/tools/ovc/openvino/tools/ovc/main.py +++ b/tools/ovc/openvino/tools/ovc/main.py @@ -10,7 +10,7 @@ except ImportError: import openvino.tools.ovc.telemetry_stub as tm from openvino.tools.ovc.convert_impl import _convert -from openvino.tools.ovc.utils import get_ir_version +from openvino.tools.ovc.cli_parser import get_model_name_from_args # pylint: disable=no-name-in-module,import-error from openvino.runtime import serialize @@ -22,9 +22,7 @@ def main(): if ngraph_function is None: return 1 - output_dir = os.getcwd() - model_path_no_ext = os.path.normpath(os.path.join(output_dir, argv.output_model)) - model_path = model_path_no_ext + '.xml' + model_path = get_model_name_from_args(argv) # TODO: replace compress_model + serialize with save_model if argv.compress_to_fp16: diff --git a/tools/ovc/openvino/tools/ovc/moc_frontend/check_config.py b/tools/ovc/openvino/tools/ovc/moc_frontend/check_config.py index 77a27870e31f7b..c891e43e50b202 100644 --- a/tools/ovc/openvino/tools/ovc/moc_frontend/check_config.py +++ b/tools/ovc/openvino/tools/ovc/moc_frontend/check_config.py @@ -97,8 +97,3 @@ def legacy_transformations_config_used(argv: argparse.Namespace): def tensorflow_custom_operations_config_update_used(argv: argparse.Namespace): return hasattr(argv, 'tensorflow_custom_operations_config_update') and \ argv.tensorflow_custom_operations_config_update is not None - - -def input_freezig_used(argv): - return hasattr(argv, 'freeze_placeholder_with_value') and argv.freeze_placeholder_with_value is not None \ - and len(argv.freeze_placeholder_with_value) > 0 diff --git a/tools/ovc/openvino/tools/ovc/moc_frontend/extractor.py b/tools/ovc/openvino/tools/ovc/moc_frontend/extractor.py index 81056b5d534f4d..638acc0505a67d 100644 --- a/tools/ovc/openvino/tools/ovc/moc_frontend/extractor.py +++ b/tools/ovc/openvino/tools/ovc/moc_frontend/extractor.py @@ -336,16 +336,15 @@ def fe_output_user_data_repack(input_model: InputModel, outputs: list, framework node = decode_name_with_port(input_model, output, framework, IOType.Output) if node is None: raise Error("Cannot find location {} in the graph".format(output)) - _outputs.append({"node": node}) + _outputs.append({"node": node, "output_name": output}) return _outputs -def find_first_unused_input(model_inputs: list, freeze_placeholder: dict, param_dict: dict, param_name: str): +def find_first_unused_input(model_inputs: list, param_dict: dict, param_name: str): """ Finds first input in model_inputs, which is not present in freeze_placeholder dictionary or param_dict. :param model_inputs: list of model inputs - :param freeze_placeholder: dictionary where key is input name, value is input value for freezing. :param param_dict: dictionary where key is input name, value is parameter value (shape or type). :param param_name: name of parameter used in exception message. @@ -355,7 +354,7 @@ def find_first_unused_input(model_inputs: list, freeze_placeholder: dict, param_ input_names = inp.get_names() name_found = False for input_name in input_names: - if input_name in freeze_placeholder or input_name in param_dict: + if input_name in param_dict: name_found = True break if name_found: @@ -366,17 +365,13 @@ def find_first_unused_input(model_inputs: list, freeze_placeholder: dict, param_ def convert_params_lists_to_dicts(input_model, input_user_shapes: [list, dict], - input_user_data_types: [list, dict], - freeze_placeholder: dict, - unnamed_freeze_placeholders: list): + input_user_data_types: [list, dict]): """ Convert lists of unnamed params to dicts using input names from input_model. :param input_model: openvino.runtime.InputModel :param input_user_shapes: list of input shapes or dictionary where key is input name, value is input shape from user. :param input_user_data_types: list of input types or dictionary where key is input name, value is input type from user. - :param freeze_placeholder: dictionary where key is input name, value is input value from user. - :param unnamed_freeze_placeholders: list of unnamed input values from user. :return: (input_user_shapes_dict, input_user_data_types_dict, freeze_placeholder), where input_user_shapes_dict - dictionary where key is input name, value is shape from user; @@ -395,7 +390,7 @@ def convert_params_lists_to_dicts(input_model, for idx, shape in enumerate(input_user_shapes): assert isinstance(shape, PartialShape), "Got incorrect format of input shapes {}.".format(type(shape)) - inp_name = find_first_unused_input(model_inputs, freeze_placeholder, input_user_shapes_dict, "shape") + inp_name = find_first_unused_input(model_inputs, input_user_shapes_dict, "shape") input_user_shapes_dict[inp_name] = shape else: input_user_shapes_dict = input_user_shapes @@ -413,7 +408,7 @@ def convert_params_lists_to_dicts(input_model, "Expected numpy type or openvino.runtime.Type, " \ "got {}.".format(type(node_type)) - inp_name = find_first_unused_input(model_inputs, freeze_placeholder, input_user_data_types_dict, "type") + inp_name = find_first_unused_input(model_inputs, input_user_data_types_dict, "type") input_user_data_types_dict[inp_name] = node_type # FE postprocessing expects input_user_shapes_dict to always have shapes for corresponding types. # If shape is not set it is expected to have None shape in input_user_shapes_dict dictionary. @@ -422,15 +417,8 @@ def convert_params_lists_to_dicts(input_model, else: input_user_data_types_dict = input_user_data_types - # unnamed_freeze_placeholders is always list, it is not empty only if unnamed inputs were used. - for value in unnamed_freeze_placeholders: - assert isinstance(value, list), "Got incorrect format of input values. " \ - "Expected list, " \ - "got {}.".format(type(value)) - inp_name = find_first_unused_input(model_inputs, freeze_placeholder, {}, "input value") - freeze_placeholder[inp_name] = value - return input_user_shapes_dict, input_user_data_types_dict, freeze_placeholder + return input_user_shapes_dict, input_user_data_types_dict def fe_user_data_repack( diff --git a/tools/ovc/openvino/tools/ovc/moc_frontend/pipeline.py b/tools/ovc/openvino/tools/ovc/moc_frontend/pipeline.py index a364239b182c39..58cc5a56b779ee 100644 --- a/tools/ovc/openvino/tools/ovc/moc_frontend/pipeline.py +++ b/tools/ovc/openvino/tools/ovc/moc_frontend/pipeline.py @@ -38,6 +38,17 @@ def get_enabled_and_disabled_transforms(): return enabled_transforms, disabled_transforms +def raise_exception_for_input_output_cut(model_inputs_or_outputs: List[Place], new_nodes: List[dict], is_input: bool): + for new_node in new_nodes: + node = new_node['node'] + + if not any([item.is_equal(node) for item in model_inputs_or_outputs]): + if is_input: + raise Exception("Name {} is not found among model inputs.".format(new_node['input_name'])) + else: + raise Exception("Name {} is not found among model outputs.".format(new_node['output_name'])) + + def moc_pipeline(argv: argparse.Namespace, moc_front_end: FrontEnd): """ Load input model and convert it to nGraph function @@ -62,13 +73,12 @@ def moc_pipeline(argv: argparse.Namespace, moc_front_end: FrontEnd): outputs = fe_output_user_data_repack(input_model, argv.output, moc_front_end.get_name()) input_model.override_all_outputs([x['node'] for x in outputs]) ''' - argv.placeholder_shapes, argv.placeholder_data_types, argv.freeze_placeholder_with_value = convert_params_lists_to_dicts( - input_model, argv.placeholder_shapes, argv.placeholder_data_types, - argv.freeze_placeholder_with_value, argv.unnamed_freeze_placeholder_with_value) + argv.placeholder_shapes, argv.placeholder_data_types = convert_params_lists_to_dicts( + input_model, argv.placeholder_shapes, argv.placeholder_data_types) user_shapes, outputs, freeze_placeholder = fe_user_data_repack( input_model, argv.placeholder_shapes, argv.placeholder_data_types, - argv.output, argv.freeze_placeholder_with_value, moc_front_end.get_name()) + argv.output, {}, moc_front_end.get_name()) def check_places_are_same(places_original: List[Place], places_new: List[Place]): """ @@ -109,10 +119,17 @@ def add_names_to_tensors(model: InputModel, places: List[Place]): model_inputs = input_model.get_inputs() inputs_equal = True if user_shapes: + + # TODO: Remove this line when new 'cut' helper is introduced + raise_exception_for_input_output_cut(model_inputs, user_shapes, True) + inputs_equal = check_places_are_same(model_inputs, user_shapes) outputs_equal = True if outputs: + # TODO: Remove this line when new 'cut' helper is introduced + raise_exception_for_input_output_cut(input_model.get_outputs(), outputs, False) + outputs_equal = check_places_are_same(input_model.get_outputs(), outputs) log.debug('Inputs are same: {}, outputs are same: {}'.format( inputs_equal, outputs_equal)) @@ -138,7 +155,7 @@ def create_target_input_shapes(new_input_places): user_shapes, outputs, _ = fe_user_data_repack( input_model, placeholder_shapes, argv.placeholder_data_types, - new_output_places_name, argv.freeze_placeholder_with_value, moc_front_end.get_name()) + new_output_places_name, {}, moc_front_end.get_name()) elif not inputs_equal: log.debug('Using override_all_inputs') add_names_to_tensors(input_model, user_shapes) @@ -150,7 +167,7 @@ def create_target_input_shapes(new_input_places): user_shapes, outputs, _ = fe_user_data_repack( input_model, placeholder_shapes, argv.placeholder_data_types, - argv.output, argv.freeze_placeholder_with_value, moc_front_end.get_name()) + argv.output, {}, moc_front_end.get_name()) elif not outputs_equal: log.debug('Using override_all_outputs') add_names_to_tensors(input_model, user_shapes) @@ -226,11 +243,6 @@ def create_target_input_shapes(new_input_places): def shape_to_array(shape: PartialShape): return [shape.get_dimension(i) for i in range(shape.rank.get_length())] - # obtain layout for all inputs - layout_values = {} - if 'layout_values' in argv and argv.layout_values: - layout_values = update_layout_to_dict(model_inputs, argv.layout_values, - lambda input_place: input_place.get_names()) ov_model = moc_front_end.convert(input_model) diff --git a/tools/ovc/unit_tests/moc_tf_fe/check_info_messages_test.py b/tools/ovc/unit_tests/moc_tf_fe/check_info_messages_test.py index 87232ed7bd7c18..0e7c0155022fb1 100644 --- a/tools/ovc/unit_tests/moc_tf_fe/check_info_messages_test.py +++ b/tools/ovc/unit_tests/moc_tf_fe/check_info_messages_test.py @@ -29,7 +29,6 @@ def arg_parse_helper(input_model, transform=[], output=None, input=None, - output_dir='.', compress_to_fp16=compress_to_fp16, extension=None ) diff --git a/tools/ovc/unit_tests/moc_tf_fe/conversion_basic_models_test.py b/tools/ovc/unit_tests/moc_tf_fe/conversion_basic_models_test.py index f8a2afa8415967..ba2a1a8a652c4f 100644 --- a/tools/ovc/unit_tests/moc_tf_fe/conversion_basic_models_test.py +++ b/tools/ovc/unit_tests/moc_tf_fe/conversion_basic_models_test.py @@ -35,132 +35,23 @@ def basic(self, input_model, argv_input, inputs, dtype, expected, only_conversio assert np.allclose(values, expected) @generate( - *[ - ( - "in1[1 4]->[1.0 2.0 3.0 4.0],in2[1 4]{f32}->[1.0 2.0 3.0 4.0]", - {}, - np.array([2.0, 4.0, 6.0, 8.0]), - np.float32, - ), - ( - "in2{f32}->[0.0 0.0 0.0 0.0]", - {"in1": np.array([[1.0, 2.0], [3.0, 4.0]])}, - np.array([[1.0, 2.0], [3.0, 4.0]]), - np.float32, - ), - ( - "in2->[1.0 15.0 15.5 1.0]", - {"in1": np.array([[2.0, 4.0], [12.0, 8.0]])}, - np.array([[3.0, 19.0], [27.5, 9.0]]), - np.float32, - ), - ( - "in1[1 4]{i32}->[1 2 3 4],in2[1 4]{i32}->[1 2 3 4]", - {}, - np.array([2.0, 4.0, 6.0, 8.0]), - np.int32, - ), - ], - ) - def test_fp32(self, input_freezing_value, inputs, expected, - dtype): - self.basic("model_fp32.pbtxt", input_freezing_value, inputs, dtype, expected) - - @generate( - *[ - ( - "in1[1 4]->[1 2 3 4],in2[1 4]{i32}->[1 2 3 4]", - {}, - np.array([1, 4, 9, 16]), - np.int32, - ), - ( - "in2->[2 5 6 7 3 2]", - {"in1": np.array([[2, 4, 1], [1, 2, 8]])}, - np.array([[4, 20, 6], [7, 6, 16]]), - np.int32, - ), - ], - ) - def test_int32(self, input_freezing_value, inputs, expected, - dtype=None): - self.basic("model_int32.pbtxt", input_freezing_value, inputs, dtype, expected) - - @generate( - *[ - ( - "in1[2]->[True False],in2[2]->[True True]", - {}, - np.array([True, False], dtype=bool), - bool, - ), - ( - "in2[2,3]->[True,True,False,True,True,False]", - {"in1": np.array([[False, True, True], [False, True, True]], dtype=bool)}, - np.array([[False, True, False], [False, True, False]], dtype=bool), - bool, - ), - ( - "in2[]->True", - {"in1": np.array([[False, True, True], [False, True, True]], dtype=bool)}, - np.array([[False, True, True], [False, True, True]], dtype=bool), - bool, - ), - ], - ) - def test_bool(self, input_freezing_value, inputs, expected, - dtype=None): - self.basic("model_bool.pbtxt", input_freezing_value, inputs, dtype, expected) - - @generate( - *[ - ( - "in1[3]->[1 2 3],in2[3]->[4 5 6],cond->False", - {}, - np.array([4, 5, 6], dtype=np.float32), - np.float32, - None - ), - ( - "in1,in2,cond->False", - {"in1": np.array([2.0, 4.0, 6.0], dtype=np.float32), - "in2": np.array([1.0, 3.0, 5.0], dtype=np.float32)}, - np.array([2, 4, 6], dtype=np.float32), - np.float32, - True # fill a bug to investigate why compilation of this model is hang on - ), - # case: input_shape + freeze_placeholder_with_value - ( - "in2,in1->[2.0 4.0 6.0],cond->True", - {"in2": np.array([1.0, 3.0, 5.0], dtype=np.float32)}, - np.array([2, 4, 6], dtype=np.float32), - np.float32, - False - ), - ], - ) - def test_bool2(self, input_freezing_value, inputs, expected, - dtype=None, only_conversion=False): - self.basic("model_bool2.pbtxt", input_freezing_value, inputs, dtype, expected, - only_conversion) - - @generate( - *[ - ( - "add:0[3],z", - {"add:0": np.array([4, 5, 6], dtype=np.float32), "z": np.array([1, 2, 3], dtype=np.float32)}, - np.array([4, 10, 18], dtype=np.float32), - np.float32, - None - ), - ( - "add:0{i32}[3],z{i32}", - {"add:0": np.array([4, 5, 6], dtype=np.int32), "z": np.array([1, 2, 3], dtype=np.int32)}, - np.array([4, 10, 18], dtype=np.int32), - np.int32, - None - ), - ], + # TODO: Return this test when new 'cut' helper is introduced + # *[ + # ( + # "add:0[3],z", + # {"add:0": np.array([4, 5, 6], dtype=np.float32), "z": np.array([1, 2, 3], dtype=np.float32)}, + # np.array([4, 10, 18], dtype=np.float32), + # np.float32, + # None + # ), + # ( + # "add:0{i32}[3],z{i32}", + # {"add:0": np.array([4, 5, 6], dtype=np.int32), "z": np.array([1, 2, 3], dtype=np.int32)}, + # np.array([4, 10, 18], dtype=np.int32), + # np.int32, + # None + # ), + # ], ) def test_cutting_fp32(self, input_freezing_value, inputs, expected, dtype=None, only_conversion=False): @@ -169,20 +60,21 @@ def test_cutting_fp32(self, input_freezing_value, inputs, expected, @generate( *[ - ( - "x[1,4],y[4]", - {"x": np.array([[3, 2, 1, 5]], dtype=np.int32), "y": np.array([0, -1, -7, 8], dtype=np.int32)}, - np.array([[3, 1, -6, 13]], dtype=np.int32), - np.int32, - None - ), - ( - "x,y", - {"x": np.array([[-3, 20, 1]], dtype=np.int32), "y": np.array([[10, -11, -17]], dtype=np.int32)}, - np.array([[7, 9, -16]], dtype=np.int32), - np.int32, - None - ), + # TODO: Return this test when new 'cut' helper is introduced + # ( + # "x[1,4],y[4]", + # {"x": np.array([[3, 2, 1, 5]], dtype=np.int32), "y": np.array([0, -1, -7, 8], dtype=np.int32)}, + # np.array([[3, 1, -6, 13]], dtype=np.int32), + # np.int32, + # None + # ), + # ( + # "x,y", + # {"x": np.array([[-3, 20, 1]], dtype=np.int32), "y": np.array([[10, -11, -17]], dtype=np.int32)}, + # np.array([[7, 9, -16]], dtype=np.int32), + # np.int32, + # None + # ), ( "x", {"x": np.array([[-3, 20, 1]], dtype=np.int32)}, @@ -197,28 +89,6 @@ def test_placeholder_with_default(self, inputs, inputs_data, expected, self.basic("placeholder_with_default.pbtxt", inputs, inputs_data, dtype, expected, only_conversion) - @generate( - *[ - ( - "x[4],y->2.0", - {"x": np.array([3, 2, 1, 5], dtype=np.float32)}, - np.array([6, 4, 2, 10], dtype=np.float32), - np.float32, - None - ), - ( - "x[1],y->[2.0,3.0]", - {"x": np.array([3], dtype=np.float32)}, - np.array([6, 9], dtype=np.float32), - np.float32, - None - ), - ], - ) - def test_freeze_placeholder_with_unknown_rank(self, inputs, inputs_data, expected, - dtype=None, only_conversion=False): - self.basic("mul_with_unknown_rank_y.pbtxt", inputs, inputs_data, dtype, expected, - only_conversion) def test_conversion_model_oneshot_iterator_default(self): self.basic("model_oneshot_iterator.pbtxt", None, None, None, None, True) diff --git a/tools/ovc/unit_tests/ovc/convert/import_from_mo_test.py b/tools/ovc/unit_tests/ovc/convert/import_from_mo_test.py index 49284fc501aef9..fa9b08d30f01ed 100644 --- a/tools/ovc/unit_tests/ovc/convert/import_from_mo_test.py +++ b/tools/ovc/unit_tests/ovc/convert/import_from_mo_test.py @@ -7,7 +7,6 @@ from generator import generator, generate from openvino.runtime import serialize -from openvino.tools.ovc import InputCutInfo from unit_tests.ovc.unit_test_with_mocked_telemetry import UnitTestWithMockedTelemetry from unit_tests.ovc.convert.utils import create_onnx_model, save_to_onnx @@ -56,8 +55,7 @@ def create_onnx_model(): return onnx_net @generate(*[ - ({}), - ({'input': InputCutInfo(name='LeakyRelu_out', shape=None, type=None, value=None)}), + ({}) ]) # Checks convert import from openvino.tools.mo def test_import(self, params): diff --git a/tools/ovc/unit_tests/ovc/convert/meta_data_test.py b/tools/ovc/unit_tests/ovc/convert/meta_data_test.py index 60261fc8c88d4a..3c7dae9d0b1cc1 100644 --- a/tools/ovc/unit_tests/ovc/convert/meta_data_test.py +++ b/tools/ovc/unit_tests/ovc/convert/meta_data_test.py @@ -71,7 +71,7 @@ def check_meta_data(ov_model): if key == 'conversion_parameters': for param_name, param_value in value.items(): val = ov_model.get_rt_info([key, param_name]).astype(str) - if param_name in ['extension', 'caffe_parser_path', 'input_model', 'k', 'output_dir']: + if param_name in ['extension', 'input_model']: val = Path(val) assert val == param_value, \ "Runtime info attribute with name {} does not match. Expected: {}, " \ diff --git a/tools/ovc/unit_tests/ovc/convert/meta_data_test_actual.py b/tools/ovc/unit_tests/ovc/convert/meta_data_test_actual.py index 006aa997bc7c12..ebb46f107347bc 100644 --- a/tools/ovc/unit_tests/ovc/convert/meta_data_test_actual.py +++ b/tools/ovc/unit_tests/ovc/convert/meta_data_test_actual.py @@ -21,7 +21,7 @@ def check_meta_data(ov_model, ref_meta): if key == 'conversion_parameters': for param_name, param_value in value.items(): val = ov_model.get_rt_info([key, param_name]).astype(str) - if param_name in ['extension', 'caffe_parser_path', 'input_model', 'k', 'output_dir']: + if param_name in ['extension', 'input_model']: val = Path(val) assert val == param_value, \ "Runtime info attribute with name {} does not match. Expected: {}, " \ diff --git a/tools/ovc/unit_tests/ovc/utils/cli_parser_test.py b/tools/ovc/unit_tests/ovc/utils/cli_parser_test.py index 629c759f33fbbb..47bd73fe0528f6 100644 --- a/tools/ovc/unit_tests/ovc/utils/cli_parser_test.py +++ b/tools/ovc/unit_tests/ovc/utils/cli_parser_test.py @@ -12,15 +12,14 @@ import numpy as np -from openvino.tools.ovc.cli_parser import input_to_input_cut_info, \ - check_positive, writable_dir, \ - readable_file_or_object, get_freeze_placeholder_values, get_all_cli_parser, \ - get_mo_convert_params +from openvino.tools.ovc.cli_parser import input_to_input_cut_info, check_positive, writable_dir, \ + readable_file_or_object, get_all_cli_parser, get_mo_convert_params from openvino.tools.ovc.convert_impl import pack_params_to_args_namespace from openvino.tools.ovc.error import Error from unit_tests.ovc.unit_test_with_mocked_telemetry import UnitTestWithMockedTelemetry from openvino.runtime import PartialShape, Dimension, Layout -from openvino.tools.ovc import InputCutInfo +from openvino.tools.ovc.cli_parser import _InputCutInfo +import openvino.runtime as ov class TestShapesParsing(UnitTestWithMockedTelemetry): @@ -28,152 +27,119 @@ def test_get_shapes_several_inputs_several_shapes2(self): # shapes specified using --input command line parameter and no values argv_input = "inp1[1,22,333,123],inp2[-1,45,7,1]" inputs = input_to_input_cut_info(argv_input) - inputs_ref = [InputCutInfo(name='inp1', shape=PartialShape([1,22,333,123])), - InputCutInfo(name='inp2', shape=PartialShape([-1,45,7,1]))] + inputs_ref = [_InputCutInfo(name='inp1', shape=PartialShape([1,22,333,123])), + _InputCutInfo(name='inp2', shape=PartialShape([-1,45,7,1]))] self.assertEqual(inputs, inputs_ref) - placeholder_values_res, input_node_names_res = get_freeze_placeholder_values(argv_input) - placeholder_values_ref = {} - self.assertEqual(list(placeholder_values_res.keys()), list(placeholder_values_ref.keys())) - for i in placeholder_values_ref.keys(): - assert np.array_equal(placeholder_values_res[i], placeholder_values_ref[i]) def test_get_shapes_and_freezing_with_scalar_and_without_shapes_in_input(self): # shapes and value for freezing specified using --input command line parameter - argv_input = "inp1,inp2->157" + argv_input = "inp1,inp2" inputs = input_to_input_cut_info(argv_input) - inputs_ref = [InputCutInfo(name='inp1'), - InputCutInfo(name='inp2', value=157)] + inputs_ref = [_InputCutInfo(name='inp1'), + _InputCutInfo(name='inp2')] self.assertEqual(inputs, inputs_ref) - placeholder_values_res, input_node_names_res = get_freeze_placeholder_values(argv_input) - placeholder_values_ref = {'inp2': 157} - - self.assertEqual(list(placeholder_values_res.keys()), list(placeholder_values_ref.keys())) - for i in placeholder_values_ref.keys(): - self.assertEqual(placeholder_values_res[i], placeholder_values_ref[i]) def test_get_shapes_and_freezing_with_scalar(self): # shapes and value for freezing specified using --input command line parameter - argv_input = "inp1,inp2[]->157" + argv_input = "inp1,inp2[]" inputs = input_to_input_cut_info(argv_input) - inputs_ref = [InputCutInfo(name='inp1'), - InputCutInfo(name='inp2', shape=PartialShape([]), value=157)] + inputs_ref = [_InputCutInfo(name='inp1'), + _InputCutInfo(name='inp2', shape=PartialShape([]))] self.assertEqual(inputs, inputs_ref) def test_get_shapes_several_inputs_several_shapes3(self): # shapes and value for freezing specified using --input command line parameter - argv_input = "inp1[3 1]->[1.0 2.0 3.0],inp2[3,2,3],inp3[5]->[1.0 1.0 2.0 3.0 5.0]" + argv_input = "inp1[3 1],inp2[3,2,3],inp3[5]" inputs = input_to_input_cut_info(argv_input) - inputs_ref = [InputCutInfo(name='inp1', shape=PartialShape([3,1]), value=['1.0', '2.0', '3.0']), - InputCutInfo(name='inp2', shape=PartialShape([3,2,3])), - InputCutInfo(name='inp3', shape=PartialShape([5]), value=['1.0', '1.0', '2.0', '3.0', '5.0'])] + inputs_ref = [_InputCutInfo(name='inp1', shape=PartialShape([3,1])), + _InputCutInfo(name='inp2', shape=PartialShape([3,2,3])), + _InputCutInfo(name='inp3', shape=PartialShape([5]))] self.assertEqual(inputs, inputs_ref) - placeholder_values_res, input_node_names_res = get_freeze_placeholder_values(argv_input) - placeholder_values_ref = {'inp1': np.array(['1.0', '2.0', '3.0']), - 'inp3': np.array(['1.0', '1.0', '2.0', '3.0', '5.0'])} - self.assertEqual(list(placeholder_values_res.keys()), list(placeholder_values_ref.keys())) - for i in placeholder_values_ref.keys(): - assert np.array_equal(placeholder_values_res[i], placeholder_values_ref[i]) def test_get_shapes_several_inputs_several_shapes3_comma_sep(self): # shapes and value for freezing specified using --input command line parameter - argv_input = "inp1[3 1]->[1.0 2.0 3.0],inp2[3 2 3],inp3[5]->[1.0, 1.0, 2.0, 3.0,5.0]" + argv_input = "inp1[3 1],inp2[3 2 3],inp3[5]" inputs = input_to_input_cut_info(argv_input) - inputs_ref = [InputCutInfo(name='inp1', shape=PartialShape([3,1]), value=['1.0', '2.0', '3.0']), - InputCutInfo(name='inp2', shape=PartialShape([3,2,3])), - InputCutInfo(name='inp3', shape=PartialShape([5]), value=['1.0', '1.0', '2.0', '3.0', '5.0'])] + inputs_ref = [_InputCutInfo(name='inp1', shape=PartialShape([3,1])), + _InputCutInfo(name='inp2', shape=PartialShape([3,2,3])), + _InputCutInfo(name='inp3', shape=PartialShape([5]))] self.assertEqual(inputs, inputs_ref) - placeholder_values_res, input_node_names_res = get_freeze_placeholder_values(argv_input) - placeholder_values_ref = {'inp1': np.array(['1.0', '2.0', '3.0']), - 'inp3': np.array(['1.0', '1.0', '2.0', '3.0', '5.0'])} - self.assertEqual(list(placeholder_values_res.keys()), list(placeholder_values_ref.keys())) - for i in placeholder_values_ref.keys(): - assert np.array_equal(placeholder_values_res[i], placeholder_values_ref[i]) def test_get_shapes_several_inputs_several_shapes6(self): # 0D value for freezing specified using --input command line parameter without shape - argv_input = "inp1[3,1]->[1.0 2.0 3.0],inp2[3,2,3],inp3->False" + argv_input = "inp1[3,1],inp2[3,2,3],inp3" inputs_list, result, _ = input_to_input_cut_info(argv_input) inputs = input_to_input_cut_info(argv_input) - inputs_ref = [InputCutInfo(name='inp1', shape=PartialShape([3,1]), value=['1.0', '2.0', '3.0']), - InputCutInfo(name='inp2', shape=PartialShape([3,2,3])), - InputCutInfo(name='inp3', value=False)] + inputs_ref = [_InputCutInfo(name='inp1', shape=PartialShape([3,1])), + _InputCutInfo(name='inp2', shape=PartialShape([3,2,3])), + _InputCutInfo(name='inp3')] self.assertEqual(inputs, inputs_ref) - placeholder_values_res, input_node_names_res = get_freeze_placeholder_values(argv_input) - placeholder_values_ref = {'inp1': np.array(['1.0', '2.0', '3.0']), 'inp3': False} - self.assertEqual(list(placeholder_values_res.keys()), list(placeholder_values_ref.keys())) - for i in placeholder_values_ref.keys(): - assert np.array_equal(placeholder_values_res[i], placeholder_values_ref[i]) def test_get_shapes_several_inputs_several_shapes7(self): # 0D shape and value for freezing specified using --input command line parameter - argv_input = "inp1[3,1]->[1.0 2.0 3.0],inp2[3,2,3],inp3[]->True" + argv_input = "inp1[3,1],inp2[3,2,3],inp3[]" inputs_list, result, _ = input_to_input_cut_info(argv_input) inputs = input_to_input_cut_info(argv_input) - inputs_ref = [InputCutInfo(name='inp1', shape=PartialShape([3,1]), value=['1.0', '2.0', '3.0']), - InputCutInfo(name='inp2', shape=PartialShape([3,2,3])), - InputCutInfo(name='inp3', shape=PartialShape([]), value=True)] + inputs_ref = [_InputCutInfo(name='inp1', shape=PartialShape([3,1])), + _InputCutInfo(name='inp2', shape=PartialShape([3,2,3])), + _InputCutInfo(name='inp3', shape=PartialShape([]))] self.assertEqual(inputs, inputs_ref) - placeholder_values_res, input_node_names_res = get_freeze_placeholder_values(argv_input) - placeholder_values_ref = {'inp1': np.array(['1.0', '2.0', '3.0']), 'inp3': True} - self.assertEqual(list(placeholder_values_res.keys()), list(placeholder_values_ref.keys())) - for i in placeholder_values_ref.keys(): - assert np.array_equal(placeholder_values_res[i], placeholder_values_ref[i]) def test_get_shapes_and_data_types1(self): - argv_input = "inp1[3 1]->[1.0 2.0 3.0],inp2[3 2 3]{i32},inp3[5]{f32}->[1.0 1.0 2.0 3.0 5.0]" + argv_input = "inp1[3 1],inp2[3 2 3]{i32},inp3[5]{f32}" inputs = input_to_input_cut_info(argv_input) - inputs_ref = [InputCutInfo(name='inp1', shape=PartialShape([3,1]), value=['1.0', '2.0', '3.0']), - InputCutInfo(name='inp2', shape=PartialShape([3,2,3]), type=np.int32), - InputCutInfo(name='inp3', shape=PartialShape([5]), type=np.float32, value=['1.0', '1.0', '2.0', '3.0', '5.0'])] + inputs_ref = [_InputCutInfo(name='inp1', shape=PartialShape([3,1])), + _InputCutInfo(name='inp2', shape=PartialShape([3,2,3]), type=np.int32), + _InputCutInfo(name='inp3', shape=PartialShape([5]), type=np.float32)] self.assertEqual(inputs, inputs_ref) def test_get_shapes_and_data_types_with_input_ports(self): - argv_input = "1:inp1[3 1]->[1.0 2.0 3.0],inp2[3 2 3]{i32},0:inp3[5]{f32}->[1.0 1.0 2.0 3.0 5.0]" + argv_input = "1:inp1[3 1],inp2[3 2 3]{i32},0:inp3[5]{f32}" inputs = input_to_input_cut_info(argv_input) - inputs_ref = [InputCutInfo(name='1:inp1', shape=PartialShape([3,1]), value=['1.0', '2.0', '3.0']), - InputCutInfo(name='inp2', shape=PartialShape([3,2,3]), type=np.int32), - InputCutInfo(name='0:inp3', shape=PartialShape([5]), type=np.float32, value=['1.0', '1.0', '2.0', '3.0', '5.0'])] + inputs_ref = [_InputCutInfo(name='1:inp1', shape=PartialShape([3,1])), + _InputCutInfo(name='inp2', shape=PartialShape([3,2,3]), type=np.int32), + _InputCutInfo(name='0:inp3', shape=PartialShape([5]), type=np.float32)] self.assertEqual(inputs, inputs_ref) def test_get_shapes_and_data_types_with_output_ports(self): - argv_input = "inp1:1[3 1]->[1.0 2.0 3.0],inp2[3 2 3]{i32},inp3:4[5]{f32}->[1.0 1.0 2.0 3.0 5.0]" + argv_input = "inp1:1[3 1],inp2[3 2 3]{i32},inp3:4[5]{f32}" inputs = input_to_input_cut_info(argv_input) - inputs_ref = [InputCutInfo(name='inp1:1', shape=PartialShape([3,1]), value=['1.0', '2.0', '3.0']), - InputCutInfo(name='inp2', shape=PartialShape([3,2,3]), type=np.int32), - InputCutInfo(name='inp3:4', shape=PartialShape([5]), type=np.float32, value=['1.0', '1.0', '2.0', '3.0', '5.0'])] + inputs_ref = [_InputCutInfo(name='inp1:1', shape=PartialShape([3,1])), + _InputCutInfo(name='inp2', shape=PartialShape([3,2,3]), type=np.int32), + _InputCutInfo(name='inp3:4', shape=PartialShape([5]), type=np.float32)] self.assertEqual(inputs, inputs_ref) def test_get_shapes_and_data_types_with_output_ports_comma_sep(self): - argv_input = "inp1:1[3,1]->[1.0,2.0 ,3.0],inp2[3,2, 3]{i32},inp3:4[5]{f32}->[1.0, 1.0,2.0, 3.0,5.0]" + argv_input = "inp1:1[3,1],inp2[3,2, 3]{i32},inp3:4[5]{f32}" inputs = input_to_input_cut_info(argv_input) - inputs_ref = [InputCutInfo(name='inp1:1', shape=PartialShape([3,1]), value=['1.0', '2.0', '3.0']), - InputCutInfo(name='inp2', shape=PartialShape([3,2,3]), type=np.int32), - InputCutInfo(name='inp3:4', shape=PartialShape([5]), type=np.float32, value=['1.0', '1.0', '2.0', '3.0', '5.0'])] + inputs_ref = [_InputCutInfo(name='inp1:1', shape=PartialShape([3,1])), + _InputCutInfo(name='inp2', shape=PartialShape([3,2,3]), type=np.int32), + _InputCutInfo(name='inp3:4', shape=PartialShape([5]), type=np.float32)] self.assertEqual(inputs, inputs_ref) def test_get_shapes_and_data_types_shape_only(self): argv_input = "placeholder1[3 1],placeholder2,placeholder3" inputs = input_to_input_cut_info(argv_input) - inputs_ref = [InputCutInfo(name='placeholder1', shape=PartialShape([3,1])), - InputCutInfo(name='placeholder2'), - InputCutInfo(name='placeholder3')] + inputs_ref = [_InputCutInfo(name='placeholder1', shape=PartialShape([3,1])), + _InputCutInfo(name='placeholder2'), + _InputCutInfo(name='placeholder3')] self.assertEqual(inputs, inputs_ref) def test_get_shapes_and_data_types_shape_with_ports_only(self): argv_input = "placeholder1:4[3 1],placeholder2,2:placeholder3" inputs = input_to_input_cut_info(argv_input) - inputs_ref = [InputCutInfo(name='placeholder1:4', shape=PartialShape([3,1])), - InputCutInfo(name='placeholder2'), - InputCutInfo(name='2:placeholder3')] + inputs_ref = [_InputCutInfo(name='placeholder1:4', shape=PartialShape([3,1])), + _InputCutInfo(name='placeholder2'), + _InputCutInfo(name='2:placeholder3')] self.assertEqual(inputs, inputs_ref) def test_get_shapes_and_data_types_when_no_freeze_value(self): argv_input = "placeholder1{i32}[3 1],placeholder2,placeholder3{i32}" inputs = input_to_input_cut_info(argv_input) - inputs_ref = [InputCutInfo(name='placeholder1', shape=PartialShape([3,1]), type=np.int32), - InputCutInfo(name='placeholder2'), - InputCutInfo(name='placeholder3', type=np.int32)] + inputs_ref = [_InputCutInfo(name='placeholder1', shape=PartialShape([3,1]), type=np.int32), + _InputCutInfo(name='placeholder2'), + _InputCutInfo(name='placeholder3', type=np.int32)] self.assertEqual(inputs, inputs_ref) def test_wrong_data_types(self): @@ -185,13 +151,6 @@ def test_shape_and_value_shape_mismatch(self): argv_input = "inp1[3 1]->[1.0 2.0 3.0],inp2[3 2 3],inp3[5 3]->[2.0 3.0 5.0]" self.assertRaises(Error, input_to_input_cut_info, argv_input) - def test_wrong_data_for_input_cmd_param(self): - # test that wrongly formatted data specified in --input is handled properly - argv_input = "abc->[1.0" - self.assertRaises(Error, get_freeze_placeholder_values, argv_input) - argv_input = "def[2 2]->[1.0 2.0 3.0 4.0],abc->1.0 34]" - self.assertRaises(Error, get_freeze_placeholder_values, argv_input) - def test_get_shapes_no_input_no_shape(self): argv_input = "" inputs = input_to_input_cut_info(argv_input) @@ -202,106 +161,84 @@ def test_get_shapes_no_input_no_shape(self): def test_get_shapes_no_input_one_shape2(self): argv_input = "[12,4,1]" inputs = input_to_input_cut_info(argv_input) - inputs_ref = [InputCutInfo(shape=PartialShape([12,4,1]))] + inputs_ref = [_InputCutInfo(shape=PartialShape([12,4,1]))] self.assertEqual(inputs, inputs_ref) def test_get_shapes_for_scalar_inputs(self): argv_input = "[]" inputs = input_to_input_cut_info(argv_input) - inputs_ref = [InputCutInfo(shape=PartialShape([]))] + inputs_ref = [_InputCutInfo(shape=PartialShape([]))] self.assertEqual(inputs, inputs_ref) def test_get_shapes_two_input_shapes_with_scalar(self): argv_input = "[12,4,1],[]" inputs = input_to_input_cut_info(argv_input) - inputs_ref = [InputCutInfo(shape=PartialShape([12,4,1])), - InputCutInfo(shape=PartialShape([]))] + inputs_ref = [_InputCutInfo(shape=PartialShape([12,4,1])), + _InputCutInfo(shape=PartialShape([]))] self.assertEqual(inputs, inputs_ref) def test_get_shapes_two_input_shapes(self): argv_input = "[12,4,1],[10]" inputs = input_to_input_cut_info(argv_input) - inputs_ref = [InputCutInfo(shape=PartialShape([12,4,1])), - InputCutInfo(shape=PartialShape([10])),] + inputs_ref = [_InputCutInfo(shape=PartialShape([12,4,1])), + _InputCutInfo(shape=PartialShape([10])),] self.assertEqual(inputs, inputs_ref) def test_get_shapes_one_input_no_shape(self): argv_input = "inp1" inputs = input_to_input_cut_info(argv_input) - inputs_ref = [InputCutInfo(name='inp1')] + inputs_ref = [_InputCutInfo(name='inp1')] self.assertEqual(inputs, inputs_ref) def test_get_shapes_several_inputs_several_partial_shapes2(self): # shapes specified using --input command line parameter and no values argv_input = "inp1[1,?,50..100,123],inp2[-1,45..,..7,1]" inputs = input_to_input_cut_info(argv_input) - inputs_ref = [InputCutInfo(name='inp1', shape=PartialShape("[1,?,50..100,123]")), - InputCutInfo(name='inp2', shape=PartialShape("[-1,45..,..7,1]"))] + inputs_ref = [_InputCutInfo(name='inp1', shape=PartialShape("[1,?,50..100,123]")), + _InputCutInfo(name='inp2', shape=PartialShape("[-1,45..,..7,1]"))] self.assertEqual(inputs, inputs_ref) - placeholder_values_res, input_node_names_res = get_freeze_placeholder_values(argv_input) - placeholder_values_ref = {} - self.assertEqual(list(placeholder_values_res.keys()), list(placeholder_values_ref.keys())) - for i in placeholder_values_ref.keys(): - assert np.array_equal(placeholder_values_res[i], placeholder_values_ref[i]) - def test_get_shapes_several_inputs_several_partial_shapes3(self): # shapes and value for freezing specified using --input command line parameter - argv_input = "inp1[3,1]->[1.0 2.0 3.0],inp2[3..,..2,5..10,?,-1],inp3[5]->[1.0 1.0 2.0 3.0 5.0]" + argv_input = "inp1[3,1],inp2[3..,..2,5..10,?,-1],inp3[5]" inputs = input_to_input_cut_info(argv_input) - inputs_ref = [InputCutInfo(name='inp1', shape=PartialShape([3,1]), value=["1.0", "2.0", "3.0"]), - InputCutInfo(name='inp2', shape=PartialShape("[3..,..2,5..10,?,-1]")), - InputCutInfo(name='inp3', shape=PartialShape([5]), value=["1.0", "1.0", "2.0", "3.0", "5.0"])] + inputs_ref = [_InputCutInfo(name='inp1', shape=PartialShape([3,1])), + _InputCutInfo(name='inp2', shape=PartialShape("[3..,..2,5..10,?,-1]")), + _InputCutInfo(name='inp3', shape=PartialShape([5]))] self.assertEqual(inputs, inputs_ref) - placeholder_values_res, input_node_names_res = get_freeze_placeholder_values(argv_input) - placeholder_values_ref = {'inp1': np.array(['1.0', '2.0', '3.0']), 'inp3': np.array(['1.0', '1.0', '2.0', '3.0', '5.0'])} - input_node_names_ref = "inp1,inp2,inp3" - self.assertEqual(list(placeholder_values_res.keys()), list(placeholder_values_ref.keys())) - for i in placeholder_values_ref.keys(): - assert np.array_equal(placeholder_values_res[i], placeholder_values_ref[i]) def test_get_shapes_several_inputs_several_partial_shapes6(self): # 0D value for freezing specified using --input command line parameter without shape - argv_input = "inp1[3 1]->[1.0 2.0 3.0],inp2[3.. ..2 5..10 ? -1],inp3->False" + argv_input = "inp1[3 1],inp2[3.. ..2 5..10 ? -1],inp3" inputs = input_to_input_cut_info(argv_input) - inputs_ref = [InputCutInfo(name='inp1', shape=PartialShape([3,1]), value=["1.0", "2.0", "3.0"]), - InputCutInfo(name='inp2', shape=PartialShape("[3..,..2,5..10,?,-1]")), - InputCutInfo(name='inp3', value=False)] + inputs_ref = [_InputCutInfo(name='inp1', shape=PartialShape([3,1])), + _InputCutInfo(name='inp2', shape=PartialShape("[3..,..2,5..10,?,-1]")), + _InputCutInfo(name='inp3')] self.assertEqual(inputs, inputs_ref) - placeholder_values_res, input_node_names_res = get_freeze_placeholder_values(argv_input) - placeholder_values_ref = {'inp1': np.array(['1.0', '2.0', '3.0']), 'inp3': False} - self.assertEqual(list(placeholder_values_res.keys()), list(placeholder_values_ref.keys())) - for i in placeholder_values_ref.keys(): - assert np.array_equal(placeholder_values_res[i], placeholder_values_ref[i]) def test_get_shapes_several_inputs_several_partial_shapes7(self): # 0D shape and value for freezing specified using --input command line parameter - argv_input = "inp1[3 1]->[1.0 2.0 3.0],inp2[3.. ..2 5..10 ? -1],inp3[]->True" + argv_input = "inp1[3 1],inp2[3.. ..2 5..10 ? -1],inp3[]" inputs = input_to_input_cut_info(argv_input) - inputs_ref = [InputCutInfo(name='inp1', shape=PartialShape([3,1]), value=["1.0", "2.0", "3.0"]), - InputCutInfo(name='inp2', shape=PartialShape("[3..,..2,5..10,?,-1]")), - InputCutInfo(name='inp3', shape=PartialShape([]), value=True)] + inputs_ref = [_InputCutInfo(name='inp1', shape=PartialShape([3,1])), + _InputCutInfo(name='inp2', shape=PartialShape("[3..,..2,5..10,?,-1]")), + _InputCutInfo(name='inp3', shape=PartialShape([]))] self.assertEqual(inputs, inputs_ref) - placeholder_values_res, input_node_names_res = get_freeze_placeholder_values(argv_input) - placeholder_values_ref = {'inp1': np.array(['1.0', '2.0', '3.0']), 'inp3': True} - self.assertEqual(list(placeholder_values_res.keys()), list(placeholder_values_ref.keys())) - for i in placeholder_values_ref.keys(): - assert np.array_equal(placeholder_values_res[i], placeholder_values_ref[i]) def test_get_shapes_and_data_types_partial_shape_with_input_port(self): - argv_input = "inp1:1[3 1]->[1.0 2.0 3.0],0:inp2[3.. ..2 5..10 ? -1]{i32},inp3:4[5]{f32}->[1.0 1.0 2.0 3.0 5.0]" + argv_input = "inp1:1[3 1],0:inp2[3.. ..2 5..10 ? -1]{i32},inp3:4[5]{f32}" inputs = input_to_input_cut_info(argv_input) - inputs_ref = [InputCutInfo(name='inp1:1', shape=PartialShape([3,1]), value=['1.0', '2.0', '3.0']), - InputCutInfo(name='0:inp2', shape=PartialShape("[3..,..2,5..10,?,-1]"), type=np.int32), - InputCutInfo(name='inp3:4', shape=PartialShape([5]), type=np.float32, value=['1.0', '1.0', '2.0', '3.0', '5.0'])] + inputs_ref = [_InputCutInfo(name='inp1:1', shape=PartialShape([3,1])), + _InputCutInfo(name='0:inp2', shape=PartialShape("[3..,..2,5..10,?,-1]"), type=np.int32), + _InputCutInfo(name='inp3:4', shape=PartialShape([5]), type=np.float32)] self.assertEqual(inputs, inputs_ref) def test_get_shapes_and_data_types_partial_shape_with_output_port(self): - argv_input = "inp1:1[3 1]->[1.0 2.0 3.0],inp2:3[3.. ..2 5..10 ? -1]{i32},inp3:4[5]{f32}->[1.0 1.0 2.0 3.0 5.0]" + argv_input = "inp1:1[3 1],inp2:3[3.. ..2 5..10 ? -1]{i32},inp3:4[5]{f32}" inputs = input_to_input_cut_info(argv_input) - inputs_ref = [InputCutInfo(name='inp1:1', shape=PartialShape([3,1]), value=['1.0', '2.0', '3.0']), - InputCutInfo(name='inp2:3', shape=PartialShape("[3..,..2,5..10,?,-1]"), type=np.int32), - InputCutInfo(name='inp3:4', shape=PartialShape([5]), type=np.float32, value=['1.0', '1.0', '2.0', '3.0', '5.0'])] + inputs_ref = [_InputCutInfo(name='inp1:1', shape=PartialShape([3,1])), + _InputCutInfo(name='inp2:3', shape=PartialShape("[3..,..2,5..10,?,-1]"), type=np.int32), + _InputCutInfo(name='inp3:4', shape=PartialShape([5]), type=np.float32)] self.assertEqual(inputs, inputs_ref) def test_partial_shapes_freeze_dynamic_negative_case1(self): @@ -316,73 +253,51 @@ def test_get_shapes_several_inputs_several_partial_shapes2_comma_separator(self) # shapes specified using --input command line parameter and no values argv_input = "inp1[1,?,50..100,123],inp2[-1,45..,..7,1]" inputs = input_to_input_cut_info(argv_input) - inputs_ref = [InputCutInfo(name='inp1', shape=PartialShape("[1,?,50..100,123]")), - InputCutInfo(name='inp2', shape=PartialShape("[-1,45..,..7,1]"))] + inputs_ref = [_InputCutInfo(name='inp1', shape=PartialShape("[1,?,50..100,123]")), + _InputCutInfo(name='inp2', shape=PartialShape("[-1,45..,..7,1]"))] self.assertEqual(inputs, inputs_ref) - placeholder_values_res, input_node_names_res = get_freeze_placeholder_values(argv_input) - placeholder_values_ref = {} - self.assertEqual(list(placeholder_values_res.keys()), list(placeholder_values_ref.keys())) - for i in placeholder_values_ref.keys(): - assert np.array_equal(placeholder_values_res[i], placeholder_values_ref[i]) - def test_get_shapes_several_inputs_several_partial_shapes3_comma_separator(self): # shapes and value for freezing specified using --input command line parameter - argv_input = "inp1[3,1]->[1.0 2.0 3.0],inp2[3..,..2,5..10,?,-1],inp3[5]->[1.0 1.0 2.0 3.0 5.0]" + argv_input = "inp1[3,1],inp2[3..,..2,5..10,?,-1],inp3[5]" inputs = input_to_input_cut_info(argv_input) - inputs_ref = [InputCutInfo(name='inp1', shape=PartialShape([3,1]), value=["1.0", "2.0", "3.0"]), - InputCutInfo(name='inp2', shape=PartialShape("[3..,..2,5..10,?,-1]")), - InputCutInfo(name='inp3', shape=PartialShape([5]), value=["1.0", "1.0", "2.0", "3.0", "5.0"])] + inputs_ref = [_InputCutInfo(name='inp1', shape=PartialShape([3,1])), + _InputCutInfo(name='inp2', shape=PartialShape("[3..,..2,5..10,?,-1]")), + _InputCutInfo(name='inp3', shape=PartialShape([5]))] self.assertEqual(inputs, inputs_ref) - placeholder_values_res, input_node_names_res = get_freeze_placeholder_values(argv_input) - placeholder_values_ref = {'inp1': np.array(['1.0', '2.0', '3.0']), - 'inp3': np.array(['1.0', '1.0', '2.0', '3.0', '5.0'])} - self.assertEqual(list(placeholder_values_res.keys()), list(placeholder_values_ref.keys())) - for i in placeholder_values_ref.keys(): - assert np.array_equal(placeholder_values_res[i], placeholder_values_ref[i]) def test_get_shapes_several_inputs_several_partial_shapes6_comma_separator(self): # 0D value for freezing specified using --input command line parameter without shape - argv_input = "inp1[3, 1]->[1.0 2.0 3.0],inp2[3.., ..2, 5..10, ?,-1],inp3->False" + argv_input = "inp1[3, 1],inp2[3.., ..2, 5..10, ?,-1],inp3" inputs = input_to_input_cut_info(argv_input) - inputs_ref = [InputCutInfo(name='inp1', shape=PartialShape([3,1]), value=["1.0", "2.0", "3.0"]), - InputCutInfo(name='inp2', shape=PartialShape("[3..,..2,5..10,?,-1]")), - InputCutInfo(name='inp3', value=False)] + inputs_ref = [_InputCutInfo(name='inp1', shape=PartialShape([3,1])), + _InputCutInfo(name='inp2', shape=PartialShape("[3..,..2,5..10,?,-1]")), + _InputCutInfo(name='inp3')] self.assertEqual(inputs, inputs_ref) - placeholder_values_res, input_node_names_res = get_freeze_placeholder_values(argv_input) - placeholder_values_ref = {'inp1': np.array(['1.0', '2.0', '3.0']), 'inp3': False} - self.assertEqual(list(placeholder_values_res.keys()), list(placeholder_values_ref.keys())) - for i in placeholder_values_ref.keys(): - assert np.array_equal(placeholder_values_res[i], placeholder_values_ref[i]) def test_get_shapes_several_inputs_several_partial_shapes7_comma_separator(self): # 0D shape and value for freezing specified using --input command line parameter - argv_input = "inp1[3,1]->[1.0 2.0 3.0],inp2[3.., ..2,5..10, ?,-1],inp3[]->True" + argv_input = "inp1[3,1],inp2[3.., ..2,5..10, ?,-1],inp3[]" inputs = input_to_input_cut_info(argv_input) - inputs_ref = [InputCutInfo(name='inp1', shape=PartialShape([3,1]), value=["1.0", "2.0", "3.0"]), - InputCutInfo(name='inp2', shape=PartialShape("[3..,..2,5..10,?,-1]")), - InputCutInfo(name='inp3', shape=PartialShape([]), value=True)] + inputs_ref = [_InputCutInfo(name='inp1', shape=PartialShape([3,1])), + _InputCutInfo(name='inp2', shape=PartialShape("[3..,..2,5..10,?,-1]")), + _InputCutInfo(name='inp3', shape=PartialShape([]))] self.assertEqual(inputs, inputs_ref) - placeholder_values_res, input_node_names_res = get_freeze_placeholder_values(argv_input) - placeholder_values_ref = {'inp1': np.array(['1.0', '2.0', '3.0']), 'inp3': True} - self.assertEqual(list(placeholder_values_res.keys()), list(placeholder_values_ref.keys())) - for i in placeholder_values_ref.keys(): - assert np.array_equal(placeholder_values_res[i], placeholder_values_ref[i]) def test_get_shapes_and_data_types_partial_shape_with_input_port_comma_separator(self): - argv_input = "inp1:1[3,1]->[1.0 2.0 3.0],0:inp2[ 3.. ,..2, 5..10, ?,-1]{i32},inp3:4[5]{f32}->[1.0 1.0 2.0 3.0 5.0]" + argv_input = "inp1:1[3,1],0:inp2[ 3.. ,..2, 5..10, ?,-1]{i32},inp3:4[5]{f32}" inputs = input_to_input_cut_info(argv_input) - inputs_ref = [InputCutInfo(name='inp1:1', shape=PartialShape([3,1]), value=['1.0', '2.0', '3.0']), - InputCutInfo(name='0:inp2', shape=PartialShape("[3..,..2,5..10,?,-1]"), type=np.int32), - InputCutInfo(name='inp3:4', shape=PartialShape([5]), type=np.float32, value=['1.0', '1.0', '2.0', '3.0', '5.0'])] + inputs_ref = [_InputCutInfo(name='inp1:1', shape=PartialShape([3,1])), + _InputCutInfo(name='0:inp2', shape=PartialShape("[3..,..2,5..10,?,-1]"), type=np.int32), + _InputCutInfo(name='inp3:4', shape=PartialShape([5]), type=np.float32)] self.assertEqual(inputs, inputs_ref) def test_get_shapes_and_data_types_partial_shape_with_output_port_comma_separator(self): - argv_input = "inp1:1[3,1]->[1.0 2.0 3.0],inp2:3[3..,..2,5..10,?,-1]{i32},inp3:4[5]{f32}->[1.0 1.0 2.0 3.0 5.0]" + argv_input = "inp1:1[3,1],inp2:3[3..,..2,5..10,?,-1]{i32},inp3:4[5]{f32}" inputs = input_to_input_cut_info(argv_input) - inputs_ref = [InputCutInfo(name='inp1:1', shape=PartialShape([3,1]), value=['1.0', '2.0', '3.0']), - InputCutInfo(name='inp2:3', shape=PartialShape("[3..,..2,5..10,?,-1]"), type=np.int32), - InputCutInfo(name='inp3:4', shape=PartialShape([5]), type=np.float32, value=['1.0', '1.0', '2.0', '3.0', '5.0'])] + inputs_ref = [_InputCutInfo(name='inp1:1', shape=PartialShape([3,1])), + _InputCutInfo(name='inp2:3', shape=PartialShape("[3..,..2,5..10,?,-1]"), type=np.int32), + _InputCutInfo(name='inp3:4', shape=PartialShape([5]), type=np.float32)] self.assertEqual(inputs, inputs_ref) def test_partial_shapes_freeze_dynamic_negative_case1_comma_separator(self): @@ -401,6 +316,11 @@ def test_partial_shapes_freeze_dynamic_negative_case4_comma_separator(self): argv_input = "inp1:1[1, 2, -1]->[1.0 2.0 3.0]" self.assertRaises(Error, input_to_input_cut_info, argv_input) + def test_not_supported_arrow(self): + with self.assertRaisesRegex(Exception, + "Incorrect format of input."): + input_to_input_cut_info("inp1->[1.0]") + class PositiveChecker(unittest.TestCase): def test_positive_checker_batch(self): @@ -485,7 +405,7 @@ def test_mo_convert_params(self): from openvino.frontend import ConversionExtension args = {'input_model': os.path.dirname(__file__), 'extension': ConversionExtension("Ext", lambda x: x), - 'input': ['name', InputCutInfo("a", [1,2,3], numpy.float32, [5, 6, 7])], + 'input': ['name', ("a", [1,2,3], ov.Type.f32)], 'output': ["a", "b", "c"]} cli_parser = get_all_cli_parser() @@ -493,7 +413,7 @@ def test_mo_convert_params(self): assert argv.input_model == args['input_model'] assert argv.extension == args['extension'] - assert argv.input == ['name', InputCutInfo("a", [1,2,3], numpy.float32, [5, 6, 7])] + assert argv.input == ['name', ("a", [1,2,3], ov.Type.f32)] assert argv.output == ["a","b","c"] for arg, value in vars(argv).items(): diff --git a/tools/pot/openvino/__init__.py b/tools/pot/openvino/__init__.py index 8f0113d5bcaf6c..90552e0befed68 100644 --- a/tools/pot/openvino/__init__.py +++ b/tools/pot/openvino/__init__.py @@ -57,6 +57,6 @@ # Tools try: # Model Conversion API - ovc should reside in the main namespace - from openvino.tools.ovc import convert_model, InputCutInfo + from openvino.tools.ovc import convert_model except ImportError: pass