[MO] fix MarkNodesWithShapeValues (#5808)

* fix marking of nodes with shape values: corrected 'leaky' starting condition

* cleaned up code

* fixed t2t type collision error: removed strict assert from Pow; now type_infer is same as for other eltwise ops; added warnings about type alignment

* type cast for Pow according to ONNX spec

* fix highlight in dump_graph_for_graphviz

* soft_get('op')

* Revert "soft_get('op')"

This reverts commit cadfe18f

* Revert "type cast for Pow according to ONNX spec"

This reverts commit bf85ebf8

* applied review comments

* comment for ShapeOf bfs, removed Error from elementwise op

* reverted back dump_graph_for_graphviz: fill_color -> node_attrs

* applied last review comments: returned back Error, added type_infer into unit-test utils
This commit is contained in:
Pavel Esir authored and GitHub committed 2021-06-04 18:49:49 +03:00
1 parent 06811924f4
commit d9d63d0d7e
5 files changed
+193 -63

No files matched your search

@@ -2,12 +2,30 @@
# SPDX-License-Identifier: Apache-2.0
import logging as log
from typing import List
import numpy as np
from extensions.middle.MarkSubgraphsWithCorrectLayout import MarkSubGraphsWithCorrectLayout
from mo.back.replacement import BackReplacementPattern
from mo.graph.graph import Graph
from mo.graph.graph import Graph, Node
shape_accepting_ops = {
'Interpolate': [1, 2], # sizes, scales inputs
'Reshape': [1], # shape
'Broadcast': [1], # target_shape
'ConvBackPropData ': [2], # output_shape
'GroupConvolutionBackpropData ': [2], # output_shape
'BatchToSpace': [1, 2, 3], # block_shape, crops_begin, crops_end
'SpaceToBatch': [1, 2, 3], # block_shape, pads_begin, pads_end
'StridedSlice': [1, 2, 3], # begin, end, strides
'VariadicSplit': [2], # split_lengths
'Tile': [1], # repeats input
'TopK': [1], # K input
'Pad': [1, 2], # pads_begin, pads_end
'Range': [0, 1, 2], # start, stop, step inputs
'OneHot': [1], # depth input
}
class MarkNodesWithShapeValues(BackReplacementPattern):
@@ -34,45 +52,44 @@ class MarkNodesWithShapeValues(BackReplacementPattern):
return []
@staticmethod
def get_operations_with_shape_inputs():
return {
'Interpolate': [1, 2], # sizes, scales inputs
'Reshape': [1], # shape
'Broadcast': [1], # target_shape
'ConvBackPropData ': [2], # output_shape
'GroupConvolutionBackpropData ': [2], # output_shape
'BatchToSpace': [1, 2, 3], # block_shape, crops_begin, crops_end
'SpaceToBatch': [1, 2, 3], # block_shape, pads_begin, pads_end
'StridedSlice': [1, 2, 3], # begin, end, strides
'VariadicSplit': [2], # split_lengths
'Tile': [1], # repeats input
'TopK': [1], # K input
'Pad': [1, 2], # pads_begin, pads_end
'Range': [0, 1, 2], # start, stop, step inputs
'OneHot': [1], # depth input
}
def find_and_replace_pattern(self, graph: Graph):
shape_input_ops_map = self.get_operations_with_shape_inputs()
nodes_with_shape_inputs = []
def get_nodes_with_shape_inputs(graph: Graph) -> List[Node]:
shape_accepting_nodes = []
for node in graph.get_op_nodes():
if node.soft_get('type') in shape_input_ops_map:
nodes_with_shape_inputs.append(node)
if node.soft_get('type') in shape_accepting_ops:
shape_accepting_nodes.append(node)
return shape_accepting_nodes
start_nodes = []
@staticmethod
def get_shape_returning_start_nodes(nodes_with_shape_inputs: List[Node]) -> List[Node]:
sources = []
for node in nodes_with_shape_inputs:
start_nodes.extend(
[node.in_port(port_idx).get_source().node for port_idx in shape_input_ops_map[node.type] if
node.is_in_port_connected(port_idx)])
assert node.soft_get('type') in shape_accepting_ops
for port_idx in shape_accepting_ops[node.soft_get('type')]:
if not node.is_in_port_connected(port_idx):
continue
source_node = node.in_port(port_idx).get_source().node
# no need to start BFS for ShapeOf nodes, indeed if there is a ShapeOf it's an end of BFS
if source_node.soft_get('type') != 'ShapeOf':
sources.append(source_node)
return sources
condition = lambda node: node.soft_get('type') != 'ShapeOf'
nodes_with_shape_values = MarkSubGraphsWithCorrectLayout.bfs(start_nodes, set(), condition, forward=False)
for node in nodes_with_shape_values:
@staticmethod
def mark_nodes(shape_returning_nodes: List[Node]):
for node in shape_returning_nodes:
node['returns_shape_value'] = True
if node.soft_get('type') == 'Const':
if node.value.dtype == np.float32:
node.out_node(0)['correct_data_type'] = True
elif node.value.dtype in [np.float16, np.float64]:
log.debug('Const nodes {} with shape values have {} type'.format(node.soft_get('name', node.id),
node.value.dtype))
log.error("Const node '{}' returns shape values of '{}' type but it must be integer or float32. "
"During Elementwise type inference will attempt to cast to float32".
format(node.soft_get('name', node.id), node.value.dtype), extra={'is_warning': True})
def find_and_replace_pattern(self, graph: Graph):
shape_accepting_nodes = self.get_nodes_with_shape_inputs(graph)
condition = lambda node: node.soft_get('type') != 'ShapeOf'
shape_returning_start_nodes = self.get_shape_returning_start_nodes(shape_accepting_nodes)
shape_returning_nodes = MarkSubGraphsWithCorrectLayout.bfs(shape_returning_start_nodes, set(),
condition, forward=False)
self.mark_nodes(shape_returning_nodes)
+12 -14
View File
@@ -10,6 +10,7 @@ from mo.graph.graph import Graph, Node
from mo.middle.passes.infer import copy_type_infer
from mo.ops.op import Op
from mo.pipeline.common import convert_const_node_value_type
from mo.utils.error import Error
def override_data_type_of_constant(node: Node):
@@ -22,13 +23,19 @@ def override_data_type_of_constant(node: Node):
# power value 2, or when replacing Neg operation with Mul with -1 as second input.
in_node_0 = node.in_port(0).get_source().node
in_node_1 = node.in_port(1).get_source().node
if in_node_0.op != 'Const' and in_node_1.op != 'Const':
raise Error("Elementwise operation '{}' has inputs of different data types: '{}' and '{}' "
"that cannot be aligned".format(node.soft_get('name'), in_type_0, in_type_1))
if in_node_0.op == 'Const':
convert_const_node_value_type(in_node_0, in_type_1)
elif in_node_1.op == 'Const':
convert_const_node_value_type(in_node_1, in_type_0)
node_to_convert, src_type, dst_type = in_node_0, in_type_0, in_type_1
else:
log.error('Elementwise operation {} has inputs of different data types: {} and {}'.format(
node.soft_get('name'), in_type_0, in_type_1))
node_to_convert, src_type, dst_type = in_node_1, in_type_1, in_type_0
log.error("Changing Const node '{}' data type from {} to {} for Elementwise operation".format(
node_to_convert.soft_get('name', node_to_convert.id), src_type, dst_type),
extra={'is_warning': True})
convert_const_node_value_type(node_to_convert, dst_type)
class Elementwise(Op):
@@ -129,15 +136,6 @@ class Pow(Elementwise):
return np.array(a.astype(np.float32) ** b, dtype=np.float32)
return a ** b
@staticmethod
def type_infer(node):
in_type_0 = node.in_port(0).get_data_type()
in_type_1 = node.in_port(1).get_data_type()
assert in_type_0 == in_type_1, \
'Power operation {} has inputs of different data types: {} and {}'.format(
node.soft_get('name'), in_type_0, in_type_1)
node.out_port(0).set_data_type(in_type_0)
class LogicalElementwise(Elementwise):
@staticmethod