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:
@@ -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 | |
|
||||
|
||||
@@ -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
|
||||
|
||||
43
tools/mo/openvino/tools/mo/front/tf/QueueDequeue_ext.py
Normal file
43
tools/mo/openvino/tools/mo/front/tf/QueueDequeue_ext.py
Normal 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
|
||||
@@ -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)
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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)
|
||||
Reference in New Issue
Block a user