Skip to content

Commit e934855

Browse files
author
Alex
committed
Merge branch 'feature/onnx_upload' into develop
2 parents 46912b7 + d7027b9 commit e934855

26 files changed

+4678
-5738
lines changed

backend/requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,3 +65,4 @@ websocket-client==0.58.0
6565
Werkzeug==1.0.1
6666
wrapt==1.12.1
6767
zipp==3.4.1
68+
onnx==1.10.1

backend/server.py

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,11 @@
77
import cairosvg
88
from flask import Flask, send_file, request, jsonify
99
from translate.translate_keras import translate_keras
10+
from translate.translate_onnx import translate_onnx
1011
from translate.graph import Graph
1112

1213
app = Flask(__name__)
13-
app.config['UPLOAD_EXTENSIONS'] = ['.h5']
14+
app.config['UPLOAD_EXTENSIONS'] = ['.h5', '.onnx']
1415
app.config['UPLOAD_PATH'] = 'models'
1516
ok_status = 200
1617
error_status = 400
@@ -168,6 +169,9 @@ def get_network(identifier):
168169
if os.path.exists(os.path.join(app.config['UPLOAD_PATH'], identifier, 'model.h5')):
169170
graph = translate_keras(os.path.join(
170171
app.config['UPLOAD_PATH'], identifier, 'model.h5'))
172+
elif os.path.exists(os.path.join(app.config['UPLOAD_PATH'], identifier, 'model.onnx')):
173+
graph = translate_onnx(os.path.join(
174+
app.config['UPLOAD_PATH'], identifier, 'model.onnx'))
171175
else:
172176
graph = translate_keras(os.path.join(
173177
app.config['UPLOAD_PATH'], identifier, 'model_current.py'))
@@ -190,7 +194,7 @@ def get_code(identifier):
190194
object -- a http response containing the code for the network
191195
"""
192196
check_upload_path_exists(identifier)
193-
if not (os.path.exists(os.path.join(app.config['UPLOAD_PATH'], identifier, 'model.h5'))):
197+
if not (os.path.exists(os.path.join(app.config['UPLOAD_PATH'], identifier, 'model.h5')) or os.path.exists(os.path.join(app.config['UPLOAD_PATH'], identifier, 'model.onnx'))):
194198
with open(os.path.join(app.config['UPLOAD_PATH'], identifier, 'model_current.py'), 'r') as myfile:
195199
keras_code = myfile.read()
196200
return keras_code, ok_status, text_type
@@ -231,7 +235,8 @@ def upload_model(identifier):
231235
file_ext = os.path.splitext(filename)[1]
232236
if file_ext not in app.config['UPLOAD_EXTENSIONS']:
233237
return "", error_status, text_type
234-
file_path = os.path.join(app.config['UPLOAD_PATH'], identifier, 'model.h5')
238+
file_path = os.path.join(
239+
app.config['UPLOAD_PATH'], identifier, f'model{file_ext}')
235240
uploaded_file.save(file_path)
236241
return "No code loaded since model file is present.", ok_status, text_type
237242

@@ -247,12 +252,14 @@ def delete_model(identifier):
247252
object -- a http response signaling the change worked
248253
"""
249254
check_upload_path_exists(identifier)
250-
model_path = os.path.join(
255+
keras_model_path = os.path.join(
251256
app.config['UPLOAD_PATH'], identifier, 'model.h5')
252-
print(model_path)
253-
if (os.path.exists(model_path)):
254-
print('test2')
255-
os.remove(model_path)
257+
onnx_model_path = os.path.join(
258+
app.config['UPLOAD_PATH'], identifier, 'model.onnx')
259+
if (os.path.exists(keras_model_path)):
260+
os.remove(keras_model_path)
261+
if (os.path.exists(onnx_model_path)):
262+
os.remove(onnx_model_path)
256263
with open(os.path.join(app.config['UPLOAD_PATH'], identifier, 'model_current.py'), 'r') as myfile:
257264
keras_code = myfile.read()
258265
return keras_code, ok_status, text_type

backend/translate/layer.py

Lines changed: 80 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,52 +1,82 @@
11
"""Used to handle layers as objects."""
2+
3+
24
class Layer:
3-
"""Representation of Layers."""
4-
# Initialize the Properties.
5-
def __init__(self, class_name, name, layer):
6-
self.properties = {}
7-
self.input = []
8-
self.input_names = []
9-
self.output = []
10-
self.name = name
11-
self.type = class_name
12-
self.dimensions = {
13-
'in': 0,
14-
'out': layer.get_output_at(0).get_shape().as_list()[1:]
15-
}
16-
if isinstance(layer.get_input_at(0), list):
17-
self.dimensions['in'] = layer.get_input_at(0)[0].get_shape().as_list()[1:]
18-
else:
19-
self.dimensions['in'] = layer.get_input_at(0).get_shape().as_list()[1:]
20-
if isinstance(layer.get_output_at(0), list):
21-
self.dimensions['out'] = layer.get_output_at(0)[0].get_shape().as_list()[1:]
22-
else:
23-
self.dimensions['out'] = layer.get_output_at(0).get_shape().as_list()[1:]
24-
25-
26-
def add_specs(self, specs):
27-
"""Add Specifications.
28-
29-
Arguments:
30-
specs {list} -- the specifications to be added to the layer
31-
"""
32-
# Update Properties based on Specs. Try to Parse the Specs.
33-
for prop in specs:
34-
if isinstance(specs[prop], dict):
35-
self.properties[prop] = specs[prop]['class_name']
36-
else:
37-
self.properties[prop] = specs[prop]
38-
39-
40-
def add_input_names(self, nodes):
41-
"""Adds all the Names of the Input Nodes to the Layer.
42-
43-
Arguments:
44-
nodes {list} -- all nodes that are inputs to the layer
45-
"""
46-
if (len(nodes) > 0):
47-
for node in nodes[0]:
48-
self.input_names.append(node[0])
49-
50-
# String Representation of the Layer.
51-
def __repr__(self):
52-
return "%s(properties: %r)" % (self.__class__, self.properties)
5+
"""Representation of Layers."""
6+
# Initialize the Properties.
7+
8+
def __init__(self, class_name, name, dimensions):
9+
self.properties = {}
10+
self.input = []
11+
self.input_names = []
12+
self.output = []
13+
self.name = name
14+
self.type = class_name
15+
self.dimensions = dimensions
16+
17+
@classmethod
18+
def from_keras(cls, class_name, name, layer):
19+
dimensions = {
20+
'in': 0,
21+
'out': layer.get_output_at(0).get_shape().as_list()[1:]
22+
}
23+
if isinstance(layer.get_input_at(0), list):
24+
dimensions['in'] = layer.get_input_at(
25+
0)[0].get_shape().as_list()[1:]
26+
else:
27+
dimensions['in'] = layer.get_input_at(0).get_shape().as_list()[1:]
28+
if isinstance(layer.get_output_at(0), list):
29+
dimensions['out'] = layer.get_output_at(
30+
0)[0].get_shape().as_list()[1:]
31+
else:
32+
dimensions['out'] = layer.get_output_at(
33+
0).get_shape().as_list()[1:]
34+
return cls(class_name, name, dimensions)
35+
36+
@classmethod
37+
def from_onnx(cls, layer, onnx_graph):
38+
dimensions = {
39+
'in': 0,
40+
'out': 0
41+
}
42+
if len(layer.input) > 0:
43+
for value_info in onnx_graph.value_info:
44+
if (layer.input[0] == value_info.name):
45+
dimensions['in'] = list(
46+
map(lambda x: x.dim_value, value_info.type.tensor_type.shape.dim[1:]))
47+
if (layer.output[0] == value_info.name):
48+
dimensions['out'] = list(
49+
map(lambda x: x.dim_value, value_info.type.tensor_type.shape.dim[1:]))
50+
for value_info in onnx_graph.input:
51+
if (layer.input[0] == value_info.name):
52+
dimensions['in'] = list(
53+
map(lambda x: x.dim_value, value_info.type.tensor_type.shape.dim[1:]))
54+
for value_info in onnx_graph.output:
55+
if (layer.output[0] == value_info.name):
56+
dimensions['out'] = list(
57+
map(lambda x: x.dim_value, value_info.type.tensor_type.shape.dim[1:]))
58+
return cls(layer.op_type, layer.name, dimensions)
59+
60+
def add_specs(self, specs):
61+
"""Add Specifications.
62+
Arguments:
63+
specs {list} -- the specifications to be added to the layer
64+
"""
65+
# Update Properties based on Specs. Try to Parse the Specs.
66+
for prop in specs:
67+
if isinstance(specs[prop], dict):
68+
self.properties[prop] = specs[prop]['class_name']
69+
else:
70+
self.properties[prop] = specs[prop]
71+
72+
def add_input_names_from_node(self, nodes):
73+
"""Adds all the Names of the Input Nodes to the Layer.
74+
Arguments:
75+
nodes {list} -- all nodes that are inputs to the layer
76+
"""
77+
if (len(nodes) > 0):
78+
for node in nodes[0]:
79+
self.input_names.append(node[0])
80+
81+
def __repr__(self):
82+
return "%s(properties: %r)" % (self.__class__, self.properties)

backend/translate/translate.py

Lines changed: 0 additions & 19 deletions
This file was deleted.

backend/translate/translate_keras.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,17 +17,17 @@ def translate_keras(filename):
1717
Returns:
1818
object -- the result of this translation, can be an error
1919
"""
20-
epicbox.configure(profiles=[
21-
epicbox.Profile('python', 'tf_plus_keras:latest')])
22-
general_reader = open('translate/keras_loader.txt', 'rb')
23-
general_code = general_reader.read()
2420
if keras_ext in filename:
2521
try:
2622
return graph_from_model_file(filename)
2723
except Exception as err:
2824
return {'error_class': '', 'line_number': 1,
2925
'detail': "Model could not be loaded correctly. Error: " + str(err)}
3026
else:
27+
epicbox.configure(profiles=[
28+
epicbox.Profile('python', 'tf_plus_keras:latest')])
29+
general_reader = open('translate/keras_loader.txt', 'rb')
30+
general_code = general_reader.read()
3131
with open(filename, 'rb') as myfile:
3232
keras_code = myfile.read()
3333
try:
@@ -99,8 +99,8 @@ def add_layer_type(layer_json, model_layer, graph, previous_node):
9999
Returns:
100100
String -- name of the new layer
101101
"""
102-
new_layer = layer.Layer(layer_json['class_name'],
103-
layer_json['config']['name'], model_layer)
102+
new_layer = layer.Layer.from_keras(layer_json['class_name'],
103+
layer_json['config']['name'], model_layer)
104104
new_layer.add_specs(layer_json['config'])
105105
return add_to_graph(new_layer, layer_json, graph, previous_node)
106106

@@ -118,9 +118,9 @@ def add_to_graph(new_layer, model_layer, graph, previous_node):
118118
String -- name of the new layer
119119
"""
120120
try:
121-
new_layer.add_input_names(model_layer['inbound_nodes'])
121+
new_layer.add_input_names_from_node(model_layer['inbound_nodes'])
122122
except Exception:
123123
if previous_node != '':
124-
new_layer.add_input_names([[[previous_node, 0, 0, {}]]])
124+
new_layer.add_input_names_from_node([[[previous_node, 0, 0, {}]]])
125125
graph.add_layer(new_layer)
126126
return new_layer.name
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import onnx
2+
from onnx import shape_inference
3+
from translate.graph import Graph
4+
import translate.layer as layer
5+
6+
7+
def translate_onnx(file):
8+
"""Get a graph from an external file defining the neural network.
9+
Arguments:
10+
file {Path} -- the path to the onnx file to be translated
11+
Returns:
12+
object -- the result of this translation, can be an error
13+
"""
14+
onnx_model = onnx.load(file)
15+
onnx_model = shape_inference.infer_shapes(onnx_model)
16+
graph = Graph()
17+
previous_node = ''
18+
for index, node in enumerate(onnx_model.graph.node):
19+
if index >= 0:
20+
add_layer_type(node, graph, previous_node, onnx_model.graph)
21+
graph.resolve_input_names()
22+
return graph
23+
24+
25+
def add_layer_type(node, graph, previous_node, onnx_graph):
26+
"""Add a Layer. Layers are identified by name and equipped using the spec.
27+
Arguments:
28+
node {onnx.Node} -- the node that represents the layer
29+
graph {object} -- neural network graph
30+
previous_node {String} -- name of the node before this one
31+
onnx_graph {onnx.Graph} -- the graph that represents this network
32+
Returns:
33+
String -- name of the new layer
34+
"""
35+
new_layer = layer.Layer.from_onnx(node, onnx_graph)
36+
add_specs_onnx(new_layer, node.attribute)
37+
return add_to_graph(new_layer, node, onnx_graph, graph, previous_node)
38+
39+
40+
def add_to_graph(new_layer, node, onnx_graph, graph, previous_node):
41+
"""Takes new layer, adds the Properties and then adds the Layer to the Graph.
42+
Arguments:
43+
new_layer {object} -- the layer to be added to the graph
44+
model_layer {dict} -- json dict of the layer props
45+
graph {object} -- the neural network graph
46+
previous_node {String} -- name of the previous layer
47+
Returns:
48+
String -- name of the new layer
49+
"""
50+
for input in node.input:
51+
for graph_node in onnx_graph.node:
52+
for output in graph_node.output:
53+
if (input == output):
54+
new_layer.input_names.append(graph_node.name)
55+
graph.add_layer(new_layer)
56+
return new_layer.name
57+
58+
59+
def add_specs_onnx(new_layer, attributes):
60+
for attribute in attributes:
61+
if (attribute.type == onnx.AttributeProto.AttributeType.FLOAT):
62+
new_layer.properties[attribute.name] = attribute.f
63+
elif (attribute.type == onnx.AttributeProto.AttributeType.FLOATS):
64+
new_layer.properties[attribute.name] = str(attribute.floats)
65+
elif (attribute.type == onnx.AttributeProto.AttributeType.INT):
66+
new_layer.properties[attribute.name] = attribute.i
67+
elif (attribute.type == onnx.AttributeProto.AttributeType.INTS):
68+
new_layer.properties[attribute.name] = str(attribute.ints)
69+
else:
70+
print(attribute.type, 'not supported yet.')

0 commit comments

Comments
 (0)