def replace_sub_graph(self, graph: nx.MultiDiGraph, match: dict): node = match['op'] if not node.has_valid('bias') or (node.has_valid('bias') and node.bias == 1): return # Calculate scale value & create Const op scale_value = np.array(1. / (pow(node.bias, node.beta))) node.alpha /= node.bias const_node = Const(graph, dict(value=scale_value, shape=scale_value.shape)) # Get all outputs for LRN layer out_nodes = [node for node in node.out_nodes().values()] # Create Mul node with inputs mul_node = Mul(graph, dict(name=node.id + "/Mul_")) mnode = mul_node.create_node(inputs=[node, const_node.create_node()]) # Move edges from LRN to Mul node for out_node in out_nodes: edge_attrs = graph.get_edge_data(node.id, out_node.id)[0] graph.remove_edge(node.id, out_node.id) graph.add_edges_from([(mnode.id, out_node.id, edge_attrs)])
def add_convolution_to_swap_xy_coordinates(graph: Graph, input_node: Node, coordinates_size: int): """ The function add convolution node after the node 'input_node' to swap xy coordinates of the boxes produced by the node 'input_node'. It is expected that box coordinates are located in the fastest changing dimension of the 'input_node' output, i.e. the input tensor could be reshaped to [num_boxes, 4] or [num_boxes, 5]. If the size is 5, then the 0-th element for each of num_boxes blocks is not changed and element 1 is swapped with element 2, element 3 is swapped with element 4. This is the case when boxes coordinates are produced by the layer "Proposal". The exact amount of elements in each block is equal to the 'coordinates_size' parameter. :param graph: graph to operate on. :param input_node: node producing boxes coordinates. :param coordinates_size: integer value equal to 4 or 5. :return convolution node that swaps coordinates. """ # swap of input tensor with 4 or 5 numbers describing boxes are supported assert (coordinates_size in [4, 5]) input_reshape_4d_node = create_op_node_with_second_input( graph, Reshape, int64_array([-1, 1, 1, coordinates_size]), dict(name=input_node.name + '/reshape_4d'), input_node) mark_input_as_in_correct_layout(input_reshape_4d_node, 0) # do not mark second input because the reshape works in initial model layout and needs to be transformed to NCHW mark_output_as_in_correct_layout(input_reshape_4d_node, 0) if coordinates_size == 5: # zero indexed element is not box coordinate ("batch id" in case of Proposal) conv_filter_data = np.array( np.array([[[[1, 0, 0, 0, 0], [0, 0, 1, 0, 0], [0, 1, 0, 0, 0], [0, 0, 0, 0, 1], [0, 0, 0, 1, 0]]]], dtype=np.float32)) else: conv_filter_data = np.array( np.array( [[[[0, 1, 0, 0], [1, 0, 0, 0], [0, 0, 0, 1], [0, 0, 1, 0]]]], dtype=np.float32)) conv_filter_data = np.transpose(conv_filter_data, [2, 3, 0, 1]) conv_filter_const_op = Const(graph, dict(value=conv_filter_data)) conv_filter_const_node = conv_filter_const_op.create_node( [], dict(name=input_node.name + '/weights')) conv_op = Convolution( graph, { 'bias_addable': True, 'channel_dims': np.array([3]), 'batch_dims': np.array([0]), 'input_feature_channel': 0, 'output_feature_channel': 1, 'group': 1, 'layout': 'NHWC', }) return conv_op.create_node([input_reshape_4d_node, conv_filter_const_node], dict(name=input_node.name + "/conv"))
def replace_sub_graph(self, graph: Graph, match: dict): node = match['softmax'] if 'temperature' in node and node['temperature'] != 1.0: in_node = node.in_node() out_nodes = [node for node in node.out_nodes().values()] graph.remove_edge(node.in_node().id, node.id) temperature = np.array([1.0 / node.temperature]) scalar_value_op = Const(graph, dict(value=temperature, shape=temperature.shape, symbol_dict={'name': node.id + '/const'})) mul_op = Mul(graph, dict(name=node.id + '/mul_', symbol_dict={'name': node.id + '/mul_'})) mul_node = mul_op.create_node(inputs=[in_node, scalar_value_op.create_node()]) edge_attrs = graph.get_edge_data(node.id, out_nodes[0].id)[0] graph.add_edges_from([(mul_node.id, node.id, edge_attrs)])
def replace_op(self, graph: Graph, node: Node): in_node = node.in_node() out_nodes = [node for node in node.out_nodes().values()] graph.remove_edge(node.in_node().id, node.id) scalar_value_op = Const(graph, dict(value=node.scalar, shape=node.scalar.shape, symbol_dict={'name': node.id + '/const'})) mul_op = Mul(graph, dict(name=node.id + '/mul_', symbol_dict={'name': node.id + '/mul_'})) mul_node = mul_op.create_node(inputs=[in_node, scalar_value_op.create_node()]) for out_node in out_nodes: edge_attrs = graph.get_edge_data(node.id, out_node.id)[0] graph.remove_edge(node.id, out_node.id) graph.add_edges_from([(mul_node.id, out_node.id, edge_attrs)]) return [mul_node.id]
def replace_sub_graph(self, graph: nx.MultiDiGraph, match: dict): # This replacer replace ImageScalar operation to Mul->Add sequence # Also it check that weights and biases are good op = match['op'] # Check that weights and biases are not useless has_bias, has_weights = True, True if all([x == 1 for x in np.nditer(op.scale)]): has_weights = False if all([x == 0 for x in np.nditer(op.bias)]): has_bias = False # Get all outputs for op node out_nodes = [node for node in op.out_nodes().values()] assert len(op.in_nodes()) == 1 last_node = op.in_node() # Create Mul & Add nodes if has_weights: mul_weights = Const(graph, dict(value=op.scale, shape=op.scale.shape)) mul_op = Mul(graph, dict(name=op.id + '/mul_')) last_node = mul_op.create_node(inputs=[last_node, mul_weights.create_node()]) if has_bias: add_bias = Const(graph, dict(value=op.bias, shape=op.bias.shape)) add_op = Add(graph, dict(name=op.id + '/add_')) last_node = add_op.create_node(inputs=[last_node, add_bias.create_node()]) # Move edges from ImageScaler to last_node (Mul or Add) for out_node in out_nodes: edge_attrs = graph.get_edge_data(op.id, out_node.id)[0] graph.remove_edge(op.id, out_node.id) graph.add_edges_from([(last_node.id, out_node.id, edge_attrs)]) # Disconnect ImageScalar node graph.remove_edge(op.in_node().id, op.id)