openvino/model-optimizer/extensions/back/ReduceTransposeDimensions.py

148 lines
5.9 KiB
Python

"""
Copyright (C) 2018-2020 Intel Corporation
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
"""
import logging as log
import numpy as np
from extensions.back.OptimizeTransposeReshapeSequence import set_reshape_new_output_shape
from mo.back.replacement import BackReplacementPattern
from mo.front.common.partial_infer.utils import int64_array
from mo.graph.graph import Graph
def sequential_dims(order: np.array):
"""
Returns first instance (counting from left) of the sequential dimensions in the 'order'
:param order: order to look for sequential dims
:return: list of indices of the sequential dimensions. If sequential dimensions are not found then return None.
"""
start_ind = cur_ind = 0
while cur_ind + 1 < len(order):
if order[cur_ind] + 1 == order[cur_ind + 1]:
cur_ind += 1
else:
if start_ind < cur_ind:
return list(range(start_ind, cur_ind + 1))
cur_ind += 1
start_ind = cur_ind
if start_ind < cur_ind:
return list(range(start_ind, cur_ind + 1))
return None
def merge_permute_order_dimensions(dims: list, permute_order: np.array):
"""
Creates updated permutation for a given permutation order and the *input* dimension indices to be merged into one.
:param dims: the input tensor dimensions indices to merge
:param permute_order: the permutation order
:return: the new permutation order after merging of the specified dimensions into one
"""
assert len(dims) >= 2
new_permute_order = list()
for permute_index in permute_order:
if permute_index < permute_order[dims[0]]:
new_permute_order.append(permute_index)
elif permute_index > permute_order[dims[-1]]:
new_permute_order.append(permute_index - len(dims) + 1)
elif permute_index == permute_order[dims[0]]:
new_permute_order.append(permute_order[dims[0]])
return int64_array(new_permute_order)
def merge_dims(dims_to_merge: np.array, shape: np.array):
"""
Merge several sequential specified dims into one.
The function does not support magic number "0" in the 'shape'.
:param dims_to_merge: the dimensions indices to merge
:param shape: shape to merge
:return: new shape with merged specified dims
"""
for ind in range(len(dims_to_merge) - 1):
assert dims_to_merge[ind] + 1 == dims_to_merge[ind + 1], 'The dims to merge must be sequential'
assert 0 not in shape, 'The value 0 is not supported during merging of the shape'
result = list()
if dims_to_merge[0] != 0:
result.extend(shape[:dims_to_merge[0]])
# handle magic number "-1"
if -1 in shape[dims_to_merge]:
result.append(-1)
else:
result.append(np.prod(shape[dims_to_merge]))
if dims_to_merge[-1] + 1 != len(shape):
result.extend(shape[dims_to_merge[-1] + 1:])
return int64_array(result)
class ReduceTransposeDimensions(BackReplacementPattern):
"""
Transformation looks for the Transpose layers with sequential dimensions in the permutation order and merges them into
one thus reducing the number of dimensions. The transformation is applied to 5D+ permutations only.
"""
enabled = False
def run_after(self):
from extensions.back.OptimizeTransposeReshapeSequence import OptimizeTransposeReshapeSequence
return [OptimizeTransposeReshapeSequence]
def run_before(self):
from extensions.back.ReshapeMutation import ReshapeMutation
from extensions.back.TransposeToPermute import TransposeToPermute
return [ReshapeMutation, TransposeToPermute]
@staticmethod
def pattern():
return dict(
nodes=[
('reshape_1', dict(kind='op', type='Reshape')), # TODO change to reshape-like
('reshape_1_data', dict(kind='data')),
('permute', dict(kind='op', type='Transpose')),
('permute_data', dict(kind='data')),
('reshape_2', dict(kind='op', type='Reshape')),
],
edges=[
('reshape_1', 'reshape_1_data'),
('reshape_1_data', 'permute'),
('permute', 'permute_data'),
('permute_data', 'reshape_2'),
]
)
@staticmethod
def replace_pattern(graph: Graph, match: dict):
permute_node = match['permute']
reshape_1_node = match['reshape_1']
order = permute_node.in_port(1).data.get_value().copy()
if len(order) >= 5:
log.debug('Trying to merge dimensions of the Transpose layer "{}"'.format(permute_node.soft_get('name')))
seq_dims = sequential_dims(order)
while seq_dims is not None:
permute_input_shape = permute_node.in_port(0).data.get_shape().copy()
# calculate new Transpose order and output of the Reshape layer
new_reshape_dims = merge_dims(order[seq_dims], permute_input_shape)
new_permute_order = merge_permute_order_dimensions(seq_dims, order)
assert reshape_1_node.has('dim')
# update data Transpose and Reshape attributes and data nodes shapes
set_reshape_new_output_shape(reshape_1_node, new_reshape_dims)
permute_node.in_port(1).data.set_value(new_permute_order)
permute_node.infer(permute_node)
order = permute_node.in_port(1).data.get_value().copy()
seq_dims = sequential_dims(order)