def replace_pattern(self, graph: Graph, match: dict): merge = match['merge'] power = Pow(graph, { 'name': merge.name + '/reciprocal_', 'type': 'PNORM' }).create_node() const1 = Const(graph, { 'value': -1.0, 'name': merge.name + '/negate_const' }).create_node() merge.in_port(0).get_connection().set_destination(power.in_port(0)) const1.out_port(0).connect(power.in_port(1)) concat_node = Concat( graph, { 'axis': 0, 'name': merge.name + '/Concat_', 'override_output_shape': True }).create_node() const3 = Const(graph, { 'name': merge.name + '/const_reduce', 'value': 0 }).create_node() for ii, idx in enumerate( range(merge.significant, merge.to_significant + 1, 1)): const_node = Const( graph, { 'value': float_array(math.pow(10.0, idx)), 'name': merge.name + '/Const_' + ii.__str__() }).create_node() mul_node = Mul(graph, { 'name': merge.name + '/Mul_' + ii.__str__() }).create_node() const_node.out_port(0).connect(mul_node.in_port(0)) power.out_port(0).connect( mul_node.in_port(1)) # connect to the graph node mul_node2 = Mul(graph, { 'name': merge.name + '/Mul_Div_' + ii.__str__() }).create_node() const_node2 = Const( graph, { 'value': float_array(math.pow(10.0, -1 * idx)), 'name': merge.name + '/Const_Pow_' + ii.__str__() }).create_node() cast_node = Cast( graph, { 'name': merge.name + '/Cast_' + idx.__str__(), 'dst_type': np.float32 }).create_node() mul_node.out_port(0).connect(cast_node.in_port(0)) const_node2.out_port(0).connect(mul_node2.in_port(1)) cast_node.out_port(0).connect(mul_node2.in_port(0)) concat_node.add_input_port(ii, skip_if_exist=True) concat_node.in_port(ii).get_connection().set_source( mul_node2.out_port(0)) reducesum_node = ReduceMean( graph, { 'name': merge.id + '/_pnorm_reduced_sum', 'keep_dims': False, 'in_ports_count': 2, 'need_shape_inference': None, 'infer': reduce_infer }).create_node() const3.out_port(0).connect(reducesum_node.in_port(1)) reducesum_node.in_port(0).get_connection().set_source( concat_node.out_port(0)) reshape = Reshape(graph, { 'name': merge.name + '/Reshape_Node' }).create_node() reshape_dim = Const(graph, { 'value': np.array([1, 5]), 'name': merge.id + '/Reshape_Dim' }).create_node() reducesum_node.out_port(0).connect(reshape.in_port(0)) reshape.in_port(1).connect(reshape_dim.out_port(0)) merge.out_port(0).get_connection().set_source(reshape.out_port(0))
def replace_pattern(graph: Graph, match: dict): node = match['pool'] node_name = node.soft_get('name', node.id) if node.pool_step is None: node.stride = int64_array([1, 1, node.window[-1], node.window[-1]]) # create Reshape before convolution # shape = [in_shape[0], pool_stride, 1, in_shape[1]/pool_stride] i_shape = Shape(graph, {'name': node_name + '/Shape'}).create_node() shape = Cast( graph, { 'name': node_name + '/to_float', 'dst_type': data_type_str_to_np(graph.graph['cmd_params'].data_type) }).create_node() i_shape.in_port(0).connect(node.in_port(0).get_source()) shape.in_port(0).connect(i_shape.out_port(0)) N, H = node_to_get_shape_value_of_indices( shape, [0]), node_to_get_shape_value_of_indices(shape, [1]) div = create_op_with_const_inputs( graph, Div, {1: float_array([node.pool_stride])}, {'name': node_name + '/div_stride_h'}) div.in_port(0).connect(H.out_port(0)) concat = create_op_with_const_inputs(graph, Concat, { 1: float_array([node.pool_stride]), 2: float_array([1]) }, { 'name': node_name + '/concat_all_dims', 'in_ports_count': 4, 'axis': 0 }) concat.in_port(0).connect(N.out_port(0)) concat.in_port(3).connect(div.out_port(0)) reshape_pattern = Cast(graph, { 'name': node_name + '/to_int', 'dst_type': np.int64 }).create_node() concat.out_port(0).connect(reshape_pattern.in_port(0)) reshape_in = Reshape(graph, { 'name': node_name + '/reshape_in' }).create_node() reshape_in.in_port(1).connect(reshape_pattern.out_port(0)) # create Reshape after Convolution reshape_out = create_op_node_with_second_input( graph, Reshape, int64_array([0, -1]), {'name': node_name + '/reshape_out'}) # connect input_reshape_node source = node.in_port(0).get_source() node.in_port(0).get_connection().set_source(reshape_in.out_port(0)) reshape_in.in_port(0).connect(source) # connect output_reshape_node node.out_port(0).get_connection().set_source(reshape_out.out_port(0)) node.out_port(0).connect(reshape_out.in_port(0))
def replace_tf_resize(graph: Graph, resize: Node, interpolation_mode: str): resize_name = resize.soft_get('name', resize.id) log.debug( "Converting of {} to Interpolate-4 is triggered for node {}.".format( resize.op, resize_name)) num_of_inputs = len([ port for port in resize.in_ports().values() if not port.disconnected() ]) assert num_of_inputs == 2, \ "Number of inputs of {} (with name {}) should be equal to 2".format(resize.op, resize_name) attrs_msg = "If half_pixel_centers attribute of the node {} with op {} is True, " \ "the attribute align_corners must be False" assert not resize.half_pixel_centers or (resize.half_pixel_centers and not resize.align_corners), \ attrs_msg.format(resize_name, resize.op) shape = Shape(graph, {'name': resize_name + '/shapeof'}).create_node() layout = graph.graph['layout'] height_dim = get_height_dim(layout, 4) width_dim = get_width_dim(layout, 4) ss = create_op_with_const_inputs( graph, StridedSlice, { 1: int64_array([height_dim]), 2: int64_array([width_dim + 1]), 3: int64_array([1]) }, { 'name': resize_name + '/StridedSlice', 'begin_mask': int64_array([1]), 'end_mask': int64_array([1]), 'new_axis_mask': int64_array([0]), 'shrink_axis_mask': int64_array([0]), 'ellipsis_mask': int64_array([0]) }) div_node = Div(graph, {'name': resize_name + '/Div'}).create_node() shape_to_float = Cast(graph, dict(dst_type=np.float32)).create_node() size_to_float = Cast(graph, dict(dst_type=np.float32)).create_node() size_to_float.out_port(0).connect(div_node.in_port(0)) shape_to_float.out_port(0).connect(div_node.in_port(1)) ss.out_port(0).connect(shape_to_float.in_port(0)) shape.out_port(0).connect(ss.in_port(0)) align_corners = resize.align_corners half_pixel_centers = resize.half_pixel_centers nearest_mode = 'floor' if interpolation_mode == 'nearest' else 'round_prefer_floor' if align_corners: coordinate_transformation_mode = 'align_corners' if interpolation_mode == 'nearest': nearest_mode = 'round_prefer_ceil' elif half_pixel_centers: coordinate_transformation_mode = 'tf_half_pixel_for_nn' if interpolation_mode == 'nearest' else 'half_pixel' else: coordinate_transformation_mode = 'asymmetric' interpolate4 = create_op_with_const_inputs( graph, Interpolate, {3: int64_array([height_dim, width_dim])}, { 'name': resize_name + '/interpolate_4', 'mode': interpolation_mode, 'antialias': False, 'coordinate_transformation_mode': coordinate_transformation_mode, 'pads_begin': int64_array([0]), 'pads_end': int64_array([0]), 'nearest_mode': nearest_mode, 'cube_coeff': -0.75, 'shape_calculation_mode': 'sizes', 'version': 'opset4', 'in_ports_count': 4, }) resize_input_connection = resize.in_port(0).get_connection() resize_input_connection.set_destination(interpolate4.in_port(0)) resize_input_connection.get_source().connect(shape.in_port(0)) div_node.out_port(0).connect(interpolate4.in_port(2)) sizes_connection = resize.in_port(1).get_connection() sizes_connection.set_destination(interpolate4.in_port(1)) sizes_connection.get_source().connect(size_to_float.in_port(0)) resize.out_port(0).get_connection().set_source(interpolate4.out_port(0)) rename_nodes([(resize, resize_name + '/delete'), (interpolate4, resize_name)])
def replace_resize(graph: Graph, resize: Node): log.debug("Converting of ONNX Resize-11 to Interpolate-4 " "is triggered for node {}.".format( resize.soft_get('name', resize.id))) input_shape = resize.in_port(0).data.get_shape() input_rank = len(input_shape) resize_name = resize.soft_get('name', resize.id) if input_rank not in {4, 5}: log.warning( 'The input shape is not 4D or 5D for op with name {}'.format( resize_name)) return num_of_inputs = len([ port for port in resize.in_ports().values() if not port.disconnected() ]) assert num_of_inputs in {3, 4}, \ "Number of inputs of ONNXResize (with name {}) should be equal to 3 or 4".format(resize_name) assert resize.soft_get('coordinate_transformation_mode') != 'tf_crop_and_resize', \ 'Mode tf_crop_and_resize is not supported for op {} with name {}'.format(resize.op, resize_name) layout = graph.graph['layout'] if input_rank == 4: begin_dim = get_height_dim(layout, input_rank) end_dim = get_width_dim(layout, input_rank) + 1 else: begin_dim = get_depth_dim(layout, input_rank) end_dim = get_width_dim(layout, input_rank) + 1 sizes_ss = create_op_with_const_inputs( graph, StridedSlice, { 1: int64_array([begin_dim]), 2: int64_array([end_dim]), 3: int64_array([1]) }, { 'name': resize_name + '/StridedSlice_sizes', 'begin_mask': int64_array([1]), 'end_mask': int64_array([1]), 'new_axis_mask': int64_array([0]), 'shrink_axis_mask': int64_array([0]), 'ellipsis_mask': int64_array([0]) }) scales_ss = create_op_with_const_inputs( graph, StridedSlice, { 1: int64_array([begin_dim]), 2: int64_array([end_dim]), 3: int64_array([1]) }, { 'name': resize_name + '/StridedSlice_scales', 'begin_mask': int64_array([1]), 'end_mask': int64_array([1]), 'new_axis_mask': int64_array([0]), 'shrink_axis_mask': int64_array([0]), 'ellipsis_mask': int64_array([0]) }) axes_node = Const( graph, { 'name': resize_name + '/axis', 'value': int64_array(np.arange(begin_dim, end_dim)) }).create_node() shape_calculation_mode = 'scales' if num_of_inputs == 3 else 'sizes' interpolate_node = Interpolate( graph, { 'version': 'opset4', 'mode': convert_mode(resize.mode), 'coordinate_transformation_mode': resize.coordinate_transformation_mode, 'cube_coeff': resize.cube_coeff, 'nearest_mode': resize.nearest_mode, 'pads_begin': int64_array([0]), 'pads_end': int64_array([0]), 'antialias': 0, 'shape_calculation_mode': shape_calculation_mode, 'in_ports_count': 4 }).create_node() axes_node.out_port(0).connect(interpolate_node.in_port(3)) shape_of = Shape(graph, {'name': resize_name + '/ShapeOf'}).create_node() add_node = create_op_with_const_inputs(graph, Add, {1: float_array([1.0e-5])}, {'name': resize_name + '/Add'}) input_data_type = data_type_str_to_np(graph.graph['cmd_params'].data_type) if num_of_inputs == 3: cast_shape_to_float = Cast(graph, { 'dst_type': input_data_type }).create_node() mul_node = Mul(graph, {'name': resize_name + '/Mul'}).create_node() shape_of.out_port(0).connect(cast_shape_to_float.in_port(0)) cast_shape_to_float.out_port(0).connect(mul_node.in_port(0)) cast_add_result_to_int = Cast(graph, { 'dst_type': np.int64 }).create_node() floor_node = Floor(graph, { 'name': resize_name + '/Floor' }).create_node() mul_node.out_port(0).connect(add_node.in_port(0)) add_node.out_port(0).connect(floor_node.in_port(0)) floor_node.out_port(0).connect(cast_add_result_to_int.in_port(0)) cast_add_result_to_int.out_port(0).connect(sizes_ss.in_port(0)) sizes_ss.out_port(0).connect(interpolate_node.in_port(1)) scales_ss.out_port(0).connect(interpolate_node.in_port(2)) connection_of_resize_input = resize.in_port(0).get_connection() connection_of_resize_input.set_destination(interpolate_node.in_port(0)) connection_of_scales = resize.in_port(2).get_connection() connection_of_scales.set_destination(scales_ss.in_port(0)) connection_of_resize_input.get_source().connect(shape_of.in_port(0)) connection_of_scales.get_source().connect(mul_node.in_port(1)) else: cast_shape_to_float = Cast(graph, { 'dst_type': input_data_type }).create_node() cast_sizes_to_float = Cast(graph, { 'dst_type': input_data_type }).create_node() div_node = Div(graph, {'name': resize_name + '/Div'}).create_node() cast_sizes_to_float.out_port(0).connect(div_node.in_port(0)) cast_shape_to_float.out_port(0).connect(div_node.in_port(1)) shape_of.out_port(0).connect(cast_shape_to_float.in_port(0)) div_node.out_port(0).connect(add_node.in_port(0)) add_node.out_port(0).connect(scales_ss.in_port(0)) scales_ss.out_port(0).connect(interpolate_node.in_port(2)) sizes_ss.out_port(0).connect(interpolate_node.in_port(1)) connection_of_resize_input = resize.in_port(0).get_connection() connection_of_resize_input.set_destination(interpolate_node.in_port(0)) connection_of_sizes = resize.in_port(3).get_connection() connection_of_sizes.set_destination(sizes_ss.in_port(0)) connection_of_resize_input.get_source().connect(shape_of.in_port(0)) connection_of_sizes.get_source().connect( cast_sizes_to_float.in_port(0)) rename_nodes([(resize, resize_name + '/delete'), (interpolate_node, resize_name)]) resize.out_port(0).get_connection().set_source( interpolate_node.out_port(0))
def replace_sub_graph(self, graph: Graph, match: dict): # obtain references to necessary nodes and their names fill = match['fill'] dims = match['dims'] strided_slice = match['strided_slice'] strided_slice_1 = match['strided_slice_1'] ctc_greedy_decoder = match['decoder'] cast = match['cast'] sparse_to_dense = match['sparse_to_dense'] strided_slice_name = strided_slice.soft_get('name', strided_slice.id) strided_slice_1_name = strided_slice_1.soft_get( 'name', strided_slice_1.id) ctc_greedy_decoder_name = ctc_greedy_decoder.soft_get( 'name', ctc_greedy_decoder.id) sparse_to_dense_name = sparse_to_dense.soft_get( 'name', sparse_to_dense.id) # unsqueeze scalar values with batch size and time dimension unsqueeze_batch_size = create_op_with_const_inputs( graph, Unsqueeze, {1: int64_array(0)}, {'name': strided_slice_name + '/Unsqueeze'}) dims.in_port(0).get_connection().set_destination( unsqueeze_batch_size.in_port(0)) unsqueeze_time_size = create_op_with_const_inputs( graph, Unsqueeze, {1: int64_array(0)}, {'name': strided_slice_1_name + '/Unsqueeze'}) fill.in_port(1).get_connection().set_destination( unsqueeze_time_size.in_port(0)) # compute a sequence mask shape [T, N] required for CTCGreedyDecoder seq_mask_shape = Concat( graph, { 'axis': 0, 'in_ports_count': 2, 'name': ctc_greedy_decoder_name + '/SequenceMaskShape' }).create_node() seq_mask_shape.in_port(0).connect(unsqueeze_time_size.out_port(0)) seq_mask_shape.in_port(1).connect(unsqueeze_batch_size.out_port(0)) # compute a sequence mask sequence_mask = create_op_with_const_inputs( graph, Broadcast, {0: np.array([1.0], dtype=np.float)}, { 'mode': 'numpy', 'name': ctc_greedy_decoder_name + '/SequenceMask' }) sequence_mask.in_port(1).connect(seq_mask_shape.out_port(0)) # create CTCGreedyDecoder with the sequence mask instead of sequence length ctc_greedy_decoder.in_port(1).disconnect() ctc_greedy_decoder.in_port(1).connect(sequence_mask.out_port(0)) # remove fill and pack nodes since they are now in unconnected component graph.remove_nodes_from([fill.id, dims.id]) # transform opset CTCGreedyDecoder output to TensorFlow's one that has a shape [N, T] # opset CTCGreedyDecoder has an output with a shape [N, T, 1, 1] squeeze_dec_seq = create_op_with_const_inputs( graph, Squeeze, {1: int64_array([2, 3])}, {'name': sparse_to_dense_name}) squeeze_dec_seq.in_port(0).connect(ctc_greedy_decoder.out_port(0)) cast_to_int = Cast(graph, { 'name': sparse_to_dense_name + '/CastToInt', 'dst_type': np.int32 }).create_node() cast_to_int.in_port(0).connect(squeeze_dec_seq.out_port(0)) # preserve output name from original graph rename_nodes([(sparse_to_dense, sparse_to_dense_name + '/AbandonedName'), (cast_to_int, sparse_to_dense_name)]) # set output of the new sub-graph as a source for SparseToDense consumer sparse_to_dense.out_port(0).get_connection().set_source( cast_to_int.out_port(0)) # cleanup a graph graph.remove_nodes_from([cast.id, sparse_to_dense.id])
def replace_sub_graph(self, graph: Graph, match: dict): identity_spw = match['identity_spw'] gather0_1 = match['gather0_1'] gather0_2 = match['gather0_2'] greaterequal0 = match['greaterequal0'] sparse_fill_empty_rows = match['sparse_fill_empty_rows'] gather = match['gather'] select = match['select'] where0 = match['where0'] sparse_segment_op = match['sparse_segment_op'] output_node_name = select.soft_get('name', select.id) log.debug('Found EmbeddingSparseSegmentsSingleFeature pattern after {} with name {}'.format( sparse_fill_empty_rows.op, sparse_fill_empty_rows.name)) split_for_indices = create_op_with_const_inputs(graph, Split, {1: int64_array(1)}, {'num_splits': 2}) squeeze_for_indices = create_op_with_const_inputs(graph, Squeeze, {1: int64_array([1])}) split_for_dense_shape = create_op_with_const_inputs(graph, Split, {1: int64_array(0)}, {'num_splits': 2}) squeeze_to_scalar = create_op_with_const_inputs(graph, Squeeze, {1: int64_array([0])}) # TODO: remove Cast nodes once we start to support EmbeddingSegmentSum (new version) with segment_ids, # indices, and num_segments of different integer type. # Because the real cases show that it is possible to have it in TensorFlow cast_indices = Cast(graph, {'name': output_node_name + '/CastIndices', 'dst_type': np.int32}).create_node() cast_segment_ids = Cast(graph, {'name': output_node_name + '/CastSegmentIds', 'dst_type': np.int32}).create_node() cast_default_value = Cast(graph, {'name': output_node_name + '/CastDefaultValue', 'dst_type': np.int32}).create_node() cast_num_segments = Cast(graph, {'name': output_node_name + '/CastSegmentsNumber', 'dst_type': np.int32}).create_node() if sparse_segment_op.op == 'SparseSegmentSum': embedding_segments_op = EmbeddingSegmentsSum(graph, {'name': output_node_name}).create_node() else: embedding_segments_op = EmbeddingSegmentsMean(graph, {'name': output_node_name}).create_node() rename_nodes([(select, output_node_name + '/AbandonedName'), (embedding_segments_op, output_node_name)]) # connect parameters table gather.in_port(0).get_connection().set_destination(embedding_segments_op.in_port(0)) # connect indices values greaterequal0.in_port(0).get_connection().set_destination(cast_indices.in_port(0)) embedding_segments_op.in_port(1).connect(cast_indices.out_port(0)) # split and connect segment ids gather0_1.in_port(0).get_connection().set_destination(split_for_indices.in_port(0)) squeeze_for_indices.in_port(0).connect(split_for_indices.out_port(0)) cast_segment_ids.in_port(0).connect(squeeze_for_indices.out_port(0)) embedding_segments_op.in_port(2).connect(cast_segment_ids.out_port(0)) # split and connect number of segments identity_spw.in_port(0).get_connection().set_destination(split_for_dense_shape.in_port(0)) squeeze_to_scalar.in_port(0).connect(split_for_dense_shape.out_port(0)) cast_num_segments.in_port(0).connect(squeeze_to_scalar.out_port(0)) embedding_segments_op.in_port(3).connect(cast_num_segments.out_port(0)) # connect default value sparse_fill_empty_rows.in_port(3).get_connection().set_destination(cast_default_value.in_port(0)) embedding_segments_op.in_port(4).connect(cast_default_value.out_port(0)) # no input port for per_sample_weight identity_spw.in_port(0).disconnect() gather0_1.in_port(0).disconnect() gather0_2.in_port(0).disconnect() greaterequal0.in_port(0).disconnect() sparse_fill_empty_rows.in_port(2).disconnect() gather.in_port(0).disconnect() select.out_port(0).get_connection().set_source(embedding_segments_op.out_port(0)) graph.remove_nodes_from( [gather0_1.id, gather0_2.id, greaterequal0.id, sparse_fill_empty_rows.id, select.id, where0.id])
def replace_resize(graph: Graph, resize: Node): log.debug("Converting of ONNX Resize-10 to Interpolate-4 " "is triggered for node {}.".format( resize.soft_get('name', resize.id))) resize_name = resize.soft_get('name', resize.id) rank_node = Rank(graph, {'name': resize_name + '/max_axes'}).create_node() range_node = create_op_with_const_inputs(graph, Range, { 0: int64_array(2), 2: int64_array(1) }, {'name': resize_name + '/axes'}) sizes_ss = create_op_with_const_inputs(graph, StridedSlice, { 1: int64_array([2]), 2: int64_array([0]), 3: int64_array([1]) }, { 'name': resize_name + '/sizes_ss', 'begin_mask': int64_array([1]), 'end_mask': int64_array([0]), 'new_axis_mask': int64_array([0]), 'shrink_axis_mask': int64_array([0]), 'ellipsis_mask': int64_array([0]) }) scales_ss = create_op_with_const_inputs( graph, StridedSlice, { 1: int64_array([2]), 2: int64_array([0]), 3: int64_array([1]) }, { 'name': resize_name + '/scales_ss', 'begin_mask': int64_array([1]), 'end_mask': int64_array([0]), 'new_axis_mask': int64_array([0]), 'shrink_axis_mask': int64_array([0]), 'ellipsis_mask': int64_array([0]) }) rank_node.out_port(0).connect(range_node.in_port(1)) interpolate_node = Interpolate( graph, { 'version': 'opset4', 'mode': 'linear_onnx' if resize.mode == 'linear' else 'nearest', 'coordinate_transformation_mode': 'asymmetric', 'cube_coeff': -0.75, 'nearest_mode': 'simple', 'pads_begin': int64_array([0]), 'pads_end': int64_array([0]), 'antialias': 0, 'shape_calculation_mode': 'scales', 'in_ports_count': 4 }).create_node() range_node.out_port(0).connect(interpolate_node.in_port(3)) shape_of = Shape(graph, {'name': resize_name + '/ShapeOf'}).create_node() # When we calculate 'sizes' input as floor(input_shape * scales), we can get incorrect 'sizes' if, e.g., # scales = [1.0, 1.0, 1.33333, 2.0], input_shape = [1, 3, 30, 200], because # input_shape * scales = [1, 3, 39.9999, 400], and floor(input_shape * scales)[2] == 39, not 40. # Maybe we need to calculate 'sizes' input as floor(input_shape * scales + eps), where eps is some small # floating point number, e.g. 1.0e-5. But, in this case, if scales = [1.0, 1.0, 1.333333, 2.0], # input_shape = [1, 3, 30, 200], floor(input_shape * scales + eps) = 39, not 40, because # input_shape[2] * scales[2] + 1.0e-5 = 39.99991. # Hence, we need to calculate 'sizes' as floor(input_shape * (scales + eps)). add_node = create_op_with_const_inputs(graph, Add, {1: float_array([1.0e-5])}, {'name': resize_name + '/Add'}) input_data_type = data_type_str_to_np(graph.graph['cmd_params'].data_type) cast_shape_to_float = Cast(graph, { 'dst_type': input_data_type }).create_node() shape_of.out_port(0).connect(cast_shape_to_float.in_port(0)) mul_node = Mul(graph, { 'name': resize_name + '/Mul' }).create_node([cast_shape_to_float, add_node]) floor_node = Floor(graph, { 'name': resize_name + '/Floor' }).create_node([mul_node]) cast_mul_result_to_int = Cast(graph, { 'dst_type': np.int64 }).create_node([floor_node]) cast_mul_result_to_int.out_port(0).connect(sizes_ss.in_port(0)) sizes_ss.out_port(0).connect(interpolate_node.in_port(1)) scales_ss.out_port(0).connect(interpolate_node.in_port(2)) connection_of_resize_input = resize.in_port(0).get_connection() connection_of_resize_input.set_destination(interpolate_node.in_port(0)) connection_of_scales = resize.in_port(1).get_connection() connection_of_scales.set_destination(scales_ss.in_port(0)) connection_of_resize_input.get_source().connect(shape_of.in_port(0)) connection_of_resize_input.get_source().connect(rank_node.in_port(0)) connection_of_scales.get_source().connect(add_node.in_port(0)) rename_nodes([(resize, resize_name + '/delete'), (interpolate_node, resize_name)]) resize.out_port(0).get_connection().set_source( interpolate_node.out_port(0))
def replace_sub_graph(self, graph: Graph, match: dict): identity_spw = match['identity_spw'] gather0_1 = match['gather0_1'] gather0_2 = match['gather0_2'] greaterequal0 = match['greaterequal0'] sparse_fill_empty_rows = match['sparse_fill_empty_rows'] gather = match['gather'] select = match['select'] where0 = match['where0'] output_node_name = select.soft_get('name', select.id) log.debug( 'Found EmbeddingSegmentsSum2 pattern after {} with name {}'.format( sparse_fill_empty_rows.op, sparse_fill_empty_rows.name)) split_for_indices = create_op_with_const_inputs( graph, Split, {1: int64_array(1)}, { 'num_splits': 2, 'name': output_node_name + '/SplitForIndices' }) squeeze_for_indices = create_op_with_const_inputs( graph, Squeeze, {1: int64_array([1])}) split_for_dense_shape = create_op_with_const_inputs( graph, Split, {1: int64_array(0)}, { 'num_splits': 2, 'name': output_node_name + '/SplitForDenseShape' }) squeeze_to_scalar = create_op_with_const_inputs( graph, Squeeze, {1: int64_array([0])}) cast_segment_ids = Cast(graph, { 'name': output_node_name + '/CastSegmentIds', 'dst_type': np.int32 }).create_node() cast_default_value = Cast(graph, { 'name': output_node_name + '/CastDefaultValue', 'dst_type': np.int32 }).create_node() cast_num_segments = Cast(graph, { 'name': output_node_name + '/CastSegmentsNumber', 'dst_type': np.int32 }).create_node() embedding_segments_sum = EmbeddingSegmentsSum(graph, { 'name': output_node_name }).create_node() rename_nodes([(select, output_node_name + '/AbandonedName'), (embedding_segments_sum, output_node_name)]) # connect parameters table gather.in_port(0).get_connection().set_destination( embedding_segments_sum.in_port(0)) # connect indices values greaterequal0.in_port(0).get_connection().set_destination( embedding_segments_sum.in_port(1)) # split and connect segment ids gather0_1.in_port(0).get_connection().set_destination( split_for_indices.in_port(0)) squeeze_for_indices.in_port(0).connect(split_for_indices.out_port(0)) # TODO: remove casting once we start to support I64 model input cast_segment_ids.in_port(0).connect(squeeze_for_indices.out_port(0)) embedding_segments_sum.in_port(2).connect(cast_segment_ids.out_port(0)) # split and connect number of segments identity_spw.in_port(0).get_connection().set_destination( split_for_dense_shape.in_port(0)) squeeze_to_scalar.in_port(0).connect(split_for_dense_shape.out_port(0)) # TODO: remove casting once we start to support I64 model input cast_num_segments.in_port(0).connect(squeeze_to_scalar.out_port(0)) embedding_segments_sum.in_port(3).connect( cast_num_segments.out_port(0)) # connect default value # TODO: remove casting once we start to support I64 model input sparse_fill_empty_rows.in_port(3).get_connection().set_destination( cast_default_value.in_port(0)) embedding_segments_sum.in_port(4).connect( cast_default_value.out_port(0)) # no input port for per_sample_weight identity_spw.in_port(0).disconnect() gather0_1.in_port(0).disconnect() gather0_2.in_port(0).disconnect() greaterequal0.in_port(0).disconnect() sparse_fill_empty_rows.in_port(2).disconnect() gather.in_port(0).disconnect() select.out_port(0).get_connection().set_source( embedding_segments_sum.out_port(0)) graph.remove_nodes_from([ gather0_1.id, gather0_2.id, greaterequal0.id, sparse_fill_empty_rows.id, select.id, where0.id ])
def replace_pattern(self, graph: Graph, match: Dict[str, Node]): group_norm_node = match['op'] group_norm_num_input_dims = len( group_norm_node.in_port(0).data.get_shape()) # node computing initial GroupNorm input shape initial_shape_op_node = Shape(graph, { 'name': group_norm_node.name + '/Shape' }).create_node() initial_shape_op_node.in_port(0).connect( group_norm_node.in_port(0).get_source()) initial_shape_op_node_float = Cast( graph, { 'name': initial_shape_op_node.name + '/to_float', 'dst_type': data_type_str_to_np(graph.graph['cmd_params'].data_type) }).create_node() initial_shape_op_node.out_port(0).connect( initial_shape_op_node_float.in_port(0)) initial_batch_dim_node = node_to_get_batch_value( initial_shape_op_node_float) initial_features_dim_node = node_to_get_features_dimension_value( initial_shape_op_node_float) initial_spatial_dims_node_int = node_to_get_spatial_dimensions_value( initial_shape_op_node) initial_spatial_dims_node = Cast( graph, { 'name': initial_spatial_dims_node_int.name + '/to_float', 'dst_type': data_type_str_to_np(graph.graph['cmd_params'].data_type) }).create_node() initial_spatial_dims_node_int.out_port(0).connect( initial_spatial_dims_node.in_port(0)) group_size_node = Const( graph, { 'value': int64_array([group_norm_node.num_groups]), 'name': group_norm_node.name + '/GroupSize' }).create_node() # calculate "features // group_size" value reciprocal_group_size_node = Const( graph, { 'value': np.array([1.0 / group_norm_node.num_groups]), 'name': group_norm_node.name + '/ReciprocalGroupSize' }).create_node() c_div_g_node = Mul(graph, {}).create_node() c_div_g_node.in_port(0).connect(initial_features_dim_node.out_port(0)) c_div_g_node.in_port(1).connect(reciprocal_group_size_node.out_port(0)) batch_mul_group_size_node = Mul(graph, {}).create_node() batch_mul_group_size_node.in_port(0).connect( initial_batch_dim_node.out_port(0)) batch_mul_group_size_node.in_port(1).connect( group_size_node.out_port(0)) # create new node which concatenates several dims to one new_shape_node_float = new_shape_node_from_shape_nodes([ batch_mul_group_size_node, c_div_g_node, initial_spatial_dims_node ]) new_shape_node = Cast(graph, { 'name': new_shape_node_float.name + '/to_int64', 'dst_type': np.int64 }).create_node() new_shape_node_float.out_port(0).connect(new_shape_node.in_port(0)) reshape_for_mvn_node = Reshape(graph, {}).create_node() group_norm_node.in_port(0).get_connection().set_destination( reshape_for_mvn_node.in_port(0)) reshape_for_mvn_node.in_port(1).connect(new_shape_node.out_port(0)) # Reshape the gamma and beta constants to correct layout from [C] to [1,C], [1,C,1], [1,C,1,1] etc gamma_beta_shape = np.ones([group_norm_num_input_dims], dtype=np.int64) gamma_beta_shape[1] = -1 gamma_value = group_norm_node.in_port(1).get_source().data.get_value() beta_value = group_norm_node.in_port(2).get_source().data.get_value() assert gamma_value is not None, 'The gamma should be constant' assert beta_value is not None, 'The beta should be constant' gamma_value = np.reshape(gamma_value, gamma_beta_shape) group_norm_node.in_port(1).get_source().data.set_value(gamma_value) beta_value = np.reshape(beta_value, gamma_beta_shape) group_norm_node.in_port(2).get_source().data.set_value(beta_value) # MVN mvn_node = MVN( graph, { 'name': group_norm_node.name + '/MVN', 'normalize_variance': 1, 'eps': group_norm_node.eps, 'eps_mode': 'inside_sqrt' }).create_node() mvn_node.in_port(0).connect(reshape_for_mvn_node.out_port(0)) # MVN axes _, rank = get_shape_and_rank_nodes_by_port( mvn_node.in_port(0).get_connection().get_source(), return_as_a_scalar=True) rng = create_op_with_const_inputs(graph, Range, { 0: int64_array(1), 2: int64_array(1) }, { 'name': group_norm_node.name + '/Range', 'output_type': np.int64 }) mvn_node.in_port(1).connect(rng.out_port(0)) rng.in_port(1).connect(rank.out_port(0)) # reshape to the initial shape before multiplying with gamma and adding beta reshape_to_initial_shape_node = Reshape(graph, {}).create_node() reshape_to_initial_shape_node.in_port(0).connect(mvn_node.out_port(0)) reshape_to_initial_shape_node.in_port(1).connect( initial_shape_op_node.out_port(0)) mul_node = Mul(graph, {'name': mvn_node.name + '/Mul'}).create_node() mul_node.in_port(0).connect(reshape_to_initial_shape_node.out_port(0)) group_norm_node.in_port(1).get_connection().set_destination( mul_node.in_port(1)) add_node = Add(graph, {'name': mul_node.name + '/Add'}).create_node() add_node.in_port(0).connect(mul_node.out_port(0)) group_norm_node.in_port(2).get_connection().set_destination( add_node.in_port(1)) group_norm_node.out_port(0).get_connection().set_source( add_node.out_port(0))
def dequantize_data(fake_quantize: Node, dst_type: type, quantized_type: type) -> Node: graph = fake_quantize.graph quantized_data = fake_quantize.in_port(0).get_source().node name = fake_quantize.soft_get('name', fake_quantize.id) assert quantized_data.soft_get('type') == 'Convert' and quantized_data.dst_type == quantized_type, \ 'Weights aren`t compressed as expected for node {}'.format(fake_quantize.soft_get('name', fake_quantize.id)) dequantizing_cast = Cast( graph, dict(name=quantized_data.name + "/to_{}".format(np_data_type_to_destination_type(dst_type)), dst_type=dst_type, stop_value_propagation=True)).create_node() fake_quantize.in_port(0).get_connection().set_destination( dequantizing_cast.in_port(0)) # limits of dequantize in_low = fake_quantize.in_port(1).get_source() in_high = fake_quantize.in_port(2).get_source() out_low = fake_quantize.in_port(3).get_source() out_high = fake_quantize.in_port(4).get_source() # scale calculation output_range = Sub(graph, { 'name': name + '/output_range' }).create_node() output_range.in_port(0).connect(out_high) output_range.in_port(1).connect(out_low) input_range = Sub(graph, {'name': name + '/input_range'}).create_node() input_range.in_port(0).connect(in_high) input_range.in_port(1).connect(in_low) scale = Div(graph, {'name': name + '/scale'}).create_node() scale.in_port(0).connect(output_range.out_port(0)) scale.in_port(1).connect(input_range.out_port(0)) # shift calculation descaled_output_low = Div(graph, { 'name': name + '/descaled_output_low' }).create_node() descaled_output_low.in_port(0).connect(out_low) descaled_output_low.in_port(1).connect(scale.out_port(0)) shift = Sub(graph, {'name': name + '/shift'}).create_node() shift.in_port(0).connect(in_low) shift.in_port(1).connect(descaled_output_low.out_port(0)) zero = Const(graph, { 'name': name + '/zero', 'value': np.array(0, dtype=dst_type) }).create_node() scale_eq_zero = Equal(graph, { 'name': name + '/scale_eq_zero' }).create_node() scale_eq_zero.in_port(0).connect(scale.out_port(0)) scale_eq_zero.in_port(1).connect(zero.out_port(0)) zero_point = Select(graph, { 'name': name + '/zero_point' }).create_node() zero_point.in_port(0).connect(scale_eq_zero.out_port(0)) zero_point.in_port(1).connect(zero.out_port(0)) zero_point.in_port(2).connect(shift.out_port(0)) # DeQuantize(x) == Mul(Sub(x, zero_point), scale) sub_zp = Sub(graph, {'name': name + '/minus_zp'}).create_node() sub_zp.in_port(0).connect(dequantizing_cast.out_port(0)) sub_zp.in_port(1).connect(zero_point.out_port(0)) mul_scale = Mul(graph, { 'name': name + '/mulpiply_by_scale' }).create_node() mul_scale.in_port(0).connect(sub_zp.out_port(0)) mul_scale.in_port(1).connect(scale.out_port(0)) fake_quantize.out_port(0).get_connection().set_source( mul_scale.out_port(0)) graph.remove_nodes_from([fake_quantize.id, fake_quantize.out_node(0)])
def replace_sub_graph(self, graph: Graph, match: dict): seq_len_tf = match['seq_len'] transpose_tf = match['transpose'] ctc_greedy_decoder_tf = match['ctc_greedy_decoder'] cast_tf = match['cast'] ctc_loss_tf = match['ctc_loss'] sparse_to_dense_tf = match['sparse_to_dense'] output_sparse_to_dense_name = sparse_to_dense_tf.soft_get( 'name', sparse_to_dense_tf.id) output_ctc_loss_name = ctc_loss_tf.soft_get('name', ctc_loss_tf.id) ctc_greedy_decoder_tf_name = ctc_greedy_decoder_tf.soft_get( 'name', ctc_greedy_decoder_tf.id) log.debug( 'Found CTCLossFrontReplacer pattern after {} with name {}'.format( ctc_greedy_decoder_tf.op, ctc_greedy_decoder_tf.name)) # create sequence mask node, sub-graph for transforming into sequence length and connect with consumers seq_len_tf_shape = seq_len_tf.soft_get('shape', None) if seq_len_tf_shape is None or len(seq_len_tf_shape) != 2: raise Error( 'The sequence length that is the second input to the CTCGreedyDecoder node "{}"' ' must be specified in a mask format.'.format( ctc_greedy_decoder_tf_name)) log.error( 'The format of input sequence length has been changed to a mask format', extra={'is_warning': True}) seq_len_tf_type = seq_len_tf.soft_get('data_type', None) seq_len_tf_name = seq_len_tf.soft_get('name', seq_len_tf.id) seq_mask_placeholder = Parameter( graph, { 'name': seq_len_tf_name, 'shape': seq_len_tf_shape, 'data_type': seq_len_tf_type }).create_node() reduce_to_seq_len_node = create_op_with_const_inputs( graph, ReduceSum, {1: np.array(1, dtype=np.int32)}, { 'name': seq_len_tf_name + '/ReduceToSeqLen', 'keep_dims': False }) reduce_to_seq_len_node.in_port(0).connect( seq_mask_placeholder.out_port(0)) seq_len_tf.out_port(0).get_connection().set_source( reduce_to_seq_len_node.out_port(0)) cast_fp_type = data_type_str_to_np(graph.graph['cmd_params'].data_type) casted_seq_mask_node = Cast(graph, { 'name': seq_len_tf_name + '/CastToFP32', 'dst_type': cast_fp_type }).create_node() casted_seq_mask_node.in_port(0).connect( seq_mask_placeholder.out_port(0)) permuted_casted_seq_mask = create_op_with_const_inputs( graph, Transpose, {1: int64_array([1, 0])}, {'name': seq_len_tf_name + '/Permute'}) permuted_casted_seq_mask.in_port(0).connect( casted_seq_mask_node.out_port(0)) rename_nodes([(seq_len_tf, seq_len_tf_name + '/AbandonedName'), (seq_mask_placeholder, seq_len_tf_name)]) # create CTCGreedyDecoder node and set mask node ctc_merge_repeated_i = ctc_greedy_decoder_tf.soft_get( 'ctc_merge_repeated', ctc_greedy_decoder_tf.id) ctc_greedy_decoder = CTCGreedyDecoderOp( graph, { 'name': output_sparse_to_dense_name, 'ctc_merge_repeated': ctc_merge_repeated_i }).create_node() ctc_greedy_decoder.in_port(1).connect( permuted_casted_seq_mask.out_port(0)) rename_nodes([(sparse_to_dense_tf, output_sparse_to_dense_name + '/AbandonedName'), (ctc_greedy_decoder, output_sparse_to_dense_name)]) # create CTCLoss node and set attributes assert ctc_loss_tf.has_valid('preprocess_collapse_repeated'), \ 'The CTCLoss node "{}" misses "preprocess_collapse_repeated" attribute'.format(output_ctc_loss_name) assert ctc_loss_tf.has_valid('ctc_merge_repeated'), \ 'The CTCLoss node "{}" misses "ctc_merge_repeated" attribute'.format(output_ctc_loss_name) assert ctc_loss_tf.has_valid('unique'), \ 'The CTCLoss node "{}" misses "unique" attribute'.format(output_ctc_loss_name) preprocess_collapse_repeated = ctc_loss_tf.preprocess_collapse_repeated ctc_merge_repeated = ctc_loss_tf.ctc_merge_repeated unique = ctc_loss_tf.unique ctc_loss = CTCLoss( graph, { 'name': output_ctc_loss_name, 'preprocess_collapse_repeated': preprocess_collapse_repeated, 'ctc_merge_repeated': ctc_merge_repeated, 'unique': unique }).create_node() rename_nodes([(ctc_loss_tf, output_ctc_loss_name + '/AbandonedName'), (ctc_loss, output_ctc_loss_name)]) # connect logits ctc_greedy_decoder_tf.in_port(0).get_connection().set_destination( ctc_greedy_decoder.in_port(0)) ctc_loss.in_port(0).disconnect() transpose_tf.in_port(0).get_connection().add_destination( ctc_loss.in_port(0)) # connect logit lengths ctc_greedy_decoder_tf.in_port(1).disconnect() ctc_loss.in_port(1).connect(reduce_to_seq_len_node.out_port(0)) # connect labels to ctc_loss squeeze_op = create_op_with_const_inputs(graph, Squeeze, {1: int64_array([2, 3])}) cast_labels_op = Cast( graph, { 'name': output_sparse_to_dense_name + '/CastLabels', 'dst_type': np.int32 }).create_node() squeeze_op.in_port(0).connect(ctc_greedy_decoder.out_port(0)) cast_labels_op.in_port(0).connect(squeeze_op.out_port(0)) ctc_loss.in_port(2).connect(cast_labels_op.out_port(0)) # connect label lengths equal_op = create_op_with_const_inputs( graph, Equal, {1: np.array([-1], dtype=np.int32)}, {'name': output_sparse_to_dense_name + '/Equal'}) equal_op.in_port(0).connect(cast_labels_op.out_port(0)) labels_shape_op = Shape( graph, { 'name': output_sparse_to_dense_name + '/ShapeOf' }).create_node() labels_shape_op.in_port(0).connect(equal_op.out_port(0)) broadcast_one = create_op_with_const_inputs( graph, Broadcast, {0: np.array([1], dtype=np.int32)}, { 'mode': 'numpy', 'name': output_sparse_to_dense_name + '/One' }) broadcast_one.in_port(1).connect(labels_shape_op.out_port(0)) broadcast_zero = create_op_with_const_inputs( graph, Broadcast, {0: np.array([0], dtype=np.int32)}, { 'mode': 'numpy', 'name': output_sparse_to_dense_name + '/Zero' }) broadcast_zero.in_port(1).connect(labels_shape_op.out_port(0)) select_node = Select(graph, { 'name': output_sparse_to_dense_name + '/Select' }).create_node() select_node.in_port(0).connect(equal_op.out_port(0)) select_node.in_port(1).connect(broadcast_zero.out_port(0)) select_node.in_port(2).connect(broadcast_one.out_port(0)) label_length_node = create_op_with_const_inputs( graph, ReduceSum, {1: int64_array([1])}, op_attrs={ 'name': output_sparse_to_dense_name + '/LabelLength', 'keep_dims': False }) label_length_node.in_port(0).connect(select_node.out_port(0)) ctc_loss.in_port(3).connect(label_length_node.out_port(0)) # set source for output of new sub-graph and remove old nodes ctc_loss_tf.out_port(0).get_connection().set_source( ctc_loss.out_port(0)) graph.remove_nodes_from([ ctc_greedy_decoder_tf.id, ctc_loss_tf.id, cast_tf.id, sparse_to_dense_tf.id ])
def replace_pattern(self, graph: Graph, match: dict): unsqueeze_node = match['unsqueeze'] unsqueeze_name = unsqueeze_node.name second_input_of_unsqueeze = unsqueeze_node.in_port( 1).get_connection().get_source().node if not second_input_of_unsqueeze.has_valid('value'): return d_idx = int(second_input_of_unsqueeze.value) second_input_of_tile = match['tile'].in_port( 1).get_connection().get_source().node if not second_input_of_tile.has_valid('value'): return input_shape_of_unsqueeze = unsqueeze_node.in_port(0).data.get_shape() if len(input_shape_of_unsqueeze) not in {4, 5}: return scale = float32_array([second_input_of_tile.value[d_idx]]) axis = d_idx - 1 axis_node = Const(graph, { 'name': unsqueeze_name + '/axis', 'value': int64_array([axis]) }).create_node() shape_node = Shape(graph, dict(name=unsqueeze_name + '/Shape')).create_node() scales_node = Const(graph, dict(name=unsqueeze_name + '/scales', value=scale)).create_node() mul_node = Mul(graph, dict(name=unsqueeze_name + '/Mul')).create_node() scales_node.out_port(0).connect(mul_node.in_port(1)) slice_begin = Const( graph, dict(name=unsqueeze_name + '/slice_begin', value=int64_array([axis]))).create_node() slice_end = Const( graph, dict(name=unsqueeze_name + '/slice_end', value=int64_array([axis + 1]))).create_node() strided_slice_node = StridedSlice( graph, { 'name': unsqueeze_name + '/StridedSlice', 'begin_mask': int64_array([1]), 'end_mask': int64_array([1]), 'new_axis_mask': int64_array([0]), 'shrink_axis_mask': int64_array([0]), 'ellipsis_mask': int64_array([0]), }).create_node() shape_node.out_port(0).connect(strided_slice_node.in_port(0)) slice_begin.out_port(0).connect(strided_slice_node.in_port(1)) slice_end.out_port(0).connect(strided_slice_node.in_port(2)) cast_shape_to_float = Cast(graph, { 'dst_type': np.float32 }).create_node() strided_slice_node.out_port(0).connect(cast_shape_to_float.in_port(0)) cast_shape_to_float.out_port(0).connect(mul_node.in_port(0)) interp_node = Interpolate( graph, dict(mode='nearest', antialias=0, pads_begin=int64_array([0]), pads_end=int64_array([0]), coordinate_transformation_mode='half_pixel', nearest_mode='round_prefer_floor', cube_coeff=-0.75, version='opset4', shape_calculation_mode='scales', in_ports_count=4, maybe_part_of_sequence=True)).create_node() floor_node = Floor(graph, { 'name': unsqueeze_name + '/Floor' }).create_node() cast_mul_result_to_int = Cast(graph, { 'dst_type': np.int64 }).create_node() mul_node.out_port(0).connect(floor_node.in_port(0)) floor_node.out_port(0).connect(cast_mul_result_to_int.in_port(0)) cast_mul_result_to_int.out_port(0).connect(interp_node.in_port(1)) scales_node.out_port(0).connect(interp_node.in_port(2)) axis_node.out_port(0).connect(interp_node.in_port(3)) reshape_node = match['reshape'] reshape_node.out_port(0).get_connection().set_source( interp_node.out_port(0)) reshape_name = reshape_node.soft_get('name', reshape_node.id) rename_nodes([(reshape_node, reshape_name + '/delete'), (interp_node, reshape_name)]) unsqueeze_connection = match['unsqueeze'].in_port(0).get_connection() before_unsqueeze = unsqueeze_connection.get_source().node unsqueeze_connection.set_destination(interp_node.in_port(0)) before_unsqueeze.out_port(0).connect(shape_node.in_port(0))
def replace_interpolate_pattern(graph: Graph, match: dict): split = match['split'] scale = np.array([get_split_scale(split)], dtype=np.float32) axis = int(split.in_port(1).get_connection().get_source().node.value) split_node_name = split.name axis_node = Const(graph, { 'name': split_node_name + '/axis', 'value': int64_array([axis]) }).create_node() shape_node = Shape(graph, dict(name=split_node_name + '/Shape')).create_node() scales_node = Const(graph, dict(name=split_node_name + '/scales', value=scale)).create_node() mul_node = Mul(graph, dict(name=split_node_name + '/Mul')).create_node() scales_node.out_port(0).connect(mul_node.in_port(1)) strided_slice_node = create_op_with_const_inputs( graph, StridedSlice, { 1: int64_array([axis]), 2: int64_array([axis + 1]) }, { 'name': split_node_name + '/StridedSlice', 'begin_mask': int64_array([1]), 'end_mask': int64_array([1]), 'new_axis_mask': int64_array([0]), 'shrink_axis_mask': int64_array([0]), 'ellipsis_mask': int64_array([0]) }) shape_node.out_port(0).connect(strided_slice_node.in_port(0)) cast_shape_to_float = Cast(graph, {'dst_type': np.float32}).create_node() strided_slice_node.out_port(0).connect(cast_shape_to_float.in_port(0)) cast_shape_to_float.out_port(0).connect(mul_node.in_port(0)) interp_node = Interpolate( graph, dict(name=split_node_name + '/Interpolate', mode='nearest', antialias=0, pads_begin=int64_array([0]), pads_end=int64_array([0]), coordinate_transformation_mode='half_pixel', nearest_mode='round_prefer_floor', cube_coeff=-0.75, version='opset4', shape_calculation_mode='scales', in_ports_count=4, maybe_part_of_sequence=True)).create_node() floor_node = Floor(graph, { 'name': split_node_name + '/Floor' }).create_node() cast_mul_result_to_int = Cast(graph, {'dst_type': np.int64}).create_node() mul_node.out_port(0).connect(floor_node.in_port(0)) floor_node.out_port(0).connect(cast_mul_result_to_int.in_port(0)) cast_mul_result_to_int.out_port(0).connect(interp_node.in_port(1)) scales_node.out_port(0).connect(interp_node.in_port(2)) axis_node.out_port(0).connect(interp_node.in_port(3)) match['concat'].out_port(0).get_connection().set_source( interp_node.out_port(0)) split_connection = split.in_port(0).get_connection() split_connection.set_destination(interp_node.in_port(0)) split_connection.get_source().connect(shape_node.in_port(0))