Add ONNX DequantizeLinear to MO (#1250)

* Add ONNX DequantizeLinear to MO

* Update docs
This commit is contained in:
Maxim Vafin 2020-07-24 18:39:09 +03:00 committed by GitHub
parent db176dfc5d
commit 663be787d6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 227 additions and 1 deletions

View File

@ -306,7 +306,7 @@ Standard ONNX\* operators:
| Cosh | No |
| Crop | No |
| CumSum | No |
| DequantizeLinear | Only in combination with QuantizeLinear, refer to the desc of the latter |
| DequantizeLinear | No |
| DetectionOutput (Intel experimental) | No |
| Div | No |
| Dropout | Not needed for inference |

View File

@ -242,6 +242,8 @@ extensions/front/onnx/crop_ext.py
extensions/front/onnx/cumsum_ext.py
extensions/front/onnx/deformable_conv_ext.py
extensions/front/onnx/depth_to_space_ext.py
extensions/front/onnx/dequantize_linear_ext.py
extensions/front/onnx/dequantize_linear_resolver.py
extensions/front/onnx/detection_output.py
extensions/front/onnx/detectionoutput_ext.py
extensions/front/onnx/dropout_ext.py
@ -594,6 +596,7 @@ extensions/ops/ctc_greedy_decoder.py
extensions/ops/cumsum.py
extensions/ops/data_augmentation.py
extensions/ops/depth_to_space.py
extensions/ops/dequantize_linear.py
extensions/ops/DetectionOutput.py
extensions/ops/detectionoutput_onnx.py
extensions/ops/elementwise.py

View File

@ -0,0 +1,33 @@
"""
Copyright (C) 2020 Intel Corporation
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
"""
import logging as log
from extensions.ops.dequantize_linear import DequantizeLinear
from mo.front.extractor import FrontExtractorOp
from mo.front.onnx.extractors.utils import onnx_attr, get_onnx_opset_version
class DequantizeLinearFrontExtractor(FrontExtractorOp):
op = 'DequantizeLinear'
enabled = True
@classmethod
def extract(cls, node):
if get_onnx_opset_version(node) >= 13:
log.warning('Ignoring "axis" attribute for DequantizeLinear-{} node, inference might be incorrect.'.format(
get_onnx_opset_version(node)))
DequantizeLinear.update_node_stat(node)
return cls.enabled

View File

@ -0,0 +1,53 @@
"""
Copyright (C) 2020 Intel Corporation
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
"""
from extensions.front.onnx.quantize_dequantize_linear import QuantizeDequantizeLinear
from extensions.ops.Cast import Cast
from extensions.ops.elementwise import Mul, Sub
from mo.front.common.replacement import FrontReplacementOp
from mo.graph.graph import Graph, Node, rename_nodes
from mo.middle.passes.convert_data_type import data_type_str_to_np
class DequantizeLinearResolver(FrontReplacementOp):
"""
DequantizeLinear can be replace with the following formula: y = (x - x_zero_point) * x_scale
"""
op = "DequantizeLinear"
enabled = True
def run_after(self):
return [QuantizeDequantizeLinear]
def replace_op(self, graph: Graph, node: Node):
node_name = node.soft_get('name', node.id)
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()
node.in_port(0).get_connection().set_destination(cast.in_port(0))
mul = Mul(graph, {}).create_node()
if node.is_in_port_connected(2):
sub = Sub(graph, {'name': node_name + '/Sub'}).create_node()
cast.out_port(0).connect(sub.in_port(0))
node.in_port(2).get_connection().set_destination(sub.in_port(1))
sub.out_port(0).connect(mul.in_port(0))
else:
cast.out_port(0).connect(mul.in_port(0))
node.in_port(1).get_connection().set_destination(mul.in_port(1))
rename_nodes([(node, node_name + '/TBD'), (mul, node_name)])
return [mul.id]

View File

@ -0,0 +1,100 @@
"""
Copyright (C) 2020 Intel Corporation
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
"""
import unittest
from argparse import Namespace
import numpy as np
from extensions.front.onnx.dequantize_linear_resolver import DequantizeLinearResolver
from mo.utils.ir_engine.compare_graphs import compare_graphs
from mo.utils.unittest.graph import build_graph
nodes1_attributes = {
'input': {'kind': 'op', 'op': 'AnyOp'},
'dequantize': {'kind': 'op', 'op': 'DequantizeLinear'},
'scale_param_dq': {'kind': 'op', 'type': 'Const', 'op': 'Const'},
'zerop_param_dq': {'kind': 'op', 'type': 'Const', 'op': 'Const'},
'out': {'kind': 'op', 'op': 'AnyOp'},
}
nodes_ref_attributes = {
'input': {'kind': 'op', 'op': 'AnyOp'},
'cast': {'kind': 'op', 'op': 'Cast', 'type': 'Convert'},
'sub': {'kind': 'op', 'op': 'Sub', 'type': 'Subtract'},
'mul': {'kind': 'op', 'op': 'Mul', 'type': 'Multiply'},
'scale_param_dq': {'kind': 'op', 'type': 'Const', 'op': 'Const'},
'zerop_param_dq': {'kind': 'op', 'type': 'Const', 'op': 'Const'},
'out': {'kind': 'op', 'op': 'AnyOp'},
}
class TestDequantizeLinearResolver(unittest.TestCase):
def test_dequantize(self):
graph = build_graph(nodes1_attributes,
[('input', 'dequantize'),
('scale_param_dq', 'dequantize'),
('zerop_param_dq', 'dequantize'),
('dequantize', 'out'),
],
{'scale_param_dq': {'shape': np.array([]), 'value': np.float32(1.0 / 255)},
'zerop_param_dq': {'shape': np.array([]), 'value': np.uint8(0)},
}, nodes_with_edges_only=True)
graph.graph['cmd_params'] = Namespace(keep_shape_ops=True, data_type='FP32')
graph_ref = build_graph(nodes_ref_attributes,
[('input', 'cast'),
('cast', 'sub'),
('zerop_param_dq', 'sub'),
('sub', 'mul'),
('scale_param_dq', 'mul'),
('mul', 'out'),
],
{'scale_param_dq': {'shape': np.array([]), 'value': np.float32(1.0 / 255)},
'zerop_param_dq': {'shape': np.array([]), 'value': np.uint8(0)}
}, nodes_with_edges_only=True)
graph.stage = 'front'
DequantizeLinearResolver().find_and_replace_pattern(graph)
(flag, resp) = compare_graphs(graph, graph_ref, 'out', check_op_attrs=True)
self.assertTrue(flag, resp)
def test_dequantize_no_zerop(self):
graph = build_graph(nodes1_attributes,
[('input', 'dequantize'),
('scale_param_dq', 'dequantize'),
('dequantize', 'out'),
],
{'scale_param_dq': {'shape': np.array([]), 'value': np.float32(1.0 / 255)},
}, nodes_with_edges_only=True)
graph.graph['cmd_params'] = Namespace(keep_shape_ops=True, data_type='FP32')
graph_ref = build_graph(nodes_ref_attributes,
[('input', 'cast'),
('cast', 'mul'),
('scale_param_dq', 'mul'),
('mul', 'out'),
],
{'scale_param_dq': {'shape': np.array([]), 'value': np.float32(1.0 / 255)}
}, nodes_with_edges_only=True)
graph.stage = 'front'
DequantizeLinearResolver().find_and_replace_pattern(graph)
(flag, resp) = compare_graphs(graph, graph_ref, 'out', check_op_attrs=True)
self.assertTrue(flag, resp)

View File

@ -0,0 +1,37 @@
"""
Copyright (C) 2020 Intel Corporation
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
"""
from mo.graph.graph import Graph
from mo.ops.op import Op
class DequantizeLinear(Op):
op = 'DequantizeLinear'
enabled = False
def __init__(self, graph: Graph, attrs: dict):
mandatory_props = {
'type': None,
'op': self.op,
'version': None,
'infer': None,
'out_ports_count': 1,
'in_ports_count': 3,
}
super().__init__(graph, mandatory_props, attrs)
def supported_attrs(self):
return ['axis']