Ejemplo n.º 1
0
    def __init__(self):
        sequence_resize = GraphSequence([ConverterSequenceNode('root', ['ResizeBilinear'])])
        sequence_resize.set_outputs(['root'])

        sequence_shape_stridedslice_resize = GraphSequence([
            NonConsumableConverterSequenceNode('input', ['?']),
            ConverterSequenceNode('shape', ['Shape']),
            ConverterSequenceNode('stridedSlice', ['StridedSlice']),
            ConverterSequenceNode('mul', ['Mul']),
            ConverterSequenceNode('const_stridedSlice_1', ['?']),
            ConverterSequenceNode('const_stridedSlice_2', ['?']),
            ConverterSequenceNode('const_stridedSlice_3', ['?']),
            ConverterSequenceNode('mul_const', ['?']),
            ConverterSequenceNode('root', ['ResizeBilinear'])])

        sequence_shape_stridedslice_resize.set_inputs('shape', ['input'])
        sequence_shape_stridedslice_resize.set_inputs('stridedSlice', ['shape',
                                                                       'const_stridedSlice_1',
                                                                       'const_stridedSlice_2',
                                                                       'const_stridedSlice_3'])
        sequence_shape_stridedslice_resize.set_inputs('mul', ['stridedSlice', 'mul_const'])
        sequence_shape_stridedslice_resize.set_inputs('root', ['mul', 'input'])
        sequence_shape_stridedslice_resize.set_outputs(['root'])

        self.sequences = [sequence_resize, sequence_shape_stridedslice_resize]
Ejemplo n.º 2
0
class Relu6LayerResolver(ReluMinMaxLayerResolver, object):
    class Descriptor(ReluMinMaxLayerResolver.Descriptor):
        def __init__(self, name, nodes):
            super(Relu6LayerResolver.Descriptor, self).__init__('Relu6',
                                                                name,
                                                                nodes,
                                                                min_clamp=0,
                                                                max_clamp=6)

    def __init__(self):
        self.sequence = GraphSequence(
            [ConverterSequenceNode('root', ['Relu6'])])
        self.sequence.set_outputs(['root'])

    def resolve_layer(self, graph_matcher, graph_helper):
        matches = graph_matcher.match_sequence(self.sequence)
        if len(matches) == 0:
            return []
        potential_descriptors = []
        for match in matches:
            relu6_op = match['root']
            consumed_nodes = match.consumed_nodes
            potential_descriptors.append(
                Relu6LayerResolver.Descriptor(str(relu6_op.name),
                                              consumed_nodes))
        return potential_descriptors
Ejemplo n.º 3
0
class FillLayerResolver(LayerResolver, object):
    class Descriptor(LayerDescriptor):
        def __init__(self, name, nodes, shape, scalar):
            super(FillLayerResolver.Descriptor, self).__init__('Fill', name, nodes)
            self.shape = shape
            self.scalar = scalar

    def __init__(self):
        self.sequence = GraphSequence([ConverterSequenceNode('root', ['Fill'])])
        self.sequence.set_outputs(['root'])

    def resolve_layer(self, graph_matcher, graph_helper):
        matches = graph_matcher.match_sequence(self.sequence)
        if len(matches) == 0:
            return []
        descriptors = []
        for match in matches:
            fill_op = match['root']
            consumed_nodes = match.consumed_nodes
            shape_tensor, scalar_tensor = GraphHelper.get_op_input_tensors(fill_op, ('?', 'Const'))
            shape = graph_helper.evaluate_tensor_output(shape_tensor).tolist()
            while len(shape) > 3:
                shape = shape[1:]

            while len(shape) < 3:
                shape = [1] + shape
            scalar = graph_helper.evaluate_tensor_output(scalar_tensor)

            d = FillLayerResolver.Descriptor(str(fill_op.name), consumed_nodes, shape, scalar)
            descriptors.append(d)

        return descriptors
Ejemplo n.º 4
0
class ReluLayerResolver(LayerResolver, object):
    class Descriptor(LayerDescriptor):
        def __init__(self, layer_type, name, nodes):
            super(ReluLayerResolver.Descriptor,
                  self).__init__(layer_type, name, nodes)

        @property
        def output_names(self):
            return [str(self.child_ops[0].outputs[0].name)]

        def is_output_op(self, op):
            return op in self.child_ops

        def get_output_names_for(self, input_tensors):
            return self.output_names

    def __init__(self):
        self.sequence = GraphSequence(
            [ConverterSequenceNode('root', ['Relu'])])
        self.sequence.set_outputs(['root'])

    def resolve_layer(self, graph_matcher, graph_helper):
        matches = graph_matcher.match_sequence(self.sequence)
        if len(matches) == 0:
            return []
        potential_descriptors = []
        for match in matches:
            relu_op = match['root']
            consumed_nodes = match.consumed_nodes
            potential_descriptors.append(
                ReluLayerResolver.Descriptor('RELU', str(relu_op.name),
                                             consumed_nodes))
        return potential_descriptors
Ejemplo n.º 5
0
    def __init__(self):
        sequence_reshape = GraphSequence([ConverterSequenceNode('root', ['Reshape', 'Squeeze', 'ExpandDims'])])
        sequence_reshape.set_outputs(['root'])

        sequence_flatten = GraphSequence([
            NonConsumableConverterSequenceNode('input', ['?']),
            ConverterSequenceNode('shape', ['Shape']),
            ConverterSequenceNode('slice_1', ['Slice']),
            ConverterSequenceNode('const_1', ['Const']),
            ConverterSequenceNode('const_2', ['Const']),
            ConverterSequenceNode('slice_2', ['Slice']),
            ConverterSequenceNode('const_3', ['Const']),
            ConverterSequenceNode('const_4', ['Const']),
            ConverterSequenceNode('prod', ['Prod']),
            ConverterSequenceNode('const_5', ['Const']),
            ConverterSequenceNode('expand_dims', ['ExpandDims']),
            ConverterSequenceNode('const_6', ['Const']),
            ConverterSequenceNode('concat', ['ConcatV2']),
            ConverterSequenceNode('const_7', ['Const']),
            ConverterSequenceNode('root', ['Reshape']),
        ])
        sequence_flatten.set_inputs('shape', ['input'])
        sequence_flatten.set_inputs('slice_1', ['shape', 'const_1', 'const_2'])
        sequence_flatten.set_inputs('slice_2', ['shape', 'const_3', 'const_4'])
        sequence_flatten.set_inputs('prod', ['slice_2', 'const_5'])
        sequence_flatten.set_inputs('expand_dims', ['prod', 'const_6'])
        sequence_flatten.set_inputs('concat', ['slice_1', 'expand_dims', 'const_7'])
        sequence_flatten.set_inputs('root', ['input', 'concat'])
        sequence_flatten.set_outputs(['root'])

        self.sequences = [sequence_reshape, sequence_flatten]
Ejemplo n.º 6
0
class LrnLayerResolver(LayerResolver, object):
    class Descriptor(LayerDescriptor):
        def __init__(self, name, operations, window_size, alpha, beta, bias):
            super(LrnLayerResolver.Descriptor,
                  self).__init__('LRN', name, operations)
            self.window_size = window_size
            self.alpha = alpha
            self.beta = beta
            self.bias = bias

    def __init__(self):
        self.sequence = GraphSequence([ConverterSequenceNode('root', ['LRN'])])
        self.sequence.set_outputs(['root'])

    def resolve_layer(self, graph_matcher, graph_helper):
        matches = graph_matcher.match_sequence(self.sequence)
        if len(matches) == 0:
            return []
        potential_descriptors = []
        for match in matches:
            lrn_op = match['root']
            window_size = 1 + lrn_op.get_attr('depth_radius') * 2
            alpha = lrn_op.get_attr('alpha')
            beta = lrn_op.get_attr('beta')
            bias = lrn_op.get_attr('bias')
            consumed_nodes = match.consumed_nodes
            potential_descriptors.append(
                LrnLayerResolver.Descriptor(str(lrn_op.name), consumed_nodes,
                                            window_size, alpha, beta, bias))
        return potential_descriptors
Ejemplo n.º 7
0
    def __init__(self):
        sequence_two_dim_softmax = GraphSequence(
            [ConverterSequenceNode('root', ['SoftMax'])])
        sequence_two_dim_softmax.set_outputs(['root'])

        sequence_multi_dim_softmax = GraphSequence([
            ConverterSequenceNode('max', ['Max']),
            ConverterSequenceNode('max_reduction_indicies', ['Const']),
            ConverterSequenceNode('sub', ['Sub']),
            ConverterSequenceNode('exp', ['Exp']),
            ConverterSequenceNode('sum', ['Sum']),
            ConverterSequenceNode('sum_reduction_indicies', ['Const']),
            ConverterSequenceNode('root', ['RealDiv']),
            NonConsumableConverterSequenceNode('input', ['?'])
        ])
        sequence_multi_dim_softmax.set_inputs(
            'max', ['input', 'max_reduction_indicies'])
        sequence_multi_dim_softmax.set_inputs('sub', ['input', 'max'])
        sequence_multi_dim_softmax.set_inputs('exp', ['sub'])
        sequence_multi_dim_softmax.set_inputs(
            'sum', ['exp', 'sum_reduction_indicies'])
        sequence_multi_dim_softmax.set_inputs('root', ['exp', 'sum'])
        sequence_multi_dim_softmax.set_outputs(['root'])

        self.sequences = [sequence_two_dim_softmax, sequence_multi_dim_softmax]
Ejemplo n.º 8
0
class CropLayerResolver(LayerResolver, object):

    class Descriptor(LayerDescriptor):
        def __init__(self, name, nodes, offset, size, output_names=None):
            super(CropLayerResolver.Descriptor, self).__init__('Crop', name, nodes, output_names=output_names)
            self.offset = offset
            self.size = size

    def __init__(self):
        self.sequence = GraphSequence([
            ConverterSequenceNode('root', ['Slice']),
            NonConsumableConverterSequenceNode('input', ['?']),
            NonConsumableConverterSequenceNode('offsets', ['?']),
            NonConsumableConverterSequenceNode('size', ['?']),
        ])
        self.sequence.set_inputs('root', ['input', 'offsets', 'size'])
        self.sequence.set_outputs(['root'])

    def resolve_layer(self, graph_matcher, graph_helper):
        matches = graph_matcher.match_sequence(self.sequence)
        descriptors = []
        for match in matches:
            slice_op = match['root']
            input_shape = graph_helper.get_op_output_shape(match['input'])
            offset = graph_helper.evaluate_tensor_output(match['offsets'].outputs[0])
            size = graph_helper.evaluate_tensor_output(match['size'].outputs[0])
            for index in range(0, len(size)):
                if size[index] == -1:
                    size[index] = input_shape[index] - offset[index]

            consumed_nodes = match.consumed_nodes
            descriptors.append(
                CropLayerResolver.Descriptor(str(slice_op.name), consumed_nodes, offset, size))
        return descriptors
Ejemplo n.º 9
0
class DilatedConvolutionLayerResolver(ConvolutionLayerResolver, object):
    class Descriptor(ConvolutionLayerResolver.Descriptor):
        pass

    def __init__(self):
        super(DilatedConvolutionLayerResolver, self).__init__()
        self.graph_sequence = GraphSequence([
            ConverterSequenceNode('space_to_batch', ['SpaceToBatchND']),
            NonConsumableConverterSequenceNode('inputs', ['?']),
            ConverterSequenceNode('dilation_sizes', ['?']),
            ConverterSequenceNode('paddings', ['?']),
            ConverterSequenceNode('conv_op', ['Conv2D']),
            ConverterSequenceNode('kernel', ['?']),
            ConverterSequenceNode('batch_to_space', ['BatchToSpaceND']),
            ConverterSequenceNode('block_shape_out', ['?']),
            ConverterSequenceNode('crops', ['?'])]
        )
        self.graph_sequence.set_inputs('space_to_batch', ['inputs', 'dilation_sizes', 'paddings'])
        self.graph_sequence.set_inputs('conv_op', ['space_to_batch', 'kernel'])
        self.graph_sequence.set_inputs('batch_to_space', ['conv_op', 'block_shape_out', 'crops'])
        self.graph_sequence.set_outputs(['batch_to_space'])

    def resolve_layer(self, graph_matcher, graph_helper):
        matches = graph_matcher.match_sequence(self.graph_sequence)
        if len(matches) == 0:
            return []
        descriptors = []
        for match in matches:
            conv_op = match['conv_op']
            strides = conv_op.get_attr(self.TF_ATTRIBUTE_STRIDES)
            padding = conv_op.get_attr(self.TF_ATTRIBUTE_PADDING)
            weights = self.get_weights(graph_helper, conv_op)
            consumed_nodes = match.consumed_nodes
            output_op_nodes_names = [str(match[node.identifier].outputs[0].name) for node in
                                     self.graph_sequence.output_nodes]
            try:
                batch_to_space_op = match['batch_to_space']
                conv_output_ops = graph_helper.get_op_outputs(batch_to_space_op)
                bias_op = GraphHelper.filter_single_op_by_type(conv_output_ops, 'BiasAdd')
                biases = self.get_biases(graph_helper, conv_op, bias_op)
                consumed_nodes.append(bias_op)
                output_op_nodes_names = [str(bias_op.outputs[0].name)]
            except OperationNotFoundError:
                bias_op = None
                biases = np.zeros(weights.shape[-1], dtype=np.float32)
            dilation_sizes = match['dilation_sizes']
            dilation_sizes = graph_helper.evaluate_tensor_output(dilation_sizes.outputs[0])
            if np.shape(dilation_sizes) != (2,):
                raise ConverterError(code_to_message.get_message('ERROR_TF_CONV_RESOLVE_DILATION')(conv_op.name))

            d = ConvolutionLayerResolver.Descriptor(str(conv_op.name), consumed_nodes,
                                                    conv_op, bias_op, strides, padding, weights, biases,
                                                    output_names=output_op_nodes_names)
            d.dilationY = int(dilation_sizes[0])
            d.dilationX = int(dilation_sizes[1])
            d.input_ops = [match['space_to_batch']]
            descriptors.append(d)
        return descriptors
Ejemplo n.º 10
0
    def __init__(self):
        sequence = GraphSequence([
            NonConsumableConverterSequenceNode('input', ['?']),
            ConverterSequenceNode('tile', ['Tile']),
            NonConsumableConverterSequenceNode('multiples', ['?'])
        ])
        sequence.set_inputs('tile', ['input', 'multiples'])
        sequence.set_outputs(['tile'])

        self.sequences = [sequence]
Ejemplo n.º 11
0
    def __init__(self):
        sequence_scalar_pow = GraphSequence([
            NonConsumableConverterSequenceNode('input', ['?']),
            ConverterSequenceNode('pow', ['Pow']),
            ConverterSequenceNode('const', ['Const'])
        ])
        sequence_scalar_pow.set_inputs('pow', ['input', 'const'])
        sequence_scalar_pow.set_outputs(['pow'])

        self.sequences = [sequence_scalar_pow]
Ejemplo n.º 12
0
    def __init__(self):
        super(GroupedConvolutionLayerResolver, self).__init__()

        # grouped convolution with split
        tree_output_node = ConverterSequenceNode('conv_op', ['Conv2D'])
        self.sequence = GraphSequence([
            ConverterSequenceNode('a', ['Split']),
            ConverterSequenceNode('b', ['Split']),
            ConverterRepeatableSequenceTreeNode('repeatable_graph', tree_output_node, tree_output_node),
            ConverterSequenceNode('concat_op', ['Concat']),
            ConverterSequenceNode('weights', ['Identity', 'Const']),
            NonConsumableConverterSequenceNode('inputs', ['?']),
            NonConsumableConverterSequenceNode('concat_dim', ['Const']),
            NonConsumableConverterSequenceNode('split_dim1', ['Const']),
            ConverterSequenceNode('split_dim2', ['Const'])
        ])
        self.sequence.set_inputs('a', ['inputs', 'split_dim1'])
        self.sequence.set_inputs('b', ['weights', 'split_dim2'])
        self.sequence.set_inputs('repeatable_graph', ['a', 'b'])
        self.sequence.set_inputs('concat_op', ['repeatable_graph', 'concat_dim'])
        self.sequence.set_outputs(['concat_op'])

        # grouped convolution with strided slice
        repeatable_sequence = GraphSequence([
                ConverterSequenceNode('ss', ['StridedSlice']),
                ConverterSequenceNode('ss_begin', ['Const']),
                ConverterSequenceNode('ss_end', ['Const']),
                ConverterSequenceNode('ss_strides', ['Const']),
                ConverterSequenceNode('conv', ['Conv2D']),
                ConverterSequenceNode('bias', ['BiasAdd']),
                ConverterSequenceNode('weights', ['Identity', 'Const']),
                ConverterSequenceNode('biases', ['Identity', 'Const'])
        ])
        repeatable_sequence.set_inputs('ss', ['ss_begin', 'ss_end', 'ss_strides'])
        repeatable_sequence.set_inputs('conv', ['ss', 'weights'])
        repeatable_sequence.set_inputs('bias', ['biases', 'conv'])
        repeatable_sequence.set_outputs(['bias'])

        self.sequence_with_strided_slice = GraphSequence([
            ConverterRepeatableSequenceTreeNode('repeatable_graph',
                                                tree_output_node=repeatable_sequence['bias'],
                                                tree_input_node=repeatable_sequence['ss']),
            ConverterSequenceNode('concat', ['Concat', 'ConcatV2']),
            ConverterSequenceNode('axis', ['Const']),
            NonConsumableConverterSequenceNode('input', ['?'])
        ])
        self.sequence_with_strided_slice.set_inputs('repeatable_graph', ['input'])
        self.sequence_with_strided_slice.set_inputs('concat', ['repeatable_graph', 'axis'])
        self.sequence_with_strided_slice.set_outputs(['concat'])
Ejemplo n.º 13
0
class SliceLayerResolver(LayerResolver, object):
    class Descriptor(LayerDescriptor):
        def __init__(self, name, nodes, axis, split_sizes, split_count):
            super(SliceLayerResolver.Descriptor,
                  self).__init__('Slice', name, nodes)
            self.axis = axis
            self.split_sizes = split_sizes
            self.split_count = split_count

        @property
        def output_names(self):
            return [str(t.name) for t in self.child_ops[-1].outputs]

    def __init__(self):
        self.sequence = GraphSequence(
            [ConverterSequenceNode('root', ['Split', 'SplitV'])])
        self.sequence.set_outputs(['root'])

    def resolve_layer(self, graph_matcher, graph_helper):
        matches = graph_matcher.match_sequence(self.sequence)
        if len(matches) == 0:
            return []
        potential_descriptors = []
        for match in matches:
            split_op = match['root']
            split_axis, split_sizes = self.get_split_axis_and_sizes(
                graph_helper, split_op)
            split_count = int(split_op.get_attr('num_split'))
            consumed_nodes = match.consumed_nodes
            potential_descriptors.append(
                SliceLayerResolver.Descriptor(str(split_op.name),
                                              consumed_nodes, split_axis,
                                              split_sizes, split_count))
        return potential_descriptors

    @classmethod
    def get_split_axis_and_sizes(cls, graph_helper, split_op):
        try:
            _, split_sizes, split_axis = GraphHelper.get_op_input_tensors(
                split_op, ('?', 'Const', 'Const'))
            split_sizes = list(
                graph_helper.evaluate_tensor_output(split_sizes))
        except TensorNotFoundError:
            split_axis, _ = GraphHelper.get_op_input_tensors(
                split_op, ('Const', '?'))
            split_sizes = []

        split_axis = int(graph_helper.evaluate_tensor_output(split_axis))
        return split_axis, split_sizes
Ejemplo n.º 14
0
class ReductionLayerResolver(LayerResolver, object):
    __metaclass__ = ABCMeta

    class Descriptor(LayerDescriptor):
        def __init__(self, layer_type, name, nodes, axes, keep_dims, output_names=None):
            super(ReductionLayerResolver.Descriptor, self).__init__(layer_type, name, nodes, output_names=output_names)
            self.axes = axes
            self.keep_dims = keep_dims

    def __init__(self, layer_type, op_type, descriptor_class):
        super(ReductionLayerResolver, self).__init__()
        self._layer_type = layer_type
        self._op_type = op_type
        self._descriptor_class = descriptor_class

        self.sequence = GraphSequence([
            ConverterSequenceNode('root', [self._op_type]),
            ConverterSequenceNode('reduction_indices', ['Const']),
            NonConsumableConverterSequenceNode('input', ['?']),
        ])
        self.sequence.set_inputs('root', ['input', 'reduction_indices'])
        self.sequence.set_outputs(['root'])

    def resolve_layer(self, graph_matcher, graph_helper):
        descriptors = []
        for match in graph_matcher.match_sequence(self.sequence):
            reduction_op = match['root']
            input_op = match['input']
            reduction_indices_op = match['reduction_indices']

            axes = graph_helper.evaluate_tensor_output(reduction_indices_op.outputs[0])
            keep_dims = bool(reduction_op.get_attr('keep_dims'))

            input_shape = graph_helper.get_op_output_shape(input_op)
            input_rank = len(input_shape)

            axes = [axes] if np.isscalar(axes) else axes.tolist()
            for i in range(len(axes)):
                axes[i] = int(axes[i])
                if axes[i] < 0:
                    axes[i] += input_rank

            reduction_descriptor = self._descriptor_class(self._layer_type, str(reduction_op.name),
                                                          match.consumed_nodes, axes, keep_dims,
                                                          output_names=[str(reduction_op.outputs[0].name)])
            descriptors.extend([reduction_descriptor])

        return descriptors
Ejemplo n.º 15
0
class BatchNormWithGlobalNormLayerResolver(BatchNormLayerResolver):
    class Descriptor(BatchNormLayerResolver.Descriptor):
        pass

    def __init__(self):
        self.sequence = GraphSequence([
            ConverterSequenceNode('root', ['BatchNormWithGlobalNormalization'])
        ])
        self.sequence.set_outputs(['root'])

    def resolve_layer(self, graph_matcher, graph_helper):
        matches = graph_matcher.match_sequence(self.sequence)
        if len(matches) == 0:
            return []
        potential_descriptors = []
        for match in matches:
            bn_op = match['root']
            parameter_tensors = self._const_inputs(graph_helper, bn_op)
            if len(parameter_tensors) < 4:
                raise ConverterError(
                    code_to_message.get_message(
                        'ERROR_TF_BATCHNORM_GLOBALNORMALIZATION_INPUT'))
            epsilon = bn_op.get_attr('variance_epsilon')
            mean = parameter_tensors[0]
            variance = parameter_tensors[1]
            beta = parameter_tensors[2]
            scale = parameter_tensors[3]
            consumed_nodes = match.consumed_nodes
            potential_descriptors.append(
                BatchNormWithGlobalNormLayerResolver.Descriptor(
                    str(bn_op.name),
                    consumed_nodes,
                    bn_mul_op=bn_op,
                    mean=mean,
                    variance=variance,
                    epsilon=epsilon,
                    scale=scale,
                    beta=beta))
        return potential_descriptors

    @classmethod
    def _const_inputs(cls, graph_helper, bn_op):
        return [
            graph_helper.evaluate_tensor_output(tensor)
            for tensor in bn_op.inputs if tensor.op.type == 'Const'
        ]
Ejemplo n.º 16
0
class PReLuLayerResolver(LayerResolver, object):
    class Descriptor(LayerDescriptor):
        def __init__(self, name, operations, coefficients, output_names):
            super(PReLuLayerResolver.Descriptor, self).__init__('PReLU', name, operations,
                                                                output_names=output_names)
            self.coefficients = coefficients

    def __init__(self):
        self.sequence = GraphSequence([
            ConverterSequenceNode('a', ['Relu']),
            ConverterSequenceNode('b', ['Abs']),
            ConverterSequenceNode('c', ['Sub']),
            ConverterSequenceNode('d', ['Mul']),
            ConverterSequenceNode('e', ['Mul']),
            ConverterSequenceNode('f', ['Add']),  # output
            ConverterSequenceNode('unknown', ['?']),
            ConverterSequenceNode('alphas', ['?']),
            NonConsumableConverterSequenceNode('inputs', ['?'])
        ])
        self.sequence.set_inputs('a', ['inputs'])
        self.sequence.set_inputs('b', ['inputs'])
        self.sequence.set_inputs('c', ['inputs', 'b'])
        self.sequence.set_inputs('d', ['alphas', 'c'])
        self.sequence.set_inputs('e', ['d', 'unknown'])
        self.sequence.set_inputs('f', ['a', 'e'])
        self.sequence.set_outputs(['f'])

    def resolve_layer(self, graph_matcher, graph_helper):
        matches = graph_matcher.match_sequence(self.sequence)
        if len(matches) == 0:
            return []
        potential_descriptors = []
        for match in matches:
            coefficients = match['alphas']
            add_op = match['f']
            if coefficients.type not in ['Identity', 'Const']:
                raise ConverterError(code_to_message.get_message('ERROR_TF_RESOLVE_PRELU_COEFF'))

            output_op_nodes_names = [str(match[node.identifier].outputs[0].name) for node in self.sequence.output_nodes]
            consumed_nodes = match.consumed_nodes
            potential_descriptors.append(
                PReLuLayerResolver.Descriptor(str(add_op.name), consumed_nodes,
                                              graph_helper.evaluate_tensor_output(coefficients.outputs[0]),
                                              output_names=output_op_nodes_names))
        return potential_descriptors
Ejemplo n.º 17
0
class FusedBatchNormNormLayerResolver(BatchNormLayerResolver):
    class Descriptor(BatchNormLayerResolver.Descriptor):
        pass

    def __init__(self):
        self.sequence = GraphSequence(
            [ConverterSequenceNode('root', ['FusedBatchNorm'])])
        self.sequence.set_outputs(['root'])

    def resolve_layer(self, graph_matcher, graph_helper):
        matches = graph_matcher.match_sequence(self.sequence)
        potential_descriptors = []
        for match in matches:
            bn_op = match['root']
            parameter_tensors = self._get_parameter_tensors(
                graph_helper, bn_op)
            if len(parameter_tensors) < 4:
                raise ConverterError(
                    code_to_message.get_message(
                        'ERROR_TF_BATCHNORM_GLOBALNORMALIZATION_INPUT'))
            epsilon = bn_op.get_attr('epsilon')
            scale = parameter_tensors[0]
            beta = parameter_tensors[1]
            mean = parameter_tensors[2]
            variance = parameter_tensors[3]
            consumed_nodes = match.consumed_nodes
            potential_descriptors.append(
                FusedBatchNormNormLayerResolver.Descriptor(str(bn_op.name),
                                                           consumed_nodes,
                                                           bn_mul_op=bn_op,
                                                           mean=mean,
                                                           variance=variance,
                                                           epsilon=epsilon,
                                                           scale=scale,
                                                           beta=beta))
        return potential_descriptors

    @classmethod
    def _get_parameter_tensors(cls, graph_helper, bn_op):
        parameter_tensors = [
            t for t in bn_op.inputs if t.op.type in ['Const', 'Identity']
        ]
        tensors_outputs = graph_helper.evaluate_tensors_output(
            parameter_tensors)
        return [tensors_outputs[t] for t in parameter_tensors]
Ejemplo n.º 18
0
class ConcatLayerResolver(LayerResolver, object):
    class Descriptor(LayerDescriptor):
        def __init__(self, name, nodes, axis):
            super(ConcatLayerResolver.Descriptor,
                  self).__init__('Concatenation', name, nodes)
            self.axis = axis

    def __init__(self):
        self.sequence = GraphSequence(
            [ConverterSequenceNode('root', ['Concat', 'ConcatV2'])])
        self.sequence.set_outputs(['root'])

    def resolve_layer(self, graph_matcher, graph_helper):
        matches = graph_matcher.match_sequence(self.sequence)
        if len(matches) == 0:
            return []
        descriptors = []
        for match in matches:
            concat_op = match['root']
            non_const_inputs = [
                tensor for tensor in concat_op.inputs
                if tensor.op.type != 'Const'
            ]
            if len(non_const_inputs) < 2:
                continue

            max_shape = 0
            for t in non_const_inputs:
                shape = graph_helper.get_op_output_shape(t.op)
                if len(shape) > max_shape:
                    max_shape = len(shape)

            axis_tensor = GraphHelper.filter_single_op_by_type(
                [t.op for t in concat_op.inputs], 'Const')
            axis = int(
                graph_helper.evaluate_tensor_output(axis_tensor.outputs[0]))
            if axis < 0:
                axis += max_shape

            consumed_nodes = match.consumed_nodes
            descriptors.append(
                ConcatLayerResolver.Descriptor(str(concat_op.name),
                                               consumed_nodes, axis))

        return descriptors
Ejemplo n.º 19
0
class GenericBatchNormLayerResolver(BatchNormLayerResolver):
    class Descriptor(BatchNormLayerResolver.Descriptor):
        pass

    def __init__(self):
        self.sequence = GraphSequence([
            NonConsumableConverterSequenceNode('inputs', ['?']),
            ConverterSequenceNode('a', ['Mul']),
            ConverterSequenceNode('b', ['Add']),
            ConverterSequenceNode('weights', ['Const', 'Identity']),
            ConverterSequenceNode('biases', ['Const', 'Identity'])
        ])
        self.sequence.set_inputs('a', ['inputs', 'weights'])
        self.sequence.set_inputs('b', ['a', 'biases'])
        self.sequence.set_outputs(['b'])

    def resolve_layer(self, graph_matcher, graph_helper):
        matches = graph_matcher.match_sequence(self.sequence)
        if len(matches) == 0:
            return []
        potential_descriptors = []
        for match in matches:
            biases_op = match['biases']
            weights_op = match['weights']
            biases_op = graph_helper.evaluate_tensor_output(
                biases_op.outputs[0])
            weights_op = graph_helper.evaluate_tensor_output(
                weights_op.outputs[0])
            consumed_nodes = match.consumed_nodes
            output_op_nodes_names = [
                str(match[node.identifier].outputs[0].name)
                for node in self.sequence.output_nodes
            ]
            bn_op = match['a']
            potential_descriptors.append(
                GenericBatchNormLayerResolver.Descriptor(
                    str(bn_op.name),
                    consumed_nodes,
                    bn_mul_op=bn_op,
                    pre_calculated=True,
                    weights=weights_op,
                    biases=biases_op,
                    output_names=output_op_nodes_names))
        return potential_descriptors
Ejemplo n.º 20
0
class AddNLayerResolver(LayerResolver, object):
    class Descriptor(LayerDescriptor):
        def __init__(self, name, nodes):
            super(AddNLayerResolver.Descriptor, self).__init__('ElementWiseSumN', name, nodes)

    def __init__(self):
        self.sequence = GraphSequence([ConverterSequenceNode('root', ['AddN'])])
        self.sequence.set_outputs(['root'])

    def resolve_layer(self, graph_matcher, graph_helper):
        matches = graph_matcher.match_sequence(self.sequence)
        if len(matches) == 0:
            return []

        descriptors = []
        for match in matches:
            add_op = match['root']
            descriptors.append(AddNLayerResolver.Descriptor(str(add_op.name), match.consumed_nodes))
        return descriptors
Ejemplo n.º 21
0
class DepthwiseConvolutionLayerResolver(ConvolutionLayerResolver, object):
    def __init__(self):
        super(DepthwiseConvolutionLayerResolver, self).__init__()
        self.graph_sequence_with_bias = GraphSequence([
            ConverterSequenceNode('conv', ['DepthwiseConv2dNative']),
            ConverterSequenceNode('bias', ['BiasAdd']),
            NonConsumableConverterSequenceNode('other', ['?'])
        ])
        self.graph_sequence_with_bias.set_inputs('bias', ['conv', 'other'])
        self.graph_sequence_with_bias.set_outputs(['bias'])

        self.graph_sequence = GraphSequence(
            [ConverterSequenceNode('conv', ['DepthwiseConv2dNative'])])
        self.graph_sequence.set_outputs(['conv'])

    def resolve_layer(self, graph_matcher, graph_helper):
        matches = graph_matcher.match_sequence(self.graph_sequence)
        matches += graph_matcher.match_sequence(self.graph_sequence_with_bias)
        descriptors = []
        for match in matches:
            self._resolve_from_match(descriptors, graph_helper, match)
        return descriptors

    def _resolve_from_match(self, descriptors, graph_helper, match):
        conv_op = match['conv']
        strides = conv_op.get_attr(self.TF_ATTRIBUTE_STRIDES)
        padding = conv_op.get_attr(self.TF_ATTRIBUTE_PADDING)
        weights = self.get_weights(graph_helper, conv_op)
        weights = np.transpose(weights, [0, 1, 3, 2])

        if 'bias' in match:
            biases = self.get_biases(graph_helper, conv_op, match['bias'])
        else:
            biases = np.zeros(np.shape(weights)[-1], dtype=np.float32)
        consumed_nodes = match.consumed_nodes
        d = ConvolutionLayerResolver.Descriptor(str(conv_op.name),
                                                consumed_nodes, conv_op, None,
                                                strides, padding, weights,
                                                biases)
        input_tensor, _ = GraphHelper.get_op_input_tensors(conv_op, ('?', '?'))
        d.groups = graph_helper.get_op_output_shape(input_tensor)[-1]
        descriptors.append(d)
Ejemplo n.º 22
0
class TanhLayerResolver(LayerResolver, object):
    class Descriptor(LayerDescriptor):
        pass

    def __init__(self):
        self.sequence = GraphSequence(
            [ConverterSequenceNode('root', ['Tanh'])])
        self.sequence.set_outputs(['root'])

    def resolve_layer(self, graph_matcher, graph_helper):
        matches = graph_matcher.match_sequence(self.sequence)
        if len(matches) == 0:
            return []
        potential_descriptors = []
        for match in matches:
            tanh_op = match['root']
            consumed_nodes = match.consumed_nodes
            potential_descriptors.append(
                TanhLayerResolver.Descriptor('Tanh', str(tanh_op.name),
                                             consumed_nodes))
        return potential_descriptors
Ejemplo n.º 23
0
    def __init__(self):
        sequence_extract_glimpse = GraphSequence([
            NonConsumableConverterSequenceNode('input', ['?']),
            NonConsumableConverterSequenceNode('size', ['?']),
            NonConsumableConverterSequenceNode('offsets', ['?']),
            ConverterSequenceNode('extract_glimpse', ['ExtractGlimpse'])
        ])
        sequence_extract_glimpse.set_inputs('extract_glimpse', ['input', 'size', 'offsets'])
        sequence_extract_glimpse.set_outputs(['extract_glimpse'])

        sequence_extract_glimpse_strided_slice = GraphSequence([
            NonConsumableConverterSequenceNode('input', ['?']),
            NonConsumableConverterSequenceNode('size', ['?']),
            ConverterSequenceNode('offsets', ['StridedSlice']),
            ConverterSequenceNode('extract_glimpse', ['ExtractGlimpse'])
        ])
        sequence_extract_glimpse_strided_slice.set_inputs('extract_glimpse',
                                                          ['input', 'size', 'offsets'])
        sequence_extract_glimpse_strided_slice.set_outputs(['extract_glimpse'])

        self.sequences = [sequence_extract_glimpse, sequence_extract_glimpse_strided_slice]
Ejemplo n.º 24
0
class PoolingLayerResolver(LayerResolver, object):
    __metaclass__ = ABCMeta

    class Descriptor(LayerDescriptor):
        def __init__(self, layer_type, name, operations, pooling_type, strides,
                     padding, kernel_dims):
            super(PoolingLayerResolver.Descriptor,
                  self).__init__(layer_type, name, operations)
            self.pooling_type = pooling_type
            self.strides = strides
            self.padding = padding
            self.kernel_dims = kernel_dims

    def __init__(self, layer_type, descriptor_type, pooling_type, op_type):
        super(PoolingLayerResolver, self).__init__()
        self._layer_type = layer_type
        self._descriptor_type = descriptor_type
        self._polling_type = pooling_type
        self._op_type = op_type

        self.sequence = GraphSequence(
            [ConverterSequenceNode('root', [self._op_type])])
        self.sequence.set_outputs(['root'])

    def resolve_layer(self, graph_matcher, graph_helper):
        matches = graph_matcher.match_sequence(self.sequence)
        if len(matches) == 0:
            return []
        potential_descriptors = []
        for match in matches:
            pooling_op = match['root']
            kernel_dims = pooling_op.get_attr('ksize')
            strides = pooling_op.get_attr('strides')
            padding = pooling_op.get_attr('padding')
            consumed_nodes = match.consumed_nodes
            potential_descriptors.append(
                self._descriptor_type(self._layer_type, str(pooling_op.name),
                                      consumed_nodes, self._polling_type,
                                      strides, padding, kernel_dims))
        return potential_descriptors
Ejemplo n.º 25
0
class ArgMaxLayerResolver(LayerResolver, object):

    class Descriptor(LayerDescriptor):
        def __init__(self, name, nodes, axis, output_names=None):
            super(ArgMaxLayerResolver.Descriptor, self).__init__('ArgMax', name, nodes, output_names=output_names)
            self.axis = axis

    def __init__(self):
        self.sequence = GraphSequence([
            ConverterSequenceNode('root', ['ArgMax']),
            ConverterSequenceNode('axis', ['Const']),
            NonConsumableConverterSequenceNode('input', ['?']),
        ])
        self.sequence.set_inputs('root', ['input', 'axis'])
        self.sequence.set_outputs(['root'])

    def resolve_layer(self, graph_matcher, graph_helper):
        descriptors = []
        for match in graph_matcher.match_sequence(self.sequence):
            argmax_op = match['root']
            input_op = match['input']
            axis_op = match['axis']

            input_shape = graph_helper.get_op_output_shape(input_op)
            input_rank = len(input_shape)

            axis = int(graph_helper.evaluate_tensor_output(axis_op.outputs[0]))
            if axis < 0:
                axis += input_rank

            if axis < 0 or axis >= input_rank:
                raise ConverterError(code_to_message.get_message('ERROR_TF_ARGMAX_INVALID_AXIS')(axis, input_rank))

            consumed_nodes = match.consumed_nodes
            argmax_descriptor = ArgMaxLayerResolver.Descriptor(
                str(argmax_op.name), consumed_nodes, axis,
                output_names=[str(argmax_op.outputs[0].name)])
            descriptors.extend([argmax_descriptor])

        return descriptors
Ejemplo n.º 26
0
    def __init__(self):
        sequence_prelu = GraphSequence([
            ConverterSequenceNode('a', ['Relu']),
            ConverterSequenceNode('b', ['Abs']),
            ConverterSequenceNode('c', ['Sub']),
            ConverterSequenceNode('d', ['Mul']),
            ConverterSequenceNode('e', ['Mul']),
            ConverterSequenceNode('f', ['Add']),  # output
            ConverterSequenceNode('unknown', ['?']),
            ConverterSequenceNode('alphas', ['?']),
            NonConsumableConverterSequenceNode('inputs', ['?'])
        ])
        sequence_prelu.set_inputs('a', ['inputs'])
        sequence_prelu.set_inputs('b', ['inputs'])
        sequence_prelu.set_inputs('c', ['inputs', 'b'])
        sequence_prelu.set_inputs('d', ['alphas', 'c'])
        sequence_prelu.set_inputs('e', ['d', 'unknown'])
        sequence_prelu.set_inputs('f', ['a', 'e'])
        sequence_prelu.set_outputs(['f'])

        sequence_prelu_negative_alpha = GraphSequence([
            ConverterSequenceNode('a', ['Relu']),
            ConverterSequenceNode('b', ['Neg']),
            ConverterSequenceNode('c', ['Neg']),
            ConverterSequenceNode('d', ['Relu']),
            ConverterSequenceNode('e', ['Mul']),
            ConverterSequenceNode('f', ['Add']),  # output
            ConverterSequenceNode('alphas', ['?']),
            NonConsumableConverterSequenceNode('inputs', ['?'])
        ])
        sequence_prelu_negative_alpha.set_inputs('a', ['inputs'])
        sequence_prelu_negative_alpha.set_inputs('b', ['inputs'])
        sequence_prelu_negative_alpha.set_inputs('c', ['alphas'])
        sequence_prelu_negative_alpha.set_inputs('d', ['b'])
        sequence_prelu_negative_alpha.set_inputs('e', ['d', 'c'])
        sequence_prelu_negative_alpha.set_inputs('f', ['a', 'e'])
        sequence_prelu_negative_alpha.set_outputs(['f'])

        sequence_prelu_negative_relu = GraphSequence([
            ConverterSequenceNode('relu_pos', ['Relu']),
            ConverterSequenceNode('neg_1', ['Neg']),
            ConverterSequenceNode('neg_2', ['Neg']),
            ConverterSequenceNode('relu_neg', ['Relu']),
            ConverterSequenceNode('mul', ['Mul']),
            ConverterSequenceNode('f', ['Add']),  # output
            ConverterSequenceNode('alphas', ['?']),
            NonConsumableConverterSequenceNode('inputs', ['?'])
        ])
        sequence_prelu_negative_relu.set_inputs('relu_pos', ['inputs'])
        sequence_prelu_negative_relu.set_inputs('neg_1', ['inputs'])
        sequence_prelu_negative_relu.set_inputs('relu_neg', ['neg_1'])
        sequence_prelu_negative_relu.set_inputs('neg_2', ['relu_neg'])
        sequence_prelu_negative_relu.set_inputs('mul', ['neg_2', 'alphas'])
        sequence_prelu_negative_relu.set_inputs('f', ['relu_pos', 'mul'])
        sequence_prelu_negative_relu.set_outputs(['f'])

        self.sequences = [
            sequence_prelu, sequence_prelu_negative_alpha,
            sequence_prelu_negative_relu
        ]
Ejemplo n.º 27
0
class ResizeBilinearLayerResolver(LayerResolver, object):
    TF_ATTRIBUTE_ALIGN_CORNERS = 'align_corners'

    class Descriptor(LayerDescriptor):
        def __init__(self,
                     name,
                     nodes,
                     input_tensor_shape,
                     align_corners=False):
            super(ResizeBilinearLayerResolver.Descriptor,
                  self).__init__('Resize', name, nodes)
            self.align_corners = align_corners
            self.input_tensor_shape = input_tensor_shape
            self.resize_mode = 0

    def __init__(self):
        self.sequence = GraphSequence(
            [ConverterSequenceNode('root', ['ResizeBilinear'])])
        self.sequence.set_outputs(['root'])

    def resolve_layer(self, graph_matcher, graph_helper):
        matches = graph_matcher.match_sequence(self.sequence)
        if len(matches) == 0:
            return []
        descriptors = []
        for match in matches:
            resize_op = match['root']
            align_corners_bool = resize_op.get_attr(
                self.TF_ATTRIBUTE_ALIGN_CORNERS)
            input_tensor, _ = GraphHelper.get_op_input_tensors(
                resize_op, ('?', '?'))
            input_tensor_shape = graph_helper.get_op_output_shape(input_tensor)
            consumed_nodes = match.consumed_nodes
            descriptors.append(
                ResizeBilinearLayerResolver.Descriptor(str(resize_op.name),
                                                       consumed_nodes,
                                                       input_tensor_shape,
                                                       align_corners_bool))
        return descriptors
Ejemplo n.º 28
0
class ReshapeLayerResolver(LayerResolver, object):
    class Descriptor(LayerDescriptor):
        def __init__(self, name, nodes):
            super(ReshapeLayerResolver.Descriptor,
                  self).__init__('Reshape', name, nodes)

    def __init__(self):
        self.sequence = GraphSequence([
            ConverterSequenceNode('root', ['Reshape', 'Squeeze', 'ExpandDims'])
        ])
        self.sequence.set_outputs(['root'])

    def resolve_layer(self, graph_matcher, graph_helper):
        matches = graph_matcher.match_sequence(self.sequence)
        if len(matches) == 0:
            return []
        descriptors = []
        for match in matches:
            reshape_op = match['root']
            consumed_nodes = match.consumed_nodes
            descriptors.append(
                ReshapeLayerResolver.Descriptor(str(reshape_op.name),
                                                consumed_nodes))
        return descriptors
Ejemplo n.º 29
0
class PadLayerResolver(LayerResolver, object):

    class Descriptor(LayerDescriptor):
        def __init__(self, name, nodes, paddings, mode, constant_values, output_names=None):
            super(PadLayerResolver.Descriptor, self).__init__('Pad', name, nodes, output_names=output_names)
            self.paddings = paddings
            self.mode = mode
            self.constant_values = constant_values

    def __init__(self):
        self.sequence_with_zero_padding = GraphSequence([
            ConverterSequenceNode('root', ['Pad', 'PadV2']),
            ConverterSequenceNode('paddings', ['Const']),
            NonConsumableConverterSequenceNode('input', ['?']),
        ])
        self.sequence_with_zero_padding.set_inputs('root', ['input', 'paddings'])
        self.sequence_with_zero_padding.set_outputs(['root'])

        self.sequence_with_const_padding = GraphSequence([
            ConverterSequenceNode('root', ['Pad', 'PadV2']),
            ConverterSequenceNode('paddings', ['Const']),
            ConverterSequenceNode('const_values', ['Const']),
            NonConsumableConverterSequenceNode('input', ['?']),
        ])
        self.sequence_with_const_padding.set_inputs('root', ['input', 'paddings', 'const_values'])
        self.sequence_with_const_padding.set_outputs(['root'])

        self.sequences = [self.sequence_with_zero_padding, self.sequence_with_const_padding]

    def resolve_layer(self, graph_matcher, graph_helper):
        descriptors = []
        for sequence in self.sequences:
            for match in graph_matcher.match_sequence(sequence):
                pad_op = match['root']
                input_op = match['input']
                paddings_op = match['paddings']

                paddings_tensor = graph_helper.evaluate_tensor_output(paddings_op.outputs[0])
                paddings_shape = graph_helper.get_op_output_shape(paddings_op)

                input_rank = len(graph_helper.get_op_output_shape(input_op))
                if [input_rank, 2] != paddings_shape:
                    raise ConverterError(code_to_message.get_message(
                        'ERROR_TF_PAD_INVALUD_PADDINGS')(str([input_rank, 2]), str(paddings_shape)))

                if 'const_values' in match:
                    const_values_op = match['const_values']
                    const_values = graph_helper.evaluate_tensor_output(const_values_op.outputs[0])
                else:
                    const_values = 0.0

                if not np.isscalar(const_values):
                    raise ConverterError(code_to_message.get_message('ERROR_TF_PAD_CONSTANT_NOT_SCALAR'))

                consumed_nodes = match.consumed_nodes
                pad_descriptor = PadLayerResolver.Descriptor(
                    str(pad_op.name), consumed_nodes, paddings_tensor,
                    snpe.modeltools.PADDING_CONSTANT, const_values,
                    output_names=[str(pad_op.outputs[0].name)])
                descriptors.extend([pad_descriptor])

        return descriptors
Ejemplo n.º 30
0
class GroupedConvolutionLayerResolver(ConvolutionLayerResolver, object):
    class Descriptor(ConvolutionLayerResolver.Descriptor):
        pass

    def __init__(self):
        super(GroupedConvolutionLayerResolver, self).__init__()
        tree_output_node = ConverterSequenceNode('conv_op', ['Conv2D'])
        self.sequence = GraphSequence([
            ConverterSequenceNode('a', ['Split']),
            ConverterSequenceNode('b', ['Split']),
            ConverterRepeatableSequenceTreeNode('repeatable_graph',
                                                tree_output_node,
                                                tree_output_node),
            ConverterSequenceNode('concat_op', ['Concat']),
            ConverterSequenceNode('weights', ['Identity', 'Const']),
            NonConsumableConverterSequenceNode('inputs', ['?']),
            NonConsumableConverterSequenceNode('concat_dim', ['Const']),
            NonConsumableConverterSequenceNode('split_dim1', ['Const']),
            ConverterSequenceNode('split_dim2', ['Const'])
        ])
        self.sequence.set_inputs('a', ['inputs', 'split_dim1'])
        self.sequence.set_inputs('b', ['weights', 'split_dim2'])
        self.sequence.set_inputs('repeatable_graph', ['a', 'b'])
        self.sequence.set_inputs('concat_op',
                                 ['repeatable_graph', 'concat_dim'])
        self.sequence.set_outputs(['concat_op'])

    def resolve_layer(self, graph_matcher, graph_helper):
        matches = graph_matcher.match_sequence(self.sequence)
        if len(matches) == 0:
            return []
        descriptors = []
        for match in matches:
            conv_op = match['conv_op_1']
            strides = conv_op.get_attr(self.TF_ATTRIBUTE_STRIDES)
            padding = conv_op.get_attr(self.TF_ATTRIBUTE_PADDING)
            weights = match['weights']
            consumed_nodes = match.consumed_nodes
            output_op_nodes_names = [
                str(match[node.identifier].outputs[0].name)
                for node in self.sequence.output_nodes
            ]
            try:
                concat_op = match['concat_op']
                concat_op_output_ops = graph_helper.get_op_outputs(concat_op)
                bias_op = GraphHelper.filter_single_op_by_type(
                    concat_op_output_ops, 'BiasAdd')
                # need to consume input of bias
                biases = self.get_biases(graph_helper, conv_op, bias_op)
                consumed_nodes.append(bias_op)
                output_op_nodes_names = [str(bias_op.outputs[0].name)]
            except OperationNotFoundError:
                bias_op = None
                biases = np.zeros(weights.outputs[0].get_shape()[-1],
                                  dtype=np.float32)

            weights = graph_helper.evaluate_tensor_output(weights.outputs[0])
            descriptor = ConvolutionLayerResolver.Descriptor(
                str(conv_op.name),
                consumed_nodes,
                conv_op,
                bias_op,
                strides,
                padding,
                weights,
                biases,
                output_names=output_op_nodes_names)
            descriptor.input_ops = [match['a'], match['b']]
            descriptors.append(descriptor)
        return descriptors