2 Copyright (c) 2018-2019 Intel Corporation
4 Licensed under the Apache License, Version 2.0 (the "License");
5 you may not use this file except in compliance with the License.
6 You may obtain a copy of the License at
8 http://www.apache.org/licenses/LICENSE-2.0
10 Unless required by applicable law or agreed to in writing, software
11 distributed under the License is distributed on an "AS IS" BASIS,
12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 See the License for the specific language governing permissions and
14 limitations under the License.
16 from __future__ import absolute_import
17 from __future__ import division
18 from __future__ import print_function
19 from __future__ import unicode_literals
26 from mo.graph.graph import create_graph_with_nodes, Graph
27 from mo.utils.error import Error, FrameworkError
30 def load_onnx_model(file_name: str):
32 onnx_model = onnx.load(file_name)
33 except Exception as e:
35 'Cannot read the model file: "{}" is incorrect ONNX model file. Details: {}',
43 def protobuf_attrs(pb):
48 ''' The result of this function should be passed to unique_id to be used as a unuque ID for new node creation. '''
52 # node may have multiple outputs, we choose the first one
59 '''Convert proto message with ONNX model to equivalent NX representation.
60 All nodes and edges are restored here as ONNX model has op/data representation,
61 that means that nodes are connected via tensor names. Name of tensors are defined
62 on demand in nodes, so we have a code similar to Caffe here. '''
63 # graph = create_graph_with_nodes(pb.graph.node, get_id=node_id, get_attrs=protobuf_attrs)
64 # convert initializers to a NX graph for easier control of model consistency and to use it as a dictionary later
65 initializers = create_graph_with_nodes(pb.graph.initializer, get_id=lambda pb: pb.name, get_attrs=protobuf_attrs)
69 # maps a tensor name to a node produced it and the node port: str -> (node_id, node_port)
72 # first go through all inputs and separate constant from placeholders
73 for inp in pb.graph.input:
75 if graph.has_node(name):
76 raise Error('Name {} of input node already exists, input names are duplicated.', name)
77 elif initializers.has_node(name):
79 graph.add_node(name, kind='op', op='Const', pb=inp, pb_init=initializers.node[name]['pb'])
81 # this is a placeholder
82 graph.add_node(name, kind='op', op='Placeholder', pb=inp)
83 # add to a tensors map
84 assert not name in data_nodes_map, 'Inconsistency between data_nodes_map and graph.nodes'
85 data_nodes_map[name] = (name, 0)
87 # go over all initializer and make sure that all of them are added to the graph
88 for initializer in initializers.nodes():
89 if not graph.has_node(initializer):
90 graph.add_node(initializer, kind='op', op='Const', pb=initializers.node[initializer]['pb'],
91 pb_init=initializers.node[initializer]['pb'])
92 data_nodes_map[initializer] = (initializer, 0)
94 # Go through all nodes in the original model order (because data nodes are defined on-the-fly and order is
96 for node in pb.graph.node:
98 id = graph.unique_id(node_id(node))
99 graph.add_node(id, pb=node, kind='op')
101 # add incoming edges based on data_nodes_map
102 for dst_port, inp in enumerate(node.input):
103 # should add edge inp --> id
104 if inp not in data_nodes_map:
106 # input is omitted; most likely it corresponds to an optional input for an operator
110 'Reference to {} is not satisfied. A node refer not existing data tensor. ONNX model is not '
111 'consistent. Protobuf fragment: {}', inp, node)
112 src_id, src_port = data_nodes_map[inp]
114 assert (graph.has_node(src_id))
119 'fw_tensor_debug_info': [(inp, inp)],
120 'in_attrs': ['in', 'name'],
121 'out_attrs': ['out', 'name'],
122 'data_attrs': ['fw_tensor_debug_info']
124 graph.add_edge(src_id, id, **edge_attrs)
126 # add outgoing edges to data_nodes_map
127 for src_port, out in enumerate(node.output):
128 if out in data_nodes_map:
129 log.debug("Detected reuse of blob {}.".format(out))
130 data_nodes_map[out] = (id, src_port)