Пример #1
0
def convert_mul_add_to_power(graph: nx.MultiDiGraph):
    for_each_sub_graph(graph, convert_mul_add_to_power)
    nodes = list(graph.nodes())
    for n in nodes:
        # As we remove nodes from graph, we should check that node exists in graph
        if n in graph:
            node = Node(graph, n)
            if node.has('op') and (node.op == 'Mul' or node.op == 'Add') and len(node.in_nodes()) == 2 and \
                    node.soft_get('can_be_scaleshift') is not False:
                scalar_idx, tensor_idx = (
                    0, 1) if not node.in_node(0).value is None else (1, 0)
                if not node.in_node(scalar_idx).value is None and node.in_node(
                        tensor_idx).value is None:
                    if np.squeeze(node.in_node(scalar_idx).value).ndim == 0:
                        node['type'] = 'Power'
                        node['scale'] = node.in_node(
                            scalar_idx).value.item() if node.op == 'Mul' else 1
                        node['power'] = 1
                        node['shift'] = node.in_node(
                            scalar_idx).value.item() if node.op == 'Add' else 0
                        node['op'] = 'Power'
                        if node.has('operation'):
                            del node.graph.node[node.id]['operation']
                        update_ie_fields(graph.node[node.id])
                        scalar_node = node.in_node(scalar_idx)
                        graph.remove_edge(scalar_node.id, node.id)
                        graph.remove_node(scalar_node.id)
Пример #2
0
    def add_reshape_after_data_node(graph: Graph, data_node_name: str):
        """
        Adds reshape operation which changes shape of the tensor produced by TFSubgraphCall from 4D to real dimension
        of the tensor. The data_node_name node contains real dimensions of the tensor but they will be changed in the
        add_reshapes_for_tf_subgraph_calls function to a 4D because IE TF call layer supports output in 4D only.
        :param graph: graph to operate on.
        :param data_node_name: name of the data node to be reshaped to correct dimensions.
        :return: None
        """
        data_node = Node(graph, data_node_name)

        # if the data node was previously marked as output then we need to mark as output new reshaped data node
        is_out_node = False
        if len(data_node.out_nodes()) == 1 and data_node.out_node().has('op') and data_node.out_node().op == 'OpOutput':
            is_out_node = True
            graph.remove_node(data_node.out_node().id)

        # save old consumers nodes with edge attributes
        old_consumer_nodes_with_attrs = list()
        for index, out_op in enumerate(data_node.out_nodes()):
            edge_attrs = graph.get_edge_data(data_node_name, out_op.name)[0]
            old_consumer_nodes_with_attrs.append((out_op.name, edge_attrs))

        # remove old consumers from the data node
        for out_op in list(data_node.out_nodes()):
            graph.remove_edge(data_node_name, out_op.name)

        # reshape operation node
        reshape_node_name = graph.unique_id("Reshape_")
        graph.add_node(reshape_node_name, kind='op', precision="FP32", type='Reshape', name=reshape_node_name,
                       op='Reshape',
                       data_type=data_node['data_type'])
        update_ie_fields(graph.node[reshape_node_name])

        # reshape shape data node
        reshape_shape_data_node_name = graph.unique_id("Reshape_shape_")
        graph.add_node(reshape_shape_data_node_name, kind='data', precision="FP32", name=reshape_shape_data_node_name,
                       value=np.array(data_node['shape']), shape=[1])

        # reshaped data node
        reshaped_value = None
        if data_node['value'] is not None:
            reshaped_value = np.array(data_node['value'])
        reshaped_data_node_name = graph.unique_id("reshaped_data_")
        graph.add_node(reshaped_data_node_name, kind='data', precision="FP32", name=reshaped_data_node_name,
                       shape=np.array(data_node['shape']), value=reshaped_value, nchw_layout=True)

        if is_out_node:
            add_opoutput(graph, reshaped_data_node_name, 0, False)

        graph.add_edges_from([
            (data_node_name, reshape_node_name, {'in': 0}),
            (reshape_shape_data_node_name, reshape_node_name, {'in': 1}),
            (reshape_node_name, reshaped_data_node_name, {'out': 0}),
        ])

        for out_node_name, edge_attrs in old_consumer_nodes_with_attrs:
            graph.add_edges_from([
                (reshaped_data_node_name, out_node_name, edge_attrs)
            ])
Пример #3
0
def create_const_nodes(graph: nx.MultiDiGraph, start_data_nodes_are_not_allowed: bool = True):

    for node_name in list(graph.nodes()):
        node = NodeWrap(graph, node_name)
        if (node.has('kind') and node.kind == 'data' and ((len(node.out_edges()) == 1 and 'bin' not in node.out_edge(0)) or node.has_and_set('is_output')) and len(node.in_nodes()) == 0):

            if node.has_valid('value'):
                const_node_name = node.id + '_const'
                log.debug("Added Const node '{}'".format(const_node_name))
                graph.add_node(const_node_name, name=const_node_name, type='Const', kind='op', op='Const',
                               precision="FP32")
                update_ie_fields(node.graph.node[const_node_name])
                graph.add_edges_from([(const_node_name, node.id, {'out': 0})])
                copy_data_node_name = unique_id(graph, node.id + '_copy_')
                graph.add_node(copy_data_node_name, kind='data', precision="FP32", shape=np.array(node.shape),
                               value=np.array(node.value))
                if node.has_valid('force_precision'):
                    Node(graph, copy_data_node_name)[
                        'force_precision'] = node.force_precision
                    Node(graph, const_node_name)[
                        'force_precision'] = node.force_precision
                graph.add_edges_from(
                    [(copy_data_node_name, const_node_name, {'in': 0, 'bin': 'custom'})])
            elif start_data_nodes_are_not_allowed:
                log.debug('node = {}'.format(node.graph.node[node.id]))
                # TODO for body sub-graph it shouldn't be reported as an error
                raise Error(
                    'Discovered data node without inputs and value, node.name = {}, consumer.name = {}. ' +
                    refer_to_faq_msg(23),
                    node.soft_get('name'),
                    node.out_node().soft_get('name') if len(node.out_nodes()) else "<no consumer>"
                )
Пример #4
0
def l2_norm_to_norm_action(graph: Graph, match: dict):
    input_data_name = match['input'].node
    output_data_name = match['l2_normalize_data'].node

    if not match['maximum_y_data'].has_valid('value'):
        return 1
    if match['maximum_y_data'].value.shape != ():
        return 1
    y = match['maximum_y_data'].value

    normalize_id = graph.unique_id()
    graph.add_node(
        normalize_id,
        **add_attrs_props(
            dict(kind='op',
                 precision="FP32",
                 type='Normalize',
                 name=str(graph.unique_id('normalize')),
                 op='Normalize',
                 shape=None,
                 eps=str(y),
                 across_spatial=str(0),
                 channel_shared=str(0),
                 data_type=None,
                 infer=None,
                 in_ports_count=2,
                 out_ports_count=1)))
    normalize_data_id = graph.unique_id()

    graph.add_node(normalize_data_id,
                   **add_attrs_props(graph.node[output_data_name]))
    update_ie_fields(graph.node[normalize_id])
    weights_id = graph.unique_id('weights_')
    graph.add_node(
        weights_id,
        **add_attrs_props(
            dict(kind='data',
                 precision="FP32",
                 name=weights_id,
                 value=None,
                 shape=None,
                 data_type=None,
                 infer=None)))
    wnode = Node(graph, weights_id)
    wnode['value'] = np.ones(
        shape=match['input'].shape[-1],
        dtype=match['input'].data_type)  # TODO feature dim instead of -1
    wnode['shape'] = np.array(wnode['value'].shape)
    output_edges = list(graph.out_edges(output_data_name, data=True))
    graph.remove_edges_from([(input_data_name, match['l2_normalize'].id),
                             (input_data_name, match['square'].id)])
    graph.remove_edges_from(list(graph.out_edges(output_data_name)))
    graph.remove_node(output_data_name)
    graph.add_edge(input_data_name, normalize_id, **{'in': 0})
    graph.add_edge(weights_id, normalize_id, **{'in': 1, 'bin': 'weights'})
    graph.add_edge(normalize_id, normalize_data_id, **{'out': 0})
    for data, owner, attr in output_edges:
        graph.add_edge(normalize_data_id, owner, **attr)
Пример #5
0
def add_reshape_before_op_node(graph: nx.MultiDiGraph, data_node_name: str,
                               op_node_name: str, edge_attrs: dict):
    """
    Adds reshape operation which expands dimension of the specified data tensor to 4D.
    :param graph: graph to operate on.
    :param data_node_name: the name of the data node to be reshaped to 4D tensor.
    :param op_node_name: name of the TFCustomSubgraphCall node which produces the tensor.
    :param edge_attrs: edge attributes which should be preserved.
    :return: None
    """
    data_node = Node(graph, data_node_name)

    graph.remove_edge(data_node_name, op_node_name)

    assert data_node['shape'] is not None

    new_shape = make_shape_4d(data_node['shape'])

    # reshape shape data node
    reshape_shape_data_node_name = unique_id(graph, "Reshape_shape_")
    graph.add_node(reshape_shape_data_node_name,
                   kind='data',
                   precision="FP32",
                   name=reshape_shape_data_node_name,
                   value=new_shape,
                   shape=[1])

    # reshape operation node
    reshape_node_name = unique_id(graph, "Reshape_")
    graph.add_node(reshape_node_name,
                   kind='op',
                   precision="FP32",
                   type='Reshape',
                   name=reshape_node_name,
                   op='Reshape',
                   data_type=data_node['data_type'])
    update_ie_fields(graph.node[reshape_node_name])

    # reshaped data node
    reshaped_value = None
    if data_node['value'] is not None:
        reshaped_value = np.reshape(data_node['value'], new_shape)
    reshaped_data_node_name = unique_id(graph, "reshaped_data_")
    graph.add_node(reshaped_data_node_name,
                   kind='data',
                   precision="FP32",
                   name=reshaped_data_node_name,
                   shape=new_shape,
                   value=reshaped_value,
                   nchw_layout=True)

    graph.add_edges_from([(data_node_name, reshape_node_name, {
        'in': 0
    }), (reshape_shape_data_node_name, reshape_node_name, {
        'in': 1
    }), (reshape_node_name, reshaped_data_node_name, {
        'out': 0
    }), (reshaped_data_node_name, op_node_name, edge_attrs)])
Пример #6
0
def set_tf_custom_call_node_attrs(node_attrs: dict):
    from mo.front.tf.partial_infer.tf import tf_subgraph_infer
    update_ie_fields(node_attrs)
    node_attrs['input_nodes_names'] = list()
    node_attrs['output_tensors_names'] = list()
    node_attrs['real_input_dims'] = list()
    node_attrs['pbs'] = dict()
    node_attrs['type'] = 'TFCustomSubgraphCall'
    node_attrs['op'] = 'TFCustomSubgraphCall'
    node_attrs['infer'] = tf_subgraph_infer
    node_attrs['kind'] = 'op'
Пример #7
0
def set_tf_custom_call_node_attrs(node_attrs: dict):
    update_ie_fields(node_attrs)
    node_attrs['input_nodes_names'] = list()
    node_attrs['output_tensors_names'] = list()
    node_attrs['real_input_dims'] = list()
    node_attrs['pbs'] = dict()
    node_attrs['type'] = 'TFCustomSubgraphCall'
    node_attrs['op'] = 'TFCustomSubgraphCall'
    node_attrs['precision'] = 'FP32'  # TODO use real precision derived from the model
    node_attrs['infer'] = tf_subgraph_infer
    node_attrs['kind'] = 'op'
Пример #8
0
def convert_add_to_scaleshift(graph: nx.MultiDiGraph):
    for n in list(graph.nodes()):
        node = Node(graph, n)
        if node.has('op') and (node.op == 'BiasAdd' or node.op
                               == 'Add') and len(node.in_nodes()) == 2:
            tensor_id, value_id = get_tensor_id(node), get_value_id(node)
            if tensor_id is not None and value_id is not None and node.soft_get(
                    'can_be_scaleshift') is not False:
                node['type'] = 'ScaleShift'
                node['op'] = 'ScaleShift'
                node.in_node(value_id).value = np.squeeze(
                    node.in_node(value_id).value)
                node.in_node(value_id).shape = node.in_node(
                    value_id).value.shape

                # if the node was created with eltwise then it has attribute 'operation' which should be removed from
                # the IR
                if node.has('operation'):
                    del graph.node[n]['operation']

                bias_data = node.in_node(value_id)
                graph[bias_data.node][node.node][0]['in'] = 2
                graph[bias_data.node][node.node][0]['bin'] = 'biases'

                input_data = node.in_node(tensor_id)
                graph[input_data.node][node.node][0]['in'] = 0

                update_ie_fields(graph.node[node.id])

                weights_id = unique_id(graph, 'weights_')
                graph.add_node(
                    weights_id,
                    **add_attrs_props(
                        dict(kind='data',
                             precision="FP32",
                             name=weights_id,
                             value=None,
                             shape=None,
                             data_type=None,
                             infer=None)))
                wnode = Node(graph, weights_id)

                wnode['value'] = np.full_like(bias_data.value,
                                              1,
                                              dtype=np.float32)
                wnode['shape'] = np.array(wnode['value'].shape)

                graph.add_edges_from([
                    (weights_id, node.node, {
                        'in': 1,
                        'bin': 'weights'
                    }),
                ])
Пример #9
0
Файл: op.py Проект: pc2/CustoNN2
 def add_node(self, attrs: dict = None):
     new_attrs = {}
     new_attrs.update(self.attrs)
     if attrs is not None:
         new_attrs.update(attrs)
     id_prefix = new_attrs['name'] if 'name' in new_attrs else ''
     id = unique_id(self.graph, id_prefix)
     new_attrs['name'] = id
     new_attrs = add_attrs_props(new_attrs)
     update_ie_fields(new_attrs, self.ir_version)
     self.substitute_ie_attrs(new_attrs)
     self.graph.add_node(id, **new_attrs)
     return Node(self.graph, id)
Пример #10
0
Файл: op.py Проект: pc2/CustoNN2
 def update_node(self, node: Node, attrs: dict = None):
     """
     Updates/creates new attributes in node based on self.attrs and attrs.
     """
     new_attrs = {}
     new_attrs.update(self.attrs)
     if attrs:
         new_attrs.update(attrs)
     new_attrs = add_attrs_props(new_attrs)
     update_ie_fields(new_attrs, self.ir_version)
     self.substitute_ie_attrs(new_attrs)
     for k, v in new_attrs.items():
         node[k] = v
Пример #11
0
    def replace_pattern(self, graph: Graph, match: dict):
        """
            Adds layers with type 'Const' that produce blob from 'bin' file. The pass finds data nodes with one output which
            doesn't have edge with 'bin' attribute (or with two outputs and at least one output havent 'bin' attr)
            and generate Const op node before the node and data node before the Const node. The data node before 'Const'
            node is needed because the op node dumps input tensors to bin file.
        """
        node = match['data']
        if len(node.in_nodes()) > 0:
            return

        if self._check_bin_attrs(node):
            if node.has_valid('value'):
                const_node_name = graph.unique_id(node.id + '_const')
                log.debug("Added Const node '{}'".format(const_node_name))
                graph.add_node(const_node_name,
                               name=const_node_name,
                               type='Const',
                               kind='op',
                               op='Const',
                               precision="FP32")
                update_ie_fields(node.graph.node[const_node_name])
                graph.add_edges_from([(const_node_name, node.id, {'out': 0})])

                copy_data_node_name = graph.unique_id(node.id + '_copy_')
                graph.add_node(copy_data_node_name,
                               kind='data',
                               precision="FP32",
                               shape=np.array(node.shape),
                               value=np.array(node.value))

                if node.has_valid('force_precision'):
                    Node(graph, copy_data_node_name
                         )['force_precision'] = node.force_precision
                    Node(graph, const_node_name
                         )['force_precision'] = node.force_precision
                graph.add_edges_from([(copy_data_node_name, const_node_name, {
                    'in': 0,
                    'bin': 'custom'
                })])
            elif not self._check_that_node_from_body(node):
                log.debug('node = {}'.format(node.graph.node[node.id]))
                raise Error(
                    'Discovered data node without inputs and value, node.name = {}, consumer.name = {}. '
                    + refer_to_faq_msg(23), node.soft_get('name'),
                    node.out_node().soft_get('name')
                    if len(node.out_nodes()) else "<no consumer>")
Пример #12
0
def create_const_nodes(graph: nx.MultiDiGraph, start_data_nodes_are_not_allowed: bool=True):
    """
    Adds layers with type 'Const' that produce blob from 'bin' file. The pass finds data nodes with one output which
    doesn't have edge with 'bin' attribute and generate Const op node before the node and data node before the Const
    node. The data node before 'Const' node is needed because the op node dumps input tensors to bin file.
    :param graph: input graph.
    :return: None
    """
    for node_name in list(graph.nodes()):
        node = NodeWrap(graph, node_name)
        if (
                node.has('kind') and
                node.kind == 'data' and (
                (len(node.out_edges()) == 1 and 'bin' not in node.out_edge(0)) or
                node.has_and_set('is_output')
        ) and
                len(node.in_nodes()) == 0):

            if node.has_valid('value'):
                const_node_name = node.id + '_const'
                log.debug("Added Const node '{}'".format(const_node_name))
                graph.add_node(const_node_name, name=const_node_name, type='Const', kind='op', op='Const',
                               precision="FP32")
                update_ie_fields(node.graph.node[const_node_name])
                graph.add_edges_from([(const_node_name, node.id, {'out': 0})])
                copy_data_node_name = unique_id(graph, node.id + '_copy_')
                graph.add_node(copy_data_node_name, kind='data', precision="FP32", shape=np.array(node.shape),
                               value=np.array(node.value))
                if node.has_valid('force_precision'):
                    Node(graph, copy_data_node_name)['force_precision'] = node.force_precision
                    Node(graph, const_node_name)['force_precision'] = node.force_precision
                graph.add_edges_from([(copy_data_node_name, const_node_name, {'in': 0, 'bin': 'custom'})])
            elif start_data_nodes_are_not_allowed:
                log.debug('node = {}'.format(node.graph.node[node.id]))
                # TODO for body sub-graph it shouldn't be reported as an error
                raise Error(
                    'Discovered data node without inputs and value, node.name = {}, consumer.name = {}. ' +
                    refer_to_faq_msg(23),
                    node.soft_get('name'),
                    node.out_node().soft_get('name') if len(node.out_nodes()) else "<no consumer>"
                )
Пример #13
0
 def test_not_set_update_ie_fields(self):
     with self.assertRaisesRegex(Error, 'Unrecognized IR version.*'):
         update_ie_fields({}, ir_version='abracadabra')
Пример #14
0
 def test_default_update_ie_fields(self):
     update_ie_fields({}, ir_version=None)
Пример #15
0
    def replace_pattern(self, graph: Graph, match: dict):
        """
        Converts specific for NasNet topology subgraph Pad->StridedSlice->AvgPool to Conv->Crop->AvgPool
        """
        input = match['input']

        pad_op = match['pad_op']

        sslice = match['sslice']
        sslice_out = match['sslice_out']
        begin = []
        end = []
        stride = []
        for s in sslice.slices:
            begin.append(s.start)
            end.append(s.stop)
            stride.append(s.step)

        if not np.array_equal(pad_op.pads,
                              np.array([[0, 0], [0, 1], [0, 1], [0, 0]])):
            log.error(" Pad values doesn't match!")
            return

        if not np.array_equal(begin, np.array([0, 1, 1, 0])):
            log.error("StridedSlice has wrong begin")
            return

        if not np.array_equal(sslice.end_mask, np.array(
            [0, 0, 0, 0])) or not np.array_equal(sslice.begin_mask,
                                                 np.array([0, 1, 1, 0])):
            log.error("StridedSlice has wrong masks")
            return

        # Cut Smth-x->Pad->StrudedSlice-x->AvgPool
        graph.remove_edge(input.id, pad_op.id)
        graph.remove_edge(sslice.id, sslice_out.id)

        # Pad -> Conv
        conv_node = graph.unique_id(pad_op.name + '/Conv_')
        conv_weights_node = graph.unique_id(pad_op.name + '/ConvW_')
        conv_weights = np.ones((input.shape[3], 1, 1, 1))
        conv_output = graph.unique_id(pad_op.name + '/ConvOut_')
        output_shape = np.array([
            input.shape[0], input.shape[1] + 1, input.shape[2] + 1,
            input.shape[3]
        ])

        graph.add_node(
            conv_node,
            **add_attrs_props(
                dict(kind='op',
                     type='Convolution',
                     name=conv_node,
                     op='Conv2D',
                     stride=np.array([1, 1, 1, 1]),
                     dilation=np.array([1, 1, 1, 1]),
                     group=input.shape[3],
                     bias_addable=True,
                     bias_term=False,
                     spatial_dims=np.array([1, 2]),
                     kernel_spatial=np.array([1, 1]),
                     pad=np.array([[0, 0], [0, 1], [0, 1], [0, 0]]),
                     output_shape=output_shape,
                     channel_dims=np.array([3]),
                     output=input.shape[3],
                     in_ports_count=3,
                     out_ports_count=1)))

        graph.add_node(
            conv_weights_node,
            **add_attrs_props(
                dict(kind='data',
                     name=conv_weights_node,
                     value=np.array(conv_weights),
                     shape=np.array(conv_weights.shape),
                     data_type=input.data_type,
                     infer=None,
                     spatial_dims=np.array([0, 1]),
                     input_channel_dim=2,
                     output_channel_dim=3,
                     dims_number=4,
                     can_be_bias=True)))
        graph.add_node(
            conv_output,
            **add_attrs_props(
                dict(kind='data',
                     name=conv_output,
                     value=None,
                     shape=output_shape,
                     data_type=input.data_type)))

        # StridedSlice -> Crop
        crop = Crop(
            graph,
            dict(name=sslice.name + '/Crop_',
                 axis=np.array([1, 2]),
                 dim=np.array([output_shape[1] - 1, output_shape[2] - 1]),
                 offset=np.array([1, 1])))
        crop.create_node_with_data([Node(graph, conv_output)],
                                   data_nodes=sslice_out)

        # Connect : Conv->Crop->AvgPool
        graph.add_edges_from([
            (input.id, conv_node, {
                'in': 0
            }),
            (conv_weights_node, conv_node, {
                'in': 1,
                'bin': 'weights'
            }),
            (conv_node, conv_output, {
                'out': 0
            }),
        ])
        update_ie_fields(graph.node[conv_node], graph.graph['ir_version'])
Пример #16
0
def muladd_to_scaleshift_action(graph: Graph, match: dict):
    mul = match['mul']
    add = match['add']
    output = match['output']

    # Pass works correctly only in case when node have only 1 output
    if len(mul.out_port(0).get_destinations()) > 1:
        return

    if mul.soft_get('can_be_scaleshift') is False or add.soft_get(
            'can_be_scaleshift') is False:
        return

    mul_weights_id = get_value_id(mul)
    mul_input_id = get_tensor_id(mul)
    add_weights_id = get_value_id(add)

    if mul_weights_id is None:
        log.debug("Mul->Add to ScaleShift: Mul {} has no weights".format(
            mul.name))
        return
    if mul_input_id is None:
        log.debug("Mul->Add to ScaleShift: Mul {} has no input".format(
            mul.name))
        return
    if add_weights_id is None:
        log.debug("Mul->Add to ScaleShift: Add {} has no weights".format(
            add.name))
        return

    input = mul.in_node(mul_input_id)
    weights = mul.in_node(mul_weights_id)
    bias = add.in_node(add_weights_id)

    # Transform values
    weights.value = np.squeeze(weights.value)
    weights.shape = np.array(weights.value.shape, dtype=np.int64)

    bias.value = np.squeeze(bias.value)
    bias.shape = np.array(bias.value.shape, dtype=np.int64)

    # Broadcast weights if they are scalar
    if weights.value.ndim == 0 and bias.value.ndim == 1:
        weights.value = np.full(bias.shape, weights.value.item())
        weights.shape = np.array(weights.value.shape, dtype=np.int64)

    if bias.shape != weights.shape:
        log.warning('Mul->Add to ScaleShift conversion stoped {} != {}'.format(
            weights.shape, bias.shape))
        return

    if bias.value.ndim != weights.value.ndim or bias.value.size != weights.value.size:
        log.debug(
            "Skipping Mul->Add to ScaleShift conversion for nodes {}, {} because of different weights "
            "and biases".format(mul.name, add.name))
        return

    if bias.value.size == 1 and weights.value.size == 1:
        log.debug(
            "Skipping Mul->Add to ScaleShift conversion for nodes {}, {}. Will be converted to Power"
            "".format(mul.name, add.name))
        return

    op_name = "ScaleShift"

    log.debug(
        "Fusing Mul->Add to {}. Input nodes: {} and {}, bias.shape = {}, weights.shape = {}"
        "".format(op_name, mul.id, add.id, bias.shape, weights.shape))

    graph.remove_edge(input.node, mul.id)
    graph.remove_edge(weights.node, mul.id)
    graph.remove_edge(bias.node, add.id)
    graph.remove_edge(add.node, output.id)

    op_node = graph.unique_id(mul.name + '/Fused{}_'.format(op_name))

    graph.add_node(
        op_node,
        **add_attrs_props(
            dict(kind='op',
                 type=op_name,
                 name=op_node,
                 op=op_name,
                 data_type=input.data_type)))
    scsh = Node(graph, op_node)
    scsh.add_input_port(0)
    scsh.add_input_port(1)
    scsh.add_input_port(2)
    scsh.add_output_port(0)

    update_ie_fields(graph.node[op_node])

    graph.add_edges_from([(input.node, op_node, {
        'in': 0
    }), (weights.node, op_node, {
        'in': 1,
        'bin': 'weights'
    }), (bias.node, op_node, {
        'in': 2,
        'bin': 'biases'
    }), (op_node, output.node, {
        'out': 0
    })])

    return
Пример #17
0
def muladd_to_scaleshift_action(graph: nx.MultiDiGraph, match: dict):
    mul = match['mul']
    add = match['add']
    output = match['output']

    if mul.soft_get('can_be_scaleshift') is False or add.soft_get(
            'can_be_scaleshift') is False:
        return

    mul_weights_id = get_value_id(mul)
    mul_input_id = get_tensor_id(mul)
    add_weights_id = get_value_id(add)

    if mul_weights_id is None:
        log.debug("Mul->Add to ScaleShift: Mul {} has no weights".format(
            mul.name))
        return
    if mul_input_id is None:
        log.debug("Mul->Add to ScaleShift: Mul {} has no input".format(
            mul.name))
        return
    if add_weights_id is None:
        log.debug("Mul->Add to ScaleShift: Add {} has no weights".format(
            add.name))
        return

    input = mul.in_node(mul_input_id)
    weights = mul.in_node(mul_weights_id)
    bias = add.in_node(add_weights_id)

    # Transform values
    weights.value = np.squeeze(weights.value)
    weights.shape = weights.value.shape

    bias.value = np.squeeze(bias.value)
    bias.shape = bias.value.shape

    # Broadcast weights if they are scalar
    if weights.value.ndim == 0 and bias.value.ndim == 1:
        weights.value = np.full(bias.shape, weights.value.item())
        weights.shape = weights.value.shape

    if bias.shape != weights.shape:
        log.warning('Mul->Add to ScaleShift conversion stoped {} != {}'.format(
            weights.shape, bias.shape))
        return

    if bias.value.ndim != weights.value.ndim or bias.value.size != weights.value.size:
        log.debug(
            "Skipping Mul->Add to scaleshift or power conversion for nodes {}, {} because of different weights "
            "and biases".format(mul.name, add.name))
        return

    op_name = "ScaleShift"
    if bias.value.size == 1 and weights.value.size == 1:
        op_name = "Power"

    log.debug(
        "Fusing Mul->Add to {}. Input nodes: {} and {}, bias.shape = {}, weights.shape = {}"
        "".format(op_name, mul.id, add.id, bias.shape, weights.shape))

    graph.remove_edge(input.node, mul.id)
    graph.remove_edge(weights.node, mul.id)
    graph.remove_edge(bias.node, add.id)
    graph.remove_edge(add.node, output.id)

    op_node = unique_id(graph, mul.name + '/Fused{}_'.format(op_name))
    if op_name == 'ScaleShift':
        graph.add_node(
            op_node,
            **add_attrs_props(
                dict(kind='op',
                     precision="FP32",
                     type=op_name,
                     name=op_node,
                     op=op_name,
                     data_type=input.data_type)))
        update_ie_fields(graph.node[op_node])
        graph.add_edges_from([(input.node, op_node, {
            'in': 0
        }), (weights.node, op_node, {
            'in': 1,
            'bin': 'weights'
        }), (bias.node, op_node, {
            'in': 2,
            'bin': 'biases'
        }), (op_node, output.node, {
            'out': 0
        })])
    else:
        graph.add_node(
            op_node,
            **add_attrs_props(
                dict(kind='op',
                     precision="FP32",
                     type=op_name,
                     name=op_node,
                     op=op_name,
                     data_type=input.data_type,
                     power=1,
                     scale=weights.value.item(),
                     shift=bias.value.item())))
        update_ie_fields(graph.node[op_node])
        graph.add_edges_from([(input.node, op_node, {
            'in': 0
        }), (op_node, output.node, {
            'out': 0
        })])

    return