Example #1
0
def make_graph(
    nodes: Sequence[NodeProto],
    name: Text,
    inputs: Sequence[ValueInfoProto],
    outputs: Sequence[ValueInfoProto],
    initializer: Optional[Sequence[TensorProto]] = None,
    doc_string: Optional[Text] = None,
    value_info: Sequence[ValueInfoProto] = [],
    sparse_initializer: Optional[Sequence[SparseTensorProto]] = None,
) -> GraphProto:
    if initializer is None:
        initializer = []
    if sparse_initializer is None:
        sparse_initializer = []
    if value_info is None:
        value_info = []
    graph = GraphProto()
    graph.node.extend(nodes)
    graph.name = name
    graph.input.extend(inputs)
    graph.output.extend(outputs)
    graph.initializer.extend(initializer)
    graph.sparse_initializer.extend(sparse_initializer)
    graph.value_info.extend(value_info)
    if doc_string:
        graph.doc_string = doc_string
    return graph
Example #2
0
def make_graph(
    nodes,  # type: Sequence[NodeProto]
    name,  # type: Text
    inputs,  # type: Sequence[ValueInfoProto]
    outputs,  # type: Sequence[ValueInfoProto]
    initializer=None,  # type: Optional[Sequence[TensorProto]]
    doc_string=None,  # type: Optional[Text]
    value_info=[],  # type: Sequence[ValueInfoProto]
    sparse_initializer=None,  # type: Optional[Sequence[SparseTensorProto]]
):  # type: (...) -> GraphProto
    if initializer is None:
        initializer = []
    if sparse_initializer is None:
        sparse_initializer = []
    if value_info is None:
        value_info = []
    graph = GraphProto()
    graph.node.extend(nodes)
    graph.name = name
    graph.input.extend(inputs)
    graph.output.extend(outputs)
    graph.initializer.extend(initializer)
    graph.sparse_initializer.extend(sparse_initializer)
    graph.value_info.extend(value_info)
    if doc_string:
        graph.doc_string = doc_string
    return graph
Example #3
0
 def test_attr_repeated_graph_proto(self):  # type: () -> None
     graphs = [GraphProto(), GraphProto()]
     graphs[0].name = "a"
     graphs[1].name = "b"
     attr = helper.make_attribute("graphs", graphs)
     self.assertEqual(attr.name, "graphs")
     self.assertEqual(list(attr.graphs), graphs)
     checker.check_attribute(attr)
Example #4
0
def make_graph(nodes, name, inputs, outputs, initializer=None, doc_string=None):
    if initializer is None:
        initializer = []
    graph = GraphProto()
    graph.node.extend(nodes)
    graph.name = name
    graph.input.extend(inputs)
    graph.output.extend(outputs)
    graph.initializer.extend(initializer)
    if doc_string:
        graph.doc_string = doc_string
    return graph
Example #5
0
def insert_node(graph: GraphProto, index: int, node: NodeProto):
    """
    Insert helper function to insert a node at a given index in the link list of the graph.

    Args:
        graph (GraphProto): Onnx graph to be modify
        index (int): Index to insert the new node
        node (NodeProto): The node to be inserted

    """
    graph.extend([graph[-1]])
    for i in reversed(range(index + 1, len(graph) - 1)):
        graph[i].CopyFrom(graph[i - 1])
    graph[index].CopyFrom(node)
Example #6
0
def make_graph(
    nodes: Sequence[NodeProto],
    name: Text,
    inputs: Sequence[ValueInfoProto],
    outputs: Sequence[ValueInfoProto],
    initializer: Optional[Sequence[TensorProto]] = None,
    doc_string: Optional[Text] = None,
    value_info: Sequence[ValueInfoProto] = [],
    sparse_initializer: Optional[Sequence[SparseTensorProto]] = None,
) -> GraphProto:
    """Construct a GraphProto

    Arguments:
        nodes: list of NodeProto
        name (string): graph name
        inputs: list of ValueInfoProto
        outputs: list of ValueInfoProto
        initializer: list of TensorProto
        doc_string (string): graph documentation
        value_info: list of ValueInfoProto
        sparse_initializer: list of SparseTensorProto
    Returns:
        GraphProto
    """
    if initializer is None:
        initializer = []
    if sparse_initializer is None:
        sparse_initializer = []
    if value_info is None:
        value_info = []
    graph = GraphProto()
    graph.node.extend(nodes)
    graph.name = name
    graph.input.extend(inputs)
    graph.output.extend(outputs)
    graph.initializer.extend(initializer)
    graph.sparse_initializer.extend(sparse_initializer)
    graph.value_info.extend(value_info)
    if doc_string:
        graph.doc_string = doc_string
    return graph
Example #7
0
    def singa_to_onnx_graph(cls, inputs, y, model_name="sonnx"):
        """
        get onnx model from singa computational graph
        Args:
            inputs: a list of input tensors (each is initialized with a name)
        Args:
            y: a list of tensors, usually the outputs of the graph
        Returns: 
            the onnx model
        """
        assert len(y) == 1  # assume there is only one output
        y = y[0]

        graph_def = GraphProto()
        graph_def.name = model_name
        topol = postorderRecursive(y.creator, y)
        # since tensor's name might change
        # we record its id
        input_tensors = {id(x): x for x in inputs}
        # print(input_tensors)
        X = []
        Y = [helper.make_tensor_value_info(y.name, TensorProto.FLOAT, y.shape)]

        for op, yid, op_t in topol:
            optype = cls._get_singa_op_type(op)
            # print(op.name, cls._get_singa_op_type(op), op_t, optype, yid)
            if yid in input_tensors and optype == 'Dummy':
                # find the input by its id
                op_t = input_tensors[yid]
                dtype = TensorProto.FLOAT
                if op_t.dtype == tensor.int32:
                    dtype = TensorProto.INT32
                X.append(
                    helper.make_tensor_value_info(op.name, dtype, op_t.shape))
            else:
                graph_def.node.extend(cls.singa_op_to_onnx_node(op, op_t))

        graph_def.input.extend(X)
        graph_def.output.extend(Y)
        return graph_def
Example #8
0
    def test_get_inputs(self):
        model = OnnxModel(model_proto=ModelProto(
            graph=GraphProto(initializer=[TensorProto(name='y')],
                             input=[
                                 ValueInfoProto(name='x'),
                                 ValueInfoProto(name='y'),
                                 ValueInfoProto(name='z')
                             ])),
                          input_data_formats=[None, None])

        self.assertEqual(model.get_inputs(),
                         [ValueInfoProto(name='x'),
                          ValueInfoProto(name='z')])
Example #9
0
def make_graph(
    nodes,  # type: Sequence[NodeProto]
    name,  # type: Text
    inputs,  # type: Sequence[ValueInfoProto]
    outputs,  # type: Sequence[ValueInfoProto]
    initializer=None,  # type: Optional[Sequence[TensorProto]]
    doc_string=None,  # type: Optional[Text]
    value_info=[],  # type: Sequence[ValueInfoProto]
):  # type: (...) -> GraphProto
    if initializer is None:
        initializer = []
    if value_info is None:
        value_info = []
    graph = GraphProto()
    graph.node.extend(nodes)
    graph.name = name
    graph.input.extend(inputs)
    graph.output.extend(outputs)
    graph.initializer.extend(initializer)
    graph.value_info.extend(value_info)
    if doc_string:
        graph.doc_string = doc_string
    return graph
Example #10
0
def make_model(nodes: Sequence[INodeProto],
               inputs: Sequence[IValueInfoProto],
               outputs: Sequence[IValueInfoProto],
               initializer=None) -> IModelProto:
    if initializer is None:
        initializer = []
    graph = GraphProto()
    graph.node.extend(nodes)
    graph.input.extend(inputs)
    graph.output.extend(outputs)
    graph.initializer.extend(initializer)

    model = ModelProto()
    model.graph.CopyFrom(graph)

    return model
Example #11
0
    def caffe2_net_to_onnx_graph(cls,
                                 predict_net,
                                 init_net=None,
                                 value_info=None):
        if value_info is None:
            value_info = {}
        if not isinstance(value_info, dict):
            raise ValueError('Please pass value_info as a '
                             'name -> (type, shape) dictionary')

        cls._filter_fake_init(init_net, value_info)
        cls._ssa_rewrite(predict_net, init_net, value_info)

        if init_net:
            initializer = cls.caffe2_init_net_to_initializer(init_net)
            value_info.update({
                init.name: (init.data_type, init.dims)
                for init in initializer
            })
        else:
            initializer = []

        # Check whether we have got type shape info of all input
        missing = (set(list(predict_net.external_input)) -
                   set(value_info.keys()))
        if missing:
            raise RuntimeError(
                'Could not find value info of inputs: {}'.format(
                    ', '.join(missing)))

        inputs = {}
        for name in predict_net.external_input:
            elem_type, shape = value_info[name]
            inputs[name] = np.random.randn(*shape).astype(
                mapping.TENSOR_TYPE_TO_NP_TYPE[elem_type])

        ws, outputs = c2_native_run_net(init_net, predict_net, inputs)

        for name in predict_net.external_output:
            output = outputs[name]
            elem_type = mapping.NP_TYPE_TO_TENSOR_TYPE[output.dtype]
            shape = output.shape
            value_info[name] = (elem_type, shape)

        graph_def = GraphProto()
        graph_def.name = predict_net.name
        graph_def.initializer.extend(initializer)
        # This is a mapping from Caffe2 names to ONNX names
        graph_def.input.extend(
            make_tensor_value_info(name=name,
                                   elem_type=value_info[name][0],
                                   shape=value_info[name][1])
            for name in predict_net.external_input)

        dummy_name(
            cls._all_names_in_net(predict_net)
            | cls._all_names_in_net(init_net))

        for op in predict_net.op:
            shapes = {}
            for name in itertools.chain(op.input, op.output):
                blob = ws.FetchBlob(name)
                if hasattr(blob, 'shape'):
                    shapes[name] = blob.shape
            nodes, const_tensors = cls.caffe2_op_to_onnx_node(op,
                                                              shapes=shapes)
            graph_def.node.extend(nodes)
            graph_def.initializer.extend(const_tensors)
            graph_def.input.extend(
                [cls._extract_value_info(tensor) for tensor in const_tensors])

        all_output = set(
            sum((list(node.output) for node in graph_def.node),
                [init.name for init in graph_def.initializer]))
        redundant_output = set(vi.name for vi in graph_def.output) - all_output
        if redundant_output:
            logger.warning(
                'There are graph output not produced by any node or initializer: {}'
                '! Will drop them.'.format(', '.join(redundant_output)))
        graph_def.output.extend(
            make_tensor_value_info(name=name,
                                   elem_type=value_info[name][0],
                                   shape=value_info[name][1])
            for name in predict_net.external_output if name in all_output)

        checker.check_graph(graph_def)
        return graph_def
Example #12
0
    def convert_mx2onnx_graph(self,
                              mx_graph,
                              mx_weights,
                              in_shape,
                              in_type,
                              log=False):
        print("\nconverting weights from MxNet NDArrays to NumPy arrays.\n")
        weights = MxNetToONNXConverter.convert_weights_to_numpy(mx_weights)

        onnx_graph = GraphProto()

        initializer = []
        all_processed_nodes = []
        onnx_processed_nodes = []
        onnx_processed_inputs = []
        onnx_processed_outputs = []

        for idx, node in enumerate(mx_graph):
            op = node["op"]
            name = node["name"]
            if log:
                print("Converting idx: %d, op: %s, name: %s" % (idx, op, name))
            converted = MxNetToONNXConverter.convert_layer(
                node,
                mx_graph=mx_graph,
                weights=weights,
                in_shape=in_shape,
                in_type=in_type,
                proc_nodes=all_processed_nodes,
                initializer=initializer)

            if isinstance(converted, onnx_pb2.ValueInfoProto):
                if idx < (len(mx_graph) - 1):
                    onnx_processed_inputs.append(converted)
                else:
                    onnx_processed_outputs.append(converted)
            elif isinstance(converted, onnx_pb2.NodeProto):
                if idx < (len(mx_graph) - 1):
                    onnx_processed_nodes.append(converted)
                else:
                    onnx_processed_nodes.append(converted)
                    onnx_processed_outputs.append(
                        make_tensor_value_info(
                            name=converted.name,
                            elem_type=mapping.NP_TYPE_TO_TENSOR_TYPE[np.dtype(
                                'float32')],
                            shape=(in_shape[0], -1)))
                    if log:
                        print("Output node is: %s" % converted.name)
            elif isinstance(converted, onnx_pb2.TensorProto):
                raise ValueError("Did not expect TensorProto")
                if idx < (len(mx_graph) - 1):
                    onnx_processed_inputs.append(converted)
                else:
                    onnx_processed_outputs.append(converted)
            else:
                print(converted)
                raise ValueError("node is of an unrecognized type: %s" %
                                 type(node))

            all_processed_nodes.append(converted)

        graph = helper.make_graph(onnx_processed_nodes, "main",
                                  onnx_processed_inputs,
                                  onnx_processed_outputs)

        graph.initializer.extend(initializer)

        checker.check_graph(graph)
        return graph
Example #13
0
    def test_merge_drop_unnecessary_initializers_and_value_info(self) -> None:
        '''
        Tests automatic removal of initializers when merging graphs
        '''
        ops = [helper.make_opsetid("", 10)]

        g = GraphProto()
        g.input.extend(
            [helper.make_tensor_value_info('x', TensorProto.FLOAT, [])])
        g.output.extend(
            [helper.make_tensor_value_info('y', TensorProto.FLOAT, [])])
        g.node.extend(
            [helper.make_node('Identity', inputs=['x'], outputs=['y'])])

        g1 = GraphProto()
        g1.CopyFrom(g)
        g1.name = 'g1'
        m1 = helper.make_model(g1, producer_name='test', opset_imports=ops)
        checker.check_model(m1)

        g2 = GraphProto()
        g2.CopyFrom(g)
        g2.name = 'g2'
        g2.initializer.extend([
            helper.make_tensor(name='x',
                               data_type=TensorProto.FLOAT,
                               dims=(),
                               vals=[0])
        ])
        m2 = helper.make_model(g2, producer_name='test', opset_imports=ops)
        checker.check_model(m2)

        g3 = GraphProto()
        g3.CopyFrom(g)
        g3.name = 'g3'
        g3.sparse_initializer.extend([_make_sparse_tensor('x')])
        m3 = helper.make_model(g3, producer_name='test', opset_imports=ops)
        checker.check_model(m3)

        g4 = GraphProto()
        g4.CopyFrom(g)
        g4.name = 'g3'
        g4.value_info.extend(
            [helper.make_tensor_value_info('x', TensorProto.FLOAT, [])])
        m4 = helper.make_model(g4, producer_name='test', opset_imports=ops)
        checker.check_model(m4)

        # Initializer 'x' from m1 is removed, because there is no longer an input with that name
        out_m1 = compose.merge_models(m1,
                                      m2,
                                      prefix1='m1/',
                                      io_map=[('y', 'x')])
        self.assertEqual(0, len(out_m1.graph.initializer))

        # Sparse initializer 'x' from m1 is removed, because there is no longer an input with that name
        out_m2 = compose.merge_models(m1,
                                      m3,
                                      prefix1='m1/',
                                      io_map=[('y', 'x')])
        self.assertEqual(0, len(out_m2.graph.initializer))

        # Value info 'x' from m1 is removed, because there is no longer an input with that name
        out_m3 = compose.merge_models(m1,
                                      m4,
                                      prefix1='m1/',
                                      io_map=[('y', 'x')])
        self.assertEqual(0, len(out_m3.graph.value_info))
Example #14
0
    def caffe2_net_to_onnx_graph(cls,
                                 predict_net,
                                 init_net=None,
                                 value_info=None):
        if value_info is None:
            value_info = {}
        if not isinstance(value_info, dict):
            raise ValueError('Please pass value_info as a '
                             'name -> (type, shape) dictionary')

        cls._filter_fake_init(init_net, value_info)
        cls._ssa_rewrite(predict_net, init_net, value_info)

        if init_net:
            initializer = cls.caffe2_init_net_to_initializer(init_net)
            value_info.update({init.name: (init.data_type, init.dims)
                               for init in initializer})
        else:
            initializer = []

        # Check whether we have got type shape info of all input
        missing = (set(list(predict_net.external_input)) -
                   set(value_info.keys()))
        if missing:
            raise RuntimeError('Could not find value info of inputs: {}'.format(
                ', '.join(missing)))

        inputs = {}
        for name in predict_net.external_input:
            elem_type, shape = value_info[name]
            inputs[name] = np.random.randn(*shape).astype(
                mapping.TENSOR_TYPE_TO_NP_TYPE[elem_type])

        ws, outputs = c2_native_run_net(
            init_net,
            predict_net,
            inputs)

        for name in predict_net.external_output:
            output = outputs[name]
            elem_type = mapping.NP_TYPE_TO_TENSOR_TYPE[output.dtype]
            shape = output.shape
            value_info[name] = (elem_type, shape)

        graph_def = GraphProto()
        graph_def.name = predict_net.name
        graph_def.initializer.extend(initializer)
        # This is a mapping from Caffe2 names to ONNX names
        graph_def.input.extend(
            make_tensor_value_info(
                name=name,
                elem_type=value_info[name][0],
                shape=value_info[name][1])
            for name in predict_net.external_input)

        cls._dummy_name.reset(cls._all_names_in_net(predict_net) | cls._all_names_in_net(init_net))

        for op in predict_net.op:
            shapes = {}
            for name in itertools.chain(op.input, op.output):
                blob = ws.FetchBlob(name)
                if hasattr(blob, 'shape'):
                    shapes[name] = blob.shape
            nodes, const_tensors = cls.caffe2_op_to_onnx_node(op, shapes=shapes)
            graph_def.node.extend(nodes)
            graph_def.initializer.extend(const_tensors)
            graph_def.input.extend([cls._extract_value_info(tensor) for tensor in const_tensors])

        all_output = set(sum((list(node.output) for node in graph_def.node),
                             [init.name for init in graph_def.initializer]))
        redundant_output = set(vi.name for vi in graph_def.output) - all_output
        if redundant_output:
            logger.warning(
                'There are graph output not produced by any node or initializer: {}'
                '! Will drop them.'.format(', '.join(redundant_output)))
        graph_def.output.extend(
            make_tensor_value_info(
                name=name,
                elem_type=value_info[name][0],
                shape=value_info[name][1])
            for name in predict_net.external_output
            if name in all_output)

        return graph_def
Example #15
0
    def test_overlapping_function_names(self) -> None:
        '''
        Tests error checking when the name of local function entries overlaps
        '''
        ops = [helper.make_opsetid("", 10), helper.make_opsetid("local", 10)]

        def _make_function(
            domain: str,
            fname: str,
            inputs: List[str],
            outputs: List[str],
            nodes: List[NodeProto],
        ) -> FunctionProto:
            f = FunctionProto()
            f.domain = domain
            f.name = fname
            f.input.extend(inputs)
            f.output.extend(outputs)
            f.node.extend(nodes)
            f.opset_import.extend(ops)
            return f

        ops = [helper.make_opsetid("", 10), helper.make_opsetid("local", 10)]

        g = GraphProto()
        g.input.extend([
            helper.make_tensor_value_info('x0', TensorProto.FLOAT, []),
            helper.make_tensor_value_info('x1', TensorProto.FLOAT, [])
        ])
        g.output.extend([
            helper.make_tensor_value_info('y', TensorProto.FLOAT, []),
        ])
        g.node.extend([
            helper.make_node('f1',
                             domain='local',
                             inputs=['x0', 'x1'],
                             outputs=['y'])
        ])

        g1 = GraphProto()
        g1.CopyFrom(g)
        g1.name = 'g1'
        m1 = helper.make_model(g1, producer_name='test', opset_imports=ops)
        m1.functions.extend([
            _make_function(
                'local', 'f1', ['x0', 'x1'], ['y'],
                [helper.make_node('Add', inputs=['x0', 'x1'], outputs=['y'])])
        ])
        checker.check_model(m1)

        g2 = GraphProto()
        g2.CopyFrom(g)
        g2.name = 'g2'
        m2 = helper.make_model(g2, producer_name='test', opset_imports=ops)
        m2.functions.extend([
            _make_function(
                'local', 'f1', ['x0', 'x1'], ['y'],
                [helper.make_node('Mul', inputs=['x0', 'x1'], outputs=['y'])])
        ])
        checker.check_model(m2)

        m = compose.merge_models(m1,
                                 m2,
                                 io_map=[('y', 'x0'), ('y', 'x1')],
                                 prefix1='m1/',
                                 prefix2='m2/')
        checker.check_model(m)

        nodes = [n.op_type for n in m.graph.node]
        self.assertEqual(['m1/f1', 'm2/f1'], nodes)

        functions = [f.name for f in m.functions]
        self.assertEqual(['m1/f1', 'm2/f1'], functions)

        g3 = GraphProto()
        g3.CopyFrom(g)
        g3.name = 'g3'
        g3.node[0].op_type = 'f2'
        m3 = helper.make_model(g3, producer_name='test', opset_imports=ops)
        m3.functions.extend([
            _make_function('local', 'f1', ['x0', 'x1'], ['y'], [
                helper.make_node('Add', inputs=['x0', 'x1'], outputs=['y0']),
                helper.make_node('Mul', inputs=['x0', 'x1'], outputs=['y1']),
                helper.make_node('Add', inputs=['y0', 'y1'], outputs=['y'])
            ]),
            _make_function('local', 'f2', ['x0', 'x1'], ['y'], [
                helper.make_node(
                    'f1', domain='local', inputs=['x0', 'x1'], outputs=['y0']),
                helper.make_node('Mul', inputs=['x0', 'x1'], outputs=['y1']),
                helper.make_node('Add', inputs=['y0', 'y1'], outputs=['y'])
            ])
        ])
        checker.check_model(m3)

        m = compose.merge_models(m1,
                                 m3,
                                 io_map=[('y', 'x0'), ('y', 'x1')],
                                 prefix1='m1/',
                                 prefix2='m3/')
        checker.check_model(m)

        nodes = [n.op_type for n in m.graph.node]
        self.assertEqual(['m1/f1', 'm3/f2'], nodes)

        functions = [f.name for f in m.functions]
        self.assertEqual(['m1/f1', 'm3/f1', 'm3/f2'], functions)

        self.assertEqual(['Add'], [n.op_type for n in m.functions[0].node])
        self.assertEqual(['Add', 'Mul', 'Add'],
                         [n.op_type for n in m.functions[1].node])
        self.assertEqual(['m3/f1', 'Mul', 'Add'],
                         [n.op_type for n in m.functions[2].node])
Example #16
0
def add_prefix_graph(
    graph: GraphProto,
    prefix: Text,
    rename_nodes: Optional[bool] = True,
    rename_edges: Optional[bool] = True,
    rename_inputs: Optional[bool] = True,
    rename_outputs: Optional[bool] = True,
    rename_initializers: Optional[bool] = True,
    rename_value_infos: Optional[bool] = True,
    inplace: Optional[bool] = False,
) -> GraphProto:
    """Adds a prefix to names of elements in a graph: nodes, edges, inputs, outputs,
    initializers, sparse initializer, value infos.

    It can be used as a utility before merging graphs that have overlapping names.
    Empty names are not prefixed.

    Arguments:
        graph (GraphProto): Graph
        prefix (Text): Prefix to be added to each name in the graph
        rename_nodes (bool): Whether to prefix node names
        rename_edges (bool): Whether to prefix node edge names
        rename_inputs (bool): Whether to prefix input names
        rename_outputs (bool): Whether to prefix output names
        rename_initializers (bool): Whether to prefix initializer and sparse initializer names
        rename_value_infos (bool): Whether to prefix value info names
        inplace (bool): If True, mutates the graph directly.
                        Otherwise, a copy will be created
    """
    if type(graph) is not GraphProto:
        raise ValueError("graph argument is not an ONNX graph")

    if not inplace:
        g = GraphProto()
        g.CopyFrom(graph)
    else:
        g = graph

    def _prefixed(prefix: Text, name: Text) -> Text:
        return prefix + name if len(name) > 0 else name

    name_map = {}
    if rename_edges:
        for n in g.node:
            for e in n.input:
                name_map[e] = _prefixed(prefix, e)
            for e in n.output:
                name_map[e] = _prefixed(prefix, e)
    else:
        if rename_outputs:
            for entry in g.output:
                name_map[entry.name] = _prefixed(prefix, entry.name)
        if rename_inputs:
            for entry in g.input:
                name_map[entry.name] = _prefixed(prefix, entry.name)

    if rename_nodes:
        for n in g.node:
            n.name = _prefixed(prefix, n.name)

    if rename_initializers:
        for init in g.initializer:
            name_map[init.name] = _prefixed(prefix, init.name)
        for sparse_init in g.sparse_initializer:
            name_map[sparse_init.values.name] = _prefixed(
                prefix, sparse_init.values.name)
            name_map[sparse_init.indices.name] = _prefixed(
                prefix, sparse_init.indices.name)

    if rename_value_infos:
        for entry in g.value_info:
            name_map[entry.name] = _prefixed(prefix, entry.name)

    for n in g.node:
        for i in range(len(n.output)):
            if n.output[i] in name_map:
                n.output[i] = name_map[n.output[i]]
        for i in range(len(n.input)):
            if n.input[i] in name_map:
                n.input[i] = name_map[n.input[i]]

    for in_desc in g.input:
        if in_desc.name in name_map:
            in_desc.name = name_map[in_desc.name]
    for out_desc in g.output:
        if out_desc.name in name_map:
            out_desc.name = name_map[out_desc.name]

    for initializer in g.initializer:
        if initializer.name in name_map:
            initializer.name = name_map[initializer.name]
    for sparse_initializer in g.sparse_initializer:
        if sparse_initializer.values.name in name_map:
            sparse_initializer.values.name = name_map[
                sparse_initializer.values.name]
        if sparse_initializer.indices.name in name_map:
            sparse_initializer.indices.name = name_map[
                sparse_initializer.indices.name]

    for value_info in g.value_info:
        if value_info.name in name_map:
            value_info.name = name_map[value_info.name]

    return g
Example #17
0
        if node_proto.output is None:
            node_proto.output = []
            output_types = output_arr.types
            type_attr = onnx.helper.make_attribute(
                output_arr.name + '-types', [
                    str(data_type).replace('tensor(', '').replace(')', '')
                    for data_type in output_types
                ])
            node_proto.attribute.append(type_attr)
        node_proto.output.append(output_arr.name)
    return node_proto


nodes = [
    create_node_from_schema(schema)
    for schema in sorted(schemas, key=lambda s: s.name)
]

with open('onnx-op-defs.pb', 'wb') as f:
    graph_proto = GraphProto()
    graph_proto.node.extend(nodes)
    f.write(graph_proto.SerializeToString())
    # for node in nodes:
    #     message_to_string = text_format.MessageToString(node, as_utf8=True)
    #     node_2 = load_node(message_to_string)
    #     f.write(message_to_string + '----f\n')

# with open('onnx.pbtxt','r') as f:
#     nodes = [load_node(node_str) for node_str in f.read().split('----f\n')]
#     print(nodes)
Example #18
0
def expand_out_dim_graph(
    graph: GraphProto,
    dim_idx: int,
    inplace: Optional[bool] = False,
) -> GraphProto:
    """Inserts an extra dimension with extent 1 to each output in the graph.

    Inserts an Unsqueeze node for each output. It can be used as a utility before merging graphs,
    for example when the second one expects a batch dimension.

    Arguments:
        graph (GraphProto): Graph
        dim_idx (int): Index of the dimension to be inserted.
                       A negative value means counting dimensions from the back.
        inplace (bool): If True, mutates the model directly.
                        Otherwise, a copy will be created
    """
    if type(graph) is not GraphProto:
        raise ValueError("graph argument is not an ONNX graph")

    if not inplace:
        g = GraphProto()
        g.CopyFrom(graph)
    else:
        g = graph

    orig_out_names = [output.name for output in g.output]

    for n in g.node:
        for i in range(len(n.output)):
            if n.output[i] in orig_out_names:
                n.output[i] = n.output[i] + f'_collapsed_dim_{dim_idx}'
        for i in range(len(n.input)):
            if n.input[i] in orig_out_names:
                n.input[i] = n.input[i] + f'_collapsed_dim_{dim_idx}'

    expand_dim_k = g.name + "_expand_out_dim_idx"
    g.node.append(
        helper.make_node('Constant',
                         inputs=[],
                         outputs=[expand_dim_k],
                         name=f"{expand_dim_k}-constant",
                         value=helper.make_tensor(name=f"{expand_dim_k}-value",
                                                  data_type=tp.INT64,
                                                  dims=[
                                                      1,
                                                  ],
                                                  vals=[
                                                      dim_idx,
                                                  ])))

    for _ in range(len(g.output)):
        o = g.output.pop(0)
        prev_output = o.name + f'_collapsed_dim_{dim_idx}'
        g.node.append(
            helper.make_node('Unsqueeze',
                             inputs=[prev_output, expand_dim_k],
                             outputs=[o.name],
                             name=f"unsqueeze-{o.name}"))
        new_shape = [d.dim_value for d in o.type.tensor_type.shape.dim]
        new_shape.insert(dim_idx, 1)
        g.output.append(
            helper.make_tensor_value_info(o.name, o.type.tensor_type.elem_type,
                                          new_shape))
    return g
Example #19
0
def merge_graphs(
    g1: GraphProto,
    g2: GraphProto,
    io_map: List[Tuple[Text, Text]],
    inputs: Optional[List[Text]] = None,
    outputs: Optional[List[Text]] = None,
    prefix1: Optional[Text] = None,
    prefix2: Optional[Text] = None,
    name: Optional[Text] = None,
    doc_string: Optional[Text] = None,
) -> GraphProto:
    """Combines two ONNX graphs into a single one.

    The combined graph is defined by connecting the specified set of outputs/inputs. Those inputs/outputs
    not specified in the io_map argument will remain as inputs/outputs of the combined graph.

    Arguments:
        g1 (GraphProto): First graph
        g2 (GraphProto): Second graph
        io_map (list of pairs of string): The pairs of names [(out0, in0), (out1, in1), ...]
                                          representing outputs of the first graph and inputs of the second
                                          to be connected
        inputs (list of string): Optional list of inputs to be included in the combined graph
                                 By default, all inputs not present in the ``io_map`` argument will be
                                 included in the combined model
        outputs (list of string): Optional list of outputs to be included in the combined graph
                                  By default, all outputs not present in the ``io_map`` argument will be
                                  included in the combined model
        prefix1 (string): Optional prefix to be added to all names in g1
        prefix2 (string): Optional prefix to be added to all names in g2
        name (string): Optional name for the combined graph
                       By default, the name is g1.name and g2.name concatenated with an undescore delimiter
        doc_string (string): Optional docstring for the combined graph
                             If not provided, a default docstring with the concatenation of g1 and g2 docstrings is used
    """
    if type(g1) is not GraphProto:
        raise ValueError("g1 argument is not an ONNX graph")
    if type(g2) is not GraphProto:
        raise ValueError("g2 argument is not an ONNX graph")

    # Prefixing names in the graph if requested, adjusting io_map accordingly
    if prefix1 or prefix2:
        if prefix1:
            g1_copy = GraphProto()
            g1_copy.CopyFrom(g1)
            g1 = g1_copy
            g1 = add_prefix_graph(g1, prefix=prefix1)
        if prefix2:
            g2_copy = GraphProto()
            g2_copy.CopyFrom(g2)
            g2 = g2_copy
            g2 = add_prefix_graph(g2, prefix=prefix2)
        io_map = [(prefix1 + io[0] if prefix1 else io[0],
                   prefix2 + io[1] if prefix2 else io[1]) for io in io_map]

    io_map_g1_outs = set([io[0] for io in io_map])
    io_map_g2_ins = set([io[1] for io in io_map])
    reversed_io_map = {in_name: out_name for out_name, in_name in io_map}
    g1_outs = set([o.name for o in g1.output])
    g2_ins = set([i.name for i in g2.input])

    # If necessary extract subgraphs
    if inputs or outputs:
        if not inputs:
            g1_inputs = [i.name for i in g1.input]
            g2_inputs = [i.name for i in g2.input]
        else:
            input_set = set(inputs)
            g1_inputs = [i.name for i in g1.input if i.name in input_set]
            g2_inputs = [
                i.name for i in g2.input
                if i.name in input_set or i.name in io_map_g2_ins
            ]

        if not outputs:
            g1_outputs = [o.name for o in g1.input]
            g2_outputs = [o.name for o in g2.input]
        else:
            output_set = set(outputs)
            g1_outputs = [
                o.name for o in g1.output
                if o.name in output_set or o.name in io_map_g1_outs
            ]
            g2_outputs = [o.name for o in g2.output if o.name in output_set]

        if len(g1_inputs) < len(g1.input) or len(g1_outputs) < len(g1.output):
            e1 = utils.Extractor(helper.make_model(g1))
            g1 = e1.extract_model(g1_inputs, g1_outputs).graph

        if len(g2_inputs) < len(g2.input) or len(g2_outputs) < len(g2.output):
            e2 = utils.Extractor(helper.make_model(g2))
            g2 = e2.extract_model(g2_inputs, g2_outputs).graph

    # Check that input/output names specified in the io_map argument are valid input/output names
    for g1_out_name, g2_in_name in io_map:
        if g1_out_name not in g1_outs:
            raise ValueError(f"Output {g1_out_name} is not present in g1")
        if g2_in_name not in g2_ins:
            raise ValueError(f"Input {g2_in_name} is not present in g2")

    # Check for name collision
    overlapping_names = check_overlapping_names(g1, g2, io_map)
    if len(overlapping_names) > 0:
        category, names = overlapping_names[0]
        raise ValueError(
            "Cant merge two graphs with overlapping names. "
            f"Found repeated {category} names: " + ", ".join(names) + "\n" +
            "Consider using ``onnx.compose.add_prefix`` to add a prefix to names in one of the graphs."
        )

    g = GraphProto()

    g.node.extend(g1.node)
    g2_nodes_begin = len(g.node)
    g.node.extend(g2.node)
    g2_nodes_end = len(g.node)

    # Connecting outputs of the first graph with the inputs of the second
    for node_idx in range(g2_nodes_begin, g2_nodes_end):
        node = g.node[node_idx]
        for index, name in enumerate(node.input):
            if name in reversed_io_map:
                node.input[index] = reversed_io_map[name]

    if inputs:
        input_set = set(inputs)
        g.input.extend([i for i in g1.input if i.name in input_set])
        g.input.extend([i for i in g2.input if i.name in input_set])
    else:
        g.input.extend(g1.input)
        g.input.extend([i for i in g2.input if i.name not in io_map_g2_ins])

    if outputs:
        output_set = set(outputs)
        g.output.extend([o for o in g1.output if o.name in output_set])
        g.output.extend([o for o in g2.output if o.name in output_set])
    else:
        g.output.extend([o for o in g1.output if o.name not in io_map_g1_outs])
        g.output.extend(g2.output)

    g.initializer.extend(g1.initializer)
    g.initializer.extend(
        [init for init in g2.initializer if init.name not in io_map_g2_ins])

    g.sparse_initializer.extend(g1.sparse_initializer)
    g.sparse_initializer.extend([
        init for init in g2.sparse_initializer
        if init.values.name not in io_map_g2_ins
    ])

    g.value_info.extend(g1.value_info)
    g.value_info.extend(
        [vi for vi in g2.value_info if vi.name not in io_map_g2_ins])

    g.name = name if name is not None else "_".join([g1.name, g2.name])

    if doc_string is None:
        doc_string = f"Graph combining {g1.name} and {g2.name}\n" + \
            g1.name + "\n\n" + g1.doc_string + "\n\n" + g2.name + "\n\n" + g2.doc_string
    g.doc_string = doc_string

    return g
Example #20
0
    def graph_def_to_onnx_graph(
        cls,
        graph_def,
        init_func=None,
        constants=None,
        value_info=None,
        graph_name=None,
        verbose=True,
        enforce_no_running=False,
    ):
        if value_info is None: value_info = {}
        if not isinstance(value_info, dict):
            raise ValueError(
                'Please pass value_info as a '
                    'name -> (type, shape) dictionary')

        leaf_tensors = extract_leaf_tensors(graph_def)
        initializer = extract_initializer(graph_def)

        # Check whether we have got type shape info of all input
        missing = (leaf_tensors - set(value_info.keys()) - initializer)
        if missing:
            raise RuntimeError('Could not find value info of inputs: {}'.format(
                ', '.join(missing)))

        # Check if value_info contains the types/shapes of all the blobs, in
        # which case we don't need to infer them by running the net.
        run_native_graph = False
        for op in graph_def.op:
            for name in itertools.chain(op.input, op.output):
                if name not in value_info:
                    run_native_graph = True
                    break

        ws = None

        # Get the value info of outputs and initializer
        if run_native_graph and not enforce_no_running:
            inputs = {}
            for name, (elem_type, shape) in value_info.items():
                inputs[name] = numpy.random.randn(*shape).astype(
                    mapping.TENSOR_TYPE_TO_NP_TYPE[elem_type])

            ws, outputs, initializer = native_run_graph(
                graph_def, inputs, initializer, init_func)

            for name in graph_def.output:
                output = outputs[name]
                elem_type = mapping.NP_TYPE_TO_TENSOR_TYPE[output.dtype]
                shape = output.shape
                value_info[name] = (elem_type, shape)

        if enforce_no_running:
            # In some cases(e.g. PyTorch), we had ran the graph
            # outputs had been in ``value_info`` already
            ws = _workspace.get_default_workspace()
            initializer = fetch_initializer(initializer)

        # Prepare to make the graph
        onnx_graph = GraphProto()
        onnx_graph.name = graph_name if graph_name else graph_def.name

        # Initializer should also be included in the inputs
        value_info.update({
            init.name: (init.data_type, init.dims)
                for init in initializer})

        # Add initializer
        onnx_graph.initializer.extend(initializer)

        # Add inputs
        onnx_graph.input.extend(
            make_tensor_value_info(
                name=name,
                elem_type=value_info[name][0],
                shape=value_info[name][1])
            for name in leaf_tensors)

        # Add outputs
        onnx_graph.output.extend(
            make_tensor_value_info(
                name=name,
                elem_type=value_info[name][0],
                shape=value_info[name][1])
            for name in set(graph_def.output))

        # Add constants
        if constants is not None:
            for k, v in constants.items():
                onnx_graph.initializer.extend(
                    [numpy_helper.from_array(v, name=k)])

        # Add nodes
        shapes, ssa_names, ssa_outputs = {}, {}, defaultdict(int)

        for op in graph_def.op:
            # Get the shape of inputs and outputs
            for name in itertools.chain(op.input, op.output):
                if ws and ws.HasTensor(name):
                    blob = ws.FetchTensor(name)
                    if hasattr(blob, 'shape'):
                        shapes[name] = blob.shape
                else:
                    shapes[name] = value_info[name][1]

            # SSA rewritten
            op, shapes, ssa_names, ssa_outputs = \
                cls._ssa_rewrite(op, shapes, ssa_names, ssa_outputs)

            # Try to translate op => nodes
            nodes, const_tensors = get_nodes_def(op, shapes, ws)

            # Directly convert outputs as const tensors if necessary
            if None in nodes:
                const_tensors = [
                    numpy_helper.from_array(
                        ws.FetchTensor(name), name=name)
                            for name in op.output]
            else:
                onnx_graph.node.extend(nodes)

            # Add const tensors
            if const_tensors is not None:
                onnx_graph.initializer.extend(const_tensors)
                onnx_graph.input.extend([
                    cls._extract_value_info(tensor)
                        for tensor in const_tensors])

        if verbose: print(printable_graph(onnx_graph))

        return onnx_graph
def _write_onnx(onnx_graph: onnx.GraphProto, out: Path) -> None:
    with open(out, "wb") as fp:
        fp.write(onnx_graph.SerializeToString())