[MO] Add reverse_infer (input rank deducing) for set of operations (#8928)
* grouped convolution resolver fix * add reverse_infer for Transpose, Elementwise, Identity and some fixes to successfully OOB convert MXNet models * add reverse_infer for BatchNorms, dynamic groups fix, multi_box_prior.py fix * add reverse infer for trivial cases * input rank setting for image detection ops * reverse_infer for Gather, AttributedGather add todo comment for Parameter * reverse infer for LSTM, swapaxis; fixes and unit-tests for Elementwise, Gather, Transpose; also generalized Parameter reverse_infer; fixed DetectionOutput and partial reverse_infer * clarified comment for LSTM reverse_infer * detalized Elementwise reverse_infer, added several more unit-tests, Split reverse_infer fix, IteratorGetNext_ext.py fix * generalized group convolution resolver * specified in_ports explicitly * clarified Split, Concat, Parameter, other minor typos * relaxed compatibility check for elementwise; simplified scalar extracting for convolution.py and gather.py, fixed replacer order for ConvolutionNormalizer.py * added routine set_input_shapes for image detection ops * fixed typos to pass locally E2E, slightly changed LSTM reverse_infer * VariadicSplit reverse_infer bugfix * IteratorGetNextCut.py added check if port is connected * explicitly specified in_port, added asserts, improved Split reverse infer * sorted imports * reverse_infer for ONNX and MXNet LSTM, GRU, RNN operations * replaced Error raising with red warning * added missing return for defined input shapes, fixed getting num groups for tf conv * added mandatory attributes; fixed failing unit-tests, fixed typo with scalar group in ConvolutionNormalizer * removed priorbox reverse_infer; Parameter reverse_infer corrected to use the only one existing out_port * fixed clarify_partial_shape_test
This commit is contained in:
1 parent
d27bbb4bdd
commit
41d4fcdf25
60 files changed
+1084
-94
No files matched your search
@@ -6,45 +6,97 @@ import numpy as np
|
||||
from openvino.tools.mo.back.ReshapeMutation import ReshapeMutation
|
||||
from openvino.tools.mo.back.ReverseInputChannels import ApplyReverseChannels
|
||||
from openvino.tools.mo.back.replacement import BackReplacementPattern
|
||||
from openvino.tools.mo.front.common.partial_infer.utils import mo_array
|
||||
from openvino.tools.mo.front.common.partial_infer.utils import shape_array, is_fully_defined, int64_array
|
||||
from openvino.tools.mo.front.common.partial_infer.utils import shape_array, is_fully_defined, int64_array, mo_array
|
||||
from openvino.tools.mo.front.tf.graph_utils import create_op_node_with_second_input, create_op_with_const_inputs
|
||||
from openvino.tools.mo.graph.graph import Graph, Node
|
||||
from openvino.tools.mo.ops.concat import Concat
|
||||
from openvino.tools.mo.ops.const import Const
|
||||
from openvino.tools.mo.ops.elementwise import Div
|
||||
from openvino.tools.mo.ops.gather import Gather
|
||||
from openvino.tools.mo.ops.reshape import Reshape
|
||||
from openvino.tools.mo.ops.shape import Shape
|
||||
from openvino.tools.mo.ops.strided_slice import StridedSlice
|
||||
from openvino.tools.mo.utils.error import Error
|
||||
|
||||
|
||||
def resolve_convolution_with_group(node: Node, group: int, ir_version: str):
|
||||
node_name = node.soft_get('name', node.id)
|
||||
input_shape = node.in_port(0).data.get_shape()
|
||||
assert len(input_shape) in [3, 4, 5]
|
||||
|
||||
weights_shape = node.in_port(1).data.get_shape()
|
||||
assert weights_shape is not None
|
||||
assert len(weights_shape) in [3, 4, 5]
|
||||
group = int64_array(group).item()
|
||||
assert weights_shape[0] % group == 0
|
||||
|
||||
if ir_version == 'V7':
|
||||
if weights_shape[0] == node.output:
|
||||
# weights are already is in [G*O I X Y] format
|
||||
return
|
||||
new_shape = shape_array([node.output, -1, *weights_shape[2:]])
|
||||
|
||||
num_spatial_dims = len(weights_shape[2:])
|
||||
# Reshape has special_zero=True, if zeros are set then original shapes are copied
|
||||
zeros_to_copy_spatial_dims = np.zeros(num_spatial_dims)
|
||||
new_shape = shape_array([node.output, -1, *zeros_to_copy_spatial_dims])
|
||||
reshape = create_op_node_with_second_input(node.graph, Reshape, new_shape,
|
||||
{'override_output_shape': True})
|
||||
elif ir_version == 'V10':
|
||||
# TODO rewrite this transformation to generate a shape-computing sub-graph. Ticket 62076
|
||||
I = input_shape[1]
|
||||
new_shape = shape_array([group, node.output // group, I // group, *weights_shape[2:]])
|
||||
assert is_fully_defined(weights_shape[2:]) and is_fully_defined(I) and \
|
||||
np.prod(weights_shape) == np.prod(new_shape), 'Initial weights shape {}, grouped weights shape {}' \
|
||||
''.format(weights_shape, new_shape)
|
||||
if is_fully_defined(weights_shape[2:]) and is_fully_defined(I):
|
||||
new_shape = shape_array([group, node.output // group, I // group, *weights_shape[2:]])
|
||||
assert np.prod(weights_shape) == np.prod(new_shape), 'Initial weights shape {}, grouped weights shape {}' \
|
||||
''.format(weights_shape, new_shape)
|
||||
reshape = create_op_node_with_second_input(node.graph, Reshape, new_shape,
|
||||
{'override_output_shape': True, 'special_zero': True})
|
||||
else:
|
||||
# if weights/or input channel dimension is dynamic need to to compute new_shape in a new subgraph
|
||||
weights_node = node.in_port(1).get_source().node
|
||||
input_node = node.in_port(0).get_source().node
|
||||
|
||||
weights_shape = Shape(node.graph, {'name': node_name + '/ShapeOfWeights'}).create_node()
|
||||
weights_shape.in_port(0).connect(weights_node.out_port(0))
|
||||
|
||||
weights_spatial_shape = create_op_with_const_inputs(node.graph, StridedSlice,
|
||||
port_value_dict={1: int64_array([2]),
|
||||
2: int64_array([-1])},
|
||||
op_attrs={'begin_mask': [1],
|
||||
'end_mask': [0],
|
||||
'new_axis_mask': [0],
|
||||
'shrink_axis_mask': [0],
|
||||
'ellipsis_mask': [0]},
|
||||
input_node=weights_shape)
|
||||
|
||||
const_part_of_shape = Const(node.graph, attrs=dict(name=node_name + '/GroupsAndOutputChannelsSize',
|
||||
value=int64_array(
|
||||
[group, node.output // group]))).create_node()
|
||||
|
||||
input_shape_node = Shape(node.graph, {'name': node_name + '/ShapeOfInput'}).create_node()
|
||||
input_shape_node.in_port(0).connect(input_node.out_port(0))
|
||||
|
||||
input_num_channels = create_op_with_const_inputs(node.graph, Gather,
|
||||
port_value_dict={1: int64_array([1]), 2: int64_array(0)},
|
||||
op_attrs={'name': node_name + '/GatherInputNumChannels'},
|
||||
input_node=input_shape_node)
|
||||
|
||||
# input channels num divided by number of groups to alight weights shape into [GROUPS C_OUT C_IN X Y]
|
||||
C_IN = create_op_with_const_inputs(node.graph, Div,
|
||||
port_value_dict={1: int64_array(group)},
|
||||
op_attrs={'name': node_name + '/Div'},
|
||||
input_node=input_num_channels)
|
||||
|
||||
new_shape_node = Concat(node.graph, {'axis': 0, 'in_ports_count': 3}).create_node()
|
||||
new_shape_node.in_port(0).connect(const_part_of_shape.out_port(0))
|
||||
new_shape_node.in_port(1).connect(C_IN.out_port(0))
|
||||
new_shape_node.in_port(2).connect(weights_spatial_shape.out_port(0))
|
||||
reshape = Reshape(node.graph, {'override_output_shape': True, 'special_zero': True}).create_node()
|
||||
reshape.in_port(1).connect(new_shape_node.out_port(0))
|
||||
|
||||
del node['group']
|
||||
node['type'] = 'GroupConvolution'
|
||||
else:
|
||||
raise Error("Unknown IR version: {}".format(ir_version))
|
||||
|
||||
reshape = create_op_node_with_second_input(node.graph, Reshape, int64_array(new_shape),
|
||||
{'override_output_shape': True})
|
||||
|
||||
node.in_port(1).get_connection().insert_node(reshape)
|
||||
|
||||
|
||||
@@ -104,7 +156,8 @@ class ConvolutionWithGroupsResolver(BackReplacementPattern):
|
||||
force_clean_up = True
|
||||
|
||||
def run_before(self):
|
||||
return [ReshapeMutation]
|
||||
from openvino.tools.mo.back.StridedSliceMasksNormalizer import StridedSliceMasksNormalizer
|
||||
return [ReshapeMutation, StridedSliceMasksNormalizer]
|
||||
|
||||
def run_after(self):
|
||||
return [ApplyReverseChannels]
|
||||
|
||||
@@ -3,11 +3,13 @@
|
||||
|
||||
import numpy as np
|
||||
|
||||
from openvino.tools.mo.front.common.partial_infer.utils import dynamic_dimension, dynamic_dimension_value
|
||||
from openvino.tools.mo.front.common.partial_infer.utils import dynamic_dimension, dynamic_dimension_value, \
|
||||
undefined_shape_of_rank, compatible_shapes, compatible_dims
|
||||
from openvino.tools.mo.graph.graph import Node
|
||||
from openvino.tools.mo.utils.error import Error
|
||||
|
||||
|
||||
def eltwise_infer(node, op=None, **kwargs):
|
||||
def eltwise_infer(node: Node, op=None, **kwargs):
|
||||
def broadcast_dims(dim1, dim2):
|
||||
if dim1 is not dynamic_dimension and dim2 is not dynamic_dimension:
|
||||
mind = min(dim1, dim2)
|
||||
@@ -80,8 +82,86 @@ def eltwise_infer(node, op=None, **kwargs):
|
||||
node.out_port(0).data.set_value(op(node.out_node().value, values[i + 1]))
|
||||
|
||||
|
||||
def eltwise_reverse_infer(node: Node):
|
||||
input_1_shape = node.in_port(0).data.get_shape()
|
||||
input_2_shape = node.in_port(1).data.get_shape()
|
||||
if input_1_shape is not None and input_2_shape is not None:
|
||||
return
|
||||
|
||||
output_shape = node.out_port(0).data.get_shape()
|
||||
node_name = node.soft_get('name', node.id)
|
||||
|
||||
if node['auto_broadcast'] is 'none':
|
||||
# input_1, input_2 and output shapes must match
|
||||
# therefore undefined partial shapes can be exactly defined from output shape
|
||||
if output_shape is not None:
|
||||
most_defined_shape = output_shape
|
||||
|
||||
# if out_shape = [4, dyn] and input_1_shape = [dyn, 13]
|
||||
# then missing shape must be [4, 13]
|
||||
if input_1_shape is not None and not compatible_shapes(output_shape, input_1_shape):
|
||||
raise Error("shapes are not compatible for node '{}'".format(node_name))
|
||||
elif input_1_shape is not None:
|
||||
most_defined_shape = find_common_partial_shape(output_shape, input_1_shape)
|
||||
|
||||
if input_2_shape is not None and not compatible_shapes(output_shape, input_2_shape):
|
||||
raise Error("shapes are not compatible for node '{}'".format(node_name))
|
||||
elif input_2_shape is not None:
|
||||
most_defined_shape = find_common_partial_shape(most_defined_shape, input_2_shape)
|
||||
|
||||
if input_1_shape is None:
|
||||
node.in_port(0).data.set_shape(most_defined_shape)
|
||||
if input_2_shape is None:
|
||||
node.in_port(1).data.set_shape(most_defined_shape)
|
||||
elif node['auto_broadcast'] == 'numpy':
|
||||
if output_shape is not None:
|
||||
out_rank = len(output_shape)
|
||||
deduced_in_shape = undefined_shape_of_rank(out_rank)
|
||||
|
||||
if input_1_shape is not None and input_2_shape is None and out_rank > len(input_1_shape):
|
||||
in_port_to_update = 1
|
||||
defined_in_shape = input_1_shape
|
||||
elif input_2_shape is not None and input_1_shape is None and out_rank > len(input_2_shape):
|
||||
in_port_to_update = 0
|
||||
defined_in_shape = input_2_shape
|
||||
else:
|
||||
return
|
||||
defined_in_rank = len(defined_in_shape)
|
||||
|
||||
for i in range(-1, -defined_in_rank - 1, -1):
|
||||
assert defined_in_shape[i] == 1 or np.ma.is_masked(defined_in_shape[i]) \
|
||||
or compatible_dims(defined_in_shape[i], output_shape[i]), \
|
||||
"Shapes of Elementwise node '{}' are not compatible for reverse_infer.".format(node_name)
|
||||
|
||||
# if defined_input_shape = [1] and output_shape = [N, 400, 400, 3]
|
||||
# partial shape information about sizes should not be lost
|
||||
if defined_in_shape[i] == 1 or output_shape[i] == 1:
|
||||
deduced_in_shape[i] = output_shape[i]
|
||||
deduced_in_shape[:-defined_in_rank] = output_shape[:-defined_in_rank]
|
||||
|
||||
node.in_port(in_port_to_update).data.set_shape(deduced_in_shape)
|
||||
|
||||
|
||||
def bias_add_infer(node, op):
|
||||
if node.in_port(0).data.get_value() is not None and node.in_port(1).data.get_value() is not None and op is not None:
|
||||
node.out_port(0).data.set_value(op(node.in_port(0).data.get_value(), node.in_port(1).data.get_value()))
|
||||
else:
|
||||
node.out_port(0).data.set_shape(node.in_port(0).data.get_shape())
|
||||
|
||||
|
||||
def find_common_partial_shape(shape1, shape2):
|
||||
"""
|
||||
Extracts maximum information from partially defined shapes.
|
||||
|
||||
For example, if shape_1 = [4, dyn] and shape_2 = [dyn, 13]
|
||||
then resulting shape will be [4, 13]
|
||||
:param shape1: partially defined shape
|
||||
:param shape2: partially defined shape
|
||||
:return:
|
||||
"""
|
||||
assert compatible_shapes(shape1, shape2), 'shapes must be compatible'
|
||||
res = shape1.copy()
|
||||
for i, (d1, d2) in enumerate(zip(shape1, shape2)):
|
||||
if np.ma.is_masked(res[i]):
|
||||
res[i] = d2
|
||||
return res
|
||||
@@ -1,7 +1,7 @@
|
||||
# Copyright (C) 2018-2022 Intel Corporation
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
from openvino.tools.mo.front.common.partial_infer.utils import int64_array
|
||||
from openvino.tools.mo.front.common.partial_infer.utils import shape_array
|
||||
from openvino.tools.mo.graph.graph import Node
|
||||
|
||||
|
||||
@@ -12,6 +12,6 @@ def multi_box_prior_infer_mxnet(node: Node):
|
||||
num_ratios = len(node.aspect_ratio)
|
||||
num_priors = len(node.min_size) + num_ratios - 1
|
||||
if v10:
|
||||
node.out_node(0).shape = int64_array([2, data_H * data_W * num_priors * 4])
|
||||
node.out_node(0).shape = shape_array([2, data_H * data_W * num_priors * 4])
|
||||
else:
|
||||
node.out_node(0).shape = int64_array([1, 2, data_H * data_W * num_priors * 4])
|
||||
node.out_node(0).shape = shape_array([1, 2, data_H * data_W * num_priors * 4])
|
||||
@@ -314,3 +314,49 @@ def is_dynamic_slice(s: [slice, int, None]):
|
||||
return isinstance(s, slice) and (s.start is dynamic_dimension or
|
||||
s.stop is dynamic_dimension or
|
||||
s.step is dynamic_dimension)
|
||||
|
||||
|
||||
def reverse_bypass_infer(node, in_ports: List[int]):
|
||||
"""
|
||||
Copies shapes from the out_port 0 into ports specified in the in_ports
|
||||
|
||||
|
||||
:param node:
|
||||
:param in_ports: input ports for which shape will be updated
|
||||
:return:
|
||||
"""
|
||||
assert node.is_out_port_connected(0)
|
||||
|
||||
output_shape = node.out_port(0).data.get_shape()
|
||||
if output_shape is not None:
|
||||
for port in in_ports:
|
||||
assert node.is_in_port_connected(port)
|
||||
if node.in_port(port).data.get_shape() is None:
|
||||
node.in_port(port).data.set_shape(output_shape)
|
||||
|
||||
|
||||
def clarify_partial_shape(shapes: List):
|
||||
"""
|
||||
returns more precise partial shape from a set of partial shapes,
|
||||
e.g. pshape_1 = [dyn, 2, dyn], pshape_2 = [10, dyn, dyn] => out_shape = [10, 2, dyn]
|
||||
:param shapes:
|
||||
:return:
|
||||
"""
|
||||
assert len(shapes) > 0
|
||||
out_shape = shapes[0]
|
||||
for shape in shapes:
|
||||
assert compatible_shapes(shape, out_shape), "shapes {} and {} are not compatible".format(
|
||||
unmask_shape(shape), unmask_shape(out_shape))
|
||||
shape_unmasked = shape.data.copy()
|
||||
for i, dim in enumerate(shape_unmasked):
|
||||
if dim != dynamic_dimension_value:
|
||||
out_shape[i] = dim
|
||||
return out_shape
|
||||
|
||||
|
||||
def set_input_shapes(node, *shapes: List):
|
||||
assert len(shapes) <= len(node.in_ports())
|
||||
|
||||
for i, shape in enumerate(shapes):
|
||||
if node.is_in_port_connected(i) and node.in_port(i).data.get_shape() is None:
|
||||
node.in_port(i).data.set_shape(shape)
|
||||
@@ -3,13 +3,14 @@
|
||||
|
||||
from openvino.tools.mo.front.common.extractors.utils import layout_attrs
|
||||
from openvino.tools.mo.front.common.partial_infer.batch_norm import batch_norm_4_infer
|
||||
|
||||
from openvino.tools.mo.front.common.partial_infer.utils import reverse_bypass_infer
|
||||
|
||||
def batch_norm_ext(attrs):
|
||||
node_attrs = {
|
||||
'type': 'BatchNormalization',
|
||||
'eps': attrs.float('eps', 0.001),
|
||||
'infer': batch_norm_4_infer,
|
||||
'reverse_infer': lambda node: reverse_bypass_infer(node, in_ports=[0]),
|
||||
'fix_gamma': attrs.bool('fix_gamma', False)
|
||||
}
|
||||
node_attrs.update(layout_attrs())
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
from openvino.tools.mo.front.caffe.extractors.utils import get_canonical_axis_index
|
||||
from openvino.tools.mo.front.common.partial_infer.utils import int64_array
|
||||
from openvino.tools.mo.utils.error import Error
|
||||
|
||||
|
||||
@@ -26,7 +25,7 @@ def mxnet_slice_axis_infer(node):
|
||||
node.axis = get_canonical_axis_index(in_shape, node.axis)
|
||||
slice_axis = node.axis
|
||||
|
||||
new_shape = int64_array(in_shape)
|
||||
new_shape = in_shape.copy()
|
||||
new_shape[slice_axis] = new_shape[slice_axis] / len(node.out_nodes())
|
||||
|
||||
axis_size = in_shape[slice_axis]
|
||||
|
||||
@@ -31,15 +31,18 @@ class IteratorGetNextCut(FrontReplacementPattern):
|
||||
iter_get_next_shapes = defaultdict(list)
|
||||
for iter_get_next in graph.get_op_nodes(op='IteratorGetNext'):
|
||||
iter_get_next_name = iter_get_next.soft_get('name', iter_get_next.id)
|
||||
for port in iter_get_next.out_ports():
|
||||
if not np_data_type_to_precision(iter_get_next.types[port]) in SUPPORTED_DATA_TYPES:
|
||||
for port_idx, port in iter_get_next.out_ports().items():
|
||||
if port.disconnected():
|
||||
continue
|
||||
|
||||
if not np_data_type_to_precision(iter_get_next.types[port_idx]) in SUPPORTED_DATA_TYPES:
|
||||
raise Error("In IteratorGetNext node '{}' data type '{}' is not supported".format(
|
||||
iter_get_next_name, iter_get_next.types[port]))
|
||||
iter_get_next_name, iter_get_next.types[port_idx]))
|
||||
|
||||
iter_get_next_shapes[iter_get_next_name].append(dict(
|
||||
shape=iter_get_next.shapes[port],
|
||||
out=port,
|
||||
data_type=iter_get_next.types[port]
|
||||
shape=iter_get_next.shapes[port_idx],
|
||||
out=port_idx,
|
||||
data_type=iter_get_next.types[port_idx]
|
||||
))
|
||||
|
||||
add_input_ops(graph, iter_get_next_shapes, True)
|
||||
@@ -20,5 +20,5 @@ class IteratorGetNextExtractor(FrontExtractorOp):
|
||||
result_shapes = []
|
||||
for shape_pb in shapes:
|
||||
result_shapes.append(tf_tensor_shape(shape_pb))
|
||||
Op.update_node_stat(node, {'shapes': result_shapes, 'types': extracted_types, 'out_ports_count': 1})
|
||||
Op.update_node_stat(node, {'shapes': result_shapes, 'types': extracted_types, 'out_ports_count': len(result_shapes)})
|
||||
return cls.enabled
|
||||
@@ -1,7 +1,7 @@
|
||||
# Copyright (C) 2018-2022 Intel Corporation
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
from openvino.tools.mo.front.common.partial_infer.utils import convert_tf_padding_to_str, int64_array
|
||||
from openvino.tools.mo.front.common.partial_infer.utils import convert_tf_padding_to_str, int64_array, dynamic_dimension
|
||||
from openvino.tools.mo.front.extractor import FrontExtractorOp
|
||||
from openvino.tools.mo.front.tf.extractors.utils import tf_data_format_channel, tf_data_format_batch, \
|
||||
tf_int_list
|
||||
@@ -16,9 +16,21 @@ class Conv2DFrontExtractor(FrontExtractorOp):
|
||||
@classmethod
|
||||
def extract(cls, node):
|
||||
attrs = tf_create_attrs(node, 2, 3)
|
||||
|
||||
def get_num_groups(node):
|
||||
if 'group' in node:
|
||||
return node.group
|
||||
elif node.in_node(0).shape is not None and node.kernel_shape is not None \
|
||||
and node.in_node(0).shape[node.channel_dims[0]] is not dynamic_dimension \
|
||||
and node.kernel_shape[node.input_feature_channel] is not dynamic_dimension:
|
||||
# if group attribute is not defined, number of groups is calculated
|
||||
# from number of input channels and filter channel size
|
||||
return node.in_node(0).shape[node.channel_dims] // node.kernel_shape[node.input_feature_channel]
|
||||
else:
|
||||
return 1
|
||||
|
||||
attrs.update({'op': __class__.op,
|
||||
'get_group': lambda node: node.group if 'group' in node and node.group is not None else
|
||||
node.in_node(0).shape[node.channel_dims] // node.kernel_shape[node.input_feature_channel],
|
||||
'get_group': get_num_groups,
|
||||
'get_output_feature_dim': lambda node: node.kernel_shape[node.output_feature_channel],
|
||||
'get_weights_permute': PermuteAttrs.Permutation(perm=int64_array([3, 2, 0, 1]),
|
||||
inv=int64_array([2, 3, 1, 0]))
|
||||
|
||||
@@ -3,8 +3,7 @@
|
||||
|
||||
import logging as log
|
||||
|
||||
from openvino.tools.mo.front.common.partial_infer.utils import mo_array
|
||||
from openvino.tools.mo.front.common.partial_infer.utils import shape_array
|
||||
from openvino.tools.mo.front.common.partial_infer.utils import mo_array, reverse_bypass_infer, shape_array
|
||||
from openvino.tools.mo.front.tf.extractors.utils import tf_dtype_extractor
|
||||
|
||||
|
||||
@@ -24,5 +23,6 @@ def tf_fused_bn_extractor(pb):
|
||||
'data_type': tf_dtype_extractor(pb.attr["T"].type),
|
||||
'eps': pb.attr['epsilon'].f,
|
||||
'infer': tf_fused_bn_infer,
|
||||
'reverse_infer': lambda node: reverse_bypass_infer(node, in_ports=[0]),
|
||||
'is_training': is_training
|
||||
}
|
||||
@@ -3,13 +3,14 @@
|
||||
|
||||
import numpy as np
|
||||
|
||||
from openvino.tools.mo.ops.transpose import Transpose
|
||||
from openvino.tools.mo.front.common.partial_infer.utils import int64_array, shape_insert, mo_array
|
||||
from openvino.tools.mo.front.common.partial_infer.utils import int64_array, shape_insert, mo_array, shape_array, \
|
||||
unmask_shape
|
||||
from openvino.tools.mo.graph.graph import Graph
|
||||
from openvino.tools.mo.middle.replacement import MiddleReplacementPattern
|
||||
from openvino.tools.mo.ops.const import Const
|
||||
from openvino.tools.mo.ops.op import Op
|
||||
from openvino.tools.mo.ops.reshape import Reshape
|
||||
from openvino.tools.mo.ops.transpose import Transpose
|
||||
|
||||
|
||||
class MXNetRNNSequenceNormalize(MiddleReplacementPattern):
|
||||
@@ -184,9 +185,9 @@ class MXNetRNNSequenceNormalize(MiddleReplacementPattern):
|
||||
mxnet_shape = lstm.out_node(0).shape.copy()
|
||||
|
||||
if lstm.batch_dim == 0:
|
||||
mo_shape = int64_array([input.shape[lstm.batch_dim], input.shape[lstm.sequence_dim], lstm.hidden_size])
|
||||
mo_shape = shape_array([input.shape[lstm.batch_dim], input.shape[lstm.sequence_dim], lstm.hidden_size])
|
||||
else:
|
||||
mo_shape = int64_array([input.shape[lstm.sequence_dim], input.shape[lstm.batch_dim], lstm.hidden_size])
|
||||
mo_shape = shape_array([input.shape[lstm.sequence_dim], input.shape[lstm.batch_dim], lstm.hidden_size])
|
||||
|
||||
if lstm.has_num_directions:
|
||||
mo_shape = shape_insert(mo_shape, 1, np.int64(num_directions))
|
||||
@@ -206,7 +207,7 @@ class MXNetRNNSequenceNormalize(MiddleReplacementPattern):
|
||||
# Add Reshape
|
||||
reshape = Reshape(graph, {'name': lstm_name + '/Reshape_mxnet/'})
|
||||
reshape_dim_data = Const(graph, {'name': lstm_name + '/Reshape_mxnet_dim',
|
||||
'value': mxnet_shape}).create_node_with_data()
|
||||
'value': int64_array(unmask_shape(mxnet_shape))}).create_node_with_data()
|
||||
|
||||
reshape.create_node_with_data([permute_data, reshape_dim_data], dict(), data_nodes=[old_data_node])
|
||||
|
||||
|
||||
@@ -3,11 +3,10 @@
|
||||
import logging as log
|
||||
|
||||
from openvino.tools.mo.front.common.partial_infer.utils import is_fully_defined, shape_array, \
|
||||
dynamic_dimension_value
|
||||
dynamic_dimension_value, unmask_shape
|
||||
from openvino.tools.mo.graph.graph import Graph
|
||||
from openvino.tools.mo.middle.passes.infer import partial_infer
|
||||
from openvino.tools.mo.middle.replacement import MiddleReplacementPattern
|
||||
from openvino.tools.mo.ops.parameter import Parameter
|
||||
|
||||
|
||||
class PartialInfer(MiddleReplacementPattern):
|
||||
@@ -27,7 +26,7 @@ class PartialInfer(MiddleReplacementPattern):
|
||||
param_shape = parameter.soft_get('shape', shape_array(dynamic_dimension_value))
|
||||
if not is_fully_defined(param_shape):
|
||||
parameter_name = parameter.soft_get('name', parameter.id)
|
||||
dynamic_inputs[parameter_name] = parameter
|
||||
dynamic_inputs[parameter_name] = param_shape
|
||||
if dynamic_inputs:
|
||||
log.error('The model contains input(s) with partially defined shapes: {}. '
|
||||
'Starting from the 2022.1 release the Model Optimizer can generate an IR with partially defined '
|
||||
@@ -36,7 +35,7 @@ class PartialInfer(MiddleReplacementPattern):
|
||||
'call "reshape" method in the Inference Engine and specify static input shapes. For optimal '
|
||||
'performance, it is still recommended to update input shapes with fixed ones using "--input" or '
|
||||
'"--input_shape" command-line parameters.'
|
||||
.format(','.join('name="{}" shape="{}"'.format(name, Parameter.shape_serialize(parameter))
|
||||
for name, parameter in dynamic_inputs.items())),
|
||||
.format(','.join(f'name="{name}" shape="{unmask_shape(param_shape)}"'
|
||||
for name, param_shape in dynamic_inputs.items())),
|
||||
extra={'is_warning': True})
|
||||
partial_infer(graph)
|
||||
@@ -117,7 +117,7 @@ def partial_infer(graph: Graph, start_node: str = None):
|
||||
not_fully_inferred = graph.get_nodes_with_attributes(is_not_fully_inferred=True)
|
||||
for n in not_fully_inferred:
|
||||
node = Node(graph, n)
|
||||
if node.has('infer') and not node.infer is None:
|
||||
if node.has_and_set('infer'):
|
||||
node.infer(node)
|
||||
|
||||
return graph
|
||||
@@ -145,6 +145,7 @@ def infer_nodes(graph: Graph, nodes: List[Node], constant_subgraph_only: bool =
|
||||
in_values = [port.data.get_value() for port in node.in_ports().values()]
|
||||
if node.soft_get('op') == 'Parameter' or any(value is None for value in in_values) or \
|
||||
(node.soft_get('op') == 'ShapeOf' and node.in_port(0).data.get_shape() is None):
|
||||
# if here will be any new ShapeOf type operation, we should update condition above
|
||||
continue
|
||||
|
||||
if debug_logger:
|
||||
@@ -335,8 +336,20 @@ def copy_type_infer(node):
|
||||
|
||||
def reverse_infer(graph: Graph, nodes: list):
|
||||
nodes = reversed(nodes)
|
||||
debug_logger = log.getLogger().isEnabledFor(log.DEBUG)
|
||||
for n in nodes:
|
||||
node = Node(graph, n)
|
||||
if node.has_valid('reverse_infer'):
|
||||
if node.has_and_set('reverse_infer'):
|
||||
log.debug("Executed reverse infer for node '{}'".format(node.soft_get('name', node.id)))
|
||||
node.reverse_infer(node)
|
||||
|
||||
if debug_logger:
|
||||
log.debug('-' * 20)
|
||||
log.debug('Reverse infer for {}'.format(node.soft_get('name')))
|
||||
log.debug('Op: {}'.format(node.soft_get('op')))
|
||||
log.debug('Outputs:')
|
||||
log_debug_dict(node.out_nodes(), 'outputs')
|
||||
|
||||
log.debug('Inputs:')
|
||||
log_debug_dict(node.in_nodes(), 'inputs')
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
# Copyright (C) 2018-2022 Intel Corporation
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
from openvino.tools.mo.front.common.partial_infer.utils import reverse_bypass_infer
|
||||
from openvino.tools.mo.graph.graph import Graph
|
||||
from openvino.tools.mo.ops.op import Op
|
||||
|
||||
@@ -19,8 +20,10 @@ class BatchNormInference(Op):
|
||||
'op': self.op,
|
||||
'in_ports_count': 5,
|
||||
'out_ports_count': 1,
|
||||
'infer': self.infer
|
||||
'infer': self.infer,
|
||||
'reverse_infer': lambda node: reverse_bypass_infer(node, in_ports=[0]),
|
||||
}, attrs)
|
||||
|
||||
@staticmethod
|
||||
def infer(node):
|
||||
node.out_port(0).data.set_shape(node.in_port(0).data.get_shape())
|
||||
@@ -5,6 +5,7 @@ import logging as log
|
||||
|
||||
import numpy as np
|
||||
|
||||
from openvino.tools.mo.front.common.partial_infer.utils import reverse_bypass_infer
|
||||
from openvino.tools.mo.graph.graph import Node, Graph
|
||||
from openvino.tools.mo.middle.passes.convert_data_type import np_data_type_to_precision, convert_blob, \
|
||||
np_data_type_to_destination_type, packed_I4, packed_U4
|
||||
@@ -22,6 +23,7 @@ class Cast(Op):
|
||||
'type': 'Convert',
|
||||
'version': 'opset1',
|
||||
'infer': self.infer,
|
||||
'reverse_infer': lambda node: reverse_bypass_infer(node, in_ports=[0]),
|
||||
'type_infer': self.type_infer,
|
||||
'dst_type': None,
|
||||
'in_ports_count': 1,
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
from openvino.tools.mo.front.common.partial_infer.elemental import copy_shape_infer
|
||||
from openvino.tools.mo.front.common.partial_infer.utils import reverse_bypass_infer
|
||||
from openvino.tools.mo.graph.graph import Node, Graph
|
||||
from openvino.tools.mo.ops.op import Op
|
||||
|
||||
@@ -17,6 +18,7 @@ class ConvertLike(Op):
|
||||
'version': 'opset1',
|
||||
|
||||
'infer': copy_shape_infer,
|
||||
'reverse_infer': lambda node: reverse_bypass_infer(node, in_ports=[0]),
|
||||
'type_infer': self.type_infer,
|
||||
|
||||
'in_ports_count': 2,
|
||||
|
||||
@@ -3,10 +3,10 @@
|
||||
|
||||
import numpy as np
|
||||
|
||||
from openvino.tools.mo.front.common.partial_infer.utils import is_fully_defined, compatible_dims
|
||||
from openvino.tools.mo.front.common.partial_infer.utils import is_fully_defined, compatible_dims, \
|
||||
undefined_shape_of_rank, set_input_shapes
|
||||
from openvino.tools.mo.front.extractor import bool_to_str
|
||||
from openvino.tools.mo.graph.graph import Graph
|
||||
from openvino.tools.mo.graph.graph import Node
|
||||
from openvino.tools.mo.graph.graph import Graph, Node
|
||||
from openvino.tools.mo.ops.op import Op
|
||||
from openvino.tools.mo.utils.error import Error
|
||||
|
||||
@@ -23,6 +23,7 @@ class DetectionOutput(Op):
|
||||
'in_ports_count': 3,
|
||||
'out_ports_count': 1,
|
||||
'infer': self.infer,
|
||||
'reverse_infer': self.reverse_infer,
|
||||
'input_width': 1,
|
||||
'input_height': 1,
|
||||
'normalized': True,
|
||||
@@ -104,3 +105,20 @@ class DetectionOutput(Op):
|
||||
|
||||
# the line below is needed for the TF framework so the MO will not change the layout
|
||||
node.graph.node[node.out_node(0).id]['nchw_layout'] = True
|
||||
|
||||
@staticmethod
|
||||
def reverse_infer(node):
|
||||
num_in_ports = len(node.in_ports())
|
||||
assert num_in_ports in [3, 6], 'incorrect number of input ports for DetectionOutput node {}'.format(node.soft_get('name', node.id))
|
||||
if num_in_ports == 3:
|
||||
set_input_shapes(node,
|
||||
undefined_shape_of_rank(2),
|
||||
undefined_shape_of_rank(2),
|
||||
undefined_shape_of_rank(3))
|
||||
elif num_in_ports == 6:
|
||||
set_input_shapes(node,
|
||||
undefined_shape_of_rank(2),
|
||||
undefined_shape_of_rank(2),
|
||||
undefined_shape_of_rank(3),
|
||||
undefined_shape_of_rank(2),
|
||||
undefined_shape_of_rank(2))
|
||||
@@ -1,9 +1,9 @@
|
||||
# Copyright (C) 2018-2022 Intel Corporation
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
from openvino.tools.mo.ops.RNN import rnn_infer
|
||||
from openvino.tools.mo.front.common.partial_infer.utils import mo_array
|
||||
from openvino.tools.mo.graph.graph import Node, Graph
|
||||
from openvino.tools.mo.ops.RNN import rnn_infer, RNN
|
||||
from openvino.tools.mo.ops.op import Op
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ class GRU(Op):
|
||||
'has_num_directions': False,
|
||||
'direction': 'forward',
|
||||
'infer': __class__.infer,
|
||||
'reverse_infer': RNN.reverse_infer,
|
||||
'multiplier': 3,
|
||||
'multilayers': False,
|
||||
'gate_order': mo_array([0, 1, 2]), # TODO: change it later
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
# Copyright (C) 2018-2022 Intel Corporation
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
from openvino.tools.mo.ops.RNN import rnn_infer
|
||||
from openvino.tools.mo.graph.graph import Node, Graph
|
||||
from openvino.tools.mo.ops.RNN import rnn_infer, RNN
|
||||
from openvino.tools.mo.ops.op import Op
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ class LSTM(Op):
|
||||
'has_num_directions': False, # if True, output shape has 4 dimensions; 3D otherwise
|
||||
'direction': 'forward',
|
||||
'infer': self.infer,
|
||||
'reverse_infer': RNN.reverse_infer,
|
||||
'multiplier': 4,
|
||||
'gate_order': None,
|
||||
'normalized': False,
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
# Copyright (C) 2018-2022 Intel Corporation
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import logging as log
|
||||
|
||||
import numpy as np
|
||||
|
||||
from openvino.tools.mo.front.common.partial_infer.utils import mark_input_bins, shape_array, shape_insert
|
||||
from openvino.tools.mo.front.common.partial_infer.utils import mark_input_bins, shape_insert, dynamic_dimension, \
|
||||
shape_array
|
||||
from openvino.tools.mo.front.common.partial_infer.utils import mo_array
|
||||
from openvino.tools.mo.graph.graph import Node, Graph, add_opoutput
|
||||
from openvino.tools.mo.graph.graph import Node, Graph, add_opoutput, Error
|
||||
from openvino.tools.mo.ops.op import Op
|
||||
|
||||
|
||||
@@ -20,6 +23,7 @@ class RNN(Op):
|
||||
'has_num_directions': False,
|
||||
'direction': 'forward',
|
||||
'infer': self.infer,
|
||||
'reverse_infer': self.reverse_infer,
|
||||
'multiplier': 1,
|
||||
'gate_order': mo_array([0]), # Only one gate in this cell
|
||||
'normalized': False,
|
||||
@@ -68,6 +72,20 @@ class RNN(Op):
|
||||
|
||||
rnn_infer(node, [1])
|
||||
|
||||
@staticmethod
|
||||
def reverse_infer(node: Node):
|
||||
if node.in_port(0).data.get_shape() is not None:
|
||||
return
|
||||
|
||||
input_size = get_rnn_input_size(node)
|
||||
batch_size, seq_len = get_rnn_batch_size_and_seq_len(node)
|
||||
# MXNet, ONNX has the same input layout
|
||||
input_shape = shape_array([seq_len, batch_size, input_size])
|
||||
if node.format == 'tf':
|
||||
input_shape = shape_array([batch_size, seq_len, input_size])
|
||||
|
||||
node.in_port(0).data.set_shape(input_shape)
|
||||
|
||||
|
||||
def rnn_infer(node: Node, out_ports=None):
|
||||
"""
|
||||
@@ -154,3 +172,100 @@ def rnn_infer(node: Node, out_ports=None):
|
||||
else:
|
||||
data_node = node.out_node(i)
|
||||
data_node.shape = shape_array(state_size)
|
||||
|
||||
|
||||
def get_rnn_batch_size_and_seq_len(node: Node):
|
||||
"""
|
||||
Gets batch_size and sequence_length from RNN constant inputs
|
||||
and output shapes retrieved during reverse_infer
|
||||
|
||||
:param node:
|
||||
:return:
|
||||
"""
|
||||
node_name = node.soft_get('name', node.id)
|
||||
out_shape = node.out_port(0).data.get_shape()
|
||||
batch_size = dynamic_dimension
|
||||
seq_len = dynamic_dimension
|
||||
in_port_with_initial_states = 3 # initial hidden size values is framework dependent
|
||||
|
||||
if out_shape is not None:
|
||||
# note that op is not in opset state but in the state of the original framework
|
||||
if node.batch_dim == 1:
|
||||
seq_len = out_shape[0]
|
||||
|
||||
if node.format == 'mxnet':
|
||||
assert len(out_shape) == 3, 'incorrect out_shape rank for node {}'.format(node_name)
|
||||
# for MXNet out_shape = [seq_len, batch_size, hidden_size]
|
||||
batch_size = out_shape[1]
|
||||
in_port_with_initial_states = 2
|
||||
elif node.format == 'onnx':
|
||||
assert len(out_shape) == 4, 'incorrect out_shape rank for node {}'.format(node_name)
|
||||
# even for ONNX in extractor 'batch_dim': 1 (front/onnx/lstm_ext.py:26) despite the fact that
|
||||
# out_shape = [seq_len, num_directions, batch_size, hidden_size]
|
||||
batch_size = out_shape[2]
|
||||
in_port_with_initial_states = 5
|
||||
elif node.format == 'tf':
|
||||
log.error('reverse infer for TensorFlow RNN operation {} is not implemented yet'.format(node_name),
|
||||
extra={'is_warning': True})
|
||||
else:
|
||||
raise Error('Incorrect framework name')
|
||||
elif node.batch_dim == 0:
|
||||
# out_shape = [batch_size, num_directions, seq_len, hidden_size]
|
||||
batch_size = out_shape[0]
|
||||
seq_len = out_shape[2]
|
||||
in_port_with_initial_states = 3
|
||||
else:
|
||||
raise Error('incorrect batch_dim for node {}'.format(node_name))
|
||||
|
||||
if batch_size is dynamic_dimension:
|
||||
if node.is_in_port_connected(in_port_with_initial_states):
|
||||
initial_hidden_state_size = node.in_port(in_port_with_initial_states).data.get_shape()
|
||||
if initial_hidden_state_size is not None:
|
||||
batch_size = initial_hidden_state_size[1]
|
||||
|
||||
if seq_len is dynamic_dimension and node.format == 'onnx':
|
||||
# ONNX can store seq_len in optional input
|
||||
if node.is_in_port_connected(4):
|
||||
seq_len_val = node.in_port(4).data.get_value()
|
||||
if seq_len_val is not None:
|
||||
seq_len = seq_len.item()
|
||||
|
||||
return [batch_size, seq_len]
|
||||
|
||||
|
||||
def get_rnn_input_size(node: Node):
|
||||
node_name = node.soft_get('name', node.id)
|
||||
assert node.is_in_port_connected(1), 'weights input is not connected'
|
||||
|
||||
if node.format == 'onnx':
|
||||
# ONNX weights on input 1 contain only W part, R, and B are connected separately
|
||||
# weights_shape = `[num_directions, 4 * hidden_size, input_size]`
|
||||
weights_size = node.in_port(1).data.get_shape()
|
||||
assert len(weights_size) == 3, 'incorrect weights ranks for MXNet {} node {}'.format(node.op, node_name)
|
||||
input_size = weights_size[2]
|
||||
return input_size
|
||||
elif node.format == 'mxnet':
|
||||
multiplier = node.multiplier
|
||||
hidden_size = node.hidden_size
|
||||
num_layers = node.num_layers
|
||||
direction = 2 if node.has_num_directions else 1
|
||||
|
||||
# for MXNet models we always get flattened weights which contains WRB
|
||||
weights_size = node.in_port(1).data.get_shape()
|
||||
assert len(weights_size) == 1, 'incorrect weights ranks for MXNet {} node {}'.format(node.op, node_name)
|
||||
weights_size = weights_size[0]
|
||||
|
||||
size = hidden_size * direction * multiplier
|
||||
other_layer_params_size = (hidden_size * direction + hidden_size + 2) * size
|
||||
first_layer_params_size = weights_size - (num_layers - 1) * other_layer_params_size
|
||||
# lhe lines above to find first_layer_params_size was taken from MXNetSplitMultiLayers.py:79
|
||||
# input_size can be calculated from the first_layer_params_size
|
||||
# if first_layer_params_size = (input_size + hidden_size + 2) * size
|
||||
# then input_size = first_layer_params_size / size - 2 - hidden_size
|
||||
input_size = first_layer_params_size / size - 2 - hidden_size
|
||||
return input_size
|
||||
elif node.format == 'tf':
|
||||
log.error('reverse infer for TensorFlow RNN operation {} is not implemented yet'.format(node_name),
|
||||
extra={'is_warning': True})
|
||||
else:
|
||||
raise Error('Incorrect framework name')
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
import numpy as np
|
||||
|
||||
from openvino.tools.mo.front.common.partial_infer.utils import int64_array
|
||||
from openvino.tools.mo.front.common.partial_infer.utils import reverse_bypass_infer
|
||||
from openvino.tools.mo.graph.graph import Graph
|
||||
from openvino.tools.mo.ops.op import Op
|
||||
|
||||
@@ -19,6 +19,7 @@ class Reverse(Op):
|
||||
'in_ports_count': 2,
|
||||
'out_ports_count': 1,
|
||||
'infer': self.infer,
|
||||
'reverse_infer': lambda node: reverse_bypass_infer(node, in_ports=[0]),
|
||||
}
|
||||
super().__init__(graph, mandatory_props, attrs)
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
import numpy as np
|
||||
|
||||
from openvino.tools.mo.front.common.partial_infer.eltwise import eltwise_infer
|
||||
from openvino.tools.mo.front.common.partial_infer.utils import reverse_bypass_infer
|
||||
from openvino.tools.mo.graph.graph import Graph, Node
|
||||
from openvino.tools.mo.ops.clamp import AttributedClamp
|
||||
from openvino.tools.mo.ops.op import Op
|
||||
@@ -24,6 +25,7 @@ class Activation(Op):
|
||||
'operation': self.operation,
|
||||
'version': self.version,
|
||||
'infer': self.infer,
|
||||
'reverse_infer': lambda node: reverse_bypass_infer(node, in_ports=[0]),
|
||||
'in_ports_count': 1,
|
||||
'out_ports_count': 1,
|
||||
}, attrs)
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
from openvino.tools.mo.front.common.partial_infer.concat import concat_infer
|
||||
from openvino.tools.mo.front.common.partial_infer.utils import dynamic_dimension
|
||||
from openvino.tools.mo.graph.graph import Node
|
||||
from openvino.tools.mo.ops.op import Op
|
||||
|
||||
|
||||
@@ -16,8 +18,24 @@ class Concat(Op):
|
||||
'version': 'opset1',
|
||||
'axis': 1,
|
||||
'infer': concat_infer,
|
||||
'reverse_infer': self.reverse_infer,
|
||||
'out_ports_count': 1,
|
||||
}, attrs)
|
||||
|
||||
def supported_attrs(self):
|
||||
return ['axis']
|
||||
|
||||
@staticmethod
|
||||
def reverse_infer(node: Node):
|
||||
assert hasattr(node, 'axis')
|
||||
out_shape = node.out_port(0).data.get_shape()
|
||||
|
||||
if out_shape is None:
|
||||
return
|
||||
|
||||
out_shape[node.axis] = dynamic_dimension
|
||||
|
||||
for in_port in node.in_ports().values():
|
||||
in_shape = in_port.data.get_shape()
|
||||
if in_shape is None:
|
||||
in_port.data.set_shape(out_shape)
|
||||
@@ -7,7 +7,6 @@ import numpy as np
|
||||
|
||||
from openvino.tools.mo.front.common.partial_infer.utils import int64_array, mark_input_bins, assign_dims_to_weights, \
|
||||
tf_window_op_pad_infer, dynamic_dimension_value, shape_array, is_fully_defined, undefined_shape_of_rank
|
||||
from openvino.tools.mo.front.common.partial_infer.utils import mo_array
|
||||
from openvino.tools.mo.front.onnx.extractors.utils import get_backend_pad
|
||||
from openvino.tools.mo.graph.graph import Node, Graph
|
||||
from openvino.tools.mo.graph.perm_inputs import PermuteInputs
|
||||
@@ -115,8 +114,12 @@ class Convolution(Op):
|
||||
'group') and node.has_valid('kernel_spatial')):
|
||||
log.error('Cannot reshape kernel due to not all required attrs was set to {} node'.format(node.id))
|
||||
return
|
||||
|
||||
# since item() unmasks values, result should be masked back
|
||||
num_in_channels = shape_array(input_shape[node.channel_dims].item())
|
||||
|
||||
# layout for Convolution weights is OIHW
|
||||
kernel_shape = shape_array([node.output, input_shape[node.channel_dims].item() / node.group,
|
||||
kernel_shape = shape_array([node.output, num_in_channels / node.group,
|
||||
*[node.kernel_spatial[i] for i in range(len(node.kernel_spatial))]])
|
||||
if node.type == 'Deconvolution': # layout for Deconvolution weights is IOHW
|
||||
kernel_shape[[0, 1]] = kernel_shape[[1, 0]]
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
# Copyright (C) 2018-2022 Intel Corporation
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
from openvino.tools.mo.graph.graph import Graph
|
||||
from openvino.tools.mo.front.common.partial_infer.utils import undefined_shape_of_rank
|
||||
from openvino.tools.mo.graph.graph import Graph, Node
|
||||
from openvino.tools.mo.ops.convolution import Convolution
|
||||
from openvino.tools.mo.ops.op import Op
|
||||
|
||||
@@ -31,3 +32,12 @@ class DeformableConvolution(Op):
|
||||
if self.get_opset() == 'opset8':
|
||||
attrs.append('bilinear_interpolation_pad')
|
||||
return attrs
|
||||
|
||||
@staticmethod
|
||||
def reverse_infer(node: Node):
|
||||
input_shape_1 = node.in_port(0).data.get_shape()
|
||||
input_shape_2 = node.in_port(1).data.get_shape()
|
||||
if input_shape_1 is None:
|
||||
node.in_port(0).data.set_shape(undefined_shape_of_rank(4))
|
||||
if input_shape_2 is None:
|
||||
node.in_port(1).data.set_shape(undefined_shape_of_rank(4))
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
import numpy as np
|
||||
|
||||
from openvino.tools.mo.front.common.partial_infer.utils import dynamic_dimension_value, shape_array, set_input_shapes
|
||||
from openvino.tools.mo.ops.op import Op
|
||||
|
||||
|
||||
@@ -16,6 +17,7 @@ class ExperimentalDetectronDetectionOutput(Op):
|
||||
op=self.op,
|
||||
version='opset6',
|
||||
infer=self.infer,
|
||||
reverse_infer=self.reverse_infer,
|
||||
type_infer=self.type_infer,
|
||||
in_ports_count=4,
|
||||
out_ports_count=3,
|
||||
@@ -54,3 +56,11 @@ class ExperimentalDetectronDetectionOutput(Op):
|
||||
node.out_port(2).set_data_type(in_data_type)
|
||||
if node.is_out_port_connected(3):
|
||||
node.out_port(3).set_data_type(np.int32) # the fourth output contains batch indices
|
||||
|
||||
@staticmethod
|
||||
def reverse_infer(node):
|
||||
set_input_shapes(node,
|
||||
shape_array([dynamic_dimension_value, 4]),
|
||||
shape_array([dynamic_dimension_value, node['num_classes'] * 4]),
|
||||
shape_array([dynamic_dimension_value, node['num_classes']]),
|
||||
shape_array([1, 3]))
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
import numpy as np
|
||||
|
||||
from openvino.tools.mo.front.common.partial_infer.eltwise import eltwise_infer, bias_add_infer
|
||||
from openvino.tools.mo.front.common.partial_infer.eltwise import eltwise_infer, bias_add_infer, eltwise_reverse_infer
|
||||
from openvino.tools.mo.front.common.partial_infer.utils import float32_array
|
||||
from openvino.tools.mo.graph.graph import Graph, Node
|
||||
from openvino.tools.mo.middle.passes.infer import copy_type_infer
|
||||
@@ -24,6 +24,7 @@ class Elementwise(Op):
|
||||
'type': self.op_type,
|
||||
'version': self.version,
|
||||
'infer': lambda node: eltwise_infer(node, self.operation),
|
||||
'reverse_infer': eltwise_reverse_infer,
|
||||
'type_infer': self.type_infer,
|
||||
'can_be_bias': True,
|
||||
'can_be_fused': True,
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
import numpy as np
|
||||
|
||||
from openvino.tools.mo.front.caffe.extractors.utils import get_canonical_axis_index
|
||||
from openvino.tools.mo.front.common.partial_infer.utils import int64_array, is_fully_defined
|
||||
from openvino.tools.mo.front.common.partial_infer.utils import mo_array
|
||||
from openvino.tools.mo.front.common.partial_infer.utils import mo_array, int64_array, is_fully_defined, shape_array, \
|
||||
dynamic_dimension_value
|
||||
from openvino.tools.mo.graph.graph import Node, Graph
|
||||
from openvino.tools.mo.ops.op import Op, PermuteAttrs
|
||||
from openvino.tools.mo.utils.error import Error
|
||||
@@ -23,6 +23,7 @@ class Gather(Op):
|
||||
'batch_dims': 0,
|
||||
'reinterp_shape': True,
|
||||
'infer': self.infer,
|
||||
'reverse_infer': self.reverse_infer,
|
||||
'force_precision_in_ports': {1: 'int32', 2: 'int64'},
|
||||
'in_ports_count': 3,
|
||||
'out_ports_count': 1,
|
||||
@@ -100,6 +101,43 @@ class Gather(Op):
|
||||
else:
|
||||
node.out_port(0).data.set_shape(out_shape)
|
||||
|
||||
@staticmethod
|
||||
def reverse_infer(node: Node):
|
||||
out_shape = node.out_port(0).data.get_shape()
|
||||
data_shape = node.in_port(0).data.get_shape()
|
||||
indices_shape = node.in_port(1).data.get_shape()
|
||||
batch_dims = node.batch_dims
|
||||
batch_dims = batch_dims + len(indices_shape) if batch_dims < 0 else batch_dims
|
||||
|
||||
axis = node.in_port(2).data.get_value()
|
||||
# axis of Gather could be accepted as both scalar and 1D tensor
|
||||
if isinstance(axis, np.ndarray):
|
||||
axis = axis.item()
|
||||
assert axis is not None, 'axis input is undefined'
|
||||
|
||||
# we can deduce data or indices partial shapes from output shape calculation formula
|
||||
# out_shape = Concat(data_shape[:axis], indices_shape[batch_dims:batch_dims + indices_rank], data_shape[axis + 1:])
|
||||
|
||||
# data partial shape is unknown
|
||||
if out_shape is not None and data_shape is None and indices_shape is not None:
|
||||
out_rank = len(out_shape)
|
||||
indices_rank = len(indices_shape)
|
||||
|
||||
deduced_data_shape = out_shape.tolist(dynamic_dimension_value)
|
||||
for i in range(indices_rank):
|
||||
deduced_data_shape.pop(axis)
|
||||
deduced_data_shape.insert(axis, dynamic_dimension_value)
|
||||
node.in_port(0).data.set_shape(shape_array(deduced_data_shape))
|
||||
|
||||
# indices partial shape is unknown
|
||||
if out_shape is not None and indices_shape is None and data_shape is not None:
|
||||
out_rank = len(out_shape)
|
||||
data_rank = len(data_shape)
|
||||
indices_rank = out_rank + 1 - data_rank + batch_dims
|
||||
|
||||
indices_shape = out_shape[axis:axis + indices_rank]
|
||||
node.in_port(1).data.set_shape(indices_shape)
|
||||
|
||||
|
||||
class AttributedGather(Op):
|
||||
op = 'AttributedGather'
|
||||
@@ -113,6 +151,7 @@ class AttributedGather(Op):
|
||||
'axis': 0,
|
||||
'reinterp_shape': True,
|
||||
'infer': self.infer,
|
||||
# reverse_infer is not needed since is replaced by Gather on the front (AttributedGatherNormalizer)
|
||||
|
||||
'force_precision_in_ports': {1: 'int32'},
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
from openvino.tools.mo.front.common.partial_infer.elemental import copy_shape_infer
|
||||
from openvino.tools.mo.front.common.partial_infer.utils import reverse_bypass_infer
|
||||
from openvino.tools.mo.graph.graph import Graph
|
||||
from openvino.tools.mo.ops.op import Op
|
||||
|
||||
@@ -16,7 +17,8 @@ class GRNOp(Op):
|
||||
'version': 'opset1',
|
||||
'in_ports_count': 1,
|
||||
'out_ports_count': 1,
|
||||
'infer': copy_shape_infer
|
||||
'infer': copy_shape_infer,
|
||||
'reverse_infer': lambda node: reverse_bypass_infer(node, in_ports=[0]),
|
||||
}
|
||||
super().__init__(graph, mandatory_props, attrs)
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
# Copyright (C) 2018-2022 Intel Corporation
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
from openvino.tools.mo.front.common.partial_infer.utils import reverse_bypass_infer
|
||||
from openvino.tools.mo.graph.graph import Graph
|
||||
from openvino.tools.mo.ops.op import Op
|
||||
|
||||
@@ -16,6 +17,7 @@ class Identity(Op):
|
||||
|
||||
'identity': True,
|
||||
'infer': self.infer,
|
||||
'reverse_infer': lambda node: reverse_bypass_infer(node, in_ports=[0]),
|
||||
|
||||
'in_ports_count': 1,
|
||||
'out_ports_count': 1,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
# Copyright (C) 2018-2022 Intel Corporation
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
from openvino.tools.mo.front.common.partial_infer.utils import reverse_bypass_infer
|
||||
from openvino.tools.mo.graph.graph import Graph
|
||||
from openvino.tools.mo.ops.op import Op
|
||||
|
||||
@@ -24,7 +25,7 @@ class LRN(Op):
|
||||
'version': 'opset1',
|
||||
|
||||
'infer': self.infer,
|
||||
|
||||
'reverse_infer': lambda node: reverse_bypass_infer(node, in_ports=[0]),
|
||||
'in_ports_count': 2,
|
||||
'out_ports_count': 1,
|
||||
}, attrs)
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
from openvino.tools.mo.front.common.partial_infer.elemental import copy_shape_infer
|
||||
from openvino.tools.mo.front.common.partial_infer.utils import reverse_bypass_infer
|
||||
from openvino.tools.mo.front.extractor import bool_to_str
|
||||
from openvino.tools.mo.graph.graph import Graph, Node
|
||||
from openvino.tools.mo.graph.perm_inputs import PermuteInputs
|
||||
@@ -24,7 +25,8 @@ class MVN(Op):
|
||||
'eps_mode': None,
|
||||
'in_ports_count': 2,
|
||||
'out_ports_count': 1,
|
||||
'infer': self.infer
|
||||
'infer': self.infer,
|
||||
'reverse_infer': lambda node: reverse_bypass_infer(node, in_ports=[0]),
|
||||
}, attrs)
|
||||
|
||||
def supported_attrs(self):
|
||||
|
||||
@@ -5,7 +5,8 @@ import logging as log
|
||||
|
||||
import numpy as np
|
||||
|
||||
from openvino.tools.mo.front.common.partial_infer.utils import dynamic_dimension, shape_array, dynamic_dimension_value
|
||||
from openvino.tools.mo.front.common.partial_infer.utils import dynamic_dimension, shape_array, dynamic_dimension_value, \
|
||||
set_input_shapes, undefined_shape_of_rank
|
||||
from openvino.tools.mo.front.extractor import bool_to_str
|
||||
from openvino.tools.mo.graph.graph import Node, Graph
|
||||
from openvino.tools.mo.middle.passes.convert_data_type import np_data_type_to_destination_type
|
||||
@@ -22,6 +23,7 @@ class NonMaxSuppression(Op):
|
||||
'op': self.op,
|
||||
'version': 'opset5',
|
||||
'infer': self.infer,
|
||||
'reverse_infer': self.reverse_infer,
|
||||
'output_type': np.int64,
|
||||
'box_encoding': 'corner',
|
||||
'in_ports_count': 5,
|
||||
@@ -116,3 +118,7 @@ class NonMaxSuppression(Op):
|
||||
node.out_port(0).set_data_type(node.output_type)
|
||||
else:
|
||||
node.out_port(0).set_data_type(np.int64)
|
||||
|
||||
@staticmethod
|
||||
def reverse_infer(node):
|
||||
set_input_shapes(node, undefined_shape_of_rank(3), undefined_shape_of_rank(3))
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
import numpy as np
|
||||
|
||||
from openvino.tools.mo.front.common.partial_infer.utils import reverse_bypass_infer
|
||||
from openvino.tools.mo.graph.graph import Graph, Node
|
||||
from openvino.tools.mo.graph.perm_inputs import PermuteInputs
|
||||
from openvino.tools.mo.ops.op import Op
|
||||
@@ -22,7 +23,8 @@ class NormalizeL2Op(Op):
|
||||
'eps_mode': None,
|
||||
'in_ports_count': 2,
|
||||
'out_ports_count': 1,
|
||||
'infer': self.infer
|
||||
'infer': self.infer,
|
||||
'reverse_infer': lambda node: reverse_bypass_infer(node, in_ports=[0]),
|
||||
}, attrs)
|
||||
|
||||
def supported_attrs(self):
|
||||
|
||||
@@ -77,4 +77,3 @@ class Parameter(Op):
|
||||
shape = node.soft_get('shape', None)
|
||||
if shape is None and node.out_port(0).data.get_shape() is not None:
|
||||
node['shape'] = node.out_port(0).data.get_shape()
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
import numpy as np
|
||||
|
||||
from openvino.tools.mo.front.common.layout import get_width_dim, get_height_dim
|
||||
from openvino.tools.mo.front.extractor import attr_getter, bool_to_str
|
||||
from openvino.tools.mo.front.extractor import attr_getter
|
||||
from openvino.tools.mo.graph.graph import Node, Graph
|
||||
from openvino.tools.mo.ops.op import Op
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
# Copyright (C) 2018-2022 Intel Corporation
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
from openvino.tools.mo.front.common.partial_infer.utils import shape_array, dynamic_dimension_value, set_input_shapes
|
||||
from openvino.tools.mo.ops.op import Op
|
||||
|
||||
|
||||
@@ -13,6 +14,7 @@ class ExperimentalDetectronPriorGridGenerator(Op):
|
||||
op=self.op,
|
||||
version='opset6',
|
||||
infer=self.infer,
|
||||
reverse_infer=self.reverse_infer,
|
||||
)
|
||||
super().__init__(graph, mandatory_props, attrs)
|
||||
|
||||
@@ -36,3 +38,11 @@ class ExperimentalDetectronPriorGridGenerator(Op):
|
||||
else:
|
||||
out_shape = [grid_h, grid_w, priors_num, 4]
|
||||
node.out_port(0).data.set_shape(out_shape)
|
||||
|
||||
@staticmethod
|
||||
def reverse_infer(node):
|
||||
priors_shape = shape_array([dynamic_dimension_value, 4])
|
||||
feature_map_shape = shape_array([1, dynamic_dimension_value, dynamic_dimension_value, dynamic_dimension_value])
|
||||
image_shape = shape_array([1, dynamic_dimension_value, dynamic_dimension_value, dynamic_dimension_value])
|
||||
|
||||
set_input_shapes(node, priors_shape, feature_map_shape, image_shape)
|
||||
@@ -1,7 +1,7 @@
|
||||
# Copyright (C) 2018-2022 Intel Corporation
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
from openvino.tools.mo.front.common.partial_infer.utils import int64_array
|
||||
from openvino.tools.mo.front.common.partial_infer.utils import undefined_shape_of_rank, set_input_shapes
|
||||
from openvino.tools.mo.front.extractor import attr_getter, bool_to_str
|
||||
from openvino.tools.mo.graph.graph import Node, Graph
|
||||
from openvino.tools.mo.ops.op import Op
|
||||
@@ -17,6 +17,7 @@ class ProposalOp(Op):
|
||||
'version': 'opset4',
|
||||
'post_nms_topn': 300, # default in caffe-shared
|
||||
'infer': ProposalOp.proposal_infer,
|
||||
'reverse_infer': self.reverse_infer,
|
||||
'in_ports_count': 3,
|
||||
'out_ports_count': 1 if attrs.get('version') == 'opset1' else 2,
|
||||
'normalize': False,
|
||||
@@ -66,3 +67,7 @@ class ProposalOp(Op):
|
||||
# the second optional output contains box probabilities
|
||||
if len(node.out_ports()) == 2 and not node.out_port(1).disconnected():
|
||||
node.out_port(1).data.set_shape([input_shape[0] * node.post_nms_topn])
|
||||
|
||||
@staticmethod
|
||||
def reverse_infer(node):
|
||||
set_input_shapes(node, undefined_shape_of_rank(4), undefined_shape_of_rank(4))
|
||||
@@ -1,6 +1,8 @@
|
||||
# Copyright (C) 2018-2022 Intel Corporation
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
from openvino.tools.mo.front.common.partial_infer.utils import dynamic_dimension_value, shape_array, \
|
||||
undefined_shape_of_rank, set_input_shapes
|
||||
from openvino.tools.mo.ops.op import Op
|
||||
|
||||
|
||||
@@ -12,6 +14,7 @@ class ExperimentalDetectronGenerateProposalsSingleImage(Op):
|
||||
type=self.op,
|
||||
op=self.op,
|
||||
version='experimental',
|
||||
reverse_infer=self.reverse_infer,
|
||||
infer=self.infer
|
||||
)
|
||||
|
||||
@@ -29,3 +32,11 @@ class ExperimentalDetectronGenerateProposalsSingleImage(Op):
|
||||
def infer(node):
|
||||
node.out_port(0).data.set_shape([node.post_nms_count, 4])
|
||||
node.out_port(1).data.set_shape([node.post_nms_count])
|
||||
|
||||
@staticmethod
|
||||
def reverse_infer(node):
|
||||
set_input_shapes(node,
|
||||
undefined_shape_of_rank(1),
|
||||
shape_array([dynamic_dimension_value, 4]),
|
||||
undefined_shape_of_rank(3),
|
||||
undefined_shape_of_rank(3))
|
||||
@@ -2,6 +2,8 @@
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
from openvino.tools.mo.front.common.layout import get_batch_dim, shape_for_layout
|
||||
from openvino.tools.mo.front.common.partial_infer.utils import dynamic_dimension_value, shape_array, \
|
||||
undefined_shape_of_rank, set_input_shapes
|
||||
from openvino.tools.mo.graph.graph import Node, Graph
|
||||
from openvino.tools.mo.ops.op import Op
|
||||
|
||||
@@ -20,6 +22,7 @@ class PSROIPoolingOp(Op):
|
||||
'out_ports_count': 1,
|
||||
'trans_std': 0,
|
||||
'no_trans': True,
|
||||
'reverse_infer': self.reverse_infer,
|
||||
'infer': PSROIPoolingOp.psroipooling_infer
|
||||
}
|
||||
super().__init__(graph, mandatory_props, attrs)
|
||||
@@ -57,6 +60,10 @@ class PSROIPoolingOp(Op):
|
||||
height=node.group_size,
|
||||
width=node.group_size)
|
||||
|
||||
@staticmethod
|
||||
def reverse_infer(node):
|
||||
set_input_shapes(node, undefined_shape_of_rank(4), shape_array([dynamic_dimension_value, 5]))
|
||||
|
||||
|
||||
class DeformablePSROIPoolingOp(PSROIPoolingOp):
|
||||
op = 'DeformablePSROIPooling'
|
||||
@@ -76,3 +83,13 @@ class DeformablePSROIPoolingOp(PSROIPoolingOp):
|
||||
|
||||
def supported_attrs(self):
|
||||
return super().supported_attrs() + ['trans_std', 'part_size']
|
||||
|
||||
@staticmethod
|
||||
def reverse_infer(node):
|
||||
transformation_values_shape = shape_array(
|
||||
[dynamic_dimension_value, dynamic_dimension_value, int(node.group_size), int(node.group_size)])
|
||||
|
||||
set_input_shapes(node,
|
||||
undefined_shape_of_rank(4),
|
||||
shape_array([dynamic_dimension_value, 5]),
|
||||
transformation_values_shape)
|
||||
@@ -5,7 +5,8 @@ import numpy as np
|
||||
|
||||
from openvino.tools.mo.front.caffe.extractors.utils import get_canonical_axis_index
|
||||
from openvino.tools.mo.front.common.layout import get_batch_dim, get_height_dim, get_width_dim, shape_for_layout
|
||||
from openvino.tools.mo.front.common.partial_infer.utils import is_fully_defined, dynamic_dimension_value
|
||||
from openvino.tools.mo.front.common.partial_infer.utils import is_fully_defined, dynamic_dimension_value, \
|
||||
undefined_shape_of_rank
|
||||
from openvino.tools.mo.front.extractor import attr_getter, bool_to_str
|
||||
from openvino.tools.mo.graph.graph import Node, Graph
|
||||
from openvino.tools.mo.ops.op import Op
|
||||
@@ -22,6 +23,7 @@ class RegionYoloOp(Op):
|
||||
'version': 'opset1',
|
||||
'in_ports_count': 1,
|
||||
'out_ports_count': 1,
|
||||
'reverse_infer': self.reverse_infer,
|
||||
'infer': RegionYoloOp.regionyolo_infer
|
||||
}
|
||||
super().__init__(graph, mandatory_props, attrs)
|
||||
@@ -73,3 +75,8 @@ class RegionYoloOp(Op):
|
||||
features=(node.classes + node.coords + 1) * len(node.mask),
|
||||
height=input_shape[get_height_dim(layout, 4)],
|
||||
width=input_shape[get_width_dim(layout, 4)]))
|
||||
|
||||
@staticmethod
|
||||
def reverse_infer(node):
|
||||
if node.in_port(0).data.get_shape() is None:
|
||||
node.in_port(0).data.set_shape(undefined_shape_of_rank(4))
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
import numpy as np
|
||||
|
||||
from openvino.tools.mo.front.common.partial_infer.utils import undefined_shape_of_rank
|
||||
from openvino.tools.mo.graph.graph import Node, Graph
|
||||
from openvino.tools.mo.ops.op import Op, PermuteAttrs
|
||||
from openvino.tools.mo.utils.error import Error
|
||||
@@ -16,6 +17,7 @@ class ReorgYoloOp(Op):
|
||||
'type': self.op,
|
||||
'op': self.op,
|
||||
'version': 'opset2',
|
||||
'reverse_infer': self.reverse_infer,
|
||||
'infer': ReorgYoloOp.reorgyolo_infer
|
||||
}
|
||||
super().__init__(graph, mandatory_props, attrs)
|
||||
@@ -41,3 +43,8 @@ class ReorgYoloOp(Op):
|
||||
|
||||
node.out_port(0).data.set_shape(output_shape)
|
||||
PermuteAttrs.create_permute_attrs(node, attrs=[('channel_dims', 'input:0'), ('spatial_dims', 'input:0')])
|
||||
|
||||
@staticmethod
|
||||
def reverse_infer(node):
|
||||
if node.in_port(0).data.get_shape() is None:
|
||||
node.in_port(0).data.set_shape(undefined_shape_of_rank(4))
|
||||
@@ -1,6 +1,7 @@
|
||||
# Copyright (C) 2018-2022 Intel Corporation
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
from openvino.tools.mo.front.common.partial_infer.utils import reverse_bypass_infer
|
||||
from openvino.tools.mo.graph.graph import Graph
|
||||
from openvino.tools.mo.ops.op import Op, PermuteAttrs
|
||||
|
||||
@@ -18,6 +19,7 @@ class ReverseSequence(Op):
|
||||
'in_ports_count': 2,
|
||||
'out_ports_count': 1,
|
||||
'infer': self.infer,
|
||||
'reverse_infer': lambda node: reverse_bypass_infer(node, in_ports=[0]),
|
||||
}
|
||||
super().__init__(graph, mandatory_props, attrs)
|
||||
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
from openvino.tools.mo.front.common.layout import get_features_dim, shape_for_layout
|
||||
from openvino.tools.mo.front.common.partial_infer.utils import compatible_dims
|
||||
from openvino.tools.mo.front.common.partial_infer.utils import dynamic_dimension_value, shape_array, \
|
||||
undefined_shape_of_rank, set_input_shapes, compatible_dims
|
||||
from openvino.tools.mo.graph.graph import Graph
|
||||
from openvino.tools.mo.ops.op import Op
|
||||
|
||||
@@ -24,6 +25,7 @@ class ROIAlign(Op):
|
||||
'version': 'opset3',
|
||||
|
||||
'infer': self.infer,
|
||||
'reverse_infer': self.reverse_infer,
|
||||
|
||||
'in_ports_count': 3,
|
||||
'out_ports_count': 1,
|
||||
@@ -69,3 +71,10 @@ class ROIAlign(Op):
|
||||
height=node.pooled_h,
|
||||
width=node.pooled_w)
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def reverse_infer(node):
|
||||
set_input_shapes(node,
|
||||
undefined_shape_of_rank(4),
|
||||
shape_array([dynamic_dimension_value, 4]),
|
||||
undefined_shape_of_rank(1))
|
||||
@@ -1,6 +1,7 @@
|
||||
# Copyright (C) 2018-2022 Intel Corporation
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
from openvino.tools.mo.front.common.partial_infer.utils import dynamic_dimension_value, shape_array, set_input_shapes
|
||||
from openvino.tools.mo.ops.op import Op
|
||||
|
||||
|
||||
@@ -13,6 +14,7 @@ class ExperimentalDetectronROIFeatureExtractor(Op):
|
||||
op=self.op,
|
||||
version='opset6',
|
||||
infer=self.infer,
|
||||
reverse_infer=self.reverse_infer,
|
||||
out_ports_count=2,
|
||||
)
|
||||
|
||||
@@ -34,3 +36,9 @@ class ExperimentalDetectronROIFeatureExtractor(Op):
|
||||
node.out_port(0).data.set_shape([rois_num, channels_num, node.output_size, node.output_size])
|
||||
if not node.out_port(1).disconnected():
|
||||
node.out_port(1).data.set_shape([rois_num, 4])
|
||||
|
||||
@staticmethod
|
||||
def reverse_infer(node):
|
||||
set_input_shapes(node,
|
||||
shape_array([dynamic_dimension_value, 4]),
|
||||
shape_array([1, dynamic_dimension_value, dynamic_dimension_value, dynamic_dimension_value]))
|
||||
@@ -2,6 +2,8 @@
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
from openvino.tools.mo.front.common.partial_infer.roipooling import roipooling_infer
|
||||
from openvino.tools.mo.front.common.partial_infer.utils import dynamic_dimension_value, shape_array, \
|
||||
undefined_shape_of_rank, set_input_shapes
|
||||
from openvino.tools.mo.ops.op import Op
|
||||
|
||||
|
||||
@@ -19,9 +21,14 @@ class ROIPooling(Op):
|
||||
'spatial_scale': 0.0625,
|
||||
'method': 'max',
|
||||
'infer': roipooling_infer,
|
||||
'reverse_infer': self.reverse_infer,
|
||||
'in_ports_count': 2,
|
||||
'out_ports_count': 1,
|
||||
}, attrs)
|
||||
|
||||
def supported_attrs(self):
|
||||
return ['pooled_h', 'pooled_w', 'spatial_scale', 'method']
|
||||
|
||||
@staticmethod
|
||||
def reverse_infer(node):
|
||||
set_input_shapes(node, undefined_shape_of_rank(4), shape_array([dynamic_dimension_value, 5]))
|
||||
@@ -2,6 +2,7 @@
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
from openvino.tools.mo.front.common.partial_infer.elemental import copy_shape_infer
|
||||
from openvino.tools.mo.front.common.partial_infer.utils import reverse_bypass_infer
|
||||
from openvino.tools.mo.graph.graph import Graph, Node
|
||||
from openvino.tools.mo.graph.perm_inputs import PermuteInputs
|
||||
from openvino.tools.mo.ops.op import Op
|
||||
@@ -20,6 +21,7 @@ class Roll(Op):
|
||||
'op': self.op,
|
||||
'version': 'opset7',
|
||||
'infer': roll_infer,
|
||||
'reverse_infer': lambda node: reverse_bypass_infer(node, in_ports=[0]),
|
||||
'in_ports_count': 3,
|
||||
'out_ports_count': 1
|
||||
}, attrs)
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
import numpy as np
|
||||
|
||||
from openvino.tools.mo.front.common.partial_infer.utils import compatible_shapes
|
||||
from openvino.tools.mo.front.common.partial_infer.utils import compatible_shapes, reverse_bypass_infer
|
||||
from openvino.tools.mo.graph.graph import Node, Graph
|
||||
from openvino.tools.mo.ops.op import Op
|
||||
|
||||
@@ -25,6 +25,7 @@ class Scatter(Op):
|
||||
|
||||
'is_scatter': True, # is used for gathering all types of scatters in common transformations
|
||||
'infer': self.infer,
|
||||
'reverse_infer': lambda node: reverse_bypass_infer(node, in_ports=[0]),
|
||||
|
||||
'in_ports_count': 4,
|
||||
'out_ports_count': 1,
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
import numpy as np
|
||||
|
||||
from openvino.tools.mo.front.caffe.extractors.utils import get_canonical_axis_index
|
||||
from openvino.tools.mo.front.common.partial_infer.utils import int64_array
|
||||
from openvino.tools.mo.front.common.partial_infer.utils import int64_array, shape_array, is_fully_defined
|
||||
from openvino.tools.mo.graph.graph import Graph
|
||||
from openvino.tools.mo.ops.op import Op
|
||||
|
||||
@@ -29,7 +29,7 @@ class SliceLike(Op):
|
||||
input_value = node.in_port(0).data.get_value()
|
||||
shape_like = node.in_port(1).data.get_shape()
|
||||
|
||||
new_shape = np.copy(input_shape)
|
||||
new_shape = shape_array(input_shape.copy())
|
||||
if node.axes is not None:
|
||||
node.axes = sorted([get_canonical_axis_index(input_shape, i) for i in node.axes])
|
||||
for i in node.axes:
|
||||
@@ -38,10 +38,10 @@ class SliceLike(Op):
|
||||
assert input_shape.size == shape_like.size,\
|
||||
'Input shape ranks are inconsistent: {} and {}'.format(input_shape.size, shape_like.size)
|
||||
node.axes = int64_array(range(shape_like.size))
|
||||
new_shape = np.copy(shape_like)
|
||||
new_shape = shape_like.copy()
|
||||
node.out_port(0).data.set_shape(new_shape)
|
||||
|
||||
if input_value is not None:
|
||||
if input_value is not None and is_fully_defined(new_shape):
|
||||
out_value = np.copy(input_value)
|
||||
|
||||
slice_indexes = []
|
||||
|
||||
@@ -5,7 +5,8 @@ import logging as log
|
||||
|
||||
import numpy as np
|
||||
|
||||
from openvino.tools.mo.front.common.partial_infer.utils import int64_array, is_fully_defined, dynamic_dimension, shape_delete
|
||||
from openvino.tools.mo.front.common.partial_infer.utils import is_fully_defined, dynamic_dimension, shape_delete, \
|
||||
clarify_partial_shape, shape_array
|
||||
from openvino.tools.mo.graph.graph import Graph, Node
|
||||
from openvino.tools.mo.graph.perm_inputs import PermuteInputs
|
||||
from openvino.tools.mo.ops.op import Op, PermuteAttrs
|
||||
@@ -107,6 +108,18 @@ class VariadicSplitBase(Op):
|
||||
elif op == 'AttributedVariadicSplit':
|
||||
PermuteAttrs.create_permute_attrs(node, attrs=[('axis', 'input:0')])
|
||||
|
||||
@staticmethod
|
||||
def reverse_infer(node: Node):
|
||||
if node.in_port(0).data.get_shape() is not None:
|
||||
return
|
||||
|
||||
axis = node.in_port(1).data.get_value() if node.op == 'VariadicSplit' else node.soft_get('axis', None)
|
||||
assert axis is not None, '{} `axis` is unknown for node {}'.format(node.op, node.soft_get('name', node.id))
|
||||
split_lengths = node.in_port(2).data.get_value() if node.op == 'VariadicSplit' else node.soft_get('split_lengths', None)
|
||||
assert split_lengths is not None, '{} `split_lengths` is unknown for node {}'.format(node.op, node.soft_get('name', node.id))
|
||||
|
||||
split_reverse_infer(node, len(split_lengths), axis)
|
||||
|
||||
|
||||
class VariadicSplit(VariadicSplitBase):
|
||||
op = 'VariadicSplit'
|
||||
@@ -129,6 +142,7 @@ class VariadicSplit(VariadicSplitBase):
|
||||
'type': self.op,
|
||||
|
||||
'infer': self.infer,
|
||||
'reverse_infer': self.reverse_infer,
|
||||
|
||||
'in_ports_count': 3,
|
||||
}, attrs)
|
||||
@@ -154,6 +168,7 @@ class AttributedVariadicSplit(VariadicSplitBase):
|
||||
'version': 'opset1',
|
||||
|
||||
'infer': self.infer,
|
||||
'reverse_infer': self.reverse_infer,
|
||||
|
||||
'in_ports_count': 1,
|
||||
}, attrs)
|
||||
@@ -212,6 +227,16 @@ class SplitBase(Op):
|
||||
elif op == 'AttributedSplit':
|
||||
PermuteAttrs.create_permute_attrs(node, attrs=[('axis', 'input:0')])
|
||||
|
||||
@staticmethod
|
||||
def reverse_infer(node: Node):
|
||||
if node.in_port(0).data.get_shape() is not None:
|
||||
return
|
||||
|
||||
assert hasattr(node, 'num_splits')
|
||||
axis = node.in_port(1).data.get_value() if node.op == 'Split' else node.soft_get('axis', None)
|
||||
assert axis is not None, '{} `axis` is unknown for node {}'.format(node.op, node.soft_get('name', node.id))
|
||||
split_reverse_infer(node, node['num_splits'], axis)
|
||||
|
||||
|
||||
class Split(SplitBase):
|
||||
op = 'Split'
|
||||
@@ -227,6 +252,7 @@ class Split(SplitBase):
|
||||
'version': 'opset1',
|
||||
|
||||
'infer': self.infer,
|
||||
'reverse_infer': self.reverse_infer,
|
||||
|
||||
'in_ports_count': 2,
|
||||
}, attrs)
|
||||
@@ -255,6 +281,7 @@ class AttributedSplit(SplitBase):
|
||||
'axis': 1,
|
||||
|
||||
'infer': self.infer,
|
||||
'reverse_infer': self.reverse_infer,
|
||||
|
||||
'in_ports_count': 1,
|
||||
}, attrs)
|
||||
@@ -263,3 +290,32 @@ class AttributedSplit(SplitBase):
|
||||
|
||||
def supported_attrs(self):
|
||||
return ['num_splits', 'axis']
|
||||
|
||||
|
||||
def split_reverse_infer(node: Node, num_splits: int, axis: int):
|
||||
aggregated_size_along_axis = 0
|
||||
shapes = []
|
||||
for i in range(num_splits):
|
||||
shape = node.out_port(i).data.get_shape()
|
||||
if shape is not None:
|
||||
# if out_shape_1 = [dyn, 4, 3], out_shape_2 = [7, dyn, 3], axis = 2
|
||||
# to get the original source shape [7, 4, 6]
|
||||
# dimensions along axis must be summed while
|
||||
# for other dimensions clarify_partial_shape should be called
|
||||
aggregated_size_along_axis += shape[axis]
|
||||
# in order to be able to call clarify_partial_shape axis dimension is masked into dynamic
|
||||
shape[axis] = dynamic_dimension
|
||||
shapes.append(shape_array(shape))
|
||||
else:
|
||||
# if at least one output shape is None/undefined
|
||||
# set value of shape along axis into dynamic
|
||||
# dynamic_dimension + static_value = dynamic_dimension
|
||||
aggregated_size_along_axis = dynamic_dimension
|
||||
continue
|
||||
|
||||
if len(shapes) == 0:
|
||||
return
|
||||
|
||||
res_partial_shape = clarify_partial_shape(shapes)
|
||||
res_partial_shape[axis] = aggregated_size_along_axis
|
||||
node.in_port(0).data.set_shape(res_partial_shape)
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
import numpy as np
|
||||
|
||||
from openvino.tools.mo.front.common.partial_infer.utils import shape_array
|
||||
from openvino.tools.mo.graph.graph import Node, Graph
|
||||
from openvino.tools.mo.ops.op import PermuteAttrs, Op
|
||||
|
||||
@@ -15,6 +16,7 @@ class SwapAxis(Op):
|
||||
super().__init__(graph, {
|
||||
'op': self.op,
|
||||
'infer': self.infer,
|
||||
'reverse_infer': self.reverse_infer,
|
||||
'in_ports_count': 1,
|
||||
'out_ports_count': 1,
|
||||
}, attrs)
|
||||
@@ -30,3 +32,11 @@ class SwapAxis(Op):
|
||||
node.out_port(0).data.set_value(np.transpose(node.in_port(0).data.get_value(), axes=node.order))
|
||||
|
||||
PermuteAttrs.create_permute_attrs(node, attrs=[('order', 'input:0')])
|
||||
|
||||
@staticmethod
|
||||
def reverse_infer(node: Node):
|
||||
output_shape = node.out_port(0).data.get_shape()
|
||||
if node.in_port(0).data.get_shape() is None and output_shape is not None:
|
||||
input_shape = output_shape.data.copy()
|
||||
input_shape[node.dim2], input_shape[node.dim1] = input_shape[node.dim1], input_shape[node.dim2]
|
||||
node.in_port(0).data.set_shape(shape_array(input_shape))
|
||||
@@ -1,6 +1,7 @@
|
||||
# Copyright (C) 2018-2022 Intel Corporation
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
from openvino.tools.mo.front.common.partial_infer.utils import dynamic_dimension_value, shape_array, set_input_shapes
|
||||
from openvino.tools.mo.ops.op import Op
|
||||
|
||||
|
||||
@@ -12,6 +13,7 @@ class ExperimentalDetectronTopKROIs(Op):
|
||||
type=self.op,
|
||||
op=self.op,
|
||||
version='experimental',
|
||||
reverse_infer=self.reverse_infer,
|
||||
infer=self.infer
|
||||
)
|
||||
super().__init__(graph, mandatory_props, attrs)
|
||||
@@ -22,3 +24,7 @@ class ExperimentalDetectronTopKROIs(Op):
|
||||
@staticmethod
|
||||
def infer(node):
|
||||
node.out_port(0).data.set_shape([node.max_rois, 4])
|
||||
|
||||
@staticmethod
|
||||
def reverse_infer(node):
|
||||
set_input_shapes(node, shape_array([dynamic_dimension_value, 4]), shape_array([dynamic_dimension_value]))
|
||||
@@ -3,7 +3,8 @@
|
||||
|
||||
import numpy as np
|
||||
|
||||
from openvino.tools.mo.graph.graph import Graph
|
||||
from openvino.tools.mo.front.common.partial_infer.utils import shape_array
|
||||
from openvino.tools.mo.graph.graph import Graph, Node
|
||||
from openvino.tools.mo.ops.op import Op
|
||||
|
||||
|
||||
@@ -17,13 +18,14 @@ class Transpose(Op):
|
||||
'op': self.op,
|
||||
'version': 'opset1',
|
||||
'infer': self.infer,
|
||||
'reverse_infer': self.reverse_infer,
|
||||
'force_precision_in_ports': {1: 'int64'},
|
||||
'in_ports_count': 2,
|
||||
'out_ports_count': 1,
|
||||
}, attrs)
|
||||
|
||||
@staticmethod
|
||||
def infer(node):
|
||||
def infer(node: Node):
|
||||
# order parameter calculation and checks
|
||||
in_ports = node.in_ports()
|
||||
connected_ports = [port for port in in_ports.values() if not port.disconnected()]
|
||||
@@ -49,4 +51,16 @@ class Transpose(Op):
|
||||
else:
|
||||
node.out_port(0).data.set_shape(input_shape[order])
|
||||
|
||||
@staticmethod
|
||||
def reverse_infer(node: Node):
|
||||
input_shape = node.in_port(0).data.get_shape()
|
||||
output_shape = node.out_port(0).data.get_shape()
|
||||
order = node.in_port(1).data.get_value()
|
||||
|
||||
if input_shape is None and output_shape is not None and order is not None:
|
||||
output_shape_unmasked = output_shape.data.copy()
|
||||
input_shape_unmasked = output_shape.data.copy()
|
||||
for curr_out_size, order_axis in zip(output_shape_unmasked, order):
|
||||
input_shape_unmasked[order_axis] = curr_out_size
|
||||
input_shape = shape_array(input_shape_unmasked)
|
||||
node.in_port(0).data.set_shape(input_shape)
|
||||
@@ -7,8 +7,10 @@ import numpy as np
|
||||
|
||||
from openvino.tools.mo.back.ConvolutionNormalizer import PullReshapeThroughFQ, V7ConvolutionWithGroupsResolver, \
|
||||
V10ConvolutionWithGroupsResolver
|
||||
from openvino.tools.mo.front.common.partial_infer.utils import int64_array, shape_array, dynamic_dimension_value
|
||||
from openvino.tools.mo.graph.graph import Node
|
||||
from openvino.tools.mo.ops.const import Const
|
||||
from openvino.tools.mo.ops.fakequantize import FakeQuantize
|
||||
from openvino.tools.mo.front.common.partial_infer.utils import int64_array
|
||||
from openvino.tools.mo.ops.reshape import Reshape
|
||||
from openvino.tools.mo.utils.ir_engine.compare_graphs import compare_graphs
|
||||
from unit_tests.utils.graph import build_graph, result, regular_op_with_shaped_data, regular_op_with_empty_data, \
|
||||
@@ -106,7 +108,7 @@ class TestV7ConvolutionWithGroupsResolver(unittest.TestCase):
|
||||
|
||||
**valued_const_with_data('weights', np.ones([3, 8, 7, 7])),
|
||||
|
||||
**valued_const_with_data('dim', int64_array([24, -1, 7, 7])),
|
||||
**valued_const_with_data('dim', int64_array([24, -1, 0, 0])),
|
||||
**regular_op_with_empty_data('reshape', {'type': 'Reshape'}),
|
||||
|
||||
**regular_op_with_shaped_data('convolution', None, {'type': 'Convolution', 'group': 3, 'output': 24}),
|
||||
@@ -155,7 +157,7 @@ class TestV7ConvolutionWithGroupsResolver(unittest.TestCase):
|
||||
|
||||
**valued_const_with_data('weights', np.ones([1, 8, 7, 7])),
|
||||
|
||||
**valued_const_with_data('dim', int64_array([8, -1, 7, 7])),
|
||||
**valued_const_with_data('dim', int64_array([8, -1, 0, 0])),
|
||||
**regular_op_with_empty_data('reshape', {'type': 'Reshape'}),
|
||||
|
||||
**regular_op_with_shaped_data('convolution', None, {'type': 'Convolution', 'group': 1, 'output': 8,
|
||||
@@ -253,3 +255,78 @@ class TestV10ConvolutionWithGroupsResolver(unittest.TestCase):
|
||||
|
||||
(flag, resp) = compare_graphs(graph, graph_ref, last_node='output', check_op_attrs=True)
|
||||
self.assertTrue(flag, resp)
|
||||
|
||||
|
||||
class TestConvolutionWithGroupsResolverForDynamicWeightsChannels(unittest.TestCase):
|
||||
def test_v10_group_convolution_resolver_for_dynamic_weights(self):
|
||||
num_groups = 2
|
||||
C_OUT = 8
|
||||
|
||||
nodes = {
|
||||
**regular_op_with_shaped_data('input', shape_array([1, dynamic_dimension_value, 224, 224]), {'type': 'Parameter'}),
|
||||
|
||||
**valued_const_with_data('weights', np.ones([num_groups, C_OUT, 7, 7])),
|
||||
|
||||
**regular_op_with_empty_data('reshape', {'type': 'Reshape'}),
|
||||
|
||||
**regular_op_with_empty_data('ss', {'type': 'StridedSlice',
|
||||
'begin_mask': [1], 'end_mask': [0],
|
||||
'new_axis_mask': [0], 'shrink_axis_mask': [0], 'ellipsis_mask': [0]}),
|
||||
|
||||
**regular_op_with_empty_data('weights_shape', {'type': 'ShapeOf'}),
|
||||
**regular_op_with_empty_data('input_shape', {'type': 'ShapeOf'}),
|
||||
**regular_op_with_empty_data('gather', {'type': 'Gather'}),
|
||||
**regular_op_with_empty_data('concat', {'type': 'Concat'}),
|
||||
**regular_op_with_empty_data('div', {'type': 'Divide'}),
|
||||
**valued_const_with_data('channels_const', int64_array([num_groups, C_OUT / num_groups])),
|
||||
**valued_const_with_data('num_groups', int64_array(num_groups)),
|
||||
**valued_const_with_data('begin', int64_array([2])),
|
||||
**valued_const_with_data('end', int64_array([-1])),
|
||||
**valued_const_with_data('channel_index', int64_array([1])),
|
||||
**valued_const_with_data('axis', int64_array(0)),
|
||||
|
||||
**regular_op_with_shaped_data('convolution', None, {'type': 'Convolution', 'group': num_groups, 'output': C_OUT}),
|
||||
|
||||
**result(),
|
||||
}
|
||||
graph = build_graph(nodes, [
|
||||
*connect('input', '0:convolution'),
|
||||
*connect('weights', '1:convolution'),
|
||||
*connect('convolution', 'output'),
|
||||
], nodes_with_edges_only=True)
|
||||
|
||||
V10ConvolutionWithGroupsResolver().find_and_replace_pattern(graph)
|
||||
|
||||
nodes['convolution']['type'] = 'GroupConvolution'
|
||||
del nodes['convolution']['group']
|
||||
|
||||
graph_ref = build_graph(nodes, [
|
||||
*connect('input', '0:convolution'),
|
||||
*connect('weights', '0:reshape'),
|
||||
|
||||
('input_d', 'input_shape', {'in': 0, 'out': 0}),
|
||||
('weights_d', 'weights_shape', {'in': 0, 'out': 0}),
|
||||
|
||||
*connect('input_shape', '0:gather'),
|
||||
*connect('channel_index', '1:gather'),
|
||||
*connect('axis', '2:gather'),
|
||||
|
||||
*connect('weights_shape', '0:ss'),
|
||||
*connect('begin', '1:ss'),
|
||||
*connect('end', '2:ss'),
|
||||
*connect('gather', '0:div'),
|
||||
*connect('num_groups', '1:div'),
|
||||
*connect('channels_const', '0:concat'),
|
||||
*connect('div', '1:concat'),
|
||||
*connect('ss', '2:concat'),
|
||||
*connect('concat', '1:reshape'),
|
||||
|
||||
*connect('reshape', '1:convolution'),
|
||||
*connect('convolution', 'output'),
|
||||
], nodes_with_edges_only=True)
|
||||
|
||||
Const.infer(Node(graph, 'convolution/GroupsAndOutputChannelsSize'))
|
||||
Const.infer(Node(graph, 'convolution/Div_input_port_1/value'))
|
||||
|
||||
(flag, resp) = compare_graphs(graph, graph_ref, last_node='output')
|
||||
self.assertTrue(flag, resp)
|
||||
@@ -6,11 +6,15 @@ import unittest
|
||||
import numpy as np
|
||||
from generator import generator, generate
|
||||
|
||||
from openvino.tools.mo.front.common.partial_infer.eltwise import eltwise_infer
|
||||
from openvino.tools.mo.front.common.partial_infer.utils import shape_array, dynamic_dimension_value, strict_compare_tensors
|
||||
from openvino.tools.mo.front.common.partial_infer.eltwise import eltwise_infer, eltwise_reverse_infer
|
||||
from openvino.tools.mo.front.common.partial_infer.utils import shape_array, strict_compare_tensors, \
|
||||
dynamic_dimension_value
|
||||
from openvino.tools.mo.graph.graph import Node
|
||||
from openvino.tools.mo.middle.passes.infer import partial_infer
|
||||
from openvino.tools.mo.ops.parameter import Parameter
|
||||
from openvino.tools.mo.utils.error import Error
|
||||
from unit_tests.utils.graph import build_graph
|
||||
from unit_tests.utils.graph import result, regular_op_with_empty_data, connect, \
|
||||
build_graph, shaped_parameter
|
||||
|
||||
nodes_attributes = {'node_1': {'value': 2, 'kind': 'data'},
|
||||
'node_2': {'value': 3, 'kind': 'data'},
|
||||
@@ -105,3 +109,99 @@ class TestEltwiseInfer(unittest.TestCase):
|
||||
|
||||
with self.assertRaisesRegex(Error, 'Input shapes mismatch*'):
|
||||
eltwise_infer(eltwise_node)
|
||||
|
||||
|
||||
dyn = dynamic_dimension_value
|
||||
|
||||
|
||||
class TestElementwiseReverseInfer(unittest.TestCase):
|
||||
@staticmethod
|
||||
def build_and_test_reverse_inference(inp_shape_1, inp_shape_2, out_shape, ref_shape, auto_broadcast='numpy'):
|
||||
in_port_with_defined_shape = 0 if inp_shape_1 is not None else 1
|
||||
defined_shape = shape_array(inp_shape_1 if inp_shape_1 is not None else inp_shape_2)
|
||||
|
||||
nodes = {
|
||||
**shaped_parameter('undefined_shape_data', None, {'reverse_infer': Parameter.reverse_infer}),
|
||||
**shaped_parameter('data', shape_array(defined_shape), {'reverse_infer': Parameter.reverse_infer}),
|
||||
**regular_op_with_empty_data('elementwise', {'op': 'Add', 'type': 'Add',
|
||||
'infer': eltwise_infer,
|
||||
'reverse_infer': eltwise_reverse_infer,
|
||||
'auto_broadcast': auto_broadcast}),
|
||||
**result('res'),
|
||||
}
|
||||
|
||||
edges = [
|
||||
*connect('undefined_shape_data', '{}:elementwise'.format(int(not in_port_with_defined_shape))),
|
||||
*connect('data', '{}:elementwise'.format(in_port_with_defined_shape)),
|
||||
*connect('elementwise', 'res')
|
||||
]
|
||||
|
||||
graph = build_graph(nodes, edges)
|
||||
graph.stage = 'middle'
|
||||
Node(graph, 'elementwise').out_port(0).data.set_shape(shape_array(out_shape))
|
||||
Node(graph, 'elementwise').in_port(in_port_with_defined_shape).data.set_shape(defined_shape)
|
||||
|
||||
partial_infer(graph)
|
||||
actual_shape = Node(graph, 'undefined_shape_data').out_port(0).data.get_shape()
|
||||
if ref_shape is None:
|
||||
assert actual_shape == ref_shape
|
||||
else:
|
||||
assert strict_compare_tensors(actual_shape, shape_array(ref_shape))
|
||||
|
||||
def test_reverse_infer_1(self):
|
||||
self.build_and_test_reverse_inference(inp_shape_1=[dyn, dyn],
|
||||
inp_shape_2=None,
|
||||
out_shape=[dyn, dyn, dyn, dyn],
|
||||
ref_shape=[dyn, dyn, dyn, dyn])
|
||||
|
||||
def test_reverse_infer_2(self):
|
||||
self.build_and_test_reverse_inference(inp_shape_1=None,
|
||||
inp_shape_2=[dyn, dyn],
|
||||
out_shape=[dyn, dyn, dyn, dyn],
|
||||
ref_shape=[dyn, dyn, dyn, dyn])
|
||||
|
||||
def test_reverse_infer_3(self):
|
||||
self.build_and_test_reverse_inference(inp_shape_1=[1],
|
||||
inp_shape_2=None,
|
||||
out_shape=[dyn, 400, 400, 3],
|
||||
ref_shape=[dyn, 400, 400, 3])
|
||||
|
||||
def test_reverse_infer_4(self):
|
||||
self.build_and_test_reverse_inference(inp_shape_1=[4, 1],
|
||||
inp_shape_2=None,
|
||||
out_shape=[dyn, dyn, 4, 3],
|
||||
ref_shape=[dyn, dyn, dyn, 3])
|
||||
|
||||
def test_reverse_infer_5(self):
|
||||
self.build_and_test_reverse_inference(inp_shape_1=[4, 1],
|
||||
inp_shape_2=None,
|
||||
out_shape=[dyn, dyn, 4, 1],
|
||||
ref_shape=[dyn, dyn, dyn, 1])
|
||||
|
||||
def test_reverse_infer_6(self):
|
||||
# both output and input has the same rank, cannot deduce other inputs rank
|
||||
with self.assertRaisesRegex(Error, 'Stopped shape/value propagation'):
|
||||
self.build_and_test_reverse_inference(inp_shape_1=[dyn, dyn, dyn, dyn],
|
||||
inp_shape_2=None,
|
||||
out_shape=[dyn, dyn, 4, 1],
|
||||
ref_shape=None)
|
||||
|
||||
def test_reverse_infer_7(self):
|
||||
self.build_and_test_reverse_inference(inp_shape_1=[4, dyn],
|
||||
inp_shape_2=None,
|
||||
out_shape=[1, dyn, dyn, 1],
|
||||
ref_shape=[1, dyn, dyn, 1])
|
||||
|
||||
def test_reverse_infer_8(self):
|
||||
with self.assertRaisesRegex(AssertionError, "Shapes of Elementwise node '.*' are not compatible"):
|
||||
self.build_and_test_reverse_inference(inp_shape_1=[4, dyn],
|
||||
inp_shape_2=None,
|
||||
out_shape=[1, dyn, 7, 1],
|
||||
ref_shape=None)
|
||||
|
||||
def test_reverse_infer_no_broadcast(self):
|
||||
self.build_and_test_reverse_inference(inp_shape_1=[1, 4, dyn, dyn],
|
||||
inp_shape_2=None,
|
||||
out_shape=[1, dyn, dyn, 1],
|
||||
ref_shape=[1, 4, dyn, 1],
|
||||
auto_broadcast='none')
|
||||
@@ -6,8 +6,9 @@ import unittest
|
||||
import numpy as np
|
||||
from generator import generator, generate
|
||||
|
||||
from openvino.tools.mo.front.common.partial_infer.utils import int64_array, mo_array, is_fully_defined, dynamic_dimension_value, \
|
||||
dynamic_dimension, shape_array, compatible_shapes, shape_delete, shape_insert, strict_compare_tensors
|
||||
from openvino.tools.mo.front.common.partial_infer.utils import int64_array, mo_array, is_fully_defined, \
|
||||
dynamic_dimension_value, dynamic_dimension, shape_array, compatible_shapes, shape_delete, shape_insert, \
|
||||
strict_compare_tensors, clarify_partial_shape
|
||||
from openvino.tools.mo.utils.error import Error
|
||||
|
||||
|
||||
@@ -143,3 +144,12 @@ class mo_array_test(unittest.TestCase):
|
||||
])
|
||||
def test_mo_array_negative(self, data, result):
|
||||
self.assertNotEqual(data.dtype, result.dtype)
|
||||
|
||||
|
||||
class clarify_partial_shape_test(unittest.TestCase):
|
||||
|
||||
def test_clarify_1(self):
|
||||
actual_result = clarify_partial_shape([shape_array([dynamic_dimension, 10, dynamic_dimension]),
|
||||
shape_array([4, dynamic_dimension, dynamic_dimension])])
|
||||
ref_result = shape_array([4, 10, dynamic_dimension])
|
||||
assert strict_compare_tensors(actual_result, ref_result)
|
||||
@@ -83,12 +83,14 @@ class IteratorGetNextAnalysisTest(unittest.TestCase):
|
||||
def test_unsupported_data_type(self):
|
||||
graph = build_graph_with_edge_attrs(
|
||||
{
|
||||
'iter_get_next': {'kind': 'op', 'op': 'IteratorGetNext', 'shapes': shape_array([[2, 2]]),
|
||||
'iter_get_next': {'kind': 'op', 'op': 'IteratorGetNext', 'shapes': [shape_array([2, 2])],
|
||||
'types': [None]},
|
||||
'sub': {'kind': 'op', 'op': 'Sub'},
|
||||
'result': {'kind': 'op', 'op': 'Result'}
|
||||
},
|
||||
[
|
||||
('iter_get_next', 'sub', {'out': 0, 'in': 0}),
|
||||
('sub', 'result', {'out': 0, 'in': 0}),
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
@@ -3,10 +3,12 @@
|
||||
|
||||
import numpy.testing as npt
|
||||
|
||||
from openvino.tools.mo.ops.gather import Gather
|
||||
from openvino.tools.mo.front.common.partial_infer.utils import int64_array
|
||||
from openvino.tools.mo.front.common.partial_infer.utils import int64_array, shape_array, strict_compare_tensors, \
|
||||
dynamic_dimension_value
|
||||
from openvino.tools.mo.graph.graph import Node
|
||||
from openvino.tools.mo.middle.passes.infer import partial_infer
|
||||
from openvino.tools.mo.ops.gather import Gather
|
||||
from openvino.tools.mo.ops.parameter import Parameter
|
||||
from openvino.tools.mo.utils.error import Error
|
||||
from unit_tests.mo.unit_test_with_mocked_telemetry import UnitTestWithMockedTelemetry
|
||||
from unit_tests.utils.graph import valued_const_with_data, result, regular_op_with_empty_data, connect, \
|
||||
@@ -266,3 +268,74 @@ class TestGatherPartialInfer(UnitTestWithMockedTelemetry):
|
||||
data_shape=[3, 4, 7],
|
||||
indices_shape=[3, 4, 2],
|
||||
ref_shape=[3, 4, 2])
|
||||
|
||||
|
||||
dyn = dynamic_dimension_value
|
||||
|
||||
|
||||
class TestElementwiseReverseInfer(UnitTestWithMockedTelemetry):
|
||||
@staticmethod
|
||||
def build_and_test_reverse_inference(data_shape, indices_shape, axis, batch_dims, out_shape, ref_shape):
|
||||
in_port_with_defined_shape = 0 if data_shape is not None else 1
|
||||
defined_shape = shape_array(data_shape if data_shape is not None else indices_shape)
|
||||
|
||||
nodes = {
|
||||
**shaped_parameter('data', data_shape, {'reverse_infer': Parameter.reverse_infer}),
|
||||
**shaped_parameter('indices', indices_shape, {'reverse_infer': Parameter.reverse_infer}),
|
||||
**valued_const_with_data('axis', int64_array(axis)),
|
||||
**regular_op_with_empty_data('gather', {'op': 'Gather', 'batch_dims': batch_dims,
|
||||
'infer': Gather.infer,
|
||||
'reverse_infer': Gather.reverse_infer}),
|
||||
**result('res'),
|
||||
}
|
||||
|
||||
edges = [
|
||||
*connect('data', '0:gather'),
|
||||
*connect('indices', '1:gather'),
|
||||
*connect('axis', '2:gather'),
|
||||
*connect('gather', 'res')
|
||||
]
|
||||
|
||||
graph = build_graph(nodes, edges)
|
||||
graph.stage = 'middle'
|
||||
|
||||
Node(graph, 'gather').out_port(0).data.set_shape(shape_array(out_shape))
|
||||
Node(graph, 'gather').in_port(in_port_with_defined_shape).data.set_shape(defined_shape)
|
||||
|
||||
partial_infer(graph)
|
||||
actual_shape = Node(graph, 'gather').in_port(int(not in_port_with_defined_shape)).data.get_shape()
|
||||
assert strict_compare_tensors(actual_shape, shape_array(ref_shape))
|
||||
|
||||
# undefined indices pshape
|
||||
def test_reverse_infer_1(self):
|
||||
self.build_and_test_reverse_inference(data_shape=[dyn, dyn],
|
||||
indices_shape=None,
|
||||
axis=0,
|
||||
batch_dims=0,
|
||||
out_shape=[dyn, dyn, dyn, dyn],
|
||||
ref_shape=[dyn, dyn, dyn])
|
||||
|
||||
def test_reverse_infer_2(self):
|
||||
self.build_and_test_reverse_inference(data_shape=[3, 10],
|
||||
indices_shape=None,
|
||||
axis=1,
|
||||
batch_dims=0,
|
||||
out_shape=[3, 40, 50, 60],
|
||||
ref_shape=[40, 50, 60])
|
||||
|
||||
# undefined data pshape
|
||||
def test_reverse_infer_3(self):
|
||||
self.build_and_test_reverse_inference(data_shape=None,
|
||||
indices_shape=[4, 5],
|
||||
axis=0,
|
||||
batch_dims=0,
|
||||
out_shape=[4, 5, 10],
|
||||
ref_shape=[dyn, 10])
|
||||
|
||||
def test_reverse_infer_4(self):
|
||||
self.build_and_test_reverse_inference(data_shape=None,
|
||||
indices_shape=[4, 67],
|
||||
axis=1,
|
||||
batch_dims=0,
|
||||
out_shape=[3, 4, 67, 100],
|
||||
ref_shape=[3, dyn, 100])
|
||||
@@ -4,15 +4,15 @@
|
||||
import unittest
|
||||
|
||||
import numpy as np
|
||||
from generator import generator, generate
|
||||
|
||||
from openvino.tools.mo.ops.split import AttributedSplit, AttributedVariadicSplit, VariadicSplit
|
||||
from openvino.tools.mo.front.common.partial_infer.utils import int64_array, shape_array, dynamic_dimension_value, strict_compare_tensors
|
||||
from openvino.tools.mo.front.common.partial_infer.utils import int64_array, shape_array, \
|
||||
dynamic_dimension_value, dynamic_dimension, strict_compare_tensors
|
||||
from openvino.tools.mo.graph.graph import Node
|
||||
from openvino.tools.mo.ops.split import AttributedSplit, AttributedVariadicSplit, VariadicSplit
|
||||
from openvino.tools.mo.utils.ir_engine.compare_graphs import compare_graphs
|
||||
from unit_tests.utils.graph import build_graph
|
||||
|
||||
from generator import generator, generate
|
||||
|
||||
|
||||
class TestSplitOp(unittest.TestCase):
|
||||
nodes = {
|
||||
@@ -324,3 +324,62 @@ class TestVariadicSplitOp(unittest.TestCase):
|
||||
except AssertionError as e:
|
||||
self.assertTrue(e.args[0] == 'VariadicSplit `axis` should be scalar or tensor with shape [1], '
|
||||
'but it`s not for node split_op')
|
||||
|
||||
|
||||
class TestSplitReverseInfer(unittest.TestCase):
|
||||
|
||||
def test_split_reverse_infer(self):
|
||||
ref_input_shape = [7, 4, 6]
|
||||
axis = 2
|
||||
num_splits = 2
|
||||
output_shape_1 = [dynamic_dimension, 4, 3]
|
||||
output_shape_2 = [7, dynamic_dimension, 3]
|
||||
|
||||
graph = build_graph(TestSplitOp.nodes, TestSplitOp.edges,
|
||||
{
|
||||
'split_input_data': {'shape': None,
|
||||
'value': None},
|
||||
'split_op': {'axis': np.array(axis), 'num_splits': np.array(num_splits)},
|
||||
'split_output_0_data': {'shape': shape_array(output_shape_1),
|
||||
'value': None},
|
||||
'split_output_1_data': {'shape': shape_array(output_shape_2),
|
||||
'value': None},
|
||||
})
|
||||
|
||||
split_node = Node(graph, 'split_op')
|
||||
AttributedSplit.reverse_infer(split_node)
|
||||
actual_input_shape = split_node.in_port(0).data.get_shape()
|
||||
self.assertTrue(strict_compare_tensors(ref_input_shape, actual_input_shape))
|
||||
|
||||
|
||||
class TestAttributedVariadicSplitReverseInfer(unittest.TestCase):
|
||||
|
||||
def test_splitv_dynamic_input(self):
|
||||
ref_input_shape = [7, 4, 11]
|
||||
axis = 2
|
||||
num_splits = 2
|
||||
output_shape_1 = [dynamic_dimension, 4, 3]
|
||||
output_shape_2 = [7, dynamic_dimension, 3]
|
||||
output_shape_3 = [7, dynamic_dimension, 5]
|
||||
|
||||
graph = build_graph(TestAttributedVariadicSplitOp.nodes, TestAttributedVariadicSplitOp.edges,
|
||||
{
|
||||
'split_input_data': {'shape': None},
|
||||
'split_op': {'axis': np.array(2), 'split_lengths': np.array([3, 3, 5]),
|
||||
'out_ports_count': 2},
|
||||
'split_output_0_data': {'shape': shape_array(output_shape_1),
|
||||
'value': None},
|
||||
'split_output_1_data': {'shape': shape_array(output_shape_2),
|
||||
'value': None},
|
||||
'split_output_2_data': {'shape': shape_array(output_shape_3),
|
||||
'value': None},
|
||||
}
|
||||
)
|
||||
node = Node(graph, 'split_op')
|
||||
for p in range(len(node.out_edges()), node.out_ports_count):
|
||||
node.add_output_port(p)
|
||||
|
||||
AttributedVariadicSplit.reverse_infer(node)
|
||||
|
||||
actual_input_shape = node.in_port(0).data.get_shape()
|
||||
self.assertTrue(strict_compare_tensors(ref_input_shape, actual_input_shape))
|
||||
@@ -7,9 +7,14 @@ import unittest
|
||||
import numpy as np
|
||||
from generator import generator, generate
|
||||
|
||||
from openvino.tools.mo.ops.transpose import Transpose
|
||||
from openvino.tools.mo.front.common.partial_infer.utils import int64_array, shape_array, strict_compare_tensors, \
|
||||
dynamic_dimension_value
|
||||
from openvino.tools.mo.graph.graph import Node
|
||||
from unit_tests.utils.graph import build_graph
|
||||
from openvino.tools.mo.middle.passes.infer import partial_infer
|
||||
from openvino.tools.mo.ops.parameter import Parameter
|
||||
from openvino.tools.mo.ops.transpose import Transpose
|
||||
from unit_tests.utils.graph import valued_const_with_data, result, regular_op_with_empty_data, connect, \
|
||||
build_graph, shaped_parameter
|
||||
|
||||
input_shape = np.array([1, 3, 224, 224])
|
||||
|
||||
@@ -100,3 +105,48 @@ class TestTransposeOp(unittest.TestCase):
|
||||
transpose_node = Node(graph, 'transpose')
|
||||
transpose_node['reverse_order'] = False
|
||||
self.assertRaises(AssertionError, Transpose.infer, transpose_node)
|
||||
|
||||
|
||||
dyn = dynamic_dimension_value
|
||||
|
||||
|
||||
class TestTransposeReverseInfer(unittest.TestCase):
|
||||
@staticmethod
|
||||
def build_and_test_reverse_inference(order, out_shape, ref_shape):
|
||||
nodes = {
|
||||
**shaped_parameter('data', None, {'reverse_infer': Parameter.reverse_infer}),
|
||||
**valued_const_with_data('order', int64_array(order)),
|
||||
**regular_op_with_empty_data('transpose', {'op': 'Transpose',
|
||||
'infer': Transpose.infer,
|
||||
'reverse_infer': Transpose.reverse_infer}),
|
||||
**result('res'),
|
||||
}
|
||||
|
||||
edges = [
|
||||
*connect('data', '0:transpose'),
|
||||
*connect('order', '1:transpose'),
|
||||
*connect('transpose', 'res')
|
||||
]
|
||||
|
||||
graph = build_graph(nodes, edges)
|
||||
graph.stage = 'middle'
|
||||
Node(graph, 'transpose').out_port(0).data.set_shape(shape_array(out_shape))
|
||||
|
||||
partial_infer(graph)
|
||||
actual_shape = Node(graph, 'data').out_port(0).data.get_shape()
|
||||
assert strict_compare_tensors(actual_shape, shape_array(ref_shape))
|
||||
|
||||
def test_reverse_infer_1(self):
|
||||
self.build_and_test_reverse_inference(order=[0, 3, 1, 2],
|
||||
out_shape=[dyn, dyn, dyn, dyn],
|
||||
ref_shape=[dyn, dyn, dyn, dyn])
|
||||
|
||||
def test_reverse_infer_2(self):
|
||||
self.build_and_test_reverse_inference(order=[0, 3, 1, 2],
|
||||
out_shape=[44, 32, 77, 1],
|
||||
ref_shape=[44, 77, 1, 32])
|
||||
|
||||
def test_reverse_infer_3(self):
|
||||
self.build_and_test_reverse_inference(order=[0, 2, 3, 1],
|
||||
out_shape=[44, 32, 77, 1],
|
||||
ref_shape=[44, 1, 32, 77])
|
||||
Reference in new issue
Block a user