[MO][TF] Fixed TFSliceToSlice conversion error on Win (#4367)
* [MO][TF] Fixed TFSliceToSlice conversion error on Win, replaced int32_max with ShapeOf, minor correction in comments * review apply: added create_op_with_const_inputs * review apply: renamed variables, updated typehints for int64_array, float_array, float32_array * renamed to correct_ends * returned back check_op_attrs=True
This commit is contained in:
1 parent
7ba9723242
commit
162bdde251
5 files changed
+55
-67
No files matched your search
@@ -1,5 +1,5 @@
|
||||
"""
|
||||
Copyright (C) 2018-2020 Intel Corporation
|
||||
Copyright (C) 2018-2021 Intel Corporation
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -19,63 +19,50 @@ import numpy as np
|
||||
from extensions.ops.Cast import Cast
|
||||
from extensions.ops.elementwise import Add, Equal
|
||||
from extensions.ops.select import Select
|
||||
from mo.front.common.partial_infer.utils import int64_array
|
||||
from mo.front.common.replacement import FrontReplacementOp
|
||||
from mo.front.tf.graph_utils import create_op_with_const_inputs
|
||||
from mo.graph.graph import Graph, rename_nodes
|
||||
from mo.ops.const import Const
|
||||
from mo.ops.shape import Shape
|
||||
from mo.ops.slice import Slice
|
||||
|
||||
|
||||
class TFSliceToSliceReplacer(FrontReplacementOp):
|
||||
"""
|
||||
This transformation converts TFSlice to internal Slice operation.
|
||||
In TFSlice size[i] == -1 means take all elements on axis i up to the end including(!) the last
|
||||
In internal MO Slice (which is borrowed from ONNX) -1 means take all excluding(!) the last (shape[i] - 1).
|
||||
Also TFSlice has 'sizes' on the second input while Slice has 'ends'.
|
||||
This transformation was added to avoid multiple if statements in future transformations.
|
||||
TFSlice has 'size' on the second input while Slice has 'ends', therefore we insert Add(begin, size).
|
||||
size[i] == -1 is a magic number that means take the whole range along axis i up to the end.
|
||||
To process the case when size[i] == -1 we insert subgraph with ShapeOf.
|
||||
"""
|
||||
op = 'TFSlice'
|
||||
enabled = True
|
||||
|
||||
def replace_sub_graph(self, graph: Graph, match: dict):
|
||||
node = match['op']
|
||||
slice_name = node.soft_get('name', node.id)
|
||||
tf_slice_node = match['op']
|
||||
slice_name = tf_slice_node.soft_get('name', tf_slice_node.id)
|
||||
slice_node = Slice(graph).create_node()
|
||||
rename_nodes([(node, slice_name + '/to_be_removed'), (slice_node, slice_name)])
|
||||
rename_nodes([(tf_slice_node, slice_name + '/to_be_removed'), (slice_node, slice_name)])
|
||||
ends_node = Add(graph, {'name': slice_name + '/ends'}).create_node()
|
||||
|
||||
eq_node = Equal(graph, {'name': slice_name + '/equal'}).create_node()
|
||||
minus_one_node = Const(graph, {'name': slice_name + '/minus_one', 'value': np.array(-1)}).create_node()
|
||||
int32_max_node = Const(graph, {'name': slice_name + '/int32_max', 'value': np.iinfo(np.int32).max}).create_node()
|
||||
select_node = Select(graph, {'name': slice_name + '/select'}).create_node()
|
||||
# reconnect input, begin, and size from TFSlice to the subgraph with Slice
|
||||
tf_slice_node.in_port(0).get_connection().set_destination(slice_node.in_port(0))
|
||||
tf_slice_node.in_port(1).get_connection().set_destination(slice_node.in_port(1))
|
||||
tf_slice_node.in_port(2).get_connection().set_destination(ends_node.in_port(0))
|
||||
slice_node.in_port(1).get_connection().add_destination(ends_node.in_port(1))
|
||||
|
||||
# node to convert sizes to ends
|
||||
sum_node = Add(graph, {'name': slice_name + '/end_const'}).create_node()
|
||||
max_ends = Shape(graph, {'name': slice_name + '/ShapeOf'}).create_node()
|
||||
slice_node.in_port(0).get_connection().add_destination(max_ends.in_port(0))
|
||||
|
||||
# reconnect input from tfslice to slice
|
||||
node.in_port(0).get_source().connect(slice_node.in_port(0))
|
||||
node.in_port(0).disconnect()
|
||||
# reconnect begin of tfslice to start of slice
|
||||
node.in_port(1).get_source().connect(slice_node.in_port(1))
|
||||
node.in_port(1).disconnect()
|
||||
# check if size[i] == -1, will be applied elementwisely: len(size) = len(begin) = input_rank
|
||||
where_max_ends_is_needed = create_op_with_const_inputs(graph, Equal, {0: int64_array(-1)},
|
||||
{'name': slice_name + '/where_max_ends_is_needed'})
|
||||
ends_node.in_port(0).get_connection().add_destination(where_max_ends_is_needed.in_port(1))
|
||||
# select requires equal dtypes, need to convert ends to I64
|
||||
ends_casted_to_i64 = Cast(graph, {'name': slice_name + '/CastToI64',
|
||||
'dst_type': np.int64}).create_node([ends_node])
|
||||
# if size[i] == 1 then take max_ends values
|
||||
correct_ends = Select(graph, {'name': slice_name + '/chosen_ends'}).create_node([where_max_ends_is_needed,
|
||||
max_ends, ends_casted_to_i64])
|
||||
correct_ends.out_port(0).connect(slice_node.in_port(2))
|
||||
|
||||
# (size -> ends) reconnect begins and sizes to sum to evaluate ends for Slice
|
||||
# connects begins to slice
|
||||
slice_node.in_port(1).get_source().connect(sum_node.in_port(0))
|
||||
node.in_port(2).get_source().connect(sum_node.in_port(1))
|
||||
node.in_port(2).disconnect()
|
||||
|
||||
# if size[i] == -1 when take int32_max as end[i]
|
||||
sum_node.in_port(1).get_source().connect(eq_node.in_port(0))
|
||||
minus_one_node.out_port(0).connect(eq_node.in_port(1))
|
||||
# from equal to 0 port of select
|
||||
eq_node.out_port(0).connect(select_node.in_port(0))
|
||||
# from int32_max to 1 of select
|
||||
int32_max_node.out_port(0).connect(select_node.in_port(1))
|
||||
# from sum to 2nd of select
|
||||
sum_node.out_port(0).connect(select_node.in_port(2))
|
||||
# out of select to end (2nd of slice)
|
||||
select_node.out_port(0).connect(slice_node.in_port(2))
|
||||
|
||||
cast = Cast(graph, dict(name=sum_node.name + '/CastToI64', dst_type=np.int64)).create_node()
|
||||
select_node.in_port(2).get_connection().insert_node(cast)
|
||||
|
||||
node.out_port(0).get_connection().set_source(slice_node.out_port(0))
|
||||
tf_slice_node.out_port(0).get_connection().set_source(slice_node.out_port(0))
|
||||
@@ -1,5 +1,5 @@
|
||||
"""
|
||||
Copyright (C) 2018-2020 Intel Corporation
|
||||
Copyright (C) 2018-2021 Intel Corporation
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -32,7 +32,7 @@ nodes = {
|
||||
|
||||
# nodes after replacement
|
||||
**const('minus_one', np.array(-1)),
|
||||
**const('int32_max', np.array(np.iinfo(np.int32).max)),
|
||||
**regular_op_with_empty_data('shapeof', {'op': 'ShapeOf', 'type': 'ShapeOf'}),
|
||||
**regular_op_with_empty_data('end_const', {'op': 'Add', 'type': 'Add'}),
|
||||
**regular_op_with_empty_data('equal', {'op': 'Equal', 'type': 'Equal'}),
|
||||
**regular_op_with_empty_data('select', {'op': 'Select', 'type': 'Select'}),
|
||||
@@ -57,6 +57,7 @@ class SliceReplacerTest(unittest.TestCase):
|
||||
|
||||
graph_ref = build_graph(nodes_attrs=nodes, edges=[
|
||||
*connect_front('input:0', 'slice'),
|
||||
*connect_front('input:0', 'shapeof'),
|
||||
*connect_front('begin:0', 'slice:1'),
|
||||
*connect_front('begin:0', 'john_doe:1'),
|
||||
|
||||
@@ -64,7 +65,7 @@ class SliceReplacerTest(unittest.TestCase):
|
||||
*connect_front('size:0', 'end_const:1'),
|
||||
*connect_front('size:0', 'equal:0'),
|
||||
|
||||
*connect_front('int32_max:0', 'select:1'),
|
||||
*connect_front('shapeof:0', 'select:1'),
|
||||
*connect_front('minus_one:0', 'equal:1'),
|
||||
|
||||
*connect_front('equal:0', 'select:0'),
|
||||
@@ -81,7 +82,7 @@ class SliceReplacerTest(unittest.TestCase):
|
||||
|
||||
def test_slice_replacer(self):
|
||||
graph = build_graph(nodes_attrs=nodes, edges=[
|
||||
*connect_front('input:0', 'tfslice'),
|
||||
*connect_front('input:0', '0:tfslice'),
|
||||
*connect_front('begin:0', '1:tfslice'),
|
||||
*connect_front('size:0', '2:tfslice'),
|
||||
*connect_front('tfslice:0', 'output'),
|
||||
@@ -92,11 +93,12 @@ class SliceReplacerTest(unittest.TestCase):
|
||||
|
||||
graph_ref = build_graph(nodes_attrs=nodes, edges=[
|
||||
*connect_front('input:0', 'slice'),
|
||||
*connect_front('input:0', 'shapeof'),
|
||||
*connect_front('begin:0', '1:slice'),
|
||||
*connect_front('begin:0', '0:end_const'),
|
||||
*connect_front('size:0', '1:end_const'),
|
||||
*connect_front('size:0', '0:equal'),
|
||||
*connect_front('int32_max:0', '1:select'),
|
||||
*connect_front('shapeof:0', '1:select'),
|
||||
*connect_front('minus_one:0', '1:equal'),
|
||||
*connect_front('equal:0', '0:select'),
|
||||
*connect_front('end_const:0', '0:cast'),
|
||||
|
||||
@@ -20,16 +20,16 @@ from typing import Iterable, List, Union
|
||||
import numpy as np
|
||||
|
||||
|
||||
def int64_array(l: Iterable):
|
||||
return np.array(l, dtype=np.int64)
|
||||
def int64_array(value: Union[Iterable[Union[float, int]], float, int]) -> np.ndarray:
|
||||
return np.array(value, dtype=np.int64)
|
||||
|
||||
|
||||
def float_array(l: list):
|
||||
return np.array(l, dtype=np.float64)
|
||||
def float_array(value: Union[Iterable[Union[float, int]], float, int]) -> np.ndarray:
|
||||
return np.array(value, dtype=np.float64)
|
||||
|
||||
|
||||
def float32_array(l: list):
|
||||
return np.array(l, dtype=np.float32)
|
||||
def float32_array(value: Union[Iterable[Union[float, int]], float, int]) -> np.ndarray:
|
||||
return np.array(value, dtype=np.float32)
|
||||
|
||||
|
||||
def mark_input_bins(node, names=('weights', 'biases'), start_port: int = 1):
|
||||
|
||||
@@ -34,7 +34,7 @@ A number of transformations take place on the front phase to convert framework s
|
||||
class AttributedSlice(Op):
|
||||
"""
|
||||
AttributedSlice is used in old versions of ONNX models (opset version < 10).
|
||||
The operation is replaced with the OpenVINO Slice operation on the front phase.
|
||||
Is replaced with internal Slice on the front phase.
|
||||
"""
|
||||
op = 'AttributedSlice'
|
||||
enabled = False
|
||||
@@ -53,7 +53,7 @@ class CaffeSlice(Op):
|
||||
"""
|
||||
Slice in Caffe is equivalent to Split operation in OpenVINO.
|
||||
https://caffe.berkeleyvision.org/tutorial/layers/slice.html
|
||||
The operation is replaced with the OpenVINO Split operation on the front phase.
|
||||
Is replaced with Split from opset on the front phase.
|
||||
"""
|
||||
op = 'CaffeSlice'
|
||||
enabled = False
|
||||
@@ -71,11 +71,10 @@ class CaffeSlice(Op):
|
||||
|
||||
class TFSlice(Op):
|
||||
"""
|
||||
Slice operation in Tensorflow is different from Slice in ONNX, Caffe and MXNet. It has begin and size inputs while
|
||||
ONNX Slice and internal MO Slice has start, end, step and axis parameters specified as inputs.
|
||||
TFSlice differs from Slice in ONNX, Caffe and MXNet.
|
||||
TFSlice has 'begin' and 'size' inputs while Slice has 'start', 'end', 'step', and 'axis' inputs.
|
||||
https://www.tensorflow.org/api_docs/python/tf/slice
|
||||
The operation is replaced with the internal Slice on the front phase.
|
||||
If size[i] == -1 is replaced to int32_max value for the end.
|
||||
Is replaced with internal Slice op on the front phase.
|
||||
"""
|
||||
op = 'TFSlice'
|
||||
enabled = False
|
||||
@@ -94,7 +93,7 @@ class MXSlice(Op):
|
||||
"""
|
||||
Slice operation in MXNet is different from ONNX, Caffe, Tensorflow. It has begin, end & step attributes
|
||||
https://mxnet.apache.org/versions/1.6/api/python/docs/api/symbol/op/index.html#mxnet.symbol.op.slice
|
||||
The operation is replaced with the OpenVINO StridedSlice operation on the front phase.
|
||||
Is replaced with the StridedSlice from opset on the front phase.
|
||||
"""
|
||||
op = 'MXSlice'
|
||||
enabled = False
|
||||
@@ -112,9 +111,9 @@ class MXSlice(Op):
|
||||
|
||||
class Slice(Op):
|
||||
"""
|
||||
Semantic of MO internal Slice operation is identical to Slice in ONNX opset >= 10.
|
||||
It has starts, ends, steps and axes inputs.
|
||||
The operation is internal (not present in the OpenVINO opset) and is replaced to StridedSlice.
|
||||
Semantic of Slice is identical to Slice in ONNX opset >= 10.
|
||||
It has 'starts', 'ends', 'steps', and 'axes' inputs.
|
||||
SliceConverter replaces it with StridedSlice from opset.
|
||||
"""
|
||||
op = 'Slice'
|
||||
enabled = False
|
||||
|
||||
Reference in new issue
Block a user