def find_and_replace_pattern(self, graph: Graph): add_output_ops(graph, graph.graph['packed_outputs'], inputs=graph.graph['user_shapes']) # For keeping tensor names information for output nodes fake outputs are added # to graph during the model loading. In the following code fake outputs are removed # and tensor names information is moved to output->Result edge. for node in graph.get_op_nodes(needs_removal=True): fw_info = None in_node = None for in_port_idx in node.in_edges(): node_idx = node.in_edge(in_port_idx)['in'] if node_idx in node.in_nodes(): in_node = node.in_node(node_idx) fw_info_value = get_edge_attribute_between_nodes( in_node, node, 'fw_tensor_debug_info') if fw_info_value: fw_info = fw_info_value break graph.erase_node(node) if fw_info is not None and in_node is not None: for out_idx in in_node.out_nodes(): set_edge_attribute_between_nodes(in_node, in_node.out_node(out_idx), 'fw_tensor_debug_info', fw_info)
def erase_node(self, node: Node): """ Erases node from the graph and reconnect edges from input node(s) to output node(s) Produces assertion error if the node being removed has multiple inputs or outputs. The function can be used in the front phase only (when there are no data nodes in the graph). :param node: Node to erase """ node_id = node.id inputs = list(self.in_edges(node_id, data=True)) outputs = list(self.out_edges(node_id, data=True)) assert node.kind == 'op' and (len(node.out_nodes()) == 0 or list(node.out_nodes().values())[0].kind != 'data'), \ "The function must be used before the partial infer when graph doesn't contain data nodes." assert len(node.out_nodes( )) <= 1, "The node {} must produce just one output tensor".format( node.soft_get('name')) assert len(inputs) <= 1, "The node {} must have just one input".format( node.soft_get('name')) if len(outputs) == 0 and len(inputs) != 0: from mo.front.extractor import add_output_ops input_ids = { input_node_id: { 'port': { 'out': [attrs['out']] } } for input_node_id, _, attrs in inputs } if node.has('op') and node.op == 'OpOutput': add_output_ops(self, input_ids) if len(outputs) == 0 or len(inputs) == 0: self.remove_node(node_id) return input_node_id = inputs[0][0] for src, dst, attrs in outputs: self.remove_edge(src, dst) # update the 'out' attribute of the edge from the node being removed attrs['out'] = inputs[0][2]['out'] self.add_edge(input_node_id, dst, **attrs) self.remove_node(node_id)
def test_output_port_cut(self, output): nodes = { 'A': { 'type': 'Identity', 'kind': 'op', 'op': 'Identity' }, 'B': { 'type': 'Identity', 'kind': 'op', 'op': 'Identity' }, 'C': { 'type': 'Identity', 'kind': 'op', 'op': 'Identity' }, 'D': { 'type': 'Identity', 'kind': 'op', 'op': 'Identity' }, 'E': { 'type': 'Identity', 'kind': 'op', 'op': 'Identity' }, } edges = [('A', 'C', { 'in': 0, 'out': 0 }), ('B', 'C', { 'in': 1, 'out': 0 }), ('C', 'D', { 'in': 0, 'out': 0 }), ('C', 'E', { 'in': 0, 'out': 1 })] graph = build_graph_with_edge_attrs(nodes, edges) sinks = add_output_ops(graph, output) graph.clean_up() self.assertEqual(len(Node(graph, 'C').out_nodes()), 1) self.assertEqual(len(Node(graph, 'C').in_nodes()), 2)
def test_output_port_cut(self, output): nodes = {'A': {'op': 'Placeholder', 'kind': 'op'}, 'B': {'op': 'Placeholder', 'kind': 'op'}, 'C': {'type': 'Identity', 'kind': 'op'}, 'D': {'type': 'Identity', 'kind': 'op'}, 'E': {'type': 'Identity', 'kind': 'op'}, } edges = [ ('A', 'C', {'in': 0, 'out': 0}), ('B', 'C', {'in': 1, 'out': 0}), ('C', 'D', {'in': 0, 'out': 0}), ('C', 'E', {'in': 0, 'out': 1}) ] graph = build_graph_with_edge_attrs(nodes, edges) sinks = add_output_ops(graph, output) eliminate.graph_clean_up(graph) self.assertEqual(len(graph.nodes()), 2)
def find_and_replace_pattern(self, graph: Graph): add_output_ops(graph, graph.graph['packed_outputs'], inputs=graph.graph['user_shapes'])
def tf2nx(argv: argparse.Namespace, model_file_name: str, output_model_name: str, outputs: list, output_dir: str, scale: float, is_binary: bool, user_shapes: [None, list, np.array] = None, mean_scale_values: [dict, list] = ()): """ Convert TF GraphDef object to NetworkX representation. The resulting graph is still TF-specific and needs normalization passes to be applied. The specific TF structure assumes each GraphDef node is converted to a single NetworkX node, node id is an original TF node name, and edges go directly from one op to another op. """ meta_info = get_meta_info(argv) 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.load_op_library(library) graph_def, variables_values = load_tf_graph_def(graph_file_name=model_file_name, is_binary=is_binary, checkpoint=argv.input_checkpoint, user_output_node_names_list=outputs, model_dir=argv.saved_model_dir, meta_graph_file=argv.input_meta_graph, saved_model_tags=argv.saved_model_tags) try: tf.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.dump_for_tensorboard(graph_def, argv.tensorboard_logdir) update_extractors_with_extensions(tf_op_extractors) try: graph = protobuf2nx(graph_def) graph.__setattr__('name', output_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['cmd_params'] = argv graph.graph['fw'] = 'tf' graph.graph['ir_version'] = 2 if argv.generate_deprecated_IR_V2 else 4 if graph.graph['ir_version'] == 2: # When the deprecated IR version was requested, # we configure only those phases that can lead to # functional regressions in the version 2. # BasicLSTMCell is one such transformation; when it is turned off, # the body of TF basic_lstm_cell is converted as-is in a decomposed form, # and should work in version 2. BasicLSTMCell.enabled = False # placeholder for request from a transformation pass to repeat the entire conversion graph.graph['repeat_conversion'] = False graph = restore_edges(graph, get_tf_edges) graph = remove_control_dependency_inputs(graph) # extract basic attributes earlier to enable some passes that relies on them before full attribute # extractor is called extract_node_attrs(graph, lambda node: (True, common_tf_fields(node))) 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), model_file_name, str(e) ) from e check_empty_graph(graph, 'protobuf2nx. It may happen due to problems with loaded model') packed_user_shapes, packed_outputs, freeze_placeholder = user_data_repack(graph, user_shapes, outputs, argv.freeze_placeholder_with_value) if freeze_placeholder is not None: FreezePlaceholderValue.enabled = True FreezePlaceholderValue.replacement_dict = freeze_placeholder update_registration() GemmResolver.enabled = False inputs = list(packed_user_shapes.keys()) if packed_user_shapes is not None and isinstance(packed_user_shapes, dict) else None graph.graph['inputs'] = inputs # save user defined inputs for other extensions output_op_nodes = add_output_ops(graph, packed_outputs, inputs=packed_user_shapes) input_op_nodes = add_input_ops(graph, packed_user_shapes, True) # this call of 'graph_clean_up' removes child nodes of outputs which is useful when custom output is specified graph_clean_up_tf(graph) check_empty_graph(graph, 'add_output_ops and add_input_ops. It may happen due to absence of \'Placeholder\' layer ' 'in the model') variables_to_constants(graph, variables_values) del variables_values graph_clean_up_tf(graph) if argv.tensorflow_custom_operations_config_update: if update_custom_replacement_config_file(graph, argv.tensorflow_custom_operations_config_update): return 0 else: return 1 unsupported_ops_to_offload_to_tf = list() MAX_ITERATIONS = 5 cur_iteration = 0 while cur_iteration < MAX_ITERATIONS: graph_copy = copy.deepcopy(graph) # create a copy of graph for the case when some ops are unsupported if argv.tensorflow_subgraph_patterns is not None: csc.replace_subgraph_calls(graph, argv.tensorflow_subgraph_patterns) if argv.tensorflow_operation_patterns is not None: csc.offload_operations_to_tf(graph, argv.tensorflow_operation_patterns) if argv.offload_unsupported_operations_to_tf and len(unsupported_ops_to_offload_to_tf): csc.offload_unsupported_operations_to_tf(graph, unsupported_ops_to_offload_to_tf) extract_node_attrs(graph, lambda node: tf_op_extractor(node, check_for_duplicates(tf_op_extractors))) if argv.tensorflow_use_custom_operations_config is not None: registry = CustomReplacementRegistry() registry.add_custom_replacement_description_from_config(argv.tensorflow_use_custom_operations_config) # automatically generate sub-classes for custom replacements that replace sub-graph with a single node for replacement_desc in registry.get_all_replacements_descriptions(): if replacement_desc.has('op'): type('FrontReplacementFromConfigFileOp' + replacement_desc.op, (FrontReplacementFromConfigFileOp,), {'replacement_id': replacement_desc.id}) update_registration() override_placeholder_shapes(graph, packed_user_shapes) # the user shapes are used to convert TensorFlow Object Detection API models graph.graph['user_shapes'] = packed_user_shapes class_registration.apply_replacements(graph, class_registration.ClassType.FRONT_REPLACER) override_batch(graph, argv.batch) create_tensor_nodes(graph) graph_clean_up_tf(graph) remove_output_ops(graph) partial_infer(graph) delete_control_flow_edges(graph) replacer = AddIsCyclicAttribute() replacer.find_and_replace_pattern(graph) # TENSOR ITERATOR CREATING BEGINS if graph.graph['is_cyclic']: replacer = DeleteSelect() replacer.find_and_replace_pattern(graph) replacer = SmartInputMatcher() replacer.find_and_replace_pattern(graph) replacer = SmartOutputMatcher() replacer.find_and_replace_pattern(graph) replacer = LoopConditionMatcher() replacer.find_and_replace_pattern(graph) replacer = SimpleConditionMather() replacer.find_and_replace_pattern(graph) replacer = BackEdgesMatching() replacer.find_and_replace_pattern(graph) replacer = ConditionChecks() replacer.find_and_replace_pattern(graph) delete_not_executable(graph) graph_clean_up_tf(graph) if graph.graph['is_cyclic']: replacer = SimpleInputMatcher() replacer.find_and_replace_pattern(graph) replacer = BackEdgeSimpleInputMatcher() replacer.find_and_replace_pattern(graph) # Here will be optimizing path (ops after Enter and before body take out of body) replacer = TensorIteratorMerge() replacer.find_and_replace_pattern(graph) # TENSOR ITERATOR CREATING ENDS check_for_cycle(graph) for_graph_and_each_sub_graph_recursively(graph, graph_clean_up_tf) check_empty_graph(graph, 'partial_infer') csc.prepare_tf_call_nodes(graph) graph_clean_up_tf(graph) duplicate_shared_weights(graph) input_op_nodes = add_input_ops(graph, packed_user_shapes, False) graph_clean_up_tf(graph) check_empty_graph(graph, 'add_input_ops') change_placeholders_types_to_FP32(graph) scale_input(graph, scale) add_mean_scale_values(graph, mean_scale_values) convert_dilated_convolution(graph) for_graph_and_each_sub_graph_recursively(graph, graph_clean_up_tf) l2_norm_to_norm(graph) graph_clean_up_tf(graph) remove_op_nodes(graph, {'identity': True}) remove_useless_split(graph) class_registration.apply_replacements(graph, class_registration.ClassType.MIDDLE_REPLACER) mean_to_avgpool(graph) convert_nasnet(graph) fuse_pad(graph) graph_clean_up_tf(graph) convert_matmul_to_fully_connected(graph) # Mark nodes with attr 'can_be_fused': False to disable fusing for specified nodes for_graph_and_each_sub_graph_recursively(graph, lambda graph: mark_unfused_nodes(graph, argv.finegrain_fusing)) # Converting FusedBatchNorm layer to Mul->Add->Mul->Add sequence # IE doesn't support BN with 4 inputs, so we have to split it to two ScaleShift convert_batch_norm(graph) graph_clean_up_tf(graph) if not argv.disable_fusing: # Converting ScaleShift layer to Mul->Add for_graph_and_each_sub_graph_recursively(graph, convert_scale_shift_to_mul_add) for_graph_and_each_sub_graph_recursively(graph, graph_clean_up_tf) # Fusing the sequences of Mul/Add operations for_graph_and_each_sub_graph_recursively(graph, fuse_mul_add_sequence) for_graph_and_each_sub_graph_recursively(graph, graph_clean_up_tf) # Fusing linear operation to Convolution for_graph_and_each_sub_graph_recursively(graph, fuse_linear_ops) for_graph_and_each_sub_graph_recursively(graph, graph_clean_up_tf) if not argv.disable_gfusing: grouped_convolutions_fusing(graph) graph_clean_up_tf(graph) if not argv.disable_fusing: fuse_linear_ops(graph) graph_clean_up_tf(graph) # Converting Mul->Add to ScaleShift node for_graph_and_each_sub_graph_recursively(graph, convert_muladd_to_scaleshift_or_power) for_graph_and_each_sub_graph_recursively(graph, graph_clean_up_tf) for_graph_and_each_sub_graph_recursively(graph, convert_mul_add_to_power) # Need to eliminate dead nodes before doing update_fully_connected_shapes # because update_fully_connected_shapes does partial inference and dead # nodes will lead to sporadic failures. for_graph_and_each_sub_graph_recursively(graph, graph_clean_up_tf) for_graph_and_each_sub_graph_recursively(graph, update_fully_connected_shapes) for_graph_and_each_sub_graph_recursively(graph, convert_mul_eltwise_to_leaky_relu) graph_clean_up_tf(graph) for_graph_and_each_sub_graph_recursively(graph, graph_clean_up_tf) for_graph_and_each_sub_graph_recursively(graph, fuse_pad) for_graph_and_each_sub_graph_recursively(graph, graph_clean_up_tf) for_graph_and_each_sub_graph_recursively(graph, convert_reshape) for_graph_and_each_sub_graph_recursively(graph, convert_squeeze) for_graph_and_each_sub_graph_recursively(graph, convert_add_to_scaleshift) # scale = 1 for_graph_and_each_sub_graph_recursively(graph, convert_mul_to_scaleshift) # biases = 0 if argv.reverse_input_channels: reverse_input_channels(graph) if argv.move_to_preprocess: move_scaleshift_to_preprocess(graph) graph_clean_up_tf(graph) for_graph_and_each_sub_graph_recursively(graph, fuse_sequence_of_reshapes) pattern = EltwiseInputNormalize() pattern.find_and_replace_pattern(graph) conv_flatten_concat(graph) for_graph_and_each_sub_graph_recursively(graph, apply_nhwc_to_nchw_permutation) for_graph_and_each_sub_graph_recursively(graph, merge_nodes_permutations) for_graph_and_each_sub_graph_recursively(graph, permute_data_nodes_attrs) for_graph_and_each_sub_graph_recursively(graph, permute_op_nodes_attrs) for_graph_and_each_sub_graph_recursively(graph, repack_fully_connected_weights_nhwc_to_nchw) for_graph_and_each_sub_graph_recursively(graph, transpose_fully_connected_weights) for_graph_and_each_sub_graph_recursively(graph, graph_clean_up_tf) if argv.offload_unsupported_operations_to_tf: unsupported_ops_to_offload_to_tf = find_unsupported_ops(graph) if len(unsupported_ops_to_offload_to_tf) == 0: log.info('All operations are supported! Exit from the loop.') if not need_to_repeat_conversion(graph): break else: print('After {} iteration there are {} unsupported ops'.format(cur_iteration + 1, len(unsupported_ops_to_offload_to_tf))) else: if not need_to_repeat_conversion(graph): break graph = graph_copy cur_iteration += 1 class_registration.apply_replacements(graph, class_registration.ClassType.BACK_REPLACER) prepare_emit_ir(graph=graph, data_type=argv.data_type, output_dir=output_dir, output_model_name=output_model_name, meta_info=meta_info) return 0
def driver_R5(onnx_modelproto_bytes, precision: str, output_model_name: str, outputs: list, output_dir: str, scale: float, user_shapes: [None, list, np.array] = None, mean_scale_values: [dict, list] = ()): try: model_proto = onnx.load_from_string(bytes(onnx_modelproto_bytes)) except Exception as e: print("[python] onnx exception: ", str(e)) model_graph = model_proto.graph # pylint: disable=no-member log.debug("Number of nodes in graph_def: {}".format(len(model_graph.node))) log.debug( "Number of all input ports (not true inputs) in graph_def: {}".format( len(model_graph.input))) log.debug("Number of initializers in graph_def: {}".format( len(model_graph.initializer))) log.debug("Number of real inputs in graph_def: {}".format( len(model_graph.input) - len(model_graph.initializer))) update_extractors_with_extensions(onnx_op_extractors) try: graph = protobuf2nx(model_proto) log.debug("Number of nodes in NX graph: {}".format( graph.number_of_nodes())) graph.__setattr__( 'name', output_model_name if output_model_name else model_proto.graph.name) # pylint: disable=no-member graph.graph['layout'] = 'NCHW' graph.graph['fw'] = 'onnx' graph.graph[ 'feature_dim'] = 1 if graph.graph['layout'] == 'NCHW' else 3 graph.graph['ir_version'] = 4 extract_node_attrs(graph, lambda node: (True, common_onnx_fields(node))) except Exception as e: raise Error( 'Cannot pre-process ONNX graph after reading from model file "{}". ' 'File is corrupt or has unsupported format. Details: {}. ' + refer_to_faq_msg(44), model_file_name, str(e)) from e check_empty_graph( graph, 'protobuf2nx. It may happen due to problems with loaded model') packed_user_shapes, packed_outputs, _ = user_data_repack( graph, user_shapes, outputs, None) output_op_nodes = add_output_ops(graph, packed_outputs) input_op_nodes = add_input_ops(graph, packed_user_shapes, True) graph_clean_up(graph) check_empty_graph(graph, 'add_output_ops and add_input_ops') extract_node_attrs( graph, lambda node: onnx_op_extractor( node, check_for_duplicates(onnx_op_extractors))) class_registration.apply_replacements( graph, class_registration.ClassType.FRONT_REPLACER) create_tensor_nodes(graph) graph_clean_up(graph) override_placeholder_shapes(graph, packed_user_shapes) graph_clean_up(graph) remove_op_nodes(graph, {'op': 'Identity'}) graph_clean_up(graph) remove_output_ops(graph) partial_infer(graph) graph_clean_up(graph) check_empty_graph(graph, 'partial_infer') input_op_nodes = add_input_ops(graph, packed_user_shapes, False) graph_clean_up(graph) check_empty_graph(graph, 'add_input_ops') scale_input(graph, scale) add_mean_scale_values(graph, mean_scale_values) convert_dilated_convolution(graph) graph_clean_up(graph) graph_clean_up(graph) remove_op_nodes(graph, {'op': 'Identity'}) remove_useless_split(graph) class_registration.apply_replacements( graph, class_registration.ClassType.MIDDLE_REPLACER) convert_gemm_to_fully_connected(graph) NormalizeFullyConnected().find_and_replace_pattern(graph) fuse_pad(graph) graph_clean_up(graph) convert_batch_norm(graph) graph_clean_up(graph) convert_scale_shift_to_mul_add(graph) graph_clean_up(graph) fuse_mul_add_sequence(graph) graph_clean_up(graph) fuse_linear_ops(graph) graph_clean_up(graph) grouped_convolutions_fusing(graph) graph_clean_up(graph) fuse_linear_ops(graph) graph_clean_up(graph) convert_muladd_to_scaleshift_or_power(graph) graph_clean_up(graph) convert_mul_add_to_power(graph) graph_clean_up(graph) convert_reshape(graph) convert_add_to_scaleshift(graph) # scale = 1 convert_mul_to_scaleshift(graph) # biases = 0 fuse_pad(graph) graph_clean_up(graph) fuse_sequence_of_reshapes(graph) graph_clean_up(graph) pattern = EltwiseInputNormalize() pattern.find_and_replace_pattern(graph) merge_nodes_permutations(graph) permute_data_nodes_attrs(graph) permute_op_nodes_attrs(graph) class_registration.apply_replacements( graph, class_registration.ClassType.BACK_REPLACER) weights, xml_string = prepare_emit_ir(graph=graph, data_type=precision, output_dir=output_dir, output_model_name=output_model_name, meta_info={'unset': []}) return weights, xml_string
def driver(argv: argparse.Namespace, proto_file_name: str, model_file_name: str, output_model_name: str, outputs: list, output_dir: str, scale: float, user_shapes: [None, list, np.array] = None, mean_scale_values: [dict, list] = (), mean_file: str = "", mean_file_offsets: tuple = None, custom_layers_mapping_path: str = None): meta_info = get_meta_info(argv) FusePermutesSequence.enabled = False proto, model = loader.load_caffe_proto_model(proto_file_name, model_file_name) update_extractors_with_extensions( caffe_type_extractors, argv.disable_omitting_optional if hasattr( argv, 'disable_omitting_optional') else False, argv.disable_flattening_optional_params if hasattr( argv, 'disable_flattening_optional_params') else False) try: graph, original_shapes = loader.caffe_pb_to_nx(proto, model) except ValueError as e: raise Error( 'Invalid prototxt file: value error {}. ' + refer_to_faq_msg(11), str(e)) from e log.debug("After caffe_pb_to_nx") print_graph_stat(graph) check_empty_graph(graph, 'load_caffe_proto_model') graph.__setattr__('proto_path', proto_file_name) graph.__setattr__('caffemodel_path', model_file_name) graph.__setattr__('name', getattr(proto, 'name', None) or output_model_name) graph.graph['layout'] = 'NCHW' graph.graph['cmd_params'] = argv graph.graph['fw'] = 'caffe' graph.graph['ir_version'] = 2 if argv.generate_deprecated_IR_V2 else 4 extract_node_attrs(graph, lambda node: (True, common_caffe_fields(node))) log.debug("After adding specific nodes for outputs") print_graph_stat(graph) custom_layers_map = custom_layers_mapping.load_layers_xml( custom_layers_mapping_path) custom_layers_mapping.update_extractors( caffe_type_extractors, custom_layers_map, argv.disable_omitting_optional if hasattr( argv, 'disable_omitting_optional') else False, argv.enable_flattening_nested_params if hasattr( argv, 'enable_flattening_nested_params') else False) extract_node_attrs( graph, lambda node: caffe_extractor( node, check_for_duplicates(caffe_type_extractors))) log.debug("After extract_node_attr") print_graph_stat(graph) packed_user_shapes, packed_outputs, freeze_placeholder = user_data_repack( graph, user_shapes, outputs, argv.freeze_placeholder_with_value) if argv.freeze_placeholder_with_value is not None: FreezePlaceholderValue.enabled = True FreezePlaceholderValue.replacement_dict = freeze_placeholder class_registration.update_registration([FrontReplacementSubgraph]) output_op_nodes = add_output_ops(graph, packed_outputs) input_op_nodes = add_input_ops(graph, packed_user_shapes, True) override_placeholder_shapes(graph, packed_user_shapes) override_batch(graph, argv.batch) graph_clean_up(graph) check_empty_graph(graph, 'add_output_ops and add_input_ops') class_registration.apply_replacements( graph, class_registration.ClassType.FRONT_REPLACER) graph = create_tensor_nodes(graph) log.debug("After create_tensor_nodes") print_graph_stat(graph) remove_op_nodes(graph, {'op': 'Identity'}) remove_output_ops(graph) graph_clean_up(graph) log.debug("After removing specific nodes for output") print_graph_stat(graph) # you need to pass required network outputs here # but we don't have a way yet, so just passing all discovered sinks mark_outputs(graph) graph_clean_up(graph) log.debug("After graph_cleanup") print_graph_stat(graph) graph = partial_infer(graph) log.debug("After partial_infer") print_graph_stat(graph) check_empty_graph(graph, 'partial_infer') duplicate_shared_weights(graph) input_op_nodes = add_input_ops(graph, packed_user_shapes, False) graph_clean_up(graph) check_empty_graph(graph, 'add_input_ops') scale_input(graph, scale) add_mean_scale_values(graph, mean_scale_values) log.debug("Split multi input convolutions") convert_multi_input_conv(graph) graph_clean_up(graph) log.debug("After graph_cleanup") print_graph_stat(graph) remove_op_nodes(graph, {'op': 'Dropout'}) remove_op_nodes(graph, {'phase': 0}) graph_clean_up(graph) class_registration.apply_replacements( graph, class_registration.ClassType.MIDDLE_REPLACER) mean_to_avgpool(graph) # Mark nodes with attr 'can_be_fused': False to disable fusing for specified nodes mark_unfused_nodes(graph, argv.finegrain_fusing) #need this pass even without fusing to convert scale with 2 inputs convert_scale_shift_to_mul_add(graph) graph_clean_up(graph) if not argv.disable_fusing: convert_bn_to_mul_add(graph) graph_clean_up(graph) fuse_mul_add_sequence(graph) graph_clean_up(graph) fuse_linear_ops(graph) graph_clean_up(graph) if not argv.disable_resnet_optimization: stride_optimization(graph) convert_muladd_to_scaleshift_or_power(graph) convert_matmul_to_fully_connected(graph) batch_norm_fuse(graph) convert_mul_add_to_power(graph) convert_add_to_scaleshift(graph) # scale = 1 convert_mul_to_scaleshift(graph) # biases = 0 graph_clean_up(graph) log.debug("After graph_cleanup") print_graph_stat(graph) if argv.reverse_input_channels: reverse_input_channels(graph) if argv.move_to_preprocess: move_scaleshift_to_preprocess(graph) graph_clean_up(graph) fuse_sequence_of_reshapes(graph) input_names = find_inputs(graph) mf = [] try: if mean_file and len(original_shapes) == 1: mf = loader.parse_mean(mean_file, original_shapes[input_names[0]], mean_file_offsets) elif mean_file: raise Error( 'Mean file for topologies with multiple inputs is not supported. ' + refer_to_faq_msg(9)) except ValueError as e: raise Error( 'Cannot load or process mean file: value error {}. ' + refer_to_faq_msg(10), str(e)) from e class_registration.apply_replacements( graph, class_registration.ClassType.BACK_REPLACER) prepare_emit_ir(graph=graph, data_type=argv.data_type, output_dir=output_dir, output_model_name=output_model_name, mean_data=mf, input_names=input_names, meta_info=meta_info) return 0
def driver(argv: argparse.Namespace, input_model: str, output_model_name: str, outputs: list, output_dir: str, scale: float, placeholder_shapes: [None, list, np.array] = None, mean_scale_values: [dict, list] = ()): meta_info = get_meta_info(argv) try: model_nodes, model_params, model_name, iteration_number = load_symbol_def(input_model, argv.input_symbol, argv.input, argv.nd_prefix_name, argv.pretrained_model_name, argv.legacy_mxnet_model) except (ValueError, mxnet.base.MXNetError) as e: raise FrameworkError( 'The following error happened while loading mxnet model {}: {}. ' + refer_to_faq_msg(53), input_model, str(e) ) from e if argv.nd_prefix_name and argv.pretrained_model_name and argv.save_params_from_nd: save_params_file(model_name, model_params._arg_params, model_params._aux_params, iteration_number) update_extractors_with_extensions(mxnet_op_extractors) graph = symbol2nx(model_nodes, model_params, argv.input) check_empty_graph(graph, 'symbol2nx. It may happen due to problems with loaded model') graph.__setattr__('name', output_model_name) graph.graph['layout'] = 'NCHW' graph.graph['cmd_params'] = argv graph.graph['fw'] = 'mxnet' graph.graph['feature_dim'] = 1 if graph.graph['layout'] == 'NCHW' else 3 graph.graph['ir_version'] = 2 if argv.generate_deprecated_IR_V2 else 4 graph = extract_node_attrs(graph, mxnet_op_extractor) check_softmax_node_inputs(graph) user_shapes, packed_outputs, _ = user_data_repack(graph, placeholder_shapes, outputs, None) output_op_nodes = add_output_ops(graph, packed_outputs) input_op_nodes = add_input_ops(graph, user_shapes, True) try: override_placeholder_shapes(graph, user_shapes, argv.batch) except ValueError as err: raise Error( 'The following error happened while processing input shapes: {}. ' + refer_to_faq_msg(54), str(err) ) from err check_empty_graph(graph, 'add_output_ops and add_input_ops') class_registration.apply_replacements(graph, class_registration.ClassType.FRONT_REPLACER) add_input_data_to_prior_boxes(graph, argv.input) graph = create_tensor_nodes(graph) graph_clean_up(graph) remove_output_ops(graph) mark_outputs(graph) remove_output_ops(graph) graph_clean_up(graph) log.debug("After removing specific nodes for output") print_graph_stat(graph) graph = partial_infer(graph) graph_clean_up(graph) check_empty_graph(graph, 'partial_infer') duplicate_shared_weights(graph) scale_input(graph, scale) add_mean_scale_values(graph, mean_scale_values) remove_op_nodes(graph, {'identity': True}) graph_clean_up(graph) class_registration.apply_replacements(graph, class_registration.ClassType.MIDDLE_REPLACER) fuse_pad(graph) # Mark nodes with attr 'can_be_fused': False to disable fusing for specified nodes mark_unfused_nodes(graph, argv.finegrain_fusing) # Converting FusedBatchNorm layer to Mul->Add->Mul->Add sequence convert_batch_norm(graph) graph_clean_up(graph) if not argv.disable_fusing: # Converting ScaleShift layer to Mul->Add convert_scale_shift_to_mul_add(graph) graph_clean_up(graph) # Fusing the sequences of Mul/Add operations fuse_mul_add_sequence(graph) graph_clean_up(graph) # Fusing linear operation to Convolution fuse_linear_ops(graph) graph_clean_up(graph) if not argv.disable_resnet_optimization: stride_optimization(graph) fuse_pad(graph) # Converting Mul->Add to ScaleShift node convert_muladd_to_scaleshift_or_power(graph) graph_clean_up(graph) convert_mul_add_to_power(graph) convert_add_to_scaleshift(graph) # scale = 1 convert_mul_to_scaleshift(graph) # biases = 0 if argv.reverse_input_channels: reverse_input_channels(graph) if argv.move_to_preprocess: move_scaleshift_to_preprocess(graph) graph_clean_up(graph) pattern = EltwiseInputNormalize() pattern.find_and_replace_pattern(graph) class_registration.apply_replacements(graph, class_registration.ClassType.BACK_REPLACER) prepare_emit_ir(graph=graph, data_type=argv.data_type, output_dir=output_dir, output_model_name=output_model_name, meta_info=meta_info) return 0
def driver(argv, input_model, output_model_name, outputs, output_dir, scale, placeholder_shapes=None, mean_scale_values=()): meta_info = get_meta_info(argv) EltwiseChecker.enabled = False try: graph, input_shapes = load_kaldi_model(input_model) except Exception as e: raise Error('Model Optimizer is not able to read Kaldi model {}. '. format(input_model) + refer_to_faq_msg(91)) from e check_empty_graph(graph, 'load_kaldi_nnet_model') graph.graph['cmd_params'] = argv graph.graph['fw'] = 'kaldi' graph.graph['ir_version'] = 2 if argv.generate_deprecated_IR_V2 else 4 update_extractors_with_extensions(kaldi_type_extractors) extract_node_attrs(graph, lambda node: kaldi_extractor(node)) class_registration.apply_replacements( graph, class_registration.ClassType.FRONT_REPLACER) output_op_nodes = add_output_ops( graph, outputs) # TODO pass real outputs instead of None log.debug("After adding specific nodes for outputs") print_graph_stat(graph) check_empty_graph(graph, 'add_output_ops') create_tensor_nodes(graph) graph_clean_up(graph) log.debug("After removing specific nodes for output") print_graph_stat(graph) override_placeholder_shapes(graph, placeholder_shapes) override_batch(graph, argv.batch) graph_clean_up(graph) log.debug("After setting input shapes") print_graph_stat(graph) graph_clean_up(graph) remove_output_ops(graph) log.debug("After removing specific nodes for output") print_graph_stat(graph) # You need to pass required network outputs here # but we don't have a way yet, so just passing all discovered sinks mark_outputs(graph) graph_clean_up(graph) log.debug("After graph_cleanup") print_graph_stat(graph) graph = partial_infer(graph) # The order is intentional, firstly eliminate repeated, then remove redundant FuseRepeatedReshapes().find_and_replace_pattern(graph) EliminateRedundantReshape().find_and_replace_pattern(graph) check_empty_graph(graph, 'partial_infer') if argv.counts: try: counts = read_counts_file(argv.counts) except Exception as e: raise Error('Model Optimizer is not able to read counts file {}'. format(argv.counts) + refer_to_faq_msg(92)) from e apply_biases_to_last_layer(graph, counts) if argv.remove_output_softmax: RemoveLastSoftMaxPattern().find_and_replace_pattern(graph) graph_clean_up(graph) log.debug("After removing softmax") print_graph_stat(graph) # Intentionally after all transformations KaldiRemoveMemoryOutputBackReplacementPattern().find_and_replace_pattern( graph) prepare_emit_ir(graph, argv.data_type, output_dir, output_model_name, meta_info=meta_info) return 0
def driver(argv: argparse.Namespace, model_file_name: str, output_model_name: str, outputs: list, output_dir: str, scale: float, user_shapes: [None, list, np.array] = None, mean_scale_values: [dict, list] = ()): meta_info = get_meta_info(argv) model_proto = load_onnx_model(model_file_name) model_graph = model_proto.graph # pylint: disable=no-member #print(model_graph) #assert len(model_graph) == 1, "An ONNX model contains more than 1 graph: unsupported" log.debug("Number of nodes in graph_def: {}".format(len(model_graph.node))) log.debug( "Number of all input ports (not true inputs) in graph_def: {}".format( len(model_graph.input))) log.debug("Number of initializers in graph_def: {}".format( len(model_graph.initializer))) log.debug("Number of real inputs in graph_def: {}".format( len(model_graph.input) - len(model_graph.initializer))) update_extractors_with_extensions(onnx_op_extractors) try: graph = protobuf2nx(model_proto) log.debug("Number of nodes in NX graph: {}".format( graph.number_of_nodes())) graph.__setattr__( 'name', output_model_name if output_model_name else model_proto.graph.name) # pylint: disable=no-member graph.graph['layout'] = 'NCHW' graph.graph['cmd_params'] = argv graph.graph['fw'] = 'onnx' graph.graph[ 'feature_dim'] = 1 if graph.graph['layout'] == 'NCHW' else 3 graph.graph['ir_version'] = 2 if argv.generate_deprecated_IR_V2 else 4 # extract basic attributes earlier to enable some passes that relies on them before full attribute # extractor is called extract_node_attrs(graph, lambda node: (True, common_onnx_fields(node))) except Exception as e: raise Error( 'Cannot pre-process ONNX graph after reading from model file "{}". ' \ 'File is corrupt or has unsupported format. Details: {}. ' + refer_to_faq_msg(44), model_file_name, str(e) ) from e check_empty_graph( graph, 'protobuf2nx. It may happen due to problems with loaded model') packed_user_shapes, packed_outputs, _ = user_data_repack( graph, user_shapes, outputs, None) output_op_nodes = add_output_ops(graph, packed_outputs) input_op_nodes = add_input_ops(graph, packed_user_shapes, True) # this call of 'graph_clean_up' removes child nodes of outputs which is useful when custom output is specified graph_clean_up(graph) check_empty_graph(graph, 'add_output_ops and add_input_ops') extract_node_attrs( graph, lambda node: onnx_op_extractor( node, check_for_duplicates(onnx_op_extractors))) class_registration.apply_replacements( graph, class_registration.ClassType.FRONT_REPLACER) create_tensor_nodes(graph) graph_clean_up(graph) override_placeholder_shapes(graph, packed_user_shapes) override_batch(graph, argv.batch) graph_clean_up(graph) remove_op_nodes(graph, {'op': 'Identity'}) graph_clean_up(graph) remove_output_ops(graph) partial_infer(graph) graph_clean_up(graph) check_empty_graph(graph, 'partial_infer') input_op_nodes = add_input_ops(graph, packed_user_shapes, False) graph_clean_up(graph) check_empty_graph(graph, 'add_input_ops') #change_placeholders_types_to_FP32(graph) scale_input(graph, scale) add_mean_scale_values(graph, mean_scale_values) convert_dilated_convolution(graph) graph_clean_up(graph) graph_clean_up(graph) remove_op_nodes(graph, {'op': 'Identity'}) remove_useless_split(graph) class_registration.apply_replacements( graph, class_registration.ClassType.MIDDLE_REPLACER) convert_gemm_to_fully_connected(graph) NormalizeFullyConnected().find_and_replace_pattern(graph) fuse_pad(graph) graph_clean_up(graph) # Mark nodes with attr 'can_be_fused': False to disable fusing for specified nodes mark_unfused_nodes(graph, argv.finegrain_fusing) # Converting FusedBatchNorm layer to Mul->Add->Mul->Add sequence # IE doesn't support BN with 4 inputs, so we have to split it to two ScaleShift convert_batch_norm(graph) graph_clean_up(graph) if not argv.disable_fusing: # Converting ScaleShift layer to Mul->Add convert_scale_shift_to_mul_add(graph) graph_clean_up(graph) # Fusing the sequences of Mul/Add operations fuse_mul_add_sequence(graph) graph_clean_up(graph) # Fusing linear operation to Convolution fuse_linear_ops(graph) graph_clean_up(graph) if not argv.disable_gfusing: grouped_convolutions_fusing(graph) graph_clean_up(graph) if not argv.disable_fusing: fuse_linear_ops(graph) graph_clean_up(graph) convert_muladd_to_scaleshift_or_power(graph) graph_clean_up(graph) convert_mul_add_to_power(graph) graph_clean_up(graph) convert_reshape(graph) convert_add_to_scaleshift(graph) # scale = 1 convert_mul_to_scaleshift(graph) # biases = 0 fuse_pad(graph) graph_clean_up(graph) if argv.reverse_input_channels: reverse_input_channels(graph) if argv.move_to_preprocess: move_scaleshift_to_preprocess(graph) graph_clean_up(graph) fuse_sequence_of_reshapes(graph) graph_clean_up(graph) pattern = EltwiseInputNormalize() pattern.find_and_replace_pattern(graph) merge_nodes_permutations(graph) permute_data_nodes_attrs(graph) permute_op_nodes_attrs(graph) class_registration.apply_replacements( graph, class_registration.ClassType.BACK_REPLACER) prepare_emit_ir(graph=graph, data_type=argv.data_type, output_dir=output_dir, output_model_name=output_model_name, meta_info=meta_info) return 0