* Allow MO to generate IR with -1 in dimensions * Some fixes to support -1 for StridedSlice operation * Updated TensorArrayGatherV3 shape infer to support dynamic output shape * Several fixes to support undefined dimensions in the Broadcast,Reshape,Slice and Tile * Fixed bug in the normalization transformation of TF NMS to opset NMS * Updated shape infer functions related to StridedSlice and NMS * Updated Select shape inference function to use common shape broadcasting function supporting dynamism * Fixed operation TFResize shape infer function to work correctly for case when model is converted with --disable_nhwc_to_nchw * Dynamic Range and update asserts in NMS * Changed the way how dynamic dimensions are specified. Refactored shape inference functions and common places to use new approach * More fixes to support dynamic shapes * More fixes for support of dynamic shapes * Fixed generation of IR with dynamic dimensions * Allow reading IRs with undefined dimensions * More changes in the IE to support dynamic dimensions * Fixes for Switch, Merge, Concat shape and value infer related to dynamism * Fixed TensorArray related ops to properly handle dynamic dimensions. Fixed StridedSlice infer for case with new_axis * Fixed shape_for_layout function to generate masked array * Fixed shape inference for Convolution and Poolings to support dynamic spatial dimensions * Updated shape infer functions for CTCGreedyDecotder, CTCLoss and Enter * Fixed shape inference with dynamic dimensions for MatMul, Split, Upsample, SpaceToBatch, some fixes for the TI * Fixes for undefined dimensions support for Proposal and DetectionOutput * Fixed ExtractImagePatches, DepthToSpace and RegionYolo shape infer functions to work with partially dynamic dimensions * Changes in tf_window_op_pad_infer to better work with dynamic dimensions * Fixed output shape calculation for StridedSlice operation * More StridedSlice fixes * Fixed resolve_convolution_with_group * Fixed unit tests * Fixed unit tests * Fixed Switch op unit tests * Fixed shape inference for Upsample operation * Updated unit tests for the Concat operation * Fixed eltwise shape infer unit tests * Fixed shape infer tests for Convolution and DetectionOutput ops * Fixed Crop shape infer function tests * Fixed Slice op unit test and minor fix in the shape inference. Fixed emitter * Updated unit test for telemetry and match_shape function for dynamism * Fixed unit test for the DetectionOutput * Added support for the TF ClipByValue operation * Fixed GatherND shape inference for dynamic shapes support * Dynamic shapes support for the MO IR Reader * Fixed BlockLSTM operation to not work as an extractor * Allow to serialize IRs with partially defined shapes * Updated SelectBroadcast transformation to not check shape values * Fixed MO IR comparator * Fixed SS value propagation when slices are dynamic * Do not re-run graph clean-up for ProposalMutation * Fixed InterpolateSequenceToInterpolate transformation to support dynamic dimensions * Fixed Loop iteration count calculation and reading IteratorGetNext shapes * Fixed unit test for serialization * Fixed serialization test * Fixed RandomUniform shape infer * Fixed several transformations related to RNN to respect dynamic output shapes * Fixed Deconvolutin shape calculation for dynamic batch. Eltwise shape infer improvements * Fixed shape infer functions for ExperimentalDetectron ops, reverted changes for NonZero and removed debug prints * Fixed check for dynamism of a list, fixed value propagation for Concat op and remove redundant shape infer for reshape * Update Eltwise value propagation to use np.ma * Fixed ExpandDims shape infer function * Shape infer functions fixes and improvements * Remove Accum op from the MO * Updated activation functions shape infer * Removed unsupported operation Correlation * Fixed shape infers for several functions * Removed unsupported DataAugmentation operation * Fixed shape infer functions for several ops in extensions directory * Removed not-support operation PowerFile * Removed unsupported SpatialTransformer,SimplerNMS and PredictionHeatmap operations * More shape infer functions updates * Merge shape infer fix * Fixed typo * Fixed TensorArraySize shape infer function * Fixed VariadicSplit and Squeeze shape infer * Fixed ONNX models Parameter extractor * Updated Select value propagation for the dynamic case * Fixed ReorgYolo shape infer and test * Removed unnecessary tests * Fixed Tile shape infer * Fixed SparseFillEmptryRows unit tests * Fixed package bom * Added extractor for the TF operation Mod * Fixed value propagation for MatMul operation * Updated Parameter extender to generate shape_array when shape is partially defined only * Fixed BOM file * Fixed issue with the TF OD API models and DetectionOutput op. Now the shape infer function for the DO do not re-infer "num_classes" attribute value if it is already known * Fixed unit test for the DO infer * Fixed num classes calculation for the DO generation for Faster/Mask-RCNN models * Changed NMS op to produce static output shape * Restore dynamic output shape calculation for the NMS for NMS-5 * Fixed CellNormalizer transformation. It should work for static shapes only * RNNCell Op class fixes * Revert some changes * Updated documentation with a list of supported operations * Revert changes * Fixes for the ConstantFill op * Removed redundant SequenceLengthToMask transformation * TensorArray* ops shape infer code style and refactoring * Reverse some unnecessary changes in the ConvolutionNormalizer * Fixes and unit tests for shape_array, compare_shapes, is_fully_defined functions * Implemented shape_insert, shape_delete functions and tests for them * Modified code to use shape_delete function * Added usage of shape_insert function where necessary * Use shape_insert function in many places * Some fixes in shape inference for various ops * Updated shape_delete function to support negative indices * Changes and unit tests for the MatMul infer function * Removed strange code from the TF Merge infer function * Merge op shape infer fixes * Fixed value propagation in the transformation EltwiseInputReshape.py for the dynamic dimension case * Code cleanup * Updated GatherND to support dynamic dimensions * Minor fixes * Fixed shape_insert and shape_delete to support np.int64 and np.int32 types * Updated Upsample operation unit tests with dynamic input shapes * Minor change in the extensions/back/ConvolutionNormalizer.py to make sure that input dimensions are static * Fixed ConvertGroupedStridedSlice transformation and added unit tests * Revert debug changes * Fixed value propagation for Unsqueeze to work with partially defined input values * Typo fix * Added unit tests for the Unsqueeze op shape infer * broadcasting functions changes and unit tests * Fixed Tile value inference for partially defined input tensor * Unit tests for Split and VariadicSplit ops * Fixes for the Concat infer + unit tests * Removed redundant tf_pack shape infer * Fixed Concat value infer and added unit tests * Fixed StridedSlice shape inference for case with dynamic slices * Fixes related to StridedSlice shape infer, changes in tests * Unit tests for the eltwise shape and value infer * Fixed Pad op value propagation to allow dynamic input values to be propagated * Unit test for Pooling dynamic input shape infer * Squeeze op unit tests for dynamic input shape * Added assert to the Squeeze op shape infer for case when squeeze dimension is dynamic value * Added message to the MO when input shapes are dynamic * Convolution dynamic unit test * Removed redundant transformation GroupedConvWeightsNormalize * Removed non-ascii character from the message * Fixed typo in the BOM file * Code style and comment fixes * Fixed copy-paste issue in the DO shape infer function * Fixed setting dynamic shape in the MO command line * Added function to compare tensor with dynamic values. Fixes in the unit tests and shape infer functions * Improved Reshape shape infer + added unit tests * Fixed value propagation for Select op * Renamed several internal functions, minor code fixes. * Code style fixes * Modified condition in the _set_shape method of the Port class to not check shape if the "override_output_shape" attribute is specified * Fixed constant value propagation for ReduceOps when inputs have dynamic values. Added unit test * Fixed shape infer for the Loop for dynamic dimensions case * Fix in the NMS shape infer to avoid ragged numpy array generation. Fixed Scatter shape infer validation * Improved shapes infer for eltwise ops with respect to dynamic dimensions * Changed code comments * Renamed tensor names in the ClipByValueTFTransformation * Changed np.ma.allequal to strict_compare_tensors in the Merge op infer * Chanded np.ma.allequal with strict_compare_tensor. * Fixed Merge op value infer * Fixed debug code * Removed commented line * Updated condition to check for dynamic shapes in the Partial infer to not fail for MxNet models * Improvements to the get_shape_from_slice and is_dynamic_slice functions * Reverted change in the `normalize_slices_attr` for ellipsis mask case * Updated shape conditions in the ScatterNDBase op to support dynamic dimensions * Crop op file refactoring * Set "type" attribute to None for SparseFillEmptyRows op which is not from any opset * Removed unnecessary extractor test * Restored Crop operation type * Removed "type" attribute from the Crop operation and updated the MO code to find Crop by "op" attribute * Fixed If shape infer function to produce dynamic dimensions * Updated If shape and value infer to properly work when condition is static * Fixed fusing transformation check to work with dynamic dimensions. Change comparison in the shape_inference function to not use strict shapes comparison * Optimize imports in the LayerNorm * ConvertGroupedStridedSlice minor fixes related to dynamism support * Fixed ConvertGroupedStridedSlice to properly check if the dimension is sliced
215 lines
8.8 KiB
Python
215 lines
8.8 KiB
Python
# Copyright (C) 2018-2021 Intel Corporation
|
|
# SPDX-License-Identifier: Apache-2.0
|
|
|
|
from extensions.middle.RNNSequenceNormalizeToIE import RNNSequenceNormalize
|
|
from extensions.ops.lstm_cell import LSTMCell
|
|
from extensions.ops.tensor_iterator import TensorIterator
|
|
from mo.front.common.partial_infer.utils import shape_delete
|
|
from mo.graph.graph import Graph, add_opoutput
|
|
from mo.middle.replacement import MiddleReplacementPattern
|
|
from mo.ops.const import Const
|
|
from mo.ops.op import Op
|
|
from mo.ops.squeeze import Squeeze
|
|
from mo.ops.unsqueeze import Unsqueeze
|
|
|
|
|
|
class LSTMToTensorIterator(MiddleReplacementPattern):
|
|
""" Converts normalized RNNSequence with op=LSTM to TensorIterator.
|
|
|
|
Normalized RNNSequence means that it should be processed by
|
|
RNNSequenceNormalize transform that ensures its strict form.
|
|
|
|
This transformation builds an alternative sub-graph for LSTMSequence
|
|
with TensorIterator connected in the same way as an original LSTMSequence
|
|
node and with internal body represented as LSTMCell op node with necessary
|
|
squeezes and unsqueezes around.
|
|
"""
|
|
|
|
enabled = True
|
|
force_clean_up = True
|
|
id = 'lstm_to_tensor_iterator'
|
|
|
|
def run_after(self):
|
|
return [RNNSequenceNormalize]
|
|
|
|
def run_before(self):
|
|
from extensions.middle.permute_tensor_iterator import TransposeTensorIteratorLSTM
|
|
return [TransposeTensorIteratorLSTM]
|
|
|
|
def pattern(self):
|
|
return dict(
|
|
nodes=[
|
|
('lstm', dict(kind='op', op='LSTM', type='RNNSequence')),
|
|
('input', dict(kind='data')),
|
|
('weights', dict(kind='data')),
|
|
('biases', dict(kind='data')),
|
|
# don't capture optional input initial states here
|
|
('output', dict(kind='data')),
|
|
# don't capture optional output last states here
|
|
],
|
|
edges=[
|
|
('input', 'lstm', {'in': 0}),
|
|
('weights', 'lstm', {'bin': 'weights', 'in': 1}),
|
|
('biases', 'lstm', {'bin': 'biases', 'in': 2}),
|
|
('lstm', 'output', {'out': 0}),
|
|
]
|
|
)
|
|
|
|
def replace_pattern(self, graph: Graph, match: dict):
|
|
lstm = match['lstm']
|
|
|
|
# Build TensorIterator body first
|
|
body = Graph(name=lstm.name + '/sub_graph')
|
|
body.graph = graph.graph
|
|
|
|
# 1. Input squeeze Reshape
|
|
inputs = [Op._create_data_node(body, lstm.name + '/inport/' + str(inp),
|
|
{'shape': lstm.in_node(inp).shape.copy(),
|
|
'value': lstm.in_node(inp).value.copy()
|
|
if lstm.in_node(inp).value is not None and inp in [1, 2] else None})
|
|
for inp in [0, 4, 5, 1, 2]] # X, WR, B, h_init, c_init
|
|
|
|
inputs[0].shape[lstm.sequence_dim] = 1
|
|
input_squeeze = Squeeze(body, dict(name=lstm.name + '/input_squeeze', internal_layer_id=0))
|
|
squeeze_dim_data = Const(body, {'name': lstm.name + '/input_squeeze_dim',
|
|
'value': [lstm.sequence_dim]}).create_node_with_data()
|
|
inputs[0] = input_squeeze.create_node_with_data([inputs[0], squeeze_dim_data],
|
|
edge_attrs=[{'internal_port_id': 0}])
|
|
|
|
# 2. Output unsqueeze Reshape
|
|
outputs = [Op._create_data_node(body, lstm.name + '/outport/' + str(out),
|
|
{'shape': lstm.out_node(out).shape.copy() if out in lstm.out_nodes()
|
|
else lstm.in_node(4).shape.copy()}) for out in [0, 1]]
|
|
for out in outputs:
|
|
add_opoutput(body, out.id, 0, False)
|
|
|
|
outputs[0].shape = shape_delete(outputs[0].shape, lstm.sequence_dim)
|
|
output_unsqueeze = Unsqueeze(body, dict(name=lstm.name + 'output_unsqueeze', internal_layer_id=2))
|
|
unsqueeze_dim_data = Const(body, {'name': lstm.name + '/output_unsqueeze_dim',
|
|
'value': [lstm.sequence_dim]}).create_node_with_data()
|
|
|
|
# 3. LSTMCell
|
|
lstm_cell_op = LSTMCell(body, dict(hidden_size=lstm.hidden_size,
|
|
activations=lstm.activations,
|
|
activation_alpha=lstm.activation_alpha,
|
|
activation_beta=lstm.activation_beta,
|
|
clip=lstm.clip,
|
|
input_forget=lstm.input_forget,
|
|
name=lstm.name + '/LSTMCell',
|
|
internal_layer_id=1))
|
|
lstm_cell_node = lstm_cell_op.create_node_with_data(inputs, data_nodes=outputs,
|
|
edge_attrs=[{}, {'internal_port_id': 1},
|
|
{'internal_port_id': 2}, {'bin': 'weights'},
|
|
{'bin': 'biases'}])
|
|
lstm_cell_node[0].in_node().out_edge(0)['internal_port_id'] = 4
|
|
lstm_cell_node[0].in_node().out_edge(1)['internal_port_id'] = 5
|
|
lstm_cell_node[0] = output_unsqueeze.create_node_with_data([lstm_cell_node[0], unsqueeze_dim_data])
|
|
lstm_cell_node[0].in_node().out_edge(0)['internal_port_id'] = 3
|
|
add_opoutput(body, lstm_cell_node[0].id, 0, False)
|
|
|
|
# 4. TensorIterator layer creating
|
|
assert lstm.direction in ['forward', 'reverse']
|
|
if lstm.direction == 'forward':
|
|
stride = 1
|
|
start = None
|
|
end = None
|
|
else:
|
|
assert lstm.direction == 'reverse'
|
|
stride = -1
|
|
start = -1
|
|
end = 0
|
|
|
|
output_port_map = [{
|
|
'external_port_id': 3,
|
|
'internal_layer_id': 2,
|
|
'internal_port_id': 3,
|
|
|
|
'axis': lstm.sequence_dim,
|
|
'stride': stride,
|
|
'start': start,
|
|
'end': end,
|
|
'part_size': 1,
|
|
}]
|
|
|
|
# Adding h_state, c_state to outputs
|
|
if len(lstm.out_nodes()) == 3:
|
|
output_port_map.extend([{
|
|
'external_port_id': 4,
|
|
'internal_layer_id': 1,
|
|
'internal_port_id': 4,
|
|
}, {
|
|
'external_port_id': 5,
|
|
'internal_layer_id': 1,
|
|
'internal_port_id': 5,
|
|
}])
|
|
|
|
ti_op = TensorIterator(graph, {
|
|
'name': lstm.name + '/TensorIterator',
|
|
'body': body,
|
|
'in_ports_count': 3,
|
|
'out_ports_count': len(lstm.out_nodes()),
|
|
|
|
'input_port_map': [
|
|
{
|
|
'external_port_id': 0,
|
|
'internal_layer_id': 0,
|
|
'internal_port_id': 0,
|
|
|
|
'axis': lstm.sequence_dim,
|
|
'stride': stride,
|
|
'start': start,
|
|
'end': end,
|
|
'part_size': 1,
|
|
},
|
|
{
|
|
'external_port_id': 1,
|
|
'internal_layer_id': 1,
|
|
'internal_port_id': 1,
|
|
},
|
|
{
|
|
'external_port_id': 2,
|
|
'internal_layer_id': 1,
|
|
'internal_port_id': 2,
|
|
},
|
|
],
|
|
|
|
'output_port_map': output_port_map,
|
|
|
|
'back_edges': [
|
|
{
|
|
'from_layer': 1,
|
|
'from_port': 4,
|
|
'to_layer': 1,
|
|
'to_port': 1,
|
|
},
|
|
{
|
|
'from_layer': 1,
|
|
'from_port': 5,
|
|
'to_layer': 1,
|
|
'to_port': 2,
|
|
},
|
|
]
|
|
})
|
|
|
|
assert sorted(lstm.out_nodes().keys()) == list(range(len(lstm.out_nodes()))), \
|
|
"There are gaps in output ports of LSTMSequence operation. Node {}".format(lstm.id)
|
|
|
|
outs = ti_op.create_node_with_data([lstm.in_node(i) for i in [0, 4, 5]], # X, h_init, c_init
|
|
data_nodes=[lstm.out_node(i) for i in range(len(lstm.out_nodes()))],
|
|
edge_attrs=[{'external_port_id': 0}, {'external_port_id': 1},
|
|
{'external_port_id': 2}])
|
|
|
|
if not isinstance(outs, list):
|
|
outs = list([outs])
|
|
|
|
graph.remove_node(lstm.id)
|
|
outs[0].in_edge(0)['external_port_id'] = 3
|
|
for i, out in enumerate(outs[1:]):
|
|
external_port_id = 4 + i
|
|
out.in_edge()['external_port_id'] = external_port_id
|
|
|
|
ti = outs[0].in_node()
|
|
TensorIterator.cover_body_input_data_nodes_with_parameter_ops(ti)
|
|
TensorIterator.cover_body_constant_data_nodes_with_const_ops(ti)
|
|
TensorIterator.normalize_internal_ids(ti)
|