From f2fdb01d050650b4074ed2869e311ea8021ab9aa Mon Sep 17 00:00:00 2001 From: shingjan <yjshi03@gmail.com> Date: Sun, 7 Jun 2020 00:55:21 -0500 Subject: [PATCH] add instruction for running onnx frontend --- hpvm/projects/onnx/frontend/README.md | 10 ++ hpvm/projects/onnx/frontend/config.py | 2 +- hpvm/projects/onnx/frontend/graph_codegen.py | 119 ------------------- hpvm/projects/onnx/frontend/hpvm_codegen.py | 10 +- hpvm/projects/onnx/frontend/main.py | 2 +- hpvm/projects/onnx/frontend/utils.py | 16 +-- 6 files changed, 25 insertions(+), 134 deletions(-) create mode 100644 hpvm/projects/onnx/frontend/README.md diff --git a/hpvm/projects/onnx/frontend/README.md b/hpvm/projects/onnx/frontend/README.md new file mode 100644 index 0000000000..69ffe81f5a --- /dev/null +++ b/hpvm/projects/onnx/frontend/README.md @@ -0,0 +1,10 @@ +### How to Run +``` +python main.py +``` +Set all your config, e.g. onnx model location, input size and emit directory for generated source code, in **config.py**. + +### Resources +1. [ONNX overview](https://github.com/onnx/onnx/blob/master/docs/IR.md) +2. [ONNX operator specs](https://github.com/onnx/onnx/blob/master/docs/Operators.md) +3. [Conversion between models - available adapters](https://github.com/onnx/onnx/blob/master/onnx/version_converter.py#L21) \ No newline at end of file diff --git a/hpvm/projects/onnx/frontend/config.py b/hpvm/projects/onnx/frontend/config.py index c8053b8d98..55ae0cebf4 100644 --- a/hpvm/projects/onnx/frontend/config.py +++ b/hpvm/projects/onnx/frontend/config.py @@ -1,6 +1,6 @@ model_name = "alexnet" input_size = [1,2,3,4] onnx_file_dir = "../models/keras/alexnet.onnx" -opset_version_default = 11 +opset_version_default = 10 src_emit_dir = "./test_src" diff --git a/hpvm/projects/onnx/frontend/graph_codegen.py b/hpvm/projects/onnx/frontend/graph_codegen.py index f47abfefab..df02685386 100644 --- a/hpvm/projects/onnx/frontend/graph_codegen.py +++ b/hpvm/projects/onnx/frontend/graph_codegen.py @@ -25,118 +25,9 @@ class GraphCodeGen(object): self.var_cnt = self.var_cnt + 1 return "var_" + str(self.var_cnt) - def emit_node_call(self, cur_node): - inst_str = "" - # check if all inputs of this node is mapped - for i in cur_node.input: - self.tensors[i].get_mapped_name() - # set var name for output node - if len(cur_node.output) > 1: - raise ValueError("Output number for a single layer larger than 1!") - if cur_node.op_type in skip_layer: - mapped_output_name = self.get_last_var() - else: - mapped_output_name = self.get_new_var() - output_name = cur_node.output[0] - self.tensors[output_name].set_mapped_name(mapped_output_name) - #print(cur_node) - if cur_node.op_type == "Conv":# or cur_node.op_type == "DepthwiseConv": - input_var_name = self.tensors[cur_node.input[0]].get_mapped_name() - weight = cur_node.input[1] - strides = list() - padding = 0 - for attr in cur_node.attribute: - if attr.name == "pads": - padding = attr.ints[0] - elif attr.name == "strides": - for stride in attr.ints: - strides.append(stride) - - inst_str += "void* " + mapped_output_name + " = " - inst_str += "tensorConvolution(" + input_var_name + ", " - inst_str += self.tensors[cur_node.input[1]].get_mapped_name() + ", " - inst_str += str(padding) + ", " - inst_str += str(padding) + ", " - inst_str += str(strides[0]) + ", " - inst_str += str(strides[1]) + ", " - inst_str += "1, 1); \n" - - # check if has bias add (Optional) - # in ONNX it is only in Conv - # in Keras bias add could exist in Dense - if len(cur_node.input) == 3: - mapped_output_name2 = self.get_new_var() - inst_str += "void* " + mapped_output_name2 + " = " - inst_str += "tensorAdd(" + mapped_output_name + ", " - inst_str += self.tensors[cur_node.input[2]].get_mapped_name() + "" - inst_str += "); \n" - self.tensors[output_name].set_mapped_name(mapped_output_name2) - elif cur_node.op_type == "MaxPool" or cur_node.op_type == "AveragePool": - input_var_name = self.tensors[cur_node.input[0]].get_mapped_name() - strides = list() - pool_size = list() - for attr in cur_node.attribute: - if attr.name == "kernel_shape": - for pool in attr.ints: - pool_size.append(pool) - elif attr.name == "strides": - for stride in attr.ints: - strides.append(stride) - # FIXME: Non-same padding is *NOT* currently supported - padding = 0 - pool_type = 0 - if cur_node.op_type == "MaxPool": - pool_type = "0" - elif cur_node.op_type == "AveragePool": - pool_type = "1" - # tensorPooling(input, pool_type, pool_h, pool_w, v_pad, h_pad, v_stride, h_stride) - inst_str += "void* " + mapped_output_name + " = " - inst_str += "tensorPooling(" + input_var_name + "," + pool_type + "," + str(pool_size[0]) + "," + str(pool_size[1]) - inst_str += "," + str(padding) + "," + str(padding) + "," + str(strides[0]) + "," + str(strides[1]) - inst_str += "); \n" - # self.program_str += inst_str - elif cur_node.op_type == "MatMul": - left_input = self.tensors[cur_node.input[0]].get_mapped_name() - right_input = self.tensors[cur_node.input[1]].get_mapped_name() - inst_str += "void* " + mapped_output_name + " = " - inst_str += "tensorGemmGPU(" + left_input + ", " + right_input + "); \n" - elif cur_node.op_type == "Add": - left_input = self.tensors[cur_node.input[0]].get_mapped_name() - right_input = self.tensors[cur_node.input[1]].get_mapped_name() - inst_str += "void* " + mapped_output_name + " = " - inst_str += "tensorAdd(" + left_input + ", " + right_input + "); \n" - elif cur_node.op_type == "Softmax": - mapped_input_name = self.tensors[cur_node.input[0]].get_mapped_name() - inst_str += "void* " + mapped_output_name + " = " - inst_str += "tensorSoftmax(" + mapped_input_name + "); \n" - elif cur_node.op_type == "Relu": - mapped_input_name = self.tensors[cur_node.input[0]].get_mapped_name() - inst_str += "void* " + mapped_output_name + " = " - inst_str += "tensorRelu(" + mapped_input_name + "); \n" - elif cur_node.op_type == "BatchNormalization": - mapped_input_name = self.tensors[cur_node.input[0]].get_mapped_name() - epsilon = "" - for attr in cur_node.attribute: - if attr.name == "epsilon": - epsilon = str(attr.f) - inst_str += "void* " + mapped_output_name + " = " - inst_str += "tensorBatchNorm(" + mapped_input_name + ", " - inst_str += self.tensors[cur_node.input[1]].get_mapped_name() + ", " - inst_str += self.tensors[cur_node.input[2]].get_mapped_name() + ", " - inst_str += self.tensors[cur_node.input[3]].get_mapped_name() + ", " - inst_str += self.tensors[cur_node.input[4]].get_mapped_name() + ", " - inst_str += str(epsilon) - inst_str += "); \n" - elif cur_node.op_type in skip_layer: - pass - else: - raise ValueError("Not supported op type:" + cur_node.op_type + "! \n") - return inst_str - ################################################ # CodeGen functions ################################################ - def emit_weights(self): weights_str = "" weights_str += "std::string dir_prefix = std::string(\"" + str(self.weights_dir) + "\");\n" @@ -178,10 +69,6 @@ class GraphCodeGen(object): self.tensors[cur_node.output[0]].set_mapped_name(mapped_output_name) self.program_str += node.codegen(self.tensors) - def emit_graph2(self): - for node in self.graph.node: - self.program_str += self.emit_node_call(node) - def emit_header(self): headers = "\n#include <stdio.h> \n" headers += "#include <stdlib.h> \n" @@ -201,10 +88,6 @@ class GraphCodeGen(object): self.program_str += initialization def emit_footer(self, test_data=None): - #if test_data is not None and self.dfg.last_node is not None: - #last_node = self.dfg.last_node - #output_var = self.output_map[last_node.layer_name] - destructors = "\nllvm_hpvm_cleanupTensorRt(); \n" end_main = "\nreturn 0; \n\n}\n" self.program_str += destructors @@ -237,12 +120,10 @@ class GraphCodeGen(object): def emit_batch_loop_end(self): end_loop_str = "" end_loop_str += "\nuint32_t* labels = readLabelsBatch3(labels_path.c_str(),start,end); \n" - #last_node = self.dfg.last_node mapped_output_var = self.tensors[self.graph.output[0].name].get_mapped_name() accuracy_call = "\nfloat accuracy = computeAccuracy3(labels, " + \ mapped_output_var + "); \n" end_loop_str += accuracy_call - #end_loop_str += "float accuracy = computeAccuracy2(labels, batch_size, var_60); " end_loop_str += "final_accuracy += accuracy; \n" end_loop_str += "freeBatchMemory(); \n " end_loop_str += "\n}\n\n" diff --git a/hpvm/projects/onnx/frontend/hpvm_codegen.py b/hpvm/projects/onnx/frontend/hpvm_codegen.py index 48cfaec36b..2446e0747f 100644 --- a/hpvm/projects/onnx/frontend/hpvm_codegen.py +++ b/hpvm/projects/onnx/frontend/hpvm_codegen.py @@ -1,18 +1,16 @@ from tensor import WeightTensor - -skip_layer = ["Identity", "Flatten", "Pad"] +from utils import skip_layer class HpvmCodeGen: - def __init__(self, DFG, weights_dir, test_data=None, test_labels=None): + def __init__(self, DFG, weights_dir, test_data_shape=None): self.program_str = "" self.graph = DFG.graph self.tensors = DFG.tensors self.nodes = DFG.nodes self.var_cnt = 0 self.weights_dir = weights_dir - self.test_data = test_data - self.test_labels = test_labels - self.filter_names = dict() # essentially tensors + self.test_data_shape = test_data_shape + self.filter_names = dict() # essentially weight tensors for tensor in self.tensors.values(): if isinstance(tensor, WeightTensor): self.filter_names[tensor.get_mapped_name()] = 1 diff --git a/hpvm/projects/onnx/frontend/main.py b/hpvm/projects/onnx/frontend/main.py index 6014569acd..b1dba8d21f 100644 --- a/hpvm/projects/onnx/frontend/main.py +++ b/hpvm/projects/onnx/frontend/main.py @@ -6,7 +6,7 @@ import glob def check_version(model, new_version): try: - opset = model.opset_import[0].version + 1 if model.opset_import else 1 + opset = model.opset_import[0].version if model.opset_import else 1 except AttributeError: opset = 1 # default opset version set to 1 if not specified print("opset version: ", opset) diff --git a/hpvm/projects/onnx/frontend/utils.py b/hpvm/projects/onnx/frontend/utils.py index fa0d9de28f..2a43557958 100644 --- a/hpvm/projects/onnx/frontend/utils.py +++ b/hpvm/projects/onnx/frontend/utils.py @@ -90,18 +90,20 @@ def dumpConvWeights(file_name, weights): print ("*DumpConvWeights") print("-min_val = ", np.amin(weights)) print("-max_val = ", np.amax(weights)) - - N = weights.shape[0] - C = weights.shape[1] - H = weights.shape[2] - W = weights.shape[3] - + weights_keras = np.einsum('NCWH->WHCN', weights) + print ("Convert shape for conv weights: " + str(weights_keras.shape)) + N = weights_keras.shape[3] + C = weights_keras.shape[2] + H = weights_keras.shape[1] + W = weights_keras.shape[0] f = open(file_name, "wb") for i in range(N): for j in range(C): for k in range(H): for l in range(W): - f.write(weights[i][j][k][l]) + # FIXME: Legacy code from Keras frontend + # should actually interchange k with l + f.write(weights_keras[k][l][j][i]) f.close() -- GitLab