diff --git a/docs/MO_DG/prepare_model/Supported_Frameworks_Layers.md b/docs/MO_DG/prepare_model/Supported_Frameworks_Layers.md index 048e0b89cdd..9724cc08427 100644 --- a/docs/MO_DG/prepare_model/Supported_Frameworks_Layers.md +++ b/docs/MO_DG/prepare_model/Supported_Frameworks_Layers.md @@ -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 | diff --git a/model-optimizer/automation/package_BOM.txt b/model-optimizer/automation/package_BOM.txt index 2288401c1f0..d54f44718d1 100644 --- a/model-optimizer/automation/package_BOM.txt +++ b/model-optimizer/automation/package_BOM.txt @@ -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 diff --git a/model-optimizer/extensions/front/onnx/dequantize_linear_ext.py b/model-optimizer/extensions/front/onnx/dequantize_linear_ext.py new file mode 100644 index 00000000000..466f7f323ff --- /dev/null +++ b/model-optimizer/extensions/front/onnx/dequantize_linear_ext.py @@ -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 diff --git a/model-optimizer/extensions/front/onnx/dequantize_linear_resolver.py b/model-optimizer/extensions/front/onnx/dequantize_linear_resolver.py new file mode 100644 index 00000000000..ae47bdcf743 --- /dev/null +++ b/model-optimizer/extensions/front/onnx/dequantize_linear_resolver.py @@ -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] diff --git a/model-optimizer/extensions/front/onnx/dequantize_linear_resolver_test.py b/model-optimizer/extensions/front/onnx/dequantize_linear_resolver_test.py new file mode 100644 index 00000000000..1a6eb753d8d --- /dev/null +++ b/model-optimizer/extensions/front/onnx/dequantize_linear_resolver_test.py @@ -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) diff --git a/model-optimizer/extensions/ops/dequantize_linear.py b/model-optimizer/extensions/ops/dequantize_linear.py new file mode 100644 index 00000000000..81fe2014559 --- /dev/null +++ b/model-optimizer/extensions/ops/dequantize_linear.py @@ -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']