def _init_lnf_from_tf_node_def(tf_node_def): lnf = lgf_pb2.LNF() lnf.name = tf_node_def.name # Node attributes lnf.supported = False if tf_node_def.op == "LGFSubgraph": # Subgraph node lnf.subgraph.SetInParent() lnf.subgraph.graph.ParseFromString( tf_node_def.attr["serialized_subgraph"].s) else: # Original node lnf.original.SetInParent() lnf.original.t = graph_types_pb2.TFSavedModel lnf.original.op = ImportTFSavedModelBase.OP_MAP.get( tf_node_def.op, ops_pb2.UNKNOWN) if lnf.original.op == ops_pb2.ENTER: lnf.original.attr[ lgf_graph.LightGraph. IS_CONST_ATTR].b = tf_node_def.attr["is_constant"].b lnf.original.serialized_node = tf_node_def.SerializeToString() return lnf
def create_simple_node(self, node_name, node_type, inputs, outputs, control_inputs, supported=True): new_node = lgf_pb2.LNF() new_node.name = node_name new_node.supported = supported getattr(new_node, node_type).SetInParent() # Inputs for inp in inputs: new_node.inputs.add().CopyFrom(inp) new_node.inputs[-1].dtype.CopyFrom(self._get_dtype()) # Outputs for outp in outputs: new_node.outputs.add().CopyFrom(outp) new_node.outputs[-1].dtype.CopyFrom(self._get_dtype()) # Control inputs new_node.control_inputs.extend(control_inputs) return new_node
def sv_max_graph(inp_shape=(1, 100), inp_dtype_t=dtypes_pb2.DT_QINT, inp_dtype_p=8, num_nodes=1): inp1 = lgf_pb2.EdgeInfo() inp1.name = "inp_ten1" inp1.port = 0 inp1.dtype.t = inp_dtype_t inp1.dtype.p = inp_dtype_p inp1.shape.d.extend(inp_shape) nodes = [] last_edge = inp1 for i in range(num_nodes): outp = lgf_pb2.EdgeInfo() outp.CopyFrom(inp1) outp.name = "out_ten_{0}".format(i) n = lgf_pb2.LNF() n.name = outp.name n.sv_max.SetInParent() n.inputs.add().CopyFrom(last_edge) n.outputs.add().CopyFrom(outp) n.sv_max.scalar = 64 n.supported = True last_edge = n.outputs[0] nodes.append(n) return lgf_graph.LightGraph(nodes, input_edges=[inp1], output_edges=[last_edge])
def transform(self, opu_node, light_graph): transform_result = self.create_transform_result() phasify_node, phasify_subgraph_nodes = self._get_foldable_phasify_nodes( opu_node, light_graph) if phasify_subgraph_nodes is None: return transform_result # Create a new opu node new_opu_node = lgf_pb2.LNF() new_opu_node.CopyFrom(opu_node) matmul = opu_op_transform.OPUOpTransform.get_matmul_from_opu_node( new_opu_node) matmul.phasify_is_folded = True # Get phases and dequant scales self._get_phases_and_dequant_scales(new_opu_node, light_graph, phasify_node, phasify_subgraph_nodes, transform_result) # Compute the dequant bias if necessary if matmul.using_quant_bias: self._get_dequant_bias(new_opu_node, light_graph, transform_result) transform_result.to_replace.add().node.CopyFrom(new_opu_node) return transform_result
def onnx_node_to_lnf(self, onnx_node): lnf = lgf_pb2.LNF() lnf.name = onnx_node.name lnf.supported = False lnf.original.SetInParent() lnf.original.t = graph_types_pb2.ONNXModel lnf.original.op = ImportONNXModel.OP_MAP.get(onnx_node.op_type, ops_pb2.UNKNOWN) lnf.original.serialized_node = onnx_node.SerializeToString() # Node inputs in edge info format are stored here node_input_edges = [ self.onnx_edge_to_edge_info(self.get_name_and_port(input_edge)) for input_edge in onnx_node.input ] for node_input_edge_info in node_input_edges: lnf.inputs.add().CopyFrom(node_input_edge_info) node_output_edges = [ self.onnx_edge_to_edge_info(self.get_name_and_port(output_edge)) for output_edge in onnx_node.input ] lnf.outputs.extend(node_output_edges) return lnf
def _add_cast_node(self, node, input_edge, input_node, matching_output_edge): """ Adds a cast node between the edges, so the graph will have input_node --{matching_output_edge}--> cast --{input_edge}--> node """ # Create a cast node cast_node = lgf_pb2.LNF() cast_node.name = self._get_new_node_name(matching_output_edge.name + "_cast") # Input and output info cast_node.inputs.add().CopyFrom(matching_output_edge) cast_node.outputs.add().CopyFrom(input_edge) cast_node.outputs[0].name = cast_node.name cast_node.outputs[0].port = 0 self.process_cast_node(cast_node, input_node, node, self._sw_config) # Create transform result return base_transform.BaseTransform.create_transform_result( to_add=[cast_node], to_reroute=[ (transform_result_pb2.ToReroute.edge_reroute.DESCRIPTOR.name, [node.name], input_edge, cast_node.outputs[0]) ])
def transform(self, opu_node, light_graph): node_name = opu_node.name if opu_op_transform.OPUOpTransform.is_part_of_batch_matmul( opu_node, light_graph): # Currently all unstacked matmul nodes from a batch matmul node # share the activation scale. The scale is stored with the # name of the original batch matmul node. node_name = (tf_batch_matmul_transform.TFSavedModelBatchMatMulV2Transform. get_batch_matmul_node_name(node_name)) activation_scale_info = self._get_activation_scale_info(node_name) # Update the quant params node quant_params_node = light_graph.get_node_by_name( opu_node.inputs[lgf_pb2.MatMulNode.QUANT_PARAMS_INDEX].name) new_quant_params = np.array( [[activation_scale_info.scale] * self._hw_specs.dimension, [activation_scale_info.bias] * self._hw_specs.dimension]) new_quant_params_node = self.create_const_node( new_quant_params, quant_params_node.name, quant_params_node.outputs[0].dtype, quant_params_node.const.const_type) # Update the opu node new_opu_node = lgf_pb2.LNF() new_opu_node.CopyFrom(opu_node) matmul = opu_op_transform.OPUOpTransform.get_matmul_from_opu_node(new_opu_node) matmul.using_quant_bias = (activation_scale_info.bias != 0) new_opu_node.inputs[lgf_pb2.MatMulNode.QUANT_PARAMS_INDEX].CopyFrom( new_quant_params_node.outputs[0]) # Update phasify node phasify_node = light_graph.get_node_by_name( opu_node.inputs[lgf_pb2.MatMulNode.PHASES_INDEX].name) new_phasify_node = lgf_pb2.LNF() new_phasify_node.CopyFrom(phasify_node) new_phasify_node.inputs[lgf_pb2.PhasifyNode.QUANT_PARAMS_INPUT_INDEX].CopyFrom( new_quant_params_node.outputs[0]) return self.create_transform_result( to_replace=[new_quant_params_node, new_opu_node, new_phasify_node])
def init_conv2d_node(self, conv2d_name): # Create a distributed depthwise conv2d node depthwise_conv2d_node = lgf_pb2.LNF() depthwise_conv2d_node.name = conv2d_name depthwise_conv2d_node.supported = True depthwise_conv2d_node.distributed_depthwise_conv2d.SetInParent() return (depthwise_conv2d_node, depthwise_conv2d_node.distributed_depthwise_conv2d.conv2d)
def init_conv2d_node(self, conv2d_name): # Create a block diagonal depthwise conv2d node depthwise_conv2d_node = lgf_pb2.LNF() depthwise_conv2d_node.name = conv2d_name depthwise_conv2d_node.supported = True depthwise_conv2d_node.block_diagonal_depthwise_conv2d.SetInParent() return (depthwise_conv2d_node, depthwise_conv2d_node.block_diagonal_depthwise_conv2d.conv2d)
def transform(self, opu_node, light_graph): # Get hist path and write an empty hist hist_keys = self._get_hist_keys(opu_node, light_graph) # New opu node new_opu_node = lgf_pb2.LNF() new_opu_node.CopyFrom(opu_node) matmul = opu_op_transform.OPUOpTransform.get_matmul_from_opu_node( new_opu_node) matmul.turn_off_adc = True matmul.hist_keys_before_adc.CopyFrom(hist_keys) return self.create_transform_result(to_replace=[new_opu_node])
def get_transforms(self, light_graph): """ Returns the transforms to collapse supported subgraphs in light_graph into single nodes. """ subgraphs = self._get_supported_subgraph_lists(light_graph) # Node transformations converting each subgraph into a single node to_add = [] to_reroute = [] to_output_swap = [] subgraph_index = self._get_next_subgraph_index(light_graph) for subgraph, control_inputs in subgraphs: subgraph_node = lgf_pb2.LNF() subgraph_node.name = self.get_subgraph_node_name(subgraph_index) subgraph_node.supported = False subgraph_node.subgraph.SetInParent() subgraph_node.subgraph.graph.CopyFrom(subgraph.as_lgf_pb()) subgraph_node.inputs.extend(subgraph.input_edges()) subgraph_node.control_inputs.extend(control_inputs) for j, old_edge in enumerate(subgraph.output_edges()): new_edge = lgf_pb2.EdgeInfo() new_edge.CopyFrom(old_edge) new_edge.name = subgraph_node.name new_edge.port = j subgraph_node.outputs.add().CopyFrom(new_edge) to_reroute.append((transform_result_pb2.ToReroute.edge_reroute. DESCRIPTOR.name, [], old_edge, new_edge)) for old_node in subgraph.nodes(): to_reroute.append((transform_result_pb2.ToReroute. control_input_reroute.DESCRIPTOR.name, [], [old_node.name], [subgraph_node.name])) if len(subgraph.output_node_names()): to_output_swap.append( (subgraph.output_node_names(), [subgraph_node.name])) to_add.append(subgraph_node) subgraph_index += 1 return [ base_transform.BaseTransform.create_transform_result( to_add=to_add, to_reroute=to_reroute, to_output_swap=to_output_swap) ]
def _modify_opu_node(self, opu_node): # New opu node new_opu_node = lgf_pb2.LNF() new_opu_node.CopyFrom(opu_node) # Get dtypes for histograms before_adc_dtype = dtypes_pb2.DType() before_adc_dtype.t = dtypes_pb2.DT_FLOAT before_adc_dtype.p = 32 after_adc_dtype = opu_node.outputs[0].dtype # Get num bins for histograms before_adc_num_bins = self._get_num_bins(before_adc_dtype) after_adc_num_bins = self._get_num_bins(after_adc_dtype) # Get hist keys from node matmul = opu_op_transform.OPUOpTransform.get_matmul_from_opu_node( new_opu_node) hist_keys_before_adc = matmul.hist_keys_before_adc hist_keys_after_adc = matmul.hist_keys_after_adc # Initialize some things hist_keys_before_adc.quant_type = common_pb2.QT_PER_TILE hist_keys_after_adc.quant_type = common_pb2.QT_PER_TILE # Histogram for each tile weight_shape = opu_node.inputs[lgf_pb2.MatMulNode.PHASES_INDEX].shape for x in range(weight_shape.d[0]): for y in range(weight_shape.d[1]): # Add keys to node hist_keys_before_adc.keys.append( self._get_new_hist_key(node=opu_node, x=x, y=y, suffix="before_adc")) hist_keys_after_adc.keys.append( self._get_new_hist_key(node=opu_node, x=x, y=y, suffix="after_adc")) # Initialize histograms self._hist_coll.initialize_empty_histogram( hist_keys_before_adc.keys[-1], before_adc_num_bins) self._hist_coll.initialize_empty_histogram( hist_keys_after_adc.keys[-1], after_adc_num_bins) return base_transform.BaseTransform.create_transform_result( to_replace=[new_opu_node])
def create_supported_nodes(self, matmul_name, input_edge, weights_edge, output_edge, control_inputs, transpose_inputs=False, transpose_weights=False): """ Creates a supported matmul node in standard format Params: matmul_name: name of original node input_edge: edge of the input for the original node weights_edge: input edge for the weights, to be fed to phasify output_edge: edge of the output for the original node control_inputs: a list of node names for the control inputs transpose_inputs: if True, transpose the inputs transpose_weights: if True, transpose the weights """ # TODO: support transpose of inputs? just insert a tranpose node? if transpose_inputs: logging.warning( "Transpose of vector in MatMul node {} is NOT supported.". format(matmul_name)) return [] # Create a matmul_node matmul_node = lgf_pb2.LNF() matmul_node.name = matmul_name matmul_node.supported = True matmul_node.matmul.SetInParent() # Phasify to_add = self._create_phasify_node(matmul_node, weights_edge, transpose_weights=transpose_weights) # Input data matmul_node.inputs[lgf_pb2.MatMulNode.INPUT_INDEX].CopyFrom(input_edge) matmul_node.inputs[lgf_pb2.MatMulNode.INPUT_INDEX].dtype.CopyFrom( self._sw_config.float_type) matmul_node.control_inputs.extend(control_inputs) # Outputs matmul_node.outputs.add().CopyFrom(output_edge) matmul_node.outputs[0].dtype.CopyFrom(self._sw_config.float_type) return [matmul_node] + to_add
def transform(self, node, light_graph): activation_scale_info = self._get_activation_scale_info(node.name) # Create new node that has a quant scale set # Update current node new_node = lgf_pb2.LNF() new_node.CopyFrom(node) node_type = getattr(new_node, new_node.WhichOneof("node")) fields = node_type.DESCRIPTOR.fields_by_name assert ("quant_scale" in fields) assert ("quant_precision" in fields) node_type.quant_scale = activation_scale_info.scale node_type.quant_precision = self._sw_config.quantized_electronic_op_precision return self.create_transform_result(to_replace=[new_node])
def transform(self, pad_node, light_graph): to_replace = [] for node_name in light_graph.get_output_node_names_of_node(pad_node): conv2d_node = light_graph.get_node_by_name(node_name) new_conv2d_node = lgf_pb2.LNF() new_conv2d_node.CopyFrom(conv2d_node) new_conv2d_node.conv2d.image_attr.padding = self._get_converted_pad_type( pad_node, conv2d_node) new_conv2d_node.inputs[lgf_pb2.MatMulNode.INPUT_INDEX].CopyFrom( pad_node.inputs[0]) to_replace.append(new_conv2d_node) return self.create_transform_result(to_replace=to_replace)
def init_conv2d_node(self, conv2d_name): """ Initializes a conv2d node Params: conv2d_name: name for the conv2d node Returns: a tuple: (a lgf_pb2.LNF() object for the node, a lgf_pb2.Conv2DNode() for the conv2d of the node) """ # Create a conv2d_node conv2d_node = lgf_pb2.LNF() conv2d_node.name = conv2d_name conv2d_node.supported = True conv2d_node.conv2d.SetInParent() return conv2d_node, conv2d_node.conv2d
def preprocess_weights(self, weights_edge): # Unpack shapes filter_height, filter_width, in_channels, channel_multiplier = \ weights_edge.shape.d k = self._hw_specs.dimension j = self.num_columns() # There are in_channels matrices, each of size # [filter_height * filter_width, channel_multiplier] # Figure out how many we can stack on each axis x_stack = k // (filter_height * filter_width) y_stack = j // (channel_multiplier) matrices_per_tile = min(x_stack, y_stack) # If the matrices are too big to fit into the matmul unit, we cannot # do the transform if not matrices_per_tile: logging.error( "Failed to transform Block Diagonal Depthwise Conv2d") return False, [], weights_edge # Number of opu tiles we will need for in_channels matrices num_tiles = np.ceil(in_channels / matrices_per_tile).astype(int) # Create depthwise conv2d reshape node depthwise_conv2d_reshape_node = lgf_pb2.LNF() depthwise_conv2d_reshape_node.name = weights_edge.name + "_depthwise_reshape" depthwise_conv2d_reshape_node.supported = True (depthwise_conv2d_reshape_node.block_diagonal_depthwise_conv2d_reshape. SetInParent()) input_edge = depthwise_conv2d_reshape_node.inputs.add() input_edge.CopyFrom(weights_edge) input_edge.dtype.CopyFrom(self._get_dtype()) output_edge = depthwise_conv2d_reshape_node.outputs.add() output_edge.name = depthwise_conv2d_reshape_node.name output_edge.port = 0 output_edge.dtype.CopyFrom(input_edge.dtype) output_edge.shape.d.extend([k, num_tiles * j]) output_edge.shape.batch_dim_indx = -1 return True, [depthwise_conv2d_reshape_node], output_edge
def get_transforms(self, light_graph): # Create a node that updates variables update_variables_node = lgf_pb2.LNF() update_variables_node.name = self.UPDATE_VARIABLES_NAME update_variables_node.supported = True update_variables_node.update_variables.SetInParent() update_variables_node.update_variables.update_info.CopyFrom( self._update_info) update_variables_node.control_inputs.extend( light_graph.output_node_names() + [e.name for e in light_graph.output_edges()]) return [ base_transform.BaseTransform.create_transform_result( to_add=[update_variables_node], to_output_swap=[(light_graph.output_node_names(), [update_variables_node.name])]) ]
def create_supported_nodes(self, dequant_name, input_edge, output_edge, control_inputs, scales, bias=0, method=lgf_pb2.DQ_STANDARD): """ Creates a supported dequant node in standard format Params: dequant_name: name of original node input_edge: edge of the input for the original node output_edge: edge of the output for the original node control_inputs: a list of node names for the control inputs scales: a list or numpy array of dequant scales bias: dequant bias method: a lgf_pb2.DequantMethod """ # Create the dequant scales const node dequant_scales_node = self.create_const_node(np.array(scales), dequant_name + "_scales", self._sw_config.float_type, lgf_pb2.ConstNode.DEQUANT_SCALE) # Create dequant node dequant_node = lgf_pb2.LNF() dequant_node.name = dequant_name dequant_node.inputs.add().CopyFrom(input_edge) dequant_node.inputs.add().CopyFrom(dequant_scales_node.outputs[0]) dequant_node.control_inputs.extend(control_inputs) dequant_node.outputs.add().CopyFrom(output_edge) dequant_node.supported = True dequant_node.dequantize.SetInParent() # dequantize attributes dequant_node.dequantize.method = method dequant_node.dequantize.bias = bias return [dequant_node, dequant_scales_node]
def transform(self, opu_node, light_graph): # New opu node new_opu_node = lgf_pb2.LNF() new_opu_node.CopyFrom(opu_node) matmul = opu_op_transform.OPUOpTransform.get_matmul_from_opu_node( new_opu_node) matmul.turn_off_adc = False # Get the old adc scales node phasify_node = light_graph.get_node_by_name( opu_node.inputs[lgf_pb2.MatMulNode.PHASES_INDEX].name) adc_scales_node = light_graph.get_node_by_name(phasify_node.inputs[ lgf_pb2.PhasifyNode.ADC_SCALES_INPUT_INDEX].name) # New adc scales node new_adc_scales = self._get_adc_scales(opu_node) new_adc_scales_node = self.create_const_node( new_adc_scales, adc_scales_node.name, adc_scales_node.outputs[0].dtype, adc_scales_node.const.const_type) return self.create_transform_result( to_replace=[new_opu_node, new_adc_scales_node])
def create_supported_nodes(self, quantize_name, input_edge, output_edge, control_inputs, scale, precision, bias=0): """ Creates a supported quant node in standard format Params: quant_name: name of original node input_edge: edge of the input for the original node output_edge: edge of the output for the original node control_inputs: a list of node names for the control inputs scale: quantization scale precision: quantization precision bias: quantization bias """ # Replace the Quantize Node in order to change the operation to a Quantize quant_node = lgf_pb2.LNF() quant_node.name = quantize_name quant_node.inputs.add().CopyFrom(input_edge) quant_node.control_inputs.extend(control_inputs) quant_node.outputs.add().CopyFrom(output_edge) quant_node.supported = True quant_node.quantize.SetInParent() # quantize attributes quant_node.quantize.scale = scale quant_node.quantize.precision = precision quant_node.quantize.bias = bias return [quant_node]
def _create_phasify_node(self, opu_node, weights_edge, transpose_weights=False): # Get shape variables from the weights edge num_x, num_y, k, j = self.get_tiled_shape(weights_edge, transpose_weights, self._hw_specs) # Create a phasify node phasify_node = lgf_pb2.LNF() phasify_node.name = opu_node.name + "_phasify" phasify_node.supported = True phasify_node.phasify.SetInParent() phasify_node.phasify.transpose = transpose_weights # Initialize inputs for _ in range(self.PHASIFY_NUM_INPUTS): phasify_node.inputs.add() # Quant params [quant_scale, quant_bias] quant_params = np.array([1, 0]) quant_params_node = self.create_const_node(quant_params, opu_node.name + "_quant_params", self._sw_config.float_type, lgf_pb2.ConstNode.GRAPH_CONST) phasify_node.inputs[lgf_pb2.PhasifyNode.QUANT_PARAMS_INPUT_INDEX].CopyFrom( quant_params_node.outputs[0]) # Weights phasify_node.inputs[lgf_pb2.PhasifyNode.WEIGHTS_INPUT_INDEX].CopyFrom( weights_edge) phasify_node.inputs[lgf_pb2.PhasifyNode.WEIGHTS_INPUT_INDEX].dtype.CopyFrom( self._sw_config.float_type) # ADC scales adc_scales = np.ones(shape=[num_x, num_y, 1, j]) adc_scales_node = self.create_const_node(adc_scales, opu_node.name + "_adc_scales", self._sw_config.float_type, lgf_pb2.ConstNode.ADC_SCALE) phasify_node.inputs[lgf_pb2.PhasifyNode.ADC_SCALES_INPUT_INDEX].CopyFrom( adc_scales_node.outputs[0]) # Initialize outputs for _ in range(self.PHASIFY_NUM_OUTPUTS): phasify_node.outputs.add() # Phases phases_edge = phasify_node.outputs[lgf_pb2.PhasifyNode.PHASES_OUTPUT_INDEX] phases_edge.name = phasify_node.name phases_edge.port = lgf_pb2.PhasifyNode.PHASES_OUTPUT_INDEX phases_edge.dtype.CopyFrom(self._phase_dtype) phases_edge.shape.d.extend([num_x, num_y, j // k, k, k]) phases_edge.shape.batch_dim_indx = -1 # Dequant scales dequant_scales_edge = phasify_node.outputs[ lgf_pb2.PhasifyNode.DEQUANT_SCALES_OUTPUT_INDEX] dequant_scales_edge.name = phasify_node.name dequant_scales_edge.port = lgf_pb2.PhasifyNode.DEQUANT_SCALES_OUTPUT_INDEX dequant_scales_edge.dtype.CopyFrom(self._sw_config.float_type) dequant_scales_edge.shape.d.extend([num_x, num_y, 1, j]) dequant_scales_edge.shape.batch_dim_indx = -1 # ADC scales adc_scales_edge = phasify_node.outputs[ lgf_pb2.PhasifyNode.ADC_SCALES_OUTPUT_INDEX] adc_scales_edge.CopyFrom(adc_scales_node.outputs[0]) adc_scales_edge.name = phasify_node.name adc_scales_edge.port = lgf_pb2.PhasifyNode.ADC_SCALES_OUTPUT_INDEX # Initialize inputs of the opu node for _ in range(self.NUM_INPUTS): opu_node.inputs.add() # Add input edges from phasify opu_node.inputs[lgf_pb2.MatMulNode.QUANT_PARAMS_INDEX].CopyFrom( phasify_node.inputs[lgf_pb2.PhasifyNode.QUANT_PARAMS_INPUT_INDEX]) opu_node.inputs[lgf_pb2.MatMulNode.PHASES_INDEX].CopyFrom( phasify_node.outputs[lgf_pb2.PhasifyNode.PHASES_OUTPUT_INDEX]) opu_node.inputs[lgf_pb2.MatMulNode.DEQUANT_SCALES_INDEX].CopyFrom( phasify_node.outputs[lgf_pb2.PhasifyNode.DEQUANT_SCALES_OUTPUT_INDEX]) opu_node.inputs[lgf_pb2.MatMulNode.ADC_SCALES_INDEX].CopyFrom( phasify_node.outputs[lgf_pb2.PhasifyNode.ADC_SCALES_OUTPUT_INDEX]) # Default attributes self._set_default_attributes(self.get_matmul_from_opu_node(opu_node)) return [phasify_node, quant_params_node, adc_scales_node]
def matmul_graph(hw_spec, sw_config, sim_params, weights, inp_shape=(1, 4), inp_dtype_t=dtypes_pb2.DT_BFLOAT, inp_dtype_p=16, add_activation=False): inp1 = lgf_pb2.EdgeInfo() inp1.name = "inp_ten1" inp1.port = 0 inp1.dtype.t = inp_dtype_t inp1.dtype.p = inp_dtype_p inp1.shape.d.extend(list(inp_shape)) weights_i = lgf_pb2.EdgeInfo() weights_i.name = "weights_ten" weights_i.port = 0 weights_i.dtype.CopyFrom(inp1.dtype) weights_i.shape.d.extend(weights.shape) outp = lgf_pb2.EdgeInfo() outp.CopyFrom(inp1) outp.name = "out_ten" outp.shape.d[1] = weights.shape[1] outp.dtype.t = inp_dtype_t outp.dtype.p = inp_dtype_p wn = lgf_pb2.LNF() wn.name = "weights_ten" wn.const.SetInParent() wn.outputs.add().CopyFrom(weights_i) wn.const.value.CopyFrom( utils.array_to_tensor_pb(weights, weights_i.dtype)) wn.const.const_type = lgf_pb2.ConstNode.GRAPH_CONST wn.supported = True mm_tx = matmul_transform.MatMulTransform(hw_spec, sw_config, sim_params) mm_nodes = mm_tx.create_supported_nodes("out_ten", inp1, weights_i, outp, []) act_nodes = [] if add_activation: bias = base_transform.BaseTransform.create_const_node( np.random.random(size=(1, inp_shape[1])), "add_bias", inp1.dtype, lgf_pb2.ConstNode.GRAPH_CONST) act_nodes.append(bias) act = lgf_pb2.LNF() act.name = "act" act.vv_add.SetInParent() act.supported = True act.inputs.add().CopyFrom(mm_nodes[0].outputs[0]) act.inputs.add().CopyFrom(bias.outputs[0]) act.outputs.add().CopyFrom(mm_nodes[0].outputs[0]) act.outputs[0].name = "act" outp = act.outputs[0] act_nodes.append(act) lg = lgf_graph.LightGraph([wn] + mm_nodes + act_nodes, input_edges=[inp1], output_edges=[outp]) folder = fold_phasify_constants.FoldPhasifyConstants( hw_spec, sw_config, sim_params) return folder.process_transforms(lg)
def _copy_node(self, node): node_copy = lgf_pb2.LNF() node_copy.CopyFrom(node) return node_copy
def transform(self, fully_connected_node, light_graph): """ Converts unsupported TFLite FULLY_CONNECTED to supported standard MATMUL and VV_ADD """ # Get the original sub_node sub_node = fully_connected_node.original # Assertions self.check_original_node(fully_connected_node) assert ("fused_activation_function" in sub_node.attr) assert ("weights_format" in sub_node.attr) assert (sub_node.attr["weights_format"].s == "DEFAULT") # Get the edges input_edge = fully_connected_node.inputs[0] weight_edge = fully_connected_node.inputs[1] bias_edge = fully_connected_node.inputs[2] output_edge = fully_connected_node.outputs[0] # Transforms to_add = [] to_replace = [] to_reroute = [] edge_reroute = transform_result_pb2.ToReroute.edge_reroute.DESCRIPTOR.name # Matmul transform matmul_transforms = matmul_transform.MatMulTransform.do_generic_transform( self, fully_connected_node.name, input_edge, weight_edge, output_edge, fully_connected_node.control_inputs) to_add.extend([t.node for t in matmul_transforms.to_add]) to_replace.extend([t.node for t in matmul_transforms.to_replace]) matmul_node = matmul_transforms.to_replace[0].node # Create the CAST node to cast the bias to the type of the matmul cast_bias_node = lgf_pb2.LNF() cast_bias_node.name = fully_connected_node.name + "_cast_bias" cast_bias_node.supported = True cast_bias_node.cast.SetInParent() # Create the CAST input edge cast_bias_input_edge = cast_bias_node.inputs.add() cast_bias_input_edge.CopyFrom(bias_edge) cast_bias_input_edge.dtype.CopyFrom(self._get_dtype()) # Create the CAST output edge cast_bias_edge = cast_bias_node.outputs.add() cast_bias_edge.name = cast_bias_node.name cast_bias_edge.port = 0 cast_bias_edge.dtype.CopyFrom(matmul_node.outputs[0].dtype) cast_bias_edge.shape.CopyFrom(bias_edge.shape) # Append the node to add to_add.append(cast_bias_node) # Append the edges to be rerouted: BIAS>FC to BIAS>CAST_BIAS to_reroute.extend([(edge_reroute, [], bias_edge, cast_bias_edge)]) # Create the ADD node add_node = lgf_pb2.LNF() add_node.name = fully_connected_node.name + "_add" add_node.supported = True add_node.vv_add.SetInParent() # Create the ADD input data edge add_node.inputs.add().CopyFrom(matmul_node.outputs[0]) # Create the ADD input bias edge add_bias_edge = add_node.inputs.add() add_bias_edge.CopyFrom(cast_bias_edge) add_bias_edge.dtype.CopyFrom(self._sw_config.float_type) # Create the ADD output edge add_output_edge = add_node.outputs.add() add_output_edge.name = add_node.name add_output_edge.port = 0 add_output_edge.dtype.CopyFrom(add_node.inputs[0].dtype) add_output_edge.shape.CopyFrom(add_node.inputs[0].shape) # Append the node to add to_add.append(add_node) to_reroute.extend([(edge_reroute, [], output_edge, add_output_edge)]) # Create the CAST node to cast the bias to the type of the matmul cast_add_node = lgf_pb2.LNF() cast_add_node.name = fully_connected_node.name + "_cast_add" cast_add_node.supported = True cast_add_node.cast.SetInParent() # Create the CAST input edge cast_add_node.inputs.add().CopyFrom(add_output_edge) # Create the CAST output edge cast_add_edge = cast_add_node.outputs.add() cast_add_edge.name = cast_add_node.name cast_add_edge.port = 0 cast_add_edge.dtype.CopyFrom(input_edge.dtype) cast_add_edge.shape.CopyFrom(add_output_edge.shape) # Append the node to add to_add.append(cast_add_node) # Append the edges to be rerouted: ADD>OUTPUT to CAST_ADD>OUTPUT to_reroute.extend([(edge_reroute, [], add_output_edge, cast_add_edge)]) # Return the transforms return self.create_transform_result(to_add=to_add, to_replace=to_replace, to_reroute=to_reroute)
def insert_collect_hist_node(edge, sw_config, hist_key, num_bins, hist_coll): to_add = [] to_reroute = [] edge_reroute = transform_result_pb2.ToReroute.edge_reroute.DESCRIPTOR.name # Cast to float if necessary insert_cast = (edge.dtype.p > sw_config.float_type.p) if insert_cast: # Cast to float cast_node = lgf_pb2.LNF() cast_node.name = "{}_{}_cast".format(edge.name, edge.port) cast_node.supported = True cast_node.cast.SetInParent() # Cast inputs and outputs cast_node.inputs.add().CopyFrom(edge) cast_output_edge = lgf_pb2.EdgeInfo() cast_output_edge.name = cast_node.name cast_output_edge.port = 0 cast_output_edge.dtype.CopyFrom(sw_config.float_type) cast_output_edge.shape.CopyFrom(edge.shape) cast_node.outputs.add().CopyFrom(cast_output_edge) to_add.append(cast_node) to_reroute.append((edge_reroute, [], edge, cast_output_edge)) else: cast_output_edge = edge # Collect hist node collect_hist_node = lgf_pb2.LNF() collect_hist_node.name = "{}_{}_collect_hist".format(edge.name, edge.port) collect_hist_node.supported = True collect_hist_node.collect_hist.SetInParent() collect_hist_node.collect_hist.hist_keys.keys.append(hist_key) collect_hist_node.collect_hist.hist_keys.quant_type = common_pb2.QT_SINGLE hist_coll.initialize_empty_histogram(hist_key, num_bins) # Calibration inputs and outputs collect_hist_node.inputs.add().CopyFrom(cast_output_edge) collect_hist_output_edge = lgf_pb2.EdgeInfo() collect_hist_output_edge.CopyFrom(cast_output_edge) collect_hist_output_edge.name = collect_hist_node.name collect_hist_output_edge.port = 0 collect_hist_node.outputs.add().CopyFrom(collect_hist_output_edge) to_add.append(collect_hist_node) to_reroute.append((edge_reroute, [], cast_output_edge, collect_hist_output_edge)) # Reverse cast if necessary if insert_cast: # Reverse cast reverse_cast_node = lgf_pb2.LNF() reverse_cast_node.name = "{}_{}_reverse_cast".format(edge.name, edge.port) reverse_cast_node.supported = True reverse_cast_node.cast.SetInParent() # Reverse cast inputs and outputs reverse_cast_node.inputs.add().CopyFrom(collect_hist_output_edge) reverse_cast_output_edge = lgf_pb2.EdgeInfo() reverse_cast_output_edge.CopyFrom(collect_hist_output_edge) reverse_cast_output_edge.name = reverse_cast_node.name reverse_cast_output_edge.dtype.CopyFrom(edge.dtype) reverse_cast_node.outputs.add().CopyFrom(reverse_cast_output_edge) to_add.append(reverse_cast_node) to_reroute.append((edge_reroute, [], collect_hist_output_edge, reverse_cast_output_edge)) return base_transform.BaseTransform.create_transform_result( to_add=to_add, to_reroute=to_reroute)