def load_kalid_nnet2_model(graph, file_descr, nnet_name): input_name = 'Input' graph.add_node(input_name, name=input_name, kind='op', op='Parameter', parameters=None, shape=None) prev_layer_id = input_name all_components = load_components(file_descr, graph) used_layers = set() for layer_id in all_components: prev_node = Node(graph, prev_layer_id) if prev_node.op == 'Parameter': parameters = Node(graph, layer_id).parameters input_dim = read_token_value(parameters, b'<InputDim>') prev_node['shape'] = np.array([1, input_dim], dtype=np.int64) prev_node.add_output_port(0) Node(graph, layer_id).add_input_port(0) graph.create_edge( prev_node, Node(graph, layer_id), 0, 0, create_edge_attrs(prev_layer_id, layer_id, prev_layer_id)) used_layers.add(prev_layer_id) prev_layer_id = layer_id log.debug('{} and {} were connected'.format(prev_layer_id, layer_id)) # Tensor names information corresponding to a node is stored on outgoing edges. # As output nodes do not have outgoing edges, fake outputs are required. In the following code # for each output Identity node is added, and tensor name for the output is kept # on (output, fake output) edge. After Result nodes adding transformation fake outputs # are deleted from graph. output_layers = graph.nodes - used_layers add_outputs_identity( graph, output_layers, lambda g, output, fake_output: g.create_edge( Node(g, output), Node(g, fake_output), 0, 0, create_edge_attrs(output, fake_output, output)))
def load(self, graph: Graph): argv = graph.graph['cmd_params'] if argv.tensorflow_custom_layer_libraries: libraries = argv.tensorflow_custom_layer_libraries.split(',') for library in libraries: log.info('Loading library "{}" with custom operations'.format( library)) tf_v1.load_op_library(library) graph_def, variables_values, framework, inputs_outputs_order = load_tf_graph_def( graph_file_name=argv.input_model, is_binary=not argv.input_model_is_text, checkpoint=argv.input_checkpoint, user_output_node_names_list=argv.output, model_dir=argv.saved_model_dir, meta_graph_file=argv.input_meta_graph, saved_model_tags=argv.saved_model_tags) if inputs_outputs_order is not None and isinstance( inputs_outputs_order, tuple): graph.inputs_order = inputs_outputs_order[0] graph.outputs_order = inputs_outputs_order[1] send_framework_info(framework) try: tf_v1.import_graph_def(graph_def, name='') except: log.warning( "TensorFlow post-processing of loaded model was unsuccessful. " "This is an optional step that Model Optimizer performs for any input model but it is not usually " "required for all models. " "It likely means that the original model is ill-formed. " "Model Optimizer will continue converting this model.") log.debug("Number of nodes in graph_def: {}".format(len( graph_def.node))) # pylint: disable=no-member if argv.tensorboard_logdir: tensorboard_util.dump_for_tensorboard(graph_def, argv.tensorboard_logdir) update_extractors_with_extensions(tf_op_extractors) try: protobuf2nx(graph, graph_def) except Exception as e: raise Error( 'Cannot pre-process TensorFlow graph after reading from model file "{}". ' \ 'File is corrupt or has unsupported format. Details: {}. ' + refer_to_faq_msg(44), argv.model_name, str(e) ) from e graph.__setattr__('name', argv.model_name) # 'layout' parameter change may cause an issue in EltwiseInputReshape replacer # and convert_nhwc_to_nchw(graph) graph.graph['layout'] = 'NCHW' if argv.disable_nhwc_to_nchw else 'NHWC' graph.graph['fw'] = 'tf' graph.graph['variables_values'] = variables_values del variables_values used_tensors = restore_edges(graph, get_tf_edges) # Tensor names information corresponding to a node is stored on outgoing edges. # As output nodes do not have outgoing edges, fake outputs are required. In the following code # for each output Identity node is added, and tensor name for the output is kept # on (output, fake output) edge. After Result nodes adding transformation fake outputs # are deleted from graph. add_outputs_identity( graph, graph.nodes - used_tensors, lambda g, output, fake_node_name: g.add_edges_from( [create_tf_edge(output, fake_node_name, 0)])) remove_control_dependency_inputs(graph) graph.check_empty_graph( 'protobuf2nx. It may happen due to problems with loaded model') extract_node_attrs( graph, lambda node: tf_op_extractor( node, check_for_duplicates(tf_op_extractors))) # try to detect layout from the nodes of the graph. If there are no convolution nodes in N(D)HWC layout then we # consider that the graph is in NCHW layout and no layout conversion should be performed if not argv.disable_nhwc_to_nchw and not graph_or_sub_graph_has_nhwc_ops( graph): if not argv.silent: log.debug('disable_nhwc_to_nchw" was automatically enabled.') for_graph_and_each_sub_graph_recursively( graph, update_cmd_params_and_layout) send_op_names_info(framework, graph) send_shapes_info(framework, graph)
def symbol2nx(graph, model_nodes, model_params, input_names: str = ''): if not input_names: input_names = ('data', ) else: input_names = input_names.split(',') rnn_states = init_rnn_states(model_nodes) names_rnn_states = list(rnn_states.keys()) # as mxnet contain input layers as index of layer, for correct set up edges, we need provide index of layer with name of graph node index_node_keys = {} fw_name_map = {} for i, node in enumerate(model_nodes): if node['name'] in model_params._arg_params and node[ 'name'] not in input_names: node['value'] = mo_array( model_params._arg_params[node['name']].asnumpy(), dtype=np.float32) elif node['name'] in model_params._aux_params and node[ 'name'] not in input_names: node['value'] = mo_array( model_params._aux_params[node['name']].asnumpy(), dtype=np.float32) elif node['name'] in names_rnn_states: node['value'] = np.zeros(rnn_states[node['name']], dtype=np.float32) node_name = graph.unique_id(node['name']) graph.add_node(node_name, **symbol_attrs(node)) if hasattr(graph, 'op_names_statistic') and 'op' in node: if node['op'] != 'null': graph.op_names_statistic[node['op']] += 1 graph.node[node_name].update( common_mxnet_fields(Node(graph, node_name))) index_node_keys[i] = node_name fw_name_map[node_name] = node['name'] used_indices_set = set() for i, attrs in enumerate(model_nodes): node = attrs edges, used_indices = get_mxnet_node_edges(node, i, list(model_nodes), index_node_keys) if len(edges) > 0: graph.add_edges_from(edges) used_indices_set = used_indices_set.union(used_indices) output_ids = [ index_node_keys[node_id] for node_id in set(range(len(model_nodes))) - used_indices_set ] # Tensor names information corresponding to a node is stored on outgoing edges. # As output nodes do not have outgoing edges, fake outputs are required. In the following code # for each output Identity node is added, and tensor name for the output is kept # on (output, fake output) edge. After Result nodes adding transformation fake outputs # are deleted from graph. add_outputs_identity( graph, output_ids, lambda g, output_id, fake_node_id, fw_name: g.add_edges_from([ create_mxnet_edge(output_id, fake_node_id, 0, 0, fw_name[output_id] ) ]), {'fw_name': fw_name_map}) return graph
def load_kalid_nnet1_model(graph, file_descr, name): prev_layer_id = 'Parameter' graph.add_node(prev_layer_id, name=prev_layer_id, kind='op', op='Parameter', parameters=None) # find out output layer, it can be only one due to chain structure of nnet1 model output_layer = None while True: component_type = find_next_component(file_descr) if component_type == end_of_nnet_tag.lower()[1:-1]: break layer_o = read_binary_integer32_token(file_descr) layer_i = read_binary_integer32_token(file_descr) if component_type == 'parallelcomponent': prev_layer_id = load_parallel_component(file_descr, graph, prev_layer_id) find_end_of_component(file_descr, component_type) continue start_index = file_descr.tell() end_tag, end_index = find_end_of_component(file_descr, component_type) end_index -= len(end_tag) layer_id = graph.unique_id(prefix=component_type) graph.add_node(layer_id, parameters=get_parameters(file_descr, start_index, end_index), op=component_type, kind='op', layer_i=layer_i, layer_o=layer_o) if hasattr(graph, 'op_names_statistic'): graph.op_names_statistic[component_type] += 1 prev_node = Node(graph, prev_layer_id) if prev_node.op == 'Parameter': prev_node['shape'] = np.array([1, layer_i], dtype=np.int64) prev_node.add_output_port(0) Node(graph, layer_id).add_input_port(0) graph.create_edge( prev_node, Node(graph, layer_id), 0, 0, create_edge_attrs(prev_layer_id, layer_id, prev_layer_id)) prev_layer_id = layer_id output_layer = layer_id log.debug('{} (type is {}) was loaded'.format(prev_layer_id, component_type)) # Tensor names information corresponding to a node is stored on outgoing edges. # As output nodes do not have outgoing edges, fake outputs are required. In the following code # for each output Identity node is added, and tensor name for the output is kept # on (output, fake output) edge. After Result nodes adding transformation fake outputs # are deleted from graph. assert output_layer is not None, "Output layer is not found in graph" add_outputs_identity( graph, [output_layer], lambda g, output, fake_output: g.create_edge( Node(g, output), Node(g, fake_output), 0, 0, create_edge_attrs(output, fake_output, output)))
def caffe_pb_to_nx(graph, proto, model): """ Converts proto/model layers to a graph. Edges are restored by bottom/top attributes. Graph nodes has two attributes: pb for prototxt definition and model_pb for caffemodel definition. Parameters ---------- proto : NetParameter Protobuf message for NetParameter, representing .prototxt. model : NetParameter Protobuf message for NetParameter, representing .caffemodel. Returns ---------- Graph built NX Directed graph. """ # Blobs in prototxt model can be reused by inplace layer. # This requires loading of pb layers in order and tracking the latest # layer that writes a particular blob. blob_producers = {} # maps layer blob name to node id in graph, port and layer name proto_layers = get_layers(proto) model_layers = None if model: model_layers = get_layers(model) input_dims = [] input_names = [] if len(proto.input_dim) > 0 and len(list(proto.input)) > 1: # example of proto input # input: "data" # input_dim: 1 # input_dim: 3 # input_dim: 500 # input_dim: 500 # input: "info" # input_dim: 1 # input_dim: 3 raise Error('Old-style inputs (via "input_dims") are not supported. ' + 'Please specify inputs via "input_shape". ' + refer_to_faq_msg(8)) elif len(list(proto.input)) == 1 and len(list(proto.input_dim)): # example of proto input # input: "data" # input_dim: 1 # input_dim: 3 # input_dim: 500 # input_dim: 500 input_dims = [np.array(list(proto.input_dim), dtype=np.int64)] input_names = [proto.input[0]] elif len(list(proto.input)) == 1 and len(list(proto.input_shape)): # example of proto input # input: "data" # input_shape # { # dim: 1 # dim: 3 # dim: 227 # dim: 227 # } input_dims = [np.array(proto.input_shape[0].dim, dtype=np.int64)] input_names = [proto.input[0]] elif len(proto.input_shape) > 0: # example of proto input # input: "data" # input_shape # { # dim: 1 # dim: 3 # dim: 600 # dim: 1000 # } # input: "im_info" # input_shape # { # dim: 1 # dim: 3 # } for i in range(len(proto.input_shape)): input_dims.append(np.array(proto.input_shape[i].dim, dtype=np.int64)) input_names.append(proto.input[i]) for i in range(len(input_names)): input_name = input_names[i] input_dim = input_dims[i] # Input is defined at the top level of proto instead of distinct Input layer graph.add_node(input_name, pb=None, model_pb=None, type='GlobalInput', name=input_name, shape=input_dim, kind='op') blob_producers[input_name] = (input_name, 0, input_name) used_blobs = set() for i, layer in enumerate(proto_layers): model_layer = None if model_layers: for ml in model_layers: if ml.name == layer.name: model_layer = ml break if layer.type == 'Input': if hasattr(layer, 'input_param'): input_param = layer.input_param else: raise Error('Input layer has no input dims. ' + refer_to_faq_msg(8)) if hasattr(input_param, 'shape'): """ example of proto input layer { name: "data" type: "Input" top: "data" input_param {shape: {dim: 1 dim: 3 dim: 600 dim: 1000}} } layer { name: "im_info" type: "Input" top: "im_info" input_param {shape: {dim: 1 dim: 3}} } """ dims = map(int, list(filter(None, str(list(input_param.shape)[0]).split('dim:')))) input_dims.append(np.array(list(dims), dtype=np.int64)) input_names.append(layer.name) node_id = graph.unique_id(layer.name) graph.add_node(node_id, pb=layer, model_pb=model_layer, kind='op', type='Parameter') if hasattr(graph, 'op_names_statistic') and hasattr(layer, 'type'): graph.op_names_statistic[layer.type] += 1 # connect inputs based on blob_producers dictionary for dst_port, bottom in enumerate(layer.bottom): add_edge_caffe(graph, bottom, node_id, blob_producers, dst_port) used_blobs.add(bottom) # update blob producers dictionary by output ports for src_port, top in enumerate(layer.top): if top in blob_producers: log.debug("Detected reuse of blob {} by layer {}".format(top, node_id)) blob_producers[top] = (node_id, src_port, layer.name) # Tensor names information corresponding to a node is stored on outgoing edges. # As output nodes do not have outgoing edges, fake outputs are required. In the following code # for each output Identity node is added, and tensor name for the output is kept # on (output, fake output) edge. After Result nodes adding transformation fake outputs # are deleted from graph. all_blobs = set(blob_producers.keys()) add_outputs_identity(graph, all_blobs - used_blobs, add_edge_caffe, {'blob_producers': blob_producers, 'dst_port': 0}) if len(input_names) <= 0: raise Error('The topology contains no "input" layers. ' + refer_to_faq_msg(79)) return {fake_node_name: shape for (fake_node_name, shape) in zip(input_names, input_dims)}