convert_model() in openvino.runtime. (#18080)

* Used pip wheel to build OpenVINO wheel

* Added convert_model() to openvino.runtime.

* Removed duplication of InputCutInfo, LayoutMap

* Switched Model Conversion API tests to convert_model from openvino.runtime.

* Small correction.

* Format correction.

* Small correction.

* Removed duplication of moc frontend files.

* Small correction.

* Removed duplication of cli_parser, offline_transformations.

* Code corrections.

* Removed code duplications.

* Removed code duplications.

* Updated codeowners.

* Switched layer tests to convert_model().

* Improvements

* Small correction.

* Caffe parser path fix.

* Added python api properly into deb / rpm packages

* Moved implementation to ovc tool.

* Moved implementation to ovc tool.

* Small correction.

* Use cmake -E variant from cmake 3.13

* Namespace fixes.

* Minor fixes.

* Pylint fixes.

* Fixed BOM file.

* Small corrections.

* Minor corrections.

* Minor fix.

* Error fixes.

* Added telemetry requirement.

* Improvements to fix CI

* Some refactoring

* Don't use developer package for scripts projects

* Added exception in case when MO is not imported.

* Removed exception from init.

* Removed changes from cmake.

* Added unit ovc tests, fixed minor errors.

* Added ovc unit tests to azure.

* Corrected imports.

* Fixed path to tests.

* Added missed files.

* Corrected github labels.

* Removed benchmark app from dev package.

* Small fix.

* Small corrections.

* Comment fixed.

* Removed changes from setup.py

* Removed not needed change.

* Removed duplicating unit tests.

* Removed wrong change.

* Removed not needed change.

* Apply suggestions from code review

Co-authored-by: Roman Kazantsev <roman.kazantsev@intel.com>

* Added ovc tool test, corrected imports.

* Added legacy TF config test.

* Removed not needed files.

---------

Co-authored-by: Ilya Lavrenov <ilya.lavrenov@intel.com>
Co-authored-by: Roman Kazantsev <roman.kazantsev@intel.com>
This commit is contained in:
Anastasiia Pnevskaia 2023-06-30 16:26:14 +02:00 committed by GitHub
parent a2b7d561e4
commit 5d399faa64
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
133 changed files with 11059 additions and 3017 deletions

View File

@ -449,6 +449,10 @@ jobs:
python3 -m pytest -s $(INSTALL_TEST_DIR)/mo/unit_tests --junitxml=$(INSTALL_TEST_DIR)/TEST-ModelOptimizer.xml
displayName: 'Model Optimizer UT'
- script: |
python3 -m pytest -s $(REPO_DIR)/tools/ovc/unit_tests --junitxml=$(INSTALL_TEST_DIR)/TEST-OpenVinoConversion.xml
displayName: 'OpenVino Conversion UT'
- script: $(RUN_PREFIX) $(INSTALL_TEST_DIR)/ov_cpu_func_tests --gtest_filter=*smoke* --gtest_print_time=1 --gtest_output=xml:$(INSTALL_TEST_DIR)/TEST-ov_cpu_func_tests.xml
displayName: 'CPU FuncTests'
condition: and(succeeded(), eq(variables['CMAKE_BUILD_SHARED_LIBS'], 'OFF'))

1
.github/CODEOWNERS vendored
View File

@ -99,6 +99,7 @@
/tools/legacy/ @openvinotoolkit/openvino-samples-maintainers
/tools/openvino_dev/ @openvinotoolkit/openvino-tools-maintainers @openvinotoolkit/openvino-ie-python-api-maintainers
/tools/mo/ @openvinotoolkit/openvino-mo-maintainers
/tools/ovc/ @openvinotoolkit/openvino-mo-maintainers
/tools/pot/ @openvinotoolkit/openvino-pot-maintainers
/thirdparty/open_model_zoo/ @openvinotoolkit/omz-maintainers @openvinotoolkit/openvino-pot-maintainers

1
.github/labeler.yml vendored
View File

@ -87,6 +87,7 @@
'category: MO':
- 'tools/mo/**/*'
- 'tools/ovc/**/*'
'category: ONNX FE':
- 'src/frontends/onnx/**/*'

View File

@ -1,2 +1,3 @@
numpy>=1.16.6
singledispatchmethod; python_version<'3.8'
openvino-telemetry>=2023.0.0

View File

@ -5,9 +5,9 @@
# mypy: ignore-errors
from openvino.tools.mo.moc_frontend.shape_utils import get_static_shape
from openvino.tools.mo.utils.versions_checker import get_environment_setup # pylint: disable=no-name-in-module
from openvino.tools.mo.utils.error import Error
from openvino.tools.ovc.moc_frontend.shape_utils import get_static_shape
from openvino.tools.ovc.environment_setup_utils import get_environment_setup # pylint: disable=no-name-in-module
from openvino.tools.ovc.error import Error
from distutils.version import LooseVersion
import logging as log

View File

@ -67,6 +67,13 @@ from openvino.runtime.ie_api import tensor_from_file
from openvino.runtime.ie_api import compile_model
# Model Conversion API
try:
from openvino.tools.ovc import convert_model, InputCutInfo, LayoutMap
except ImportError:
pass
# Extend Node class to support binary operators
Node.__add__ = opset11.add
Node.__sub__ = opset11.subtract

View File

@ -175,6 +175,18 @@ PY_INSTALL_CFG = {
"install_dir": PY_PACKAGES_DIR,
"binary_dir": OPENVINO_PYTHON_BINARY_DIR,
},
"ovc": {
"entry_point": {
"console_scripts": [
"ovc = openvino.tools.ovc.main:main",
],
},
"name": f"pyopenvino_{PYTHON_VERSION}",
"prefix": f"{BUILD_BASE}/site-packages",
"source_dir": f"{OPENVINO_SOURCE_DIR}/tools/ovc",
"install_dir": PY_PACKAGES_DIR,
"binary_dir": "ovc",
},
# "benchmark_app": { # noqa: E731
# "entry_point": { # noqa: E731
# "console_scripts": [ # noqa: E731
@ -187,18 +199,6 @@ PY_INSTALL_CFG = {
# "install_dir": PY_PACKAGES_DIR, # noqa: E731
# "binary_dir": "benchmark_app", # noqa: E731
# }, # noqa: E731
# "model_optimizer": { # noqa: E731
# "entry_point": { # noqa: E731
# "console_scripts": [ # noqa: E731
# "mo = openvino.tools.mo.main:main", # noqa: E731
# ], # noqa: E731
# }, # noqa: E731
# "name": f"pyopenvino_{PYTHON_VERSION}", # noqa: E731
# "prefix": f"{BUILD_BASE}/site-packages", # noqa: E731
# "source_dir": f"{OPENVINO_SOURCE_DIR}/tools/mo", # noqa: E731
# "install_dir": PY_PACKAGES_DIR, # noqa: E731
# "binary_dir": "model_optimizer", # noqa: E731
# }, # noqa: E731
}

View File

@ -11,8 +11,7 @@ from pathlib import Path
import numpy as np
from common.constants import test_device, test_precision
from common.layer_utils import IEInfer, InferAPI20
from common.utils.common_utils import generate_ir
from common.utils.parsers import mapping_parser
from common.utils.common_utils import generate_ir_python_api
class CommonLayerTest:
@ -60,7 +59,7 @@ class CommonLayerTest:
else:
mo_params["use_legacy_frontend"] = True
exit_code, stderr = generate_ir(**mo_params)
exit_code, stderr = generate_ir_python_api(**mo_params)
del os.environ['MO_ENABLED_TRANSFORMS']
del os.environ['MO_DISABLED_TRANSFORMS']

View File

@ -3,9 +3,9 @@
from pathlib import Path
from openvino.runtime import serialize
from openvino.runtime import serialize, convert_model
from openvino.tools.mo import convert_model as legacy_convert_model
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,10 @@ class CommonMOConvertTest:
output_dir = kwargs['output_dir']
model_name = kwargs['model_name']
del kwargs['output_dir']
model = convert_model(**kwargs)
if 'use_legacy_frontend' in kwargs:
model = legacy_convert_model(**kwargs)
else:
model = convert_model(**kwargs)
serialize(model, str(Path(output_dir, model_name + '.xml')))
def _test(self, temp_dir, test_params, ref_params):

View File

@ -2,6 +2,7 @@
# SPDX-License-Identifier: Apache-2.0
import logging
import os
import shutil
import subprocess
import sys
@ -46,6 +47,20 @@ def generate_ir(coverage=False, **kwargs):
return exit_code, stderr
def generate_ir_python_api(coverage=False, **kwargs):
from openvino.runtime import convert_model, serialize
from openvino.tools.mo import convert_model as legacy_convert_model
if "use_legacy_frontend" in kwargs and kwargs['use_legacy_frontend']:
ov_model = legacy_convert_model(**kwargs)
else:
ov_model = convert_model(**kwargs)
out_dir = kwargs['output_dir'] + os.sep + kwargs['model_name'] + ".xml"
serialize(ov_model, out_dir)
return 0, ""
def shell(cmd, env=None, cwd=None, out_format="plain"):
"""
Run command execution in specified environment

View File

@ -1,7 +1,7 @@
# Copyright (C) 2018-2023 Intel Corporation
# SPDX-License-Identifier: Apache-2.0
from openvino.tools.mo import convert_model
from openvino.runtime import convert_model
if __name__ == "__main__":
convert_model(help=True)

View File

@ -4,8 +4,7 @@
import numpy as np
import os
import pytest
from openvino.runtime import Model, Layout, PartialShape, Shape, layout_helpers, Type, Dimension
from openvino.tools.mo.convert import InputCutInfo, LayoutMap
from openvino.runtime import Model, Layout, PartialShape, Shape, layout_helpers, Type, Dimension, InputCutInfo, LayoutMap
from common.mo_convert_test_class import CommonMOConvertTest
from common.tf_layer_test_class import save_to_pb

View File

@ -9,8 +9,7 @@ import openvino.runtime as ov
import pytest
import torch
import unittest
from openvino.runtime import PartialShape, Dimension, Model, Type
from openvino.tools.mo import InputCutInfo
from openvino.runtime import PartialShape, Dimension, Model, Type, InputCutInfo
from common.mo_convert_test_class import CommonMOConvertTest
@ -747,7 +746,7 @@ def create_pt_model_with_custom_op():
class ConvertRaises(unittest.TestCase):
def test_example_inputs(self):
from openvino.tools.mo import convert_model
from openvino.runtime import convert_model
pytorch_model = create_pt_model_with_custom_op()
# Check that mo raises error message of wrong argument.

View File

@ -666,7 +666,7 @@ class TFConvertTest(unittest.TestCase):
@pytest.mark.precommit
def test_tf_function_no_signature(self):
import tensorflow as tf
from openvino.tools.mo import convert_model
from openvino.runtime import convert_model
@tf.function()
def function(x1, x2):

View File

@ -6,7 +6,7 @@ import os
import sys
import unittest
from openvino.tools.mo import mo
from openvino.tools.mo.utils.cli_parser import get_mo_convert_params
from openvino.tools.ovc.cli_parser import get_mo_convert_params
from pathlib import Path
from common.utils.common_utils import shell

View File

@ -0,0 +1,89 @@
# Copyright (C) 2018-2023 Intel Corporation
# SPDX-License-Identifier: Apache-2.0
import os
import sys
from pathlib import Path
import numpy as np
import openvino.runtime as ov
from openvino.runtime import PartialShape, Model
from openvino.test_utils import compare_functions
from openvino.tools.ovc import ovc
from common.mo_convert_test_class import CommonMOConvertTest
from common.tf_layer_test_class import save_to_pb
from common.utils.common_utils import shell
def generate_ir_ovc(coverage=False, **kwargs):
# Get OVC file directory
ovc_path = Path(ovc.__file__).parent
ovc_runner = ovc_path.joinpath('main.py').as_posix()
if coverage:
params = [sys.executable, '-m', 'coverage', 'run', '-p', '--source={}'.format(ovc_runner.parent),
'--omit=*_test.py', ovc_runner]
else:
params = [sys.executable, ovc_runner]
for key, value in kwargs.items():
if key == "batch":
params.extend(("-b", str(value)))
elif key == "k":
params.extend(("-k", str(value)))
# for FP32 set explicitly compress_to_fp16=False,
# if we omit this argument for FP32, it will be set implicitly to True as the default
elif key == 'compress_to_fp16':
params.append("--{}={}".format(key, value))
elif isinstance(value, bool) and value:
params.append("--{}".format(key))
elif isinstance(value, bool) and not value:
continue
elif (isinstance(value, tuple) and value) or (isinstance(value, str)):
params.extend(("--{}".format(key), str('"{}"'.format(value))))
elif key == "mean_values" and (' ' in value or '(' in value):
params.extend(("--{}".format(key), str('"{}"'.format(value))))
else:
params.extend(("--{}".format(key), str(value)))
exit_code, stdout, stderr = shell(params)
return exit_code, stderr
def create_ref_graph():
shape = PartialShape([1, 3, 2, 2])
param = ov.opset8.parameter(shape, dtype=np.float32)
relu = ov.opset8.relu(param)
sigm = ov.opset8.sigmoid(relu)
return Model([sigm], [param], "test")
class TestOVCTool(CommonMOConvertTest):
def create_tf_model(self, tmp_dir):
import tensorflow as tf
tf.compat.v1.reset_default_graph()
with tf.compat.v1.Session() as sess:
inp = tf.compat.v1.placeholder(tf.float32, [1, 3, 2, 2], 'Input')
relu = tf.nn.relu(inp, name='Relu')
output = tf.nn.sigmoid(relu, name='Sigmoid')
tf.compat.v1.global_variables_initializer()
tf_net = sess.graph_def
# save model to .pb and return path to the model
return save_to_pb(tf_net, tmp_dir)
def test_ovc_tool(self, ie_device, precision, ir_version, temp_dir, use_new_frontend, use_old_api):
from openvino.runtime import Core
model_path = self.create_tf_model(temp_dir)
core = Core()
# tests for MO cli tool
exit_code, stderr = generate_ir_ovc(coverage=False, **{"input_model": model_path, "output_dir": temp_dir})
assert not exit_code
ov_model = core.read_model(os.path.join(temp_dir, "model.xml"))
flag, msg = compare_functions(ov_model, create_ref_graph(), False)
assert flag, msg

View File

@ -139,7 +139,7 @@ class PytorchLayerTest:
def convert_via_mo(self, model, example_input, trace_model, dynamic_shapes, ov_inputs):
import torch
from openvino.tools.mo import convert_model
from openvino.runtime import convert_model
kwargs = {"example_input": example_input if len(
example_input) > 1 else example_input[0], "compress_to_fp16": False}
with torch.no_grad():

View File

@ -36,10 +36,15 @@ add_subdirectory(mo)
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/pot/openvino/tools/pot/version.txt.in"
"${CMAKE_CURRENT_SOURCE_DIR}/pot/openvino/tools/pot/version.txt" @ONLY)
# Benchmark Tool
if(ENABLE_PYTHON)
# Benchmark Tool
add_subdirectory(benchmark_tool)
# OpenVino Conversion Tool
add_subdirectory(ovc)
endif()
# wheel openvino-dev

View File

@ -41,12 +41,10 @@ openvino/tools/mo/back/MatMulNormalizer.py
openvino/tools/mo/back/MaxPool.py
openvino/tools/mo/back/names_uniqueness_check.py
openvino/tools/mo/back/NormalizeToNormalizeL2.py
openvino/tools/mo/back/offline_transformations.py
openvino/tools/mo/back/op_versioning.py
openvino/tools/mo/back/OptimizeTransposeReshapeSequence.py
openvino/tools/mo/back/PackBinaryWeights.py
openvino/tools/mo/back/pass_separator.py
openvino/tools/mo/back/preprocessing.py
openvino/tools/mo/back/priorbox_mutation.py
openvino/tools/mo/back/ProposalMutation.py
openvino/tools/mo/back/ReduceMerge.py
@ -831,16 +829,6 @@ openvino/tools/mo/mo_mxnet.py
openvino/tools/mo/mo_onnx.py
openvino/tools/mo/mo_paddle.py
openvino/tools/mo/mo_tf.py
openvino/tools/mo/moc_frontend/__init__.py
openvino/tools/mo/moc_frontend/analysis.py
openvino/tools/mo/moc_frontend/check_config.py
openvino/tools/mo/moc_frontend/extractor.py
openvino/tools/mo/moc_frontend/layout_utils.py
openvino/tools/mo/moc_frontend/paddle_frontend_utils.py
openvino/tools/mo/moc_frontend/pipeline.py
openvino/tools/mo/moc_frontend/pytorch_frontend_utils.py
openvino/tools/mo/moc_frontend/serialize.py
openvino/tools/mo/moc_frontend/shape_utils.py
openvino/tools/mo/ops/__init__.py
openvino/tools/mo/ops/activation.py
openvino/tools/mo/ops/activation_ops.py
@ -1038,7 +1026,6 @@ openvino/tools/mo/utils/find_inputs.py
openvino/tools/mo/utils/get_ov_update_message.py
openvino/tools/mo/utils/graph.py
openvino/tools/mo/utils/guess_framework.py
openvino/tools/mo/utils/help.py
openvino/tools/mo/utils/ie_version.py
openvino/tools/mo/utils/import_extensions.py
openvino/tools/mo/utils/ir_engine/__init__.py
@ -1098,12 +1085,10 @@ openvino/tools/mo/utils/shape.py
openvino/tools/mo/utils/simple_proto_parser.py
openvino/tools/mo/utils/str_to.py
openvino/tools/mo/utils/summarize_graph.py
openvino/tools/mo/utils/telemetry_params.py
openvino/tools/mo/utils/telemetry_stub.py
openvino/tools/mo/utils/telemetry_utils.py
openvino/tools/mo/utils/tensorboard_util.py
openvino/tools/mo/utils/type_utils.py
openvino/tools/mo/utils/unsupported_ops.py
openvino/tools/mo/utils/utils.py
openvino/tools/mo/utils/version.py
openvino/tools/mo/utils/versions_checker.py
openvino/tools/mo/utils/version.py

View File

@ -1,4 +1,5 @@
# Copyright (C) 2018-2023 Intel Corporation
# SPDX-License-Identifier: Apache-2.0
from .convert import convert_model, InputCutInfo, LayoutMap
from openvino.tools.mo.convert import convert_model
from openvino.tools.ovc import InputCutInfo, LayoutMap # pylint: disable=no-name-in-module,import-error

View File

@ -6,7 +6,7 @@ from openvino.tools.mo.back.replacement import BackReplacementPattern
from openvino.tools.mo.front.common.partial_infer.utils import mo_array
from openvino.tools.mo.graph.graph import Graph
from openvino.tools.mo.ops.crop import Crop
from openvino.tools.mo.utils.logger import log
from openvino.tools.ovc.logger import log # pylint: disable=no-name-in-module,import-error
class CutMemoryInput(BackReplacementPattern):

View File

@ -2,16 +2,13 @@
# SPDX-License-Identifier: Apache-2.0
import os
import pathlib
from collections import namedtuple
from typing import Any
from openvino.runtime import PartialShape, Shape, Layout, Model
from openvino.tools.mo.convert_impl import _convert
from openvino.tools.mo.utils.cli_parser import get_all_cli_parser
from openvino.tools.mo.utils.logger import get_logger_state, restore_logger_state
InputCutInfo = namedtuple("InputInfo", ["name", "shape", "type", "value"], defaults=[None, None, None, None])
LayoutMap = namedtuple("LayoutMap", ["source_layout", "target_layout"], defaults=[None, None])
from openvino.tools.ovc import InputCutInfo, LayoutMap # pylint: disable=no-name-in-module,import-error
from openvino.tools.ovc.cli_parser import get_all_cli_parser # pylint: disable=no-name-in-module,import-error
from openvino.tools.ovc.logger import get_logger_state, restore_logger_state # pylint: disable=no-name-in-module,import-error
def convert_model(
@ -68,8 +65,8 @@ def convert_model(
# Caffe*-specific parameters:
input_proto: [str, pathlib.Path] = None,
caffe_parser_path: [str, pathlib.Path] = os.path.join(os.path.dirname(__file__), 'front', 'caffe', 'proto'),
k: [str, pathlib.Path] = os.path.join(os.path.dirname(__file__), 'front', 'caffe', 'CustomLayersMapping.xml'),
caffe_parser_path: [str, pathlib.Path] = None,
k: [str, pathlib.Path] = None,
disable_omitting_optional: bool = False,
enable_flattening_nested_params: bool = False,

View File

@ -10,7 +10,6 @@ import sys
import traceback
from collections import OrderedDict
from copy import deepcopy
from distutils.version import LooseVersion
from pathlib import Path
try:
@ -19,17 +18,19 @@ except ImportError:
import openvino.tools.mo.utils.telemetry_stub as tm
from openvino.tools.mo.back.SpecialNodesFinalization import RemoveConstOps, CreateConstNodesReplacement, NormalizeTI
from openvino.tools.mo.moc_frontend.check_config import legacy_transformations_config_used, \
tensorflow_custom_operations_config_update_used, new_extensions_used
from openvino.tools.mo.moc_frontend.pipeline import moc_pipeline
from openvino.tools.mo.moc_frontend.serialize import moc_emit_ir
from openvino.tools.ovc.moc_frontend.check_config import legacy_transformations_config_used, \
tensorflow_custom_operations_config_update_used, new_extensions_used # pylint: disable=no-name-in-module,import-error
from openvino.tools.ovc.moc_frontend.pipeline import moc_pipeline # pylint: disable=no-name-in-module,import-error
from openvino.tools.ovc.moc_frontend.moc_emit_ir import moc_emit_ir # pylint: disable=no-name-in-module,import-error
from openvino.tools.mo.graph.graph import Graph
from openvino.tools.mo.middle.pattern_match import for_graph_and_each_sub_graph_recursively
from openvino.tools.mo.middle.passes.convert_data_type import destination_type_to_np_data_type
from openvino.tools.mo.pipeline.common import prepare_emit_ir
from openvino.tools.mo.pipeline.unified import unified_pipeline
from openvino.tools.mo.utils import import_extensions
from openvino.tools.mo.utils.cli_parser import check_available_transforms, \
# pylint: disable=no-name-in-module,import-error
from openvino.tools.ovc.cli_parser import check_available_transforms, \
get_advanced_cli_options, get_available_front_ends, get_caffe_cli_options, \
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, \
@ -38,19 +39,20 @@ from openvino.tools.mo.utils.cli_parser import check_available_transforms, \
input_shape_to_input_cut_info, freeze_placeholder_to_input_cut_info
from openvino.tools.mo.utils.error import Error, FrameworkError
from openvino.tools.mo.utils.get_ov_update_message import get_ov_update_message, get_ov_api20_message, \
get_tf_fe_message, get_try_legacy_fe_message, get_compression_message
from openvino.tools.ovc.get_ov_update_message import get_ov_update_message, get_ov_api20_message, \
get_tf_fe_message, get_compression_message # pylint: disable=no-name-in-module,import-error
from openvino.tools.mo.utils.get_ov_update_message import get_try_legacy_fe_message
from openvino.tools.mo.utils.model_analysis import AnalysisResults
from openvino.tools.mo.utils.version import VersionChecker
from openvino.tools.mo.utils.guess_framework import deduce_legacy_frontend_by_namespace
from openvino.tools.mo.utils.logger import init_logger, progress_printer
from openvino.tools.ovc.logger import init_logger, progress_printer # pylint: disable=no-name-in-module,import-error
from openvino.tools.mo.utils.utils import refer_to_faq_msg, check_values_equal
from openvino.tools.mo.utils.telemetry_utils import send_params_info, send_framework_info, send_conversion_result, \
get_tid
from openvino.tools.mo.moc_frontend.check_config import legacy_extensions_used
from openvino.tools.mo.moc_frontend.pytorch_frontend_utils import get_pytorch_decoder, extract_input_info_from_example
from openvino.tools.mo.moc_frontend.paddle_frontend_utils import paddle_frontend_converter
from openvino.tools.mo.moc_frontend.shape_utils import parse_input_shapes
from openvino.tools.ovc.moc_frontend.check_config import legacy_extensions_used # pylint: disable=no-name-in-module,import-error
from openvino.tools.ovc.moc_frontend.pytorch_frontend_utils import get_pytorch_decoder, extract_input_info_from_example # pylint: disable=no-name-in-module,import-error
from openvino.tools.ovc.moc_frontend.paddle_frontend_utils import paddle_frontend_converter # pylint: disable=no-name-in-module,import-error
from openvino.tools.ovc.moc_frontend.shape_utils import parse_input_shapes # pylint: disable=no-name-in-module,import-error
# pylint: disable=no-name-in-module,import-error
from openvino.frontend import FrontEndManager, OpConversionFailure, ProgressReporterExtension, TelemetryExtension
@ -491,7 +493,7 @@ def emit_ir(graph: Graph, argv: argparse.Namespace, non_default_params: dict):
return_code = "not executed"
if not (argv.framework == 'tf' and argv.tensorflow_custom_operations_config_update):
try:
from openvino.tools.mo.back.offline_transformations import apply_offline_transformations
from openvino.tools.ovc.moc_frontend.offline_transformations import apply_offline_transformations # pylint: disable=no-name-in-module,import-error
func = apply_offline_transformations(func, argv)
if "compress_to_fp16" in argv and argv.compress_to_fp16:
# restore data_type cmd parameter
@ -841,7 +843,7 @@ def _convert(cli_parser: argparse.ArgumentParser, framework, args, python_api_us
elif 'example_inputs' in args:
raise AssertionError("'example_inputs' argument is not recognized, maybe you meant to provide 'example_input'?")
decoder = get_pytorch_decoder(args['input_model'], parse_input_shapes(args), example_inputs, args)
decoder = get_pytorch_decoder(args['input_model'], parse_input_shapes(args), example_inputs, args)
if model_framework == "paddle":
example_inputs = None
if 'example_input' in args and args['example_input'] is not None:
@ -951,6 +953,6 @@ def _convert(cli_parser: argparse.ArgumentParser, framework, args, python_api_us
send_conversion_result('fail')
if python_api_used:
raise e#.with_traceback(None)
raise e.with_traceback(None)
else:
return None, argv

View File

@ -11,7 +11,7 @@ from pathlib import Path
from openvino.tools.mo.graph.graph import Node
from openvino.tools.mo.utils.error import Error, FrameworkError
from openvino.tools.mo.utils.utils import refer_to_faq_msg
from openvino.tools.mo.utils.versions_checker import get_environment_setup
from openvino.tools.ovc.environment_setup_utils import get_environment_setup # pylint: disable=no-name-in-module,import-error
# do not print INFO and WARNING messages from TensorFlow
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'

View File

@ -1,6 +1,7 @@
# Copyright (C) 2018-2023 Intel Corporation
# SPDX-License-Identifier: Apache-2.0
import os
from openvino.tools.mo.load.loader import Loader
from openvino.tools.mo.front.caffe import custom_layers_mapping, loader
from openvino.tools.mo.front.caffe.extractor import caffe_type_extractors, caffe_extractor
@ -17,6 +18,8 @@ class CaffeLoader(Loader):
def load(self, graph: Graph):
argv = graph.graph['cmd_params']
if argv.caffe_parser_path is None:
argv.caffe_parser_path = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))), 'front', 'caffe', 'proto')
caffe_pb2 = loader.import_caffe_pb2(argv.caffe_parser_path)
proto, model = loader.load_caffe_proto_model(caffe_pb2, argv.input_proto, argv.input_model)
@ -42,6 +45,8 @@ class CaffeLoader(Loader):
graph.graph['original_shapes'] = original_shapes
graph.graph['caffe_pb2'] = caffe_pb2
if argv.k is None:
argv.k = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))), 'front', 'caffe', 'CustomLayersMapping.xml')
custom_layers_map = custom_layers_mapping.load_layers_xml(argv.k)
custom_layers_mapping.update_extractors(
caffe_type_extractors,

View File

@ -34,5 +34,5 @@ def main(cli_parser: argparse.ArgumentParser, framework=None):
if __name__ == "__main__":
from openvino.tools.mo.utils.cli_parser import get_all_cli_parser
from openvino.tools.ovc.cli_parser import get_all_cli_parser # pylint: disable=no-name-in-module,import-error
sys.exit(main(get_all_cli_parser(), None))

View File

@ -3,7 +3,7 @@
import sys
from openvino.tools.mo.utils.cli_parser import get_caffe_cli_parser
from openvino.tools.ovc.cli_parser import get_caffe_cli_parser # pylint: disable=no-name-in-module,import-error
if __name__ == "__main__":
from openvino.tools.mo.main import main

View File

@ -3,7 +3,7 @@
import sys
from openvino.tools.mo.utils.cli_parser import get_kaldi_cli_parser
from openvino.tools.ovc.cli_parser import get_kaldi_cli_parser # pylint: disable=no-name-in-module,import-error
if __name__ == "__main__":
from openvino.tools.mo.main import main

View File

@ -3,7 +3,7 @@
import sys
from openvino.tools.mo.utils.cli_parser import get_mxnet_cli_parser
from openvino.tools.ovc.cli_parser import get_mxnet_cli_parser # pylint: disable=no-name-in-module,import-error
if __name__ == "__main__":
from openvino.tools.mo.main import main

View File

@ -3,7 +3,7 @@
import sys
from openvino.tools.mo.utils.cli_parser import get_onnx_cli_parser
from openvino.tools.ovc.cli_parser import get_onnx_cli_parser # pylint: disable=no-name-in-module,import-error
if __name__ == "__main__":
from openvino.tools.mo.main import main

View File

@ -3,7 +3,7 @@
import sys
from openvino.tools.mo.utils.cli_parser import get_all_cli_parser
from openvino.tools.ovc.cli_parser import get_all_cli_parser # pylint: disable=no-name-in-module,import-error
from openvino.frontend import FrontEndManager # pylint: disable=no-name-in-module,import-error

View File

@ -3,7 +3,7 @@
import sys
from openvino.tools.mo.utils.cli_parser import get_tf_cli_parser
from openvino.tools.ovc.cli_parser import get_tf_cli_parser # pylint: disable=no-name-in-module,import-error
if __name__ == "__main__":
from openvino.tools.mo.main import main

View File

@ -10,88 +10,10 @@ from openvino.tools.mo.graph.graph import Node, Graph
from openvino.tools.mo.utils.error import Error
from openvino.tools.mo.utils.utils import refer_to_faq_msg
"""
Packed data of custom types are stored in numpy uint8 data type.
To distinguish true uint8 and custom data we introduce this class not to store,
but to have unique data type in SUPPORTED_DATA_TYPES map
"""
class packed_U1(np.generic):
pass
class packed_U4(np.generic):
pass
class packed_I4(np.generic):
pass
SUPPORTED_DATA_TYPES = {
'float': (np.float32, 'FP32', 'f32'),
'half': (np.float16, 'FP16', 'f16'),
'FP32': (np.float32, 'FP32', 'f32'),
'FP64': (np.float64, 'FP64', 'f64'),
'FP16': (np.float16, 'FP16', 'f16'),
'I32': (np.int32, 'I32', 'i32'),
'I64': (np.int64, 'I64', 'i64'),
'int8': (np.int8, 'I8', 'i8'),
'int32': (np.int32, 'I32', 'i32'),
'int64': (np.int64, 'I64', 'i64'),
'bool': (bool, 'BOOL', 'boolean'),
'uint8': (np.uint8, 'U8', 'u8'),
'uint32': (np.uint32, 'U32', 'u32'),
'uint64': (np.uint64, 'U64', 'u64'),
# custom types
'U1': (packed_U1, 'U1', 'u1'),
'int4': (packed_I4, 'I4', 'i4'),
'uint4': (packed_U4, 'U4', 'u4'),
'I4': (packed_I4, 'I4', 'i4'),
'U4': (packed_U4, 'U4', 'u4'),
}
def data_type_str_to_np(data_type_str: str):
return SUPPORTED_DATA_TYPES[data_type_str][0] if data_type_str in SUPPORTED_DATA_TYPES else None
def data_type_str_to_precision(data_type_str: str):
return SUPPORTED_DATA_TYPES[data_type_str][1] if data_type_str in SUPPORTED_DATA_TYPES else None
def data_type_str_to_destination_type(data_type_str: str):
return SUPPORTED_DATA_TYPES[data_type_str][2] if data_type_str in SUPPORTED_DATA_TYPES else None
def np_data_type_to_precision(np_data_type):
for np_t, precision, _ in SUPPORTED_DATA_TYPES.values():
if np_t == np_data_type:
return precision
raise Error('Data type "{}" is not supported'.format(np_data_type))
def np_data_type_to_destination_type(np_data_type):
for np_t, _, destination_type in SUPPORTED_DATA_TYPES.values():
if np_t == np_data_type:
return destination_type
raise Error('Data type "{}" is not supported'.format(np_data_type))
def destination_type_to_np_data_type(dst_type):
for np_t, _, destination_type in SUPPORTED_DATA_TYPES.values():
if destination_type == dst_type:
return np_t
raise Error('Destination type "{}" is not supported'.format(dst_type))
def precision_to_destination_type(data_type_str):
for _, precision, destination_type in SUPPORTED_DATA_TYPES.values():
if precision == data_type_str:
return destination_type
raise Error('Data type "{}" is not supported'.format(data_type_str))
# pylint: disable=no-name-in-module,import-error
from openvino.tools.ovc.convert_data_type import packed_U1, packed_U4, packed_I4, SUPPORTED_DATA_TYPES, \
data_type_str_to_np, data_type_str_to_precision, data_type_str_to_destination_type, np_data_type_to_precision, \
np_data_type_to_destination_type, destination_type_to_np_data_type, precision_to_destination_type
def convert_blob(blob: np.ndarray, dst_type: type):

View File

@ -12,8 +12,7 @@ from openvino.tools.mo.graph.graph import Graph
from openvino.tools.mo.middle.passes.eliminate import shape_inference
from openvino.tools.mo.middle.pattern_match import for_graph_and_each_sub_graph_recursively
from openvino.tools.mo.utils.error import Error, InternalError, FrameworkError
from openvino.tools.mo.utils.logger import progress_bar
from openvino.tools.mo.utils.utils import refer_to_faq_msg
from openvino.tools.ovc.logger import progress_bar # pylint: disable=no-name-in-module,import-error
_registered_classes_dict = {}

File diff suppressed because it is too large Load Diff

View File

@ -1,49 +1,4 @@
# Copyright (C) 2018-2023 Intel Corporation
# SPDX-License-Identifier: Apache-2.0
import re
class BasicError(Exception):
""" Base class for all exceptions in Model Optimizer
It operates like Exception but when it is converted to str,
it formats string as args[0].format(*args[1:]), where
args are arguments provided when an exception instance is
created.
"""
def __str__(self):
if len(self.args) <= 1:
return Exception.__str__(self)
return self.args[0].format(*self.args[1:]) # pylint: disable=unsubscriptable-object
class FrameworkError(BasicError):
""" User-friendly error: raised when the error on the framework side. """
pass
class Error(BasicError):
""" User-friendly error: raised when the error on the user side. """
pass
class InternalError(BasicError):
""" Not user-friendly error: user cannot fix it and it points to the bug inside MO. """
pass
def classify_error_type(e):
patterns = [
# Example: No module named 'openvino._offline_transformations.offline_transformations_api'
r"No module named \'\S+\'",
# Example: cannot import name 'IECore' from 'openvino.inference_engine' (unknown location)
r"cannot import name \'\S+\'",
]
error_message = str(e)
for pattern in patterns:
m = re.search(pattern, error_message)
if m:
return m.group(0)
return "undefined"
from openvino.tools.ovc.error import Error, InternalError, FrameworkError, classify_error_type # pylint: disable=no-name-in-module,import-error

View File

@ -1,48 +1,6 @@
# Copyright (C) 2018-2023 Intel Corporation
# SPDX-License-Identifier: Apache-2.0
import datetime
msg_fmt = 'Check for a new version of Intel(R) Distribution of OpenVINO(TM) toolkit here {0} ' \
'or on https://github.com/openvinotoolkit/openvino'
def get_ov_update_message():
expected_update_date = datetime.date(year=2023, month=12, day=1)
current_date = datetime.date.today()
link = 'https://software.intel.com/content/www/us/en/develop/tools/openvino-toolkit/download.html?cid=other&source=prod&campid=ww_2023_bu_IOTG_OpenVINO-2023-0&content=upg_all&medium=organic'
return msg_fmt.format(link) if current_date >= expected_update_date else None
def get_ov_api20_message():
link = "https://docs.openvino.ai/2023.0/openvino_2_0_transition_guide.html"
message = '[ INFO ] The model was converted to IR v11, the latest model format that corresponds to the source DL framework ' \
'input/output format. While IR v11 is backwards compatible with OpenVINO Inference Engine API v1.0, ' \
'please use API v2.0 (as of 2022.1) to take advantage of the latest improvements in IR v11.\n' \
'Find more information about API v2.0 and IR v11 at {}'.format(link)
return message
def get_tf_fe_message():
link = "https://docs.openvino.ai/2023.0/openvino_docs_MO_DG_TensorFlow_Frontend.html"
message = '[ INFO ] IR generated by new TensorFlow Frontend is compatible only with API v2.0. Please make sure to use API v2.0.\n' \
'Find more information about new TensorFlow Frontend at {}'.format(link)
return message
def get_compression_message():
link = "https://docs.openvino.ai/2023.0/openvino_docs_MO_DG_FP16_Compression.html"
message = '[ INFO ] Generated IR will be compressed to FP16. ' \
'If you get lower accuracy, please consider disabling compression ' \
'by removing argument --compress_to_fp16 or set it to false --compress_to_fp16=False.\n' \
'Find more information about compression to FP16 at {}'.format(link)
return message
def get_try_legacy_fe_message():
message = '[ INFO ] You can also try to use legacy TensorFlow Frontend by using argument --use_legacy_frontend.\n'
return message

View File

@ -1,160 +1,6 @@
# Copyright (C) 2018-2023 Intel Corporation
# SPDX-License-Identifier: Apache-2.0
import importlib.util
import logging as log
import os
import re
import sys
from argparse import Namespace
from copy import copy
# WA for abseil bug that affects logging while importing TF starting 1.14 version
# Link to original issue: https://github.com/abseil/abseil-py/issues/99
if importlib.util.find_spec('absl') is not None:
import absl.logging
log.root.removeHandler(absl.logging._absl_handler)
handler_num = 0
class LvlFormatter(log.Formatter):
format_dict = {
log.DEBUG: "[ %(asctime)s ] [ %(levelname)s ] [ %(module)s:%(lineno)d ] %(msg)s",
log.INFO: "[ %(levelname)s ] %(msg)s",
log.WARNING: "[ WARNING ] %(msg)s",
log.ERROR: "[ %(levelname)s ] %(msg)s",
log.CRITICAL: "[ %(levelname)s ] %(msg)s",
'framework_error': "[ FRAMEWORK ERROR ] %(msg)s",
'analysis_info': "[ ANALYSIS INFO ] %(msg)s"
}
def __init__(self, lvl, fmt=None):
log.Formatter.__init__(self, fmt)
self.lvl = lvl
def format(self, record: log.LogRecord):
if self.lvl == 'DEBUG':
self._style._fmt = self.format_dict[log.DEBUG]
else:
self._style._fmt = self.format_dict[record.levelno]
if 'is_warning' in record.__dict__.keys():
self._style._fmt = self.format_dict[log.WARNING]
if 'framework_error' in record.__dict__.keys():
self._style._fmt = self.format_dict['framework_error']
if 'analysis_info' in record.__dict__.keys():
self._style._fmt = self.format_dict['analysis_info']
return log.Formatter.format(self, record)
class TagFilter(log.Filter):
def __init__(self, regex: str):
self.regex = regex
def filter(self, record: log.LogRecord):
if record.__dict__['funcName'] == 'load_grammar': # for nx not to log into our logs
return False
if self.regex:
if 'tag' in record.__dict__.keys():
tag = record.__dict__['tag']
return re.findall(self.regex, tag)
else:
return False
return True # if regex wasn't set print all logs
def init_logger(lvl: str, silent: bool):
global handler_num
log_exp = os.environ.get('MO_LOG_PATTERN')
if silent:
lvl = 'ERROR'
fmt = LvlFormatter(lvl=lvl)
handler = log.StreamHandler()
handler.setFormatter(fmt)
logger = log.getLogger()
logger.setLevel(lvl)
logger.addFilter(TagFilter(regex=log_exp))
if handler_num == 0 and len(logger.handlers) == 0:
logger.addHandler(handler)
handler_num += 1
def get_logger_state():
logger = log.getLogger()
return logger.level, copy(logger.filters), copy(logger.handlers)
def restore_logger_state(state: tuple):
level, filters, handlers = state
logger = log.getLogger()
logger.setLevel(level)
logger.filters = filters
logger.handlers = handlers
def progress_bar(function: callable):
"""
Decorator for model conversion pipeline progress display
Works in combination with function: mo.utils.class_registration.apply_transform
"""
def wrapper(*args, **kwargs):
for arg in ['graph', 'curr_transform_num', 'num_transforms']:
msg = 'Progress bar decorator is enabled for Model Optimizer transformation applying cycle only. ' \
'Argument `{}` {}'
assert arg in kwargs, msg.format(arg, 'is missing')
assert kwargs[arg] is not None, msg.format(arg, 'should not be None')
if 'progress' in kwargs['graph'].graph['cmd_params'] and kwargs['graph'].graph['cmd_params'].progress:
bar_len = 20
total_replacers_count = kwargs['num_transforms']
def progress(i):
return int((i + 1) / total_replacers_count * bar_len)
def percent(i):
return (i + 1) / total_replacers_count * 100
end = '' if not kwargs['graph'].graph['cmd_params'].stream_output else '\n'
curr_i = kwargs['curr_transform_num']
print('\rProgress: [{:{}}]{:>7.2f}% done'.format('.' * progress(curr_i), bar_len, percent(curr_i)), end=end)
sys.stdout.flush()
function(*args, **kwargs)
return wrapper
def progress_printer(argv: Namespace):
"""
A higher-order factory function returning a configurable callback displaying a progress bar
Depending on the configuration stored in 'argv' the progress bar can be one-line, multi-line, or silent.
"""
def _progress_bar(progress, total, completed, endline):
bar_len = 20
def dots():
return '.' * int(progress * bar_len)
print('\rProgress: [{:{}}]{:>7.2f}% done'.format(dots(), bar_len, progress*100), end=endline)
sys.stdout.flush()
def no_progress_bar(progress, total, completed):
""" A 'dummy' progressbar which doesn't print anything """
pass
def oneline_progress_bar(progress, total, completed):
""" A callback that always prints the progress in the same line (mimics real GUI progress bar)"""
_progress_bar(progress, total, completed, '')
def newline_progress_bar(progress, total, completed):
""" A callback that prints an updated progress bar in separate lines """
_progress_bar(progress, total, completed, '\n')
if "progress" in argv and argv.progress:
if "stream_output" in argv and argv.stream_output:
return newline_progress_bar
else:
return oneline_progress_bar
else:
return no_progress_bar
from openvino.tools.ovc.logger import init_logger, LvlFormatter, TagFilter, get_logger_state, restore_logger_state, \
progress_bar, progress_printer # pylint: disable=no-name-in-module,import-error

View File

@ -1,29 +1,4 @@
# Copyright (C) 2018-2023 Intel Corporation
# SPDX-License-Identifier: Apache-2.0
class Telemetry(object):
"""
Stab file for the Telemetry class which is used when Telemetry class is not available.
"""
def __init__(self, *arg, **kwargs):
pass
def send_event(self, *arg, **kwargs):
pass
def send_error(self, *arg, **kwargs):
pass
def start_session(self, *arg, **kwargs):
pass
def end_session(self, *arg, **kwargs):
pass
def force_shutdown(self, *arg, **kwargs):
pass
def send_stack_trace(self, *arg, **kwargs):
pass
from openvino.tools.ovc.telemetry_stub import Telemetry # pylint: disable=no-name-in-module,import-error

View File

@ -1,18 +1,15 @@
# Copyright (C) 2018-2023 Intel Corporation
# SPDX-License-Identifier: Apache-2.0
import argparse
from collections import Counter
import numpy as np
import numbers
from openvino.tools.ovc.telemetry_utils import init_mo_telemetry, send_framework_info, get_tid, \
send_conversion_result, arg_to_str, send_params_info # pylint: disable=no-name-in-module,import-error
from openvino.tools.mo.front.common.partial_infer.utils import is_fully_defined, unmask_shape, int64_array
from openvino.tools.mo.graph.graph import Graph
from openvino.tools.mo.middle.pattern_match import for_graph_and_each_sub_graph_recursively
from openvino.tools.mo.utils.cli_parser import get_params_with_paths_list
from openvino.tools.mo.utils.telemetry_params import telemetry_params
from openvino.tools.mo.utils.version import VersionChecker
from openvino.tools.mo.utils.utils import check_values_equal
try:
import openvino_telemetry as tm
@ -20,10 +17,6 @@ except ImportError:
import openvino.tools.mo.utils.telemetry_stub as tm
def init_mo_telemetry():
_ = tm.Telemetry(tid=get_tid(), app_name='Model Optimizer', app_version=VersionChecker().get_mo_simplified_version())
def send_op_names_info(framework: str, graph: Graph):
"""
This function sends information about operations in model.
@ -68,58 +61,3 @@ def send_shapes_info(framework: str, graph: Graph):
t.send_event('mo', 'input_shapes', message_str)
t.send_event('mo', 'partially_defined_shape',
"{partially_defined_shape:" + is_partially_defined + ",fw:" + framework + "}")
def arg_to_str(arg):
# This method converts to string only known types, otherwise returns string with name of the type
from openvino.runtime import PartialShape, Shape, Type, Layout
if isinstance(arg, (PartialShape, Shape, Type, Layout)):
return str(arg)
if isinstance(arg, (str, numbers.Number, bool)):
return str(arg)
return str(type(arg))
def send_params_info(argv: argparse.Namespace, cli_parser: argparse.ArgumentParser):
"""
This function sends information about used command line parameters.
:param argv: command line parameters.
:param cli_parser: command line parameters parser.
"""
t = tm.Telemetry()
params_with_paths = get_params_with_paths_list()
for arg in vars(argv):
arg_value = getattr(argv, arg)
if not check_values_equal(arg_value, cli_parser.get_default(arg)):
if arg in params_with_paths:
# If command line argument value is a directory or a path to file it is not sent
# as it may contain confidential information. "1" value is used instead.
param_str = arg + ":" + str(1)
else:
param_str = arg + ":" + arg_to_str(arg_value)
t.send_event('mo', 'cli_parameters', param_str)
def send_framework_info(framework: str):
"""
This function sends information about used framework.
:param framework: framework name.
"""
t = tm.Telemetry()
t.send_event('mo', 'framework', framework)
def get_tid():
"""
This function returns the ID of the database to send telemetry.
"""
return telemetry_params['TID']
def send_conversion_result(conversion_result: str, need_shutdown=True):
t = tm.Telemetry()
t.send_event('mo', 'conversion_result', conversion_result)
t.end_session('mo')
if need_shutdown:
t.force_shutdown(1.0)

View File

@ -10,24 +10,7 @@ from typing import Callable
import numpy as np
from openvino.tools.mo.front.common.partial_infer.utils import dynamic_dimension
try:
import openvino_telemetry as tm
except ImportError:
import openvino.tools.mo.utils.telemetry_stub as tm
def refer_to_faq_msg(question_num: int):
try:
t = tm.Telemetry()
t.send_event('mo', 'error_info', "faq:" + str(question_num))
except Exception:
# Telemetry can be not initialized if it is used in MO IR Reader
pass
return '\n For more information please refer to Model Optimizer FAQ, question #{0}. ' \
'(https://docs.openvino.ai/2023.0/openvino_docs_MO_DG_prepare_model_Model_Optimizer_FAQ.html' \
'?question={0}#question-{0})'.format(question_num)
from openvino.tools.ovc.utils import refer_to_faq_msg, check_values_equal # pylint: disable=no-name-in-module,import-error
class NamedAttrsClass:
@ -145,14 +128,3 @@ def unique_by(xs: list, predicate: Callable) -> list:
"""
groups = group_by_with_binary_predicate(xs, predicate)
return [group[0] for group in groups]
def check_values_equal(val1, val2):
# This method is needed to check equality of values where some values can be None
if val1 is None and val2 is None:
return True
if val1 is None:
return False
if val2 is None:
return False
return val1 == val2

View File

@ -11,6 +11,8 @@ from openvino.runtime import get_version as get_ie_version
from openvino.tools.mo.utils.error import Error
from openvino.tools.mo.utils.find_ie_version import find_ie_version
from openvino.tools.mo.utils.utils import get_mo_root_dir
from openvino.tools.ovc.version import extract_release_version, simplify_version, extract_hash_from_version, \
SingletonMetaClass # pylint: disable=no-name-in-module,import-error
def get_version_file_path():
@ -39,28 +41,6 @@ def get_version():
return f.readline().replace('\n', '')
def extract_release_version(version: str):
patterns = [
# captures release version set by CI for example: '2021.1.0-1028-55e4d5673a8'
r"^([0-9]+).([0-9]+)*",
# captures release version generated by MO from release branch, for example: 'custom_releases/2021/1_55e4d567'
r"_releases/([0-9]+)/([0-9]+)_*"
]
for pattern in patterns:
m = re.search(pattern, version)
if m and len(m.groups()) == 2:
return m.group(1), m.group(2)
return None, None
def simplify_version(version: str):
release_version = extract_release_version(version)
if release_version == (None, None):
return "custom"
return "{}.{}".format(*release_version)
def get_simplified_mo_version():
return simplify_version(get_version())
@ -79,25 +59,6 @@ def get_simplified_ie_version(env=dict(), version=None):
return simplify_version(version)
def extract_hash_from_version(full_version: str):
res = re.findall(r'[-_]([a-f0-9]{7,40})', full_version)
if len(res) > 0:
return res[0]
else:
return None
class SingletonMetaClass(type):
def __init__(self, cls_name, super_classes, dic):
self.__single_instance = None
super().__init__(cls_name, super_classes, dic)
def __call__(cls, *args, **kwargs):
if cls.__single_instance is None:
cls.__single_instance = super(SingletonMetaClass, cls).__call__(*args, **kwargs)
return cls.__single_instance
class VersionChecker(metaclass=SingletonMetaClass):
def __init__(self):
self.runtime_checked = False

View File

@ -9,7 +9,7 @@ from unit_tests.mo.unit_test_with_mocked_telemetry import UnitTestWithMockedTele
try:
# pylint: disable=no-name-in-module,import-error
from openvino.tools.mo.back.preprocessing import apply_preprocessing
from openvino.tools.ovc.moc_frontend.preprocessing import apply_preprocessing
# pylint: disable=no-name-in-module,import-error
import openvino.runtime.opset8 as ops

View File

@ -3,7 +3,7 @@
import unittest
from openvino.tools.mo.moc_frontend.extractor import decode_name_with_port
from openvino.tools.ovc.moc_frontend.extractor import decode_name_with_port
from openvino.tools.mo.utils.error import Error
import pytest

View File

@ -22,7 +22,7 @@ class TestConvertImplTmpIrsCleanup(unittest.TestCase):
return False
def test_tmp_irs_cleanup_convert_impl_1(self):
with patch("openvino.tools.mo.back.offline_transformations.apply_offline_transformations") as emit_ir_func:
with patch("openvino.tools.ovc.moc_frontend.offline_transformations.apply_offline_transformations") as emit_ir_func:
emit_ir_func.side_effect = Error('offline transformations step has failed')
params = {'input_model': self.test_model_file, 'input_model_is_text': True, 'input': 'x[3],y[1 3]',

View File

@ -11,7 +11,7 @@ import openvino.runtime.opset10 as opset10
from openvino.runtime import Model, serialize, Core, PartialShape, Dimension
from openvino.tools.mo.utils.ir_reader.restore_graph import restore_graph_from_ir, save_restored_graph
from openvino.tools.mo.utils.logger import init_logger
from openvino.tools.ovc.logger import init_logger
# required to be in global area to run MO IR Reader
init_logger('ERROR', False)

View File

@ -11,7 +11,7 @@ import json
import argparse
from openvino.tools.mo.convert_impl import prepare_ir
from openvino.frontend import FrontEndManager # pylint: disable=no-name-in-module,import-error
from openvino.tools.mo.moc_frontend.analysis import json_model_analysis_dump
try:
import openvino_telemetry as tm
@ -112,7 +112,7 @@ class TestMoFallback(unittest.TestCase):
os.remove(name)
@patch('openvino.tools.mo.moc_frontend.analysis.json_model_analysis_print')
@patch('openvino.tools.ovc.moc_frontend.analysis.json_model_analysis_print')
def test_model(self, json_print):
args = base_args_config()
args.input_model = "test_model.onnx"
@ -132,7 +132,7 @@ class TestMoFallback(unittest.TestCase):
"add_out": {"shape": "None", "data_type": "None", "value": "None"}}')
@patch('openvino.tools.mo.moc_frontend.analysis.json_model_analysis_print')
@patch('openvino.tools.ovc.moc_frontend.analysis.json_model_analysis_print')
def test_model_with_dyn_shapes(self, json_print):
args = base_args_config()
args.input_model = "test_model_2.onnx"
@ -156,7 +156,7 @@ class TestMoFallback(unittest.TestCase):
"add_out": {"shape": "None", "data_type": "None", "value": "None"}}')
@patch('openvino.tools.mo.moc_frontend.analysis.json_model_analysis_print')
@patch('openvino.tools.ovc.moc_frontend.analysis.json_model_analysis_print')
def test_multi_outputs_model(self, json_print):
args = base_args_config()
args.input_model = "test_model_3.onnx"

View File

@ -9,8 +9,7 @@ from contextlib import redirect_stdout
from unittest.mock import patch
from openvino.tools.mo.main import main
from openvino.tools.mo.utils.get_ov_update_message import get_tf_fe_message, get_compression_message, \
get_try_legacy_fe_message
from openvino.tools.ovc.get_ov_update_message import get_tf_fe_message
def arg_parse_helper(input_model,
@ -56,36 +55,6 @@ def arg_parse_helper(input_model,
)
class TestInfoMessagesTFFE(unittest.TestCase):
@patch('argparse.ArgumentParser.parse_args',
return_value=arg_parse_helper(input_model="model_int32.pbtxt",
use_legacy_frontend=False, use_new_frontend=True,
framework=None, input_model_is_text=True))
def test_api20_only(self, mock_argparse):
f = io.StringIO()
with redirect_stdout(f):
main(argparse.ArgumentParser())
std_out = f.getvalue()
tf_fe_message_found = get_tf_fe_message() in std_out
assert tf_fe_message_found
@patch('openvino.tools.mo.convert_impl.driver', side_effect=Exception('MESSAGE'))
def run_fail_tf_fe(self, mock_driver):
from openvino.tools.mo import convert_model
path = os.path.dirname(__file__)
convert_model(os.path.join(path, "test_models", "model_int32.pbtxt"), silent=False)
def test_suggest_legacy_fe(self):
f = io.StringIO()
with redirect_stdout(f):
try:
self.run_fail_tf_fe()
except:
pass
std_out = f.getvalue()
assert get_try_legacy_fe_message() in std_out
class TestInfoMessagesTFFEWithFallback(unittest.TestCase):
@patch('argparse.ArgumentParser.parse_args',
return_value=arg_parse_helper(input_model="model_switch_merge.pbtxt",
@ -100,17 +69,3 @@ class TestInfoMessagesTFFEWithFallback(unittest.TestCase):
tf_fe_message_found = get_tf_fe_message() in std_out
assert not tf_fe_message_found, 'TF FE Info message is found for the fallback case'
class TestInfoMessagesCompressFP16(unittest.TestCase):
@patch('argparse.ArgumentParser.parse_args',
return_value=arg_parse_helper(input_model="model_int32.pbtxt",
use_legacy_frontend=False, use_new_frontend=True,
compress_to_fp16=True,
framework=None, input_model_is_text=True))
def test_compress_to_fp16(self, mock_argparse):
f = io.StringIO()
with redirect_stdout(f):
main(argparse.ArgumentParser())
std_out = f.getvalue()
fp16_compression_message_found = get_compression_message() in std_out
assert fp16_compression_message_found

View File

@ -40,272 +40,10 @@ class TestMoFreezePlaceholderTFFE(unittest.TestCase):
assert values.dtype == dtype
assert np.allclose(values, expected)
@generate(
*[
(
"in1[1 4]->[1.0 2.0 3.0 4.0],in2[1 4]{f32}->[1.0 2.0 3.0 4.0]",
{},
np.array([2.0, 4.0, 6.0, 8.0]),
np.float32,
),
(
"in2{f32}->[0.0 0.0 0.0 0.0]",
{"in1": np.array([[1.0, 2.0], [3.0, 4.0]])},
np.array([[1.0, 2.0], [3.0, 4.0]]),
np.float32,
),
(
"in2->[1.0 15.0 15.5 1.0]",
{"in1": np.array([[2.0, 4.0], [12.0, 8.0]])},
np.array([[3.0, 19.0], [27.5, 9.0]]),
np.float32,
),
(
"in1[1 4]{i32}->[1 2 3 4],in2[1 4]{i32}->[1 2 3 4]",
{},
np.array([2.0, 4.0, 6.0, 8.0]),
np.int32,
),
],
)
def test_fp32(self, input_freezing_value, inputs, expected,
dtype):
self.basic("model_fp32.pbtxt", input_freezing_value, inputs, dtype, expected)
@generate(
*[
(
"in1[1 4]->[1 2 3 4],in2[1 4]{i32}->[1 2 3 4]",
{},
np.array([1, 4, 9, 16]),
np.int32,
),
(
"in2->[2 5 6 7 3 2]",
{"in1": np.array([[2, 4, 1], [1, 2, 8]])},
np.array([[4, 20, 6], [7, 6, 16]]),
np.int32,
),
],
)
def test_int32(self, input_freezing_value, inputs, expected,
dtype=None):
self.basic("model_int32.pbtxt", input_freezing_value, inputs, dtype, expected)
@generate(
*[
(
"in1[2]->[True False],in2[2]->[True True]",
{},
np.array([True, False], dtype=bool),
bool,
),
(
"in2[2,3]->[True,True,False,True,True,False]",
{"in1": np.array([[False, True, True], [False, True, True]], dtype=bool)},
np.array([[False, True, False], [False, True, False]], dtype=bool),
bool,
),
(
"in2[]->True",
{"in1": np.array([[False, True, True], [False, True, True]], dtype=bool)},
np.array([[False, True, True], [False, True, True]], dtype=bool),
bool,
),
],
)
def test_bool(self, input_freezing_value, inputs, expected,
dtype=None):
self.basic("model_bool.pbtxt", input_freezing_value, inputs, dtype, expected)
@generate(
*[
(
"in1[3]->[1 2 3],in2[3]->[4 5 6],cond->False",
{},
np.array([4, 5, 6], dtype=np.float32),
np.float32,
None
),
(
None,
{"in1": np.array([2.0, 4.0, 6.0], dtype=np.float32),
"in2": np.array([1.0, 3.0, 5.0], dtype=np.float32)},
np.array([2, 4, 6], dtype=np.float32),
np.float32,
"cond->False",
None,
True # fill a bug to investigate why compilation of this model is hang on
),
# case: input_shape + freeze_placeholder_with_value
(
None,
{"in2": np.array([1.0, 3.0, 5.0], dtype=np.float32)},
np.array([2, 4, 6], dtype=np.float32),
np.float32,
"in1->[2.0 4.0 6.0],cond->True",
"[3]",
False
),
],
)
def test_bool2(self, input_freezing_value, inputs, expected,
dtype=None, freeze_placeholder_with_value=None, input_shape=None, only_conversion=False):
self.basic("model_bool2.pbtxt", input_freezing_value, inputs, dtype, expected, freeze_placeholder_with_value,
input_shape, only_conversion)
@generate(
*[
(
"add:0[3],z",
{"add:0": np.array([4, 5, 6], dtype=np.float32), "z": np.array([1, 2, 3], dtype=np.float32)},
np.array([4, 10, 18], dtype=np.float32),
np.float32,
None
),
(
"add:0{i32}[3],z{i32}",
{"add:0": np.array([4, 5, 6], dtype=np.int32), "z": np.array([1, 2, 3], dtype=np.int32)},
np.array([4, 10, 18], dtype=np.int32),
np.int32,
None
),
],
)
def test_cutting_fp32(self, input_freezing_value, inputs, expected,
dtype=None, freeze_placeholder_with_value=None, input_shape=None, only_conversion=False):
self.basic("model_three_inputs.pbtxt", input_freezing_value, inputs, dtype, expected,
freeze_placeholder_with_value,
input_shape, only_conversion, True)
@generate(
*[
(
"x[1,4],y[4]",
{"x": np.array([[3, 2, 1, 5]], dtype=np.int32), "y": np.array([0, -1, -7, 8], dtype=np.int32)},
np.array([[3, 1, -6, 13]], dtype=np.int32),
np.int32,
None
),
(
"x,y",
{"x": np.array([[-3, 20, 1]], dtype=np.int32), "y": np.array([[10, -11, -17]], dtype=np.int32)},
np.array([[7, 9, -16]], dtype=np.int32),
np.int32,
None
),
(
"x",
{"x": np.array([[-3, 20, 1]], dtype=np.int32)},
np.array([[-2, 22, 4], [1, 25, 7]], dtype=np.int32),
np.int32,
None
),
],
)
def test_placeholder_with_default(self, inputs, inputs_data, expected,
dtype=None, freeze_placeholder_with_value=None, input_shape=None,
only_conversion=False):
self.basic("placeholder_with_default.pbtxt", inputs, inputs_data, dtype, expected,
freeze_placeholder_with_value,
input_shape, only_conversion, True)
@generate(
*[
(
"x[4],y->2.0",
{"x": np.array([3, 2, 1, 5], dtype=np.float32)},
np.array([6, 4, 2, 10], dtype=np.float32),
np.float32,
None
),
(
"x[1],y->[2.0,3.0]",
{"x": np.array([3], dtype=np.float32)},
np.array([6, 9], dtype=np.float32),
np.float32,
None
),
],
)
def test_freeze_placeholder_with_unknown_rank(self, inputs, inputs_data, expected,
dtype=None, freeze_placeholder_with_value=None, input_shape=None,
only_conversion=False):
self.basic("mul_with_unknown_rank_y.pbtxt", inputs, inputs_data, dtype, expected,
freeze_placeholder_with_value,
input_shape, only_conversion, True)
def test_conversion_failure_fallback_default(self):
self.basic("ctc_model_based.pbtxt", None, None, None, None,
None, None, True, True, False, False)
def test_conversion_failure_fallback_use_new_frontend(self):
with self.assertRaisesRegex(Exception,
"\[TensorFlow Frontend\] Internal error, no translator found for operation\(s\)\: "
"Enter\, Exit\, LoopCond\, Merge\, NextIteration\, Switch\, TensorArrayGatherV3\, "
"TensorArraySizeV3\, TensorArrayV3"):
self.basic("ctc_model_based.pbtxt", None, None, None, None,
None, None, True, True, True, False)
@unittest.skip("88349: Fix auto-pruning in legacy FE")
def test_conversion_model_oneshot_iterator_use_legacy_frontend(self):
self.basic("model_oneshot_iterator.pbtxt", None, None, None, None,
None, None, True, True, False, True)
def test_conversion_model_oneshot_iterator_default(self):
self.basic("model_oneshot_iterator.pbtxt", None, None, None, None,
None, None, True, True, False, False)
@generate(
*[
(
"in2{f32}->[0.0 0.0 0.0 0.0]",
{"in1": np.array([[1.0, 2.0], [3.0, 4.0]])},
np.array([[1.0, 2.0], [3.0, 4.0]]),
np.float32,
),
(
"in2->[1.0 15.0 15.5 1.0]",
{"in1": np.array([[2.0, 4.0], [12.0, 8.0]])},
np.array([[3.0, 19.0], [27.5, 9.0]]),
np.float32,
),
],
)
@unittest.skip("109220: Use generating script for this test model instead of Git LFS")
def test_conversion_model_with_non_standard_extension(self, input_freezing_value, inputs, expected,
dtype):
self.basic("model_fp32.frozen", input_freezing_value, inputs, dtype, expected, only_conversion=False,
input_model_is_text=False, use_new_frontend=True,
use_legacy_frontend=False)
@unittest.skip("109220: Make TF FE to return the error")
def test_conversion_dir_model(self):
with self.assertRaisesRegex(Exception,
"Internal error or inconsistent input model: the frontend supports "
"only frozen binary protobuf format."):
self.basic(".", None, None, None, None,
only_conversion=True, input_model_is_text=False, use_new_frontend=True,
use_legacy_frontend=False)
@generate(
*[
(
{"x": np.array([1, 2], dtype=np.int32), "y": np.array([4], dtype=np.int32)},
np.array([-3, -2], dtype=np.int32),
np.int32,
),
(
{"x": np.array([20, 25], dtype=np.int32), "y": np.array([10], dtype=np.int32)},
np.array([30, 35], dtype=np.int32),
np.int32,
)
],
)
def test_conversion_pbtxt_model_with_inference(self, inputs, expected, dtype):
self.basic("model_with_if.pbtxt", None, inputs, dtype, expected, only_conversion=False,
input_model_is_text=False, use_new_frontend=True, use_legacy_frontend=False)
@generate(
*[
# legacy frontend
@ -323,21 +61,6 @@ class TestMoFreezePlaceholderTFFE(unittest.TestCase):
np.array([0, 0], dtype=np.int32),
np.int32, False, True,
),
# new frontend
(
"model_add_with_undefined_constant.pbtxt",
"x[2,3]",
{"x": np.array([[12, 13, 10], [11, 14, 16]], dtype=np.float32)},
np.array([[12, 13, 10], [11, 14, 16]], dtype=np.float32),
np.float32, True, False,
),
(
"model_mul_with_undefined_constant.pbtxt",
"x[2]",
{"x": np.array([11, -12], dtype=np.int32)},
np.array([0, 0], dtype=np.int32),
np.int32, True, False,
),
],
)
def test_conversion_model_with_undefined_constant(self, model_name, argv_input, inputs, expected, dtype,

View File

@ -1,7 +1,6 @@
# Copyright (C) 2018-2023 Intel Corporation
# SPDX-License-Identifier: Apache-2.0
import os
import tempfile
import unittest
@ -9,38 +8,8 @@ from generator import generator, generate
from openvino.tools.mo.convert import convert_model
@generator
class TestMoFreezePlaceholderTFFE(unittest.TestCase):
@generate(
*[
# the default frontend
(
False, False, None
),
(
False, False, "tf"
),
# new frontend
(
True, False, None
),
(
True, False, "tf"
),
],
)
def test_conversion_fake_pb_model(self, use_new_frontend, use_legacy_frontend, framework):
with self.assertRaisesRegex(Exception,
"Internal error or inconsistent input model: the frontend supports frozen formats"
" \(.pb and .pbtxt\), SavedModel and MetaGraph \(.meta\), and v1 checkpoints."):
path = os.path.dirname(__file__)
input_model = os.path.join(path, "test_models", "fake.pb")
convert_model(input_model,
use_new_frontend=use_new_frontend, use_legacy_frontend=use_legacy_frontend,
framework=framework)
@generate(
*[
# the default frontend

36
tools/ovc/CMakeLists.txt Normal file
View File

@ -0,0 +1,36 @@
# Copyright (C) 2018-2023 Intel Corporation
# SPDX-License-Identifier: Apache-2.0
#
cmake_minimum_required (VERSION 3.13)
project(OpenVINOConverter)
#
# Packages & settings
#
if(NOT DEFINED OpenVINO_SOURCE_DIR)
get_filename_component(OpenVINO_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../.." REALPATH)
endif()
if(NOT IEDevScripts_FOUND)
find_package(IEDevScripts REQUIRED
PATHS "${OpenVINO_SOURCE_DIR}/cmake/developer_package"
NO_CMAKE_FIND_ROOT_PATH
NO_DEFAULT_PATH)
endif()
#
# Installation rules
#
ov_get_pyversion(pyversion)
ov_cpack_add_component(${OV_CPACK_COMP_PYTHON_OPENVINO}_${pyversion}
HIDDEN)
install(DIRECTORY ${OpenVINOConverter_SOURCE_DIR}/openvino
DESTINATION ${OV_CPACK_PYTHONDIR}
COMPONENT ${OV_CPACK_COMP_PYTHON_OPENVINO}_${pyversion}
${OV_CPACK_COMP_PYTHON_OPENVINO_EXCLUDE_ALL}
USE_SOURCE_PERMISSIONS)

View File

@ -0,0 +1,12 @@
# Copyright (C) 2018-2023 Intel Corporation
# SPDX-License-Identifier: Apache-2.0
from openvino.tools.ovc.convert import convert_model, InputCutInfo, LayoutMap
try:
import openvino.runtime
openvino.runtime.convert_model = convert_model
openvino.runtime.InputCutInfo = InputCutInfo
openvino.runtime.LayoutMap = LayoutMap
except:
pass

View File

@ -0,0 +1,10 @@
# Copyright (C) 2018-2023 Intel Corporation
# SPDX-License-Identifier: Apache-2.0
import sys
from openvino.tools.ovc.telemetry_utils import init_mo_telemetry
from openvino.tools.ovc.main import main
init_mo_telemetry()
sys.exit(main())

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,366 @@
# Copyright (C) 2018-2023 Intel Corporation
# SPDX-License-Identifier: Apache-2.0
# flake8: noqa
# mypy: ignore-errors
import os
import pathlib
from collections import namedtuple
from typing import Any
from openvino.runtime import PartialShape, Shape, Layout, Model
from openvino.tools.ovc.convert_impl import _convert
from openvino.tools.ovc.logger import get_logger_state, restore_logger_state
from openvino.tools.ovc.cli_parser import get_all_cli_parser
InputCutInfo = namedtuple("InputInfo", ["name", "shape", "type", "value"], defaults=[None, None, None, None])
LayoutMap = namedtuple("LayoutMap", ["source_layout", "target_layout"], defaults=[None, None])
def convert_model(
input_model: [str, pathlib.Path, Any] = None,
# Optional parameters
help: bool = False,
framework: [str] = None,
# Framework-agnostic parameters
input: [str, list, tuple, InputCutInfo] = None,
output: [str, list] = None,
input_shape: [str, PartialShape, Shape, list] = None,
example_input: Any = None,
batch: int = None,
mean_values: [str, dict, list] = (),
scale_values: [str, dict, list] = (),
scale: [str, float] = None,
reverse_input_channels: bool = False,
source_layout: [str, Layout, dict] = (),
target_layout: [str, Layout, dict] = (),
layout: [str, Layout, LayoutMap, list, dict] = (),
compress_to_fp16: bool = False,
extensions: [str, pathlib.Path, list, Any] = None,
transform: [str, list, tuple] = "",
transformations_config: [str, pathlib.Path] = None,
silent: bool = True,
log_level: str = 'ERROR',
version: bool = None,
progress: bool = False,
stream_output: bool = False,
# PaddlePaddle-specific parameters:
example_output: Any = None,
# TensorFlow*-specific parameters
input_model_is_text: bool = None,
input_checkpoint: [str, pathlib.Path] = None,
input_meta_graph: [str, pathlib.Path] = None,
saved_model_dir: [str, pathlib.Path] = None,
saved_model_tags: [str, list] = None,
tensorflow_custom_operations_config_update: [str, pathlib.Path] = None,
tensorflow_object_detection_api_pipeline_config: [str, pathlib.Path] = None,
tensorboard_logdir: [str, pathlib.Path] = None,
tensorflow_custom_layer_libraries: [str, pathlib.Path] = None,
# MXNet-specific parameters:
input_symbol: [str, pathlib.Path] = None,
nd_prefix_name: str = None,
pretrained_model_name: str = None,
save_params_from_nd: bool = None,
legacy_mxnet_model: bool = None,
enable_ssd_gluoncv: bool = False,
# Caffe*-specific parameters:
input_proto: [str, pathlib.Path] = None,
caffe_parser_path: [str, pathlib.Path] = None,
k: [str, pathlib.Path] = None,
disable_omitting_optional: bool = False,
enable_flattening_nested_params: bool = False,
# Kaldi-specific parameters:
counts: [str, pathlib.Path] = None,
remove_output_softmax: bool = False,
remove_memory: bool = False,
**args
) -> Model:
"""
Converts the model from original framework to OpenVino Model.
Args:
:param help:
Print available parameters.
:param framework:
Name of the framework used to train the input model.
Framework-agnostic parameters:
:param 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:
PaddlePaddle
paddle.hapi.model.Model
paddle.fluid.dygraph.layers.Layer
paddle.fluid.executor.Executor
PyTorch
torch.nn.Module
torch.jit.ScriptModule
torch.jit.ScriptFunction
TF
tf.compat.v1.Graph
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
:param input:
Input can be set by passing a list of InputCutInfo objects or by a list
of tuples. Each tuple can contain optionally input name, input
type or input shape. Example: input=("op_name", PartialShape([-1,
3, 100, 100]), Type(np.float32)). Alternatively input can be set by
a string or list of strings of the following format. Quoted list of comma-separated
input nodes names with shapes, data types, and values for freezing.
If operation names are specified, the order of inputs in converted
model will be the same as order of specified operation names (applicable for TF2, ONNX, MxNet).
The shape and value are specified as comma-separated lists. The data type of input node is specified
in braces and can have one of the values: f64 (float64), f32 (float32), f16 (float16), i64
(int64), i32 (int32), u8 (uint8), boolean (bool). Data type is optional.
If it's not specified explicitly then there are two options: if input
node is a parameter, data type is taken from the original node dtype,
if input node is not a parameter, data type is set to f32. Example, to set
`input_1` with shape [1,100], and Parameter node `sequence_len` with
scalar input with value `150`, and boolean input `is_training` with
`False` value use the following format: "input_1[1,100],sequence_len->150,is_training->False".
Another example, use the following format to set input port 0 of the node
`node_name1` with the shape [3,4] as an input node and freeze output
port 1 of the node `node_name2` with the value [20,15] of the int32 type
and shape [2]: "0:node_name1[3,4],node_name2:1[2]{i32}->[20,15]".
:param output:
The name of the output operation of the model or list of names. For TensorFlow*,
do not add :0 to this name.The order of outputs in converted model is the
same as order of specified operation names.
:param input_shape:
Input shape(s) that should be fed to an input node(s) of the model. Input
shapes can be defined by passing a list of objects of type PartialShape,
Shape, [Dimension, ...] or [int, ...] or by a string of the following
format. Shape is defined as a comma-separated list of integer numbers
enclosed in parentheses or square brackets, for example [1,3,227,227]
or (1,227,227,3), where the order of dimensions depends on the framework
input layout of the model. For example, [N,C,H,W] is used for ONNX* models
and [N,H,W,C] for TensorFlow* models. The shape can contain undefined
dimensions (? or -1) and should fit the dimensions defined in the input
operation of the graph. Boundaries of undefined dimension can be specified
with ellipsis, for example [1,1..10,128,128]. One boundary can be
undefined, for example [1,..100] or [1,3,1..,1..]. If there are multiple
inputs in the model, "input_shape" should contain definition of shape
for each input separated by a comma, for example: [1,3,227,227],[2,4]
for a model with two inputs with 4D and 2D shapes. Alternatively, specify
shapes with the "input" option.
:param example_input:
Sample of model input in original framework.
For PyTorch it can be torch.Tensor.
For Tensorflow it can be tf.Tensor or numpy.ndarray.
For PaddlePaddle it can be Paddle Variable.
:param batch:
Set batch size. It applies to 1D or higher dimension inputs.
The default dimension index for the batch is zero.
Use a label 'n' in "layout" or "source_layout" option to set the batch dimension.
For example, "x(hwnc)" defines the third dimension to be the batch.
:param mean_values:
Mean values to be used for the input image per channel. Mean values can
be set by passing a dictionary, where key is input name and value is mean
value. For example mean_values={'data':[255,255,255],'info':[255,255,255]}.
Or mean values can be set by a string of the following format. Values to
be provided in the (R,G,B) or [R,G,B] format. Can be defined for desired
input of the model, for example: mean_values="data[255,255,255],info[255,255,255]".
The exact meaning and order of channels depend on how the original model
was trained.
:param scale_values:
Scale values to be used for the input image per channel. Scale values
can be set by passing a dictionary, where key is input name and value is
scale value. For example scale_values={'data':[255,255,255],'info':[255,255,255]}.
Or scale values can be set by a string of the following format. Values
are provided in the (R,G,B) or [R,G,B] format. Can be defined for desired
input of the model, for example: scale_values="data[255,255,255],info[255,255,255]".
The exact meaning and order of channels depend on how the original model
was trained. If both "mean_values" and "scale_values" are specified,
the mean is subtracted first and then scale is applied regardless of
the order of options in command line.
:param scale:
All input values coming from original network inputs will be divided
by this value. When a list of inputs is overridden by the "input" parameter,
this scale is not applied for any input that does not match with the original
input of the model. If both "mean_values" and "scale" are specified,
the mean is subtracted first and then scale is applied regardless of
the order of options in command line.
:param reverse_input_channels:
Switch the input channels order from RGB to BGR (or vice versa). Applied
to original inputs of the model if and only if a number of channels equals
3. When "mean_values"/"scale_values" are also specified, reversing
of channels will be applied to user's input data first, so that numbers
in "mean_values" and "scale_values" go in the order of channels used
in the original model. In other words, if both options are specified,
then the data flow in the model looks as following: Parameter -> ReverseInputChannels
-> Mean apply-> Scale apply -> the original body of the model.
:param source_layout:
Layout of the input or output of the model in the framework. Layout can
be set by passing a dictionary, where key is input name and value is LayoutMap
object. Or layout can be set by string of the following format. Layout
can be specified in the short form, e.g. nhwc, or in complex form, e.g.
"[n,h,w,c]". Example for many names: "in_name1([n,h,w,c]),in_name2(nc),out_name1(n),out_name2(nc)".
Layout can be partially defined, "?" can be used to specify undefined
layout for one dimension, "..." can be used to specify undefined layout
for multiple dimensions, for example "?c??", "nc...", "n...c", etc.
:param target_layout:
Same as "source_layout", but specifies target layout that will be in
the model after processing by ModelOptimizer.
:param layout:
Combination of "source_layout" and "target_layout". Can't be used
with either of them. If model has one input it is sufficient to specify
layout of this input, for example "layout" nhwc. To specify layouts
of many tensors, names must be provided, for example: layout="name1(nchw),name2(nc)".
It is possible to instruct ModelOptimizer to change layout, for example:
layout="name1(nhwc->nchw),name2(cn->nc)".
Also "*" in long layout form can be used to fuse dimensions, for example "[n,c,...]->[n*c,...]".
:param compress_to_fp16:
If the original model has FP32 weights or biases, they are compressed
to FP16. All intermediate data is kept in original precision. Option
can be specified alone as "compress_to_fp16", or explicit True/False
values can be set, for example: "compress_to_fp16=False", or "compress_to_fp16=True"
:param extensions:
Paths to libraries (.so or .dll) with extensions, comma-separated
list of paths, objects derived from BaseExtension class or lists of
objects. For the legacy MO path (if "use_legacy_frontend" is used),
a directory or a comma-separated list of directories with extensions
are supported. To disable all extensions including those that are placed
at the default location, pass an empty string.
:param transform:
Apply additional transformations. 'transform' can be set by a list
of tuples, where the first element is transform name and the second element
is transform parameters. For example: [('LowLatency2', {{'use_const_initializer':
False}}), ...] transform="transformation_name1[args],transformation_name2..."
where [args] is key=value pairs separated by semicolon. Examples:
transform="LowLatency2" or
transform="Pruning" or
transform="LowLatency2[use_const_initializer=False]" or
transform="MakeStateful[param_res_names=
{'input_name_1':'output_name_1','input_name_2':'output_name_2'}]"
Available transformations: "LowLatency2", "MakeStateful", "Pruning"
:param transformations_config:
Use the configuration file with transformations description or pass
object derived from BaseExtension class. Transformations file can
be specified as relative path from the current directory, as absolute
path or as relative path from the mo root directory.
:param silent:
Prevent any output messages except those that correspond to log level
equals ERROR, that can be set with the following option: "log_level".
By default, log level is already ERROR.
:param log_level:
Logger level of logging massages from MO.
Expected one of ['CRITICAL', 'ERROR', 'WARN', 'WARNING', 'INFO', 'DEBUG', 'NOTSET'].
:param version:
Version of Model Conversion API
:param progress:
Enable model conversion progress display.
:param stream_output:
Switch model conversion progress display to a multiline mode.
PaddlePaddle-specific parameters:
:param example_output:
Sample of model output in original framework. For PaddlePaddle it can be Paddle Variable.
TensorFlow*-specific parameters:
:param input_model_is_text:
TensorFlow*: treat the input model file as a text protobuf format. If
not specified, the convert_model() treats it as a binary file by default.
:param input_checkpoint:
TensorFlow*: variables file to load.
:param input_meta_graph:
Tensorflow*: a file with a meta-graph of the model before freezing
:param saved_model_dir:
TensorFlow*: directory with a model in SavedModel format of TensorFlow
1.x or 2.x version.
:param saved_model_tags:
Group of tag(s) of the MetaGraphDef to load, in string format, separated
by ','. For tag-set contains multiple tags, all tags must be passed in.
:param tensorflow_custom_operations_config_update:
TensorFlow*: update the configuration file with node name patterns
with input/output nodes information.
:param tensorflow_object_detection_api_pipeline_config:
TensorFlow*: path to the pipeline configuration file used to generate
model created with help of Object Detection API.
:param tensorboard_logdir:
TensorFlow*: dump the input graph to a given directory that should be
used with TensorBoard.
:param tensorflow_custom_layer_libraries:
TensorFlow*: comma separated list of shared libraries with TensorFlow*
custom operations implementation.
MXNet-specific parameters:
:param input_symbol:
Symbol file (for example, model-symbol.json) that contains a topology
structure and layer attributes
:param nd_prefix_name:
Prefix name for args.nd and argx.nd files.
:param pretrained_model_name:
Name of a pretrained MXNet model without extension and epoch number.
This model will be merged with args.nd and argx.nd files
:param save_params_from_nd:
Enable saving built parameters file from .nd files
:param legacy_mxnet_model:
Enable MXNet loader to make a model compatible with the latest MXNet
version. Use only if your model was trained with MXNet version lower
than 1.0.0
:param enable_ssd_gluoncv:
Enable pattern matchers replacers for converting gluoncv ssd topologies.
Caffe*-specific parameters:
:param input_proto:
Deploy-ready prototxt file that contains a topology structure and
layer attributes
:param caffe_parser_path:
Path to Python Caffe* parser generated from caffe.proto
:param k:
Path to CustomLayersMapping.xml to register custom layers
:param disable_omitting_optional:
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. Default behavior is to transfer the attributes with default values
and the attributes defined by the user to IR.
:param enable_flattening_nested_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.
Kaldi-specific parameters:
:param counts:
Path to the counts file
:param remove_output_softmax:
Removes the SoftMax layer that is the output layer
:param remove_memory:
Removes the Memory layer and use additional inputs outputs instead
Returns:
openvino.runtime.Model
"""
params = locals()
logger_state = get_logger_state()
del params['args']
params.update(args)
cli_parser = get_all_cli_parser()
ov_model, _ = _convert(cli_parser, params, True)
restore_logger_state(logger_state)
return ov_model

View File

@ -0,0 +1,92 @@
# Copyright (C) 2018-2023 Intel Corporation
# SPDX-License-Identifier: Apache-2.0
# flake8: noqa
# mypy: ignore-errors
import numpy as np
from openvino.tools.ovc.error import Error
"""
Packed data of custom types are stored in numpy uint8 data type.
To distinguish true uint8 and custom data we introduce this class not to store,
but to have unique data type in SUPPORTED_DATA_TYPES map
"""
class packed_U1(np.generic):
pass
class packed_U4(np.generic):
pass
class packed_I4(np.generic):
pass
SUPPORTED_DATA_TYPES = {
'float': (np.float32, 'FP32', 'f32'),
'half': (np.float16, 'FP16', 'f16'),
'FP32': (np.float32, 'FP32', 'f32'),
'FP64': (np.float64, 'FP64', 'f64'),
'FP16': (np.float16, 'FP16', 'f16'),
'I32': (np.int32, 'I32', 'i32'),
'I64': (np.int64, 'I64', 'i64'),
'int8': (np.int8, 'I8', 'i8'),
'int32': (np.int32, 'I32', 'i32'),
'int64': (np.int64, 'I64', 'i64'),
'bool': (bool, 'BOOL', 'boolean'),
'uint8': (np.uint8, 'U8', 'u8'),
'uint32': (np.uint32, 'U32', 'u32'),
'uint64': (np.uint64, 'U64', 'u64'),
# custom types
'U1': (packed_U1, 'U1', 'u1'),
'int4': (packed_I4, 'I4', 'i4'),
'uint4': (packed_U4, 'U4', 'u4'),
'I4': (packed_I4, 'I4', 'i4'),
'U4': (packed_U4, 'U4', 'u4'),
}
def data_type_str_to_np(data_type_str: str):
return SUPPORTED_DATA_TYPES[data_type_str][0] if data_type_str in SUPPORTED_DATA_TYPES else None
def data_type_str_to_precision(data_type_str: str):
return SUPPORTED_DATA_TYPES[data_type_str][1] if data_type_str in SUPPORTED_DATA_TYPES else None
def data_type_str_to_destination_type(data_type_str: str):
return SUPPORTED_DATA_TYPES[data_type_str][2] if data_type_str in SUPPORTED_DATA_TYPES else None
def np_data_type_to_precision(np_data_type):
for np_t, precision, _ in SUPPORTED_DATA_TYPES.values():
if np_t == np_data_type:
return precision
raise Error('Data type "{}" is not supported'.format(np_data_type))
def np_data_type_to_destination_type(np_data_type):
for np_t, _, destination_type in SUPPORTED_DATA_TYPES.values():
if np_t == np_data_type:
return destination_type
raise Error('Data type "{}" is not supported'.format(np_data_type))
def destination_type_to_np_data_type(dst_type):
for np_t, _, destination_type in SUPPORTED_DATA_TYPES.values():
if destination_type == dst_type:
return np_t
raise Error('Destination type "{}" is not supported'.format(dst_type))
def precision_to_destination_type(data_type_str):
for _, precision, destination_type in SUPPORTED_DATA_TYPES.values():
if precision == data_type_str:
return destination_type
raise Error('Data type "{}" is not supported'.format(data_type_str))

View File

@ -0,0 +1,831 @@
# Copyright (C) 2018-2023 Intel Corporation
# SPDX-License-Identifier: Apache-2.0
# flake8: noqa
# mypy: ignore-errors
import argparse
import datetime
import logging as log
import os
import sys
import traceback
from collections import OrderedDict
from pathlib import Path
try:
import openvino_telemetry as tm
except ImportError:
import openvino.tools.ovc.telemetry_stub as tm
from openvino.tools.ovc.moc_frontend.check_config import legacy_transformations_config_used, \
tensorflow_custom_operations_config_update_used, new_extensions_used
from openvino.tools.ovc.moc_frontend.pipeline import moc_pipeline
from openvino.tools.ovc.moc_frontend.moc_emit_ir import moc_emit_ir
from openvino.tools.ovc.convert_data_type import destination_type_to_np_data_type
from openvino.tools.ovc.cli_parser import check_available_transforms, \
get_advanced_cli_options, get_available_front_ends, get_caffe_cli_options, \
get_common_cli_options, get_kaldi_cli_options, get_layout_values, get_freeze_placeholder_values, \
get_mean_scale_dictionary, get_mxnet_cli_options, get_onnx_cli_options, \
get_placeholder_shapes, get_tf_cli_options, parse_transform, parse_tuple_pairs, \
get_model_name_from_args, depersonalize, get_mo_convert_params, input_to_input_cut_info, \
input_shape_to_input_cut_info, freeze_placeholder_to_input_cut_info
from openvino.tools.ovc.error import Error, FrameworkError, legacy_path_error
from openvino.tools.ovc.get_ov_update_message import get_ov_update_message, get_ov_api20_message, \
get_tf_fe_message, get_try_legacy_fe_message, get_compression_message
from openvino.tools.ovc.version import VersionChecker
from openvino.tools.ovc.utils import deduce_legacy_frontend_by_namespace, refer_to_faq_msg, check_values_equal
from openvino.tools.ovc.logger import init_logger, progress_printer
from openvino.tools.ovc.telemetry_utils import send_params_info, send_conversion_result, \
get_tid
from openvino.tools.ovc.moc_frontend.check_config import legacy_extensions_used
from openvino.tools.ovc.moc_frontend.check_config import default_path as extensions_default_path
from openvino.tools.ovc.moc_frontend.pytorch_frontend_utils import get_pytorch_decoder, extract_input_info_from_example
from openvino.tools.ovc.moc_frontend.paddle_frontend_utils import paddle_frontend_converter
from openvino.tools.ovc.moc_frontend.shape_utils import parse_input_shapes
# pylint: disable=no-name-in-module,import-error
from openvino.frontend import FrontEndManager, OpConversionFailure, ProgressReporterExtension, TelemetryExtension
from openvino.runtime import get_version as get_rt_version
from openvino.runtime import Type, PartialShape
try:
from openvino.frontend.tensorflow.utils import type_supported_by_tf_fe, create_tf_graph_iterator, \
extract_model_graph # pylint: disable=no-name-in-module,import-error
tf_frontend_with_python_bindings_installed = True
except (ModuleNotFoundError, ImportError):
tf_frontend_with_python_bindings_installed = False
def replace_ext(name: str, old: str, new: str):
base, ext = os.path.splitext(name)
log.debug("base: {}, ext: {}".format(base, ext))
if ext == old:
return base + new
def print_argv(argv: argparse.Namespace, is_caffe: bool, is_tf: bool, is_mxnet: bool, is_kaldi: bool, is_onnx: bool,
model_name: str):
print('Model Conversion arguments:')
props = OrderedDict()
props['common_args'] = get_common_cli_options(model_name)
props['advanced_args'] = get_advanced_cli_options()
if is_caffe:
props['caffe_args'] = get_caffe_cli_options()
if is_tf:
props['tf_args'] = get_tf_cli_options()
if is_mxnet:
props['mxnet_args'] = get_mxnet_cli_options()
if is_kaldi:
props['kaldi_args'] = get_kaldi_cli_options()
if is_onnx:
props['onnx_args'] = get_onnx_cli_options()
framework_specifics_map = {
'common_args': 'Common parameters:',
'advanced_args': 'Advanced parameters:',
'caffe_args': 'Caffe specific parameters:',
'tf_args': 'TensorFlow specific parameters:',
'mxnet_args': 'MXNet specific parameters:',
'kaldi_args': 'Kaldi specific parameters:',
'onnx_args': 'ONNX specific parameters:',
}
lines = []
for key in props:
lines.append(framework_specifics_map[key])
for (op, desc) in props[key].items():
if isinstance(desc, list):
lines.append('\t{}: \t{}'.format(desc[0], desc[1](getattr(argv, op, 'NONE'))))
else:
if op == 'k':
default_path = os.path.join(os.path.dirname(sys.argv[0]),
'openvino/tools/mo/front/caffe/CustomLayersMapping.xml')
if getattr(argv, op, 'NONE') == default_path:
lines.append('\t{}: \t{}'.format(desc, 'Default'))
continue
lines.append('\t{}: \t{}'.format(desc, getattr(argv, op, 'NONE')))
print('\n'.join(lines), flush=True)
def legacy_framework_check(is_caffe, is_mxnet, is_kaldi):
if is_caffe:
legacy_path_error("The provided model is from Caffe framework. This is legacy functionality. ")
if is_mxnet:
legacy_path_error("The provided model is from MxNet framework. This is legacy functionality. ")
if is_kaldi:
legacy_path_error("The provided model is from Kaldi framework. This is legacy functionality. ")
def check_legacy_args(non_default_params, python_api_used):
ignored_cli_options = ["output_dir", "model_name"]
legacy_groups = ['Kaldi-specific parameters:', 'Caffe*-specific parameters:', 'MXNet-specific parameters:']
tf_legacy_args = ['tensorflow_custom_operations_config_update', 'tensorflow_object_detection_api_pipeline_config',
'tensorboard_logdir', 'tensorflow_custom_layer_libraries', 'saved_model_tags']
mo_convert_params = get_mo_convert_params()
for key, value in non_default_params.items():
if key in ignored_cli_options:
if python_api_used:
print("The provided option \"{}\" is applicable in command line tool only. The option will be ignored.".format(key))
for group in legacy_groups:
if key in mo_convert_params[group]:
legacy_path_error("The provided option \"{}\" refers to legacy functionality. ".format(key))
if key in tf_legacy_args:
legacy_path_error("The provided option \"{}\" refers to legacy functionality. ".format(key))
def arguments_post_parsing(argv: argparse.Namespace):
use_legacy_frontend = argv.use_legacy_frontend
use_new_frontend = argv.use_new_frontend
if argv.extensions is None:
argv.extensions = [extensions_default_path()]
if use_new_frontend and use_legacy_frontend:
raise Error('Options "use_new_frontend" and "use_legacy_frontend" must not be used simultaneously.')
if use_legacy_frontend:
legacy_path_error('Option "use_legacy_frontend" was used, but legacy frontends are not available. ')
moc_front_end, available_moc_front_ends = get_moc_frontends(argv)
if not moc_front_end and use_new_frontend:
raise Error('Option "use_new_frontend" is specified but the Model Conversion API is unable to find new frontend. '
'Please ensure that your environment contains new frontend for the input model format or '
'try to install openvino-dev and convert the model using convert_model() from openvino.tools.mo.')
is_tf, is_caffe, is_mxnet, is_kaldi, is_onnx = \
deduce_legacy_frontend_by_namespace(argv) if not moc_front_end else [False, False, False, False, False]
legacy_framework_check(is_caffe, is_mxnet, is_kaldi)
is_legacy_frontend = any([is_tf, is_caffe, is_mxnet, is_kaldi, is_onnx])
# handle a default case, i.e. use_new_frontend and use_legacy_frontend are not specified, when no frontend is found
if not is_legacy_frontend and not moc_front_end:
legacy_frameworks = ['tf', 'caffe', 'mxnet', 'kaldi', 'onnx']
frameworks = list(set(legacy_frameworks + available_moc_front_ends))
if not argv.framework:
raise Error('Framework name can not be deduced from the given options: {}={}. '
'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)
if is_tf and not argv.input_model and not argv.saved_model_dir and not argv.input_meta_graph:
raise Error('Path to input model or saved model dir is required: use "input_model", "saved_model_dir" or '
'"input_meta_graph"')
elif is_onnx and not argv.input_model:
raise Error('Path to input model is required: use "input_model".')
log.debug("Model Conversion API started")
log.debug('Output model name would be {}{{.xml, .bin}}'.format(argv.model_name))
if not argv.silent:
print_argv(argv, is_caffe, is_tf, is_mxnet, is_kaldi, is_onnx, argv.model_name)
argv.data_type = 'FP32' # if compression was enabled will be restored back to 'FP16' after apply_offline_transformations
# This is just to check that transform key is valid and transformations are available
check_available_transforms(parse_transform(argv.transform))
if argv.scale and argv.scale_values:
raise Error(
'Both "scale" and "scale_values" are defined. Specify either scale factor or scale values per input ' +
'channels. ' + refer_to_faq_msg(19))
if argv.scale and argv.scale < 1.0:
log.error("The scale value is less than 1.0. This is most probably an issue because the scale value specifies "
"floating point value which all input values will be *divided*.", extra={'is_warning': True})
if argv.input_model and (is_tf and argv.saved_model_dir):
raise Error('Both "input_model" and "saved_model_dir" are defined. '
'Specify either input model or saved model directory.')
if is_tf:
if argv.saved_model_tags is not None:
if ' ' in argv.saved_model_tags:
raise Error('Incorrect saved model tag was provided. Specify "saved_model_tags" with no spaces in it')
argv.saved_model_tags = argv.saved_model_tags.split(',')
if hasattr(argv, 'is_python_api_used') and argv.is_python_api_used:
python_api_params_parsing(argv)
else:
argv.inputs_list, argv.placeholder_shapes, argv.placeholder_data_types = get_placeholder_shapes(
argv.input, argv.input_shape, argv.batch)
argv.freeze_placeholder_with_value, argv.input = get_freeze_placeholder_values(
argv.input,
argv.freeze_placeholder_with_value)
argv.unnamed_freeze_placeholder_with_value = {}
argv.output = argv.output.split(',') if argv.output else None
argv.layout_values = get_layout_values(argv.layout, argv.source_layout, argv.target_layout)
mean_values = parse_tuple_pairs(argv.mean_values)
scale_values = parse_tuple_pairs(argv.scale_values)
mean_scale = get_mean_scale_dictionary(mean_values, scale_values, argv.input)
argv.mean_scale_values = mean_scale
log.debug("Placeholder shapes : {}".format(argv.placeholder_shapes))
return argv
def check_fallback(argv: argparse.Namespace):
fallback_reasons = {}
# Some frontend such as PDPD does not have legacy path so it has no reasons to fallback
if not any(deduce_legacy_frontend_by_namespace(argv)):
return fallback_reasons
if argv.use_new_frontend:
return fallback_reasons
fallback_reasons['extensions'] = legacy_extensions_used
fallback_reasons['transformations_config'] = legacy_transformations_config_used
fallback_reasons['tensorflow_custom_operations_config_update'] = tensorflow_custom_operations_config_update_used
reasons = [reason for reason, is_applicable in fallback_reasons.items() if is_applicable(argv)]
return reasons
def update_fallback_with_conversion_error(use_new_frontend: bool, is_tf: bool, ex_msg: str, fallback_reasons: list):
import re
if not is_tf:
# this sort of fallback is only used by TensorFlow Frontend
return False
if use_new_frontend:
# this option forces to use new TensorFlow Frontend
# so it is not possible for the fallback
return False
# for TensorFlow FE we have a set of operations that should lead to the fallback to the legacy
conversion_error_re = r"^(\[TensorFlow\ Frontend\]\ Internal\ error\,\ no\ translator\ found\ for\ operation\(s\)\:\ )((\w+)(\,\ \w+)*)$"
conversion_error_match = re.findall(conversion_error_re, ex_msg, re.MULTILINE)
all_fallback_operations = [
# corresponds to TF1 While operation
"TensorArrayScatterV3", "TensorArrayV3", "TensorArraySizeV3", "TensorArrayGatherV3",
"LoopCond", "Enter", "NextIteration", "Exit",
# corresponds to TF1 If and TF1 While operations
"Switch", "Merge",
# corresponds to operations with complex tensors
"FFT", "FFT2D", "FFT3D", "IFFT", "IFFT2D", "IFFT3D",
"RFFT", "RFFT2D", "RFFT3D", "IRFFT", "IRFFT2D", "IRFFT3D",
"Complex", "ComplexAbs", "Real", "Imag",
]
if len(conversion_error_match) < 1 or len(conversion_error_match[0]) != 4:
# no match for the fallback by unsupported operation
return False
unsupported_operations = conversion_error_match[0][1].replace(" ", "").split(",")
fallback_operations = [operation for operation in unsupported_operations if operation in all_fallback_operations]
if len(fallback_operations) == 0:
return False
fallback_reasons.append("Fallback to the legacy TF FE due to operation(s): " + ', '.join(fallback_operations))
return True
def get_default_frontends():
# Set which frontend to use by default, values should be 'new' or 'legacy'
default_frontends = {
'onnx': 'new',
'tf': 'new'
}
return default_frontends
def get_moc_frontends(argv: argparse.Namespace):
fem = argv.feManager
# Read user flags:
use_legacy_frontend = argv.use_legacy_frontend
use_new_frontend = argv.use_new_frontend
if not fem or use_legacy_frontend:
return None, []
available_moc_front_ends = get_available_front_ends(fem)
if not argv.framework and argv.input_model:
moc_front_end = fem.load_by_model(argv.input_model)
if not moc_front_end:
return None, available_moc_front_ends
argv.framework = moc_front_end.get_name()
elif argv.framework in available_moc_front_ends:
moc_front_end = fem.load_by_framework(argv.framework)
else:
return None, []
default_frontends = get_default_frontends()
# Disable MOC frontend if default is set to legacy and no user override
if default_frontends.get(moc_front_end.get_name()) == 'legacy' and not use_new_frontend:
return None, available_moc_front_ends
# This check as a workaround to skip IR frontend
if not moc_front_end.get_name() in available_moc_front_ends:
return None, available_moc_front_ends
return moc_front_end, available_moc_front_ends
def prepare_ir(argv: argparse.Namespace):
# TODO: remove this workaround once new TensorFlow frontend supports non-frozen formats: checkpoint, MetaGraph, and SavedModel
# Now it converts all TensorFlow formats to the frozen .pb format in case new TensorFlow frontend
is_tf, _, _, _, _ = deduce_legacy_frontend_by_namespace(argv)
argv = arguments_post_parsing(argv)
t = tm.Telemetry()
graph = None
fallback_reasons = []
moc_front_end, available_moc_front_ends = get_moc_frontends(argv)
if moc_front_end:
fallback_reasons = check_fallback(argv)
if len(fallback_reasons) == 0:
if is_tf and tf_frontend_with_python_bindings_installed and \
type_supported_by_tf_fe(argv.input_model):
argv.input_model = create_tf_graph_iterator(argv.input_model,
argv.placeholder_shapes,
argv.placeholder_data_types,
getattr(argv, "example_input", None))
try:
t.send_event("mo", "conversion_method", moc_front_end.get_name() + "_frontend")
moc_front_end.add_extension(TelemetryExtension("mo", t.send_event, t.send_error, t.send_stack_trace))
moc_front_end.add_extension(ProgressReporterExtension(progress_printer(argv)))
if legacy_transformations_config_used(argv):
raise Error('Legacy extensions are not supported for the new frontend')
if legacy_extensions_used(argv):
raise Error('Legacy transformations configuration is not supported for the new frontend')
if tensorflow_custom_operations_config_update_used(argv) and is_tf:
raise Error('TensorFlow custom operation config is not supported for the new frontend')
if new_extensions_used(argv):
for extension in argv.extensions:
moc_front_end.add_extension(extension)
ngraph_function = moc_pipeline(argv, moc_front_end)
return graph, ngraph_function
except OpConversionFailure as ex:
# in some set of operations (TF1 While), we have to fallback to the Legacy TensorFlow Frontend
# this is the second attempt for the fallback
if not update_fallback_with_conversion_error(argv.use_new_frontend, is_tf, str(ex), fallback_reasons):
# re-throw exception for all frontends except TensorFlow FE
# and in case unexpected conversion failures
raise
if len(fallback_reasons) > 0:
reasons_message = ", ".join(fallback_reasons)
t.send_event("mo", "fallback_reason", reasons_message)
log.warning("The IR preparation cannot be executed with new frontend. "
f"The detailed reason why fallback to legacy is needed: not supported {reasons_message} were used. " +
refer_to_faq_msg(105))
assert not hasattr(argv, 'is_fallback'), '`is_fallback` argument must not exist.'
argv.is_fallback = True
t.send_event("mo", "conversion_method", "mo_legacy")
legacy_path_error("The provided model cannot be converted with new frontend, as fallback to legacy is needed. ")
return None, None
def check_model_object(argv):
model = argv['input_model']
if 'tensorflow' in sys.modules:
if tf_frontend_with_python_bindings_installed and extract_model_graph(argv):
return "tf"
if 'torch' in sys.modules:
import torch
if isinstance(model, (torch.nn.Module, torch.jit.ScriptFunction)):
return "pytorch"
try:
from openvino.frontend.pytorch.decoder import TorchScriptPythonDecoder
if isinstance(model, TorchScriptPythonDecoder):
return "pytorch"
except Exception as e:
pass
import io
if isinstance(model, io.BytesIO):
return 'onnx'
if 'paddle' in sys.modules:
import paddle
if isinstance(model, paddle.hapi.model.Model) or isinstance(model,
paddle.fluid.dygraph.layers.Layer) or isinstance(
model, paddle.fluid.executor.Executor):
return "paddle"
raise Error('Unknown model type: {}'.format(type(model)))
def driver(argv: argparse.Namespace, non_default_params: dict):
init_logger(argv.log_level.upper(), argv.silent)
# Log dictionary with non-default cli parameters where complex classes are excluded.
log.debug(str(non_default_params))
start_time = datetime.datetime.now()
graph, ngraph_function = prepare_ir(argv)
legacy_path = False
if graph is not None:
legacy_path_error()
else:
res_ngraph_function = moc_emit_ir(ngraph_function, argv)
if res_ngraph_function is None:
return res_ngraph_function
if not argv.silent:
elapsed_time = datetime.datetime.now() - start_time
print('[ SUCCESS ] Total execution time: {:.2f} seconds. '.format(elapsed_time.total_seconds()))
try:
import resource
mem_usage = round(resource.getrusage(resource.RUSAGE_SELF).ru_maxrss / 1024)
if sys.platform == 'darwin':
mem_usage = round(mem_usage / 1024)
print('[ SUCCESS ] Memory consumed: {} MB. '.format(mem_usage))
except ImportError:
pass
return res_ngraph_function, legacy_path
def args_dict_to_list(cli_parser, **kwargs):
# This method is needed to prepare args from convert_model() for args_parse().
# The method will not be needed when cli_parser checks are moved from cli_parser to a separate pass.
import inspect
from openvino.tools.ovc import convert_model
signature = inspect.signature(convert_model)
result = []
for key, value in kwargs.items():
if value is None:
continue
if key in signature.parameters and check_values_equal(signature.parameters[key].default, value):
continue
if check_values_equal(cli_parser.get_default(key), value):
continue
# skip parser checking for non str objects
if not isinstance(value, (str, bool)):
continue
result.append('--{}'.format(key))
if not isinstance(value, bool):
result.append(value)
return result
def get_non_default_params(argv, cli_parser):
import numbers
import inspect
from openvino.tools.ovc import convert_model
signature = inspect.signature(convert_model)
# make dictionary with parameters which have non-default values to be serialized in IR in rt_info
non_default_params = {}
for arg, arg_value in vars(argv).items():
if arg in signature.parameters and check_values_equal(arg_value, signature.parameters[arg].default):
continue
if check_values_equal(arg_value, cli_parser.get_default(arg)):
continue
value = depersonalize(arg_value, arg)
# Skip complex classes in params to prevent
# serializing it to rt_info
if isinstance(value, (str, bool, numbers.Number)):
non_default_params[arg] = value
return non_default_params
def params_to_string(**kwargs):
all_params = {}
for key, value in get_mo_convert_params().items():
all_params.update(value)
for key, value in kwargs.items():
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.replace('\n', "\n ").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():
mo_convert_params = get_mo_convert_params()
for group_name, group in mo_convert_params.items():
print(group_name)
for param_name in group:
param_data = group[param_name]
text = param_data.description.replace(" ", '')
text = add_line_breaks(text, 56, "\n\t\t\t")
print(" :param {} {}".format(param_name, text))
print()
def input_model_is_object(argv):
# Input model can be set as object only for "input_model" parameter.
# "saved_model_dir" or meta specific options are only used to store paths to the input model.
if 'input_model' not in argv:
return False
if isinstance(argv['input_model'], (str, Path)):
return False
if argv['input_model'] is None:
return False
return True
def python_api_params_parsing(argv: argparse.Namespace):
"""
Parses params passed to convert_model and wraps resulting values into dictionaries or lists.
After working of this method following values are set in argv:
argv.input, argv.inputs_list - list of input names. Both values are used in some parts of MO.
Could be good to refactor it and use only one of these values.
argv.placeholder_shapes - dictionary where key is node name, value is PartialShape,
or list of PartialShape if node names were not set.
argv.placeholder_data_types - dictionary where key is node name, value is node np.type,
or list of np.types if node names were not set.
argv.freeze_placeholder_with_value - dictionary where key is node name, value is np.ndarray
argv.unnamed_freeze_placeholder_with_value - list with np.ndarray
:param argv: MO arguments
"""
# Parse input to list of InputCutInfo
inputs = input_to_input_cut_info(argv.input)
# Make list of input names
input_names_list = []
for inp in inputs:
if inp.name is not None:
input_names_list.append(inp.name)
if len(input_names_list) > 0:
assert len(input_names_list) == len(inputs), "\"input\" parameter has unnamed inputs and named inputs. " \
"Please either set names for all inputs, " \
"or do not set names for all inputs."
argv.inputs_list = input_names_list
argv.input = ','.join(input_names_list)
# Parse input_shape param and update InputCutInfo list
input_shape_to_input_cut_info(argv.input_shape, inputs)
# Parse freeze_placeholder_with_value.
# values for freezing can be set both by named and unnamed approach if
# 'input' was used without names and 'freeze_placeholder_with_value' was used with names.
# So named and unnamed values are stored separately.
argv.freeze_placeholder_with_value, argv.unnamed_freeze_placeholder_with_value = \
freeze_placeholder_to_input_cut_info(argv.freeze_placeholder_with_value, inputs)
if len(input_names_list) > 0:
# Named inputs case
shape_dict = {}
data_type_dict = {}
for inp in inputs:
if inp.shape is not None:
# Wrap shape to PartialShape for uniformity of stored values
shape_dict[inp.name] = PartialShape(inp.shape)
else:
shape_dict[inp.name] = None
if inp.type is not None:
# Convert type to numpy type for uniformity of stored values
if isinstance(inp.type, str):
data_type_dict[inp.name] = destination_type_to_np_data_type(inp.type)
elif isinstance(inp.type, Type):
data_type_dict[inp.name] = inp.type.to_dtype().type
else:
data_type_dict[inp.name] = inp.type
argv.placeholder_shapes = shape_dict if shape_dict else None
argv.placeholder_data_types = data_type_dict if data_type_dict else {}
else:
# Unnamed inputs case
shape_list = []
data_type_list = []
for inp in inputs:
if inp.shape is not None:
# Wrap shape to PartialShape for uniformity of stored values
shape_list.append(PartialShape(inp.shape))
if inp.type is not None:
# Convert type to numpy type for uniformity of stored values
if isinstance(inp.type, str):
data_type_list.append(destination_type_to_np_data_type(inp.type))
elif isinstance(inp.type, Type):
data_type_list.append(inp.type.to_dtype().type)
else:
data_type_list.append(inp.type)
argv.placeholder_shapes = shape_list if shape_list else None
argv.placeholder_data_types = data_type_list if data_type_list else {}
if argv.framework == "pytorch" and getattr(argv, "example_input", None) is not None:
extract_input_info_from_example(argv, inputs)
def pack_params_to_args_namespace(args: dict, cli_parser: argparse.ArgumentParser):
if len(args) > 0:
args_string = params_to_string(**args)
argv, _ = cli_parser.parse_known_args(args_dict_to_list(cli_parser, **args_string))
# get list of all available params for convert_model()
all_params = {}
for key, value in get_mo_convert_params().items():
all_params.update(value)
# check that there are no unknown params provided
for key, value in args_string.items():
if key not in argv and key not in all_params.keys():
raise Error("Unrecognized argument: {}".format(key))
# Non string params like input_model or extensions are ignored by parse_args()
# so we need to set them in argv separately
if value is not None and not check_values_equal(getattr(argv, key, None), value):
setattr(argv, key, value)
else:
argv = cli_parser.parse_args()
return argv
def update_args_for_saved_model_dir(args: dict):
"""
If directory is set in 'input_model' argument, the directory is considered as TF saved model.
In this case this method updates args and moves saved model directory to 'saved_model_dir' param.
:param args: dictionary with arguments from user
"""
if 'saved_model_dir' in args and args['saved_model_dir'] is not None and \
'input_model' in args and args['input_model'] is not None:
raise Error("Both \"input_model\" and \"saved_model_dir\" are defined. "
"Please specify either \"input_model\" or \"saved_model_dir\" directory.")
if 'input_model' in args and isinstance(args['input_model'], (str, Path)) and os.path.isdir(args['input_model']):
args['saved_model_dir'] = args['input_model']
args['input_model'] = None
def silent_is_false(argv: argparse.Namespace):
return argv is not None and hasattr(argv, 'silent') and argv.silent is False
def framework_is_tf(args, argv):
if input_model_is_object(args) and check_model_object(args) == "tf":
return True
if argv is not None:
is_tf, _, _, _, _ = deduce_legacy_frontend_by_namespace(argv)
return is_tf
return False
def _convert(cli_parser: argparse.ArgumentParser, args, python_api_used):
if 'help' in args and args['help']:
show_mo_convert_help()
return None, None
framework = None
simplified_ie_version = VersionChecker().get_ie_simplified_version()
telemetry = tm.Telemetry(tid=get_tid(), app_name='Model Conversion API', app_version=simplified_ie_version)
telemetry.start_session('mo')
telemetry.send_event('mo', 'version', simplified_ie_version)
# 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)
argv = None
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":
example_inputs = None
if 'example_input' in args and args['example_input'] is not None:
example_inputs = args['example_input']
elif 'example_inputs' in args:
raise AssertionError(
"'example_inputs' argument is not recognized, maybe you meant to provide 'example_input'?")
decoder = get_pytorch_decoder(args['input_model'], parse_input_shapes(args), example_inputs, args)
if model_framework == "paddle":
example_inputs = None
if 'example_input' in args and args['example_input'] is not None:
example_inputs = args['example_input']
example_outputs = None
if 'example_output' in args and args['example_output'] is not None:
example_outputs = args['example_output']
paddle_runtime_converter = paddle_frontend_converter(args['input_model'], example_inputs,
example_outputs)
pdmodel = paddle_runtime_converter.convert_paddle_to_pdmodel()
args['input_model'] = pdmodel
args['framework'] = model_framework
update_args_for_saved_model_dir(args)
argv = pack_params_to_args_namespace(args, cli_parser)
argv.feManager = FrontEndManager()
frameworks = list(set(['tf', 'caffe', 'mxnet', 'kaldi', 'onnx'] + (get_available_front_ends(argv.feManager)
if argv.feManager else [])))
framework = argv.framework if hasattr(argv, 'framework') and argv.framework is not None else framework
if framework is not None:
assert framework in frameworks, "error: argument \"framework\": invalid choice: '{}'. " \
"Expected one of {}.".format(framework, frameworks)
setattr(argv, 'framework', framework)
# send telemetry with params info
send_params_info(argv, cli_parser)
non_default_params = get_non_default_params(argv, cli_parser)
check_legacy_args(non_default_params, python_api_used)
argv.is_python_api_used = python_api_used
if inp_model_is_object:
argv.model_name = "model"
if not hasattr(argv, "model_name") or 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
ov_model, legacy_path = driver(argv, {"conversion_parameters": non_default_params})
if inp_model_is_object and model_framework == "paddle":
if paddle_runtime_converter:
paddle_runtime_converter.destroy()
# add MO meta data to model
ov_model.set_rt_info(get_rt_version(), "Runtime_version")
ov_model.set_rt_info(str(legacy_path), "legacy_frontend")
for key, value in non_default_params.items():
ov_model.set_rt_info(str(value), ["conversion_parameters", str(key)])
if silent_is_false(argv) or not python_api_used:
if 'compress_to_fp16' in argv and argv.compress_to_fp16:
print(get_compression_message())
ov_update_message = get_ov_update_message()
ov_api20_message = get_ov_api20_message()
if ov_update_message is not None:
print(ov_update_message)
if ov_api20_message is not None and ov_model is not None:
print(ov_api20_message)
is_fallback = getattr(argv, 'is_fallback', False)
if not argv.use_legacy_frontend and framework_is_tf(args, argv) and not is_fallback:
# now TF FE is default frontend for TensorFlow models conversion
print(get_tf_fe_message())
send_conversion_result('success')
return ov_model, argv
except Exception as e:
if silent_is_false(argv) or not python_api_used:
if isinstance(e, (FileNotFoundError, NotADirectoryError)):
log.error('File {} was not found'.format(str(e).split('No such file or directory:')[1]))
log.debug(traceback.format_exc())
elif isinstance(e, Error):
log.error(e)
log.debug(traceback.format_exc())
elif isinstance(e, FrameworkError):
log.error(e, extra={'framework_error': True})
log.debug(traceback.format_exc())
else:
log.error("-------------------------------------------------")
log.error("----------------- INTERNAL ERROR ----------------")
log.error("Unexpected exception happened.")
log.error("Please contact Model Conversion API developers and forward the following information:")
log.error(str(e))
log.error(traceback.format_exc())
log.error("---------------- END OF BUG REPORT --------------")
log.error("-------------------------------------------------")
is_fallback = getattr(argv, 'is_fallback', False) if argv is not None else False
if not argv.use_legacy_frontend and framework_is_tf(args, argv) and not is_fallback:
print(get_try_legacy_fe_message())
send_conversion_result('fail')
if python_api_used:
raise e.with_traceback(None)
else:
return None, argv

View File

@ -1,6 +1,9 @@
# Copyright (C) 2018-2023 Intel Corporation
# SPDX-License-Identifier: Apache-2.0
# flake8: noqa
# mypy: ignore-errors
import os
import sys
@ -19,7 +22,7 @@ def get_imported_module_version(imported_module):
installed_version = getattr(imported_module, attr, None)
if isinstance(installed_version, str):
return installed_version
else:
else:
installed_version = None
if installed_version is None:

View File

@ -0,0 +1,56 @@
# Copyright (C) 2018-2023 Intel Corporation
# SPDX-License-Identifier: Apache-2.0
# flake8: noqa
# mypy: ignore-errors
import re
class BasicError(Exception):
""" Base class for all exceptions in Model Conversion API
It operates like Exception but when it is converted to str,
it formats string as args[0].format(*args[1:]), where
args are arguments provided when an exception instance is
created.
"""
def __str__(self):
if len(self.args) <= 1:
return Exception.__str__(self)
return self.args[0].format(*self.args[1:]) # pylint: disable=unsubscriptable-object
class FrameworkError(BasicError):
""" User-friendly error: raised when the error on the framework side. """
pass
class Error(BasicError):
""" User-friendly error: raised when the error on the user side. """
pass
class InternalError(BasicError):
""" Not user-friendly error: user cannot fix it and it points to the bug inside MO. """
pass
def classify_error_type(e):
patterns = [
# Example: No module named 'openvino._offline_transformations.offline_transformations_api'
r"No module named \'\S+\'",
# Example: cannot import name 'IECore' from 'openvino.inference_engine' (unknown location)
r"cannot import name \'\S+\'",
]
error_message = str(e)
for pattern in patterns:
m = re.search(pattern, error_message)
if m:
return m.group(0)
return "undefined"
def legacy_path_error(functionality_description):
raise Exception("{}Please try to install openvino-dev and use convert_model() "
"from openvino.tools.mo.".format(functionality_description))

View File

@ -0,0 +1,51 @@
# Copyright (C) 2018-2023 Intel Corporation
# SPDX-License-Identifier: Apache-2.0
# flake8: noqa
# mypy: ignore-errors
import datetime
msg_fmt = 'Check for a new version of Intel(R) Distribution of OpenVINO(TM) toolkit here {0} ' \
'or on https://github.com/openvinotoolkit/openvino'
def get_ov_update_message():
expected_update_date = datetime.date(year=2023, month=12, day=1)
current_date = datetime.date.today()
link = 'https://software.intel.com/content/www/us/en/develop/tools/openvino-toolkit/download.html?cid=other&source=prod&campid=ww_2023_bu_IOTG_OpenVINO-2023-0&content=upg_all&medium=organic'
return msg_fmt.format(link) if current_date >= expected_update_date else None
def get_ov_api20_message():
link = "https://docs.openvino.ai/2023.0/openvino_2_0_transition_guide.html"
message = '[ INFO ] The model was converted to IR v11, the latest model format that corresponds to the source DL framework ' \
'input/output format. While IR v11 is backwards compatible with OpenVINO Inference Engine API v1.0, ' \
'please use API v2.0 (as of 2022.1) to take advantage of the latest improvements in IR v11.\n' \
'Find more information about API v2.0 and IR v11 at {}'.format(link)
return message
def get_tf_fe_message():
link = "https://docs.openvino.ai/2023.0/openvino_docs_MO_DG_TensorFlow_Frontend.html"
message = '[ INFO ] IR generated by new TensorFlow Frontend is compatible only with API v2.0. Please make sure to use API v2.0.\n' \
'Find more information about new TensorFlow Frontend at {}'.format(link)
return message
def get_compression_message():
link = "https://docs.openvino.ai/2023.0/openvino_docs_MO_DG_FP16_Compression.html"
message = '[ INFO ] Generated IR will be compressed to FP16. ' \
'If you get lower accuracy, please consider disabling compression ' \
'by removing argument "compress_to_fp16" or set it to false "compress_to_fp16=False".\n' \
'Find more information about compression to FP16 at {}'.format(link)
return message
def get_try_legacy_fe_message():
message = '[ INFO ] You can also try to install openvino-dev and use convert_model from openvino.tools.mo.\n'
return message

View File

@ -1,13 +1,15 @@
# Copyright (C) 2018-2023 Intel Corporation
# SPDX-License-Identifier: Apache-2.0
# flake8: noqa
# mypy: ignore-errors
def get_convert_model_help_specifics():
from openvino.tools.mo.utils.cli_parser import CanonicalizeTransformationPathCheckExistenceAction, \
from openvino.tools.ovc.cli_parser import CanonicalizeTransformationPathCheckExistenceAction, \
CanonicalizePathCheckExistenceAction, CanonicalizeExtensionsPathCheckExistenceAction, \
CanonicalizePathCheckExistenceIfNeededAction, readable_file_or_dir, readable_dirs_or_files_or_empty, \
check_positive
from openvino.tools.mo.utils.version import VersionChecker
from openvino.tools.ovc.version import VersionChecker
return {
'input_model':
{'description':
@ -127,7 +129,7 @@ def get_convert_model_help_specifics():
{'action': CanonicalizePathCheckExistenceIfNeededAction},
'version':
{'action': 'version',
'version': 'Version of Model Optimizer is: {}'.format(VersionChecker().get_mo_version())},
'version': 'Version of Model Optimizer is: {}'.format(VersionChecker().get_ie_version())},
'scale':
{'type': float,
'aliases': {'-s'}},
@ -143,7 +145,7 @@ def get_convert_model_help_specifics():
# TODO: remove this when internal converting of params to string is removed
def get_to_string_methods_for_params():
from openvino.tools.mo.utils.cli_parser import path_to_str_or_object, str_list_to_str, \
from openvino.tools.ovc.cli_parser import path_to_str_or_object, str_list_to_str, \
mean_scale_value_to_str, source_target_layout_to_str, layout_param_to_str, transform_param_to_str, \
extensions_to_str_or_extensions_class, batch_to_int, transformations_config_to_str
return {

View File

@ -0,0 +1,163 @@
# Copyright (C) 2018-2023 Intel Corporation
# SPDX-License-Identifier: Apache-2.0
# flake8: noqa
# mypy: ignore-errors
import importlib.util
import logging as log
import os
import re
import sys
from argparse import Namespace
from copy import copy
# WA for abseil bug that affects logging while importing TF starting 1.14 version
# Link to original issue: https://github.com/abseil/abseil-py/issues/99
if importlib.util.find_spec('absl') is not None:
import absl.logging
log.root.removeHandler(absl.logging._absl_handler)
handler_num = 0
class LvlFormatter(log.Formatter):
format_dict = {
log.DEBUG: "[ %(asctime)s ] [ %(levelname)s ] [ %(module)s:%(lineno)d ] %(msg)s",
log.INFO: "[ %(levelname)s ] %(msg)s",
log.WARNING: "[ WARNING ] %(msg)s",
log.ERROR: "[ %(levelname)s ] %(msg)s",
log.CRITICAL: "[ %(levelname)s ] %(msg)s",
'framework_error': "[ FRAMEWORK ERROR ] %(msg)s",
'analysis_info': "[ ANALYSIS INFO ] %(msg)s"
}
def __init__(self, lvl, fmt=None):
log.Formatter.__init__(self, fmt)
self.lvl = lvl
def format(self, record: log.LogRecord):
if self.lvl == 'DEBUG':
self._style._fmt = self.format_dict[log.DEBUG]
else:
self._style._fmt = self.format_dict[record.levelno]
if 'is_warning' in record.__dict__.keys():
self._style._fmt = self.format_dict[log.WARNING]
if 'framework_error' in record.__dict__.keys():
self._style._fmt = self.format_dict['framework_error']
if 'analysis_info' in record.__dict__.keys():
self._style._fmt = self.format_dict['analysis_info']
return log.Formatter.format(self, record)
class TagFilter(log.Filter):
def __init__(self, regex: str):
self.regex = regex
def filter(self, record: log.LogRecord):
if record.__dict__['funcName'] == 'load_grammar': # for nx not to log into our logs
return False
if self.regex:
if 'tag' in record.__dict__.keys():
tag = record.__dict__['tag']
return re.findall(self.regex, tag)
else:
return False
return True # if regex wasn't set print all logs
def init_logger(lvl: str, silent: bool):
global handler_num
log_exp = os.environ.get('MO_LOG_PATTERN')
if silent:
lvl = 'ERROR'
fmt = LvlFormatter(lvl=lvl)
handler = log.StreamHandler()
handler.setFormatter(fmt)
logger = log.getLogger()
logger.setLevel(lvl)
logger.addFilter(TagFilter(regex=log_exp))
if handler_num == 0 and len(logger.handlers) == 0:
logger.addHandler(handler)
handler_num += 1
def get_logger_state():
logger = log.getLogger()
return logger.level, copy(logger.filters), copy(logger.handlers)
def restore_logger_state(state: tuple):
level, filters, handlers = state
logger = log.getLogger()
logger.setLevel(level)
logger.filters = filters
logger.handlers = handlers
def progress_bar(function: callable):
"""
Decorator for model conversion pipeline progress display
Works in combination with function: mo.utils.class_registration.apply_transform
"""
def wrapper(*args, **kwargs):
for arg in ['graph', 'curr_transform_num', 'num_transforms']:
msg = 'Progress bar decorator is enabled for Model Conversion API transformation applying cycle only. ' \
'Argument `{}` {}'
assert arg in kwargs, msg.format(arg, 'is missing')
assert kwargs[arg] is not None, msg.format(arg, 'should not be None')
if 'progress' in kwargs['graph'].graph['cmd_params'] and kwargs['graph'].graph['cmd_params'].progress:
bar_len = 20
total_replacers_count = kwargs['num_transforms']
def progress(i):
return int((i + 1) / total_replacers_count * bar_len)
def percent(i):
return (i + 1) / total_replacers_count * 100
end = '' if not kwargs['graph'].graph['cmd_params'].stream_output else '\n'
curr_i = kwargs['curr_transform_num']
print('\rProgress: [{:{}}]{:>7.2f}% done'.format('.' * progress(curr_i), bar_len, percent(curr_i)), end=end)
sys.stdout.flush()
function(*args, **kwargs)
return wrapper
def progress_printer(argv: Namespace):
"""
A higher-order factory function returning a configurable callback displaying a progress bar
Depending on the configuration stored in 'argv' the progress bar can be one-line, multi-line, or silent.
"""
def _progress_bar(progress, total, completed, endline):
bar_len = 20
def dots():
return '.' * int(progress * bar_len)
print('\rProgress: [{:{}}]{:>7.2f}% done'.format(dots(), bar_len, progress*100), end=endline)
sys.stdout.flush()
def no_progress_bar(progress, total, completed):
""" A 'dummy' progressbar which doesn't print anything """
pass
def oneline_progress_bar(progress, total, completed):
""" A callback that always prints the progress in the same line (mimics real GUI progress bar)"""
_progress_bar(progress, total, completed, '')
def newline_progress_bar(progress, total, completed):
""" A callback that prints an updated progress bar in separate lines """
_progress_bar(progress, total, completed, '\n')
if "progress" in argv and argv.progress:
if "stream_output" in argv and argv.stream_output:
return newline_progress_bar
else:
return oneline_progress_bar
else:
return no_progress_bar

View File

@ -0,0 +1,38 @@
# Copyright (C) 2018-2023 Intel Corporation
# SPDX-License-Identifier: Apache-2.0
import argparse
import os
import sys
try:
import openvino_telemetry as tm
except ImportError:
import openvino.tools.ovc.telemetry_stub as tm
from openvino.tools.ovc.convert_impl import _convert
from openvino.tools.ovc.version import VersionChecker
# pylint: disable=no-name-in-module,import-error
from openvino.runtime import serialize
def main():
from openvino.tools.ovc.cli_parser import get_all_cli_parser
ngraph_function, argv = _convert(get_all_cli_parser(), {}, False)
if ngraph_function is None:
return 1
output_dir = argv.output_dir if argv.output_dir != '.' else os.getcwd()
model_path_no_ext = os.path.normpath(os.path.join(output_dir, argv.model_name))
model_path = model_path_no_ext + '.xml'
serialize(ngraph_function, model_path.encode('utf-8'), model_path.replace('.xml', '.bin').encode('utf-8'))
print('[ SUCCESS ] Generated IR version {} model.'.format(VersionChecker().get_ie_simplified_version()))
print('[ SUCCESS ] XML file: {}'.format(model_path))
print('[ SUCCESS ] BIN file: {}'.format(model_path.replace('.xml', '.bin')))
return 0
if __name__ == "__main__":
sys.exit(main())

View File

@ -1,2 +1,5 @@
# Copyright (C) 2018-2023 Intel Corporation
# SPDX-License-Identifier: Apache-2.0
# flake8: noqa
# mypy: ignore-errors

View File

@ -1,9 +1,12 @@
# Copyright (C) 2022 Intel Corporation
# SPDX-License-Identifier: Apache-2.0
# flake8: noqa
# mypy: ignore-errors
import json
from openvino.runtime import PartialShape, Model, Type # pylint: disable=no-name-in-module,import-error
from openvino.runtime.utils.types import get_dtype
from openvino.tools.ovc.types import get_dtype
def json_model_analysis_dump(framework_model: Model):

View File

@ -1,11 +1,19 @@
# Copyright (C) 2022-2023 Intel Corporation
# SPDX-License-Identifier: Apache-2.0
# flake8: noqa
# mypy: ignore-errors
import argparse
from pathlib import Path
from openvino.tools.mo.utils import import_extensions
from openvino.tools.mo.utils.error import Error
from openvino.tools.ovc.error import Error
import os
def default_path():
EXT_DIR_NAME = '.'
return os.path.abspath(os.getcwd().join(EXT_DIR_NAME))
def any_extensions_used(argv: argparse.Namespace):
@ -22,7 +30,7 @@ def any_extensions_used(argv: argparse.Namespace):
if not isinstance(ext, str):
has_non_str_objects = True
continue
if len(ext) == 0 or ext == import_extensions.default_path():
if len(ext) == 0 or ext == default_path():
continue
has_non_default_path = True
@ -38,7 +46,7 @@ def legacy_extensions_used(argv: argparse.Namespace):
for extension in extensions:
if not isinstance(extension, str):
continue
if extension == import_extensions.default_path():
if extension == default_path():
continue
if not Path(extension).is_file():
legacy_ext_counter += 1

View File

@ -1,6 +1,9 @@
# Copyright (C) 2018-2023 Intel Corporation
# SPDX-License-Identifier: Apache-2.0
# flake8: noqa
# mypy: ignore-errors
import re
from enum import Enum
@ -8,8 +11,18 @@ import numpy as np
from openvino._pyopenvino import Place, PartialShape
from openvino.frontend import InputModel # pylint: disable=no-name-in-module,import-error
from openvino.tools.mo.front.extractor import raise_no_node, raise_node_name_collision
from openvino.tools.mo.utils.error import Error
from openvino.tools.ovc.error import Error
def raise_no_node(node_name: str):
raise Error('No node with name {}'.format(node_name))
def raise_node_name_collision(node_name: str, found_nodes: list):
raise Error('Name collision was found, there are several nodes for mask "{}": {}. '
'If your intention was to specify port for node, please instead specify node names connected to '
'this port. If your intention was to specify the node name, please add port to the node '
'name'.format(node_name, found_nodes))
class IOType(Enum):
@ -91,7 +104,7 @@ def decode_name_with_port(
else:
return node.get_input_port(input_port_index=int(port_index))
else:
None
return None
regexp_post = r"(.+):(\d+)"
match = re.search(regexp_post, node_name)
@ -153,12 +166,12 @@ def fe_input_user_data_repack(
Transforms node names to node ids.
:param input_model: current input model
:param input_user_shapes: data structure representing user input cutting request. It may be:
# None value if user did not provide neither --input nor --input_shape keys
# None value if user did not provide neither "input" nor "input_shape" keys
# list instance which contains input layer names with or without ports if user provided
only --input key
only "input" key
# dict instance which contains input layer names with or without ports as keys and shapes as
values if user provided both --input and --input_shape
# np.ndarray if user provided only --input_shape key
values if user provided both "input" and "input_shape"
# np.ndarray if user provided only "input_shape" key
:param freeze_placeholder: dictionary with placeholder names as keys and freezing value as values
:param input_user_data_types: dictionary with input nodes and its data types
:return: restructured input shapes and freeze placeholder shapes information
@ -188,7 +201,7 @@ def fe_input_user_data_repack(
_input_shapes = []
_input_names = []
model_inputs = input_model.get_inputs()
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:
@ -232,7 +245,7 @@ def fe_input_user_data_repack(
elif isinstance(input_user_shapes, PartialShape):
# this branch covers the single use of `input_shape` without `input` option
# but it can be used along with `freeze_placeholder_with_value` option
# for example, --input_shape [3] --freeze_placeholder_with_value "is_training->False"
# for example, input_shape [3] freeze_placeholder_with_value "is_training->False"
# means the model has two inputs: one is is_training to be frozen, the other to re-write the shape
# NOTE: the logic relies on parameters with the single name
frozen_names = freeze_placeholder.keys()
@ -415,8 +428,8 @@ def convert_params_lists_to_dicts(input_model,
# unnamed_freeze_placeholders is always list, it is not empty only if unnamed inputs were used.
for value in unnamed_freeze_placeholders:
assert isinstance(value, list), "Got incorrect format of input values. " \
"Expected list, " \
"got {}.".format(type(value))
"Expected list, " \
"got {}.".format(type(value))
inp_name = find_first_unused_input(model_inputs, freeze_placeholder, {}, "input value")
freeze_placeholder[inp_name] = value

View File

@ -1,11 +1,14 @@
# Copyright (C) 2018-2023 Intel Corporation
# SPDX-License-Identifier: Apache-2.0
# flake8: noqa
# mypy: ignore-errors
from typing import Callable
from openvino.runtime import PartialShape # pylint: disable=no-name-in-module,import-error
from openvino.tools.mo.utils.error import Error
from openvino.tools.mo.utils.utils import refer_to_faq_msg
from openvino.tools.ovc.error import Error
from openvino.tools.ovc.utils import refer_to_faq_msg
def update_layout_to_dict(inputs: list, layout: [list, dict], get_names_func: Callable):
@ -19,7 +22,7 @@ def update_layout_to_dict(inputs: list, layout: [list, dict], get_names_func: Ca
if len(input_names) > 1:
raise Error('Layout without name can be specified for models with only one input, '
'but provided model has {} inputs: \'{}\'. '
'Please specify explicitly input/output name for --layout option'
'Please specify explicitly input/output name for "layout" option'
.format(len(input_names), input_names))
layout = {
input_names[0]: {

View File

@ -1,23 +1,23 @@
# Copyright (C) 2018-2023 Intel Corporation
# SPDX-License-Identifier: Apache-2.0
# flake8: noqa
# mypy: ignore-errors
import argparse
import os
from openvino.runtime import Model # pylint: disable=no-name-in-module,import-error
from openvino.tools.mo.back.preprocessing import apply_preprocessing
from openvino.tools.mo.utils.cli_parser import parse_transform
from openvino.tools.ovc.cli_parser import parse_transform
from openvino.tools.ovc.moc_frontend.preprocessing import apply_preprocessing
def moc_emit_ir(ngraph_function: Model, argv: argparse.Namespace):
output_dir = argv.output_dir if argv.output_dir != '.' else os.getcwd()
# Apply preprocessing (mean/scale/reverse_channels/convert_layout/etc)
apply_preprocessing(ov_function=ngraph_function, argv=argv)
# Apply transformations
from openvino.tools.mo.back.offline_transformations import apply_user_transformations, apply_moc_transformations, \
from openvino.tools.ovc.moc_frontend.offline_transformations import apply_user_transformations, apply_moc_transformations, \
apply_moc_legacy_transformations, apply_fused_names_cleanup
apply_moc_transformations(ngraph_function)
@ -33,7 +33,7 @@ def moc_emit_ir(ngraph_function: Model, argv: argparse.Namespace):
apply_user_transformations(ngraph_function, parse_transform(argv.transform))
if argv.compress_to_fp16:
from openvino.tools.mo.back.offline_transformations import compress_model
from openvino.tools.ovc.moc_frontend.offline_transformations import compress_model
compress_model(ngraph_function)
apply_fused_names_cleanup(ngraph_function)

View File

@ -1,14 +1,71 @@
# Copyright (C) 2018-2023 Intel Corporation
# SPDX-License-Identifier: Apache-2.0
# flake8: noqa
# mypy: ignore-errors
import argparse
from typing import List
from openvino.tools.mo.front.extractor import create_params_with_custom_types
from openvino.tools.mo.utils.cli_parser import parse_transform
from openvino.tools.mo.utils.error import Error
from openvino.tools.ovc.cli_parser import parse_transform
from openvino.tools.ovc.error import Error
from openvino.runtime import Model
def get_new_placeholder_name(node_id: str, is_out_port: bool = False, port: int = 0):
"""
Forms a name of new placeholder created by cutting a graph
:param node_id: a node name that is cut
:param is_out_port: it is True iff output port is cut
:param port: a port number
:return: a name of new placeholder created by cutting a graph
"""
port_type = '_out' if is_out_port else ''
return '{}/placeholder{}_port_{}'.format(node_id, port_type, port)
def create_params_with_custom_types(packed_user_shapes: [None, dict]):
"""
Compute a list of placeholder names for which an user specifies custom type
:param packed_user_shapes: packed data that contains input node names,
their port numbers, shapes and data types
:return: a list of placeholder names for which an user specifies custom type
Example of packed_user_shapes dictionary:
packed_user_shapes =
{
'node_ID':
[
{'shape': None, 'in': 0},
{'shape': None, 'in': 1},
],
'node_1_ID':
[
{'shape': [1, 227, 227, 3], 'port': None, 'data_type': np.int32}
],
'node_2_ID':
[
{'shape': None, 'out': 3}
]
}
For which the function returns a list ['node_1_ID'] because this node only has custom data type
"""
if packed_user_shapes is None:
return []
params_with_custom_types = []
for input_name in packed_user_shapes:
for desc in packed_user_shapes[input_name]:
p_name = input_name
if 'port' in desc and desc['port'] is None: # neither input nor output port specified
user_defined_type = desc.get('data_type', None)
else: # need to check the particular port the Parameter was created for
p_name = get_new_placeholder_name(input_name, 'out' in desc,
desc['out'] if 'out' in desc else desc['in'])
user_defined_type = desc.get('data_type', None)
if user_defined_type is not None:
params_with_custom_types.append(p_name)
return params_with_custom_types
def get_available_transformations():
try:
from openvino._offline_transformations import apply_low_latency_transformation # pylint: disable=import-error,no-name-in-module
@ -54,7 +111,7 @@ def apply_fused_names_cleanup(func: object):
def apply_offline_transformations(func: Model, argv: argparse.Namespace):
from openvino.tools.mo.back.preprocessing import apply_preprocessing # pylint: disable=no-name-in-module,import-error
from openvino.tools.ovc.moc_frontend.preprocessing import apply_preprocessing # pylint: disable=no-name-in-module,import-error
# Apply preprocessing (mean/scale/reverse_channels/convert_layout/etc)
apply_preprocessing(ov_function=func, argv=argv)

View File

@ -1,10 +1,14 @@
# Copyright (C) 2018-2023 Intel Corporation
# SPDX-License-Identifier: Apache-2.0
# flake8: noqa
# mypy: ignore-errors
import os
import sys
import tempfile
class paddle_frontend_converter:
def __init__(self, model, inputs=None, outputs=None):
self.model = model

View File

@ -1,6 +1,9 @@
# Copyright (C) 2018-2023 Intel Corporation
# SPDX-License-Identifier: Apache-2.0
# flake8: noqa
# mypy: ignore-errors
import argparse
import io
import logging as log
@ -9,18 +12,34 @@ from copy import copy
from typing import List
import numpy as np
import os
from openvino.frontend import FrontEnd, InputModel, NotImplementedFailure, \
Place # pylint: disable=no-name-in-module,import-error
from openvino.runtime import PartialShape, Type # pylint: disable=no-name-in-module,import-error
from openvino.runtime.utils.types import get_element_type, \
from openvino.tools.ovc.types import get_element_type, \
get_numpy_ctype # pylint: disable=no-name-in-module,import-error
from openvino.tools.mo.middle.passes.infer import validate_batch_in_shape
from openvino.tools.mo.moc_frontend.analysis import json_model_analysis_dump
from openvino.tools.mo.moc_frontend.extractor import fe_user_data_repack, convert_params_lists_to_dicts, fe_output_user_data_repack
from openvino.tools.mo.moc_frontend.layout_utils import update_layout_to_dict, get_dimension_index_by_label
from openvino.tools.mo.utils.class_registration import get_enabled_and_disabled_transforms
from openvino.tools.mo.utils.error import Error
from openvino.tools.ovc.moc_frontend.analysis import json_model_analysis_dump
from openvino.tools.ovc.moc_frontend.extractor import fe_user_data_repack, convert_params_lists_to_dicts, fe_output_user_data_repack
from openvino.tools.ovc.moc_frontend.layout_utils import update_layout_to_dict, get_dimension_index_by_label
from openvino.tools.ovc.error import Error
from openvino.tools.ovc.utils import np_map_cast, mo_array, validate_batch_in_shape
def get_enabled_and_disabled_transforms():
"""
:return: tuple of lists with force enabled and disabled id of transformations.
"""
disabled_transforms = os.environ['MO_DISABLED_TRANSFORMS'] if 'MO_DISABLED_TRANSFORMS' in os.environ else ''
enabled_transforms = os.environ['MO_ENABLED_TRANSFORMS'] if 'MO_ENABLED_TRANSFORMS' in os.environ else ''
assert isinstance(enabled_transforms, str)
assert isinstance(disabled_transforms, str)
disabled_transforms = disabled_transforms.split(',')
enabled_transforms = enabled_transforms.split(',')
return enabled_transforms, disabled_transforms
def moc_pipeline(argv: argparse.Namespace, moc_front_end: FrontEnd):
@ -194,8 +213,6 @@ def moc_pipeline(argv: argparse.Namespace, moc_front_end: FrontEnd):
input_model.set_element_type(place, ov_type)
# prepare and cast value to dtype
from openvino.tools.mo.utils.type_utils import np_map_cast
from openvino.tools.mo.front.common.partial_infer.utils import mo_array
if isinstance(value, list):
casted_list = list()
for v in mo_array(value):

View File

@ -1,6 +1,9 @@
# Copyright (C) 2018-2023 Intel Corporation
# SPDX-License-Identifier: Apache-2.0
# flake8: noqa
# mypy: ignore-errors
import argparse
import logging as log
from copy import copy
@ -9,9 +12,9 @@ from openvino.preprocess import PrePostProcessor # pylint: disable=no-name-in-m
# pylint: disable=no-name-in-module,import-error
from openvino.runtime import Model, Layout, PartialShape, layout_helpers
from openvino.tools.mo.moc_frontend.layout_utils import update_layout_to_dict
from openvino.tools.mo.utils.error import Error
from openvino.tools.mo.utils.utils import refer_to_faq_msg
from openvino.tools.ovc.moc_frontend.layout_utils import update_layout_to_dict
from openvino.tools.ovc.error import Error
from openvino.tools.ovc.utils import refer_to_faq_msg
def update_mean_scale_to_dict(input_nodes: list, mean_scale_val, scale):
@ -19,7 +22,7 @@ def update_mean_scale_to_dict(input_nodes: list, mean_scale_val, scale):
Internal function. Updates mean/scale values from array to dictionary
:param: input_nodes Inputs of model
:param: mean_scale_val Parsed 'mean_scale_val' object from command line arguments
:param: scale Global scale factor for all inputs from --scale command line arguments
:param: scale Global scale factor for all inputs from scale command line arguments
"""
if not isinstance(mean_scale_val, dict):
if len(mean_scale_val) != len(input_nodes):

View File

@ -1,13 +1,17 @@
# Copyright (C) 2018-2023 Intel Corporation
# SPDX-License-Identifier: Apache-2.0
# flake8: noqa
# mypy: ignore-errors
import logging as log
import numpy as np
from openvino.tools.mo.moc_frontend.shape_utils import get_static_shape
from openvino.tools.mo.utils.error import Error
from openvino.tools.ovc.moc_frontend.shape_utils import get_static_shape
from openvino.tools.ovc.error import Error
from openvino.runtime import Tensor, Type, PartialShape
from openvino.runtime.utils.types import get_element_type_str
from openvino.tools.mo.utils.cli_parser import input_to_input_cut_info, input_shape_to_input_cut_info
from openvino.tools.ovc.types import get_element_type_str
from openvino.tools.ovc.cli_parser import input_to_input_cut_info, input_shape_to_input_cut_info
def get_pytorch_decoder(model, input_shape, example_inputs, args):

View File

@ -1,10 +1,13 @@
# Copyright (C) 2018-2023 Intel Corporation
# SPDX-License-Identifier: Apache-2.0
# flake8: noqa
# mypy: ignore-errors
import numpy as np
from openvino.runtime import PartialShape, Dimension
from openvino.tools.mo.utils.error import Error
from openvino.tools.mo.utils.cli_parser import get_placeholder_shapes, split_shapes
from openvino.tools.ovc.error import Error
from openvino.tools.ovc.cli_parser import get_placeholder_shapes, split_shapes
def get_static_shape(shape: [PartialShape, list, tuple], dynamic_value=None):

View File

@ -0,0 +1,13 @@
#!/usr/bin/env python3
# Copyright (C) 2018-2023 Intel Corporation
# SPDX-License-Identifier: Apache-2.0
import sys
if __name__ == "__main__":
from openvino.tools.ovc.telemetry_utils import init_mo_telemetry
from openvino.tools.ovc.main import main
init_mo_telemetry()
sys.exit(main())

View File

@ -1,6 +1,9 @@
# Copyright (C) 2018-2023 Intel Corporation
# SPDX-License-Identifier: Apache-2.0
# flake8: noqa
# mypy: ignore-errors
telemetry_params = {
'TID': "UA-17808594-29"
}

View File

@ -0,0 +1,31 @@
# Copyright (C) 2018-2023 Intel Corporation
# SPDX-License-Identifier: Apache-2.0
# flake8: noqa
# mypy: ignore-errors
class Telemetry(object):
"""
Stab file for the Telemetry class which is used when Telemetry class is not available.
"""
def __init__(self, *arg, **kwargs):
pass
def send_event(self, *arg, **kwargs):
pass
def send_error(self, *arg, **kwargs):
pass
def start_session(self, *arg, **kwargs):
pass
def end_session(self, *arg, **kwargs):
pass
def force_shutdown(self, *arg, **kwargs):
pass
def send_stack_trace(self, *arg, **kwargs):
pass

View File

@ -0,0 +1,77 @@
# Copyright (C) 2018-2023 Intel Corporation
# SPDX-License-Identifier: Apache-2.0
# flake8: noqa
# mypy: ignore-errors
import argparse
import numbers
from openvino.runtime import get_version as get_rt_version
from openvino.tools.ovc.cli_parser import get_params_with_paths_list
from openvino.tools.ovc.telemetry_params import telemetry_params
from openvino.tools.ovc.utils import check_values_equal
try:
import openvino_telemetry as tm
except ImportError:
import openvino.tools.ovc.telemetry_stub as tm
def init_mo_telemetry():
_ = tm.Telemetry(tid=get_tid(), app_name='Model Conversion API', app_version=get_rt_version())
def send_framework_info(framework: str):
"""
This function sends information about used framework.
:param framework: framework name.
"""
t = tm.Telemetry()
t.send_event('mo', 'framework', framework)
def get_tid():
"""
This function returns the ID of the database to send telemetry.
"""
return telemetry_params['TID']
def send_conversion_result(conversion_result: str, need_shutdown=False):
t = tm.Telemetry()
t.send_event('mo', 'conversion_result', conversion_result)
t.end_session('mo')
if need_shutdown:
t.force_shutdown(1.0)
def arg_to_str(arg):
# This method converts to string only known types, otherwise returns string with name of the type
from openvino.runtime import PartialShape, Shape, Type, Layout
if isinstance(arg, (PartialShape, Shape, Type, Layout)):
return str(arg)
if isinstance(arg, (str, numbers.Number, bool)):
return str(arg)
return str(type(arg))
def send_params_info(argv: argparse.Namespace, cli_parser: argparse.ArgumentParser):
"""
This function sends information about used command line parameters.
:param argv: command line parameters.
:param cli_parser: command line parameters parser.
"""
t = tm.Telemetry()
params_with_paths = get_params_with_paths_list()
for arg in vars(argv):
arg_value = getattr(argv, arg)
if not check_values_equal(arg_value, cli_parser.get_default(arg)):
if arg in params_with_paths:
# If command line argument value is a directory or a path to file it is not sent
# as it may contain confidential information. "1" value is used instead.
param_str = arg + ":" + str(1)
else:
param_str = arg + ":" + arg_to_str(arg_value)
t.send_event('mo', 'cli_parameters', param_str)

View File

@ -0,0 +1,159 @@
# Copyright (C) 2018-2023 Intel Corporation
# SPDX-License-Identifier: Apache-2.0
"""Functions related to converting between Python and numpy types and openvino types."""
import logging
from typing import List, Union
import numpy as np
from openvino.runtime.exceptions import OVTypeError
from openvino.runtime import Node, Shape, Output, Type
from openvino.runtime.op import Constant
log = logging.getLogger(__name__)
TensorShape = List[int]
NumericData = Union[int, float, np.ndarray]
NumericType = Union[type, np.dtype]
ScalarData = Union[int, float]
NodeInput = Union[Node, NumericData]
openvino_to_numpy_types_map = [
(Type.boolean, bool),
(Type.f16, np.float16),
(Type.f32, np.float32),
(Type.f64, np.float64),
(Type.i8, np.int8),
(Type.i16, np.int16),
(Type.i32, np.int32),
(Type.i64, np.int64),
(Type.u8, np.uint8),
(Type.u16, np.uint16),
(Type.u32, np.uint32),
(Type.u64, np.uint64),
(Type.bf16, np.uint16),
]
openvino_to_numpy_types_str_map = [
("boolean", bool),
("f16", np.float16),
("f32", np.float32),
("f64", np.float64),
("i8", np.int8),
("i16", np.int16),
("i32", np.int32),
("i64", np.int64),
("u8", np.uint8),
("u16", np.uint16),
("u32", np.uint32),
("u64", np.uint64),
]
def get_element_type(data_type: NumericType) -> Type:
"""Return an ngraph element type for a Python type or numpy.dtype."""
if data_type is int:
log.warning("Converting int type of undefined bitwidth to 32-bit ngraph integer.")
return Type.i32
if data_type is float:
log.warning("Converting float type of undefined bitwidth to 32-bit ngraph float.")
return Type.f32
ov_type = next(
(ov_type for (ov_type, np_type) in openvino_to_numpy_types_map if np_type == data_type),
None,
)
if ov_type:
return ov_type
raise OVTypeError("Unidentified data type %s", data_type)
def get_element_type_str(data_type: NumericType) -> str:
"""Return an ngraph element type string representation for a Python type or numpy dtype."""
if data_type is int:
log.warning("Converting int type of undefined bitwidth to 32-bit ngraph integer.")
return "i32"
if data_type is float:
log.warning("Converting float type of undefined bitwidth to 32-bit ngraph float.")
return "f32"
ov_type = next(
(ov_type for (ov_type, np_type) in openvino_to_numpy_types_str_map if np_type == data_type),
None,
)
if ov_type:
return ov_type
raise OVTypeError("Unidentified data type %s", data_type)
def get_dtype(openvino_type: Type) -> np.dtype:
"""Return a numpy.dtype for an openvino element type."""
np_type = next(
(np_type for (ov_type, np_type) in openvino_to_numpy_types_map if ov_type == openvino_type),
None,
)
if np_type:
return np.dtype(np_type)
raise OVTypeError("Unidentified data type %s", openvino_type)
def get_numpy_ctype(openvino_type: Type) -> type:
"""Return numpy ctype for an openvino element type."""
np_type = next(
(np_type for (ov_type, np_type) in openvino_to_numpy_types_map if ov_type == openvino_type),
None,
)
if np_type:
return np_type
raise OVTypeError("Unidentified data type %s", openvino_type)
def get_ndarray(data: NumericData) -> np.ndarray:
"""Wrap data into a numpy ndarray."""
if type(data) == np.ndarray:
return data # type: ignore
return np.array(data)
def get_shape(data: NumericData) -> TensorShape:
"""Return a shape of NumericData."""
if type(data) == np.ndarray:
return data.shape # type: ignore
elif type(data) == list:
return [len(data)] # type: ignore
return []
def make_constant_node(value: NumericData, dtype: Union[NumericType, Type] = None) -> Constant:
"""Return an openvino Constant node with the specified value."""
ndarray = get_ndarray(value)
if dtype is not None:
element_type = get_element_type(dtype) if isinstance(dtype, (type, np.dtype)) else dtype
else:
element_type = get_element_type(ndarray.dtype)
return Constant(element_type, Shape(ndarray.shape), ndarray.flatten().tolist())
def as_node(input_value: NodeInput) -> Node:
"""Return input values as nodes. Scalars will be converted to Constant nodes."""
if issubclass(type(input_value), Node):
return input_value
if issubclass(type(input_value), Output):
return input_value
return make_constant_node(input_value)
def as_nodes(*input_values: NodeInput) -> List[Node]:
"""Return input values as nodes. Scalars will be converted to Constant nodes."""
return [as_node(input_value) for input_value in input_values]

View File

@ -0,0 +1,139 @@
# Copyright (C) 2018-2023 Intel Corporation
# SPDX-License-Identifier: Apache-2.0
# flake8: noqa
# mypy: ignore-errors
import os
import re
from typing import Iterable, Union
import numpy as np
from openvino.tools.ovc.error import Error
try:
import openvino_telemetry as tm
except ImportError:
import openvino.tools.ovc.telemetry_stub as tm
dynamic_dimension = np.ma.masked
def refer_to_faq_msg(question_num: int):
try:
t = tm.Telemetry()
t.send_event('mo', 'error_info', "faq:" + str(question_num))
except Exception:
# Telemetry can be not initialized if it is used in MO IR Reader
pass
return '\n For more information please refer to Model Conversion API FAQ, question #{0}. ' \
'(https://docs.openvino.ai/2023.0/openvino_docs_MO_DG_prepare_model_Model_Optimizer_FAQ.html' \
'?question={0}#question-{0})'.format(question_num)
def get_mo_root_dir():
"""
Return the absolute path to the Model Conversion API root directory (where mo folder is located)
:return: path to the MO root directory
"""
return os.path.normpath(os.path.join(os.path.dirname(os.path.abspath(os.path.realpath(__file__))), os.pardir))
def check_values_equal(val1, val2):
# This method is needed to check equality of values where some values can be None
if val1 is None and val2 is None:
return True
if val1 is None:
return False
if val2 is None:
return False
return val1 == val2
np_map_cast = {bool: lambda x: bool_cast(x),
np.int8: lambda x: np.int8(x),
np.int16: lambda x: np.int16(x),
np.int32: lambda x: np.int32(x),
np.int64: lambda x: np.int64(x),
np.uint8: lambda x: np.uint8(x),
np.uint16: lambda x: np.uint16(x),
np.uint32: lambda x: np.uint32(x),
np.uint64: lambda x: np.uint64(x),
np.float16: lambda x: np.float16(x),
np.float32: lambda x: np.float32(x),
np.double: lambda x: np.double(x),
str: lambda x: str(x)}
def bool_cast(x):
if isinstance(x, str):
return False if x.lower() in ['false', '0'] else True if x.lower() in ['true', '1'] else 'unknown_boolean_cast'
else:
return bool(x)
def mo_array(value: Union[Iterable[Union[float, int]], float, int], dtype=None) -> np.ndarray:
"""
This function acts in a same way as np.array except for the case when dtype is not provided
and np.array return fp64 array this function returns fp32 array
"""
x = np.array(value, dtype=dtype)
if not isinstance(value, np.ndarray) and x.dtype == np.float64 and dtype != np.float64:
x = x.astype(np.float32)
return x
def validate_batch_in_shape(shape, layer_name: str):
"""
Raises Error #39 if shape is not valid for setting batch size
Parameters
----------
shape: current shape of layer under validation
layer_name: name of layer under validation
"""
if len(shape) == 0 or (shape[0] is not dynamic_dimension and shape[0] not in (-1, 0, 1)):
raise Error(('The input layer {} has a shape {} defined in the model. \n\n' +
'When you use "batch" option, Model Conversion API applies its value to the first ' +
'element of the shape if it is equal to -1, 0 or 1. Otherwise, this is the ambiguous ' +
'situation - it is not known in advance whether the layer has the batch ' +
'dimension or not.\n\n For example, you want to set batch dimension equals 100 ' +
'for the input layer "data" with shape (10,34). Although you can not use "batch", ' +
'you should pass "input_shape=[100,34]" instead of "batch=100". \n\n' +
'You can also specify batch dimension by setting "layout". \n\n')
.format(layer_name, shape))
def deduce_legacy_frontend_by_namespace(argv):
if not hasattr(argv, 'framework') or not argv.framework:
if getattr(argv, 'saved_model_dir', None) or getattr(argv, 'input_meta_graph', None):
argv.framework = 'tf'
elif getattr(argv, 'input_symbol', None) or getattr(argv, 'pretrained_model_name', None):
argv.framework = 'mxnet'
elif getattr(argv, 'input_proto', None):
argv.framework = 'caffe'
elif argv.input_model is None:
raise Error('Path to input model is required: use "input_model".')
else:
argv.framework = guess_framework_by_ext(argv.input_model)
return map(lambda x: argv.framework == x, ['tf', 'caffe', 'mxnet', 'kaldi', 'onnx'])
def guess_framework_by_ext(input_model_path: str) -> int:
if re.match(r'^.*\.caffemodel$', input_model_path):
return 'caffe'
elif re.match(r'^.*\.pb$', input_model_path):
return 'tf'
elif re.match(r'^.*\.pbtxt$', input_model_path):
return 'tf'
elif re.match(r'^.*\.params$', input_model_path):
return 'mxnet'
elif re.match(r'^.*\.nnet$', input_model_path):
return 'kaldi'
elif re.match(r'^.*\.mdl', input_model_path):
return 'kaldi'
elif re.match(r'^.*\.onnx$', input_model_path):
return 'onnx'

View File

@ -0,0 +1,83 @@
# Copyright (C) 2018-2023 Intel Corporation
# SPDX-License-Identifier: Apache-2.0
# flake8: noqa
# mypy: ignore-errors
import re
from openvino.runtime import get_version as get_ie_version
def extract_release_version(version: str):
patterns = [
# captures release version set by CI for example: '2021.1.0-1028-55e4d5673a8'
r"^([0-9]+).([0-9]+)*",
# captures release version generated by MO from release branch, for example: 'custom_releases/2021/1_55e4d567'
r"_releases/([0-9]+)/([0-9]+)_*"
]
for pattern in patterns:
m = re.search(pattern, version)
if m and len(m.groups()) == 2:
return m.group(1), m.group(2)
return None, None
def simplify_version(version: str):
release_version = extract_release_version(version)
if release_version == (None, None):
return "custom"
return "{}.{}".format(*release_version)
def get_simplified_ie_version(version=None):
from openvino.runtime import get_version
if version is None:
version = get_version()
# To support legacy IE versions
m = re.match(r"^([0-9]+).([0-9]+).(.*)", version)
if m and len(m.groups()) == 3:
return simplify_version(m.group(3))
return simplify_version(version)
def extract_hash_from_version(full_version: str):
res = re.findall(r'[-_]([a-f0-9]{7,40})', full_version)
if len(res) > 0:
return res[0]
else:
return None
class SingletonMetaClass(type):
def __init__(self, cls_name, super_classes, dic):
self.__single_instance = None
super().__init__(cls_name, super_classes, dic)
def __call__(cls, *args, **kwargs):
if cls.__single_instance is None:
cls.__single_instance = super(SingletonMetaClass, cls).__call__(*args, **kwargs)
return cls.__single_instance
class VersionChecker(metaclass=SingletonMetaClass):
def __init__(self):
self.runtime_checked = False
self.mo_version = None
self.ie_version = None
self.mo_simplified_version = None
self.ie_simplified_version = None
def get_ie_version(self):
if self.ie_version:
return self.ie_version
self.ie_version = get_ie_version()
return self.ie_version
def get_ie_simplified_version(self):
if self.ie_simplified_version:
return self.ie_simplified_version
self.ie_simplified_version = get_simplified_ie_version()
return self.ie_simplified_version

View File

View File

@ -0,0 +1,134 @@
# Copyright (C) 2018-2023 Intel Corporation
# SPDX-License-Identifier: Apache-2.0
import argparse
import io
import os
import unittest
from contextlib import redirect_stdout
from unittest.mock import patch
from openvino.tools.ovc.main import main
from openvino.tools.ovc.get_ov_update_message import get_tf_fe_message, get_compression_message
from openvino.tools.ovc.get_ov_update_message import get_try_legacy_fe_message
def arg_parse_helper(input_model,
use_legacy_frontend,
use_new_frontend,
input_model_is_text,
framework,
compress_to_fp16=False,
freeze_placeholder_with_value=None,
tensorflow_object_detection_api_pipeline_config=None):
path = os.path.dirname(__file__)
input_model = os.path.join(path, "test_models", input_model)
return argparse.Namespace(
input_model=input_model,
use_legacy_frontend=use_legacy_frontend,
use_new_frontend=use_new_frontend,
framework=framework,
input_model_is_text=input_model_is_text,
log_level='INFO',
silent=True,
model_name=None,
transform=[],
scale=None,
output=None,
input=None,
input_shape=None,
batch=None,
input_checkpoint=None,
saved_model_dir=None,
input_meta_graph=None,
saved_model_tags=None,
output_dir='.',
mean_values=(),
scale_values=(),
layout={},
source_layout={},
target_layout={},
freeze_placeholder_with_value=freeze_placeholder_with_value,
data_type=None,
tensorflow_custom_operations_config_update=None,
tensorflow_object_detection_api_pipeline_config=tensorflow_object_detection_api_pipeline_config,
compress_to_fp16=compress_to_fp16,
extensions=None
)
class TestInfoMessagesTFFE(unittest.TestCase):
@patch('argparse.ArgumentParser.parse_args',
return_value=arg_parse_helper(input_model="model_int32.pbtxt",
use_legacy_frontend=False, use_new_frontend=True,
framework=None, input_model_is_text=True))
def test_api20_only(self, mock_argparse):
f = io.StringIO()
with redirect_stdout(f):
main()
std_out = f.getvalue()
tf_fe_message_found = get_tf_fe_message() in std_out
assert tf_fe_message_found
@patch('openvino.tools.ovc.convert_impl.driver', side_effect=Exception('MESSAGE'))
def run_fail_tf_fe(self, mock_driver):
from openvino.tools.ovc import convert_model
path = os.path.dirname(__file__)
convert_model(os.path.join(path, "test_models", "model_int32.pbtxt"), silent=False)
def test_suggest_legacy_fe(self):
f = io.StringIO()
with redirect_stdout(f):
try:
self.run_fail_tf_fe()
except:
pass
std_out = f.getvalue()
assert get_try_legacy_fe_message() in std_out
class TestInfoMessagesTFFEWithFallback(unittest.TestCase):
@patch('argparse.ArgumentParser.parse_args',
return_value=arg_parse_helper(input_model="model_switch_merge.pbtxt",
use_legacy_frontend=False, use_new_frontend=False,
framework=None, input_model_is_text=True,
freeze_placeholder_with_value="is_training->False"))
def test_tf_fe_message_fallback(self, mock_argparse):
f = io.StringIO()
with redirect_stdout(f):
main()
std_out = f.getvalue()
tf_fe_message_found = get_try_legacy_fe_message() in std_out
assert not tf_fe_message_found, 'TF FE Info message is found for the fallback case'
@patch('argparse.ArgumentParser.parse_args',
return_value=arg_parse_helper(input_model="model_int32.pbtxt",
use_legacy_frontend=False, use_new_frontend=True,
compress_to_fp16=True,
framework=None, input_model_is_text=True,
tensorflow_object_detection_api_pipeline_config="config.yml"))
def test_tf_fe_message_fallback(self, mock_argparse):
f = io.StringIO()
with redirect_stdout(f):
main()
std_out = f.getvalue()
tf_fe_message_found = "The provided option \"tensorflow_object_detection_api_pipeline_config\" " \
"refers to legacy functionality. Please try to install openvino-dev and " \
"use convert_model() from openvino.tools.mo." in std_out
assert not tf_fe_message_found, 'TF FE Info message is found for the fallback case'
class TestInfoMessagesCompressFP16(unittest.TestCase):
@patch('argparse.ArgumentParser.parse_args',
return_value=arg_parse_helper(input_model="model_int32.pbtxt",
use_legacy_frontend=False, use_new_frontend=True,
compress_to_fp16=True,
framework=None, input_model_is_text=True))
def test_compress_to_fp16(self, mock_argparse):
f = io.StringIO()
with redirect_stdout(f):
main()
std_out = f.getvalue()
fp16_compression_message_found = get_compression_message() in std_out
assert fp16_compression_message_found

View File

@ -0,0 +1,328 @@
# Copyright (C) 2018-2023 Intel Corporation
# SPDX-License-Identifier: Apache-2.0
import os
import unittest
import numpy as np
from generator import generator, generate
from openvino.runtime import Core
from openvino.tools.ovc.convert import convert_model
@generator
class TestMoFreezePlaceholderTFFE(unittest.TestCase):
def basic(self, input_model, argv_input, inputs, dtype, expected, freeze_placeholder_with_value=None,
input_shape=None, only_conversion=False, input_model_is_text=True, use_new_frontend=True,
use_legacy_frontend=False):
path = os.path.dirname(__file__)
input_model = os.path.join(path, "test_models", input_model)
try:
model = convert_model(input_model, input=argv_input,
freeze_placeholder_with_value=freeze_placeholder_with_value,
input_shape=input_shape, input_model_is_text=input_model_is_text,
use_new_frontend=use_new_frontend, use_legacy_frontend=use_legacy_frontend,
framework="tf")
except Exception as ex:
self.fail("Model conversion failed due to error: {}".format(ex))
if only_conversion:
return
ie = Core()
exec_net = ie.compile_model(model, "CPU")
req = exec_net.create_infer_request()
results = req.infer(inputs)
values = list(results.values())[0]
if dtype is not None:
assert values.dtype == dtype
assert np.allclose(values, expected)
@generate(
*[
(
"in1[1 4]->[1.0 2.0 3.0 4.0],in2[1 4]{f32}->[1.0 2.0 3.0 4.0]",
{},
np.array([2.0, 4.0, 6.0, 8.0]),
np.float32,
),
(
"in2{f32}->[0.0 0.0 0.0 0.0]",
{"in1": np.array([[1.0, 2.0], [3.0, 4.0]])},
np.array([[1.0, 2.0], [3.0, 4.0]]),
np.float32,
),
(
"in2->[1.0 15.0 15.5 1.0]",
{"in1": np.array([[2.0, 4.0], [12.0, 8.0]])},
np.array([[3.0, 19.0], [27.5, 9.0]]),
np.float32,
),
(
"in1[1 4]{i32}->[1 2 3 4],in2[1 4]{i32}->[1 2 3 4]",
{},
np.array([2.0, 4.0, 6.0, 8.0]),
np.int32,
),
],
)
def test_fp32(self, input_freezing_value, inputs, expected,
dtype):
self.basic("model_fp32.pbtxt", input_freezing_value, inputs, dtype, expected)
@generate(
*[
(
"in1[1 4]->[1 2 3 4],in2[1 4]{i32}->[1 2 3 4]",
{},
np.array([1, 4, 9, 16]),
np.int32,
),
(
"in2->[2 5 6 7 3 2]",
{"in1": np.array([[2, 4, 1], [1, 2, 8]])},
np.array([[4, 20, 6], [7, 6, 16]]),
np.int32,
),
],
)
def test_int32(self, input_freezing_value, inputs, expected,
dtype=None):
self.basic("model_int32.pbtxt", input_freezing_value, inputs, dtype, expected)
@generate(
*[
(
"in1[2]->[True False],in2[2]->[True True]",
{},
np.array([True, False], dtype=bool),
bool,
),
(
"in2[2,3]->[True,True,False,True,True,False]",
{"in1": np.array([[False, True, True], [False, True, True]], dtype=bool)},
np.array([[False, True, False], [False, True, False]], dtype=bool),
bool,
),
(
"in2[]->True",
{"in1": np.array([[False, True, True], [False, True, True]], dtype=bool)},
np.array([[False, True, True], [False, True, True]], dtype=bool),
bool,
),
],
)
def test_bool(self, input_freezing_value, inputs, expected,
dtype=None):
self.basic("model_bool.pbtxt", input_freezing_value, inputs, dtype, expected)
@generate(
*[
(
"in1[3]->[1 2 3],in2[3]->[4 5 6],cond->False",
{},
np.array([4, 5, 6], dtype=np.float32),
np.float32,
None
),
(
None,
{"in1": np.array([2.0, 4.0, 6.0], dtype=np.float32),
"in2": np.array([1.0, 3.0, 5.0], dtype=np.float32)},
np.array([2, 4, 6], dtype=np.float32),
np.float32,
"cond->False",
None,
True # fill a bug to investigate why compilation of this model is hang on
),
# case: input_shape + freeze_placeholder_with_value
(
None,
{"in2": np.array([1.0, 3.0, 5.0], dtype=np.float32)},
np.array([2, 4, 6], dtype=np.float32),
np.float32,
"in1->[2.0 4.0 6.0],cond->True",
"[3]",
False
),
],
)
def test_bool2(self, input_freezing_value, inputs, expected,
dtype=None, freeze_placeholder_with_value=None, input_shape=None, only_conversion=False):
self.basic("model_bool2.pbtxt", input_freezing_value, inputs, dtype, expected, freeze_placeholder_with_value,
input_shape, only_conversion)
@generate(
*[
(
"add:0[3],z",
{"add:0": np.array([4, 5, 6], dtype=np.float32), "z": np.array([1, 2, 3], dtype=np.float32)},
np.array([4, 10, 18], dtype=np.float32),
np.float32,
None
),
(
"add:0{i32}[3],z{i32}",
{"add:0": np.array([4, 5, 6], dtype=np.int32), "z": np.array([1, 2, 3], dtype=np.int32)},
np.array([4, 10, 18], dtype=np.int32),
np.int32,
None
),
],
)
def test_cutting_fp32(self, input_freezing_value, inputs, expected,
dtype=None, freeze_placeholder_with_value=None, input_shape=None, only_conversion=False):
self.basic("model_three_inputs.pbtxt", input_freezing_value, inputs, dtype, expected,
freeze_placeholder_with_value,
input_shape, only_conversion, True)
@generate(
*[
(
"x[1,4],y[4]",
{"x": np.array([[3, 2, 1, 5]], dtype=np.int32), "y": np.array([0, -1, -7, 8], dtype=np.int32)},
np.array([[3, 1, -6, 13]], dtype=np.int32),
np.int32,
None
),
(
"x,y",
{"x": np.array([[-3, 20, 1]], dtype=np.int32), "y": np.array([[10, -11, -17]], dtype=np.int32)},
np.array([[7, 9, -16]], dtype=np.int32),
np.int32,
None
),
(
"x",
{"x": np.array([[-3, 20, 1]], dtype=np.int32)},
np.array([[-2, 22, 4], [1, 25, 7]], dtype=np.int32),
np.int32,
None
),
],
)
def test_placeholder_with_default(self, inputs, inputs_data, expected,
dtype=None, freeze_placeholder_with_value=None, input_shape=None,
only_conversion=False):
self.basic("placeholder_with_default.pbtxt", inputs, inputs_data, dtype, expected,
freeze_placeholder_with_value,
input_shape, only_conversion, True)
@generate(
*[
(
"x[4],y->2.0",
{"x": np.array([3, 2, 1, 5], dtype=np.float32)},
np.array([6, 4, 2, 10], dtype=np.float32),
np.float32,
None
),
(
"x[1],y->[2.0,3.0]",
{"x": np.array([3], dtype=np.float32)},
np.array([6, 9], dtype=np.float32),
np.float32,
None
),
],
)
def test_freeze_placeholder_with_unknown_rank(self, inputs, inputs_data, expected,
dtype=None, freeze_placeholder_with_value=None, input_shape=None,
only_conversion=False):
self.basic("mul_with_unknown_rank_y.pbtxt", inputs, inputs_data, dtype, expected,
freeze_placeholder_with_value,
input_shape, only_conversion, True)
def test_conversion_failure_fallback_use_new_frontend(self):
with self.assertRaisesRegex(Exception,
"\[TensorFlow Frontend\] Internal error, no translator found for operation\(s\)\: "
"Enter\, Exit\, LoopCond\, Merge\, NextIteration\, Switch\, TensorArrayGatherV3\, "
"TensorArraySizeV3\, TensorArrayV3"):
self.basic("ctc_model_based.pbtxt", None, None, None, None,
None, None, True, True, True, False)
@unittest.skip("88349: Fix auto-pruning in legacy FE")
def test_conversion_model_oneshot_iterator_use_legacy_frontend(self):
self.basic("model_oneshot_iterator.pbtxt", None, None, None, None,
None, None, True, True, False, True)
def test_conversion_model_oneshot_iterator_default(self):
self.basic("model_oneshot_iterator.pbtxt", None, None, None, None,
None, None, True, True, False, False)
@generate(
*[
(
"in2{f32}->[0.0 0.0 0.0 0.0]",
{"in1": np.array([[1.0, 2.0], [3.0, 4.0]])},
np.array([[1.0, 2.0], [3.0, 4.0]]),
np.float32,
),
(
"in2->[1.0 15.0 15.5 1.0]",
{"in1": np.array([[2.0, 4.0], [12.0, 8.0]])},
np.array([[3.0, 19.0], [27.5, 9.0]]),
np.float32,
),
],
)
@unittest.skip("109220: Use generating script for this test model instead of Git LFS")
def test_conversion_model_with_non_standard_extension(self, input_freezing_value, inputs, expected,
dtype):
self.basic("model_fp32.frozen", input_freezing_value, inputs, dtype, expected, only_conversion=False,
input_model_is_text=False, use_new_frontend=True,
use_legacy_frontend=False)
@unittest.skip("109220: Make TF FE to return the error")
def test_conversion_dir_model(self):
with self.assertRaisesRegex(Exception,
"Internal error or inconsistent input model: the frontend supports "
"only frozen binary protobuf format."):
self.basic(".", None, None, None, None,
only_conversion=True, input_model_is_text=False, use_new_frontend=True,
use_legacy_frontend=False)
@generate(
*[
(
{"x": np.array([1, 2], dtype=np.int32), "y": np.array([4], dtype=np.int32)},
np.array([-3, -2], dtype=np.int32),
np.int32,
),
(
{"x": np.array([20, 25], dtype=np.int32), "y": np.array([10], dtype=np.int32)},
np.array([30, 35], dtype=np.int32),
np.int32,
)
],
)
def test_conversion_pbtxt_model_with_inference(self, inputs, expected, dtype):
self.basic("model_with_if.pbtxt", None, inputs, dtype, expected, only_conversion=False,
input_model_is_text=False, use_new_frontend=True, use_legacy_frontend=False)
@generate(
*[
# new frontend
(
"model_add_with_undefined_constant.pbtxt",
"x[2,3]",
{"x": np.array([[12, 13, 10], [11, 14, 16]], dtype=np.float32)},
np.array([[12, 13, 10], [11, 14, 16]], dtype=np.float32),
np.float32, True, False,
),
(
"model_mul_with_undefined_constant.pbtxt",
"x[2]",
{"x": np.array([11, -12], dtype=np.int32)},
np.array([0, 0], dtype=np.int32),
np.int32, True, False,
),
],
)
def test_conversion_model_with_undefined_constant(self, model_name, argv_input, inputs, expected, dtype,
use_new_frontend, use_legacy_frontend):
self.basic(model_name, argv_input, inputs, dtype, expected, only_conversion=False,
input_model_is_text=True, use_new_frontend=use_new_frontend, use_legacy_frontend=use_legacy_frontend)

View File

@ -0,0 +1,42 @@
# Copyright (C) 2018-2023 Intel Corporation
# SPDX-License-Identifier: Apache-2.0
import os
import tempfile
import unittest
from generator import generator, generate
from openvino.tools.ovc.convert import convert_model
@generator
class TestMoFreezePlaceholderTFFE(unittest.TestCase):
@generate(
*[
# the default frontend
(
False, False, None
),
(
False, False, "tf"
),
# new frontend
(
True, False, None
),
(
True, False, "tf"
),
],
)
def test_conversion_fake_pb_model(self, use_new_frontend, use_legacy_frontend, framework):
with self.assertRaisesRegex(Exception,
"Internal error or inconsistent input model: the frontend supports frozen formats"
" \(.pb and .pbtxt\), SavedModel and MetaGraph \(.meta\), and v1 checkpoints."):
path = os.path.dirname(__file__)
input_model = os.path.join(path, "test_models", "fake.pb")
convert_model(input_model,
use_new_frontend=use_new_frontend, use_legacy_frontend=use_legacy_frontend,
framework=framework)

View File

@ -10,8 +10,8 @@ from generator import generator, generate
import openvino.runtime.opset11 as opset11
from openvino.runtime import Model
from openvino.runtime import PartialShape, Dimension
from openvino.tools.mo.convert import convert_model
from openvino.tools.mo.utils.error import Error
from openvino.tools.ovc.convert import convert_model
from openvino.tools.ovc.error import Error
@generator
@ -92,6 +92,6 @@ class TestConversionWithBatchAndLayout(unittest.TestCase):
def test_model_expected_failure(self, model_name: str, batch: int, layout: str, refs_shapes: dict):
# try to override batch size by default index (without specifying layout)
with self.assertRaisesRegex(Error,
"When you use -b \(--batch\) option, Model Optimizer applies its value to the first "
"When you use \"batch\" option, Model Conversion API applies its value to the first "
"element of the shape if it is equal to -1, 0 or 1\."):
self.basic_check(model_name, batch, layout, refs_shapes)

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,58 @@
node {
name: "x"
op: "Placeholder"
attr {
key: "dtype"
value {
type: DT_FLOAT
}
}
attr {
key: "shape"
value {
shape {
dim {
size: 2
}
dim {
size: 3
}
}
}
}
}
node {
name: "Const"
op: "Const"
attr {
key: "dtype"
value {
type: DT_FLOAT
}
}
attr {
key: "value"
value {
tensor {
dtype: DT_FLOAT
tensor_shape {
dim {
size: 3
}
}
}
}
}
}
node {
name: "add"
op: "AddV2"
input: "x"
input: "Const"
attr {
key: "T"
value {
type: DT_FLOAT
}
}
}

Some files were not shown because too many files have changed in this diff Show More