Model Optimizer incorrectly convert per-channel Quantize/Dequantize operatros to FakeQuantize (#8321)
* add q_dq_resolver * prepare FQ for offline transfornations * fix quantize_dequantize_linear_resolver.py * fix tools/mo/openvino/tools/mo/middle/dequantize_linear_resolver.py * enable compress_quantized_weights.py * fix sub case * delete force_clean_up * add isolated attribute * add test for quantize_dequantize_linear_resolver * fix comments * apply comments * aplying comments * applying comments * applying comments * Replace q_dq_resolver using pattern * clean imports * fix imports * fix pattern * delete refernce to unit tests
This commit is contained in:
1 parent
9f343fa37f
commit
439e3b26aa
8 files changed
+289
-58
No files matched your search
@@ -788,6 +788,7 @@ openvino/tools/mo/middle/permute_tensor_iterator.py
|
||||
openvino/tools/mo/middle/PoolV2ToAttributedPool.py
|
||||
openvino/tools/mo/middle/preprocessing.py
|
||||
openvino/tools/mo/middle/PreserveRuntimeInfo.py
|
||||
openvino/tools/mo/middle/quantize_dequantize_linear_resolver.py
|
||||
openvino/tools/mo/middle/quantize_fuses.py
|
||||
openvino/tools/mo/middle/quantize_linear_resolver.py
|
||||
openvino/tools/mo/middle/ReluQuantizeFuse.py
|
||||
|
||||
@@ -232,6 +232,9 @@ class CompressQuantizeWeights(BackReplacementPattern):
|
||||
def replace_pattern(self, graph: Graph, match: Dict[str, Node]):
|
||||
fake_quantize = match['fake_quantize']
|
||||
|
||||
if fake_quantize.has_and_set('stop_compression'):
|
||||
return
|
||||
|
||||
if 'convert' in match:
|
||||
dst_type = match['convert'].dst_type
|
||||
match['convert']['stop_value_propagation'] = False
|
||||
|
||||
@@ -183,6 +183,10 @@ def float32_array(value: Union[Iterable[Union[float, int]], float, int]) -> np.n
|
||||
return np.array(value, dtype=np.float32)
|
||||
|
||||
|
||||
def int8_array(value: Union[Iterable[Union[float, int]], float, int]) -> np.ndarray:
|
||||
return np.array(value, dtype=np.int8)
|
||||
|
||||
|
||||
def float_array(value: Union[Iterable[Union[float, int]], float, int]) -> np.ndarray:
|
||||
return float32_array(value)
|
||||
|
||||
|
||||
@@ -42,4 +42,9 @@ class Sub(FrontReplacementPattern):
|
||||
|
||||
def find_and_replace_pattern(self, graph: Graph):
|
||||
for sub in graph.get_op_nodes(op='Sub'):
|
||||
|
||||
# The attribute zero_point_sub indicates that the node can be used in ConvertQuantizeDequantize
|
||||
# transformation (offline transformations). Pattern of such transformation expects Subtract node.
|
||||
if sub.has_and_set('zero_point_sub'):
|
||||
continue
|
||||
self.sub_to_add_replacement(sub)
|
||||
@@ -25,6 +25,10 @@ class DequantizeLinearResolver(MiddleReplacementPattern):
|
||||
enabled = True
|
||||
graph_condition = [lambda graph: graph.graph['layout'] == 'NCHW']
|
||||
|
||||
def run_after(self):
|
||||
from openvino.tools.mo.middle.quantize_dequantize_linear_resolver import QuantizeDequantizeLinearResolver
|
||||
return [QuantizeDequantizeLinearResolver]
|
||||
|
||||
def find_and_replace_pattern(self, graph: Graph):
|
||||
for dequantize_node in graph.get_op_nodes(op='DequantizeLinear'):
|
||||
node_name = dequantize_node.soft_get('name', dequantize_node.id)
|
||||
@@ -33,11 +37,13 @@ class DequantizeLinearResolver(MiddleReplacementPattern):
|
||||
model_data_type = data_type_str_to_np(graph.graph['cmd_params'].data_type)
|
||||
cast = Cast(graph, {'dst_type': model_data_type, 'name': node_name + '/Cast'}).create_node()
|
||||
dequantize_node.in_port(0).get_connection().set_destination(cast.in_port(0))
|
||||
mul = Mul(graph, {}).create_node()
|
||||
mul = Mul(graph, {'can_be_fused': False}).create_node()
|
||||
|
||||
is_second_port_connected = dequantize_node.is_in_port_connected(2)
|
||||
if is_second_port_connected:
|
||||
sub = Sub(graph, {'name': node_name + '/Sub'}).create_node()
|
||||
# its is necessary not to replace subrtract for pattern in offline transformations
|
||||
# See ConvertQuantizeDequantize transformation in ngraph
|
||||
sub = Sub(graph, {'name': node_name + '/Sub', 'zero_point_sub': True}).create_node()
|
||||
cast.out_port(0).connect(sub.in_port(0))
|
||||
dequantize_node.in_port(2).get_connection().set_destination(sub.in_port(1))
|
||||
sub.out_port(0).connect(mul.in_port(0))
|
||||
@@ -55,12 +61,12 @@ class DequantizeLinearResolver(MiddleReplacementPattern):
|
||||
target_shape[axis] = input_shape[axis]
|
||||
|
||||
mul_reshape = create_op_with_const_inputs(graph, Reshape, {1: int64_array(target_shape)},
|
||||
{'name': node_name + '/Reshape/Mul'})
|
||||
{'name': node_name + '/Reshape/Mul'})
|
||||
mul.in_port(1).get_connection().set_destination(mul_reshape.in_port(0))
|
||||
mul_reshape.out_port(0).connect(mul.in_port(1))
|
||||
|
||||
if is_second_port_connected:
|
||||
sub_reshape = create_op_with_const_inputs(graph, Reshape, {1: int64_array(target_shape)},
|
||||
{'name': node_name + '/Reshape/Sub'})
|
||||
{'name': node_name + '/Reshape/Sub'})
|
||||
sub.in_port(1).get_connection().set_destination(sub_reshape.in_port(0))
|
||||
sub_reshape.out_port(0).connect(sub.in_port(1))
|
||||
@@ -0,0 +1,62 @@
|
||||
# Copyright (C) 2018-2021 Intel Corporation
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import numpy as np
|
||||
|
||||
from openvino.tools.mo.graph.graph import Graph, rename_nodes
|
||||
from openvino.tools.mo.middle.quantize_linear_resolver import QuantizeLinearResolver
|
||||
from openvino.tools.mo.middle.replacement import MiddleReplacementPattern
|
||||
|
||||
|
||||
class QuantizeDequantizeLinearResolver(MiddleReplacementPattern):
|
||||
"""
|
||||
This transformation replaces QuantizeLinear in pair QuantizeLinear/DequantizeLinear with
|
||||
constant inputs to FakeQuantize with flag stop_value_propagation=True. This transformation prepare FakeQuantize for
|
||||
ConvertQuantizeDequantize in offline transformations.
|
||||
"""
|
||||
enabled = True
|
||||
graph_condition = [lambda graph: graph.graph['layout'] == 'NCHW']
|
||||
|
||||
def pattern(self):
|
||||
return dict(
|
||||
nodes=[('const_input', dict(kind='op', op='Const')),
|
||||
('const_input_d', dict(kind='data')),
|
||||
('quantize', dict(kind='op', op='QuantizeLinear')),
|
||||
('quantize_d', dict(kind='data')),
|
||||
('dequantize', dict(kind='op', op='DequantizeLinear')),
|
||||
],
|
||||
edges=[('const_input', 'const_input_d'),
|
||||
('const_input_d', 'quantize', {'in': 0}),
|
||||
('quantize', 'quantize_d'),
|
||||
('quantize_d', 'dequantize', {'in': 0})
|
||||
]
|
||||
)
|
||||
|
||||
def run_after(self):
|
||||
from openvino.tools.mo.middle.quantize_fuses import MarkNodesToFuseUpToFakeQuantize
|
||||
return [MarkNodesToFuseUpToFakeQuantize]
|
||||
|
||||
def replace_pattern(self, graph: Graph, match: dict):
|
||||
dequantize_node = match['dequantize']
|
||||
quantize_node = match['quantize']
|
||||
|
||||
scale_zerop_is_exist = quantize_node.is_in_port_connected(1) and quantize_node.is_in_port_connected(2) and \
|
||||
dequantize_node.is_in_port_connected(1) and dequantize_node.is_in_port_connected(2)
|
||||
if not scale_zerop_is_exist:
|
||||
return
|
||||
q_scale = quantize_node.in_port(1).get_source().node
|
||||
q_zerop = quantize_node.in_port(2).get_source().node
|
||||
dq_scale = dequantize_node.in_port(1).get_source().node
|
||||
dq_zerop = dequantize_node.in_port(2).get_source().node
|
||||
scales_and_zerop_is_const = q_scale.soft_get('type') == 'Const' and dq_scale.soft_get('type') == 'Const' and \
|
||||
q_zerop.soft_get('type') == 'Const' and dq_zerop.soft_get('type') == 'Const'
|
||||
scales_and_zerop_equals = np.array_equal(q_scale.value, dq_scale.value) and \
|
||||
np.array_equal(q_zerop.value, dq_zerop.value)
|
||||
|
||||
# only constant as for zero_point/scale supported
|
||||
# only patterns with same scale/zero_point values for Q and DQ are supported
|
||||
if not (scales_and_zerop_is_const or scales_and_zerop_equals):
|
||||
return
|
||||
|
||||
QuantizeLinearResolver.quantize_to_fakequantize(graph, quantize_node, True)
|
||||
quantize_node['isolated'] = True
|
||||
@@ -9,7 +9,7 @@ from openvino.tools.mo.ops.fakequantize import FakeQuantize
|
||||
from openvino.tools.mo.front.common.partial_infer.utils import float_array, int64_array
|
||||
from openvino.tools.mo.front.common.partial_infer.utils import mo_array
|
||||
from openvino.tools.mo.front.tf.graph_utils import create_op_with_const_inputs
|
||||
from openvino.tools.mo.graph.graph import Graph, rename_nodes
|
||||
from openvino.tools.mo.graph.graph import Graph, rename_nodes, Node
|
||||
from openvino.tools.mo.middle.replacement import MiddleReplacementPattern
|
||||
from openvino.tools.mo.ops.const import Const
|
||||
from openvino.tools.mo.ops.reshape import Reshape
|
||||
@@ -35,68 +35,79 @@ class QuantizeLinearResolver(MiddleReplacementPattern):
|
||||
graph_condition = [lambda graph: graph.graph['layout'] == 'NCHW']
|
||||
|
||||
def run_after(self):
|
||||
from openvino.tools.mo.middle.quantize_fuses import MarkNodesToFuseUpToFakeQuantize
|
||||
return [MarkNodesToFuseUpToFakeQuantize]
|
||||
from openvino.tools.mo.middle.quantize_dequantize_linear_resolver import QuantizeDequantizeLinearResolver
|
||||
return [QuantizeDequantizeLinearResolver]
|
||||
|
||||
def find_and_replace_pattern(self, graph: Graph):
|
||||
for quantize_node in graph.get_op_nodes(op='QuantizeLinear'):
|
||||
node_name = quantize_node.soft_get('name', quantize_node.id)
|
||||
axis = quantize_node.soft_get('axis', None)
|
||||
scale_y_shape = quantize_node.in_port(1).data.get_shape()
|
||||
if quantize_node.has_and_set('isolated'): # node is detached and will be eliminated
|
||||
# during the next clean up
|
||||
continue
|
||||
QuantizeLinearResolver.quantize_to_fakequantize(graph, quantize_node)
|
||||
|
||||
if quantize_node.is_in_port_connected(2):
|
||||
zerop = quantize_node.in_port(2).get_source().node
|
||||
else:
|
||||
zerop = Const(graph,
|
||||
{'value': mo_array(0, dtype=np.uint8), 'name': node_name + '/ZeroPoint'}).create_node()
|
||||
@staticmethod
|
||||
def quantize_to_fakequantize(graph: Graph, quantize_node: Node, set_stop_value_propagation=False):
|
||||
node_name = quantize_node.soft_get('name', quantize_node.id)
|
||||
axis = quantize_node.soft_get('axis', None)
|
||||
scale_y_shape = quantize_node.in_port(1).data.get_shape()
|
||||
|
||||
assert zerop.soft_get('type') == 'Const', 'only constant for zero_point is supported for QuantizeLinear'
|
||||
zero_point_type = zerop.value.dtype
|
||||
# data type affects range of output values: [-128..127] or [0..255]
|
||||
if zero_point_type == np.int8:
|
||||
output_low_value = -128.0
|
||||
output_high_value = 127.0
|
||||
elif zero_point_type == np.uint8:
|
||||
output_low_value = 0.0
|
||||
output_high_value = 255.0
|
||||
else:
|
||||
raise Error('Not expected type {} for zero point value in node {}'.format(
|
||||
zero_point_type, zerop.soft_get('name')))
|
||||
if quantize_node.is_in_port_connected(2):
|
||||
zerop = quantize_node.in_port(2).get_source().node
|
||||
else:
|
||||
zerop = Const(graph,
|
||||
{'value': mo_array(0, dtype=np.uint8), 'name': node_name + '/ZeroPoint'}).create_node()
|
||||
|
||||
fake_quantize = create_op_with_const_inputs(graph, FakeQuantize, {3: float_array(output_low_value),
|
||||
4: float_array(output_high_value)},
|
||||
{'levels': 256, 'name': node_name + '/FakeQuantize'})
|
||||
quantize_node.in_port(0).get_connection().set_destination(fake_quantize.in_port(0))
|
||||
assert zerop.soft_get('type') == 'Const', 'only constant for zero_point is supported for QuantizeLinear'
|
||||
zero_point_type = zerop.value.dtype
|
||||
# data type affects range of output values: [-128..127] or [0..255]
|
||||
if zero_point_type == np.int8:
|
||||
output_low_value = -128.0
|
||||
output_high_value = 127.0
|
||||
elif zero_point_type == np.uint8:
|
||||
output_low_value = 0.0
|
||||
output_high_value = 255.0
|
||||
else:
|
||||
raise Error('Not expected type {} for zero point value in node {}'.format(
|
||||
zero_point_type, zerop.soft_get('name')))
|
||||
|
||||
# Calculate input_low value
|
||||
mul_low = create_op_with_const_inputs(graph, Mul, {1: float_array(output_low_value - zerop.value)},
|
||||
{'name': node_name + '/Mul/Low'})
|
||||
quantize_node.in_port(1).get_connection().set_destination(mul_low.in_port(0))
|
||||
mul_low.out_port(0).connect(fake_quantize.in_port(1))
|
||||
fake_quantize = create_op_with_const_inputs(graph, FakeQuantize, {3: float_array(output_low_value),
|
||||
4: float_array(output_high_value)},
|
||||
{'levels': 256, 'name': node_name + '/FakeQuantize'})
|
||||
if set_stop_value_propagation:
|
||||
fake_quantize['stop_compression'] = True
|
||||
fake_quantize['stop_value_propagation'] = True
|
||||
quantize_node.in_port(0).get_connection().set_destination(fake_quantize.in_port(0))
|
||||
|
||||
# Calculate input_high value
|
||||
mul_high = create_op_with_const_inputs(graph, Mul, {1: float_array(output_high_value - zerop.value)},
|
||||
{'name': node_name + '/Mul/High'})
|
||||
mul_low.in_port(0).get_connection().add_destination(mul_high.in_port(0))
|
||||
mul_high.out_port(0).connect(fake_quantize.in_port(2))
|
||||
# Calculate input_low value
|
||||
mul_low = create_op_with_const_inputs(graph, Mul, {1: float_array(output_low_value - zerop.value)},
|
||||
{'name': node_name + '/Mul/Low'})
|
||||
quantize_node.in_port(1).get_connection().set_destination(mul_low.in_port(0))
|
||||
mul_low.out_port(0).connect(fake_quantize.in_port(1))
|
||||
|
||||
cast = Cast(graph, {'dst_type': zero_point_type, 'name': node_name + '/Cast'}).create_node()
|
||||
fake_quantize.out_port(0).connect(cast.in_port(0))
|
||||
quantize_node.out_port(0).get_connection().set_source(cast.out_port(0))
|
||||
rename_nodes([(quantize_node, node_name + '/TBD'), (cast, node_name)])
|
||||
# Calculate input_high value
|
||||
mul_high = create_op_with_const_inputs(graph, Mul, {1: float_array(output_high_value - zerop.value)},
|
||||
{'name': node_name + '/Mul/High'})
|
||||
mul_low.in_port(0).get_connection().add_destination(mul_high.in_port(0))
|
||||
mul_high.out_port(0).connect(fake_quantize.in_port(2))
|
||||
|
||||
assert scale_y_shape is not None
|
||||
if axis is not None and len(scale_y_shape) > 0 and scale_y_shape[0] > 1:
|
||||
input_shape = fake_quantize.in_port(0).data.get_shape()
|
||||
target_shape = np.ones(len(input_shape), np.int)
|
||||
target_shape[axis] = input_shape[axis]
|
||||
mul_low_reshape = create_op_with_const_inputs(graph, Reshape, {1: int64_array(target_shape)},
|
||||
{'name': node_name + '/Reshape/Mul/Low'})
|
||||
mul_high_reshape = create_op_with_const_inputs(graph, Reshape, {1: int64_array(target_shape)},
|
||||
{'name': node_name + '/Reshape/Mul/high'})
|
||||
cast = Cast(graph, {'dst_type': zero_point_type, 'name': node_name + '/Cast'}).create_node()
|
||||
fake_quantize.out_port(0).connect(cast.in_port(0))
|
||||
quantize_node.out_port(0).get_connection().set_source(cast.out_port(0))
|
||||
rename_nodes([(quantize_node, node_name + '/TBD'), (cast, node_name)])
|
||||
|
||||
fake_quantize.in_port(1).get_connection().set_destination(mul_low_reshape.in_port(0))
|
||||
fake_quantize.in_port(2).get_connection().set_destination(mul_high_reshape.in_port(0))
|
||||
assert scale_y_shape is not None, "{0} contains scale(input with port 1) with shape None".\
|
||||
format(quantize_node.soft_get('name', soft_get('id')))
|
||||
if axis is not None and len(scale_y_shape) > 0 and scale_y_shape[0] > 1:
|
||||
input_shape = fake_quantize.in_port(0).data.get_shape()
|
||||
target_shape = np.ones(len(input_shape), np.int)
|
||||
target_shape[axis] = input_shape[axis]
|
||||
mul_low_reshape = create_op_with_const_inputs(graph, Reshape, {1: int64_array(target_shape)},
|
||||
{'name': node_name + '/Reshape/Mul/Low'})
|
||||
mul_high_reshape = create_op_with_const_inputs(graph, Reshape, {1: int64_array(target_shape)},
|
||||
{'name': node_name + '/Reshape/Mul/high'})
|
||||
|
||||
mul_low_reshape.out_port(0).connect(fake_quantize.in_port(1))
|
||||
mul_high_reshape.out_port(0).connect(fake_quantize.in_port(2))
|
||||
fake_quantize.in_port(1).get_connection().set_destination(mul_low_reshape.in_port(0))
|
||||
fake_quantize.in_port(2).get_connection().set_destination(mul_high_reshape.in_port(0))
|
||||
|
||||
mul_low_reshape.out_port(0).connect(fake_quantize.in_port(1))
|
||||
mul_high_reshape.out_port(0).connect(fake_quantize.in_port(2))
|
||||
@@ -0,0 +1,139 @@
|
||||
# Copyright (C) 2018-2021 Intel Corporation
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import numpy as np
|
||||
import unittest
|
||||
|
||||
from openvino.tools.mo.front.common.partial_infer.utils import int64_array, float32_array, int8_array
|
||||
from openvino.tools.mo.middle.quantize_dequantize_linear_resolver import QuantizeDequantizeLinearResolver
|
||||
from openvino.tools.mo.middle.quantize_linear_resolver import QuantizeLinearResolver
|
||||
from openvino.tools.mo.utils.ir_engine.compare_graphs import compare_graphs
|
||||
from unit_tests.utils.graph import build_graph, regular_op_with_shaped_data, result, connect, connect_data, \
|
||||
valued_const_with_data, regular_op_with_empty_data
|
||||
|
||||
nodes_attributes = {
|
||||
**valued_const_with_data('y_scale_1', float32_array(1.0 / 255.0)),
|
||||
**valued_const_with_data('y_scale_2', float32_array(1.0 / 255.0)),
|
||||
**valued_const_with_data('y_zeropoint_1', int8_array(0)),
|
||||
**valued_const_with_data('y_zeropoint_2', int8_array(0)),
|
||||
**valued_const_with_data('x_scale_1', float32_array(1.0 / 255.0)),
|
||||
**valued_const_with_data('x_scale_2', float32_array(1.0 / 255.0)),
|
||||
**valued_const_with_data('x_zeropoint_1', int8_array(0)),
|
||||
**valued_const_with_data('x_zeropoint_2', int8_array(0)),
|
||||
**valued_const_with_data('const_input', float32_array([[0.3, 0.6], [-0.7, -0.9]])),
|
||||
**valued_const_with_data('in_low', float32_array(-128.0)),
|
||||
**valued_const_with_data('in_high', float32_array(127.0)),
|
||||
**valued_const_with_data('out_low', float32_array(-128.0)),
|
||||
**valued_const_with_data('out_high', float32_array(127.0)),
|
||||
**valued_const_with_data('non_const_in_low', float32_array(-128.0)),
|
||||
**valued_const_with_data('non_const_in_high', float32_array(127.0)),
|
||||
**valued_const_with_data('non_const_out_low', float32_array(-128.0)),
|
||||
**valued_const_with_data('non_const_out_high', float32_array(127.0)),
|
||||
**regular_op_with_shaped_data('input', [1, 2, 2], {'op': 'Parameter', 'type': 'Parameter'}),
|
||||
**regular_op_with_empty_data('const_quantize', {'op': 'QuantizeLinear'}),
|
||||
**regular_op_with_empty_data('non_const_quantize', {'op': 'QuantizeLinear'}),
|
||||
**regular_op_with_empty_data('const_dequantize', {'op': 'DequantizeLinear'}),
|
||||
**regular_op_with_empty_data('non_const_dequantize', {'op': 'DequantizeLinear'}),
|
||||
**regular_op_with_empty_data('add', {'op': 'Add'}),
|
||||
**regular_op_with_empty_data('mul_low', {'op': 'Mul'}),
|
||||
**regular_op_with_empty_data('mul_high', {'op': 'Mul'}),
|
||||
**regular_op_with_empty_data('const_fq', {'op': 'FakeQuantize'}),
|
||||
**regular_op_with_empty_data('const_cast', {'op': 'Cast', 'type': 'Convert', 'dst_type': np.uint8}),
|
||||
**regular_op_with_empty_data('non_const_mul_low', {'op': 'Mul'}),
|
||||
**regular_op_with_empty_data('non_const_mul_high', {'op': 'Mul'}),
|
||||
**regular_op_with_empty_data('non_const_fq', {'op': 'FakeQuantize'}),
|
||||
**regular_op_with_empty_data('non_const_cast', {'op': 'Cast', 'type': 'Convert', 'dst_type': np.uint8}),
|
||||
**result('result'),
|
||||
|
||||
}
|
||||
|
||||
|
||||
class QuantizeDequantizeLinearResolverTest(unittest.TestCase):
|
||||
def test_quantize_dequantize_linear_resolver(self):
|
||||
graph = build_graph(nodes_attrs=nodes_attributes,
|
||||
edges=[
|
||||
*connect('input', '0:non_const_quantize'),
|
||||
*connect('y_scale_2', '1:non_const_quantize'),
|
||||
*connect('y_zeropoint_2', '2:non_const_quantize'),
|
||||
*connect('non_const_quantize', '0:non_const_dequantize'),
|
||||
*connect('x_scale_2', '1:non_const_dequantize'),
|
||||
*connect('x_zeropoint_2', '2:non_const_dequantize'),
|
||||
|
||||
*connect('const_input', '0:const_quantize'),
|
||||
*connect('y_scale_1', '1:const_quantize'),
|
||||
*connect('y_zeropoint_1', '2:const_quantize'),
|
||||
*connect('const_quantize', '0:const_dequantize'),
|
||||
*connect('x_scale_1', '1:const_dequantize'),
|
||||
*connect('x_zeropoint_1', '2:const_dequantize'),
|
||||
*connect('const_dequantize', '0:add'),
|
||||
*connect('non_const_dequantize', '1:add'),
|
||||
*connect('add', 'result')
|
||||
], nodes_with_edges_only=True)
|
||||
|
||||
const_ref_graph = build_graph(nodes_attrs=nodes_attributes,
|
||||
edges=[
|
||||
*connect('input', '0:non_const_quantize'),
|
||||
*connect('y_scale_2', '1:non_const_quantize'),
|
||||
*connect('y_zeropoint_2', '2:non_const_quantize'),
|
||||
*connect('non_const_quantize', '0:non_const_dequantize'),
|
||||
*connect('x_scale_2', '1:non_const_dequantize'),
|
||||
*connect('x_zeropoint_2', '2:non_const_dequantize'),
|
||||
|
||||
*connect('const_input', '0:const_fq'),
|
||||
*connect('y_scale_1:0', '0:mul_low'),
|
||||
*connect('in_low', '1:mul_low'),
|
||||
('y_scale_1_d', 'mul_high', {'out': 1, 'in': 0}),
|
||||
*connect('in_high', '1:mul_high'),
|
||||
*connect('mul_low', '1:const_fq'),
|
||||
*connect('mul_high', '2:const_fq'),
|
||||
*connect('out_low', '3:const_fq'),
|
||||
*connect('out_high', '4:const_fq'),
|
||||
*connect('const_fq', 'const_cast'),
|
||||
*connect('const_cast', '0:const_dequantize'),
|
||||
*connect('x_scale_1', '1:const_dequantize'),
|
||||
*connect('x_zeropoint_1', '2:const_dequantize'),
|
||||
*connect('const_dequantize', '0:add'),
|
||||
*connect('non_const_dequantize', '1:add'),
|
||||
*connect('add', 'result')
|
||||
],nodes_with_edges_only=True)
|
||||
QuantizeDequantizeLinearResolver().find_and_replace_pattern(graph)
|
||||
graph.graph['layout'] = 'NCHW'
|
||||
(flag, resp) = compare_graphs(graph, const_ref_graph, 'result')
|
||||
self.assertTrue(flag, resp)
|
||||
|
||||
ref_graph = build_graph(nodes_attrs=nodes_attributes,
|
||||
edges=[
|
||||
*connect('input', '0:non_const_fq'),
|
||||
*connect('y_scale_2:0', '0:non_const_mul_low'),
|
||||
*connect('non_const_in_low', '1:non_const_mul_low'),
|
||||
('y_scale_2_d', 'non_const_mul_high', {'out': 1, 'in': 0}),
|
||||
*connect('non_const_in_high', '1:non_const_mul_high'),
|
||||
*connect('non_const_mul_low', '1:non_const_fq'),
|
||||
*connect('non_const_mul_high', '2:non_const_fq'),
|
||||
*connect('non_const_out_low', '3:non_const_fq'),
|
||||
*connect('non_const_out_high', '4:non_const_fq'),
|
||||
*connect('non_const_fq', 'non_const_cast'),
|
||||
*connect('non_const_cast', '0:non_const_dequantize'),
|
||||
*connect('x_scale_2', '1:non_const_dequantize'),
|
||||
*connect('x_zeropoint_2', '2:non_const_dequantize'),
|
||||
|
||||
*connect('const_input', '0:const_fq'),
|
||||
*connect('y_scale_1:0', '0:mul_low'),
|
||||
*connect('in_low', '1:mul_low'),
|
||||
('y_scale_1_d', 'mul_high', {'out': 1, 'in': 0}),
|
||||
*connect('in_high', '1:mul_high'),
|
||||
*connect('mul_low', '1:const_fq'),
|
||||
*connect('mul_high', '2:const_fq'),
|
||||
*connect('out_low', '3:const_fq'),
|
||||
*connect('out_high', '4:const_fq'),
|
||||
*connect('const_fq', 'const_cast'),
|
||||
*connect('const_cast', '0:const_dequantize'),
|
||||
*connect('x_scale_1', '1:const_dequantize'),
|
||||
*connect('x_zeropoint_1', '2:const_dequantize'),
|
||||
*connect('const_dequantize', '0:add'),
|
||||
*connect('non_const_dequantize', '1:add'),
|
||||
*connect('add', 'result')
|
||||
], nodes_with_edges_only=True)
|
||||
QuantizeLinearResolver().find_and_replace_pattern(graph)
|
||||
(flag, resp) = compare_graphs(graph, ref_graph, 'result')
|
||||
self.assertTrue(flag, resp)
|
||||
Reference in new issue
Block a user