[MO] disabling inserting redundant Converts for int in legacy IR serialization (#17572)

* disabling inserting redundant Converts for integer types for legacy IR serialization

* Revert "disabling inserting redundant Converts for integer types for legacy IR serialization"

This reverts commit ddd96e2034.

* more selective skip; added unit-tests

* corrected unit-tests

* fix unit-tests: replace dynamic Parameter input with Const

* fix unit-tests: implemented reading with IR frontend

* sort imports

* final revision of unit-tests

---------

Co-authored-by: Evgenya Stepyreva <evgenya.stepyreva@intel.com>
This commit is contained in:
Pavel Esir 2023-05-30 09:51:56 +02:00 committed by GitHub
parent a1a753bb03
commit 84f6deb757
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 341 additions and 8 deletions

View File

@ -169,16 +169,23 @@ def convert_inputs_of_specific_ops(graph: Graph):
''.format(port_id, node.soft_get('name', node.id), precision))
in_port = node.in_port(port_id)
np_type = data_type_str_to_np(precision)
if in_port.get_source().node.type == 'Const':
const_node = node.in_port(port_id).get_source().node
const_type = const_node.out_port(0).get_data_type()
if np.issubdtype(const_type, np.integer) and np.issubdtype(np_type, np.integer):
in_node = node.in_port(port_id).get_source().node
in_type = in_node.out_port(0).get_data_type()
if in_node.type == 'Const':
if np.issubdtype(in_type, np.integer) and np.issubdtype(np_type, np.integer):
# do not convert Constant value if both source and destination types are of integer types
# otherwise, it affects compatibility of MO IR Engine and TF FE
# TF FE intents to use original model type for layers if it is possible
continue
convert_const_node_value_type(const_node, np_type)
convert_const_node_value_type(in_node, np_type)
else:
allowed_int_types = [np.int32, np.int64, np.uint32, np.uint64]
if in_type in allowed_int_types and np_type in allowed_int_types:
# do not convert if both source and destination types are within the set of
# int32/int64/uint32/uint64. It prevents from getting different IRs from the original
# cpp serializer and from the legacy serialized when restored with ir_reader_utils
continue
in_port.get_connection().insert_node(Cast(graph, {'dst_type': np_type}).create_node())

View File

@ -90,8 +90,8 @@ def _update(cls, registered_list: list, registered_dict: dict, key: str, enabled
if hasattr(c, key) and getattr(c, key) is not None:
k = getattr(c, key)
if k.lower() in new_keys_lower:
log.warning('Attempt to register of custom name {} for the second time as class {}. '
'Note that custom names are case-insensitive. ' + refer_to_faq_msg(55), k, c)
# log.warning('Attempt to register of custom name {} for the second time as class {}. '
# 'Note that custom names are case-insensitive. ' + refer_to_faq_msg(55), k, c)
continue
else:
new_keys_lower[k.lower()] = k

View File

@ -4,8 +4,17 @@
import os
import tempfile
import unittest
import numpy as np
from defusedxml.common import EntitiesForbidden
from openvino.tools.mo.utils.ir_reader.restore_graph import restore_graph_from_ir
import openvino.tools.mo.utils.ir_reader.extenders.convert_extender
from openvino.tools.mo.middle.passes.convert_data_type import destination_type_to_np_data_type
from openvino.tools.mo.middle.passes.infer import type_infer
from openvino.tools.mo.utils.graph import Node
from openvino.tools.mo.utils.ir_engine.compare_graphs import compare_graphs
from openvino.tools.mo.utils.ir_reader.extender import Extender
from openvino.tools.mo.utils.ir_reader.restore_graph import restore_graph_from_ir, save_restored_graph
class TestIRReader(unittest.TestCase):
@ -127,3 +136,320 @@ class TestIRReader(unittest.TestCase):
malformed_ir_file.close()
self.assertRaises(ValueError, restore_graph_from_ir, malformed_ir_file.name)
os.remove(malformed_ir_file.name)
class PatchedConvert_extender(Extender):
"""
Original ConvertExtender contains setting 'stop_value_propagation', and because axis value goes to the Gather
through Convert during shape_infer axis turns out to be None and shape_infer fails.
For purposes of this unit-test we patch extender so that it will not add 'stop_value_propagation' attr.
Outside the unit-test Convert_extender is left unchanged because inserting 'stop_value_propagation'
is needed in other cases for CompressQuantizeWeights.
See description of openvino/tools/mo/utils/ir_reader/extenders/convert_extender.py
"""
op = 'Convert'
@staticmethod
def extend(op: Node):
op['dst_type'] = destination_type_to_np_data_type(op.destination_type)
class TestIRSerializeAndRestore(unittest.TestCase):
test_ir_xml = """<?xml version="1.0"?>
<net name="test_ir" version="11">
<layers>
<layer id="0" name="input_1" type="Parameter" version="opset1">
<data shape="1,128" element_type="f32" />
<output>
<port id="0" precision="FP32" names="input_1">
<dim>1</dim>
<dim>128</dim>
</port>
</output>
</layer>
<layer id="1" name="input_2" type="Parameter" version="opset1">
<data shape="10" element_type="i32" />
<output>
<port id="0" precision="I32" names="input_2">
<dim>4</dim>
</port>
</output>
</layer>
<layer id="3" name="gather_axis" type="Const" version="opset1">
<data element_type="i32" shape="1" offset="0" size="4" />
<output>
<port id="0" precision="I32">
<dim>1</dim>
</port>
</output>
</layer>
<layer id="501" name="gather" type="Gather" version="opset8">
<data batch_dims="0" />
<input>
<port id="0" precision="FP32">
<dim>1</dim>
<dim>128</dim>
</port>
<port id="1" precision="I32">
<dim>10</dim>
</port>
<port id="2" precision="I32">
<dim>1</dim>
</port>
</input>
<output>
<port id="3" precision="FP32" names="gather">
<dim>1</dim>
<dim>10</dim>
</port>
</output>
</layer>
<layer id="590" name="result" type="Result" version="opset1">
<input>
<port id="0" precision="FP32">
<dim>1</dim>
<dim>10</dim>
</port>
</input>
</layer>
</layers>
<edges>
<edge from-layer="0" from-port="0" to-layer="501" to-port="0" />
<edge from-layer="1" from-port="0" to-layer="501" to-port="1" />
<edge from-layer="3" from-port="0" to-layer="501" to-port="2" />
<edge from-layer="501" from-port="3" to-layer="590" to-port="0" />
</edges>
</net>
"""
def test_save_and_restore(self):
original_xml_file = tempfile.NamedTemporaryFile(delete=False)
original_xml_file.write(bytes(self.test_ir_xml, 'utf-8'))
original_xml_file.close()
axis_const_blob = np.array([1], dtype=np.int32)
original_bin_file = tempfile.NamedTemporaryFile(mode='wb', delete=False)
axis_const_blob.tofile(original_bin_file)
original_bin_file.close()
graph_orig, _ = restore_graph_from_ir(original_xml_file.name, original_bin_file.name)
type_infer(graph_orig)
os.remove(original_xml_file.name)
os.remove(original_bin_file.name)
restored_ir_dir = tempfile.TemporaryDirectory()
save_restored_graph(graph_orig.copy(), restored_ir_dir.name, {})
restored_xml_name = restored_ir_dir.name + '/test_ir.xml'
restored_bin_name = restored_ir_dir.name + '/test_ir.bin'
# Gather is listed in convert_inputs_of_specific_ops as 'Gather': {2: 'int64'}, but
# no additional converts will be inserted, because input is int32
graph_restored, _ = restore_graph_from_ir(restored_xml_name, restored_bin_name)
os.remove(restored_xml_name)
os.remove(restored_bin_name)
os.remove(restored_xml_name.replace('xml', 'mapping'))
os.removedirs(restored_ir_dir.name)
flag, msg = compare_graphs(graph_orig, graph_restored, 'result', 'gather/sink_port_0')
self.assertTrue(flag, msg)
test_ir_xml_with_i8 = """<?xml version="1.0"?>
<net name="test_ir" version="11">
<layers>
<layer id="0" name="input_1" type="Parameter" version="opset1">
<data shape="1,128" element_type="f32" />
<output>
<port id="0" precision="FP32" names="input_1">
<dim>1</dim>
<dim>128</dim>
</port>
</output>
</layer>
<layer id="1" name="input_2" type="Parameter" version="opset1">
<data shape="10" element_type="i32" />
<output>
<port id="0" precision="I32" names="input_2">
<dim>10</dim>
</port>
</output>
</layer>
<layer id="3" name="gather_axis" type="Const" version="opset1">
<data element_type="i8" shape="1" offset="0" size="1" />
<output>
<port id="0" precision="I8">
<dim>1</dim>
</port>
</output>
</layer>
<layer id="501" name="gather" type="Gather" version="opset8">
<data batch_dims="0" />
<input>
<port id="0" precision="FP32">
<dim>1</dim>
<dim>128</dim>
</port>
<port id="1" precision="I32">
<dim>10</dim>
</port>
<port id="2" precision="I32">
<dim>1</dim>
</port>
</input>
<output>
<port id="3" precision="FP32" names="gather">
<dim>1</dim>
<dim>10</dim>
</port>
</output>
</layer>
<layer id="590" name="result" type="Result" version="opset1">
<input>
<port id="0" precision="FP32">
<dim>1</dim>
<dim>10</dim>
</port>
</input>
</layer>
</layers>
<edges>
<edge from-layer="0" from-port="0" to-layer="501" to-port="0" />
<edge from-layer="1" from-port="0" to-layer="501" to-port="1" />
<edge from-layer="3" from-port="0" to-layer="501" to-port="2" />
<edge from-layer="501" from-port="3" to-layer="590" to-port="0" />
</edges>
</net>
"""
test_ir_xml_with_convert = """<?xml version="1.0"?>
<net name="test_ir" version="11">
<layers>
<layer id="0" name="input_1" type="Parameter" version="opset1">
<data shape="1,128" element_type="f32" />
<output>
<port id="0" precision="FP32" names="input_1">
<dim>1</dim>
<dim>128</dim>
</port>
</output>
</layer>
<layer id="1" name="input_2" type="Parameter" version="opset1">
<data shape="10" element_type="i32" />
<output>
<port id="0" precision="I32" names="input_2">
<dim>10</dim>
</port>
</output>
</layer>
<layer id="3" name="gather_axis" type="Const" version="opset1">
<data element_type="i8" shape="1" offset="0" size="1" />
<output>
<port id="0" precision="I8">
<dim>1</dim>
</port>
</output>
</layer>
<layer id="583" name="convert" type="Convert" version="opset1">
<data destination_type="i64" />
<input>
<port id="0" precision="I8">
<dim>1</dim>
</port>
</input>
<output>
<port id="1" precision="I64">
<dim>1</dim>
</port>
</output>
</layer>
<layer id="501" name="gather" type="Gather" version="opset8">
<data batch_dims="0" />
<input>
<port id="0" precision="FP32">
<dim>1</dim>
<dim>128</dim>
</port>
<port id="1" precision="I32">
<dim>10</dim>
</port>
<port id="2" precision="I32">
<dim>1</dim>
</port>
</input>
<output>
<port id="3" precision="FP32" names="gather">
<dim>1</dim>
<dim>10</dim>
</port>
</output>
</layer>
<layer id="590" name="result" type="Result" version="opset1">
<input>
<port id="0" precision="FP32">
<dim>1</dim>
<dim>10</dim>
</port>
</input>
</layer>
</layers>
<edges>
<edge from-layer="0" from-port="0" to-layer="501" to-port="0" />
<edge from-layer="1" from-port="0" to-layer="501" to-port="1" />
<edge from-layer="3" from-port="0" to-layer="583" to-port="0" />
<edge from-layer="583" from-port="1" to-layer="501" to-port="2" />
<edge from-layer="501" from-port="3" to-layer="590" to-port="0" />
</edges>
</net>
"""
def test_save_and_restore_with_converts(self):
original_xml_file = tempfile.NamedTemporaryFile(delete=False)
original_xml_file.write(bytes(self.test_ir_xml_with_i8, 'utf-8'))
original_xml_file.close()
gather_axis_blob = np.array([1], dtype=np.int8)
original_bin_file = tempfile.NamedTemporaryFile(mode='wb', delete=False)
gather_axis_blob.tofile(original_bin_file)
original_bin_file.close()
graph_orig, _ = restore_graph_from_ir(original_xml_file.name, original_bin_file.name)
type_infer(graph_orig)
os.remove(original_xml_file.name)
restored_ir_dir = tempfile.TemporaryDirectory()
save_restored_graph(graph_orig.copy(), restored_ir_dir.name, {})
ir_file_with_convert = tempfile.NamedTemporaryFile(delete=False)
ir_file_with_convert.write(bytes(self.test_ir_xml_with_convert, 'utf-8'))
ir_file_with_convert.close()
from openvino.tools.mo.utils.ir_reader.extender import Extender
if 'Convert' in Extender.registered_ops:
Extender.registered_ops['Convert'] = PatchedConvert_extender
graph_with_convert, _ = restore_graph_from_ir(ir_file_with_convert.name, original_bin_file.name)
type_infer(graph_with_convert)
os.remove(ir_file_with_convert.name)
if 'Convert' in Extender.registered_ops:
Extender.registered_ops['Convert'] = openvino.tools.mo.utils.ir_reader.extenders.convert_extender.Convert_extender
restored_xml_file = restored_ir_dir.name + '/test_ir.xml'
restored_bin_file = restored_ir_dir.name + '/test_ir.bin'
# Gather is listed in convert_inputs_of_specific_ops as 'Gather': {2: 'int64'},
# converts from int8 to int64 will be inserted
graph_restored, _ = restore_graph_from_ir(restored_xml_file, restored_bin_file)
os.remove(original_bin_file.name)
os.remove(restored_xml_file)
os.remove(restored_bin_file)
os.remove(restored_xml_file.replace('xml', 'mapping'))
os.removedirs(restored_ir_dir.name)
flag, msg = compare_graphs(graph_orig, graph_restored, 'result', 'gather/sink_port_0')
self.assertTrue(flag, msg)