def convert_muladd_to_scaleshift(graph: Graph): if hasattr(graph, 'graph') and 'cmd_params' in graph.graph and graph.graph['cmd_params'].generate_experimental_IR_V10: return # TODO nGraph remove BEGIN apply_pattern( graph, nodes=[ ('input', dict(kind='data')), ('weights', dict(kind='data')), ('bias', dict(kind='data')), ('mout', dict(kind='data')), ('output', dict(kind='data')), ('mul', dict(kind='op', op='Mul')), ('add', dict(kind='op', op='Add')) ], edges=[ ('weights', 'mul'), ('input', 'mul'), ('mul', 'mout'), ('mout', 'add'), ('bias', 'add'), ('add', 'output'), ], action=muladd_to_scaleshift_action )
def remove_op_nodes(graph: nx.MultiDiGraph, attrs: dict): op_attrs = {'kind': 'op'} op_attrs.update(attrs) apply_pattern(graph, nodes=[('identity', op_attrs)], edges=[], action=remove_identity_action)
def analyze(self, graph: Graph): pattern_instance_counter.counter = 0 apply_pattern(graph, **YOLO_PATTERN, action=pattern_instance_counter) flavor = None if pattern_instance_counter.counter > 0: if pattern_instance_counter.counter == 22: flavor = 'YOLOV2Full' elif pattern_instance_counter.counter == 8: flavor = 'YOLOV2Tiny' if flavor is not None: message = "Your model looks like YOLOv1 or YOLOv2 Model.\n" \ "To generate the IR, provide TensorFlow YOLOv1 or YOLOv2 Model to the Model Optimizer with the following parameters:\n" \ "\t--input_model <path_to_model>/<model_name>.pb\n" \ "\t--batch 1\n" \ "\t--tensorflow_use_custom_operations_config <OPENVINO_INSTALL_DIR>/deployment_tools/model_optimizer/extensions/front/tf/<yolo_config>.json\n" \ "All detailed information about conversion of this model can be fount at\n" \ "https://docs.openvinotoolkit.org/latest/_docs_MO_DG_prepare_model_convert_model_tf_specific_Convert_YOLO_From_Tensorflow.html" return { 'model_type': { 'YOLO': get_YOLO_params_by_flavor(flavor) } }, message else: return None, None
def move_scaleshift_to_preprocess(graph: Graph): """ This function finds scaleshift layer after input layer and if it has weights with ones, it deletes scaleshift layer and creates graph dict attribute : {'input':np.array(...), 'input2': ... } """ apply_pattern(graph, nodes=[ ('weights', dict(kind='data')), ('biases', dict(kind='data')), ('input_output', dict(kind='data')), ('scsh_output', dict(kind='data')), ('input_op', dict(kind='op', type='Parameter')), ('scale_shift', dict(kind='op', type='ScaleShift')), ], edges=[ ('input_op', 'input_output'), ('scale_shift', 'scsh_output'), ('input_output', 'scale_shift', { 'in': 0 }), ('weights', 'scale_shift', { 'in': 1 }), ('biases', 'scale_shift', { 'in': 2 }), ], action=move_scaleshift_to_preprocess_action)
def convert_muladd_to_scaleshift_or_power(graph: nx.MultiDiGraph): apply_pattern(graph, nodes=[('input', dict(kind='data')), ('weights', dict(kind='data')), ('bias', dict(kind='data')), ('mout', dict(kind='data')), ('output', dict(kind='data')), ('mul', dict(kind='op', op='Mul')), ('add', dict(kind='op', op='Add'))], edges=[ ('weights', 'mul', { 'in': 0 }), ('input', 'mul', { 'in': 1 }), ('mul', 'mout'), ('mout', 'add', { 'in': 0 }), ('bias', 'add', { 'in': 1 }), ('add', 'output'), ], action=muladd_to_scaleshift_action)
def convert_nasnet(graph: nx.MultiDiGraph): apply_pattern(graph, nodes=[ ('input', dict(kind='data')), ('pad_op', dict(kind='op', op='Pad')), ('pad_out', dict(kind='data')), ('begin', dict(kind='data')), ('end', dict(kind='data')), ('stride', dict(kind='data')), ('sslice', dict(kind='op', op='StridedSlice')), ('sslice_out', dict(kind='data')), ('avg_pool', dict(kind='op', op='AvgPool')), ('output', dict(kind='data')), ], edges=[('input', 'pad_op', { 'in': 0 }), ('pad_op', 'pad_out'), ('begin', 'sslice', { 'in': 1 }), ('end', 'sslice', { 'in': 2 }), ('stride', 'sslice', { 'in': 3 }), ('pad_out', 'sslice', { 'in': 0 }), ('sslice', 'sslice_out'), ('sslice_out', 'avg_pool', { 'in': 0 }), ('avg_pool', 'output')], action=convert_nasnet_action) return graph
def rename_fqs_in_the_end(self, graph: Graph): def change_names(_, match): fq_node = match['fq'] input_node = get_node_input(fq_node, 0) new_fq_name = copy(input_node.name) if 'orig_node_name' in input_node: new_fq_name = copy(input_node['orig_node_name']) input_node_outputs = get_all_node_outputs(input_node) if len(input_node_outputs) > 1 and all([op.type == 'FakeQuantize' for op in input_node_outputs]): new_fq_name += '.{}'.format(fq_node.in_port(0).get_source().idx) fq_node['orig_fq_name'] = copy(fq_node.name) rename_node(fq_node, new_fq_name) if 'orig_node_name' not in input_node: input_node['orig_node_name'] = copy(input_node.name) rename_node(input_node, f'{input_node.name}/pre_fq_input') pattern = get_fq_result_pattern() apply_pattern( graph, nodes=pattern['nodes'], edges=pattern['edges'], action=change_names )
def batch_norm_fuse(graph: Graph): apply_pattern( graph, nodes=[('kernel', dict(kind='data')), ('conv', dict(kind='op', op='Conv2D')), ('conv_output', dict(kind='data')), ('norm', dict(kind='data')), ('mul', dict(kind='op', op='Mul')), ('mul_output', dict(kind='data'))], edges=[ ('kernel', 'conv', { 'in': 1 }), ('conv', 'conv_output'), ( 'conv_output', 'mul', { 'in': 0 } ), # TODO get rid of explicit input port number, mul is a commutative op ( 'norm', 'mul', { 'in': 1 } ), # TODO get rig of explicit input port number, mul is a commutative op ('mul', 'mul_output') ], action=batch_norm_fuse_action) return graph
def conv_flatten_concat(graph: Graph): apply_pattern(graph, nodes=[ ('conv', dict(kind='op', type='Convolution')), ('conv_data', dict(kind='data')), ('reshape', dict(kind='op', type='Reshape')), ('reshape_data', dict(kind='data')), ], edges=[ ('conv', 'conv_data'), ('conv_data', 'reshape'), ('reshape', 'reshape_data'), ], action=conv_flatten_concat_action) apply_pattern(graph, nodes=[ ('real_conv', dict(kind='op', type='Convolution')), ('real_conv_data', dict(kind='data')), ('conv', dict(kind='op', type='ReLU')), ('conv_data', dict(kind='data')), ('reshape', dict(kind='op', type='Reshape')), ('reshape_data', dict(kind='data')), ], edges=[ ('real_conv', 'real_conv_data'), ('real_conv_data', 'conv'), ('conv', 'conv_data'), ('conv_data', 'reshape'), ('reshape', 'reshape_data'), ], action=conv_flatten_concat_action)
def l2_norm_to_norm(graph: Graph): apply_pattern(graph, nodes=[ ('input', dict(kind='data')), ('l2_normalize', dict(kind='op', op='Mul')), ('l2_normalize_data', dict(kind='data')), ('maximum', dict(kind='op', op='Maximum')), ('maximum_data', dict(kind='data')), ('maximum_y_data', dict(kind='data')), ('rsqrt', dict(kind='op', op='Rsqrt')), ('rsqrt_data', dict(kind='data')), ('square', dict(kind='op', op='Square')), ('square_data', dict(kind='data')), ('sum', dict(kind='op', op='Reduce', reduce_type='sum')), ('sum_data', dict(kind='data')), ], edges=[ ('input', 'square'), ('square', 'square_data'), ('square_data', 'sum'), ('sum', 'sum_data'), ('maximum_y_data', 'maximum'), ('sum_data', 'maximum'), ('maximum', 'maximum_data'), ('maximum_data', 'rsqrt'), ('rsqrt', 'rsqrt_data'), ('rsqrt_data', 'l2_normalize'), ('input', 'l2_normalize'), ('l2_normalize', 'l2_normalize_data'), ], action=l2_norm_to_norm_action)
def convert_mul_eltwise_to_leaky_relu(graph: Graph): """ This function finds next subgraph: -->Data-------->Maximum-->Data `-->Mul---` and replace with ReLU with negative slope """ apply_pattern( graph, nodes=[ ('data', dict(kind='data')), ('mul_data', dict(kind='data')), ('max_op', dict(kind='op', type='Maximum')), ('const_op', dict(kind='op', type='Const')), ('const_data', dict(kind='data')), ('mul_op', dict(kind='op', type='Multiply')), ], edges=[ ('data', 'mul_op'), ('mul_op', 'mul_data'), ('data', 'max_op'), ('mul_data', 'max_op'), ('const_op', 'const_data'), ('const_data', 'mul_op') ], action=_convert_to_leaky_relu_action ) return graph
def remove_useless_split(graph: nx.MultiDiGraph): apply_pattern(graph, nodes=[('split', { 'kind': 'op', 'op': 'Split', 'num_split': 1 })], edges=[], action=remove_useless_split_action)
def convert_matmul_to_fully_connected(graph: Graph): apply_pattern( graph, nodes=[ ('matmul', dict(kind='op', op='MatMul')), ('output', dict(kind='data'))], edges=[('matmul', 'output')], action=matmul_to_fully_connected_action )
def convert_reshape(graph: Graph): apply_pattern(graph, nodes=[('shape', dict(kind='data')), ('reshape', dict(kind='op', op='Reshape')), ('output', dict(kind='data'))], edges=[('shape', 'reshape', { 'in': 1 }), ('reshape', 'output')], action=reshape_squeeze_transform)
def fuse_pad(graph: Graph): for op_type in ['Convolution', 'Pooling', 'Deconvolution']: apply_pattern(graph, nodes=[('pad_op', dict(kind='op', op='Pad')), ('pad_output', dict(kind='data')), ('op', dict(kind='op', type=op_type))], edges=[('pad_op', 'pad_output'), ('pad_output', 'op', { 'in': 0 })], action=pad_op_transform)
def mark_ignored_blocks_(patterns): for types_list in patterns: for pattern in patterns[types_list]: if isinstance(pattern, tuple): pattern, check_pattern_fn = pattern mark_fn = partial(self.mark_block_nodes, check_pattern_fn) else: mark_fn = partial(self.mark_block_nodes, None) apply_pattern(graph, nodes=pattern['nodes'], edges=pattern['edges'], action=mark_fn)
def convert_dilated_convolution(graph: nx.MultiDiGraph): for op in ['Conv2D', 'DepthwiseConv2dNative', 'Conv3D']: apply_pattern(graph, nodes=[('conv', dict(kind='op', op=op)), ('space_to_batch', dict(kind='op', op='SpaceToBatchND')), ('batch_to_space', dict(kind='op', op='BatchToSpaceND')), ('input', dict(kind='data')), ('output', dict(kind='data')), ('conv_output', dict(kind='data')), ('stb_output', dict(kind='data')), ('stb_bs', dict(kind='data')), ('stb_pad', dict(kind='data')), ('bts_bs', dict(kind='data')), ('bts_crop', dict(kind='data'))], edges=[ ('input', 'space_to_batch', { 'in': 0 }), ('stb_bs', 'space_to_batch', { 'in': 1 }), ('stb_pad', 'space_to_batch', { 'in': 2 }), ('space_to_batch', 'stb_output', { 'out': 0 }), ('stb_output', 'conv', { 'in': 0 }), ('conv', 'conv_output', { 'out': 0 }), ('conv_output', 'batch_to_space', { 'in': 0 }), ('bts_bs', 'batch_to_space', { 'in': 1 }), ('bts_crop', 'batch_to_space', { 'in': 2 }), ('batch_to_space', 'output', { 'out': 0 }), ], action=dilated_convolution_action)
def mean_to_avgpool(graph: nx.MultiDiGraph): """ Translate Mean as a average pooling with kernel size equals to reduced dimensions and with no padding. """ apply_pattern(graph, nodes=[('input', dict(kind='data')), ('axis', dict(kind='data')), ('mean', dict(kind='op', op='Mean'))], edges=[('input', 'mean', { 'in': 0 }), ('axis', 'mean', { 'in': 1 })], action=mean_to_avgpool_action) return graph
def analyze(self, graph: Graph): pattern_instance_counter.counter = 0 apply_pattern(graph, **YOLO_PATTERN, action=pattern_instance_counter) flavor = None if pattern_instance_counter.counter > 0: if pattern_instance_counter.counter == 22: flavor = 'YOLOV2Full' elif pattern_instance_counter.counter == 8: flavor = 'YOLOV2Tiny' if flavor is not None: return {'model_type': {'YOLO': get_YOLO_params_by_flavor(flavor)}} else: return None
def rename_fqs_in_the_end(self, graph: Graph): def change_names(_, match): fq_node = match['fq'] input_node = get_node_input(fq_node, 0) fq_node['orig_fq_name'] = copy(fq_node.name) fq_node.name = copy(input_node.name) input_node['orig_node_name'] = copy(input_node.name) input_node.name = '{original_name}/pre_fq_input'.format( original_name=input_node.name) pattern = get_fq_result_pattern() apply_pattern(graph, nodes=pattern['nodes'], edges=pattern['edges'], action=change_names)
def convert_bn_to_mul_add(graph: Graph): apply_pattern( graph, nodes=[ ('input', dict(kind='data')), ('mean', dict(kind='data')), ('variance', dict(kind='data')), ('output', dict(kind='data')), ('batch_norm', dict(kind='op', op='BatchNormalization')), ], edges=[ ('input', 'batch_norm', {'in': 0}), ('mean', 'batch_norm', {'in': 1}), ('variance', 'batch_norm', {'in': 2}), ('batch_norm', 'output'), ], action=_bn_to_mul_add_action )
def scale_input(graph: nx.MultiDiGraph, scale: float): """ Searches for all entries of Placeholder in graph and passes it to the the replace transform Args: graph: an instance of nx graph scale: integer value for the scale """ if scale is None or scale == 1: return apply_pattern(graph, nodes=[('placeholder', dict(kind='op', op='Placeholder')), ('data', dict(kind='data'))], edges=[ ('placeholder', 'data'), ], action=lambda graph, match: _scale_input_action_mul( graph, match, scale))
def analyze(self, graph: Graph): pattern_instance_counter.counter = 0 apply_pattern(graph, **RETINANET_PATTERN, action=pattern_instance_counter) if pattern_instance_counter.counter > 0: result = dict() result['mandatory_parameters'] = {'tensorflow_use_custom_operations_config': 'extensions/front/tf/retinanet.json'} message = "Your model looks like TensorFlow RetinaNet Model.\n" \ "To generate the IR, provide model to the Model Optimizer with the following parameters:\n" \ "\t--input_model <path_to_model>/<model>.pb\n" \ "\t--input_shape [1,600,600,3]\n" \ "\t--tensorflow_use_custom_operations_config <OPENVINO_INSTALL_DIR>/deployment_tools/model_optimizer/extensions/front/tf/retinanet.json\n" \ "\t--reverse_input_channels" return {'model_type': {'TF_RetinaNet': result}}, message return None, None
def convert_mul_eltwise_to_leaky_relu(graph: nx.MultiDiGraph): """ This function finds next subgraph: -->Data-------->Eltwise(Max)-->Data `-->Mul---` and replace with ReLU with negative slope """ apply_pattern(graph, nodes=[ ('data', dict(kind='data')), ('power_data', dict(kind='data')), ('eltwise_op', dict(kind='op', type='Eltwise')), ('power_op', dict(kind='op', type='Power')), ], edges=[ ('data', 'power_op'), ('power_op', 'power_data'), ('data', 'eltwise_op'), ('power_data', 'eltwise_op'), ], action=_convert_to_leaky_relu_action) return graph
def find_and_replace_pattern(self, graph: Graph): apply_pattern(graph, action=self.replace_sub_graph, **self.pattern())
def convert_squeeze(graph: nx.MultiDiGraph): apply_pattern(graph, nodes=[('reshape', dict(kind='op', op='Squeeze')), ('output', dict(kind='data'))], edges=[('reshape', 'output')], action=reshape_squeeze_transform)
def find_and_replace_pattern(self, graph: Graph): apply_pattern(graph, **self.pattern(), action=self.replace_pattern) # pylint: disable=no-member
def convert_gemm_to_fully_connected(graph: nx.MultiDiGraph): apply_pattern(graph, nodes=[('gemm', dict(kind='op', op='Gemm')), ('output', dict(kind='data'))], edges=[('gemm', 'output')], action=gemm_to_fully_connected_action)
def find_and_replace_pattern(self, graph: Graph): log.info('Enabled LayerNorm pattern recognition') apply_pattern(graph, **self.pattern1(), action=self.replace_layer_norm) apply_pattern(graph, **self.pattern2(), action=self.replace_layer_norm)
def find_and_replace_pattern(self, graph: Graph): log.info('Enabled GeLU Merger replacement for approximation with Erf') apply_pattern(graph, **self.pattern1(), action=self.replace_gelu) apply_pattern(graph, **self.pattern2(), action=self.replace_gelu) apply_pattern(graph, **self.pattern3(), action=self.replace_gelu)