Ejemplo n.º 1
0
    def _create_data_if_necessary(self):
        if self.node.graph.stage == 'front':
            raise Error("_create_data_if_necessary method is not applicable for front Graph phase!")
        if self.type == 'in':
            raise Error("_create_data_if_necessary method is not applicable for 'in' Port type!")

        if self.idx not in self.node.out_nodes(control_flow=self.control_flow):
            from mo.ops.op import Op
            Op.create_data_node(self.node.graph, self.node, out_port=self.idx)
            self.node['need_shape_inference'] = True
        return self.node.out_node(self.idx, control_flow=self.control_flow)
Ejemplo n.º 2
0
    def apply_mean_value(graph: Graph, input_node: Node,
                         node_mean_scale_values: dict):
        if 'mean' in node_mean_scale_values and node_mean_scale_values[
                'mean'] is not None:
            if all([x == 0 for x in node_mean_scale_values['mean']]):
                return
            out_node = input_node.out_node()
            if not input_node.has_valid('shape'):
                raise Error("Node {} has not valid shape attribute".format(
                    input_node.id))
            input_shape = input_node.shape
            # Create Add node
            graph.remove_edge(input_node.id, out_node.id)

            value = np.array(node_mean_scale_values['mean']) * (-1)

            add_node = Add(graph, dict(name="Add_"))
            add_data = Op.create_input_data_node(graph, "data_add_",
                                                 np.array(value))
            Op.expand_node_shape(add_data,
                                 (len(input_shape) -
                                  2 if graph.graph['layout'] == 'NCHW' else 0))
            add_input = Op.create_data_node(graph, input_node,
                                            {'shape': out_node.shape})

            add_node.create_node_with_data(inputs=[add_input, add_data],
                                           data_nodes=out_node)
Ejemplo n.º 3
0
    def apply_scale(graph: Graph, input_node: Node,
                    node_mean_scale_values: dict):
        if 'scale' in node_mean_scale_values and node_mean_scale_values[
                'scale'] is not None:
            if all([x == 1 for x in node_mean_scale_values['scale']]):
                return
            out_node = input_node.out_node()
            if not input_node.has_valid('shape'):
                raise Error("Node {} has not valid shape attribute".format(
                    input_node.id))
            input_shape = input_node.shape

            # Create Mul node
            value = 1 / np.array(node_mean_scale_values['scale'])
            graph.remove_edge(input_node.id, out_node.id)

            mul_node = Mul(graph, dict(name="Mul_"))
            mul_data = Op.create_input_data_node(graph, "data_mul_",
                                                 np.array(value))
            Op.expand_node_shape(mul_data,
                                 (len(input_shape) -
                                  2 if graph.graph['layout'] == 'NCHW' else 0))
            mul_input = Op.create_data_node(graph, input_node,
                                            {'shape': out_node.shape})

            mul_node.create_node_with_data(inputs=[mul_input, mul_data],
                                           data_nodes=out_node)
Ejemplo n.º 4
0
    def insert_node_with_data_after(self,
                                    out,
                                    new_op_class: callable,
                                    op_after_params: dict = None):
        """
        Inserts operation node with op_after_params and data node after current operation

        :param out: output data node of current node
        :param new_op_class: class of operation that will be inserted after current operation node
        :param op_after_params:  parameters to be added to operation that will be inserted after current operation

        Before calling:
        [...] -> Cur_Op -> Cur_Data -> [...]

        After calling:
        [...] -> Cur_Op -> Cur_Data -> New_Op_aft -> New_Data_aft(==out) -> [...]
                                   [op_after_params]
        """
        # we import it here because Op imports Node and unique_id from this file
        from mo.ops.op import Op

        graph = self.graph
        node = Node(graph, self.node)
        cls_name = new_op_class.op
        op_after_params = {} if op_after_params is None else op_after_params

        new_op_after = new_op_class(graph, op_after_params)
        graph.remove_edge(node.id, out.id)
        new_out = Op.create_data_node(graph, node)
        node.infer(node)
        new_op_after.create_node_with_data(
            [new_out], {'name': node.name + cls_name + '/After'},
            data_nodes=out)
Ejemplo n.º 5
0
def _scale_input_action_mul(graph: nx.MultiDiGraph, match: dict, scale: float):
    assert (len(match['placeholder'].out_nodes()))

    tinput = match['placeholder']
    if not tinput.has_valid('shape'):
        raise Error("Node {} has not valid shape attribute".format(tinput.id))

    input_shape = tinput.shape
    toutput = match['data']

    # Create Mul node
    value = np.array([1 / scale])

    # Disconnect input with data node
    graph.remove_edge(tinput.id, toutput.id)

    # Create Mul node
    mul_node = Mul(graph, dict(name="Mul1_"))
    mul_data = Op.create_input_data_node(graph, "data_mul_scale_",
                                         np.array(value))
    Op.expand_node_shape(
        mul_data,
        len(input_shape) - 2 if graph.graph['layout'] == 'NCHW' else 0)
    mul_input = Op.create_data_node(graph, tinput, {'shape': toutput.shape})

    mul_node.create_node_with_data(inputs=[mul_input, mul_data],
                                   data_nodes=toutput)
Ejemplo n.º 6
0
    def insert_abs_max(self, model_graph, node, type_stat, node_name,
                       **kwargs):
        axis_const = self.find_axis(node, kwargs.get('granularity'))
        if isinstance(axis_const, str):
            return (True, node.name)
        abs_node = Abs(node.graph, {
            "name": f'abs_{node_name}'
        }).create_node_with_data([node.out_node(0)]).in_node(0)
        max_op = create_op_node_with_second_input(
            node.graph, ReduceMax, int64_array(axis_const),
            dict(name=f'{type_stat}_{node_name}'))

        if node.graph != model_graph:
            Op.create_data_node(max_op.graph, max_op, {'shape': [1]})
        max_op['fullname'] = reset_node_fullname(node.fullname, max_op.name)
        abs_node.out_port(0).connect(max_op.in_port(0))
        return self.insert_result(model_graph, node, max_op, type_stat)
Ejemplo n.º 7
0
 def insert_reduce(self,
                   model_graph,
                   insert_op,
                   node,
                   granularity,
                   type_stat,
                   node_name,
                   axis=1):
     axis_const = self.find_axis(node, granularity, axis)
     if isinstance(axis_const, str):
         return (True, node.name)
     reduce_op = create_op_node_with_second_input(
         node.graph, insert_op, int64_array(axis_const),
         dict(name=f'{type_stat}_{node_name}'))
     reduce_op['fullname'] = reset_node_fullname(node.fullname,
                                                 reduce_op.name)
     if node.graph != model_graph:
         Op.create_data_node(reduce_op.graph, reduce_op, {'shape': [1]})
     node.out_port(0).connect(reduce_op.in_port(0))
     return self.insert_result(model_graph, node, reduce_op, type_stat)
Ejemplo n.º 8
0
    def replace_pattern(self, graph: Graph, match: dict):
        if match['axis'].value is None or match['input'].shape is None:
            return
        dims = len(match['input'].shape)
        ones = np.ones(dims, dtype=np.int64)
        axis = np.array(match['axis'].value)
        axis = axis if axis.ndim != 0 else np.array([axis], dtype=np.int64)

        mean = graph.node[match['mean'].node]
        mean['stride'] = np.array(ones)
        # TODO: need to check axis with real layout
        spatial_dims = np.array(axis)
        mean['spatial_dims'] = spatial_dims
        mean['pad'] = np.zeros((dims, 2), np.int64)
        mean['pad_spatial_shape'] = np.array(mean['pad'][spatial_dims])
        window = np.array(ones)
        window[spatial_dims] = match['input'].shape[spatial_dims]
        mean['window'] = window
        mean['TF_op'] = mean['op']
        mean['op'] = 'AvgPool'
        mean['pool_method'] = 'avg'
        mean['rounding_type'] = 'ceil'
        mean['exclude_pad'] = 'true'
        mean['kernel_spatial'] = window[spatial_dims]
        graph.remove_edge(match['axis'].node, match['mean'].node)
        mean['permute_attrs'] = PermuteAttrs().update_attrs(attrs=[(
            'pad',
            'input:0'), ('stride',
                         'input:0'), ('window',
                                      'input:0'), ('spatial_dims', 'input:0')])

        if match['mean'].keep_dims == False:
            output = match['mean'].out_node()
            pool_node = match['mean']

            # Keep dims for AvgPool
            shape = np.array(output.shape)
            for idx in spatial_dims:
                shape = np.insert(shape, idx, 1)

            graph.remove_edge(pool_node.id, output.id)
            # Create new data for pool with all dims
            pool_data = Op.create_data_node(graph, pool_node,
                                            {'shape': np.array(shape)})
            # Create and connect reshape node
            reshape_op = Reshape(graph, {'dim': np.array(output.shape)})
            reshape_node = reshape_op.create_node(
                [pool_data],
                dict(name='Reshape_',
                     permute_attrs=PermuteAttrs().update_attrs(
                         attrs=[('dim', 'output:0')])))
            graph.create_edge(reshape_node, output)
Ejemplo n.º 9
0
    def replace_pattern(self, graph: Graph, match: dict):
        """
            This pass normalize FC layer
            Example:

            (2,16,512)-->FC->(2,16,101)    =>    (2,16,512)-->Reshape-->(32,512)-->FC-->(32,101)-->Reshape-->(2,16,101)

        """
        fc = match['fc']
        fc_weights = fc.in_node(1)
        fc_output = match['fc_output']
        fc_input = fc.in_node()

        input_shape = fc.in_node().shape
        if len(input_shape) <= 2 or np.prod(
                fc_input.shape[1:]) == fc_weights.shape[
                    fc_weights.input_channel_dim]:
            return

        # Insert Reshape to normalize input for FC layer that should be in [N,C] layout
        first_reshape_shape = np.array(
            [np.prod(input_shape[0:-1]), input_shape[-1]], dtype=np.int64)
        second_reshape_shape = np.array([*input_shape[0:-1], fc['out-size']],
                                        dtype=np.int64)
        fc_out_shape = np.array([np.prod(input_shape[0:-1]), fc['out-size']],
                                dtype=np.int64)
        first_reshape = Reshape(graph, {'dim': np.array(first_reshape_shape)})
        second_reshape = Reshape(graph,
                                 {'dim': np.array(second_reshape_shape)})

        input_edge_attrs = graph.get_edge_data(fc_input.id, fc.id)[0]
        output_edge_attrs = graph.get_edge_data(fc.id, fc_output.id)[0]

        graph.remove_edge(fc_input.id, fc.id)
        graph.remove_edge(fc.id, fc_output.id)

        # Insert Reshapes before and after FullyConnected layer
        reshape_data = first_reshape.create_node_with_data(inputs=[fc_input])
        graph.add_edge(reshape_data.id, fc.id, **input_edge_attrs)

        new_fc_output = Op.create_data_node(graph,
                                            fc, {'shape': fc_out_shape},
                                            edge_attrs=output_edge_attrs)

        second_reshape.create_node_with_data(inputs=[new_fc_output],
                                             data_nodes=fc_output)
Ejemplo n.º 10
0
    def insert_node_with_data_after(self,
                                    out,
                                    new_op_class: callable,
                                    op_after_params: dict = None,
                                    additional_inputs: list = None):
        """
        Inserts operation node with op_after_params and data node after current operation

        :param out: output data node of current node
        :param new_op_class: class of operation that will be inserted after current operation node
        :param op_after_params:  parameters to be added to operation that will be inserted after current operation
        :param additional_inputs:  other parameters for a new operation node in addition to one that is created
            at the 'out' placed; new nodes are added after 0-th input

            TODO Allow indexing for input parameters as well as for 'out' data node to explicitly
                specify ports that are connected to.

        Before calling:
        [...] -> Cur_Op -> Cur_Data -> [...]

        After calling:
        [...] -> Cur_Op -> Cur_Data -> New_Op_aft -> New_Data_aft(==out) -> [...]
                                   [op_after_params]
        """
        # we import it here because Op imports Node and unique_id from this file
        from mo.ops.op import Op

        graph = self.graph
        node = Node(graph, self.node)
        cls_name = new_op_class.op
        op_after_params = {} if op_after_params is None else op_after_params

        new_op_after = new_op_class(graph, op_after_params)
        graph.remove_edge(node.id, out.id)
        new_out = Op.create_data_node(graph, node)
        node.infer(node)
        # form a list of input nodes for a new op node combining new_out and additional_inputs
        inputs = [new_out] + (additional_inputs if additional_inputs else [])
        new_op_after.create_node_with_data(
            inputs, {'name': node.name + cls_name + '/After'}, data_nodes=out)
    def find_and_replace_pattern(self, graph: Graph):
        for node in list(graph.nodes()):
            node = Node(graph, node)
            # Check that node layout mismatch with graph layout
            # For example: NHWC and NCHW or NCDHW and NDHWC
            if node.kind == 'op' and node.has_valid(
                    'layout') and node.layout != indices_mapping[len(
                        node.layout)][graph.graph['layout']]:
                input = node.in_node()
                output = node.out_node()

                # Calculate permutation for further Transpose operations
                if graph.graph['layout'] == 'NCHW':
                    # if Node has NCHW and graph has NHWC layout
                    permutation = PermuteAttrs.get_nhwc_to_nchw_permutation(
                        len(node.layout))
                else:
                    # if Node has NHWC and graph has NCHW layout
                    permutation = PermuteAttrs.get_nchw_to_nhwc_permutation(
                        len(node.layout))

                # Schematic representation of transformation below
                #
                #                                           \            NCHW                              NCHW
                #            NHWC                        --  \            |  permutation       permutation  |
                #   data-->Convolution(example)-->data   --  /            |      |       NCHW      |        |
                #                                           /   data->Transpose->data->Convolution->data->Transpose->data

                # 1. Insert input Transpose
                #    This Transpose will permute input from original input layout to operation layout
                edge_attrs = graph.get_edge_data(input.id, node.id)[0]
                graph.remove_edge(input.id, node.id)

                input_order_const = Const(graph, {
                    'value': permutation.perm
                }).create_node_with_data()
                input_permute_op = Transpose(
                    graph, dict(name=node.name + '/Transpose_'))
                input_permute_data_node = input_permute_op.create_node_with_data(
                    [input, input_order_const])

                graph.add_edge(input_permute_data_node.id, node.id,
                               **edge_attrs)

                # 2. Insert output Transpose
                #    This Transpose will permute output from operation layout to original input layout
                edge_attrs = graph.get_edge_data(node.id, output.id)[0]
                graph.remove_edge(node.id, output.id)

                input_data_node = Op.create_data_node(
                    graph, node, {'shape': output.shape[permutation.perm]},
                    edge_attrs)

                output_order_const = Const(graph, {
                    'value': permutation.inv
                }).create_node_with_data()
                output_permute_op = Transpose(
                    graph, dict(name=node.name +
                                '/Transpose_')).create_node_with_data(
                                    [input_data_node, output_order_const],
                                    data_nodes=output)

                # 3. Add permutations for Node
                #    Here we use permutation mechanism where data nodes takes permutation attribute.
                #    And then we call permute_attrs method that permutes node attributes according to permutations on
                #    data nodes.
                node.in_node()['permutation'] = permutation
                node.out_node()['permutation'] = permutation
                node.permute_attrs.permute_attrs(node)

                node.in_node()['permutation'] = None
                node.out_node()['permutation'] = None