Support of passing model from memory to mo.convert_model() (#13457)

* convert() method added.

* Moved conversion to convert() method.

* Fixed commits.

* Output dir fix.

* Added objects support for extesions param.

* Added support for transformations_config extension objects.

* Input to str unit tests.

* Added tests, added comments.

* Updated BOM.

* Removed commented code.

* Fixed extension passing.

* Small corrections.

* Fixed for python 3.6.

* Small fix.

* Moved dir creating to ov.serialize(), removed mo.serialize(), small fixes.

* Small fix.

* Small correction.

* Removed coping of params, moved convert implemetation to separate module.

* Import fixes.

* Moved hiding of exceptions to main().

* Updated comment.

* Fixed unit tests.

* Comment changed.

* Fixed dir creating.

* Tests fixed.

* Small fixes.

* Test fix.

* Added meta data generation, removed printing of execution time for silent mode.

* Import fix.

* Conflict fix.

* Fixed error.

* Fix for custom config.

* Added version, data_type params to help.

* Added mo.convert() full-functional tests.

* Small corrections.

* Comment correction.

* Moved convert to openvino package, moved LayotMap and InputCutInfo to openvino.convert.

* Added help param.

* Wrong change removed.

* Small fix.

* Removed unnecessary comments.

* Removed .xml extension check from append_ir_info.

* Added missed file.

* Fixed error.

* Fix for bool value in InputCutInfo.

* Moved InputCutInfo, LayoutMap to openvino.tools.mo.

* Moved InputCutInfo, LayoutMap to openvino.tools.mo.

* Moved check and read_model to emit_ir.

* Small correction.

* Added comment.

* Added unit_tests with convert().

* Small corrections.

* Removed convert alias from openvino.

* Fixed conflicting unit tests.

* Removed unnecessary warnings.

* Params check fix.

* Small correction.

* Added paths checks.

* Added negative tests for to_str methods, fixed errors.

* Added tuples support in input parameter.

* Added direct support of BytesIO and TF1 graph def.

* Moved reminders to update OV and use API 2.0 to main().

* Fixed keras loading.

* Returned .mapping file generating.

* Added positional input_model param.

* Added test for unnamed input_model.

* Optimize imports.

* Added more informative error for brackets syntax in --input.

* Keras direct model support.

* Keras direct model support.

* Small fix.

* Conflict fix.

* Conflict fix.

* Added direct support of BytesIO and TF1 graph def.

* Fixed keras loading.

* Keras direct model support.

* Keras direct model support.

* Small fix.

* Tests for importing from memory.

* Support of other tf/pytorch formats.

* Updated mo_convert extensions tests to use ov models.

* Removed debug output.

* Implemented PyTorch converting logic.

* Small corrections.

* Small corrections.

* Added comments.

* Fixed for single input case.

* Added switching between save to file and BytesIO.

* Small fixes.

* Rename convert() to convert_model().

* Added env variable to disable converting to onnx.

* Tests refactoring.

* Add MO Python API tests to precommit.

* Add MO Python API tests to precommit.

* Added PyTorch to layer tests requirements.

* Added supported formats description.

* Fixed errors, added tests.

* Fixed bugs, added support of numpy and ov.Tensor sample_input.

* Added more torch.Size tests.

* Small correction.

* Renamed sample_input->example_inputs.

* Tests refactoring.

* Code style.

* Added support of dict in example_inputs.

* Small correction.

* Added removing of tmp onnx model in case of conversion error.

* Fix for lists of tensors in example_inputs.

* Support of dynamic axes for Keras layer, Keras module.

* Removed disabling of eager execution.

* Tests fixed.

* Added pytest requirement for layer tests.

* Added convert_model to openvino.runtime, fixed test runs.

* Small fix.

* Tests fix.

* Better help message.

* Error fixed.

* Renamed example_inputs->example_input.

* Small fix.

* Added support of list of layouts.

* Removed wrong change.

* Added tuple support for --layout.

* Removed convert_model from openvino.runtime.

* Use of default onnx opset version.

* Added support of dynamic input shapes without example_input.

* Made better error message.

* Removed stack trace from exceptions.

* Fixed tests.

* Small fix.

* Removed wrong change.

* Added import model from memory tests in unit tests.

* Replaced compare_functions() with MO IR reader compare.

* Removed test.

* Removed not needed change.

* Fixed conflicts.
This commit is contained in:
Anastasiia Pnevskaia
2022-11-19 11:21:44 +03:00
committed by GitHub
parent 68e7f062c5
commit 558714ccdd
32 changed files with 2100 additions and 296 deletions
+27
View File
@@ -544,6 +544,33 @@ jobs:
displayName: 'TensorFlow 2 Layer Tests - Legacy FE'
continueOnError: false
- script: |
. $(PY_VENV)/bin/activate
python3 -m pip install -r $(LAYER_TESTS_DIR)/requirements.txt
export PYTHONPATH=$(LAYER_TESTS_DIR):$PYTHONPATH
export TEST_DEVICE=CPU
$(RUN_PREFIX) python3 -m pytest $(LAYER_TESTS_DIR)/mo_python_api_tests/test_mo_convert_complex_params.py --ir_version=11 --junitxml=./TEST-test_mo_convert_complex_params.xmlTEST
displayName: 'MO Python API Tests - Complex Python params'
continueOnError: false
- script: |
. $(PY_VENV)/bin/activate
python3 -m pip install -r $(LAYER_TESTS_DIR)/requirements.txt
export PYTHONPATH=$(LAYER_TESTS_DIR):$PYTHONPATH
export TEST_DEVICE=CPU
$(RUN_PREFIX) python3 -m pytest $(LAYER_TESTS_DIR)/mo_python_api_tests/test_mo_convert_tf.py --ir_version=11 --junitxml=./TEST-test_mo_convert_tf.xmlTEST
displayName: 'MO Python API Tests - Import TF model from memory'
continueOnError: false
- script: |
. $(PY_VENV)/bin/activate
python3 -m pip install -r $(LAYER_TESTS_DIR)/requirements.txt
export PYTHONPATH=$(LAYER_TESTS_DIR):$PYTHONPATH
export TEST_DEVICE=CPU
$(RUN_PREFIX) python3 -m pytest $(LAYER_TESTS_DIR)/mo_python_api_tests/test_mo_convert_pytorch.py --ir_version=11 --junitxml=./TEST-test_mo_convert_pytorch.xmlTEST
displayName: 'MO Python API Tests - Import PyTorch model from memory'
continueOnError: false
- task: PublishTestResults@2
condition: always()
inputs:
@@ -13,16 +13,20 @@ namespace py = pybind11;
PYBIND11_MODULE(test_utils_api, m) {
m.def(
"compare_functions",
[](const ov::Model& lhs, const ov::Model& rhs) {
[](const ov::Model& lhs, const ov::Model& rhs, bool compare_tensor_names) {
const auto lhs_ptr = std::const_pointer_cast<ov::Model>(lhs.shared_from_this());
const auto rhs_ptr = std::const_pointer_cast<ov::Model>(rhs.shared_from_this());
const auto fc = FunctionsComparator::with_default()
.enable(FunctionsComparator::ATTRIBUTES)
.enable(FunctionsComparator::CONST_VALUES);
auto fc = FunctionsComparator::with_default()
.enable(FunctionsComparator::ATTRIBUTES)
.enable(FunctionsComparator::CONST_VALUES);
if (!compare_tensor_names)
fc.disable(FunctionsComparator::TENSOR_NAMES);
const auto results = fc.compare(lhs_ptr, rhs_ptr);
return std::make_pair(results.valid, results.message);
},
py::arg("lhs"),
py::arg("rhs"));
py::arg("rhs"),
py::arg("compare_tensor_names") = true);
}
@@ -4,8 +4,8 @@
from pathlib import Path
from openvino.runtime import serialize
from openvino.tools.mo import convert
from openvino.tools.mo.utils.ir_engine.ir_engine import IREngine
from openvino.test_utils import compare_functions
from openvino.tools.mo import convert_model
from common.utils.common_utils import generate_ir
@@ -16,7 +16,7 @@ class CommonMOConvertTest:
output_dir = kwargs['output_dir']
model_name = kwargs['model_name']
del kwargs['output_dir']
model = convert(**kwargs)
model = convert_model(**kwargs)
serialize(model, str(Path(output_dir, model_name + '.xml')))
def _test(self, temp_dir, test_params, ref_params):
@@ -24,6 +24,9 @@ class CommonMOConvertTest:
Generates two IRs using MO Python API and using cmd tool.
Then two IRs are compared.
"""
from openvino.runtime import Core
core = Core()
test_params.update({"model_name": 'model_test', "output_dir": temp_dir})
ref_params.update({"model_name": 'model_ref', "output_dir": temp_dir})
@@ -33,19 +36,25 @@ class CommonMOConvertTest:
assert not exit_code, (
"Reference IR generation failed with {} exit code: {}".format(exit_code, stderr))
ir_test = IREngine(Path(temp_dir, 'model_test.xml'), Path(temp_dir, 'model_test.bin'))
ir_ref = IREngine(Path(temp_dir, 'model_ref.xml'), Path(temp_dir, 'model_ref.bin'))
flag, resp = ir_test.compare(ir_ref)
assert flag, '\n'.join(resp)
ir_test = core.read_model(Path(temp_dir, 'model_test.xml'))
ir_ref = core.read_model(Path(temp_dir, 'model_ref.xml'))
def _test_by_ref_graph(self, temp_dir, test_params, ref_graph):
flag, msg = compare_functions(ir_test, ir_ref)
assert flag, '\n'.join(msg)
def _test_by_ref_graph(self, temp_dir, test_params, ref_graph, compare_tensor_names=True, compare_layout=True):
"""
Generates IR using MO Python API, reads it and compares with reference graph.
"""
from openvino.runtime import Core
core = Core()
test_params.update({"model_name": 'model_test', "output_dir": temp_dir})
self.generate_ir_python_api(**test_params)
ir_test = core.read_model(Path(temp_dir, 'model_test.xml'))
flag, msg = compare_functions(ir_test, ref_graph, compare_tensor_names=compare_tensor_names)
assert flag, msg
ir_test = IREngine(Path(temp_dir, 'model_test.xml'), Path(temp_dir, 'model_test.bin'))
flag, resp = ir_test.compare(ref_graph)
assert flag, '\n'.join(resp)
if compare_layout:
for idx in range(len(ir_test.inputs)):
assert ir_test.inputs[idx].node.layout == ref_graph.inputs[idx].node.layout
@@ -2,11 +2,15 @@
# 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
from unit_tests.utils.graph import build_graph
import openvino.runtime as ov
from openvino.runtime import PartialShape, Model
class TestExtensions(CommonMOConvertTest):
def create_onnx_model(self, tmp_dir):
@@ -77,44 +81,26 @@ class TestExtensions(CommonMOConvertTest):
return ConversionExtension("Elu", custom_converter)
def create_ref_graph1():
nodes_attributes = {
'input': {'kind': 'op', 'type': 'Parameter'},
'input_data': {'shape': [2, 3, 4], 'kind': 'data'},
'relu': {'kind': 'op', 'type': 'ReLU'},
'relu_data': {'shape': [2, 3, 4], 'kind': 'data'},
'elu': {'kind': 'op', 'type': 'Elu'},
'elu_data': {'shape': [2, 3, 4], 'kind': 'data'},
'result': {'kind': 'op', 'type': 'Result'}
}
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 build_graph(nodes_attributes,
[('input', 'input_data'),
('input_data', 'relu'),
('relu', 'relu_data'),
('relu_data', 'elu'),
('elu', 'elu_data'),
('elu_data', 'result'),
])
return Model([elu], [param], "test")
def create_ref_graph2():
nodes_attributes = {
'input': {'kind': 'op', 'type': 'Parameter'},
'input_data': {'shape': [2, 3, 4], 'kind': 'data'},
'relu': {'kind': 'op', 'type': 'ReLU'},
'relu_data': {'shape': [2, 3, 4], 'kind': 'data'},
'sigmoid': {'kind': 'op', 'type': 'Sigmoid'},
'sigmoid_data': {'shape': [2, 3, 4], 'kind': 'data'},
'result': {'kind': 'op', 'type': 'Result'}
}
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 build_graph(nodes_attributes,
[('input', 'input_data'),
('input_data', 'relu'),
('relu', 'relu_data'),
('relu_data', 'sigmoid'),
('sigmoid', 'sigmoid_data'),
('sigmoid_data', 'result'),
])
return Model([sigmoid], [param], "test")
test_data = [
{'params_test': {'extensions': create_custom_extension_leaky_relu_to_relu()},
@@ -0,0 +1,528 @@
# Copyright (C) 2018-2022 Intel Corporation
# SPDX-License-Identifier: Apache-2.0
import os
import numpy
import numpy as np
import openvino.runtime as ov
import pytest
import torch
from openvino.runtime import PartialShape, Dimension, Model
from common.mo_convert_test_class import CommonMOConvertTest
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_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)
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)
add = ov.opset8.add(param1, param2)
relu = ov.opset8.relu(add)
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, 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_shape': [PartialShape([-1, 3, -1, -1]), PartialShape([-1, 3, -1, -1])],
'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_shape': ["[?,3,?,?]", PartialShape([-1, 3, -1, -1])],
'example_input': sample_input, 'onnx_opset_version': 11}
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_shape': "[?,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([1, 3, 10, 10])
return pt_model, ref_model, {'example_input': sample_input}
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_shape': inp_shape}
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': shape}
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_shape': torch.Size([1, 3, 2, 10])}
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=numpy.int32)
return pt_model, ref_model, {'example_input': sample_input,
'input_shape': shape}
def create_pytorch_nn_module_sample_input_int32_two_inputs(tmp_dir):
pt_model = make_pt_model_two_inputs()
inp_shapes = ["[?,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_shape': inp_shapes,
'example_input': sample_input, 'onnx_opset_version': 11}
def create_pytorch_nn_module_compare_convert_paths_case1(tmp_dir):
from openvino.tools.mo import convert_model
pt_model = make_pt_model_one_input()
sample_input = torch.zeros(1, 3, 10, 10, dtype=torch.int32)
onnx_model_path = os.path.join(tmp_dir, 'export.onnx')
torch.onnx.export(pt_model, sample_input, onnx_model_path, opset_version=16)
ref_model = convert_model(onnx_model_path)
return pt_model, ref_model, {'example_input': sample_input, 'onnx_opset_version': 16}
def create_pytorch_nn_module_compare_convert_paths_case2(tmp_dir):
from openvino.tools.mo import convert_model
pt_model = make_pt_model_one_input()
sample_input = torch.zeros(1, 3, 10, 10, dtype=torch.int32)
onnx_model_path = os.path.join(tmp_dir, 'export.onnx')
torch.onnx.export(pt_model, sample_input, onnx_model_path, opset_version=16)
ref_model = convert_model(onnx_model_path)
return pt_model, ref_model, {'example_input': sample_input,
'input_shape': [1, 3, 10, 10],
'onnx_opset_version': 16}
def create_pytorch_nn_module_compare_convert_paths_case3(tmp_dir):
from openvino.tools.mo import convert_model
pt_model = make_pt_model_one_input()
sample_input = torch.zeros(1, 3, 10, 10, dtype=torch.float32)
onnx_model_path = os.path.join(tmp_dir, 'export.onnx')
torch.onnx.export(pt_model, sample_input, onnx_model_path, opset_version=16)
ref_model = convert_model(onnx_model_path)
return pt_model, ref_model, {'input_shape': [1, 3, 10, 10],
'onnx_opset_version': 16}
def create_pytorch_nn_module_compare_convert_paths_case4(tmp_dir):
from openvino.tools.mo import convert_model
pt_model = make_pt_model_two_inputs()
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)
onnx_model_path = os.path.join(tmp_dir, 'export.onnx')
torch.onnx.export(pt_model, sample_input, onnx_model_path, opset_version=16)
ref_model = convert_model(onnx_model_path)
return pt_model, ref_model, {'example_input': sample_input, 'onnx_opset_version': 16}
def create_pytorch_nn_module_compare_convert_paths_case5(tmp_dir):
from openvino.tools.mo import convert_model
pt_model = make_pt_model_two_inputs()
sample_input1 = torch.zeros(1, 3, 10, 10, dtype=torch.int32)
sample_input2 = torch.zeros(1, 3, 10, 10, dtype=torch.int32)
sample_input = tuple([sample_input1, sample_input2])
onnx_model_path = os.path.join(tmp_dir, 'export.onnx')
torch.onnx.export(pt_model, sample_input, onnx_model_path, opset_version=16)
ref_model = convert_model(onnx_model_path)
return pt_model, ref_model, {'example_input': sample_input,
'input_shape': [torch.Size([1, 3, 10, 10]), PartialShape([1, 3, 10, 10])],
'onnx_opset_version': 16}
def create_pytorch_nn_module_compare_convert_paths_case6(tmp_dir):
from openvino.tools.mo import convert_model
pt_model = make_pt_model_two_inputs()
sample_input1 = torch.zeros(1, 3, 10, 10, dtype=torch.float32)
sample_input2 = torch.zeros(1, 3, 10, 10, dtype=torch.float32)
sample_input = tuple([sample_input1, sample_input2])
onnx_model_path = os.path.join(tmp_dir, 'export.onnx')
torch.onnx.export(pt_model, sample_input, onnx_model_path, opset_version=16)
ref_model = convert_model(onnx_model_path)
return pt_model, ref_model, {'input_shape': [torch.Size([1, 3, 10, 10]), torch.Size([1, 3, 10, 10])],
'onnx_opset_version': 16}
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_shape': [PartialShape([1, 3, 5, 5]), PartialShape([1, 3, 5, 5])]}
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)
return scripted_fn, ref_model, {'input_shape': [inp_shape, inp_shape]}
def create_pytorch_nn_module_sample_input_numpy(tmp_dir):
from openvino.tools.mo import convert_model
pt_model = make_pt_model_one_input()
example_inputs = np.array(torch.zeros(1, 3, 10, 10, dtype=torch.int32))
onnx_model_path = os.path.join(tmp_dir, 'export.onnx')
torch.onnx.export(pt_model, torch.zeros(1, 3, 10, 10, dtype=torch.int32), onnx_model_path, opset_version=16)
ref_model = convert_model(onnx_model_path)
return pt_model, ref_model, {'example_input': example_inputs,
'input_shape': [1, 3, 10, 10],
'onnx_opset_version': 16}
def create_pytorch_nn_module_sample_input_dict(tmp_dir):
from openvino.tools.mo import convert_model
pt_model = make_pt_model_one_input()
example_inputs = {"x": np.array(torch.zeros(1, 3, 10, 10, dtype=torch.int32))}
onnx_model_path = os.path.join(tmp_dir, 'export.onnx')
torch.onnx.export(pt_model, torch.zeros(1, 3, 10, 10, dtype=torch.int32), onnx_model_path, opset_version=16)
ref_model = convert_model(onnx_model_path)
return pt_model, ref_model, {'example_input': example_inputs,
'onnx_opset_version': 16}
def create_pytorch_nn_module_sample_input_dict_two_inputs(tmp_dir):
from openvino.tools.mo import convert_model
pt_model = make_pt_model_two_inputs()
example_inputs = {"y": np.array(torch.zeros(1, 3, 10, 10, dtype=torch.int32)),
"x": np.array(torch.zeros(1, 3, 10, 10, dtype=torch.int32))}
onnx_model_path = os.path.join(tmp_dir, 'export.onnx')
torch.onnx.export(pt_model, {"y": torch.zeros(1, 3, 10, 10, dtype=torch.int32),
"x": torch.zeros(1, 3, 10, 10, dtype=torch.int32)}, onnx_model_path, opset_version=16)
ref_model = convert_model(onnx_model_path)
return pt_model, ref_model, {'example_input': example_inputs,
'onnx_opset_version': 16}
def create_pytorch_nn_module_sample_list_of_tensors(tmp_dir):
from openvino.tools.mo import convert_model
pt_model = make_pt_model_one_input()
example_inputs = [torch.zeros(3, 10, 10, dtype=torch.float32)]
onnx_model_path = os.path.join(tmp_dir, 'export.onnx')
torch.onnx.export(pt_model, torch.unsqueeze(example_inputs[0], 0), onnx_model_path, opset_version=16)
ref_model = convert_model(onnx_model_path)
return pt_model, ref_model, {'example_input': example_inputs,
'onnx_opset_version': 16}
def create_pytorch_nn_module_sample_input_ov_host_tensor(tmp_dir):
from openvino.tools.mo import convert_model
from openvino.runtime import Tensor
pt_model = make_pt_model_one_input()
sample_input = Tensor(np.zeros([1, 3, 10, 10], dtype=np.int32))
onnx_model_path = os.path.join(tmp_dir, 'export.onnx')
torch.onnx.export(pt_model, torch.zeros(1, 3, 10, 10, dtype=torch.int32), onnx_model_path, opset_version=16)
ref_model = convert_model(onnx_model_path)
return pt_model, ref_model, {'example_input': sample_input,
'input_shape': [1, 3, 10, 10],
'onnx_opset_version': 16}
def create_pytorch_nn_module_sample_input_ov_host_tensor_two_inputs(tmp_dir):
from openvino.tools.mo import convert_model
from openvino.runtime import Tensor
pt_model = make_pt_model_two_inputs()
sample_input1 = Tensor(np.zeros([1, 3, 10, 10], dtype=np.int32))
sample_input2 = Tensor(np.zeros([1, 3, 10, 10], dtype=np.int32))
sample_input = sample_input1, sample_input2
onnx_model_path = os.path.join(tmp_dir, 'export.onnx')
torch.onnx.export(pt_model, tuple([torch.zeros(1, 3, 10, 10, dtype=torch.int32),
torch.zeros(1, 3, 10, 10, dtype=torch.int32)]),
onnx_model_path, opset_version=16)
ref_model = convert_model(onnx_model_path)
return pt_model, ref_model, {'example_input': sample_input,
'onnx_opset_version': 16}
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': [shape, shape], 'layout': ['nchw', Layout('nhwc')],
'onnx_opset_version': 11}
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': [shape, shape], 'layout': ('nchw', Layout('nhwc')),
'onnx_opset_version': 11}
def create_pytorch_nn_module_mean_list(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]]]], dtype=np.float32)
const2 = ov.opset8.constant([[[[0, 0, 0]]]], dtype=np.float32)
sub1 = ov.opset8.subtract(param1, const1)
sub2 = ov.opset8.subtract(param2, const2)
add = ov.opset8.add(sub1, sub2)
relu = ov.opset8.relu(add)
sigm = ov.opset8.sigmoid(relu)
parameter_list = [param1, param2]
ref_model = Model([sigm], parameter_list, "test")
return pt_model, ref_model, {'input_shape': [shape, shape], 'mean_values': [[0, 0, 0], [0, 0, 0]],
'onnx_opset_version': 11}
def create_pytorch_nn_module_scale_list(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)
add = ov.opset8.add(sub1, sub2)
relu = ov.opset8.relu(add)
sigm = ov.opset8.sigmoid(relu)
parameter_list = [param1, param2]
ref_model = Model([sigm], parameter_list, "test")
return pt_model, ref_model, {'input_shape': [shape, shape], 'scale_values': [[1, 1, 1], [1, 1, 1]],
'onnx_opset_version': 11}
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])
return pt_model, ref_model, {'input_shape': [[1, 3, 20, 20], [1, 3, 20, 20]], 'onnx_opset_version': 11}
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="input_0", dtype=np.float32)
param2 = ov.opset8.parameter(PartialShape(inp_shapes[1]), name="input_1", dtype=np.float32)
add = ov.opset8.add(param1, param2)
relu = ov.opset8.relu(add)
sigm = ov.opset8.sigmoid(relu)
parameter_list = [param1, param2]
ref_model = Model([sigm], parameter_list, "test")
return pt_model, ref_model, {'input_shape': inp_shapes, 'onnx_opset_version': 11}
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])
return pt_model, ref_model, {'input_shape': inp_shapes, 'onnx_opset_version': 11}
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])
return pt_model, ref_model, {'input_shape': inp_shapes, 'onnx_opset_version': 11}
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_torch_size,
create_pytorch_nn_module_sample_input_int32,
create_pytorch_nn_module_sample_input_int32_two_inputs,
create_pytorch_nn_module_compare_convert_paths_case1,
create_pytorch_nn_module_compare_convert_paths_case2,
create_pytorch_nn_module_compare_convert_paths_case4,
create_pytorch_nn_module_compare_convert_paths_case5,
create_pytorch_nn_module_compare_convert_paths_case6,
create_pytorch_nn_module_sample_input_numpy,
create_pytorch_nn_module_sample_input_ov_host_tensor,
create_pytorch_nn_module_sample_input_ov_host_tensor_two_inputs,
create_pytorch_nn_module_sample_input_dict,
create_pytorch_nn_module_sample_input_dict_two_inputs,
create_pytorch_nn_module_sample_list_of_tensors,
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,
create_pytorch_nn_module_scale_list,
create_pytorch_nn_module_shapes_list_static,
create_pytorch_nn_module_shapes_list_dynamic,
create_pytorch_nn_module_shapes_list_dynamic_single_input,
create_pytorch_nn_module_shapes_list_static_single_input
]
@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)
@@ -0,0 +1,356 @@
# Copyright (C) 2018-2022 Intel Corporation
# SPDX-License-Identifier: Apache-2.0
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
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_shape': [PartialShape([1, 2, 3]), PartialShape([1, 2, 3])]}
def create_tf_module_layout_list(tmp_dir):
from openvino.runtime import Layout
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")
model_ref.inputs[0].node.layout = Layout('NCH')
model_ref.inputs[1].node.layout = Layout('NHC')
net = Net()
return net, model_ref, {'input_shape': [PartialShape([1, 2, 3]), PartialShape([1, 2, 3])], 'layout': ["NCH", "NHC"]}
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))
shape = PartialShape([-1, 3, 4])
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_shape': [PartialShape([-1, Dimension(3, -1), Dimension(4)]),
PartialShape([-1, Dimension(3), Dimension(4, -1)])]}
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_shape': [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))
shape = PartialShape([-1, 3, 4])
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_shape': [PartialShape([-1, Dimension(3, -1), Dimension(4)]),
PartialShape([-1, Dimension(3), Dimension(4, -1)])]}
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
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.function(
input_signature=[tf.TensorSpec(shape=[1, 2, 3], dtype=tf.float32),
tf.TensorSpec(shape=[1, 2, 3], dtype=tf.float32)])
def f(x):
return keras_net(x)
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 keras_net, model_ref, None
def create_tf_saved_model(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])
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")
tf.saved_model.save(keras_net, temp_dir + "/model")
saved_model = tf.saved_model.load(temp_dir + "/model")
return saved_model, model_ref, None
class TestMoConvertTF(CommonMOConvertTest):
test_data = [
# TF2
create_keras_model,
create_keras_layer,
create_tf_function,
create_tf_module,
create_tf_checkpoint,
create_tf_saved_model,
create_keras_layer_dynamic,
create_tf_module_dynamic,
create_tf_module_layout_list,
# TF1
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(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)
+2
View File
@@ -1,2 +1,4 @@
requests>=2.25.1
numpy>=1.19.2
torch
pytest
+1 -1
View File
@@ -1,4 +1,4 @@
# Copyright (C) 2018-2022 Intel Corporation
# SPDX-License-Identifier: Apache-2.0
from .convert import convert, InputCutInfo, LayoutMap
from .convert import convert_model, InputCutInfo, LayoutMap
@@ -59,6 +59,32 @@ def update_mean_scale_to_dict(input_nodes: list, mean_scale_val, scale):
return mean_scale_val
def update_layout_to_dict(input_nodes: list, layout: [list, dict]):
"""
Internal function. Updates layout values from array to dictionary
:param: input_nodes Inputs of model
:param: layout Parsed 'layout' object from command line arguments
"""
if isinstance(layout, dict):
return layout
if isinstance(layout, list):
if len(layout) != len(input_nodes):
raise Error('Numbers of inputs and mean/scale values do not match. ' + refer_to_faq_msg(61))
layout_dict = {}
for idx, node in enumerate(input_nodes):
names_list = list(node.get_tensor().get_names())
if not names_list:
raise Error("Empty tensor names list for node {}".format(node.name))
node_name = names_list[0]
layout_dict.update(
{
node_name: layout[idx]
}
)
return layout_dict
raise Error("Unknown layout type. Expected dict, list. Got {}".format(type(layout)))
def check_keys_valid(ov_function: Model, dict_to_validate: dict, search_outputs: bool):
"""
Internal function: checks if keys from cmd line arguments correspond to ov_function's inputs/outputs
@@ -360,7 +386,7 @@ def apply_preprocessing(ov_function: Model, argv: argparse.Namespace):
layout_values = {}
if 'layout_values' in argv and argv.layout_values:
layout_values = argv.layout_values
layout_values = update_layout_to_dict(ov_function.inputs, argv.layout_values)
if '' in layout_values:
if len(ov_function.inputs) > 1:
+22 -1
View File
@@ -8,15 +8,36 @@ InputCutInfo = namedtuple("InputInfo", ["name", "shape", "type", "value"])
LayoutMap = namedtuple("LayoutMap", ["source_layout", "target_layout"])
def convert(input_model=None, **args):
def convert_model(input_model=None, **args):
"""
Converts the model from original framework to OpenVino Model.
Args:
input_model:
Model object in original framework (PyTorch, Tensorflow) or path to model file.
Tensorflow*: a file with a pre-trained model (binary or text .pb file after freezing).
Caffe*: a model proto file with model weights
Supported formats of input model:
PyTorch
torch.nn.Module
torch.jit.ScriptModule
torch.jit.ScriptFunction
TF
tf.compat.v1.GraphDef
tf.compat.v1.wrap_function
tf.compat.v1.session
TF2 / Keras
tf.keras.Model
tf.keras.layers.Layer
tf.function
tf.Module
tf.train.checkpoint
tf.python.training.tracking.base.Trackable for case when it is output from tf.saved_model.load()
Run convert(help=true) to list all available parameters.
Returns:
+353 -15
View File
@@ -10,6 +10,8 @@ import sys
from collections import OrderedDict
from copy import deepcopy
import numpy as np
try:
import openvino_telemetry as tm
except ImportError:
@@ -30,7 +32,7 @@ from openvino.tools.mo.utils.cli_parser import check_available_transforms, \
get_common_cli_options, get_freeze_placeholder_values, get_kaldi_cli_options, get_layout_values, \
get_mean_scale_dictionary, get_mxnet_cli_options, get_onnx_cli_options, \
get_placeholder_shapes, get_tf_cli_options, get_tuple_values, parse_transform, parse_tuple_pairs, \
get_all_cli_parser, mo_convert_params, get_model_name_from_args, depersonalize
get_all_cli_parser, mo_convert_params, get_model_name_from_args, split_shapes, depersonalize
from openvino.tools.mo.utils.error import Error
from openvino.tools.mo.utils.find_ie_version import find_ie_version
@@ -46,6 +48,7 @@ from openvino.tools.mo.moc_frontend.check_config import legacy_extensions_used
# pylint: disable=no-name-in-module,import-error
from openvino.frontend import FrontEndManager, ProgressReporterExtension, TelemetryExtension, JsonConfigExtension
from openvino.runtime import PartialShape, Dimension
from openvino.runtime import get_version as get_rt_version
@@ -158,6 +161,9 @@ def arguments_post_parsing(argv: argparse.Namespace):
'Please use --framework with one from the list: {}.',
'--input_model', argv.input_model, frameworks)
elif argv.framework not in frameworks:
if argv.framework == 'ir':
raise Error('OpenVINO IR is passed as input_model in convert_model/mo, the IR doesn\'t need '
'conversion, please use it in runtime for inference with read_model/compile_model.')
raise Error('Framework {} is not a valid target. Please use --framework with one from the list: {}. ' +
refer_to_faq_msg(15), argv.framework, frameworks)
@@ -486,6 +492,229 @@ def emit_ir(graph: Graph, argv: argparse.Namespace, non_default_params: dict):
return func
def get_static_shape(shape: [PartialShape, list, tuple], dynamic_value=None):
# Current function returns list with static dimensions with following logic.
# For dynamic dimensions return lower boundaries if they are set, otherwise
# return upper boundaries if they are set. If dimension is fully dynamic then raise error.
shape_list = []
for idx, dim in enumerate(shape):
if isinstance(dim, int):
if dim == -1:
shape_list.append(dynamic_value)
continue
shape_list.append(dim)
elif isinstance(dim, np.int64):
if dim == np.int64(-1):
shape_list.append(dynamic_value)
continue
shape_list.append(dim)
elif isinstance(dim, tuple):
# tuple where (min_length, max_length), the format which uses MO cli parser
assert len(dim) == 2, "Unknown dimension type {}".format(dim)
if dim[0] > 0:
shape_list.append(dim[0])
elif dim[1] < np.iinfo(np.int64).max:
shape_list.append(dim[1])
else:
shape_list.append(dynamic_value)
continue
elif isinstance(dim, Dimension):
if dim.is_static or dim.get_min_length() > 0:
shape_list.append(dim.get_min_length())
elif dim.get_max_length() != -1:
shape_list.append(dim.get_max_length())
else:
shape_list.append(dynamic_value)
continue
else:
raise Error("Unknown dimension type {}".format(dim))
return tuple(shape_list)
def get_dynamic_dims(shape: [PartialShape, list, tuple]):
dynamic_dims = []
for idx, dim in enumerate(shape):
if isinstance(dim, int):
if dim == -1:
dynamic_dims.append(idx)
if isinstance(dim, np.int64):
if dim == np.int64(-1):
dynamic_dims.append(idx)
elif isinstance(dim, tuple):
dynamic_dims.append(idx)
elif isinstance(dim, Dimension):
if dim.get_min_length() == 0 and dim.get_max_length() == -1:
dynamic_dims.append(idx)
return dynamic_dims
def check_model_object(argv):
model = argv['input_model']
if 'tensorflow' in sys.modules:
import tensorflow as tf
from tensorflow.python.training.tracking.base import Trackable
if isinstance(model, tf.compat.v1.GraphDef):
return "tf"
if isinstance(model, tf.compat.v1.Session):
argv['input_model'] = model.graph_def
return "tf"
if isinstance(model, tf.types.experimental.ConcreteFunction):
argv['input_model'] = model.graph.as_graph_def()
return "tf"
if isinstance(model, tf.keras.Model):
return "tf"
if isinstance(model, tf.train.Checkpoint):
if isinstance(model.root, tf.keras.Model):
argv['input_model'] = model.root
return "tf"
else:
raise Error("Unknown checkpoint format.")
if isinstance(model, tf.keras.layers.Layer) or isinstance(model, tf.Module):
assert 'input_shape' in argv and argv['input_shape'] is not None, \
"Converting of {} requires providing of input_shape.".format(type(model))
assert len(argv['input_shape']) > 0, "Please provide non-empty input shape."
inputs = []
for shape_idx, shape in enumerate(parse_input_shapes(argv)):
inp_shape = get_static_shape(shape)
batch_size = None
if len(inp_shape) > 1:
batch_size = inp_shape[0]
inp_shape = inp_shape[1:]
inputs.append(tf.keras.Input(shape=inp_shape, batch_size=batch_size))
outputs = model(*inputs)
argv['input_model'] = tf.keras.Model(inputs, outputs)
argv['input_shape'] = None
return "tf"
if isinstance(model, Trackable):
return "tf"
if 'torch' in sys.modules:
import torch
if isinstance(model, torch.nn.Module) or isinstance(model, torch.jit.ScriptFunction):
return "pytorch"
import io
if isinstance(model, io.BytesIO):
return 'onnx'
raise Error('Unknown model type: {}'.format(type(model)))
def get_onnx_temp_filename(output_dir):
output_dir = output_dir if output_dir is not None else os.getcwd()
return os.path.normpath(os.path.join(output_dir, "model.onnx"))
def to_torch_tensor(tensor):
import torch
from openvino.runtime import Tensor
if isinstance(tensor, torch.Tensor):
return tensor
if isinstance(tensor, np.ndarray):
return torch.tensor(tensor)
if isinstance(tensor, np.ndarray):
return torch.tensor(tensor)
if isinstance(tensor, Tensor):
return torch.tensor(tensor.data)
else:
raise Error("Unexpected type of example_input. Supported types torch.Tensor, np.array or ov.Tensor. "
"Got {}".format(type(tensor)))
def convert_pytorch_to_onnx(model, input_shape, opset_version, example_inputs, output_dir):
import io
import torch
input_names = None
if example_inputs is not None:
inputs = example_inputs
if isinstance(inputs, list):
inputs = [to_torch_tensor(x) for x in inputs]
if len(inputs) == 1:
inputs = torch.unsqueeze(inputs[0], 0)
else:
inputs = inputs
elif isinstance(inputs, tuple):
inputs = [to_torch_tensor(x) for x in inputs]
inputs = tuple(inputs)
elif isinstance(inputs, dict):
for name, tensor in inputs.items():
assert isinstance(name, str), "Expected dictionary where keys are input names of string type and" \
" values are tensors. Got key of type {}".format(type(name))
inputs[name] = to_torch_tensor(tensor)
else:
inputs = to_torch_tensor(inputs)
elif input_shape is not None:
inputs = []
for shape_idx, shape in enumerate(input_shape):
static_shape = get_static_shape(shape, dynamic_value=1)
inputs.append(torch.zeros(static_shape))
inputs = tuple(inputs)
else:
raise Error("Please provide input_shape or example_input for converting PyTorch model.")
dynamic_dims_dict = {}
if input_shape is not None and input_names is None:
input_names = ["input_{}".format(idx) for idx in range(len(input_shape))]
for shape_idx, shape in enumerate(input_shape):
dynamic_dims = get_dynamic_dims(shape)
if len(dynamic_dims) > 0:
dynamic_dims_dict[input_names[shape_idx]] = dynamic_dims
additional_params = {}
if len(dynamic_dims_dict) > 0:
additional_params.update({'dynamic_axes': dynamic_dims_dict})
if input_names is not None and len(input_names) > 0:
additional_params.update({'input_names': input_names})
if os.environ.get('SAVE_TO_BYTES_IO_ONNX_MODEL'):
model_onnx = io.BytesIO()
else:
model_onnx = get_onnx_temp_filename(output_dir)
if opset_version is not None:
additional_params.update({'opset_version': opset_version})
torch.onnx.export(model,
inputs,
model_onnx,
**additional_params)
return model_onnx
def parse_input_shapes(argv):
input_shapes = None
if 'input_shape' in argv and argv['input_shape'] is not None:
shapes = argv['input_shape']
if isinstance(shapes, str):
shapes = ["[{}]".format(x) for x in split_shapes(shapes)]
if isinstance(shapes, list) or isinstance(shapes, tuple):
input_shapes = []
is_single_shape = False
for shape in shapes:
if isinstance(shape, str):
_, shape_tuple, _ = get_placeholder_shapes(argv_input=None, argv_input_shape=shape)
input_shapes.append(shape_tuple)
if is_single_shape:
raise Error("Incorrect format of shape.")
elif isinstance(shape, int) or isinstance(shape, np.int64) or isinstance(shape, Dimension):
is_single_shape = True
input_shapes.append(shape)
else:
input_shapes.append(shape)
if is_single_shape:
return [input_shapes]
else:
return input_shapes
elif isinstance(shapes, PartialShape) or isinstance(shapes, torch.Size):
return [shapes]
else:
raise Error("Unknown type of input shape {}.".format(type(shapes)))
return input_shapes
def driver(argv: argparse.Namespace, non_default_params: dict):
init_logger(argv.log_level.upper(), argv.silent)
@@ -535,8 +764,13 @@ def pack_params_to_args_namespace(**kwargs):
fe_manager = FrontEndManager()
cli_parser = get_all_cli_parser(fe_manager)
argv = cli_parser.parse_args(args_dict_to_list(cli_parser, **kwargs))
all_params = {}
for key, value in mo_convert_params.items():
all_params.update(value)
for key, value in kwargs.items():
if key not in argv and key not in mo_convert_params.keys():
if key not in argv and key not in all_params.keys():
raise Error("Unrecognized argument: {}".format(key))
if value is not None:
setattr(argv, key, value)
@@ -551,19 +785,73 @@ def pack_params_to_args_namespace(**kwargs):
def params_to_string(**kwargs):
all_params = {}
for key, value in mo_convert_params.items():
all_params.update(value)
for key, value in kwargs.items():
if key in mo_convert_params.keys():
param_data = mo_convert_params[key]
if key in all_params:
param_data = all_params[key]
if param_data.to_string is not None:
kwargs[key] = param_data.to_string(value)
return kwargs
def add_line_breaks(text: str, char_num: int, line_break: str):
words = text.split(" ")
cnt = 0
for i, w in enumerate(words):
cnt += len(w)
if '\n' in w:
cnt = len(w) - w.find('\n') - 1
if cnt > char_num:
if words[i][-1] not in ['\n', '\t']:
words[i] = w + '\n'
cnt = 0
text = ' '.join(words).replace("\n ", "\n")
return line_break + text.replace("\n", line_break)
def show_mo_convert_help():
print('MO convert parameters:')
for param_name in mo_convert_params.keys():
param_data = mo_convert_params[param_name]
print("{}: {}".format(param_name, param_data.description.format(param_data.possible_types_python_api)))
for group_name, group in mo_convert_params.items():
if group_name == "optional":
print("optional arguments:")
elif group_name == "fw_agnostic":
print("Framework-agnostic parameters:")
elif group_name == "tf":
print("TensorFlow*-specific parameters:")
elif group_name == "caffe":
print("Caffe*-specific parameters:")
elif group_name == "mxnet":
print("Mxnet-specific parameters:")
elif group_name == "kaldi":
print("Kaldi-specific parameters:")
elif group_name == "pytorch":
print("Pytorch-specific parameters:")
else:
raise Error("Unknown parameters group {}.".format(group_name))
for param_name in group:
param_data = group[param_name]
text = param_data.description.format(param_data.possible_types_python_api)
text = add_line_breaks(text, 56, "\n\t\t\t")
print(" --{} {}".format(param_name, text))
print()
def input_model_is_object(argv):
if isinstance(argv['input_model'], str):
return False
if argv['input_model'] is None:
return False
return True
def remove_tmp_onnx_model(out_dir):
if not os.environ.get('SAVE_TO_BYTES_IO_ONNX_MODEL'):
tmp_onnx_model = get_onnx_temp_filename(out_dir)
if os.path.exists(tmp_onnx_model):
os.remove(tmp_onnx_model)
def _convert(**args):
@@ -574,13 +862,63 @@ def _convert(**args):
telemetry = tm.Telemetry(tid=get_tid(), app_name='Model Optimizer', app_version=get_simplified_mo_version())
telemetry.start_session('mo')
telemetry.send_event('mo', 'version', get_simplified_mo_version())
args = params_to_string(**args)
argv, non_default_params = pack_params_to_args_namespace(**args)
if argv.model_name is None:
argv.model_name = get_model_name_from_args(argv)
try:
model_framework = None
inp_model_is_object = input_model_is_object(args)
if inp_model_is_object:
model_framework = check_model_object(args)
if model_framework == "pytorch" and not os.environ.get('USE_PYTORCH_FRONTEND'):
opset_version = None
if 'onnx_opset_version' in args and args['onnx_opset_version'] is not None:
opset_version = args['onnx_opset_version']
example_inputs = None
if 'example_input' in args and args['example_input'] is not None:
example_inputs = args['example_input']
out_dir = args['output_dir'] if 'output_dir' in args else None
model_onnx = convert_pytorch_to_onnx(args['input_model'],
parse_input_shapes(args),
opset_version,
example_inputs,
out_dir)
args['input_model'] = model_onnx
if os.environ.get('SAVE_TO_BYTES_IO_ONNX_MODEL'):
args['use_legacy_frontend'] = True
args['example_input'] = None
args['onnx_opset_version'] = None
try:
ov_model = _convert(**args)
except Exception as e:
remove_tmp_onnx_model(out_dir)
raise e
remove_tmp_onnx_model(out_dir)
return ov_model
args = params_to_string(**args)
argv, non_default_params = pack_params_to_args_namespace(**args)
if inp_model_is_object:
argv.model_name = "model"
if argv.model_name is None:
argv.model_name = get_model_name_from_args(argv)
if model_framework is not None:
if argv.framework is not None:
if argv.framework != model_framework:
raise Error("Provided model does not correspond to provided framework. The provided "
"framework is {}, the model type is {} which is expected to be {} framework.".format(
argv.framework,
type(argv.input_model),
model_framework))
else:
argv.framework = model_framework
# Initialize logger with 'ERROR' as default level to be able to form nice messages
# before arg parser deliver log_level requested by user
init_logger('ERROR', False)
@@ -603,4 +941,4 @@ def _convert(**args):
telemetry.send_event('mo', 'conversion_result', 'fail')
telemetry.end_session('mo')
telemetry.force_shutdown(1.0)
raise e
raise e.with_traceback(None)
@@ -633,6 +633,8 @@ def input_user_data_repack(graph: Graph, input_user_shapes: [None, list, dict, n
if input_user_shapes is None:
# None User did not provide neither --input nor --input_shape keys
_input_shapes = None
elif isinstance(input_user_shapes, list) and len(input_user_shapes) > 1 and isinstance(input_user_shapes[0], PartialShape):
raise Error('Please provide input layer names for input layer shapes. ' + refer_to_faq_msg(58))
elif isinstance(input_user_shapes, list) or isinstance(input_user_shapes, dict):
# list [layer names w or w/o ports]. User provided only --input key
# dict {layer names w or w/o ports as keys: shapes as values}. User provided both --input and --input_shape
+73 -36
View File
@@ -16,9 +16,6 @@ from openvino.tools.mo.utils.versions_checker import get_environment_setup
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'
try:
import tensorflow.compat.v1 as tf_v1
# disable eager execution of TensorFlow 2 environment immediately
tf_v1.disable_eager_execution()
import tensorflow as tf
from tensorflow.python.framework.convert_to_constants import convert_variables_to_constants_v2
except ImportError:
@@ -175,9 +172,81 @@ def deducing_metagraph_path(meta_graph_file: str):
return meta_graph_file
def freeze_tf2_concrete_function(model, concrete_func, env_setup):
if "tensorflow" in env_setup and env_setup["tensorflow"] >= LooseVersion("2.2.0"):
frozen_func = convert_variables_to_constants_v2(concrete_func,
lower_control_flow=False,
aggressive_inlining=True) # pylint: disable=E1123
else:
frozen_func = convert_variables_to_constants_v2(concrete_func,
lower_control_flow=False) # pylint: disable=E1123
graph_def = frozen_func.graph.as_graph_def(add_shapes=True)
input_names = []
if hasattr(model, 'inputs') and model.inputs is not None:
# Extract tensor names order from Keras model
input_names = [tensor.name for tensor in model.inputs]
# After model freezing output tensor names are changing and recieve "Func/PartitionedCall" prefix,
# so output_names from saved_model cannot be used. Here tensor names from frozen graph are used,
# as TF adds indexed Identity nodes during freezing to each output, so this indexing is used for
# order alignment.
output_names = [tensor.name for tensor in frozen_func.outputs]
inputs_outputs_order = (input_names, output_names)
return graph_def, {}, 'tf2', inputs_outputs_order
def prepare_graph_def(model):
from tensorflow.python.training.tracking.base import Trackable
if isinstance(model, tf_v1.GraphDef):
nodes_to_clear_device = model.node
for node in nodes_to_clear_device:
node.device = ""
return model, {}, "tf", None
if isinstance(model, tf.keras.Model):
env_setup = get_environment_setup("tf")
assert hasattr(model, "inputs") and model.inputs is not None, "Model inputs specification is required."
model_inputs = []
for inp in model.inputs:
if isinstance(inp, tf.Tensor):
model_inputs.append(inp)
elif tf.keras.backend.is_keras_tensor(inp):
model_inputs.append(inp.type_spec)
else:
raise Error("Unknown input tensor type {}".format(type(input)))
@tf.function
def tf_function(x):
return model(x)
conc_func = tf_function.get_concrete_function(model_inputs)
return freeze_tf2_concrete_function(model, conc_func, env_setup)
if isinstance(model, Trackable):
env_setup = get_environment_setup("tf")
return saved_model_load(model, env_setup)
raise Exception("Unknown model type {}.".format(type(model)))
def saved_model_load(imported, env_setup):
# to get a signature by key throws KeyError for TF 1.x SavedModel format in case TF 2.x installed
concrete_func = imported.signatures[tf.saved_model.DEFAULT_SERVING_SIGNATURE_DEF_KEY]
# the aggressive inlining parameter needs to freeze a table of embeddings for Keras Embedding operation
# and a model with Embedding operation cannot properly converted to IR without this function parameter
return freeze_tf2_concrete_function(imported, concrete_func, env_setup)
def load_tf_graph_def(graph_file_name: str = "", is_binary: bool = True, checkpoint: str = "",
model_dir: str = "", saved_model_tags: list = [], meta_graph_file: str = "",
user_output_node_names_list: list = []):
if not isinstance(graph_file_name, str) and graph_file_name is not None:
return prepare_graph_def(graph_file_name)
# As a provisional solution, use a native TF methods to load a model protobuf
graph_def = tf_v1.GraphDef()
if isinstance(graph_file_name, str) and (re.match(r'.*\.(ckpt|meta)$', graph_file_name)):
@@ -230,8 +299,6 @@ def load_tf_graph_def(graph_file_name: str = "", is_binary: bool = True, checkpo
# saved model directory
try:
env_setup = get_environment_setup("tf")
# enable eager execution temporarily while TensorFlow 2 model is being loaded
tf_v1.enable_eager_execution()
try:
# Code to extract Keras model.
@@ -241,38 +308,8 @@ def load_tf_graph_def(graph_file_name: str = "", is_binary: bool = True, checkpo
except:
imported = tf.saved_model.load(model_dir, saved_model_tags) # pylint: disable=E1120
# to get a signature by key throws KeyError for TF 1.x SavedModel format in case TF 2.x installed
concrete_func = imported.signatures[tf.saved_model.DEFAULT_SERVING_SIGNATURE_DEF_KEY]
# the aggressive inlining parameter needs to freeze a table of embeddings for Keras Embedding operation
# and a model with Embedding operation cannot properly converted to IR without this function parameter
if "tensorflow" in env_setup and env_setup["tensorflow"] >= LooseVersion("2.2.0"):
frozen_func = convert_variables_to_constants_v2(concrete_func,
lower_control_flow=False,
aggressive_inlining=True) # pylint: disable=E1123
else:
frozen_func = convert_variables_to_constants_v2(concrete_func,
lower_control_flow=False) # pylint: disable=E1123
graph_def = frozen_func.graph.as_graph_def(add_shapes=True)
# disable eager execution since next steps are executed with a graph in non-eager mode
tf_v1.disable_eager_execution()
input_names = []
if hasattr(imported, 'inputs') and imported.inputs is not None:
# Extract tensor names order from Keras model
input_names = [tensor.name for tensor in imported.inputs]
# After model freezing output tensor names are changing and recieve "Func/PartitionedCall" prefix,
# so output_names from saved_model cannot be used. Here tensor names from frozen graph are used,
# as TF adds indexed Identity nodes during freezing to each output, so this indexing is used for
# order alignment.
output_names = [tensor.name for tensor in frozen_func.outputs]
inputs_outputs_order = (input_names, output_names)
return graph_def, variables_values, 'tf2', inputs_outputs_order
return saved_model_load(imported, env_setup)
except:
# disable eager execution since TensorFlow 1 model is handled
tf_v1.disable_eager_execution()
# code to extract GraphDef for TF 1.0 SavedModel format
tags = saved_model_tags if saved_model_tags is not None else [tf_v1.saved_model.tag_constants.SERVING]
with tf_v1.Session() as sess:
@@ -11,8 +11,6 @@ import numpy as np
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'
try:
import tensorflow.compat.v1 as tf_v1
# disable eager execution of TensorFlow 2 environment immediately
tf_v1.disable_eager_execution()
except ImportError:
import tensorflow as tf_v1
@@ -20,6 +18,7 @@ except ImportError:
tf_v1.get_logger().setLevel("ERROR")
from google.protobuf import text_format
from tensorflow.python.eager.context import graph_mode
from openvino.tools.mo.front.extractor import node_defs_to_str
from openvino.tools.mo.front.tf.extractors.utils import tf_dtype_extractor, tf_tensor_shape, get_tf_node_port
@@ -27,7 +26,6 @@ from openvino.tools.mo.graph.graph import Node
from openvino.tools.mo.utils.graph import node_incoming_neighbourhood, node_outcoming_neighbourhood
from openvino.tools.mo.front.common.partial_infer.utils import mo_array
def tf_native_tf_node_infer(node: Node):
"""
The infer function should be used to infer shape and data type of the TF operation not supported by IE.
@@ -58,7 +56,9 @@ def tf_native_tf_node_infer(node: Node):
for ind in range(len(tmp_node.out_edges())):
tmp_node_attrs['output_tensors_names'].append(tmp_node.id + ":" + str(ind))
tf_subgraph_infer(tmp_node)
with graph_mode():
tf_subgraph_infer(tmp_node)
# the shape and value has been inferred and saved to the tmp_node's out nodes attribute. Let's copy it back!
for tmp_out_port, tmp_out_node in tmp_node.out_nodes().items():
if tmp_out_node.value is not None:
@@ -24,8 +24,16 @@ class ONNXLoader(Loader):
run_not_recursively = True
def load(self, graph: Graph):
import onnx
import io
argv = graph.graph['cmd_params']
model_proto = load_onnx_model(argv.input_model)
if isinstance(argv.input_model, str):
model_proto = load_onnx_model(argv.input_model)
elif isinstance(argv.input_model, io.BytesIO):
model_proto = onnx.load_model_from_string(argv.input_model.getvalue())
else:
raise Error('Unknown ONNX model type: {}'.format(type(argv.input_model)))
model_graph = model_proto.graph # pylint: disable=no-member
# print(model_graph)
# assert len(model_graph) == 1, "An ONNX model contains more than 1 graph: unsupported"
@@ -7,8 +7,6 @@ import os
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'
try:
import tensorflow.compat.v1 as tf_v1
# disable eager execution of TensorFlow 2 environment immediately
tf_v1.disable_eager_execution()
except ImportError:
import tensorflow as tf_v1
+2 -2
View File
@@ -11,7 +11,7 @@ try:
except ImportError:
import openvino.tools.mo.utils.telemetry_stub as tm
from openvino.tools.mo.convert import convert
from openvino.tools.mo.convert import convert_model
from openvino.tools.mo.pipeline.common import get_ir_version
from openvino.tools.mo.utils.cli_parser import get_model_name_from_args
from openvino.tools.mo.utils.logger import init_logger
@@ -40,7 +40,7 @@ def main(cli_parser: argparse.ArgumentParser, framework=None):
ngraph_function = None
try:
ngraph_function = convert(**argv)
ngraph_function = convert_model(**argv)
ov_update_message = get_ov_update_message()
ov_api20_message = get_ov_api20_message()
if ov_update_message is not None:
@@ -55,8 +55,6 @@ class CustomSubgraphCall(MiddleReplacementPattern):
"""
try:
import tensorflow.compat.v1 as tf_v1
# disable eager execution of TensorFlow 2 environment immediately
tf_v1.disable_eager_execution()
except ImportError:
import tensorflow as tf_v1
# in some environment suppressing through TF_CPP_MIN_LOG_LEVEL does not work
@@ -278,8 +276,6 @@ class CustomSubgraphCall(MiddleReplacementPattern):
"""
try:
import tensorflow.compat.v1 as tf_v1
# disable eager execution of TensorFlow 2 environment immediately
tf_v1.disable_eager_execution()
except ImportError:
import tensorflow as tf_v1
# in some environment suppressing through TF_CPP_MIN_LOG_LEVEL does not work
@@ -188,7 +188,14 @@ def fe_input_user_data_repack(
}
"""
_input_shapes = []
if isinstance(input_user_shapes, list) or isinstance(input_user_shapes, dict):
if isinstance(input_user_shapes, list) and len(input_user_shapes) > 1 and isinstance(input_user_shapes[0], PartialShape):
for shape in input_user_shapes:
assert isinstance(shape, PartialShape), "Got incorrect format of input shapes."
model_inputs = input_model.get_inputs()
assert len(model_inputs) == len(input_user_shapes)
for idx, model_input in enumerate(model_inputs):
_input_shapes.append({"node": model_input, "shape": input_user_shapes[idx]})
elif isinstance(input_user_shapes, list) or isinstance(input_user_shapes, dict):
for input_name in input_user_shapes:
node = decode_name_with_port(
input_model, input_name, framework, IOType.Input
@@ -2,6 +2,7 @@
# SPDX-License-Identifier: Apache-2.0
import argparse
import io
import logging as log
from typing import List
import sys
@@ -27,7 +28,11 @@ def moc_pipeline(argv: argparse.Namespace, moc_front_end: FrontEnd):
:param: moc_front_end: Loaded Frontend for converting input model
:return: converted nGraph function ready for serialization
"""
input_model = moc_front_end.load(argv.input_model)
if isinstance(argv.input_model, io.BytesIO):
raise Exception("ONNX frontend does not support input model as BytesIO object. "
"Please use use_legacy_frontend=True to convert the model.")
else:
input_model = moc_front_end.load(argv.input_model)
user_shapes, outputs, freeze_placeholder = fe_user_data_repack(
input_model, argv.placeholder_shapes, argv.placeholder_data_types,
@@ -80,6 +85,9 @@ def moc_pipeline(argv: argparse.Namespace, moc_front_end: FrontEnd):
inputs_equal, outputs_equal))
def create_target_input_shapes(new_input_places):
if isinstance(new_input_places, list) and len(new_input_places) > 1 \
and isinstance(new_input_places[0], tuple):
return new_input_places
new_input_place_names = [x.get_names()[0] for x in new_input_places]
shapes = [shape for shape in argv.placeholder_shapes.values()]
return dict(zip(new_input_place_names, shapes))
@@ -63,12 +63,21 @@ class ClassType(Enum):
def _update(cls, registered_list: list, registered_dict: dict, key: str, enabled_transforms: list,
disabled_transforms: list):
disabled_transforms: list, exclude_modules: set):
new_keys = {} # maps a custom name to class
new_keys_lower = {} # translates lowered custom name to its original form
# print('Registering new subclasses for', cls)
for c in cls.__subclasses__():
# skip importing loaders of other frameworks
if cls.__name__ == 'Loader':
need_exclude = False
for framework in exclude_modules:
if framework in c.__module__:
need_exclude = True
break
if need_exclude:
continue
# Force enabling operations
if hasattr(c, 'id') and c.id in enabled_transforms or \
".".join([c.__module__, c.__name__]) in enabled_transforms:
@@ -87,10 +96,9 @@ def _update(cls, registered_list: list, registered_dict: dict, key: str, enabled
if hasattr(c, key) and getattr(c, key) is not None:
k = getattr(c, key)
if k.lower() in new_keys_lower:
raise Error(
'Attempt to register of custom name {} for the second time as class {}. ' \
'Note that custom names are case-insensitive. ' +
refer_to_faq_msg(55), k, c)
log.warning('Attempt to register of custom name {} for the second time as class {}. '
'Note that custom names are case-insensitive. ' + refer_to_faq_msg(55), k, c)
continue
else:
new_keys_lower[k.lower()] = k
new_keys[k] = c
@@ -100,9 +108,9 @@ def _update(cls, registered_list: list, registered_dict: dict, key: str, enabled
registered_dict.update(new_keys)
def update_registration(classes: list, enabled_transforms: list, disabled_transforms: list):
def update_registration(classes: list, enabled_transforms: list, disabled_transforms: list, exclude_modules: set):
for cls in classes:
_update(cls, cls.registered_cls, cls.registered_ops, 'op', enabled_transforms, disabled_transforms)
_update(cls, cls.registered_cls, cls.registered_ops, 'op', enabled_transforms, disabled_transforms, exclude_modules)
_registered_classes_dict.setdefault(cls.class_type(), set()).add(cls)
@@ -326,3 +334,7 @@ def apply_replacements(graph: Graph, replacements_type: list):
"""
replacers_order = get_replacers_order(replacements_type)
apply_replacements_list(graph, replacers_order)
def clear_registered_classes_dict():
_registered_classes_dict.clear()
+198 -107
View File
@@ -74,6 +74,15 @@ def path_to_str(path):
raise Exception("Incorrect type of {} expected str or Path, got {}".format(path, type(path)))
def path_to_str_or_object(value):
if value is None or isinstance(value, str):
return value
elif isinstance(value, Path):
return str(value)
else:
return value
def paths_to_str(paths):
if paths is None:
return None
@@ -271,7 +280,7 @@ def layout_to_str(layout):
if isinstance(layout, Layout):
return layout.to_string()
raise Exception("Incorrect layout type. Expected Layout or string or dictionary, "
"where key is operation name and value is Layout, got {}".format(type(layout)))
"where key is operation name and value is layout or list of layouts, got {}".format(type(layout)))
def source_target_layout_to_str(value):
@@ -320,6 +329,13 @@ def layout_param_to_str(value):
raise Exception("Incorrect operation name type. Expected string, got {}".format(type(op_name)))
values_str.append(op_name + "(" + layoutmap_to_str(layout) + ")")
return ",".join(values_str)
if isinstance(value, openvino.tools.mo.LayoutMap):
return layoutmap_to_str(value)
if isinstance(value, list) or isinstance(value, tuple):
values_str = []
for layout in value:
values_str.append(layoutmap_to_str(layout))
return ",".join(values_str)
return layoutmap_to_str(value)
@@ -401,13 +417,26 @@ def transform_param_to_str(value):
ParamDescription = namedtuple("ParamData",
["description", "possible_types_command_line", "possible_types_python_api", "to_string"])
mo_convert_params = {
'input_model': ParamDescription(
'Tensorflow*: a file with a pre-trained model ' +
' (binary or text .pb file after freezing).\n' +
' Caffe*: a model proto file with model weights', '', '',
path_to_str),
'optional':
{
'help': ParamDescription(
'Print available parameters.', '', '', None),
'framework': ParamDescription(
'Name of the framework used to train the input model.', '', '', None),
},
'fw_agnostic':
{
'input_model': ParamDescription(
'{} Tensorflow*: a file with a pre-trained model ' +
' (binary or text .pb file after freezing).\n' +
' Caffe*: a model proto file with model weights', '',
'Model object in original framework (PyTorch, Tensorflow) or path to model file. \n' +
'Supported object formats of input model:\n PyTorch - torch.nn.Module, torch.jit.ScriptModule, torch.jit.ScriptFunction' +
'TF - tf.compat.v1.GraphDef, tf.compat.v1.wrap_function, tf.compat.v1.session\n ' +
'TF2 / Keras - tf.keras.Model, tf.keras.layers.Layer, tf.function, tf.Module, tf.train.checkpoint, ' +
'tf.python.training.tracking.base.Trackable for case when it is output from tf.saved_model.load().\n' +
'File formats examples:\n',
path_to_str_or_object),
'model_name': ParamDescription(
'Model_name parameter passed to the final create_ir transform. ' +
'This parameter is used to name ' +
@@ -536,10 +565,11 @@ mo_convert_params = {
'Apply additional transformations. {}' +
'"--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='
'Examples:' +
' "--transform LowLatency2" or \n' +
' "--transform Pruning" or \n' +
' "--transform LowLatency2[use_const_initializer=False]" or \n' +
' "--transform \"MakeStateful[param_res_names=\n'
'{{\'input_name_1\':\'output_name_1\',\'input_name_2\':\'output_name_2\'}}]\"" ' +
'Available transformations: "LowLatency2", "MakeStateful", "Pruning"', 'Usage: ',
'\'transform\' can be set by a list of tuples, where the first element is '
@@ -588,6 +618,17 @@ mo_convert_params = {
'Force the usage of legacy Frontend of Model Optimizer for model conversion into IR. '
'The legacy Frontend is Python based and is available for TensorFlow*, ONNX*, MXNet*, '
'Caffe*, and Kaldi* models.', '', '', None),
},
"caffe":
{
'input_proto': ParamDescription(
'Deploy-ready prototxt file that contains a topology structure ' +
'and layer attributes', '', '', path_to_str),
'caffe_parser_path': ParamDescription(
'Path to Python Caffe* parser generated from caffe.proto', '', '',
path_to_str),
'k': ParamDescription(
'Path to CustomLayersMapping.xml to register custom layers', '', '', path_to_str),
'disable_omitting_optional': ParamDescription(
'Disable omitting optional attributes to be used for custom layers. ' +
'Use this option if you want to transfer all attributes of a custom layer to IR. ' +
@@ -598,6 +639,9 @@ mo_convert_params = {
'Enable flattening optional params to be used for custom layers. ' +
'Use this option if you want to transfer attributes of a custom layer to IR with flattened nested parameters. ' +
'Default behavior is to transfer the attributes without flattening nested parameters.', '', '', None),
},
"tf":
{
'input_model_is_text': ParamDescription(
'TensorFlow*: treat the input model file as a text protobuf format. If not specified, ' +
'the Model Optimizer treats it as a binary file by default.', '', '', None),
@@ -624,14 +668,9 @@ mo_convert_params = {
'tensorflow_custom_layer_libraries': ParamDescription(
'TensorFlow*: comma separated list of shared libraries with TensorFlow* custom '
'operations implementation.', '', '', path_to_str),
'input_proto': ParamDescription(
'Deploy-ready prototxt file that contains a topology structure ' +
'and layer attributes', '', '', path_to_str),
'caffe_parser_path': ParamDescription(
'Path to Python Caffe* parser generated from caffe.proto', '', '',
path_to_str),
'k': ParamDescription(
'Path to CustomLayersMapping.xml to register custom layers', '', '', path_to_str),
},
"mxnet":
{
'input_symbol': ParamDescription(
'Symbol file (for example, model-symbol.json) that contains a topology structure ' +
'and layer attributes', '', '', path_to_str),
@@ -650,6 +689,9 @@ mo_convert_params = {
'enable_ssd_gluoncv': ParamDescription(
"Enable pattern matchers replacers for converting gluoncv ssd topologies.",
'', '', None),
},
"kaldi":
{
'counts': ParamDescription(
"Path to the counts file", '', '', path_to_str),
'remove_output_softmax': ParamDescription(
@@ -657,8 +699,14 @@ mo_convert_params = {
'remove_memory': ParamDescription(
"Removes the Memory layer and use additional inputs outputs instead", '', '',
None),
'help': ParamDescription(
'Print available parameters.', '', '', None),
},
"pytorch":
{
'example_input': ParamDescription('Sample of model input in original framework. '
'For PyTorch it can be torch.Tensor.', '', '', None),
'onnx_opset_version': ParamDescription('Version of ONNX opset that is used for converting from PyTorch to ONNX.',
'', '', None)
}
}
@@ -891,9 +939,10 @@ def get_common_cli_parser(parser: argparse.ArgumentParser = None):
if not parser:
parser = argparse.ArgumentParser()
common_group = parser.add_argument_group('Framework-agnostic parameters')
mo_convert_params_common = mo_convert_params['fw_agnostic']
# Common parameters
common_group.add_argument('--input_model', '-w', '-m',
help=mo_convert_params['input_model'].description,
help=mo_convert_params_common['input_model'].description,
action=CanonicalizePathCheckExistenceAction,
type=readable_file_or_dir)
common_group.add_argument('--model_name', '-n',
@@ -907,8 +956,8 @@ def get_common_cli_parser(parser: argparse.ArgumentParser = None):
action=CanonicalizePathAction,
type=writable_dir)
common_group.add_argument('--input_shape',
help=mo_convert_params['input_shape'].description.format(
mo_convert_params['input_shape'].possible_types_command_line))
help=mo_convert_params_common['input_shape'].description.format(
mo_convert_params_common['input_shape'].possible_types_command_line))
common_group.add_argument('--scale', '-s',
type=float,
help='All input values coming from original network inputs will be ' +
@@ -935,39 +984,39 @@ def get_common_cli_parser(parser: argparse.ArgumentParser = None):
'DEBUG', 'NOTSET'],
default='ERROR')
common_group.add_argument('--input',
help=mo_convert_params['input'].description.format(
mo_convert_params['input'].possible_types_command_line))
help=mo_convert_params_common['input'].description.format(
mo_convert_params_common['input'].possible_types_command_line))
common_group.add_argument('--output',
help=mo_convert_params['output'].description.format(
mo_convert_params['output'].possible_types_command_line))
help=mo_convert_params_common['output'].description.format(
mo_convert_params_common['output'].possible_types_command_line))
common_group.add_argument('--mean_values', '-ms',
help=mo_convert_params['mean_values'].description.format(
mo_convert_params['mean_values'].possible_types_command_line),
help=mo_convert_params_common['mean_values'].description.format(
mo_convert_params_common['mean_values'].possible_types_command_line),
default=())
common_group.add_argument('--scale_values',
help=mo_convert_params['scale_values'].description.format(
mo_convert_params['scale_values'].possible_types_command_line),
help=mo_convert_params_common['scale_values'].description.format(
mo_convert_params_common['scale_values'].possible_types_command_line),
default=())
common_group.add_argument('--source_layout',
help=mo_convert_params['source_layout'].description.format(
mo_convert_params['source_layout'].possible_types_command_line),
help=mo_convert_params_common['source_layout'].description.format(
mo_convert_params_common['source_layout'].possible_types_command_line),
default=())
common_group.add_argument('--target_layout',
help=mo_convert_params['target_layout'].description.format(
mo_convert_params['target_layout'].possible_types_command_line),
help=mo_convert_params_common['target_layout'].description.format(
mo_convert_params_common['target_layout'].possible_types_command_line),
default=())
common_group.add_argument('--layout',
help=mo_convert_params['layout'].description.format(
mo_convert_params['layout'].possible_types_command_line),
help=mo_convert_params_common['layout'].description.format(
mo_convert_params_common['layout'].possible_types_command_line),
default=())
# TODO: isn't it a weights precision type
common_group.add_argument('--data_type',
help=mo_convert_params['data_type'].description,
help=mo_convert_params_common['data_type'].description,
choices=["FP16", "FP32", "half", "float"],
default='float')
common_group.add_argument('--transform',
help=mo_convert_params['transform'].description.format(
mo_convert_params['transform'].possible_types_command_line),
help=mo_convert_params_common['transform'].description.format(
mo_convert_params_common['transform'].possible_types_command_line),
default="")
common_group.add_argument('--disable_fusing',
help='[DEPRECATED] Turn off fusing of linear operations to Convolution.',
@@ -984,22 +1033,22 @@ def get_common_cli_parser(parser: argparse.ArgumentParser = None):
action=DeprecatedStoreTrue, default=False)
# we use CanonicalizeDirCheckExistenceAction instead of readable_dirs to handle empty strings
common_group.add_argument("--extensions",
help=mo_convert_params['extensions'].description.format(
mo_convert_params['extensions'].possible_types_command_line),
help=mo_convert_params_common['extensions'].description.format(
mo_convert_params_common['extensions'].possible_types_command_line),
default=[import_extensions.default_path()],
action=CanonicalizePathCheckExistenceAction,
type=readable_dirs_or_files_or_empty)
common_group.add_argument("--batch", "-b",
type=check_positive,
default=None,
help=mo_convert_params['batch'].description)
help=mo_convert_params_common['batch'].description)
common_group.add_argument("--version",
action='version',
version='Version of Model Optimizer is: {}'.format(get_version()),
help=mo_convert_params['version'].description)
help=mo_convert_params_common['version'].description)
common_group.add_argument('--silent',
help=mo_convert_params['silent'].description,
help=mo_convert_params_common['silent'].description,
type=check_bool,
default=True)
common_group.add_argument('--freeze_placeholder_with_value',
@@ -1009,26 +1058,26 @@ def get_common_cli_parser(parser: argparse.ArgumentParser = None):
'Use --input option to specify a value for freezing.',
default=None)
common_group.add_argument('--static_shape',
help=mo_convert_params['static_shape'].description,
help=mo_convert_params_common['static_shape'].description,
action='store_true', default=False)
common_group.add_argument('--disable_weights_compression',
help='[DEPRECATED] Disable compression and store weights with original precision.',
action=DeprecatedStoreTrue, default=False)
common_group.add_argument('--progress',
help=mo_convert_params['progress'].description,
help=mo_convert_params_common['progress'].description,
action='store_true', default=False)
common_group.add_argument('--stream_output',
help=mo_convert_params['stream_output'].description,
help=mo_convert_params_common['stream_output'].description,
action='store_true', default=False)
common_group.add_argument('--transformations_config',
help=mo_convert_params['transformations_config'].description.format(
mo_convert_params['transformations_config'].possible_types_command_line),
help=mo_convert_params_common['transformations_config'].description.format(
mo_convert_params_common['transformations_config'].possible_types_command_line),
action=CanonicalizeTransformationPathCheckExistenceAction)
common_group.add_argument("--use_new_frontend",
help=mo_convert_params['use_new_frontend'].description,
help=mo_convert_params_common['use_new_frontend'].description,
action='store_true', default=False)
common_group.add_argument("--use_legacy_frontend",
help=mo_convert_params['use_legacy_frontend'].description,
help=mo_convert_params_common['use_legacy_frontend'].description,
action='store_true', default=False)
return parser
@@ -1143,18 +1192,19 @@ def get_caffe_cli_parser(parser: argparse.ArgumentParser = None):
get_common_cli_parser(parser=parser)
caffe_group = parser.add_argument_group('Caffe*-specific parameters')
mo_convert_params_caffe = mo_convert_params['caffe']
caffe_group.add_argument('--input_proto', '-d',
help=mo_convert_params['input_proto'].description,
help=mo_convert_params_caffe['input_proto'].description,
type=str,
action=CanonicalizePathCheckExistenceAction)
caffe_group.add_argument('--caffe_parser_path',
help=mo_convert_params['caffe_parser_path'].description,
help=mo_convert_params_caffe['caffe_parser_path'].description,
type=str,
default=os.path.join(os.path.dirname(__file__), os.pardir, 'front', 'caffe', 'proto'),
action=CanonicalizePathCheckExistenceAction)
caffe_group.add_argument('-k',
help=mo_convert_params['k'].description,
help=mo_convert_params_caffe['k'].description,
type=str,
default=os.path.join(os.path.dirname(__file__), os.pardir, os.pardir, 'extensions',
'front', 'caffe',
@@ -1175,11 +1225,11 @@ def get_caffe_cli_parser(parser: argparse.ArgumentParser = None):
'from the upper left corner of the mean image',
default=None)
caffe_group.add_argument('--disable_omitting_optional',
help=mo_convert_params['disable_omitting_optional'].description,
help=mo_convert_params_caffe['disable_omitting_optional'].description,
action='store_true',
default=False)
caffe_group.add_argument('--enable_flattening_nested_params',
help=mo_convert_params['enable_flattening_nested_params'].description,
help=mo_convert_params_caffe['enable_flattening_nested_params'].description,
action='store_true',
default=False)
return parser
@@ -1196,39 +1246,40 @@ def get_tf_cli_parser(parser: argparse.ArgumentParser = None):
if not parser:
parser = argparse.ArgumentParser(usage='%(prog)s [options]')
get_common_cli_parser(parser=parser)
mo_convert_params_tf = mo_convert_params['tf']
tf_group = parser.add_argument_group('TensorFlow*-specific parameters')
tf_group.add_argument('--input_model_is_text',
help=mo_convert_params['input_model_is_text'].description,
help=mo_convert_params_tf['input_model_is_text'].description,
action='store_true')
tf_group.add_argument('--input_checkpoint', type=str, default=None,
help=mo_convert_params['input_checkpoint'].description,
help=mo_convert_params_tf['input_checkpoint'].description,
action=CanonicalizePathCheckExistenceAction)
tf_group.add_argument('--input_meta_graph',
help=mo_convert_params['input_meta_graph'].description,
help=mo_convert_params_tf['input_meta_graph'].description,
action=CanonicalizePathCheckExistenceAction,
type=readable_file)
tf_group.add_argument('--saved_model_dir', default=None,
help=mo_convert_params['saved_model_dir'].description,
help=mo_convert_params_tf['saved_model_dir'].description,
action=CanonicalizePathCheckExistenceAction,
type=readable_dirs)
tf_group.add_argument('--saved_model_tags', type=str, default=None,
help=mo_convert_params['saved_model_tags'].description)
help=mo_convert_params_tf['saved_model_tags'].description)
tf_group.add_argument('--tensorflow_custom_operations_config_update',
help=mo_convert_params['tensorflow_custom_operations_config_update'].description,
help=mo_convert_params_tf['tensorflow_custom_operations_config_update'].description,
action=CanonicalizePathCheckExistenceAction)
tf_group.add_argument('--tensorflow_use_custom_operations_config',
help='Use the configuration file with custom operation description.',
action=DeprecatedCanonicalizePathCheckExistenceAction)
tf_group.add_argument('--tensorflow_object_detection_api_pipeline_config',
help=mo_convert_params['tensorflow_object_detection_api_pipeline_config'].description,
help=mo_convert_params_tf['tensorflow_object_detection_api_pipeline_config'].description,
action=CanonicalizePathCheckExistenceAction)
tf_group.add_argument('--tensorboard_logdir',
help=mo_convert_params['tensorboard_logdir'].description,
help=mo_convert_params_tf['tensorboard_logdir'].description,
default=None,
action=CanonicalizePathCheckExistenceAction)
tf_group.add_argument('--tensorflow_custom_layer_libraries',
help=mo_convert_params['tensorflow_custom_layer_libraries'].description,
help=mo_convert_params_tf['tensorflow_custom_layer_libraries'].description,
default=None,
action=CanonicalizePathCheckExistenceAction)
tf_group.add_argument('--disable_nhwc_to_nchw',
@@ -1251,26 +1302,27 @@ def get_mxnet_cli_parser(parser: argparse.ArgumentParser = None):
get_common_cli_parser(parser=parser)
mx_group = parser.add_argument_group('Mxnet-specific parameters')
mo_convert_params_mxnet = mo_convert_params['mxnet']
mx_group.add_argument('--input_symbol',
help=mo_convert_params['input_symbol'].description,
help=mo_convert_params_mxnet['input_symbol'].description,
type=str,
action=CanonicalizePathCheckExistenceAction)
mx_group.add_argument("--nd_prefix_name",
help=mo_convert_params['nd_prefix_name'].description,
help=mo_convert_params_mxnet['nd_prefix_name'].description,
default=None)
mx_group.add_argument("--pretrained_model_name",
help=mo_convert_params['pretrained_model_name'].description,
help=mo_convert_params_mxnet['pretrained_model_name'].description,
default=None)
mx_group.add_argument("--save_params_from_nd",
action='store_true',
help=mo_convert_params['save_params_from_nd'].description)
help=mo_convert_params_mxnet['save_params_from_nd'].description)
mx_group.add_argument("--legacy_mxnet_model",
action='store_true',
help=mo_convert_params['legacy_mxnet_model'].description)
help=mo_convert_params_mxnet['legacy_mxnet_model'].description)
mx_group.add_argument("--enable_ssd_gluoncv",
action='store_true',
help=mo_convert_params['enable_ssd_gluoncv'].description,
help=mo_convert_params_mxnet['enable_ssd_gluoncv'].description,
default=False)
return parser
@@ -1289,19 +1341,20 @@ def get_kaldi_cli_parser(parser: argparse.ArgumentParser = None):
get_common_cli_parser(parser=parser)
kaldi_group = parser.add_argument_group('Kaldi-specific parameters')
mo_convert_params_kaldi = mo_convert_params['kaldi']
kaldi_group.add_argument("--counts",
help=mo_convert_params['counts'].description,
help=mo_convert_params_kaldi['counts'].description,
default=None,
action=CanonicalizePathCheckExistenceIfNeededAction)
kaldi_group.add_argument("--remove_output_softmax",
help=mo_convert_params['remove_output_softmax'].description,
help=mo_convert_params_kaldi['remove_output_softmax'].description,
action='store_true',
default=False)
kaldi_group.add_argument("--remove_memory",
help=mo_convert_params['remove_memory'].description,
help=mo_convert_params_kaldi['remove_memory'].description,
action='store_true',
default=False)
return parser
@@ -1569,7 +1622,38 @@ def write_found_layout(name: str, found_layout: str, parsed: dict, dest: str = N
parsed[name] = {'source_layout': s_layout, 'target_layout': t_layout}
def parse_layouts_by_destination(s: str, parsed: dict, dest: str = None) -> None:
def write_found_layout_list(idx: int, found_layout: str, parsed: list, dest: str = None):
"""
Writes found layout data to the 'parsed' dict.
:param idx: idx of of the node to add layout
:param found_layout: string containing layout for the node
:param parsed: list where result will be stored
:param dest: type of the command line:
* 'source' is --source_layout
* 'target' is --target_layout
* None is --layout
"""
s_layout = None
t_layout = None
if idx < len(parsed):
s_layout = parsed[idx]['source_layout']
t_layout = parsed[idx]['target_layout']
if dest == 'source':
s_layout = found_layout
elif dest == 'target':
t_layout = found_layout
else:
s_layout, t_layout = split_layouts_by_arrow(found_layout)
validate_layout(s_layout)
validate_layout(t_layout)
if idx < len(parsed):
parsed[idx] = {'source_layout': s_layout, 'target_layout': t_layout}
else:
parsed.append({'source_layout': s_layout, 'target_layout': t_layout})
def parse_layouts_by_destination(s: str, parsed: dict, parsed_list: list, dest: str = None) -> None:
"""
Parses layout command line to get all names and layouts from it. Adds all found data in the 'parsed' dict.
:param s: string to parse
@@ -1584,29 +1668,25 @@ def parse_layouts_by_destination(s: str, parsed: dict, dest: str = None) -> None
# single layout case
write_found_layout('', list_s[0], parsed, dest)
else:
for layout_str in list_s:
for idx, layout_str in enumerate(list_s):
# case for: "name1(nhwc->[n,c,h,w])"
p1 = re.compile(r'(\S+)\((\S+)\)')
p1 = re.compile(r'(\w*)\((\S+)\)')
m1 = p1.match(layout_str)
# case for: "name1[n,h,w,c]->[n,c,h,w]"
p2 = re.compile(r'(\S+)(\[\S*\])')
p2 = re.compile(r'(\w*)(\[\S*\])')
m2 = p2.match(layout_str)
if m1:
found_g = m1.groups()
elif m2:
found_g = m2.groups()
else:
error_msg = "Invalid usage of --{}layout parameter. Please use following syntax for each tensor " \
"or operation name:" \
"\n name(nchw)" \
"\n name[n,c,h,w]".format(dest + '_' if dest else '')
if dest is None:
error_msg += "\n name(nhwc->[n,h,w,c])" \
"\n name[n,h,w,c]->[n,c,h,w]"
error_msg += '\n Please do not forget to surround whole expression with quotes, otherwise' \
' symbols >[]() would be treated as special characters.'
raise Error(error_msg)
write_found_layout(found_g[0], found_g[1], parsed, dest)
# case for layout without name
write_found_layout_list(idx, layout_str, parsed_list, dest)
continue
if len(found_g[0]) > 0:
write_found_layout(found_g[0], found_g[1], parsed, dest)
else:
write_found_layout_list(idx, found_g[1], parsed_list, dest)
def get_layout_values(argv_layout: str = '', argv_source_layout: str = '', argv_target_layout: str = ''):
@@ -1621,13 +1701,20 @@ def get_layout_values(argv_layout: str = '', argv_source_layout: str = '', argv_
raise Error("--layout is used as well as --source_layout and/or --target_layout which is not allowed, please "
"use one of them.")
res = {}
res_list = []
if argv_layout:
parse_layouts_by_destination(argv_layout, res)
parse_layouts_by_destination(argv_layout, res, res_list)
if argv_source_layout:
parse_layouts_by_destination(argv_source_layout, res, 'source')
parse_layouts_by_destination(argv_source_layout, res, res_list, 'source')
if argv_target_layout:
parse_layouts_by_destination(argv_target_layout, res, 'target')
return res
parse_layouts_by_destination(argv_target_layout, res, res_list, 'target')
if len(res) > 0 and len(res_list) > 0:
raise Error("Some layout values are provided with names, and some without names. "
"Please provide ether all layouts with names or all layouts without names.")
if len(res) > 0:
return res
else:
return res_list
def get_freeze_placeholder_values(argv_input: str, argv_freeze_placeholder_with_value: str):
@@ -1708,6 +1795,18 @@ def split_inputs(input_str):
def split_shapes(argv_input_shape: str):
range_reg = r'([0-9]*\.\.[0-9]*)'
first_digit_reg = r'([0-9 ]+|-1|\?|{})'.format(range_reg)
next_digits_reg = r'(,{})*'.format(first_digit_reg)
tuple_reg = r'((\({}{}\))|(\[{}{}\]))'.format(first_digit_reg, next_digits_reg,
first_digit_reg, next_digits_reg)
full_reg = r'^{}(\s*,\s*{})*$|^$'.format(tuple_reg, tuple_reg)
if not re.match(full_reg, argv_input_shape):
raise Error('Input shape "{}" cannot be parsed. ' + refer_to_faq_msg(57), argv_input_shape)
return re.findall(r'[(\[]([0-9,\.\? -]+)[)\]]', argv_input_shape)
def get_placeholder_shapes(argv_input: str, argv_input_shape: str, argv_batch=None):
"""
Parses input layers names and input shapes from the cli and returns the parsed object.
@@ -1769,16 +1868,9 @@ def get_placeholder_shapes(argv_input: str, argv_input_shape: str, argv_batch=No
inputs_list = list()
placeholder_shapes = None
range_reg = r'([0-9]*\.\.[0-9]*)'
first_digit_reg = r'([0-9 ]+|-1|\?|{})'.format(range_reg)
next_digits_reg = r'(,{})*'.format(first_digit_reg)
tuple_reg = r'((\({}{}\))|(\[{}{}\]))'.format(first_digit_reg, next_digits_reg,
first_digit_reg, next_digits_reg)
if argv_input_shape:
full_reg = r'^{}(\s*,\s*{})*$|^$'.format(tuple_reg, tuple_reg)
if not re.match(full_reg, argv_input_shape):
raise Error('Input shape "{}" cannot be parsed. ' + refer_to_faq_msg(57), argv_input_shape)
shapes = re.findall(r'[(\[]([0-9,\.\? -]+)[)\]]', argv_input_shape)
shapes = split_shapes(argv_input_shape)
if argv_input:
inputs = split_inputs(argv_input)
@@ -1786,10 +1878,9 @@ def get_placeholder_shapes(argv_input: str, argv_input_shape: str, argv_batch=No
# check number of shapes with no input provided
if argv_input_shape and not argv_input:
if len(shapes) > 1:
raise Error('Please provide input layer names for input layer shapes. ' + refer_to_faq_msg(58))
else:
placeholder_shapes = PartialShape(shapes[0])
placeholder_shapes = [PartialShape(shape) for shape in shapes]
if len(placeholder_shapes) == 1:
placeholder_shapes = PartialShape(placeholder_shapes[0])
# check if number of shapes does not match number of passed inputs
elif argv_input and (len(shapes) == len(inputs) or len(shapes) == 0):
# clean inputs from values for freezing
@@ -9,8 +9,6 @@ import sys
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'
try:
import tensorflow.compat.v1 as tf_v1
# disable eager execution of TensorFlow 2 environment immediately
tf_v1.disable_eager_execution()
except ImportError:
import tensorflow as tf_v1
@@ -12,10 +12,22 @@ from openvino.tools.mo.load.loader import Loader
from openvino.tools.mo.middle.replacement import MiddleReplacementPattern
from openvino.tools.mo.ops.op import Op
from openvino.tools.mo.utils.class_registration import _check_unique_ids, update_registration, \
get_enabled_and_disabled_transforms
get_enabled_and_disabled_transforms, clear_registered_classes_dict
from openvino.tools.mo.utils.model_analysis import AnalyzeAction
def get_internal_dirs(framework: str, get_front_classes: callable):
front_classes = get_front_classes()
return {
('ops', ): [Op],
('analysis',): [AnalyzeAction],
('load', framework): [Loader],
('front', ): front_classes,
('front', framework): front_classes,
('front', framework, 'extractors'): front_classes,
('middle', ): [MiddleReplacementPattern],
('back', ): [BackReplacementPattern]}
def import_by_path(path: str, middle_names: list = (), prefix: str = ''):
for module_loader, name, ispkg in pkgutil.iter_modules([path]):
importlib.import_module('{}{}.{}'.format(prefix, '.'.join(middle_names), name))
@@ -60,27 +72,28 @@ def load_dir(framework: str, path: str, get_front_classes: callable):
enabled_transforms, disabled_transforms = get_enabled_and_disabled_transforms()
front_classes = get_front_classes()
internal_dirs = {
('ops', ): [Op],
('analysis',): [AnalyzeAction],
('load', framework): [Loader],
('front', ): front_classes,
('front', framework): front_classes,
('front', framework, 'extractors'): front_classes,
('middle', ): [MiddleReplacementPattern],
('back', ): [BackReplacementPattern]}
internal_dirs = get_internal_dirs(framework, get_front_classes)
prefix = 'openvino.tools.' if ext == 'mo' else ''
exclude_modules = {'tf', 'onnx', 'kaldi', 'mxnet', 'caffe'}
exclude_modules.remove(framework)
for p in internal_dirs.keys():
import_by_path(os.path.join(path, *p), [ext, *p], prefix)
update_registration(internal_dirs[p], enabled_transforms, disabled_transforms)
update_registration(internal_dirs[p], enabled_transforms, disabled_transforms, exclude_modules)
sys.path.remove(root_dir)
def load_dirs(framework: str, dirs: list, get_front_classes: callable):
if dirs is None:
return
internal_dirs = get_internal_dirs(framework, get_front_classes)
for p, dir_names in internal_dirs.items():
for d in dir_names:
d.registered_cls = []
d.registered_ops = {}
clear_registered_classes_dict()
mo_inner_extensions = os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir, os.pardir, 'mo'))
dirs.insert(0, mo_inner_extensions)
@@ -65,7 +65,7 @@ def collect_ops(path: str):
import_by_path(os.path.join(path, 'mo', 'ops'), ['mo', 'ops'], 'openvino.tools.')
update_registration(classes=[Op, Activation, Elementwise, UnaryElementwise, LogicalElementwise,
EmbeddingBagBase, ReduceOp, Scatter, ScatterNDBase, FFTBase],
enabled_transforms=[], disabled_transforms=[])
enabled_transforms=[], disabled_transforms=[], exclude_modules=set())
def collect_extenders(path: str):
@@ -76,7 +76,7 @@ def collect_extenders(path: str):
"""
import_by_path(os.path.join(path, 'mo', 'utils', 'ir_reader', 'extenders'),
['mo', 'utils', 'ir_reader', 'extenders'], 'openvino.tools.')
update_registration(classes=[Extender], enabled_transforms=[], disabled_transforms=[])
update_registration(classes=[Extender], enabled_transforms=[], disabled_transforms=[], exclude_modules=set())
def collect_node_outputs(node: Node) -> dict:
@@ -11,8 +11,6 @@ import sys
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'
try:
import tensorflow.compat.v1 as tf_v1
# disable eager execution of TensorFlow 2 environment immediately
tf_v1.disable_eager_execution()
except ImportError:
import tensorflow as tf_v1
@@ -7,13 +7,12 @@ import os
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'
try:
import tensorflow.compat.v1 as tf_v1
# disable eager execution of TensorFlow 2 environment immediately
tf_v1.disable_eager_execution()
except ImportError:
import tensorflow as tf_v1
#in some environment suppressing through TF_CPP_MIN_LOG_LEVEL does not work
tf_v1.get_logger().setLevel("ERROR")
from tensorflow.python.eager.context import graph_mode
try:
import tensorflow.contrib # pylint: disable=no-name-in-module,import-error
@@ -27,8 +26,9 @@ def dump_for_tensorboard(graph_def: tf_v1.GraphDef, logdir: str):
try:
# TODO: graph_def is a deprecated argument, use graph instead
print('Writing an event file for the tensorboard...')
with tf_v1.summary.FileWriter(logdir=logdir, graph_def=graph_def) as writer:
writer.flush()
with graph_mode():
with tf_v1.summary.FileWriter(logdir=logdir, graph_def=graph_def) as writer:
writer.flush()
print('Done writing an event file.')
except Exception as err:
raise Error('Cannot write an event file for the tensorboard to directory "{}". ' +
@@ -4,6 +4,7 @@
import os
import tempfile
import numpy as np
from generator import generator, generate
from openvino.runtime import serialize
@@ -16,7 +17,6 @@ from utils import create_onnx_model, save_to_onnx
@generator
class ConvertImportMOTest(UnitTestWithMockedTelemetry):
# Checks convert import from openvino.tools.mo
test_directory = os.path.dirname(os.path.realpath(__file__))
@generate(*[
@@ -24,15 +24,16 @@ class ConvertImportMOTest(UnitTestWithMockedTelemetry):
({'input': InputCutInfo(name='LeakyRelu_out', shape=None, type=None, value=None)}),
({'layout': {'input': LayoutMap(source_layout='NCHW', target_layout='NHWC')}}),
])
# Checks convert import from openvino.tools.mo
def test_import(self, params):
from openvino.tools.mo import convert
from openvino.tools.mo import convert_model
with tempfile.TemporaryDirectory(dir=self.test_directory) as tmpdir:
model = create_onnx_model()
model_path = save_to_onnx(model, tmpdir)
out_xml = os.path.join(tmpdir, "model.xml")
ov_model = convert(input_model=model_path, **params)
ov_model = convert_model(input_model=model_path, **params)
serialize(ov_model, out_xml.encode('utf-8'), out_xml.replace('.xml', '.bin').encode('utf-8'))
assert os.path.exists(out_xml)
@@ -93,14 +94,13 @@ class ConvertImportMOTest(UnitTestWithMockedTelemetry):
('sigmoid_data', 'result'),
])
from openvino.tools.mo import convert
from openvino.tools.mo import convert_model
with tempfile.TemporaryDirectory(dir=self.test_directory) as tmpdir:
model = create_onnx_model()
model_path = save_to_onnx(model, tmpdir)
out_xml = os.path.join(tmpdir, "model.xml")
ov_model = convert(model_path)
ov_model = convert_model(model_path)
serialize(ov_model, out_xml.encode('utf-8'), out_xml.replace('.xml', '.bin').encode('utf-8'))
ir = IREngine(out_xml, out_xml.replace('.xml', '.bin'))
@@ -9,7 +9,7 @@ from generator import generator
from openvino.runtime import get_version as get_rt_version
from openvino.runtime import serialize
from openvino.tools.mo import convert
from openvino.tools.mo import convert_model
from openvino.tools.mo.utils import import_extensions
from openvino.tools.mo.utils.version import get_version
from unit_tests.mo.unit_test_with_mocked_telemetry import UnitTestWithMockedTelemetry
@@ -93,7 +93,7 @@ class MetaDataTest(UnitTestWithMockedTelemetry):
model_path = save_to_onnx(model, tmpdir)
out_xml = os.path.join(tmpdir, "model.xml")
ov_model = convert(model_path)
ov_model = convert_model(model_path)
check_meta_data(ov_model)
serialize(ov_model, out_xml.encode('utf-8'), out_xml.replace('.xml', '.bin').encode('utf-8'))
@@ -34,19 +34,21 @@ class ConvertToPBTests(unittest.TestCase):
def test_meta_format(self):
try:
import tensorflow.compat.v1 as tf_v1
tf_v1.disable_eager_execution()
except ImportError:
import tensorflow as tf_v1
from tensorflow.python.eager.context import graph_mode
with tempfile.TemporaryDirectory(dir=self.test_directory) as tmp_dir:
a = tf_v1.get_variable("A", initializer=tf_v1.constant(3, shape=[2]))
b = tf_v1.get_variable("B", initializer=tf_v1.constant(5, shape=[2]))
tf_v1.add(a, b, name='Add')
init_op = tf_v1.global_variables_initializer()
saver = tf_v1.train.Saver()
with tf_v1.Session() as sess:
sess.run(init_op)
saver.save(sess, os.path.join(tmp_dir, 'model'))
with graph_mode():
a = tf_v1.get_variable("A", initializer=tf_v1.constant(3, shape=[2]))
b = tf_v1.get_variable("B", initializer=tf_v1.constant(5, shape=[2]))
tf_v1.add(a, b, name='Add')
init_op = tf_v1.global_variables_initializer()
saver = tf_v1.train.Saver()
with tf_v1.Session() as sess:
sess.run(init_op)
saver.save(sess, os.path.join(tmp_dir, 'model'))
self.argv.input_meta_graph = os.path.join(tmp_dir, 'model.meta')
self.argv.output_dir = tmp_dir
path_to_pb = convert_to_pb(self.argv)
@@ -225,3 +225,18 @@ class TestConvertingConvertArgumentsToString(UnitTestWithMockedTelemetry):
self.assertRaises(Exception, layout_param_to_str, **{"value": {"op": Dimension(1)}})
self.assertRaises(Exception, layout_param_to_str, **{"value": {("a", "b"): Layout("nhwc")}})
self.assertRaises(Exception, layout_param_to_str, **{"value": Dimension(1)})
layout = ["nhwc", "[n,c]"]
self.assertTrue(layout_param_to_str(layout) == "nhwc,[n,c]")
layout = ["abc->cab", "..nc"]
self.assertTrue(layout_param_to_str(layout) == "abc->cab,..nc")
layout_map1 = LayoutMap(source_layout=Layout("n??"), target_layout=None)
layout = [layout_map1, "..nc"]
self.assertTrue(layout_param_to_str(layout) == "[N,?,?],..nc")
layout_map2 = LayoutMap(source_layout=Layout("nhwc"), target_layout=("nchw"))
layout_map3 = LayoutMap(source_layout="abc", target_layout="cab")
layout = [layout_map2, layout_map3]
self.assertTrue(layout_param_to_str(layout) == "[N,H,W,C]->nchw,abc->cab")
+345 -23
View File
@@ -738,11 +738,6 @@ class TestShapesParsing(UnitTestWithMockedTelemetry):
input_shapes = "(1,22,333,123), (-1,45,7,1), (-1,456,7,1)"
self.assertRaises(Error, get_placeholder_shapes, argv_input, input_shapes)
def test_get_shapes_several_shapes_no_input(self):
argv_input = ""
input_shapes = "(1,22,333,123), (-1,45,7,1), (-1,456,7,1)"
self.assertRaises(Error, get_placeholder_shapes, argv_input, input_shapes)
def test_get_shapes_one_input_one_shape(self):
argv_input = "inp1"
input_shapes = "(1,22,333,123)"
@@ -774,10 +769,6 @@ class TestShapesParsing(UnitTestWithMockedTelemetry):
exp_res = np.array([12, 4, 1])
assert np.array_equal(result, exp_res)
def test_get_shapes_no_input_two_shapes(self):
argv_input = ""
input_shapes = "(12,4,1),(5,4,3)"
self.assertRaises(Error, get_placeholder_shapes, argv_input, input_shapes)
def test_get_shapes_one_input_no_shape(self):
argv_input = "inp1"
@@ -1606,20 +1597,351 @@ class TestLayoutParsing(unittest.TestCase):
res = get_layout_values(argv_layout=argv_layout)
print(res)
def test_get_layout_raises_multiple_layouts_without_names(self):
argv_layout = "nhwc->nchw,nhwc->nchw"
with self.assertRaises(Error):
res = get_layout_values(argv_layout=argv_layout)
print(res)
def test_get_layout_raises_multiple_layouts_without_names_source_layout(self):
class TestLayoutParsingEmptyNames(unittest.TestCase):
def test_get_layout_1(self):
argv_layout = "([n,h,w,c]),([n,h,w,c]->[n,c,h,w])"
result = get_layout_values(argv_layout)
exp_res = [{'source_layout': '[n,h,w,c]', 'target_layout': None},
{'source_layout': '[n,h,w,c]', 'target_layout': '[n,c,h,w]'}]
self.assertEqual(exp_res, result)
for i in range(len(exp_res)):
assert np.array_equal(result[i], exp_res[i])
def test_get_layout_2(self):
argv_layout = "(nhwc),(nhwc->nchw)"
result = get_layout_values(argv_layout)
exp_res = [{'source_layout': 'nhwc', 'target_layout': None},
{'source_layout': 'nhwc', 'target_layout': 'nchw'}]
self.assertEqual(exp_res, result)
for i in range(len(exp_res)):
assert np.array_equal(result[i], exp_res[i])
def test_get_layout_3(self):
argv_layout = "(n...c),(n...c->nc...)"
result = get_layout_values(argv_layout)
exp_res = [{'source_layout': 'n...c', 'target_layout': None},
{'source_layout': 'n...c', 'target_layout': 'nc...'}]
self.assertEqual(exp_res, result)
for i in range(len(exp_res)):
assert np.array_equal(result[i], exp_res[i])
def test_get_layout_scalar(self):
argv_layout = "(nhwc),([])"
result = get_layout_values(argv_layout)
exp_res = [{'source_layout': 'nhwc', 'target_layout': None},
{'source_layout': '[]', 'target_layout': None}]
self.assertEqual(exp_res, result)
for i in range(len(exp_res)):
assert np.array_equal(result[i], exp_res[i])
def test_get_layout_source_layout_3(self):
argv_source_layout = "(nhwc),(nchw)"
result = get_layout_values(argv_source_layout=argv_source_layout)
exp_res = [{'source_layout': 'nhwc', 'target_layout': None},
{'source_layout': 'nchw', 'target_layout': None}]
self.assertEqual(exp_res, result)
for i in range(len(exp_res)):
assert np.array_equal(result[i], exp_res[i])
def test_get_layout_source_layout_4(self):
argv_source_layout = "([n,h,w,c]),([n,c,h,w])"
result = get_layout_values(argv_source_layout=argv_source_layout)
exp_res = [{'source_layout': '[n,h,w,c]', 'target_layout': None},
{'source_layout': '[n,c,h,w]', 'target_layout': None}]
self.assertEqual(exp_res, result)
for i in range(len(exp_res)):
assert np.array_equal(result[i], exp_res[i])
def test_get_layout_source_layout_5(self):
argv_source_layout = "(nhwc),([n,c,h,w])"
result = get_layout_values(argv_source_layout=argv_source_layout)
exp_res = [{'source_layout': 'nhwc', 'target_layout': None},
{'source_layout': '[n,c,h,w]', 'target_layout': None}]
self.assertEqual(exp_res, result)
for i in range(len(exp_res)):
assert np.array_equal(result[i], exp_res[i])
def test_get_layout_source_layout_6(self):
argv_source_layout = "(nhwc),[n,c,h,w]"
result = get_layout_values(argv_source_layout=argv_source_layout)
exp_res = [{'source_layout': 'nhwc', 'target_layout': None},
{'source_layout': '[n,c,h,w]', 'target_layout': None}]
self.assertEqual(exp_res, result)
for i in range(len(exp_res)):
assert np.array_equal(result[i], exp_res[i])
def test_get_layout_source_layout_scalar(self):
argv_source_layout = "(nhwc),([])"
result = get_layout_values(argv_source_layout=argv_source_layout)
exp_res = [{'source_layout': 'nhwc', 'target_layout': None},
{'source_layout': '[]', 'target_layout': None}]
self.assertEqual(exp_res, result)
for i in range(len(exp_res)):
assert np.array_equal(result[i], exp_res[i])
def test_get_layout_target_layout_3(self):
argv_target_layout = "(nhwc),(nchw)"
result = get_layout_values(argv_target_layout=argv_target_layout)
exp_res = [{'source_layout': None, 'target_layout': 'nhwc'},
{'source_layout': None, 'target_layout': 'nchw'}]
self.assertEqual(exp_res, result)
for i in range(len(exp_res)):
assert np.array_equal(result[i], exp_res[i])
def test_get_layout_target_layout_4(self):
argv_target_layout = "([n,h,w,c]),([n,c,h,w])"
result = get_layout_values(argv_target_layout=argv_target_layout)
exp_res = [{'source_layout': None, 'target_layout': '[n,h,w,c]'},
{'source_layout': None, 'target_layout': '[n,c,h,w]'}]
self.assertEqual(exp_res, result)
for i in range(len(exp_res)):
assert np.array_equal(result[i], exp_res[i])
def test_get_layout_target_layout_5(self):
argv_target_layout = "(nhwc),([n,c,h,w])"
result = get_layout_values(argv_target_layout=argv_target_layout)
exp_res = [{'source_layout': None, 'target_layout': 'nhwc'},
{'source_layout': None, 'target_layout': '[n,c,h,w]'}]
self.assertEqual(exp_res, result)
for i in range(len(exp_res)):
assert np.array_equal(result[i], exp_res[i])
def test_get_layout_target_layout_6(self):
argv_target_layout = "(nhwc),[n,c,h,w]"
result = get_layout_values(argv_target_layout=argv_target_layout)
exp_res = [{'source_layout': None, 'target_layout': 'nhwc'},
{'source_layout': None, 'target_layout': '[n,c,h,w]'}]
self.assertEqual(exp_res, result)
for i in range(len(exp_res)):
assert np.array_equal(result[i], exp_res[i])
def test_get_layout_target_layout_scalar(self):
argv_target_layout = "(nhwc),[]"
result = get_layout_values(argv_target_layout=argv_target_layout)
exp_res = [{'source_layout': None, 'target_layout': 'nhwc'},
{'source_layout': None, 'target_layout': '[]'}]
self.assertEqual(exp_res, result)
for i in range(len(exp_res)):
assert np.array_equal(result[i], exp_res[i])
def test_get_layout_source_target_layout_3(self):
argv_source_layout = "(nhwc),(nhwc)"
argv_target_layout = "(nchw),(nchw)"
result = get_layout_values(argv_source_layout=argv_source_layout, argv_target_layout=argv_target_layout)
exp_res = [{'source_layout': 'nhwc', 'target_layout': 'nchw'},
{'source_layout': 'nhwc', 'target_layout': 'nchw'}]
self.assertEqual(exp_res, result)
for i in range(len(exp_res)):
assert np.array_equal(result[i], exp_res[i])
def test_get_layout_source_target_layout_4(self):
argv_source_layout = "([n,h,w,c]),([n,h,w,c])"
argv_target_layout = "([n,c,h,w]),([n,c,h,w])"
result = get_layout_values(argv_source_layout=argv_source_layout, argv_target_layout=argv_target_layout)
exp_res = [{'source_layout': '[n,h,w,c]', 'target_layout': '[n,c,h,w]'},
{'source_layout': '[n,h,w,c]', 'target_layout': '[n,c,h,w]'}]
self.assertEqual(exp_res, result)
for i in range(len(exp_res)):
assert np.array_equal(result[i], exp_res[i])
def test_get_layout_source_target_layout_5(self):
argv_source_layout = "(nhwc),[n,h,w,c]"
argv_target_layout = "(nchw),[n,c,h,w]"
result = get_layout_values(argv_source_layout=argv_source_layout, argv_target_layout=argv_target_layout)
exp_res = [{'source_layout': 'nhwc', 'target_layout': 'nchw'},
{'source_layout': '[n,h,w,c]', 'target_layout': '[n,c,h,w]'}]
self.assertEqual(exp_res, result)
for i in range(len(exp_res)):
assert np.array_equal(result[i], exp_res[i])
def test_get_layout_source_target_layout_scalar(self):
argv_source_layout = "(nhwc),[]"
argv_target_layout = "(nchw),[]"
result = get_layout_values(argv_source_layout=argv_source_layout, argv_target_layout=argv_target_layout)
exp_res = [{'source_layout': 'nhwc', 'target_layout': 'nchw'},
{'source_layout': '[]', 'target_layout': '[]'}]
self.assertEqual(exp_res, result)
for i in range(len(exp_res)):
assert np.array_equal(result[i], exp_res[i])
class TestLayoutParsingEmptyNamesNoBrackets(unittest.TestCase):
def test_get_layout_1(self):
argv_layout = "[n,h,w,c],[n,h,w,c]->[n,c,h,w]"
result = get_layout_values(argv_layout)
exp_res = [{'source_layout': '[n,h,w,c]', 'target_layout': None},
{'source_layout': '[n,h,w,c]', 'target_layout': '[n,c,h,w]'}]
self.assertEqual(exp_res, result)
for i in range(len(exp_res)):
assert np.array_equal(result[i], exp_res[i])
def test_get_layout_2(self):
argv_layout = "nhwc,nhwc->nchw"
result = get_layout_values(argv_layout)
exp_res = [{'source_layout': 'nhwc', 'target_layout': None},
{'source_layout': 'nhwc', 'target_layout': 'nchw'}]
self.assertEqual(exp_res, result)
for i in range(len(exp_res)):
assert np.array_equal(result[i], exp_res[i])
def test_get_layout_3(self):
argv_layout = "n...c,n...c->nc..."
result = get_layout_values(argv_layout)
exp_res = [{'source_layout': 'n...c', 'target_layout': None},
{'source_layout': 'n...c', 'target_layout': 'nc...'}]
self.assertEqual(exp_res, result)
for i in range(len(exp_res)):
assert np.array_equal(result[i], exp_res[i])
def test_get_layout_scalar(self):
argv_layout = "nhwc,[]"
result = get_layout_values(argv_layout)
exp_res = [{'source_layout': 'nhwc', 'target_layout': None},
{'source_layout': '[]', 'target_layout': None}]
self.assertEqual(exp_res, result)
for i in range(len(exp_res)):
assert np.array_equal(result[i], exp_res[i])
def test_get_layout_source_layout_3(self):
argv_source_layout = "nhwc,nchw"
result = get_layout_values(argv_source_layout=argv_source_layout)
exp_res = [{'source_layout': 'nhwc', 'target_layout': None},
{'source_layout': 'nchw', 'target_layout': None}]
self.assertEqual(exp_res, result)
for i in range(len(exp_res)):
assert np.array_equal(result[i], exp_res[i])
def test_get_layout_source_layout_4(self):
argv_source_layout = "[n,h,w,c],[n,c,h,w]"
result = get_layout_values(argv_source_layout=argv_source_layout)
exp_res = [{'source_layout': '[n,h,w,c]', 'target_layout': None},
{'source_layout': '[n,c,h,w]', 'target_layout': None}]
self.assertEqual(exp_res, result)
for i in range(len(exp_res)):
assert np.array_equal(result[i], exp_res[i])
def test_get_layout_source_layout_5(self):
argv_source_layout = "nhwc,[n,c,h,w]"
result = get_layout_values(argv_source_layout=argv_source_layout)
exp_res = [{'source_layout': 'nhwc', 'target_layout': None},
{'source_layout': '[n,c,h,w]', 'target_layout': None}]
self.assertEqual(exp_res, result)
for i in range(len(exp_res)):
assert np.array_equal(result[i], exp_res[i])
def test_get_layout_source_layout_6(self):
argv_source_layout = "nhwc,[n,c,h,w]"
result = get_layout_values(argv_source_layout=argv_source_layout)
exp_res = [{'source_layout': 'nhwc', 'target_layout': None},
{'source_layout': '[n,c,h,w]', 'target_layout': None}]
self.assertEqual(exp_res, result)
for i in range(len(exp_res)):
assert np.array_equal(result[i], exp_res[i])
def test_get_layout_source_layout_scalar(self):
argv_source_layout = "nhwc,[]"
result = get_layout_values(argv_source_layout=argv_source_layout)
exp_res = [{'source_layout': 'nhwc', 'target_layout': None},
{'source_layout': '[]', 'target_layout': None}]
self.assertEqual(exp_res, result)
for i in range(len(exp_res)):
assert np.array_equal(result[i], exp_res[i])
def test_get_layout_target_layout_3(self):
argv_target_layout = "nhwc,nchw"
result = get_layout_values(argv_target_layout=argv_target_layout)
exp_res = [{'source_layout': None, 'target_layout': 'nhwc'},
{'source_layout': None, 'target_layout': 'nchw'}]
self.assertEqual(exp_res, result)
for i in range(len(exp_res)):
assert np.array_equal(result[i], exp_res[i])
def test_get_layout_target_layout_4(self):
argv_target_layout = "[n,h,w,c],[n,c,h,w]"
result = get_layout_values(argv_target_layout=argv_target_layout)
exp_res = [{'source_layout': None, 'target_layout': '[n,h,w,c]'},
{'source_layout': None, 'target_layout': '[n,c,h,w]'}]
self.assertEqual(exp_res, result)
for i in range(len(exp_res)):
assert np.array_equal(result[i], exp_res[i])
def test_get_layout_target_layout_5(self):
argv_target_layout = "nhwc,[n,c,h,w]"
result = get_layout_values(argv_target_layout=argv_target_layout)
exp_res = [{'source_layout': None, 'target_layout': 'nhwc'},
{'source_layout': None, 'target_layout': '[n,c,h,w]'}]
self.assertEqual(exp_res, result)
for i in range(len(exp_res)):
assert np.array_equal(result[i], exp_res[i])
def test_get_layout_target_layout_6(self):
argv_target_layout = "nhwc,[n,c,h,w]"
result = get_layout_values(argv_target_layout=argv_target_layout)
exp_res = [{'source_layout': None, 'target_layout': 'nhwc'},
{'source_layout': None, 'target_layout': '[n,c,h,w]'}]
self.assertEqual(exp_res, result)
for i in range(len(exp_res)):
assert np.array_equal(result[i], exp_res[i])
def test_get_layout_target_layout_scalar(self):
argv_target_layout = "nhwc,[]"
result = get_layout_values(argv_target_layout=argv_target_layout)
exp_res = [{'source_layout': None, 'target_layout': 'nhwc'},
{'source_layout': None, 'target_layout': '[]'}]
self.assertEqual(exp_res, result)
for i in range(len(exp_res)):
assert np.array_equal(result[i], exp_res[i])
def test_get_layout_source_target_layout_3(self):
argv_source_layout = "nhwc,nhwc"
with self.assertRaises(Error):
res = get_layout_values(argv_source_layout=argv_source_layout)
print(res)
def test_get_layout_raises_multiple_layouts_without_names_target_layout(self):
argv_target_layout = "nchw,nchw"
with self.assertRaises(Error):
res = get_layout_values(argv_target_layout=argv_target_layout)
print(res)
result = get_layout_values(argv_source_layout=argv_source_layout, argv_target_layout=argv_target_layout)
exp_res = [{'source_layout': 'nhwc', 'target_layout': 'nchw'},
{'source_layout': 'nhwc', 'target_layout': 'nchw'}]
self.assertEqual(exp_res, result)
for i in range(len(exp_res)):
assert np.array_equal(result[i], exp_res[i])
def test_get_layout_source_target_layout_4(self):
argv_source_layout = "[n,h,w,c],[n,h,w,c]"
argv_target_layout = "[n,c,h,w],[n,c,h,w]"
result = get_layout_values(argv_source_layout=argv_source_layout, argv_target_layout=argv_target_layout)
exp_res = [{'source_layout': '[n,h,w,c]', 'target_layout': '[n,c,h,w]'},
{'source_layout': '[n,h,w,c]', 'target_layout': '[n,c,h,w]'}]
self.assertEqual(exp_res, result)
for i in range(len(exp_res)):
assert np.array_equal(result[i], exp_res[i])
def test_get_layout_source_target_layout_5(self):
argv_source_layout = "nhwc,[n,h,w,c]"
argv_target_layout = "nchw,[n,c,h,w]"
result = get_layout_values(argv_source_layout=argv_source_layout, argv_target_layout=argv_target_layout)
exp_res = [{'source_layout': 'nhwc', 'target_layout': 'nchw'},
{'source_layout': '[n,h,w,c]', 'target_layout': '[n,c,h,w]'}]
self.assertEqual(exp_res, result)
for i in range(len(exp_res)):
assert np.array_equal(result[i], exp_res[i])
def test_get_layout_source_target_layout_scalar(self):
argv_source_layout = "nhwc,[]"
argv_target_layout = "nchw,[]"
result = get_layout_values(argv_source_layout=argv_source_layout, argv_target_layout=argv_target_layout)
exp_res = [{'source_layout': 'nhwc', 'target_layout': 'nchw'},
{'source_layout': '[]', 'target_layout': '[]'}]
self.assertEqual(exp_res, result)
for i in range(len(exp_res)):
assert np.array_equal(result[i], exp_res[i])
def wrong_case_1(self):
argv_source_layout = "[n,h,w,c]),[n,h,w,c]"
argv_target_layout = "[n,c,h,w],[n,c,h,w]"
self.assertRaises(get_layout_values(argv_source_layout=argv_source_layout, argv_target_layout=argv_target_layout))
def wrong_case_2(self):
argv_source_layout = "[nchv"
self.assertRaises(get_layout_values(argv_source_layout=argv_source_layout))
def wrong_case_3(self):
argv_source_layout = "nchv->"
self.assertRaises(get_layout_values(argv_source_layout=argv_source_layout))