FifoQueueDequeue replacer (#8891)

* added_replacer

* updated comments

* move cut to fifo_replacer

* extend shape serializer for parametr node

* warning message and docstrings

* docs update

* doc fix
This commit is contained in:
Yegor Kruglov
2021-12-24 11:38:21 +03:00
committed by GitHub
parent 6b8cfac82c
commit bd2880812f
6 changed files with 242 additions and 2 deletions

View File

@@ -269,7 +269,9 @@ Some TensorFlow\* operations do not match to any Inference Engine layer, but are
| Placeholder | |
| PlaceholderWithDefault | |
| Prod | |
| QueueDequeue | Supported only when it is part of a sub-graph of the special form |
| QueueDequeueUpToV2 | Supported only when it is part of a sub-graph of the special form |
| QueueDequeueV2 | Supported only when it is part of a sub-graph of the special form |
| RandomUniform | |
| RandomUniformInt | |
| Range | |

View File

@@ -604,6 +604,7 @@ openvino/tools/mo/front/tf/placeholder_ext.py
openvino/tools/mo/front/tf/placeholder_with_default_ext.py
openvino/tools/mo/front/tf/pooling_ext.py
openvino/tools/mo/front/tf/prelu.py
openvino/tools/mo/front/tf/QueueDequeue_ext.py
openvino/tools/mo/front/tf/random_uniform_ext.py
openvino/tools/mo/front/tf/random_uniform_int_ext.py
openvino/tools/mo/front/tf/range_ext.py

View File

@@ -0,0 +1,43 @@
# Copyright (C) 2018-2021 Intel Corporation
# SPDX-License-Identifier: Apache-2.0
from openvino.tools.mo.front.extractor import FrontExtractorOp
from openvino.tools.mo.front.tf.extractors.utils import tf_dtype_extractor, tf_tensor_shape
from openvino.tools.mo.graph.graph import Node
from openvino.tools.mo.ops.op import Op
def get_attrs(node: Node):
shapes = node.pb.attr["_output_shapes"].list.shape
tf_types = node.pb.attr["component_types"].list.type
extracted_types = []
for t in tf_types:
extracted_types.append(tf_dtype_extractor(t))
result_shapes = []
for shape_pb in shapes:
result_shapes.append(tf_tensor_shape(shape_pb))
assert len(result_shapes) == len(extracted_types), "Output shapes do not match output" \
"types in the node {}".format(node.soft_get('name', node.id))
attrs = {"shapes": result_shapes, "types": extracted_types, 'out_ports_count': len(result_shapes)}
return attrs
class QueueDequeueV1Extractor(FrontExtractorOp):
op = "QueueDequeue"
enabled = True
@classmethod
def extract(cls, node):
attrs = get_attrs(node)
Op.update_node_stat(node, attrs)
return cls.enabled
class QueueDequeueV2Extractor(FrontExtractorOp):
op = "QueueDequeueV2"
enabled = True
@classmethod
def extract(cls, node):
attrs = get_attrs(node)
Op.update_node_stat(node, attrs)
return cls.enabled

View File

@@ -2,12 +2,19 @@
# SPDX-License-Identifier: Apache-2.0
import logging as log
from collections import defaultdict
import numpy as np
from openvino.tools.mo.front.extractor import add_input_ops
from openvino.tools.mo.front.output_cut import OutputCut
from openvino.tools.mo.front.user_data_repack import UserDataRepack
from openvino.tools.mo.middle.passes.convert_data_type import np_data_type_to_precision, SUPPORTED_DATA_TYPES
from openvino.tools.mo.ops.parameter import Parameter
from openvino.tools.mo.front.common.replacement import FrontReplacementSubgraph
from openvino.tools.mo.front.common.replacement import FrontReplacementSubgraph, FrontReplacementPattern
from openvino.tools.mo.graph.graph import Graph, Node
from openvino.tools.mo.utils.error import Error
class FIFOQueue(FrontReplacementSubgraph):
@@ -113,3 +120,52 @@ class QueueDequeueManyV2(FrontReplacementSubgraph):
graph.remove_node(match['queue_deque'].id)
graph.remove_node(match['fifo_queue'].id)
class FIFOQueueDequeueCut(FrontReplacementPattern):
"""
Cuts FIFOQueue -> QueueDequeue pattern in order to enable Out Of the Box (OOB) usage.
Pass runs only if user didn't specify any input names and shapes.
This transformation relies on output shapes and types extracted from QueueDequeue node.
In the meantime, the transformations FIFOQueue and QueueDequeueManyV2 expects output shapes and types extracted
from FIFOQueue node.
"""
enabled = True
graph_condition = [lambda graph: graph.graph['cmd_params'].input is None]
def run_before(self):
return [OutputCut]
def run_after(self):
return [UserDataRepack]
def find_and_replace_pattern(self, graph: Graph):
fifo_qd_shapes = defaultdict(list)
for node in graph.get_op_nodes():
if node.op not in ["QueueDequeue", "QueueDequeueV2"]:
continue
new_inputs = ""
fifo_qd_name = node.soft_get('name', node.id)
for port_idx, port in node.out_ports().items():
if port.disconnected():
continue
if not np_data_type_to_precision(node.types[port_idx]) in SUPPORTED_DATA_TYPES:
raise Error("Data type {} is not supported for the"
"node {}".format(node.types[port_idx], fifo_qd_name))
fifo_qd_shapes[fifo_qd_name].append(dict(
shape=node.shapes[port_idx],
out=port_idx,
data_type=node.types[port_idx]
))
new_inputs += "{}:{}, ".format(fifo_qd_name, port_idx)
log.error(
"Found TF {} operation in the model. "
"PLEASE NOTE, the model will contain new input(s) ".format(node.op)
+ new_inputs +
"created due to automatically triggered pruning transformation for this operation.",
extra={'is_warning': True}
)
add_input_ops(graph, fifo_qd_shapes, True)

View File

@@ -50,6 +50,8 @@ class Parameter(Op):
if not node.has_valid('user_shape'):
return ','.join([str(i) for i in unmask_shape(node.shape)])
shape = node.soft_get('user_shape')
if isinstance(shape, np.ma.masked_array):
shape = unmask_shape(shape)
return ','.join(map(serialize_dimension, shape))
def supported_attrs(self):

View File

@@ -5,7 +5,9 @@ import unittest
import numpy as np
from openvino.tools.mo.front.tf.fifo_replacer import FIFOQueue
from openvino.tools.mo.front.common.partial_infer.utils import shape_array
from openvino.tools.mo.front.tf.fifo_replacer import FIFOQueue, FIFOQueueDequeueCut
from openvino.tools.mo.utils.ir_engine.compare_graphs import compare_graphs
from unit_tests.utils.graph import build_graph_with_edge_attrs
@@ -64,3 +66,137 @@ class TestFIFOQueueReplacement(unittest.TestCase):
self.fail("Can't get new placeholder. Broken edge. Additional information: {}".format(e))
self.assertEqual(new_ph_dict['name'], 'batch_join/fifo_queue')
self.assertTrue(np.array_equal(new_ph_dict['shape'], np.array([1, 2, 3])))
class FIFOQueueDequeueCutTest(unittest.TestCase):
def test_one_output_v1(self):
graph = build_graph_with_edge_attrs(
{
'queue_dequeue': {'kind': 'op', 'op': 'QueueDequeue', 'shapes': shape_array([[2, 2]]),
'types': [np.int32]},
'sub': {'kind': 'op', 'op': 'Sub'},
},
[
('queue_dequeue', 'sub', {'out': 0, 'in': 0}),
]
)
graph_ref = build_graph_with_edge_attrs(
{
'parameter_1': {'kind': 'op', 'op': 'Parameter', 'shape': shape_array([2, 2]), 'type': np.int32},
'sub': {'kind': 'op', 'op': 'Sub'},
},
[
('parameter_1', 'sub', {'out': 0, 'in': 0}),
]
)
FIFOQueueDequeueCut().find_and_replace_pattern(graph)
flag, msg = compare_graphs(graph, graph_ref, last_node='sub')
self.assertTrue(flag, msg)
def test_one_output_v2(self):
graph = build_graph_with_edge_attrs(
{
'queue_dequeue': {'kind': 'op', 'op': 'QueueDequeueV2', 'shapes': shape_array([[2, 2]]),
'types': [np.int32]},
'sub': {'kind': 'op', 'op': 'Sub'},
},
[
('queue_dequeue', 'sub', {'out': 0, 'in': 0}),
]
)
graph_ref = build_graph_with_edge_attrs(
{
'parameter_1': {'kind': 'op', 'op': 'Parameter', 'shape': shape_array([2, 2]), 'type': np.int32},
'sub': {'kind': 'op', 'op': 'Sub'},
},
[
('parameter_1', 'sub', {'out': 0, 'in': 0}),
]
)
FIFOQueueDequeueCut().find_and_replace_pattern(graph)
flag, msg = compare_graphs(graph, graph_ref, last_node='sub')
self.assertTrue(flag, msg)
def test_two_outputs_v1(self):
graph = build_graph_with_edge_attrs(
{
'queue_dequeue': {'kind': 'op', 'op': 'QueueDequeue', 'shapes': [shape_array([2, 2]),
shape_array([1, 1])],
'types': [np.int32, np.float32]},
'sub': {'kind': 'op', 'op': 'Sub'},
'add': {'kind': 'op', 'op': 'Add'},
'concat': {'kind': 'op', 'op': 'Concat'}
},
[
('queue_dequeue', 'sub', {'out': 0, 'in': 0}),
('queue_dequeue', 'add', {'out': 1, 'in': 0}),
('sub', 'concat', {'out': 0, 'in': 0}),
('add', 'concat', {'out': 0, 'in': 1})
]
)
graph_ref = build_graph_with_edge_attrs(
{
'parameter_1': {'kind': 'op', 'op': 'Parameter', 'shape': shape_array([2, 2]), 'data_type': np.int32},
'parameter_2': {'kind': 'op', 'op': 'Parameter', 'shape': shape_array([1, 1]), 'data_type': np.float32},
'sub': {'kind': 'op', 'op': 'Sub'},
'add': {'kind': 'op', 'op': 'Add'},
'concat': {'kind': 'op', 'op': 'Concat'}
},
[
('parameter_1', 'sub', {'out': 0, 'in': 0}),
('parameter_2', 'add', {'out': 0, 'in': 0}),
('sub', 'concat', {'out': 0, 'in': 0}),
('add', 'concat', {'out': 0, 'in': 1})
]
)
FIFOQueueDequeueCut().find_and_replace_pattern(graph)
flag, msg = compare_graphs(graph, graph_ref, last_node='concat', check_op_attrs=True)
self.assertTrue(flag, msg)
def test_two_outputs_v2(self):
graph = build_graph_with_edge_attrs(
{
'queue_dequeue': {'kind': 'op', 'op': 'QueueDequeueV2', 'shapes': [shape_array([2, 2]),
shape_array([1, 1])],
'types': [np.int32, np.float32]},
'sub': {'kind': 'op', 'op': 'Sub'},
'add': {'kind': 'op', 'op': 'Add'},
'concat': {'kind': 'op', 'op': 'Concat'}
},
[
('queue_dequeue', 'sub', {'out': 0, 'in': 0}),
('queue_dequeue', 'add', {'out': 1, 'in': 0}),
('sub', 'concat', {'out': 0, 'in': 0}),
('add', 'concat', {'out': 0, 'in': 1})
]
)
graph_ref = build_graph_with_edge_attrs(
{
'parameter_1': {'kind': 'op', 'op': 'Parameter', 'shape': shape_array([2, 2]), 'data_type': np.int32},
'parameter_2': {'kind': 'op', 'op': 'Parameter', 'shape': shape_array([1, 1]), 'data_type': np.float32},
'sub': {'kind': 'op', 'op': 'Sub'},
'add': {'kind': 'op', 'op': 'Add'},
'concat': {'kind': 'op', 'op': 'Concat'}
},
[
('parameter_1', 'sub', {'out': 0, 'in': 0}),
('parameter_2', 'add', {'out': 0, 'in': 0}),
('sub', 'concat', {'out': 0, 'in': 0}),
('add', 'concat', {'out': 0, 'in': 1})
]
)
FIFOQueueDequeueCut().find_and_replace_pattern(graph)
flag, msg = compare_graphs(graph, graph_ref, last_node='concat', check_op_attrs=True)
self.assertTrue(flag, msg)