Show message with suggestion to try legacy FE in case of conversion error (#17088)

* Moved exception checks to _convert(), added suggestion to try legacy TF in case of conversion fail.

* Added test.

* Added send_conversion_result() method.

* Small correction.

* Update tools/mo/openvino/tools/mo/convert_impl.py

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

* Moved test_suggest_legacy_fe() test to check_info_messages_test.py.

* Removed not needed import.

* Small correction.

---------

Co-authored-by: Roman Kazantsev <roman.kazantsev@intel.com>
This commit is contained in:
Anastasiia Pnevskaia 2023-04-25 15:57:01 +02:00 committed by GitHub
parent 57d4ca27e6
commit acd424bb5e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 106 additions and 72 deletions

View File

@ -348,6 +348,6 @@ def convert_model(
del params['args'] del params['args']
params.update(args) params.update(args)
cli_parser = get_all_cli_parser() cli_parser = get_all_cli_parser()
ov_model, _ = _convert(cli_parser, framework, params) ov_model, _ = _convert(cli_parser, framework, params, True)
restore_logger_state(logger_state) restore_logger_state(logger_state)
return ov_model return ov_model

View File

@ -7,6 +7,7 @@ import logging as log
import os import os
import platform import platform
import sys import sys
import traceback
from collections import OrderedDict from collections import OrderedDict
from copy import deepcopy from copy import deepcopy
from distutils.version import LooseVersion from distutils.version import LooseVersion
@ -36,14 +37,17 @@ from openvino.tools.mo.utils.cli_parser import check_available_transforms, \
get_model_name_from_args, depersonalize, get_mo_convert_params, input_to_input_cut_info, \ 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 input_shape_to_input_cut_info, freeze_placeholder_to_input_cut_info
from openvino.tools.mo.utils.error import Error 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.mo.utils.model_analysis import AnalysisResults
from openvino.tools.mo.utils.version import VersionChecker 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.guess_framework import deduce_legacy_frontend_by_namespace
from openvino.tools.mo.utils.logger import init_logger, progress_printer from openvino.tools.mo.utils.logger import init_logger, progress_printer
from openvino.tools.mo.utils.utils import refer_to_faq_msg from openvino.tools.mo.utils.utils import refer_to_faq_msg
from openvino.tools.mo.utils.telemetry_utils import send_params_info, send_framework_info from openvino.tools.mo.utils.telemetry_utils import send_params_info, send_framework_info, send_conversion_result, \
get_tid
from openvino.tools.mo.utils.versions_checker import check_requirements, get_environment_setup # pylint: disable=no-name-in-module from openvino.tools.mo.utils.versions_checker import check_requirements, get_environment_setup # pylint: disable=no-name-in-module
from openvino.tools.mo.utils.telemetry_utils import get_tid
from openvino.tools.mo.moc_frontend.check_config import legacy_extensions_used 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, convert_pytorch_via_onnx from openvino.tools.mo.moc_frontend.pytorch_frontend_utils import get_pytorch_decoder, convert_pytorch_via_onnx
from openvino.tools.mo.moc_frontend.shape_utils import parse_input_shapes, get_static_shape from openvino.tools.mo.moc_frontend.shape_utils import parse_input_shapes, get_static_shape
@ -812,10 +816,8 @@ def pack_params_to_args_namespace(args: dict, cli_parser: argparse.ArgumentParse
# so we need to set them in argv separately # so we need to set them in argv separately
if value is not None and getattr(argv, key, None) != value: if value is not None and getattr(argv, key, None) != value:
setattr(argv, key, value) setattr(argv, key, value)
argv.is_python_api_used = True
else: else:
argv = cli_parser.parse_args() argv = cli_parser.parse_args()
argv.is_python_api_used = False
return argv return argv
@ -835,7 +837,20 @@ def update_args_for_saved_model_dir(args: dict):
args['input_model'] = None args['input_model'] = None
def _convert(cli_parser: argparse.ArgumentParser, framework, args): 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, framework, args, python_api_used):
if 'help' in args and args['help']: if 'help' in args and args['help']:
show_mo_convert_help() show_mo_convert_help()
return None, None return None, None
@ -846,6 +861,7 @@ def _convert(cli_parser: argparse.ArgumentParser, framework, args):
# Initialize logger with 'ERROR' as default level to be able to form nice messages # Initialize logger with 'ERROR' as default level to be able to form nice messages
# before arg parser deliver log_level requested by user # before arg parser deliver log_level requested by user
init_logger('ERROR', False) init_logger('ERROR', False)
argv = None
try: try:
model_framework = None model_framework = None
inp_model_is_object = input_model_is_object(args) inp_model_is_object = input_model_is_object(args)
@ -869,6 +885,7 @@ def _convert(cli_parser: argparse.ArgumentParser, framework, args):
update_args_for_saved_model_dir(args) update_args_for_saved_model_dir(args)
argv = pack_params_to_args_namespace(args, cli_parser) argv = pack_params_to_args_namespace(args, cli_parser)
argv.is_python_api_used = python_api_used
argv.feManager = FrontEndManager() argv.feManager = FrontEndManager()
frameworks = list(set(['tf', 'caffe', 'mxnet', 'kaldi', 'onnx'] + (get_available_front_ends(argv.feManager) frameworks = list(set(['tf', 'caffe', 'mxnet', 'kaldi', 'onnx'] + (get_available_front_ends(argv.feManager)
@ -909,12 +926,54 @@ def _convert(cli_parser: argparse.ArgumentParser, framework, args):
for key, value in non_default_params.items(): for key, value in non_default_params.items():
ov_model.set_rt_info(str(value), ["conversion_parameters", str(key)]) ov_model.set_rt_info(str(value), ["conversion_parameters", str(key)])
telemetry.send_event('mo', 'conversion_result', 'success') if silent_is_false(argv) or not python_api_used:
telemetry.end_session('mo') if 'compress_to_fp16' in argv and argv.compress_to_fp16:
telemetry.force_shutdown(1.0) 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 return ov_model, argv
except Exception as e: except Exception as e:
telemetry.send_event('mo', 'conversion_result', 'fail') if silent_is_false(argv) or not python_api_used:
telemetry.end_session('mo') if isinstance(e, (FileNotFoundError, NotADirectoryError)):
telemetry.force_shutdown(1.0) log.error('File {} was not found'.format(str(e).split('No such file or directory:')[1]))
raise e.with_traceback(None) log.debug(traceback.format_exc())
elif isinstance(e, Error):
analysis_results = AnalysisResults()
if analysis_results.get_messages() is not None:
for el in analysis_results.get_messages():
log.error(el, extra={'analysis_info': True})
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 Optimizer 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

@ -2,7 +2,6 @@
# SPDX-License-Identifier: Apache-2.0 # SPDX-License-Identifier: Apache-2.0
import argparse import argparse
import logging as log
import os import os
import sys import sys
@ -10,69 +9,15 @@ try:
import openvino_telemetry as tm import openvino_telemetry as tm
except ImportError: except ImportError:
import openvino.tools.mo.utils.telemetry_stub as tm import openvino.tools.mo.utils.telemetry_stub as tm
from openvino.tools.mo.utils.get_ov_update_message import get_compression_message
from openvino.tools.mo.convert_impl import _convert from openvino.tools.mo.convert_impl import _convert
from openvino.tools.mo.pipeline.common import get_ir_version from openvino.tools.mo.pipeline.common import get_ir_version
from openvino.tools.mo.utils.logger import init_logger
from openvino.tools.mo.utils.error import Error, FrameworkError
import traceback
from openvino.tools.mo.utils.get_ov_update_message import get_ov_update_message, get_ov_api20_message, \
get_tf_fe_message
from openvino.tools.mo.utils.model_analysis import AnalysisResults
from openvino.tools.mo.utils.guess_framework import deduce_legacy_frontend_by_namespace
# pylint: disable=no-name-in-module,import-error # pylint: disable=no-name-in-module,import-error
from openvino.frontend import FrontEndManager
from openvino.runtime import serialize from openvino.runtime import serialize
def main(cli_parser: argparse.ArgumentParser, framework=None): def main(cli_parser: argparse.ArgumentParser, framework=None):
# Initialize logger with 'ERROR' as default level to be able to form nice messages ngraph_function, argv = _convert(cli_parser, framework, {}, False)
# before arg parser deliver log_level requested by user
init_logger('ERROR', False)
ngraph_function = None
argv = None
try:
ngraph_function, argv = _convert(cli_parser, framework, {})
is_tf, _, _, _, _ = deduce_legacy_frontend_by_namespace(argv)
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 ngraph_function is not None:
print(ov_api20_message)
is_fallback = getattr(argv, 'is_fallback', False)
if not argv.use_legacy_frontend and is_tf and not is_fallback:
# now TF FE is default frontend for TensorFlow models conversion
print(get_tf_fe_message())
except (FileNotFoundError, NotADirectoryError) as e:
log.error('File {} was not found'.format(str(e).split('No such file or directory:')[1]))
log.debug(traceback.format_exc())
except Error as err:
analysis_results = AnalysisResults()
if analysis_results.get_messages() is not None:
for el in analysis_results.get_messages():
log.error(el, extra={'analysis_info': True})
log.error(err)
log.debug(traceback.format_exc())
except FrameworkError as err:
log.error(err, extra={'framework_error': True})
log.debug(traceback.format_exc())
except Exception as err:
log.error("-------------------------------------------------")
log.error("----------------- INTERNAL ERROR ----------------")
log.error("Unexpected exception happened.")
log.error("Please contact Model Optimizer developers and forward the following information:")
log.error(str(err))
log.error(traceback.format_exc())
log.error("---------------- END OF BUG REPORT --------------")
log.error("-------------------------------------------------")
if ngraph_function is None: if ngraph_function is None:
return 1 return 1

View File

@ -138,7 +138,7 @@ def convert_pytorch_via_onnx(args, example_inputs, cli_parser, framework, main_c
args['input_model'] = model_onnx args['input_model'] = model_onnx
ov_model, argv = main_convert(cli_parser, framework, args) ov_model, argv = main_convert(cli_parser, framework, args, True)
except Exception as e: except Exception as e:
raise e raise e
finally: finally:

View File

@ -41,3 +41,8 @@ def get_compression_message():
'by removing argument --compress_to_fp16 or set it to false --compress_to_fp16=False.\n' \ '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) 'Find more information about compression to FP16 at {}'.format(link)
return message 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

@ -103,3 +103,11 @@ def get_tid():
This function returns the ID of the database to send telemetry. This function returns the ID of the database to send telemetry.
""" """
return telemetry_params['TID'] 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

@ -9,7 +9,8 @@ from contextlib import redirect_stdout
from unittest.mock import patch from unittest.mock import patch
from openvino.tools.mo.main import main from openvino.tools.mo.main import main
from openvino.tools.mo.utils.get_ov_update_message import get_tf_fe_message, get_compression_message from openvino.tools.mo.utils.get_ov_update_message import get_tf_fe_message, get_compression_message, \
get_try_legacy_fe_message
def arg_parse_helper(input_model, def arg_parse_helper(input_model,
@ -68,6 +69,22 @@ class TestInfoMessagesTFFE(unittest.TestCase):
tf_fe_message_found = get_tf_fe_message() in std_out tf_fe_message_found = get_tf_fe_message() in std_out
assert tf_fe_message_found 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): class TestInfoMessagesTFFEWithFallback(unittest.TestCase):
@patch('argparse.ArgumentParser.parse_args', @patch('argparse.ArgumentParser.parse_args',