Skip to content
Snippets Groups Projects
Commit f2fdb01d authored by shingjan's avatar shingjan
Browse files

add instruction for running onnx frontend

parent 8ba057dd
No related branches found
No related tags found
No related merge requests found
### 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
model_name = "alexnet" model_name = "alexnet"
input_size = [1,2,3,4] input_size = [1,2,3,4]
onnx_file_dir = "../models/keras/alexnet.onnx" onnx_file_dir = "../models/keras/alexnet.onnx"
opset_version_default = 11 opset_version_default = 10
src_emit_dir = "./test_src" src_emit_dir = "./test_src"
...@@ -25,118 +25,9 @@ class GraphCodeGen(object): ...@@ -25,118 +25,9 @@ class GraphCodeGen(object):
self.var_cnt = self.var_cnt + 1 self.var_cnt = self.var_cnt + 1
return "var_" + str(self.var_cnt) 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 # CodeGen functions
################################################ ################################################
def emit_weights(self): def emit_weights(self):
weights_str = "" weights_str = ""
weights_str += "std::string dir_prefix = std::string(\"" + str(self.weights_dir) + "\");\n" weights_str += "std::string dir_prefix = std::string(\"" + str(self.weights_dir) + "\");\n"
...@@ -178,10 +69,6 @@ class GraphCodeGen(object): ...@@ -178,10 +69,6 @@ class GraphCodeGen(object):
self.tensors[cur_node.output[0]].set_mapped_name(mapped_output_name) self.tensors[cur_node.output[0]].set_mapped_name(mapped_output_name)
self.program_str += node.codegen(self.tensors) 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): def emit_header(self):
headers = "\n#include <stdio.h> \n" headers = "\n#include <stdio.h> \n"
headers += "#include <stdlib.h> \n" headers += "#include <stdlib.h> \n"
...@@ -201,10 +88,6 @@ class GraphCodeGen(object): ...@@ -201,10 +88,6 @@ class GraphCodeGen(object):
self.program_str += initialization self.program_str += initialization
def emit_footer(self, test_data=None): 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" destructors = "\nllvm_hpvm_cleanupTensorRt(); \n"
end_main = "\nreturn 0; \n\n}\n" end_main = "\nreturn 0; \n\n}\n"
self.program_str += destructors self.program_str += destructors
...@@ -237,12 +120,10 @@ class GraphCodeGen(object): ...@@ -237,12 +120,10 @@ class GraphCodeGen(object):
def emit_batch_loop_end(self): def emit_batch_loop_end(self):
end_loop_str = "" end_loop_str = ""
end_loop_str += "\nuint32_t* labels = readLabelsBatch3(labels_path.c_str(),start,end); \n" 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() mapped_output_var = self.tensors[self.graph.output[0].name].get_mapped_name()
accuracy_call = "\nfloat accuracy = computeAccuracy3(labels, " + \ accuracy_call = "\nfloat accuracy = computeAccuracy3(labels, " + \
mapped_output_var + "); \n" mapped_output_var + "); \n"
end_loop_str += accuracy_call 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 += "final_accuracy += accuracy; \n"
end_loop_str += "freeBatchMemory(); \n " end_loop_str += "freeBatchMemory(); \n "
end_loop_str += "\n}\n\n" end_loop_str += "\n}\n\n"
......
from tensor import WeightTensor from tensor import WeightTensor
from utils import skip_layer
skip_layer = ["Identity", "Flatten", "Pad"]
class HpvmCodeGen: 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.program_str = ""
self.graph = DFG.graph self.graph = DFG.graph
self.tensors = DFG.tensors self.tensors = DFG.tensors
self.nodes = DFG.nodes self.nodes = DFG.nodes
self.var_cnt = 0 self.var_cnt = 0
self.weights_dir = weights_dir self.weights_dir = weights_dir
self.test_data = test_data self.test_data_shape = test_data_shape
self.test_labels = test_labels self.filter_names = dict() # essentially weight tensors
self.filter_names = dict() # essentially tensors
for tensor in self.tensors.values(): for tensor in self.tensors.values():
if isinstance(tensor, WeightTensor): if isinstance(tensor, WeightTensor):
self.filter_names[tensor.get_mapped_name()] = 1 self.filter_names[tensor.get_mapped_name()] = 1
......
...@@ -6,7 +6,7 @@ import glob ...@@ -6,7 +6,7 @@ import glob
def check_version(model, new_version): def check_version(model, new_version):
try: 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: except AttributeError:
opset = 1 # default opset version set to 1 if not specified opset = 1 # default opset version set to 1 if not specified
print("opset version: ", opset) print("opset version: ", opset)
......
...@@ -90,18 +90,20 @@ def dumpConvWeights(file_name, weights): ...@@ -90,18 +90,20 @@ def dumpConvWeights(file_name, weights):
print ("*DumpConvWeights") print ("*DumpConvWeights")
print("-min_val = ", np.amin(weights)) print("-min_val = ", np.amin(weights))
print("-max_val = ", np.amax(weights)) print("-max_val = ", np.amax(weights))
weights_keras = np.einsum('NCWH->WHCN', weights)
N = weights.shape[0] print ("Convert shape for conv weights: " + str(weights_keras.shape))
C = weights.shape[1] N = weights_keras.shape[3]
H = weights.shape[2] C = weights_keras.shape[2]
W = weights.shape[3] H = weights_keras.shape[1]
W = weights_keras.shape[0]
f = open(file_name, "wb") f = open(file_name, "wb")
for i in range(N): for i in range(N):
for j in range(C): for j in range(C):
for k in range(H): for k in range(H):
for l in range(W): 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() f.close()
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment