def infer_shape_prior_box(self, op): output_shape = [1, 2, 1] input_shape = list(self._output_shape_cache[op.input[0]]) input_w = input_shape[3] input_h = input_shape[2] min_size = ConverterUtil.get_arg(op, MaceKeyword.mace_min_size_str).floats # noqa max_size = ConverterUtil.get_arg(op, MaceKeyword.mace_max_size_str).floats # noqa aspect_ratio = ConverterUtil.get_arg(op, MaceKeyword.mace_aspect_ratio_str).floats # noqa num_prior = len(aspect_ratio) * len(min_size) + len(max_size) output_shape[2] = num_prior * input_h * input_w * 4 self.add_output_shape(op, [output_shape])
def add_tensorflow_padding_value(self): for op in self._model.op: padding_type = ConverterUtil.get_arg(op, MaceKeyword.mace_padding_str) if padding_type is None: continue padding_arg = op.arg.add() padding_arg.name = MaceKeyword.mace_padding_values_str if padding_type.i == PaddingMode.VALID.value: padding_arg.ints.extend([0, 0, 0, 0]) elif padding_type.i == PaddingMode.SAME.value: stride = ConverterUtil.get_arg( op, MaceKeyword.mace_strides_str).ints kernel = [] dilation = [1, 1] if op.type == MaceOp.Conv2D.name or \ op.type == MaceOp.DepthwiseConv2d.name: if ConverterUtil.get_arg( op, MaceKeyword.mace_dilations_str) is not None: dilation = ConverterUtil.get_arg( op, MaceKeyword.mace_dilations_str).ints for tensor in self._model.tensors: if tensor.name == op.input[1]: kernel = tensor.dims[1:3] break else: kernel = ConverterUtil.get_arg( op, MaceKeyword.mace_kernel_str).ints in_size = [] for input_info in self._model.input_info: if input_info.name == op.input[0]: in_size = input_info.dims[1:3] break for _op in self._model.op: for out in _op.output: if out == op.input[0]: in_size = _op.output_shape[0].dims[1:3] break if len(in_size) > 0: break out_size = op.output_shape[0].dims[1:3] h = (out_size[0] - 1) * stride[0] \ + ((kernel[0] - 1) * dilation[0] + 1) - in_size[0] w = (out_size[1] - 1) * stride[1] \ + ((kernel[1] - 1) * dilation[1] + 1) - in_size[1] top = int(np.floor(h / 2)) left = int(np.floor(w / 2)) bottom = h - top right = w - left padding_arg.ints.extend([top, right, bottom, left])
def __init__(self, net_def): self.net_def = net_def self.idle_mem = set() self.op_mem = {} # op_name->mem_id self.mem_block = {} # mem_id->[size] or mem_id->[x, y] self.total_mem_count = 0 self.input_ref_counter = {} self.mem_ref_counter = {} ocl_mem_type_arg = ConverterUtil.get_arg( net_def, MaceKeyword.mace_opencl_mem_type) self.cl_mem_type = ocl_mem_type_arg.i if ocl_mem_type_arg is not None \ else None consumers = {} for op in net_def.op: if not self.op_need_optimize_memory(op): continue for ipt in op.input: if ipt not in consumers: consumers[ipt] = [] consumers[ipt].append(op) # only ref op's output tensor for op in net_def.op: if not self.op_need_optimize_memory(op): continue for output in op.output: tensor_name = output if tensor_name in consumers: self.input_ref_counter[tensor_name] = \ len(consumers[tensor_name]) else: self.input_ref_counter[tensor_name] = 0
def infer_shape_slice(self, op): output_shape = self._output_shape_cache[op.input[0]] axis = ConverterUtil.get_arg(op, MaceKeyword.mace_axis_str).i output_shape[axis] /= len(op.output) output_shapes = [] for _ in op.output: output_shapes.append(output_shape) self.add_output_shape(op, output_shapes)
def infer_shape_concat(self, op): output_shape = self._output_shape_cache[op.input[0]] axis = ConverterUtil.get_arg(op, MaceKeyword.mace_axis_str).i for input_node in op.input: input_shape = self._output_shape_cache[input_node] output_shape[axis] += input_shape[axis] self.add_output_shape(op, [output_shape])
def infer_shape_crop(self, op): mace_check(len(op.input) == 2, "crop layer needs two inputs") output_shape = self._output_shape_cache[op.input[0]] input1_shape = self._output_shape_cache[op.input[1]] offsets = ConverterUtil.get_arg(op, MaceKeyword.mace_offset_str).ints for i in range(len(offsets)): if offsets[i] >= 0: output_shape[i] = input1_shape[i] self.add_output_shape(op, [output_shape])
def infer_shape_concat(self, op): output_shape = list(self._output_shape_cache[op.input[0]]) axis = ConverterUtil.get_arg(op, MaceKeyword.mace_axis_str).i if axis < 0: axis = len(output_shape) + axis output_shape[axis] = 0 for input_node in op.input: input_shape = list(self._output_shape_cache[input_node]) output_shape[axis] = output_shape[axis] + input_shape[axis] self.add_output_shape(op, [output_shape])
def add_int_list_tensor_from_arg(self, op, keyword): list_value_arg = ConverterUtil.get_arg(op, keyword) mace_check(list_value_arg.ints is not None, op.name + ': ' + keyword + ' value ints should not be None') list_value_tensor = self._model.tensors.add() list_value_tensor.name = op.name + '/' + keyword + ':0' list_value_tensor.data_type = mace_pb2.DT_INT32 list_value_tensor.dims.extend([len(list_value_arg.ints)]) list_value_tensor.int32_data.extend(list_value_arg.ints) op.input.extend([list_value_tensor.name])
def add_int_tensor_from_arg(self, op, keyword): int_value_arg = ConverterUtil.get_arg(op, keyword) mace_check(int_value_arg.i is not None, op.name + ': ' + keyword + ' value i should not be None') int_value_tensor = self._model.tensors.add() int_value_tensor.name = op.name + '/' + keyword + ':0' int_value_tensor.data_type = mace_pb2.DT_INT32 int_value_tensor.dims.extend([1]) int_value_tensor.int32_data.extend([int_value_arg.i]) op.input.extend([int_value_tensor.name])
def add_size_tensor_from_arg(self, op, keyword): size_value_arg = ConverterUtil.get_arg(op, keyword) mace_check( len(size_value_arg.ints) == 2, op.name + ': ' + keyword + ' value does not have size 2') size_value_tensor = self._model.tensors.add() size_value_tensor.name = op.name + '/' + keyword + ':0' size_value_tensor.data_type = mace_pb2.DT_INT32 size_value_tensor.dims.extend([2]) size_value_tensor.int32_data.extend(size_value_arg.ints) op.input.extend([size_value_tensor.name])
def infer_shape_deconv(self, op): input_shape = self._output_shape_cache[op.input[0]] output_shape = np.zeros_like(input_shape) filter_shape = self._output_shape_cache[op.input[1]] paddings = ConverterUtil.get_arg(op, MaceKeyword.mace_padding_values_str).ints # noqa strides = ConverterUtil.get_arg(op, MaceKeyword.mace_strides_str).ints dilations_arg = ConverterUtil.get_arg(op, MaceKeyword.mace_dilations_str) if dilations_arg is not None: dilations = dilations_arg.ints else: dilations = [1, 1] round_func = math.floor group_arg = ConverterUtil.get_arg(op, MaceKeyword.mace_group_str) output_shape[0] = input_shape[0] if ConverterUtil.data_format(op) == DataFormat.NCHW \ and ConverterUtil.filter_format(self._net) == FilterFormat.OIHW: # noqa # filter format: IOHW output_shape[1] = filter_shape[1] if group_arg is not None and group_arg.i > 1: output_shape[1] = group_arg.i * filter_shape[1] output_shape[2] = int( round_func((input_shape[2] - 1) * strides[0] + (filter_shape[2] - 1) * (dilations[0] - 1) + filter_shape[2] - paddings[0])) output_shape[3] = int( round_func((input_shape[3] - 1) * strides[1] + (filter_shape[3] - 1) * (dilations[1] - 1) + filter_shape[3] - paddings[1])) else: mace_check(False, "Mace can only infer shape for" " NCHW input and OIHW filter") print("deconv layer %s (%s) input:%s filter:%s output:%s" % (op.name, op.type, input_shape, filter_shape, output_shape)) self.add_output_shape(op, [output_shape])
def add_padding_tensor_from_arg(self, op): padding_value_arg = ConverterUtil.get_arg( op, MaceKeyword.mace_padding_values_str) mace_check( len(padding_value_arg.ints) == 4, op.name + ': padding value does not have size 4') padding_value_tensor = self._model.tensors.add() padding_value_tensor.name = op.name + '/padding:0' padding_value_tensor.data_type = mace_pb2.DT_INT32 padding_value_tensor.dims.extend([4]) padding_value_tensor.int32_data.extend(padding_value_arg.ints) op.input.extend([padding_value_tensor.name])
def infer_shape_conv_pool_shape(self, op): input_shape = self._output_shape_cache[op.input[0]] output_shape = np.zeros_like(input_shape) if op.type == MaceOp.Pooling: filter_shape = list( ConverterUtil.get_arg(op, MaceKeyword.mace_kernel_str).ints) if ConverterUtil.data_format(op) == DataFormat.NCHW: filter_shape = [input_shape[1], input_shape[1]] + filter_shape if ConverterUtil.get_arg(op, MaceKeyword.mace_global_pooling_str) \ is not None: filter_shape[2] = input_shape[2] filter_shape[3] = input_shape[3] else: # NHWC filter_shape = filter_shape + [input_shape[1], input_shape[1]] if ConverterUtil.get_arg(op, MaceKeyword.mace_global_pooling_str) \ is not None: filter_shape[0] = input_shape[1] filter_shape[1] = input_shape[2] else: filter_shape = self._output_shape_cache[op.input[1]] paddings = ConverterUtil.get_arg(op, MaceKeyword.mace_padding_values_str).ints # noqa strides = ConverterUtil.get_arg(op, MaceKeyword.mace_strides_str).ints dilations_arg = ConverterUtil.get_arg(op, MaceKeyword.mace_dilations_str) if dilations_arg is not None: dilations = dilations_arg.ints else: dilations = [1, 1] if op.type == MaceOp.Pooling: round_func = math.ceil else: round_func = math.floor output_shape[0] = input_shape[0] if ConverterUtil.data_format(op) == DataFormat.NCHW \ and ConverterUtil.filter_format(self._net) == FilterFormat.OIHW: # noqa # filter format: OIHW if op.type == MaceOp.DepthwiseConv2d.name: output_shape[1] = filter_shape[0] * filter_shape[1] else: output_shape[1] = filter_shape[0] output_shape[2] = int( round_func((input_shape[2] + paddings[0] - filter_shape[2] - (filter_shape[2] - 1) * (dilations[0] - 1)) / float(strides[0]))) + 1 output_shape[3] = int( round_func((input_shape[3] + paddings[1] - filter_shape[3] - (filter_shape[3] - 1) * (dilations[1] - 1)) / float(strides[1]))) + 1 else: mace_check(False, "Mace can only infer shape for" " NCHW input and OIHW filter") self.add_output_shape(op, [output_shape])
def infer_shape_resize_bilinear(self, op): input_shape = self._output_shape_cache[op.input[0]] size = ConverterUtil.get_arg( op, MaceKeyword.mace_resize_size_str).ints if ConverterUtil.data_format(op) == DataFormat.NCHW: output_shape = [input_shape[0], input_shape[1], size[0], size[1]] elif ConverterUtil.data_format(op) == DataFormat.NHWC: output_shape = [input_shape[0], size[0], size[1], input_shape[3]] else: output_shape = [] mace_check(False, "format %s is not supported" % ConverterUtil.data_format(op)) self.add_output_shape(op, [output_shape])
def infer_shape_reshape(self, op): if ConverterUtil.get_arg(op, MaceKeyword.mace_dim_str) is not None: dim = ConverterUtil.get_arg(op, MaceKeyword.mace_dim_str).ints output_shape = list(dim) product = input_size = 1 idx = -1 for i in range(len(self._output_shape_cache[op.input[0]])): input_size *= self._output_shape_cache[op.input[0]][i] for i in range(len(dim)): if dim[i] == 0: output_shape[i] = self._output_shape_cache[op.input[0]][i] product *= self._output_shape_cache[op.input[0]][i] elif dim[i] == -1: idx = i output_shape[i] = 1 else: output_shape[i] = dim[i] product *= dim[i] if idx != -1: output_shape[idx] = input_size / product self.add_output_shape(op, [output_shape]) else: output_shape = [] axis = ConverterUtil.get_arg(op, MaceKeyword.mace_axis_str).i end_axis = ConverterUtil.get_arg( op, MaceKeyword.mace_end_axis_str).i # noqa end_axis = end_axis if end_axis > 0 else end_axis + len( list(self._output_shape_cache[op.input[0]])) dim = 1 for i in range(0, axis): output_shape.append(self._output_shape_cache[op.input[0]][i]) for i in range(axis, end_axis + 1): dim *= self._output_shape_cache[op.input[0]][i] output_shape.append(-1) for i in range(end_axis + 1, len(list(self._output_shape_cache[op.input[0]]))): output_shape.append(self._output_shape_cache[op.input[0]][i]) output_shape[axis] = dim self.add_output_shape(op, [output_shape])
def run(self): self.use_uint8_in_out() self.add_op_output_type() self.ensure_bias_vector() self.common_check() if ConverterUtil.get_arg(self._model.op[0], MaceKeyword.mace_framework_type_str).i == \ FrameworkType.TENSORFLOW.value: self.add_tensorflow_padding_value() const_data_num_arg = self._model.arg.add() const_data_num_arg.name = MaceKeyword.mace_const_data_num_arg_str const_data_num_arg.i = len(self._model.tensors) self.convert_ops() self.add_node_id() return self._model
def common_check(self): for op in self._model.op: mace_check( len(op.input) >= 1, op.name + ': apu does not support op with 0 input') mace_check( len(op.output) == 1, op.name + ': apu only support single output op') mace_check( len(op.output) == len(op.output_shape), op.name + ': length of output and output_shape not' ' match') mace_check( len(op.output_shape[0].dims) <= 4, op.name + ': apu only support 1D~4D tensor') mace_check( len(op.output) == len(op.quantize_info), op.name + ': length of output and quantize_info not' ' match') data_format = ConverterUtil.data_format(op) if data_format is not None and len(op.output_shape[0].dims) == 4: mace_check((data_format == DataFormat.NHWC) or (data_format == DataFormat.AUTO), op.name + ': apu only support 4D tensor with NHWC' ' or AUTO format but find ' + str(data_format)) act_mode_arg = ConverterUtil.get_arg( op, MaceKeyword.mace_activation_type_str) if act_mode_arg is not None: mace_check( act_mode_arg.s == b'RELU' or act_mode_arg.s == b'RELUX', op.name + ': apu only support activation RELU and' ' RELUX') for tensor in self._model.tensors: mace_check( len(tensor.dims) <= 4, tensor.name + ': apu only support 1D~4D tensor') for input_info in self._model.input_info: mace_check( len(input_info.dims) <= 4, input_info.name + ': apu only support 1D~4D tensor') mace_check(input_info.data_type == mace_pb2.DT_FLOAT, input_info.name + ': apu only support float input') if len(input_info.dims) == 4: mace_check( input_info.data_format == DataFormat.NHWC.value, input_info.name + ': apu only support 4D tensor' ' with NHWC format')
def main(unused_args): mace_check(os.path.isfile(FLAGS.model_file), "Input graph file '" + FLAGS.model_file + "' does not exist!") mace_check(os.path.isdir(FLAGS.output_dir), "Output directory '" + FLAGS.output_dir + "' does not exist!") net_def = mace_pb2.NetDef() with open(FLAGS.model_file, "rb") as f: net_def.ParseFromString(f.read()) quantize_flag = ConverterUtil.get_arg( net_def, MaceKeyword.mace_quantize_flag_arg_str) quantize_flag = False if quantize_flag is None else quantize_flag.i == 1 hexagon_flag = False index = 0 end_index = len(net_def.op) if quantize_flag: while index < end_index: # omit op quantize if net_def.op[index].type == MaceOp.Quantize.name or \ net_def.op[index].type == \ HexagonOp.QuantizeINPUT_f_to_8.name: index += 1 # omit op dequantize elif net_def.op[end_index - 1].type == MaceOp.Dequantize.name or \ net_def.op[end_index - 1].type == \ HexagonOp.DequantizeOUTPUT_8tof.name: end_index -= 1 else: break mace_check(0 < index < end_index < len(net_def.op), "Wrong number of op quantize(%d) or dequantize(%d)." % (index, len(net_def.op) - end_index)) if net_def.op[-1].type == HexagonOp.DequantizeOUTPUT_8tof.name: hexagon_flag = True # omit original output end_index -= 1 data_format = net_def.output_info[0].data_format output_configs = {"subgraphs": []} while index < end_index: # omit BatchToSpaceND and op before that due to changed graph if net_def.op[index].type == MaceOp.BatchToSpaceND.name or \ net_def.op[index].type == HexagonOp.BatchToSpaceND_8.name or \ (index + 1 < end_index and (net_def.op[index + 1].type == MaceOp.BatchToSpaceND.name or net_def.op[index + 1].type == HexagonOp.BatchToSpaceND_8.name)): # noqa index += 1 continue net = copy.deepcopy(net_def) if hexagon_flag: # reuse dequantize op and it's min/max tensor's node_id del net.op[index+1:end_index+1] else: del net.op[index+1:] del net.output_info[:] op = net.op[index] index += 1 output_tensors = [] output_shapes = [] op_name = op.name if quantize_flag: op.name = MaceKeyword.mace_output_node_name + '_' + op.name if hexagon_flag: mace_check(len(op.output) == 1, "Only supports number of outputs of Hexagon op be 1.") for i in range(len(op.output)): output_tensors.append(str(op.output[i])) output_shapes.append( ",".join([str(dim) for dim in op.output_shape[i].dims])) # modify output info output_info = net.output_info.add() output_info.name = op.output[i] output_info.data_format = data_format output_info.dims.extend(op.output_shape[i].dims) output_info.data_type = mace_pb2.DT_FLOAT # modify output op if quantize_flag: output_name = op.output[i] new_output_name = \ MaceKeyword.mace_output_node_name + '_' + op.output[i] op.output[i] = new_output_name if not hexagon_flag: dequantize_op = net.op.add() dequantize_op.name = normalize_op_name(output_name) dequantize_op.type = MaceOp.Dequantize.name dequantize_op.input.append(new_output_name) dequantize_op.output.append(output_name) output_shape = dequantize_op.output_shape.add() output_shape.dims.extend(op.output_shape[i].dims) dequantize_op.output_type.append(mace_pb2.DT_FLOAT) ConverterUtil.add_data_type_arg(dequantize_op, mace_pb2.DT_UINT8) else: dequantize_op = net.op[-1] dequantize_op.name = normalize_op_name(output_name) del dequantize_op.input[:] del dequantize_op.output[:] dequantize_op.input.append(new_output_name) dequantize_op.output.append(output_name) input_min = new_output_name[:-1] + '1' input_max = new_output_name[:-1] + '2' dequantize_op.input.extend([input_min, input_max]) dequantize_op.node_input[0].node_id = op.node_id dequantize_op.node_input[1].node_id = op.node_id dequantize_op.node_input[2].node_id = op.node_id model_path = save_model_to_proto(net, normalize_op_name(op_name), FLAGS.output_dir) output_config = {"model_file_path": str(model_path), "output_tensors": output_tensors, "output_shapes": output_shapes} output_configs["subgraphs"].append(output_config) output_configs_path = FLAGS.output_dir + "outputs.yml" with open(output_configs_path, "w") as f: yaml.dump(output_configs, f, default_flow_style=False)
def convert_ops(self): print("Convert mace graph to apu.") for op in self._model.op: if not self._apu_ops.has_op(op.type): raise Exception('Unsupported op: ', op) if op.type == MaceOp.Conv2D.name \ or op.type == MaceOp.DepthwiseConv2d.name: mace_check( len(op.input) == 3, op.name + ': apu only support ' + op.type + ' op' ' with 3 input') self.add_size_tensor_from_arg(op, MaceKeyword.mace_strides_str) self.add_padding_tensor_from_arg(op) self.add_size_tensor_from_arg(op, MaceKeyword.mace_dilations_str) if op.type == MaceOp.DepthwiseConv2d.name: multiplier = self._model.tensors.add() multiplier.name = op.name + '/multiplier:0' multiplier.data_type = mace_pb2.DT_INT32 multiplier.dims.extend([1]) for tensor in self._model.tensors: if tensor.name == op.input[1]: multiplier.int32_data.extend([tensor.dims[0]]) break op.input.extend([multiplier.name]) elif op.type == MaceOp.Eltwise.name: mace_check( len(op.input) == 2, op.name + ': apu only support eltwise op with 2' ' input') eltwise_type = ConverterUtil.get_arg( op, MaceKeyword.mace_element_type_str).i mace_check(eltwise_type == EltwiseType.SUM.value, op.name + ': apu only support eltwise type SUM') elif op.type == MaceOp.Pooling.name: mace_check( len(op.input) == 1, op.name + ': apu only support pooling op with 1' ' input') pooling_type_arg = ConverterUtil.get_arg( op, MaceKeyword.mace_pooling_type_str) mace_check( PoolingType(pooling_type_arg.i) == PoolingType.AVG, op.name + ': apu only support pooling type AVG') self.add_padding_tensor_from_arg(op) self.add_size_tensor_from_arg(op, MaceKeyword.mace_strides_str) self.add_size_tensor_from_arg(op, MaceKeyword.mace_kernel_str) elif op.type == MaceOp.Concat.name: self.add_int_tensor_from_arg(op, MaceKeyword.mace_axis_str) elif op.type == MaceOp.Reduce.name: mace_check( len(op.input) == 1, op.name + ': apu only support reduce op with 1' ' input') self.add_int_list_tensor_from_arg(op, MaceKeyword.mace_axis_str) self.add_int_tensor_from_arg(op, MaceKeyword.mace_keepdims_str) elif op.type == MaceOp.ResizeBilinear.name: mace_check( len(op.input) == 1, op.name + ': apu only support resize bilinear op' ' with 1 input') self.add_int_tensor_from_arg( op, MaceKeyword.mace_align_corners_str) elif op.type == MaceOp.Reshape.name: mace_check( len(op.input) == 1 or len(op.input) == 2, op.name + ': apu only support reshape op with 1 or' ' 2 input') elif op.type == MaceOp.Softmax.name: mace_check( len(op.input) == 1, op.name + ': apu only support softmax op with 1' ' input') beta_value_tensor = self._model.tensors.add() beta_value_tensor.name = op.name + '/beta:0' beta_value_tensor.data_type = mace_pb2.DT_FLOAT beta_value_tensor.dims.extend([1]) beta_value_tensor.float_data.extend([1.0]) op.input.extend([beta_value_tensor.name]) elif op.type == MaceOp.Squeeze.name: mace_check( len(op.input) == 1, op.name + ': apu only support squeeze op with 1' ' input') self.add_int_list_tensor_from_arg(op, MaceKeyword.mace_axis_str) op.type = self._apu_ops.map_nn_op(op.type)
def convert_ops(self): print("Convert mace graph to hexagon.") for op in self._model.op: if not self._hexagon_ops.has_op(op.type): raise Exception('Unsupported op: ', op) for i in range(len(op.input)): if ':' not in op.input[i]: node_name = op.input[i] op.input[i] += ':0' if node_name in self._quantize_activation_info: self._quantize_activation_info[op.input[i]] = \ self._quantize_activation_info[node_name] if op.type == MaceOp.Conv2D.name \ or op.type == MaceOp.DepthwiseConv2d.name: channels = op.output_shape[0].dims[3] if len(op.input) < 3: print('Supernode requires biasadd, we add it.') bias_data = np.zeros(channels, dtype=int) bias_tensor = self._model.tensors.add() bias_tensor.data_type = mace_pb2.DT_INT32 bias_tensor.dims.extend([channels]) bias_tensor.int32_data.extend(bias_data) bias_tensor.minval = 0 bias_tensor.maxval = 0 bias_tensor.name = op.name + "/bias:0" bias = bias_tensor.name self._consts[bias] = bias_tensor else: bias = op.input.pop() self.add_min_max_const_node(op, op.input[0]) self.add_min_max_const_node(op, op.input[1]) strides_arg = ConverterUtil.get_arg(op, 'strides') mace_check(strides_arg is not None, "Missing strides of Conv or Depthwise Conv.") strides = self.add_shape_const_node( op, [1, strides_arg.ints[0], strides_arg.ints[1], 1], MaceKeyword.mace_strides_str) op.input.extend([strides, bias]) self.add_min_max_const_node(op, bias) self.add_min_max_const_node(op, op.output[0], True, True, False) elif op.type == MaceOp.Eltwise.name: self.add_min_max_const_node(op, op.input[0]) self.add_min_max_const_node(op, op.input[1]) self.add_min_max_const_node(op, op.output[0], True, True, False) elif op.type == MaceOp.BatchToSpaceND.name \ or op.type == MaceOp.SpaceToBatchND.name: strides_arg = ConverterUtil.get_arg( op, MaceKeyword.mace_space_batch_block_shape_str) strides_tensor = self._model.tensors.add() strides_tensor.name = op.name + '/strides:0' strides_tensor.data_type = mace_pb2.DT_INT32 strides_tensor.dims.extend([1, 1, 1, len(strides_arg.ints)]) strides_tensor.int32_data.extend(strides_arg.ints) if op.type == MaceOp.BatchToSpaceND.name: pad_arg = ConverterUtil.get_arg( op, MaceKeyword.mace_batch_to_space_crops_str) else: pad_arg = ConverterUtil.get_arg( op, MaceKeyword.mace_paddings_str) pad_tensor = self._model.tensors.add() pad_tensor.name = op.name + '/pad:0' pad_tensor.data_type = mace_pb2.DT_INT32 pad_tensor.dims.extend([1, 1, len(pad_arg.ints) / 2, 2]) pad_tensor.int32_data.extend(pad_arg.ints) op.input.extend([strides_tensor.name, pad_tensor.name]) self.add_min_max_const_node(op, op.input[0]) elif op.type == MaceOp.Pooling.name: self.add_min_max_const_node(op, op.input[0]) window_arg = ConverterUtil.get_arg(op, MaceKeyword.mace_kernel_str) window_tensor = self._model.tensors.add() window_tensor.name = op.name + '/window:0' window_tensor.data_type = mace_pb2.DT_INT32 window_tensor.dims.extend( [1, window_arg.ints[0], window_arg.ints[1], 1]) strides_arg = ConverterUtil.get_arg( op, MaceKeyword.mace_strides_str) strides_tensor = self._model.tensors.add() strides_tensor.name = op.name + '/strides:0' strides_tensor.data_type = mace_pb2.DT_INT32 strides_tensor.dims.extend( [1, strides_arg.ints[0], strides_arg.ints[1], 1]) op.input.extend([window_tensor.name, strides_tensor.name]) elif op.type == MaceOp.Reduce.name: self.add_min_max_const_node(op, op.input[0]) reduce_type_arg = ConverterUtil.get_arg( op, MaceKeyword.mace_reduce_type_str) mace_check(reduce_type_arg.i == ReduceType.MEAN.value, "Hexagon Reduce only supports Mean now.") keep_dims_arg = ConverterUtil.get_arg( op, MaceKeyword.mace_keepdims_str) mace_check(keep_dims_arg.i == 1, "Hexagon Reduce Mean only supports keep dims now.") axis_arg = ConverterUtil.get_arg(op, MaceKeyword.mace_axis_str) mace_check(1 <= len(axis_arg.ints) <= 2, "Hexagon Reduce Mean only supports spatial now.") for i in axis_arg.ints: mace_check( 1 <= i <= 2, "Hexagon Reduce Mean only supports spatial now") producer_op_name, _ = get_op_and_port_from_tensor(op.input[0]) input_dims = None for producer_op in self._model.op: if producer_op.name == producer_op_name: input_dims = producer_op.output_shape[0].dims break mace_check(input_dims is not None, "Missing input shape.") window_tensor = self._model.tensors.add() window_tensor.name = op.name + '/window:0' window_tensor.data_type = mace_pb2.DT_INT32 if len(axis_arg.ints) == 1: dim1, dim2 = (input_dims[1], 1) \ if axis_arg.ints[0] == 1 else (1, input_dims[2]) else: dim1, dim2 = input_dims[1], input_dims[2] window_tensor.dims.extend([1, dim1, dim2, 1]) strides_tensor = self._model.tensors.add() strides_tensor.name = op.name + '/strides:0' strides_tensor.data_type = mace_pb2.DT_INT32 strides_tensor.dims.extend([1, dim1, dim2, 1]) op.input.extend([window_tensor.name, strides_tensor.name]) elif op.type == MaceOp.ResizeBilinear.name: newdim_arg = ConverterUtil.get_arg( op, MaceKeyword.mace_resize_size_str) newdim_tensor = self._model.tensors.add() newdim_tensor.name = op.name + '/newdim:0' newdim_tensor.data_type = mace_pb2.DT_INT32 newdim_tensor.dims.extend([len(newdim_arg.ints)]) newdim_tensor.int32_data.extend(newdim_arg.ints) op.input.extend([newdim_tensor.name]) self.add_min_max_const_node(op, op.input[0]) align_corners_arg = ConverterUtil.get_arg( op, MaceKeyword.mace_align_corners_str) align_corners_tensor = self._model.tensors.add() align_corners_tensor.name = op.name + '/align_corners:0' align_corners_tensor.data_type = mace_pb2.DT_INT32 align_corners_tensor.dims.extend([1]) align_corners_tensor.int32_data.extend([align_corners_arg.i]) op.input.extend([align_corners_tensor.name]) elif op.type == MaceOp.Concat.name: inputs = copy.deepcopy(op.input) for ipt in inputs: self.add_min_max_const_node(op, ipt, True, False) for ipt in inputs: self.add_min_max_const_node(op, ipt, False, True) dim_arg = ConverterUtil.get_arg(op, MaceKeyword.mace_axis_str) dim_tensor = self._model.tensors.add() dim_tensor.name = op.name + '/dim:0' dim_tensor.data_type = mace_pb2.DT_INT32 dim_tensor.dims.extend([1]) dim_tensor.int32_data.extend([dim_arg.i]) op.input.insert(0, dim_tensor.name) elif op.type in [MaceOp.Softmax.name, MaceOp.Dequantize.name]: self.add_min_max_const_node(op, op.input[0]) if op.type != MaceOp.Dequantize.name: min_output_shape = op.output_shape.add() min_output_shape.dims.extend([1]) max_output_shape = op.output_shape.add() max_output_shape.dims.extend([1]) op.output_type.extend( [mace_pb2.DT_UINT8, mace_pb2.DT_FLOAT, mace_pb2.DT_FLOAT]) for i in range(len(op.output_shape)): out_max_byte_size = reduce(mul, op.output_shape[i].dims) if op.output_type[i] == mace_pb2.DT_FLOAT: out_max_byte_size *= 4 op.out_max_byte_size.extend([out_max_byte_size]) op.padding = padding_mode[PaddingMode.NA] arg = ConverterUtil.get_arg(op, MaceKeyword.mace_padding_str) if arg is not None: op.padding = padding_mode[PaddingMode(arg.i)] if (op.type == MaceOp.Eltwise.name and ConverterUtil.get_arg( op, MaceKeyword.mace_element_type_str).i == EltwiseType.SUM.value): op.type = HexagonOp.QuantizedAdd_8p8to8.name elif op.type == MaceOp.Pooling.name: pooling_type_arg = ConverterUtil.get_arg( op, MaceKeyword.mace_pooling_type_str) if PoolingType(pooling_type_arg.i) == PoolingType.AVG: op.type = HexagonOp.QuantizedAvgPool_8.name else: op.type = HexagonOp.QuantizedMaxPool_8.name else: op.type = self._hexagon_ops.map_nn_op(op.type)
def infer_shape_permute(self, op): output_shape = list(self._output_shape_cache[op.input[0]]) dims = ConverterUtil.get_arg(op, MaceKeyword.mace_dims_str).ints for i in xrange(len(dims)): output_shape[i] = self._output_shape_cache[op.input[0]][dims[i]] self.add_output_shape(op, [output_shape])
def convert_ops(self): print("Convert mace graph to hexagon.") for op in self._model.op: if not self._hexagon_ops.has_op(op.type): raise Exception('Unsupported op: ', op) print('Op: ', op.name, op.type) for i in range(len(op.input)): if ':' not in op.input[i]: node_name = op.input[i] op.input[i] += ':0' if node_name in self._quantize_activation_info: self._quantize_activation_info[op.input[i]] = \ self._quantize_activation_info[node_name] if op.type == MaceOp.Conv2D.name \ or op.type == MaceOp.DepthwiseConv2d.name: mace_check( len(op.input) == 3, "Missing bias of Conv or Depthwise Conv.") bias = op.input.pop() self.add_min_max_const_node(op, op.input[0]) self.add_min_max_const_node(op, op.input[1]) strides_arg = ConverterUtil.get_arg(op, 'strides') mace_check(strides_arg is not None, "Missing strides of Conv or Depthwise Conv.") strides = self.add_shape_const_node( op, [1, strides_arg.ints[0], strides_arg.ints[1], 1], MaceKeyword.mace_strides_str) op.input.extend([strides, bias]) self.add_min_max_const_node(op, bias) self.add_min_max_const_node(op, op.output[0], True, True, False) elif op.type == MaceOp.Eltwise.name: self.add_min_max_const_node(op, op.input[0]) self.add_min_max_const_node(op, op.input[1]) self.add_min_max_const_node(op, op.output[0], True, True, False) elif op.type == MaceOp.BatchToSpaceND.name \ or op.type == MaceOp.SpaceToBatchND.name: strides_arg = ConverterUtil.get_arg( op, MaceKeyword.mace_space_batch_block_shape_str) strides_tensor = self._model.tensors.add() strides_tensor.name = op.name + '/strides:0' strides_tensor.data_type = mace_pb2.DT_INT32 strides_tensor.dims.extend([1, 1, 1, len(strides_arg.ints)]) strides_tensor.int32_data.extend(strides_arg.ints) if op.type == MaceOp.BatchToSpaceND.name: pad_arg = ConverterUtil.get_arg( op, MaceKeyword.mace_batch_to_space_crops_str) else: pad_arg = ConverterUtil.get_arg( op, MaceKeyword.mace_paddings_str) pad_tensor = self._model.tensors.add() pad_tensor.name = op.name + '/pad:0' pad_tensor.data_type = mace_pb2.DT_INT32 pad_tensor.dims.extend([1, 1, len(pad_arg.ints) / 2, 2]) pad_tensor.int32_data.extend(pad_arg.ints) op.input.extend([strides_tensor.name, pad_tensor.name]) self.add_min_max_const_node(op, op.input[0]) elif op.type == MaceOp.Pooling.name: self.add_min_max_const_node(op, op.input[0]) window_arg = ConverterUtil.get_arg(op, MaceKeyword.mace_kernel_str) window_tensor = self._model.tensors.add() window_tensor.name = op.name + '/window:0' window_tensor.data_type = mace_pb2.DT_INT32 window_tensor.dims.extend( [1, window_arg.ints[0], window_arg.ints[1], 1]) strides_arg = ConverterUtil.get_arg( op, MaceKeyword.mace_strides_str) strides_tensor = self._model.tensors.add() strides_tensor.name = op.name + '/strides:0' strides_tensor.data_type = mace_pb2.DT_INT32 strides_tensor.dims.extend( [1, strides_arg.ints[0], strides_arg.ints[1], 1]) op.input.extend([window_tensor.name, strides_tensor.name]) elif op.type == MaceOp.ResizeBilinear.name: newdim_arg = ConverterUtil.get_arg( op, MaceKeyword.mace_resize_size_str) newdim_tensor = self._model.tensors.add() newdim_tensor.name = op.name + '/newdim:0' newdim_tensor.data_type = mace_pb2.DT_INT32 newdim_tensor.dims.extend([len(newdim_arg.ints)]) newdim_tensor.int32_data.extend(newdim_arg.ints) op.input.extend([newdim_tensor.name]) self.add_min_max_const_node(op, op.input[0]) elif op.type == MaceOp.Concat.name: inputs = copy.deepcopy(op.input) for ipt in inputs: self.add_min_max_const_node(op, ipt, True, False) for ipt in inputs: self.add_min_max_const_node(op, ipt, False, True) dim_arg = ConverterUtil.get_arg(op, MaceKeyword.mace_axis_str) dim_tensor = self._model.tensors.add() dim_tensor.name = op.name + '/dim:0' dim_tensor.data_type = mace_pb2.DT_INT32 dim_tensor.dims.extend([1]) dim_tensor.int32_data.extend([dim_arg.i]) op.input.insert(0, dim_tensor.name) elif op.type in [MaceOp.Softmax.name, MaceOp.Dequantize.name]: self.add_min_max_const_node(op, op.input[0]) if op.type != MaceOp.Dequantize.name: min_output_shape = op.output_shape.add() min_output_shape.dims.extend([1]) max_output_shape = op.output_shape.add() max_output_shape.dims.extend([1]) op.output_type.extend( [mace_pb2.DT_UINT8, mace_pb2.DT_FLOAT, mace_pb2.DT_FLOAT]) for i in range(len(op.output_shape)): out_max_byte_size = reduce(mul, op.output_shape[i].dims) if op.output_type[i] == mace_pb2.DT_FLOAT: out_max_byte_size *= 4 op.out_max_byte_size.extend([out_max_byte_size]) op.padding = padding_mode[PaddingMode.NA] arg = ConverterUtil.get_arg(op, MaceKeyword.mace_padding_str) if arg is not None: op.padding = padding_mode[PaddingMode(arg.i)] if (op.type == MaceOp.Eltwise.name and ConverterUtil.get_arg( op, MaceKeyword.mace_element_type_str).i == EltwiseType.SUM.value): op.type = 'QuantizedAdd_8p8to8' elif op.type == MaceOp.Pooling.name: pooling_type_arg = ConverterUtil.get_arg( op, MaceKeyword.mace_pooling_type_str) if PoolingType(pooling_type_arg.i) == PoolingType.AVG: op.type = 'QuantizedAvgPool_8' else: op.type = 'QuantizedMaxPool_8' else: op.type = self._hexagon_ops.map_nn_op(op.type)