diff --git a/.DS_Store b/.DS_Store
new file mode 100644
index 0000000..5008ddf
Binary files /dev/null and b/.DS_Store differ
diff --git a/.idea/misc.xml b/.idea/misc.xml
index d1e22ec..db4bfb1 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -1,4 +1,4 @@
-
+
\ No newline at end of file
diff --git a/.idea/nn4mc_py.iml b/.idea/nn4mc_py.iml
index 0e4e9fa..8288a35 100644
--- a/.idea/nn4mc_py.iml
+++ b/.idea/nn4mc_py.iml
@@ -4,7 +4,7 @@
-
+
\ No newline at end of file
diff --git a/nn4mc/parser/__init__.py b/nn4mc/parser/__init__.py
index caed150..6d9aeb7 100644
--- a/nn4mc/parser/__init__.py
+++ b/nn4mc/parser/__init__.py
@@ -1,2 +1,3 @@
from .hdf5_parser._hdf5parser import HDF5Parser
+from .onnx_parser._onnxparser import ONNXParser
from .pytorch_parser._pytorch_parser import PYTorchParser
diff --git a/nn4mc/parser/hdf5_parser/_hdf5parser.py b/nn4mc/parser/hdf5_parser/_hdf5parser.py
index 48081a3..710149c 100644
--- a/nn4mc/parser/hdf5_parser/_hdf5parser.py
+++ b/nn4mc/parser/hdf5_parser/_hdf5parser.py
@@ -3,7 +3,10 @@
from ._layerbuilder import *
import h5py
import numpy as np
+import keras
from nn4mc.parser.hdf5_parser.helpers import bytesToJSON
+import distutils.spawn
+import os
# This class deals with parsing an HDF5 file from a keras neural network model.
# It will scrape the file and generate a NeuralNetwork object
@@ -20,6 +23,7 @@ class HDF5Parser(Parser):
'SimpleRNN' : 'SimpleRNNBuilder()',
'GRU' : 'GRUBuilder()',
'LSTM' : 'LSTMBuilder()',
+
'Input' : 'InputBuilder()',
'InputLayer': 'InputBuilder()',
'Activation' : 'ActivationBuilder()'}
@@ -46,11 +50,31 @@ def parse(self):
#Close the file
h5file.close()
+ def onnx_parse(self, h5file):
+ # Parse model configuration (i.e metadata)
+ model = keras.models.model_from_json(h5file.to_json())
+ model.set_weights(h5file.weights)
+ model.save('model.hdf5', overwrite = True)
+ h5file = h5py.File('model.hdf5', 'r')
+
+ self.parseModelConfig(h5file)
+
+ # Parse weights and biases
+ self.parseWeights(h5file, True)
+
+ # Close the file
+ h5file.close()
+
#Parses all of the layer metadata
#NOTE:
def parseModelConfig(self, h5file):
+
# with h5py.File(self.file_name, 'r') as h5file: #Open hdf5 file
+ #if not isinstance(h5file, keras.engine.functional.Functional):
configAttr = h5file['/'].attrs['model_config'] #Gets all metadata
+ #else:
+ # configAttr = h5file.to_json()
+
configJSON = bytesToJSON(configAttr)
self.parse_nn_input(configJSON['config'])
@@ -79,41 +103,44 @@ def parseModelConfig(self, h5file):
#Parses all of the weights
#NOTE:
- def parseWeights(self, h5file):
+ def parseWeights(self, h5file, onnx_parser = False):
+ #if not isinstance(h5file, keras.engine.functional.Functional):
weightGroup = h5file['model_weights'] #Open weight group
- # NOTE(sarahaguasvivas) here, the order matters,
- # therefore, using different list
- for layer in self.nn.iterate_layer_list():
- id = layer.identifier
- if id in weightGroup.keys() and 'max_pooling1d' not in id \
- and 'max_pooling2d' not in id and 'flatten' not in id and \
- 'input' not in id:
- # NOTE(sarahaguasvivas): kernel/weight assigment
- gru_keys = [k for k, v in weightGroup[id][id].items() if 'gru_cell' in k]
- if len(gru_keys) > 0:
- weight = np.array(weightGroup[id][id][gru_keys[0]]['kernel:0'])
- else:
- weight = np.array(weightGroup[id][id]['kernel:0'][()])
- # NOTE(sarahaguasvivas): bias
- if len(gru_keys) > 0:
- bias = np.array(weightGroup[id][id][gru_keys[0]]['bias:0'])
- else:
- bias = np.array(weightGroup[id][id]['bias:0'][()])
- # NOTE(sarahaguasvivas): recurrent weights
- if len(gru_keys) > 0:
- rec_weight = np.array(weightGroup[id][id][gru_keys[0]]['recurrent_kernel:0'][()])
- else:
- rec_weight = None
- layer.setParameters('weight', (id + '_W', weight))
- layer.setParameters('bias', (id + '_b', bias))
- layer.setParameters('weight_rec', (id + '_Wrec', rec_weight))
-
- # NOTE(sarahaguasvivas): calculating output shapes
- input_shape = self.nn_input_size
- for layer in self.nn.iterate_layer_list():
- if "input" not in layer.identifier:
- input_shape = layer.computeOutShape(input_shape)
- print(layer.getParameters())
+ if (not onnx_parser):
+ #if not isinstance(h5file, keras.engine.functional.Functional):
+ for layer in self.nn.iterate_layer_list():
+ id = layer.identifier
+ if id in weightGroup.keys() and 'max_pooling1d' not in id \
+ and 'max_pooling2d' not in id and 'flatten' not in id and \
+ 'input' not in id:
+ # NOTE(sarahaguasvivas): kernel/weight assigment
+ gru_keys = [k for k, v in weightGroup[id][id].items() if 'gru_cell' in k]
+ if len(gru_keys) > 0:
+ weight = np.array(weightGroup[id][id][gru_keys[0]]['kernel:0'])
+ else:
+ weight = np.array(weightGroup[id][id]['kernel:0'][()])
+ # NOTE(sarahaguasvivas): bias
+ if len(gru_keys) > 0:
+ bias = np.array(weightGroup[id][id][gru_keys[0]]['bias:0'])
+ else:
+ bias = np.array(weightGroup[id][id]['bias:0'][()])
+ # NOTE(sarahaguasvivas): recurrent weights
+ if len(gru_keys) > 0:
+ rec_weight = np.array(weightGroup[id][id][gru_keys[0]]['recurrent_kernel:0'][()])
+ else:
+ rec_weight = None
+ layer.setParameters('weight', (id + '_W', weight))
+ layer.setParameters('bias', (id + '_b', bias))
+ layer.setParameters('weight_rec', (id + '_Wrec', rec_weight))
+
+ # NOTE(sarahaguasvivas): calculating output shapes
+ input_shape = self.nn_input_size
+ for layer in self.nn.iterate_layer_list():
+ if "input" not in layer.identifier:
+ input_shape = layer.computeOutShape(input_shape)
+ print(layer.getParameters())
+ else:
+ pass
#parses model for input size
def parse_nn_input(self, model_config : dict):
diff --git a/nn4mc/parser/hdf5_parser/_layerbuilder.py b/nn4mc/parser/hdf5_parser/_layerbuilder.py
index 70b896e..c0464e4 100644
--- a/nn4mc/parser/hdf5_parser/_layerbuilder.py
+++ b/nn4mc/parser/hdf5_parser/_layerbuilder.py
@@ -128,6 +128,7 @@ def build_layer(self, json_obj, id, layer_type):
return new_layer
+
class FlattenBuilder(LayerBuilder):
def build_layer(self, json_obj, id, layer_type):
new_layer = Flatten(id, layer_type)
diff --git a/nn4mc/parser/onnx_parser/_onnxparser.py b/nn4mc/parser/onnx_parser/_onnxparser.py
new file mode 100644
index 0000000..7567665
--- /dev/null
+++ b/nn4mc/parser/onnx_parser/_onnxparser.py
@@ -0,0 +1,129 @@
+from nn4mc.parser._parser import Parser
+from nn4mc.datastructures import NeuralNetwork
+# from ._layerbuilder import *
+import h5py
+import onnx
+import keras
+# from onnx2keras import onnx_to_keras
+# from nn4mc.parser.onnx_parser.onnx_helpers import HDF5Parser
+import numpy as np
+from nn4mc.parser.onnx_parser.onnx_helpers import onnx2keras
+from nn4mc.parser.hdf5_parser._hdf5parser import HDF5Parser
+from tensorflow import keras
+
+class ONNXParser(Parser):
+
+ def __init__(self, file):
+ self.file = file
+ self.nn = NeuralNetwork()
+ self.nn_input_size = None
+
+ def parse(self):
+ h5format = onnx2keras(self.file)
+
+ onnx_model = onnx.load(self.file)
+ # print(type(onnx_model))
+
+ h5parser = HDF5Parser(h5format)
+ h5parser.file = self.file
+ h5parser.onnx_parse(h5format)
+ self.nn = h5parser.nn
+
+ self.parseModelConfig(onnx_model)
+
+ # parse weights and biases
+ self.parseWeights(onnx_model)
+
+ # close the file
+ # h5format.close()
+
+ def parseModelConfig(self, h5file):
+
+ # with h5py.File(self.file_name, 'r') as h5file: #Open hdf5 file
+ #if not isinstance(h5file, keras.engine.functional.Functional):
+ configAttr = h5file['/'].attrs['model_config'] #Gets all metadata
+ #else:
+ # configAttr = h5file.to_json()
+
+ configJSON = bytesToJSON(configAttr)
+
+ self.parse_nn_input(configJSON['config'])
+
+ #This adds an input layer before everything, not sure if it is
+ #really neccessary.
+ #NOTE: Determine if this is neccessary
+ last_layer = Input('input_1','input')
+ self.nn.addLayer(last_layer)
+
+ #NOTE: Could check to see if its sequential here
+ for model_layer in configJSON['config']['layers']:
+ type_ = model_layer['class_name']
+ name = model_layer['config']['name']
+
+ if type_ in self.builder_map.keys():
+ builder = eval(self.builder_map[type_])
+
+ #Build a layer object from metadata
+ layer = builder.build_layer(model_layer['config'], name.lower(), type_.lower())
+
+ self.nn.addLayer(layer) #Add Layer to neural network
+ self.nn.addEdge(last_layer, layer)
+
+ last_layer = layer
+
+ def _parseONNX(self):
+ return self.parse.h5format
+
+ def parseWeights(self, h5file, _parseONNX = True):
+
+ weightGroup = h5file.graph.initializer
+
+ if (not _parseONNX):
+ pass
+
+ else:
+ for layer in self.nn.iterate_layer_list():
+
+ id = layer.identifier
+
+ if id in weightGroup and 'max_pooling1d' not in id \
+ and 'max_pooling2d' not in id and 'flatten' not in id and \
+ 'input' not in id:
+
+ gru_keys = [k for k, v in weightGroup[id][id].items() if 'gru_cell' in k]
+ # kernel/weight assignment
+ if len(gru_keys) > 0:
+ weight = np.array(weightGroup[id][id][gru_keys[0]]['kernel:0'])
+ else:
+ weight = np.array(weightGroup[id][id]['kernel:0'][()])
+ # bias
+ if len(gru_keys) > 0:
+ bias = np.array(weightGroup[id][id][gru_keys[0]]['bias:0'])
+ else:
+ bias = np.array(weightGroup[id][id]['bias:0'][()])
+ # weight
+ if len(gru_keys) > 0:
+ rec_weight = np.array(weightGroup[id][id][gru_keys[0]]['recurrent_kernel:0'][()])
+ else:
+ rec_weight = None
+
+ layer.setParameters('weight', (id + '_W', weight))
+ layer.setParameters('bias', (id + '_b', bias))
+ layer.setParameters('weight_rec', (id + '_Wrec', rec_weight))
+
+ input_shape = self.nn_input_size
+ # print('aaaaa', input_shape)
+ for layer in self.nn.iterate_layer_list():
+ if "input" not in layer.identifier:
+ input_shape = layer.computeOutShape(input_shape)
+ print(layer.getParameters())
+
+ def parse_nn_input(self, model_config : dict):
+ """
+ INPUT: model_config is the json object dictionary
+ OUTPUT: numpy array with the input size of the model
+ """
+ if model_config.get('build_input_shape'):
+ self.nn_input_size = model_config['build_input_shape'][1:]
+ if model_config['layers'][0].get('config','batch_input_shape'):
+ self.nn_input_size = model_config['layers'][0]['config']
\ No newline at end of file
diff --git a/nn4mc/parser/onnx_parser/onnx_helpers.py b/nn4mc/parser/onnx_parser/onnx_helpers.py
new file mode 100644
index 0000000..21d2d7c
--- /dev/null
+++ b/nn4mc/parser/onnx_parser/onnx_helpers.py
@@ -0,0 +1,25 @@
+import onnx
+from onnx2keras import onnx_to_keras
+
+# /*------------------------------------------------- onnx2keras -----
+# | Function: onnx2keras
+# |
+# | Purpose: CONVERTING AN ONNX MODEL TO KERA VERSION
+# |
+# | Parameters: .onnx file
+# |
+# | Returns: Kera model
+# *-------------------------------------------------------------------*/
+
+
+def onnx2keras(file):
+ # Load ONNX model
+ onnx_model = onnx.load(file)
+
+ input_all = [node.name for node in onnx_model.graph.input]
+
+ # Call the converter
+ k_model = onnx_to_keras(onnx_model, [input_all[0]])
+
+
+ return k_model
\ No newline at end of file
diff --git a/requirements.txt b/requirements.txt
new file mode 100644
index 0000000..4545bc1
--- /dev/null
+++ b/requirements.txt
@@ -0,0 +1,4 @@
+numpy
+h5py
+onnx2keras
+onnx
diff --git a/tests/data/resnet18-v2-7.onnx b/tests/data/resnet18-v2-7.onnx
new file mode 100644
index 0000000..2aff14a
Binary files /dev/null and b/tests/data/resnet18-v2-7.onnx differ
diff --git a/tests/test_translator/__init__.py b/tests/test_translator/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/tests/test_translator/functional.hdf5 b/tests/test_translator/functional.hdf5
new file mode 100644
index 0000000..11c6e12
Binary files /dev/null and b/tests/test_translator/functional.hdf5 differ
diff --git a/tests/test_translator/model.hdf5 b/tests/test_translator/model.hdf5
new file mode 100644
index 0000000..473582c
Binary files /dev/null and b/tests/test_translator/model.hdf5 differ
diff --git a/tests/test_translator/parser_weight_save.h5 b/tests/test_translator/parser_weight_save.h5
new file mode 100644
index 0000000..85d5b7e
Binary files /dev/null and b/tests/test_translator/parser_weight_save.h5 differ
diff --git a/tests/test_translator/test_full.py b/tests/test_translator/test_full_hdf5.py
similarity index 100%
rename from tests/test_translator/test_full.py
rename to tests/test_translator/test_full_hdf5.py
diff --git a/tests/test_translator/test_full_onnx.py b/tests/test_translator/test_full_onnx.py
new file mode 100644
index 0000000..8a96ac9
--- /dev/null
+++ b/tests/test_translator/test_full_onnx.py
@@ -0,0 +1,27 @@
+import nn4mc.parser as nnPr
+import nn4mc.datastructures as nnDs
+import nn4mc.generator as nnGn
+import unittest
+import os
+
+class TestTranslator(unittest.TestCase):
+
+ def setUp(self):
+ pass
+
+ def test_file(self):
+ p = nnPr.ONNXParser('../data/resnet18-v2-7.onnx')
+
+ p.parse()
+
+ path = os.path.dirname(os.path.abspath(__file__))
+ if (not os.path.exists(os.path.join(path, 'output'))):
+ os.makedirs(os.path.join(path, 'output'))
+ path2 = os.path.join(path, 'output')
+
+ generator = nnGn.Generator(p.nn)
+
+ generator.generate(path2)
+
+if __name__=='__main__':
+ unittest.main()
diff --git a/tests/test_translator/weights.h5 b/tests/test_translator/weights.h5
new file mode 100644
index 0000000..285ccdd
Binary files /dev/null and b/tests/test_translator/weights.h5 differ