def make_interpolate_reshapeable(interpolate, concat): assert interpolate.soft_get('type') == 'Interpolate' assert concat.soft_get('type') == 'Concat' output_shape = interpolate.out_port(0).data.get_shape() interp_axes = [get_canonical_axis_index(output_shape, axis) for axis in Interpolate.get_axes(interpolate)] concat_axis = get_canonical_axis_index(output_shape, concat.axis) if concat_axis in interp_axes: return concat_srcs = [port.get_source() for port in concat.in_ports().values() if not port.disconnected()] non_interp_concat_srcs = [src for src in concat_srcs if src.node.soft_get('type') != 'Interpolate'] if len(non_interp_concat_srcs) == 0: return graph = interpolate.graph src = non_interp_concat_srcs[0] shape = Shape(graph, {'name': src.node.soft_get('name', src.node.id) + '/Shape'}).create_node() shape.in_port(0).connect(src) gather = create_op_with_const_inputs(graph, Gather, {1: np.array(interp_axes, dtype=np.int32), 2: int64_array(0)}, {'name': shape.name + '/Gathered'}, shape) interpolate.in_port(1).get_connection().set_source(gather.out_port(0))
def regionyolo_infer(node: Node): input_shape = node.in_port(0).data.get_shape() axis = get_canonical_axis_index(input_shape, node.axis) end_axis = get_canonical_axis_index(input_shape, node.end_axis) node.axis = axis node.end_axis = end_axis if node.do_softmax: dims_to_flatten = input_shape[axis:end_axis + 1] if is_fully_defined(dims_to_flatten): flat_dim = np.ma.prod(dims_to_flatten) else: flat_dim = dynamic_dimension_value node.out_port(0).data.set_shape( [*input_shape[:axis], flat_dim, *input_shape[end_axis + 1:]]) else: layout = node.graph.graph['layout'] assert len(layout) == 4 node.out_port(0).data.set_shape( shape_for_layout(layout, batch=input_shape[get_batch_dim(layout, 4)], 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)]))
def infer(node: Node): real_squeeze_dims = int64_array([]) input_shape = node.in_port(0).data.get_shape() node_name = node.soft_get('name', node.id) if input_shape is None: raise Error( 'Input shape is not defined for node {}'.format(node_name)) output_shape = input_shape.copy() assert len(node.in_nodes( )) == 2, 'The Squeeze node {} must have 2 inputs'.format(node_name) # TODO remove the following 'if' statement when IE start support 0D tensors squeeze_dims = node.in_port(1).data.get_value() if squeeze_dims.ndim == 0: squeeze_dims = squeeze_dims.reshape([1]) for dim in squeeze_dims: if output_shape[dim] == 1 or output_shape[dim] is dynamic_dimension: real_squeeze_dims = np.ma.append( real_squeeze_dims, get_canonical_axis_index(output_shape, dim)) else: raise Error( 'Trying to squeeze dimension not equal to 1 for node "{}"'. format(node_name)) # if squeeze_dims empty then all 1s should be removed (tf specification of Squeeze op) if squeeze_dims.size == 0: for i in range(output_shape.size): if output_shape[i] == 1: real_squeeze_dims = np.ma.append( real_squeeze_dims, get_canonical_axis_index(output_shape, i)) assert is_fully_defined( real_squeeze_dims ), 'Squeeze dimension(s) is not defined for op "{}"'.format(node_name) output_shape = shape_delete(output_shape, real_squeeze_dims) node.out_port(0).data.set_shape(output_shape) # make dimensions positive to correctly translate from NHWC to NCHW layout if node.in_port(1).get_source().node.op == 'Const': node.in_port(1).data.set_value(real_squeeze_dims) if node.in_port(0).data.get_value() is not None: node.out_port(0).data.set_value( node.in_port(0).data.get_value().reshape(output_shape)) # the squeeze_dim attribute will be converted to the second input in the end of the Middle phase PermuteInputs().set_input_permutation(node.in_node(1), node, 'input:0', 'axis')
def infer(node): input_shape = node.in_port(0).data.get_shape() input_value = node.in_port(0).data.get_value() shape_like = node.in_port(1).data.get_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: new_shape[i] = shape_like[i] else: 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 = shape_like.copy() node.out_port(0).data.set_shape(new_shape) if input_value is not None and is_fully_defined(new_shape): out_value = np.copy(input_value) slice_indexes = [] for s in out_value.shape: slice_indexes.append(slice(0, s)) for axis in node.axes: slice_indexes[axis] = slice(0, new_shape[axis]) out_value = out_value[tuple(slice_indexes)] node.out_port(0).data.set_value(out_value)
def arg_ops_infer(node: Node): shape = node.in_port(0).data.get_shape() node_name = node.soft_get('name', node.id) assert shape is not None, "Input shape for the node {} is None".format(node_name) # there are two inputs in TensorFlow. The second input is the axis for ArgMax connected_in_ports = [port for port in node.in_ports().values() if not port.disconnected()] if len(connected_in_ports) == 2: axis = node.in_port(1).data.get_value() if axis is None: log.debug('The second argument to {} is None'.format(node.soft_get('name', node.id))) return node.axis = axis # remove the unnecessary input node.in_port(1).disconnect() num_top_axes = shape.size if num_top_axes < 3: num_top_axes = 3 out_shape = np.ones(num_top_axes, dtype=np.int64) if node.has_valid('axis'): axis = get_canonical_axis_index(shape, node.axis) node.axis = axis out_shape = shape.copy() out_shape[axis] = node.top_k PermuteAttrs.create_permute_attrs(node, attrs=[('axis', 'input:0')]) else: out_shape[0] = shape[0] out_shape[2] = node.top_k if node.has_and_set('out_max_val'): out_shape[1] = 2 node.out_port(0).data.set_shape(out_shape)
def find_and_replace_pattern(self, graph: Graph): for node in graph.get_op_nodes(op='LayerNorm'): node_name = node.soft_get('name', node.id) if node.output_mean_var is True: if not node.out_port(1).disconnected() or not node.out_port(2).disconnected(): raise Error("Node {} is supported with only one output".format(node_name)) log.error('LayerNorm node {} with attribute "output_mean_var" = True is not supported.' 'But since the node has one output, the conversion will continue.'.format(node_name), extra={'is_warning': True}) input_shape = node.in_port(0).data.get_shape() assert node.has_valid('axis'), 'Incorrect axis value for the node {}'.format(node_name) axis = node.axis mvn = create_op_node_with_second_input(graph, MVN, int64_array([axis]), dict(eps=node.epsilon, name=node_name + '/LayerNorm/MVN_', across_channels=1, normalize_variance=1, eps_mode='inside_sqrt')) mul = Mul(graph, {'name': node_name + '/LayerNorm/mul_'}).create_node() add = Add(graph, {'name': mul.name + '/LayerNorm/add_'}).create_node() node.in_port(0).get_connection().set_destination(mvn.in_port(0)) node.in_port(1).get_connection().set_destination(mul.in_port(1)) node.in_port(2).get_connection().set_destination(add.in_port(1)) mvn.out_port(0).connect(mul.in_port(0)) mul.out_port(0).connect(add.in_port(0)) node.out_port(0).get_connection().set_source(add.out_port(0)) # MXNet LayerNorm gamma and beta attributes are 1D tensors with shape = [input_shape[axis]] # We have to unsqueeze values for Mul and Add operations to avoid shapes incompatibility problems # if axis != -1 canonical_axis = get_canonical_axis_index(input_shape, axis) unsqueeze_value = [] for idx, val in enumerate(input_shape): if idx != canonical_axis: unsqueeze_value.append(idx) mul_const_unsqueeze = create_op_node_with_second_input(graph, Unsqueeze, int64_array(unsqueeze_value), dict(name=mul.name + '/Unsqueeze', override_output_shape=True)) add_const_unsqueeze = create_op_node_with_second_input(graph, Unsqueeze, int64_array(unsqueeze_value), dict(name=add.name + '/Unsqueeze', override_output_shape=True)) mul.in_port(1).get_connection().insert_node(mul_const_unsqueeze) add.in_port(1).get_connection().insert_node(add_const_unsqueeze) rename_nodes([(node, node_name + '/ShouldBeDeleted'), (add, node_name)])
def replace_pattern(self, graph: Graph, match: dict): matmul = match['matmul'] reshape = match['reshape'] other_input_port_idx = 0 if match['matmul'].in_port( 0).get_source().node.id == match['other_input'].id else 1 shape_source = match['matmul'].in_port( other_input_port_idx).get_source() initial_reshape_pattern = reshape.in_port(1).data.get_value() if len(initial_reshape_pattern) != 2: return reshape_is_A_input = matmul.in_port( 0).get_source().node.id == reshape.id if reshape_is_A_input: idx = -1 if matmul.transpose_b else -2 else: idx = -2 if matmul.transpose_a else -1 idx = get_canonical_axis_index(initial_reshape_pattern, idx) shape_name = shape_source.node.soft_get('name', shape_source.node.id) shape = Shape(graph, {'name': shape_name + '/Shape'}).create_node() shape.in_port(0).connect(shape_source) C = node_to_get_shape_value_of_indices(shape, [idx]) N = Const(graph, { 'name': shape_name + '/MinusOne', 'value': int64_array([-1]) }).create_node() if len(initial_reshape_pattern) == 2: if reshape_is_A_input: reshape_pattern = [C, N] if matmul.transpose_a else [N, C] else: reshape_pattern = [N, C] if matmul.transpose_b else [C, N] new_reshape_pattern = new_shape_node_from_shape_nodes( reshape_pattern) reshape.in_port(1).get_connection().set_source( new_reshape_pattern.out_port(0)) else: return
def infer(node: Node): name = node.soft_get('name', node.id) connected_in_ports = { idx: port for idx, port in node.in_ports().items() if not port.disconnected() } assert len(connected_in_ports) == 2 and 0 in connected_in_ports and 1 in connected_in_ports, \ "AttributedGather should have 2 connected input port, but it doesn't for node: `{}`. Ports: {}" \ "".format(name, connected_in_ports) axis = node.soft_get('axis', None) assert axis is not None data_shape = node.in_port(0).data.get_shape() assert data_shape is not None indices_shape = node.in_port(1).data.get_shape() assert indices_shape is not None # Convert negative axis axis = get_canonical_axis_index(data_shape, axis) node.axis = axis PermuteAttrs.create_permute_attrs(node, attrs=[('axis', 'input:0')]) data_value = node.in_port(0).data.get_value() indices_value = node.in_port(1).data.get_value() if data_value is not None and indices_value is not None: node.out_port(0).data.set_value( mo_array(np.take(data_value, indices_value, axis), dtype=data_value.dtype)) return shape = np.concatenate((data_shape[:axis], indices_shape)) if axis < len(data_shape) - 1: shape = np.concatenate((shape, data_shape[axis + 1:])) node.out_port(0).data.set_shape(int64_array(shape))
def mxnet_slice_axis_infer(node): in_shape = node.in_port(0).data.get_shape() node.axis = get_canonical_axis_index(in_shape, node.axis) slice_axis = node.axis new_shape = np.array(in_shape, dtype=np.int64) new_shape[slice_axis] = new_shape[slice_axis] / len(node.out_nodes()) axis_size = in_shape[slice_axis] if node.offset < 0: node.offset += axis_size if not node.dim: node.dim = axis_size elif node.dim < 0: node.dim += axis_size input_dim = in_shape.size node.dim = (node.dim - node.offset) if node.dim > in_shape[slice_axis]: raise Error( '{0} node dimension value is bigger than the corresponding value in the input shape {1}. ' + '\nIn particular {2} is bigger than {3}. The Model Optimizer does not support this case. ' + '\nTo overcome, try to edit the original model "end" property of the {0} layer.', node.name, ','.join(str(i) for i in in_shape), str(node.dim), str(in_shape[slice_axis])) for i in range(0, input_dim): if i == slice_axis: new_shape[i] = node.dim else: new_shape[i] = in_shape[i] for i in range(0, len(node.out_nodes())): node.out_node(i)['shape'] = new_shape
def find_and_replace_pattern(self, graph: Graph): for node in graph.get_op_nodes(op='Slice'): node_name = node.soft_get('name', node.id) input_shape = node.in_port(0).data.get_shape() if node.is_in_port_connected(3): axes = node.in_port(3).data.get_value().copy() assert axes is not None, 'The input with axes is not constant for node {}'.format( node_name) for i, val in enumerate(axes): axes[i] = get_canonical_axis_index(input_shape, val) else: axes = int64_array(range(len(input_shape))) ss_begin = create_ss_interval_border(graph, node.in_port(1).get_source(), input_shape, axes, node_name) ss_end = create_ss_interval_border(graph, node.in_port(2).get_source(), input_shape, axes, node_name) node.in_port(1).disconnect() node.in_port(2).disconnect() rename_nodes([(ss_begin, node_name + '/Begin'), (ss_end, node_name + '/End')]) if node.is_in_port_connected(4): steps = node.in_port(4).data.get_value() assert steps is not None, 'The input with steps is not constant for node {}'.format( node_name) else: steps = np.ones([axes.size]) ss_begin_mask = np.zeros(len(input_shape), dtype=np.int64) ss_end_mask = np.zeros(len(input_shape), dtype=np.int64) ss_step = np.ones(len(input_shape), dtype=np.int64) for i, axis in enumerate(axes): ss_begin_mask[axis] = 1 ss_end_mask[axis] = 1 ss_step[axis] = steps[i] ss_strides = Const( graph, dict(name=node_name + '/Strides', value=ss_step)).create_node() ss = StridedSlice( graph, dict(name='ss', new_axis_mask=np.zeros(len(input_shape), dtype=np.int64), shrink_axis_mask=np.zeros(len(input_shape), dtype=np.int64), ellipsis_mask=np.zeros(len(input_shape), dtype=np.int64), begin_mask=ss_begin_mask, end_mask=ss_end_mask)).create_node() node.in_port(0).get_connection().set_destination(ss.in_port(0)) ss.in_port(1).connect(ss_begin.out_port(0)) ss.in_port(2).connect(ss_end.out_port(0)) ss.in_port(3).connect(ss_strides.out_port(0)) node.out_port(0).get_connection().set_source(ss.out_port(0)) rename_nodes([(node, node_name + '/ShouldBeDeleted'), (ss, node_name)])
def test_posirive_index(self): shape = [1, 2, 3, 4] inds = [0, 1, 2, 3] expected_inds = [0, 1, 2, 3] for i in range(len(inds)): assert get_canonical_axis_index(shape, inds[i]) == expected_inds[i]
def crop_infer(node): """ Crops the shape of the output blob according to input ones be specified params. Node should have 2 input blobs - 1st blob is getting cropped by specified axis according to the the 2nd (reference) blob. The result blob is written to output node shape, and reference blob is removed from graph. In order to save the reference dims, it is written to dims parameter. Parameters ---------- node """ N = len(node.in_nodes()) if N < 2: log.debug('Wrong number of bottom blobs in ' + node.node) return shapes = [node.in_node(i).shape for i in range(N)] if any(s is None for s in shapes): return input_shape = np.array(shapes[0]) start_axis = get_canonical_axis_index(input_shape, node.axis) node.axis = start_axis reference_shape = np.array(shapes[1]) input_dim = input_shape.size # set new shape to current shape new_shape = input_shape.copy() ir_axis = [] ir_offset = [] dim = [] for i in range(0, input_dim): if i < start_axis: new_shape[i] = input_shape[i] continue crop_offset = 0 if len(node.offset) == 1: crop_offset = node.offset[0] elif len(node.offset) > 1: crop_offset = node.offset[i - start_axis] if input_shape[i] - crop_offset < reference_shape[i]: log.error('The crop for dimension is out of bounds in ' + node.node) return dim.append(reference_shape[i]) ir_axis.append(i) ir_offset.append(crop_offset) new_shape[i] = reference_shape[i] node.axis = ir_axis node.offset = ir_offset node.dim = dim node.out_node().shape = new_shape
def concat_infer(node): node_name = node.soft_get('name', node.id) if not node.has('axis'): N = node.N axis_input = node.in_node(N) if axis_input.has_valid('value') and axis_input.value.size == 1: node['axis'] = axis_input.value.item() node.graph.remove_edge( axis_input.node, node.node) # TODO add skip attribute instead of deleting else: raise Error( 'Input with value is not specified for node "{}"'.format( node_name)) else: N = len(node.in_nodes()) shapes = [node.in_node(i).shape for i in range(N)] if any(s is None for s in shapes): raise Error( 'One of the input shapes is not defined for node "{}"'.format( node_name)) shape = shape_array(shapes[0]) axis = get_canonical_axis_index(shape, node.axis) node.axis = axis mask = np.zeros_like(shape, dtype=np.bool) mask[axis] = True # pylint: disable=unsupported-assignment-operation not_mask = np.logical_not(mask) # pylint: disable=assignment-from-no-return for s in shapes[1:]: s = shape_array(s) if np.ma.allequal(shape[not_mask], s[not_mask]): shape[mask] += s[mask] else: raise Error( 'Concat input shapes do not match for node "{}" with axis {}'. format(node_name, axis)) # dynamic dimensions in the output (except the concat axis) can be deduced from input shape for pos in range(len(shape)): if shape[pos] is dynamic_dimension and pos != axis: for in_shape in shapes: if in_shape[pos] is not dynamic_dimension: shape[pos] = in_shape[pos] node.out_port(0).data.set_shape(shape) PermuteAttrs.create_permute_attrs(node, attrs=[('axis', 'input:0')]) values = [node.in_node(i).value for i in range(N)] if any([v is None for v in values]): return # if one of the input values are dynamic, the output tensor type is inferred from one of the fully defined inputs output_dtype = np.int64 for input in values: if is_fully_defined(input): output_dtype = input.dtype if any(not is_fully_defined(v) for v in values): node.out_port(0).data.set_value( np.ma.concatenate(values, axis=node.axis).astype(output_dtype)) else: # there is a serious performance benefit to use concatenation as it is implemented below node.out_node(0).value = np.concatenate(values, axis=node.axis).astype( values[0].dtype, copy=False) node.out_node(0).shape = shape_array(node.out_node(0).value.shape)
def _two_inputs_infer(node: Node): N = len(node.in_nodes()) node_name = node.soft_get('name', node.id) shapes = [node.in_port(i).data.get_shape() for i in range(N)] if any(s is None for s in shapes): raise Error('Not all input shapes were defined for {} node'.format(node_name)) if not node.has_valid('axis'): raise Error('axis attribute is missing for {} node. should be set in crop extractor'.format(node_name)) if not node.has_valid('offset'): raise Error('offset attribute is missing for {} node. should be set in crop extractor'.format(node_name)) input_shape = shapes[0].copy() start_axis = get_canonical_axis_index(input_shape, node.axis) node.axis = start_axis reference_shape = shapes[1].copy() if node.has_valid('axes'): # The axes parameter contain shape indexes for second input and show which shape indexes we need to use for # dim attribute. input_dim = node.axes node.in_port(1).disconnect() else: input_dim = list(range(0, input_shape.size)) # set new shape to current shape new_shape = input_shape.copy() ir_axis = [] ir_offset = [] dim = [] for i in input_dim: if i < start_axis: new_shape[i] = input_shape[i] continue crop_offset = 0 if len(node.offset) == 1: crop_offset = node.offset[0] elif len(node.offset) > 1: crop_offset = node.offset[i - start_axis] if input_shape[i] - crop_offset < reference_shape[i]: raise Error('The crop for dimension is out of bounds in node {}'.format(node_name)) dim.append(reference_shape[i]) ir_axis.append(i) ir_offset.append(crop_offset) new_shape[i] = reference_shape[i] node.axis = ir_axis node.offset = ir_offset node['dim'] = dim node.out_port(0).data.set_shape(new_shape) if node.in_node(0).has_valid('value') and \ not getattr(node.graph.graph['cmd_params'], 'enable_ssd_gluoncv', False): out_value = np.copy(node.in_node(0).value) slice_indexes = [] for s in out_value.shape: slice_indexes.append(slice(0, s)) for axis in input_dim: slice_indexes[axis] = slice(0, new_shape[axis]) out_value = out_value[tuple(slice_indexes)] node.out_port(0).data.set_value(out_value) PermuteAttrs.create_permute_attrs(node, attrs=[('axis', 'input:0')])