From 9708c7750c39c7d3d23c3df007d46dde4381eb31 Mon Sep 17 00:00:00 2001 From: Umesh Date: Mon, 21 Oct 2019 18:21:22 -0500 Subject: [PATCH 01/23] Add new Plugin From the webgme cli --- .gitignore | 32 -------- src/plugins/ImportKeras/ImportKeras.js | 84 ++++++++++++++++++++ src/plugins/ImportKeras/metadata.json | 15 ++++ test/plugins/ImportKeras/ImportKeras.spec.js | 82 +++++++++++++++++++ webgme-setup.json | 4 + 5 files changed, 185 insertions(+), 32 deletions(-) delete mode 100644 .gitignore create mode 100644 src/plugins/ImportKeras/ImportKeras.js create mode 100644 src/plugins/ImportKeras/metadata.json create mode 100644 test/plugins/ImportKeras/ImportKeras.spec.js diff --git a/.gitignore b/.gitignore deleted file mode 100644 index a1c6189..0000000 --- a/.gitignore +++ /dev/null @@ -1,32 +0,0 @@ -*.swp -*.swo -# Logs -logs -*.log - -# Runtime data -pids -*.pid -*.seed - -# Directory for instrumented libs generated by jscoverage/JSCover -lib-cov - -# Coverage directory used by tools like istanbul -coverage - -# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) -.grunt - -# node-waf configuration -.lock-wscript - -# Compiled binary addons (http://nodejs.org/api/addons.html) -build/Release - -# Dependency directory -# https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git -node_modules -tmp/ -blob-local-storage/ -notes/ diff --git a/src/plugins/ImportKeras/ImportKeras.js b/src/plugins/ImportKeras/ImportKeras.js new file mode 100644 index 0000000..d0e90be --- /dev/null +++ b/src/plugins/ImportKeras/ImportKeras.js @@ -0,0 +1,84 @@ +/*globals define*/ +/*eslint-env node, browser*/ + +/** + * Generated by PluginGenerator 2.20.5 from webgme on Mon Oct 21 2019 18:17:16 GMT-0500 (Central Daylight Time). + * A plugin that inherits from the PluginBase. To see source code documentation about available + * properties and methods visit %host%/docs/source/PluginBase.html. + */ + +define([ + 'plugin/PluginConfig', + 'text!./metadata.json', + 'plugin/PluginBase' +], function ( + PluginConfig, + pluginMetadata, + PluginBase) { + 'use strict'; + + pluginMetadata = JSON.parse(pluginMetadata); + + /** + * Initializes a new instance of ImportKeras. + * @class + * @augments {PluginBase} + * @classdesc This class represents the plugin ImportKeras. + * @constructor + */ + function ImportKeras() { + // Call base class' constructor. + PluginBase.call(this); + this.pluginMetadata = pluginMetadata; + } + + /** + * Metadata associated with the plugin. Contains id, name, version, description, icon, configStructure etc. + * This is also available at the instance at this.pluginMetadata. + * @type {object} + */ + ImportKeras.metadata = pluginMetadata; + + // Prototypical inheritance from PluginBase. + ImportKeras.prototype = Object.create(PluginBase.prototype); + ImportKeras.prototype.constructor = ImportKeras; + + /** + * Main function for the plugin to execute. This will perform the execution. + * Notes: + * - Always log with the provided logger.[error,warning,info,debug]. + * - Do NOT put any user interaction logic UI, etc. inside this method. + * - callback always has to be called even if error happened. + * + * @param {function(Error|null, plugin.PluginResult)} callback - the result callback + */ + ImportKeras.prototype.main = function (callback) { + // Use this to access core, project, result, logger etc from PluginBase. + + // Using the logger. + this.logger.debug('This is a debug message.'); + this.logger.info('This is an info message.'); + this.logger.warn('This is a warning message.'); + this.logger.error('This is an error message.'); + + // Using the coreAPI to make changes. + this.core.setAttribute(nodeObject, 'name', 'My new obj'); + this.core.setRegistry(nodeObject, 'position', {x: 70, y: 70}); + + + // This will save the changes. If you don't want to save; + // exclude self.save and call callback directly from this scope. + this.save('ImportKeras updated model.') + .then(() => { + this.result.setSuccess(true); + callback(null, self.result); + }) + .catch((err) => { + // Result success is false at invocation. + this.logger.error(err.stack); + callback(err, self.result); + }); + }; + + return ImportKeras; +}); diff --git a/src/plugins/ImportKeras/metadata.json b/src/plugins/ImportKeras/metadata.json new file mode 100644 index 0000000..6cf3c99 --- /dev/null +++ b/src/plugins/ImportKeras/metadata.json @@ -0,0 +1,15 @@ +{ + "id": "ImportKeras", + "name": "ImportKeras", + "version": "0.1.0", + "description": "", + "icon": { + "class": "glyphicon glyphicon-cog", + "src": "" + }, + "disableServerSideExecution": false, + "disableBrowserSideExecution": false, + "dependencies": [], + "writeAccessRequired": false, + "configStructure": [] +} \ No newline at end of file diff --git a/test/plugins/ImportKeras/ImportKeras.spec.js b/test/plugins/ImportKeras/ImportKeras.spec.js new file mode 100644 index 0000000..a244a06 --- /dev/null +++ b/test/plugins/ImportKeras/ImportKeras.spec.js @@ -0,0 +1,82 @@ +/*eslint-env node, mocha*/ +/** + * Generated by PluginGenerator 2.20.5 from webgme on Mon Oct 21 2019 18:17:16 GMT-0500 (Central Daylight Time). + */ + +describe('ImportKeras', function () { + var testFixture = require('../../globals'), + gmeConfig = testFixture.getGmeConfig(), + expect = testFixture.expect, + logger = testFixture.logger.fork('ImportKeras'), + PluginCliManager = testFixture.WebGME.PluginCliManager, + projectName = 'testProject', + pluginName = 'ImportKeras', + project, + gmeAuth, + storage, + commitHash; + + before(function (done) { + testFixture.clearDBAndGetGMEAuth(gmeConfig, projectName) + .then(function (gmeAuth_) { + gmeAuth = gmeAuth_; + // This uses in memory storage. Use testFixture.getMongoStorage to persist test to database. + storage = testFixture.getMemoryStorage(logger, gmeConfig, gmeAuth); + return storage.openDatabase(); + }) + .then(function () { + var importParam = { + projectSeed: testFixture.path.join(testFixture.SEED_DIR, 'EmptyProject.webgmex'), + projectName: projectName, + branchName: 'master', + logger: logger, + gmeConfig: gmeConfig + }; + + return testFixture.importProject(storage, importParam); + }) + .then(function (importResult) { + project = importResult.project; + commitHash = importResult.commitHash; + return project.createBranch('test', commitHash); + }) + .nodeify(done); + }); + + after(function (done) { + storage.closeDatabase() + .then(function () { + return gmeAuth.unload(); + }) + .nodeify(done); + }); + + it('should run plugin and update the branch', function (done) { + var manager = new PluginCliManager(null, logger, gmeConfig), + pluginConfig = { + }, + context = { + project: project, + commitHash: commitHash, + branchName: 'test', + activeNode: '/1', + }; + + manager.executePlugin(pluginName, pluginConfig, context, function (err, pluginResult) { + try { + expect(err).to.equal(null); + expect(typeof pluginResult).to.equal('object'); + expect(pluginResult.success).to.equal(true); + } catch (e) { + done(e); + return; + } + + project.getBranchHash('test') + .then(function (branchHash) { + expect(branchHash).to.not.equal(commitHash); + }) + .nodeify(done); + }); + }); +}); diff --git a/webgme-setup.json b/webgme-setup.json index 3e97f0a..0e34032 100644 --- a/webgme-setup.json +++ b/webgme-setup.json @@ -12,6 +12,10 @@ "ValidateKeras": { "src": "src/plugins/ValidateKeras", "test": "test/plugins/ValidateKeras" + }, + "ImportKeras": { + "src": "src/plugins/ImportKeras", + "test": "test/plugins/ImportKeras" } }, "visualizers": { From 5bfd953c53e321593c1bba1c2643fdf680f0c844 Mon Sep 17 00:00:00 2001 From: Umesh Date: Mon, 21 Oct 2019 18:25:05 -0500 Subject: [PATCH 02/23] Modify .gitignore, add LF/CRLF fix to .gitattributes --- .gitattributes | 1 + .gitignore | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+) create mode 100644 .gitattributes create mode 100644 .gitignore diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..07764a7 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +* text eol=lf \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..522298b --- /dev/null +++ b/.gitignore @@ -0,0 +1,38 @@ +*.swp +*.swo +# Logs +logs +*.log + +# Runtime data +pids +*.pid +*.seed + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage + +# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (http://nodejs.org/api/addons.html) +build/Release + +# Dependency directory +# https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git +node_modules +tmp/ +blob-local-storage/ +notes/ + +#IntelliJ +.idea + +#vscode +.vscode \ No newline at end of file From 01c312aa43f15380deef33fc1b026b8ffafa15a7 Mon Sep 17 00:00:00 2001 From: Umesh Date: Mon, 21 Oct 2019 18:35:37 -0500 Subject: [PATCH 03/23] Add Plugin Implmentation, flatten JSON file from utils/json-model-parser.js --- src/plugins/ImportKeras/ImportKeras.js | 267 ++++++++++++++++-- src/plugins/ImportKeras/metadata.json | 18 +- .../ImportKeras/utils/JSONModelMaps.js | 26 ++ .../ImportKeras/utils/json-model-parser.js | 176 ++++++++++++ 4 files changed, 455 insertions(+), 32 deletions(-) create mode 100644 src/plugins/ImportKeras/utils/JSONModelMaps.js create mode 100644 src/plugins/ImportKeras/utils/json-model-parser.js diff --git a/src/plugins/ImportKeras/ImportKeras.js b/src/plugins/ImportKeras/ImportKeras.js index d0e90be..3d7b461 100644 --- a/src/plugins/ImportKeras/ImportKeras.js +++ b/src/plugins/ImportKeras/ImportKeras.js @@ -2,7 +2,7 @@ /*eslint-env node, browser*/ /** - * Generated by PluginGenerator 2.20.5 from webgme on Mon Oct 21 2019 18:17:16 GMT-0500 (Central Daylight Time). + * Generated by PluginGenerator 2.20.5 from webgme on Tue Sep 10 2019 15:28:36 GMT-0500 (Central Daylight Time). * A plugin that inherits from the PluginBase. To see source code documentation about available * properties and methods visit %host%/docs/source/PluginBase.html. */ @@ -10,15 +10,21 @@ define([ 'plugin/PluginConfig', 'text!./metadata.json', - 'plugin/PluginBase' + 'plugin/PluginBase', + './utils/JSONModelMaps', + './utils/json-model-parser', ], function ( PluginConfig, pluginMetadata, - PluginBase) { + PluginBase, + ModelMaps, + JSONLayerParser, +) { 'use strict'; pluginMetadata = JSON.parse(pluginMetadata); + /** * Initializes a new instance of ImportKeras. * @class @@ -26,11 +32,11 @@ define([ * @classdesc This class represents the plugin ImportKeras. * @constructor */ - function ImportKeras() { + var ImportKeras = function () { // Call base class' constructor. PluginBase.call(this); this.pluginMetadata = pluginMetadata; - } + }; /** * Metadata associated with the plugin. Contains id, name, version, description, icon, configStructure etc. @@ -52,32 +58,233 @@ define([ * * @param {function(Error|null, plugin.PluginResult)} callback - the result callback */ - ImportKeras.prototype.main = function (callback) { - // Use this to access core, project, result, logger etc from PluginBase. - - // Using the logger. - this.logger.debug('This is a debug message.'); - this.logger.info('This is an info message.'); - this.logger.warn('This is a warning message.'); - this.logger.error('This is an error message.'); - - // Using the coreAPI to make changes. - this.core.setAttribute(nodeObject, 'name', 'My new obj'); - this.core.setRegistry(nodeObject, 'position', {x: 70, y: 70}); - - - // This will save the changes. If you don't want to save; - // exclude self.save and call callback directly from this scope. - this.save('ImportKeras updated model.') - .then(() => { - this.result.setSuccess(true); - callback(null, self.result); - }) - .catch((err) => { - // Result success is false at invocation. - this.logger.error(err.stack); - callback(err, self.result); + ImportKeras.prototype.main = async function(callback){ + let srcJsonHash = this.getCurrentConfig().srcModel; + if (!srcJsonHash) { + return callback(new Error('Keras Json Not Provided'), this.result); + } + try { + this.archName = this.getCurrentConfig().archName; + let metadata = await this.blobClient.getMetadata(srcJsonHash); + this.archName = this.archName ? this.archName : metadata.name.replace('.json', ''); + let modelJson = await this.blobClient.getObjectAsJSON(srcJsonHash); + this.modelInfo = JSONLayerParser.flatten(modelJson).config; + this.addNewArchitecture(); + this.addLayers(); + await this.addConnections(); + await this.save('Completed Import Model'); + this.result.setSuccess(true); + return callback(null, this.result); + } catch (err) { + this.logger.debug(`Something Went Wrong, Error Message: ${err}`); + return callback(err, this.result); + } + }; + + ImportKeras.prototype.addNewArchitecture = function () { + // Add Architecture + this.importedArch = this.core.createNode({ + parent: this.activeNode, + base: this.META.Architecture + }); + const uniqueName = this.archName; + this.core.setAttribute(this.importedArch, 'name', uniqueName); + + this.logger.debug(`Added ${uniqueName} as a new architecture.`); + }; + + // This should add layers. constraints, initializers, regularizers as well as activations to the layer. + ImportKeras.prototype.addLayers = function () { + let layers = this.modelInfo.layers; + let layerToCreate = null; + this.layerInfo = {}; + layers.forEach((layer) => { + layerToCreate = this._getMetaTypeForClass(layer.class_name); + let layerNode = this.core.createNode({ + parent: this.importedArch, + base: this.META[layerToCreate] }); + this.logger.debug(`Added ${layerToCreate}\ + to ${this.core.getAttribute(this.importedArch, 'name')}`); + + // Add all attributes, from the model JSON, as well as from the layers schema + this._addLayerAttributes(layerNode, layer); + this._addConfigurableNodes(layerNode, layer); + }); + }; + + // All the attributes, which do not require a separate node to be created + // 1. First find the validAttributeNames for the layer + // 2. If the name is in layer, set it. + // 3. If the name is in layer.config, set it. + // 4. Finally, check the layers schema for remaining attributes. + ImportKeras.prototype._addLayerAttributes = function (layerNode, attrObj) { + let config = attrObj.config; + let validAttributeNamesForThisLayer = this.core.getValidAttributeNames(layerNode); + let configKeys = Object.keys(config); + let remainingKeys = Object.keys(attrObj) + .filter(value => value !== 'config'); + + validAttributeNamesForThisLayer.forEach((attribute) => { + if (remainingKeys.indexOf(this._jsonConfigToNodeAttr(attribute)) > -1) { + this.core.setAttribute(layerNode, attribute, this._toPythonIterable(attrObj[this._jsonConfigToNodeAttr(attribute)])); + this.logger.debug(`Set ${attribute} for ${this.core.getGuid(layerNode)}` + + ` to ${this.core.getAttribute(layerNode, attribute)}`); + } else if (configKeys.indexOf(this._jsonConfigToNodeAttr(attribute)) > -1) { + this.core.setAttribute(layerNode, attribute, this._toPythonIterable(config[this._jsonConfigToNodeAttr(attribute)])); + this.logger.debug(`Set ${attribute} for ${this.core.getGuid(layerNode)}` + + ` to ${this.core.getAttribute(layerNode, attribute)}`); + + } + }); + let layerName = this.core.getAttribute(layerNode, 'name'); + this.layerInfo[layerName] = layerNode; + + }; + + ImportKeras.prototype._addConfigurableNodes = function (layerNode, layerConfig) { + let allPointerNames = this.core.getValidPointerNames(layerNode); + let config = layerConfig.config; + this.logger.debug(`Layer ${this.core.getAttribute(layerNode, 'name')}` + + ` has following configurable attributes ${allPointerNames.join(', ')}`); + allPointerNames.filter(pointer => !!config[pointer]) + .forEach((pointer) => { + if (typeof config[pointer] == 'string') { + let configurableNode = this.core.createNode({ + parent: layerNode, + base: this.META[config[pointer]] + }); + // This will set the necessary pointers. + // Of things like activations and so on... + this.core.setPointer(layerNode, pointer, configurableNode); + this.logger.debug(`Added ${this.core.getAttribute(configurableNode, 'name')}` + + ` as ${pointer} to the layer ` + + `${this.core.getAttribute(layerNode, 'name')}`); + } else { + let pluggableNode = this.core.createNode({ + parent: layerNode, + base: this.META[config[pointer].class_name] + }); + this.logger.debug(`Added ${this.core.getAttribute(pluggableNode, 'name')} as ${pointer} to the layer ` + + `${this.core.getAttribute(layerNode, 'name')}`); + let validArgumentsForThisNode = this.core.getValidAttributeNames(pluggableNode); + let configForAddedNode = config[pointer].config; + if (validArgumentsForThisNode && configForAddedNode) { + validArgumentsForThisNode.forEach((arg) => { + if (configForAddedNode[arg]) { + this.core.setAttribute(pluggableNode, arg, this._toPythonIterable(configForAddedNode[arg])); + } + }); + } + } + }); + + }; + + // This method is used to convert javascript arrays to a + // tuple/ list(Python) in string Representation. Needed for + // Code generation. + ImportKeras.prototype._toPythonIterable = function (obj) { + if (obj == null) { + return 'None'; + } + if (obj instanceof Array) { + return '[' + obj.map((val) => { + return this._toPythonIterable(val); + }).join(', ') + ']'; + } else { + return obj; + } + }; + + // This method is used to convert various classes from the + // keras JSON to deepforge meta Nodes + ImportKeras.prototype._getMetaTypeForClass = function (kerasClass) { + let classMap = ModelMaps.CLASS_MAP; + if (Object.keys(classMap).indexOf(kerasClass) > -1) { + return classMap[kerasClass]; + } else { + return kerasClass; + } + }; + + // Change the model converts some JSON + // layer attributes names (from keras) to the correct + // attribute name for deepforge-keras nodes + ImportKeras.prototype._jsonConfigToNodeAttr = function (orgName) { + let argMap = ModelMaps.ARGUMENTS_MAP; + if (Object.keys(argMap).indexOf(orgName) > -1) { + return argMap[orgName]; + } else { + return orgName; + } + }; + + /**********************The functions below Add Connections between the Layers**************/ + ImportKeras.prototype.addConnections = async function () { + // this._findNodeByName(); + let layers = this.modelInfo.layers; + let layerInputConnections = {}; + let layerOutputConnections = {}; + let connections = null; + layers.forEach((layer) => { + layerInputConnections[layer.name] = []; + layerOutputConnections[layer.name] = []; + }); + + layers.forEach((layer) => { + if (layer.inbound_nodes.length > 0) { + connections = layer.inbound_nodes; + connections.forEach((connection) => { + + if (this._layerNameExists(connection)) { + layerInputConnections[layer.name].push(connection); + layerOutputConnections[connection].push(layer.name); + } + }); + + } + }); + + await this._updateConnections(layerInputConnections, layerOutputConnections); + }; + + + ImportKeras.prototype._layerNameExists = function (layerName) { + let allLayerNames = this.modelInfo.layers.map((layer) => { + return layer.name; + }); + + return allLayerNames.indexOf(layerName) > -1; + }; + + ImportKeras.prototype._updateConnections = function (inputs, outputs) { + let allLayerNames = Object.keys(inputs); + return Promise.all(allLayerNames.map((layerName) => { + let dstLayer = this.layerInfo[layerName]; + let srcs = inputs[layerName]; + return Promise.all(srcs.map((src, index) => { + return this._connectLayers(this.layerInfo[src], dstLayer, index); + })); + })); + }; + + ImportKeras.prototype._connectLayers = async function (srcLayer, dstLayer, index) { + + let srcPort = await this.core.loadMembers(srcLayer, 'outputs'); + let dstPort = await this.core.loadMembers(dstLayer, 'inputs'); + if (dstPort && srcPort) { + this.core.addMember(dstPort[0], 'source', srcPort[0]); + this.core.setMemberRegistry(dstPort[0], + 'source', + this.core.getPath(srcPort[0]), + 'position', {x: 100, y: 100}); + this.core.setMemberAttribute(dstPort[0], 'source', + this.core.getPath(srcPort[0]), + 'index', index); + this.logger.debug(`Connected ${this.core.getAttribute(srcLayer, 'name')} ` + + `with ${this.core.getAttribute(dstLayer, 'name')} as input ${index}`); + } }; return ImportKeras; diff --git a/src/plugins/ImportKeras/metadata.json b/src/plugins/ImportKeras/metadata.json index 6cf3c99..862c815 100644 --- a/src/plugins/ImportKeras/metadata.json +++ b/src/plugins/ImportKeras/metadata.json @@ -4,12 +4,26 @@ "version": "0.1.0", "description": "", "icon": { - "class": "glyphicon glyphicon-cog", + "class": "glyphicon glyphicon-download-alt", "src": "" }, "disableServerSideExecution": false, "disableBrowserSideExecution": false, "dependencies": [], "writeAccessRequired": false, - "configStructure": [] + "configStructure": [{ + "name": "srcModel", + "displayName": "Keras Model JSON File", + "description": "The Keras model JSON to import.", + "valueType": "asset", + "readOnly": false + }, + { + "name": "archName", + "displayName": "Model Name", + "description": "Name of the imported model", + "valueType": "string", + "readOnly": false + } + ] } \ No newline at end of file diff --git a/src/plugins/ImportKeras/utils/JSONModelMaps.js b/src/plugins/ImportKeras/utils/JSONModelMaps.js new file mode 100644 index 0000000..2dacfa3 --- /dev/null +++ b/src/plugins/ImportKeras/utils/JSONModelMaps.js @@ -0,0 +1,26 @@ +/*globals define */ +define([], function() { + const ModelMaps = {}; + + ModelMaps.CLASS_MAP = { + InputLayer: 'Input' + }; + + ModelMaps.ARGUMENTS_MAP = { + batch_shape: 'batch_input_shape' + }; + + ModelMaps.ModelTypes = { + sequential : 'Sequential', + functional : 'Model' + }; + + ModelMaps.AbstractLayerTypeMapping = { + Activation: 'activation', + ActivityRegularization: 'activity_regularizer' + }; + + + return ModelMaps; + +}); \ No newline at end of file diff --git a/src/plugins/ImportKeras/utils/json-model-parser.js b/src/plugins/ImportKeras/utils/json-model-parser.js new file mode 100644 index 0000000..6228e6a --- /dev/null +++ b/src/plugins/ImportKeras/utils/json-model-parser.js @@ -0,0 +1,176 @@ +/*globals define*/ +define(['./JSONModelMaps'], function (JSONModelMaps) { + const ModelParser = { + countNumberOfModels: countNumberOfModels, + flatten: flatten + }; + + function countNumberOfModels(modelConfig) { + return countModels(modelConfig); + } + + /* + This function will flatten nested models (i.e. a Model inside a model) + and create a simple array of all the layers that can be directly used by + the plugin + */ + function flatten(modelConfig) { + let alteredModelConfig = JSON.parse(JSON.stringify(modelConfig)); + let layersInfo = []; + let replacementInboundNodeKeys = {}; + flattenNestedModel(alteredModelConfig, layersInfo, replacementInboundNodeKeys); + alteredModelConfig.config.layers = layersInfo; + addInputLayer(alteredModelConfig); + inboundNodesToStringArray(alteredModelConfig); + replaceModelInboundKeyWithFirstLayer(alteredModelConfig, replacementInboundNodeKeys); + return alteredModelConfig; + } + + function countModels(modelConfig) { + if(isModel(modelConfig)){ + return modelConfig.config.layers + .filter(layer => isModel(layer)) + .map(nestedModel => countModels(nestedModel)) + .reduce((a, b) => a+b, 1); + } + return 0; + } + + function flattenNestedModel(modelConfig, layersInfo = [], inboundNodeKeys = {}) { + let layers = modelConfig.config.layers; + if (layers) { + layers.forEach((layer, index, records) => { + if (isModel(layer)) { + addInboundNodesForSequential(layer); + let lastIndex = layer.config.layers.length - 1; + inboundNodeKeys[layer.name] = layer.config.layers[lastIndex].config.name; // Change model inbound to first layer in the model. + if (layer.inbound_nodes) { + layer.config.layers[0].inbound_nodes = layer.inbound_nodes; + } else { + if (records[index - 1]) { + layer.config.layers[0].inbound_nodes = records[index - 1].config.name; + } + else { + layer.config.layers[0].inbound_nodes = []; + } + } + flattenNestedModel(layer, layersInfo); + } else { + layersInfo.push(layer); + } + }); + } + } + + + function addInboundNodesForSequential(layersInfo) { + if (determineModelType(layersInfo) !== JSONModelMaps.ModelTypes.sequential) { + return; + } + layersInfo = layersInfo.config.layers; + for (let i = 0; i < layersInfo.length - 1; i++) { + layersInfo[i + 1].inbound_nodes = [layersInfo[i].config.name]; + if (i === 0) { + layersInfo[i].inbound_nodes = []; + layersInfo[i].name = layersInfo[i].config.name; + } + // This adds Consistency with the functional models, because + // the functional JSON file has duplicate name keys, + // one inside config and one at the top level + if (!layersInfo[i + 1].name) { + layersInfo[i + 1].name = layersInfo[i + 1].config.name; + } + } + } + + + function isModel(modelConfig) { + return Array.isArray(modelConfig.config.layers); + } + + + function addInputLayer(modelConfig) { + if (determineModelType(modelConfig) === JSONModelMaps.ModelTypes.sequential) { + let inputLayerToAdd = { + 'class_name': 'InputLayer', + 'config': { + 'batch_input_shape': modelConfig.config.layers[0].config.batch_input_shape, + 'dtype': 'float32', + 'name': 'input', + 'sparse': false + }, + 'name': 'input', + 'inbound_nodes': [] + }; + + delete modelConfig.config.layers[0].config.batch_input_shape; + modelConfig.config.layers.splice(0, 0, inputLayerToAdd); + addInboundNodesForSequential(modelConfig); + } + } + + // Determine whether the model is 'Sequential' Or 'Functional' + // Its necessary because 'Sequential' Models do not have the property inbound_nodes + // In their layer so, it should be added externally + // Cases for a sequential model: + // 1. The model has an attribute called class_name and is equal to 'Sequential' + // 2. The model has an no attribute inbound_nodes + function determineModelType(model) { + if (model.class_name && model.class_name === JSONModelMaps.ModelTypes.sequential) { + return JSONModelMaps.ModelTypes.sequential; + } + let layers = model.config.layers; + if (layers) { + for (let i = 0; i < layers.length; i++) { + if (!layers[i].inbound_nodes) { + return JSONModelMaps.ModelTypes.sequential; + } + } + } + return JSONModelMaps.ModelTypes.functional; + } + + + // After getting flattened layers, change from + function inboundNodesToStringArray(modelConfig) { + if (determineModelType(modelConfig) === JSONModelMaps.ModelTypes.sequential) { + return; + } + let layers = modelConfig.config.layers; + layers.forEach((layer) => { + + if (layer.inbound_nodes[0] && layer.inbound_nodes[0][0] && layer.inbound_nodes[0][0] instanceof Array) { + let inBoundNodesArr = layer.inbound_nodes[0]; + let inBoundNodesNames = []; + for (let i = 0; i < inBoundNodesArr.length; i++) { + inBoundNodesNames.push(inBoundNodesArr[i][0]); + //ToDo: Implement Logic for Custom Layers + } + layer.inbound_nodes = inBoundNodesNames; + } + + }); + + + } + + function replaceModelInboundKeyWithFirstLayer(modelInfo, replacementLayerPairs) { + let replacementKeys = Object.keys(replacementLayerPairs); + let layers = modelInfo.config.layers; + if (replacementKeys.length > 0) { + replacementKeys.forEach(key => { + for (let i = 0; i < layers.length; i++) { + let inboundNodesArray = layers[i].inbound_nodes; + inboundNodesArray.forEach((node, index, records) => { + if (node === key) { + records[index] = replacementLayerPairs[key]; + } + }); + } + }); + } + } + + return ModelParser; + +}); \ No newline at end of file From 90a403fd57b5cded883d335ca4ee333a7ff9aa09 Mon Sep 17 00:00:00 2001 From: Umesh Date: Mon, 21 Oct 2019 18:58:02 -0500 Subject: [PATCH 04/23] Remove return from main --- src/plugins/ImportKeras/ImportKeras.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/plugins/ImportKeras/ImportKeras.js b/src/plugins/ImportKeras/ImportKeras.js index 3d7b461..83bb8f6 100644 --- a/src/plugins/ImportKeras/ImportKeras.js +++ b/src/plugins/ImportKeras/ImportKeras.js @@ -61,7 +61,8 @@ define([ ImportKeras.prototype.main = async function(callback){ let srcJsonHash = this.getCurrentConfig().srcModel; if (!srcJsonHash) { - return callback(new Error('Keras Json Not Provided'), this.result); + callback(new Error('Keras Json Not Provided'), this.result); + return; } try { this.archName = this.getCurrentConfig().archName; @@ -74,10 +75,10 @@ define([ await this.addConnections(); await this.save('Completed Import Model'); this.result.setSuccess(true); - return callback(null, this.result); + callback(null, this.result); } catch (err) { this.logger.debug(`Something Went Wrong, Error Message: ${err}`); - return callback(err, this.result); + callback(err, this.result); } }; From 8979f696262bab6f418be72d52a67a3aff4ea622 Mon Sep 17 00:00:00 2001 From: Umesh Date: Mon, 21 Oct 2019 19:43:23 -0500 Subject: [PATCH 05/23] Refactor ImportKeras util files --- src/plugins/ImportKeras/ImportKeras.js | 4 +- .../{ImportKeras/utils => }/JSONModelMaps.js | 0 .../utils => }/json-model-parser.js | 0 .../modelJsons/functional_memnn_babi.json | 493 ++++ .../modelJsons/functional_rnn_babi.json | 317 +++ test/test-cases/modelJsons/redshiftModel.json | 2122 +++++++++++++++++ .../modelJsons/sequential_conv_cifar.json | 378 +++ .../modelJsons/sequential_conv_mnist.json | 209 ++ .../modelJsons/sequential_dense.json | 153 ++ .../modelJsons/sequential_nested.json | 125 + .../modelJsons/sequential_subclass.json | 169 ++ 11 files changed, 3968 insertions(+), 2 deletions(-) rename src/plugins/{ImportKeras/utils => }/JSONModelMaps.js (100%) rename src/plugins/{ImportKeras/utils => }/json-model-parser.js (100%) create mode 100644 test/test-cases/modelJsons/functional_memnn_babi.json create mode 100644 test/test-cases/modelJsons/functional_rnn_babi.json create mode 100644 test/test-cases/modelJsons/redshiftModel.json create mode 100644 test/test-cases/modelJsons/sequential_conv_cifar.json create mode 100644 test/test-cases/modelJsons/sequential_conv_mnist.json create mode 100644 test/test-cases/modelJsons/sequential_dense.json create mode 100644 test/test-cases/modelJsons/sequential_nested.json create mode 100644 test/test-cases/modelJsons/sequential_subclass.json diff --git a/src/plugins/ImportKeras/ImportKeras.js b/src/plugins/ImportKeras/ImportKeras.js index 83bb8f6..0e7a943 100644 --- a/src/plugins/ImportKeras/ImportKeras.js +++ b/src/plugins/ImportKeras/ImportKeras.js @@ -11,8 +11,8 @@ define([ 'plugin/PluginConfig', 'text!./metadata.json', 'plugin/PluginBase', - './utils/JSONModelMaps', - './utils/json-model-parser', + '../JSONModelMaps', + '../json-model-parser', ], function ( PluginConfig, pluginMetadata, diff --git a/src/plugins/ImportKeras/utils/JSONModelMaps.js b/src/plugins/JSONModelMaps.js similarity index 100% rename from src/plugins/ImportKeras/utils/JSONModelMaps.js rename to src/plugins/JSONModelMaps.js diff --git a/src/plugins/ImportKeras/utils/json-model-parser.js b/src/plugins/json-model-parser.js similarity index 100% rename from src/plugins/ImportKeras/utils/json-model-parser.js rename to src/plugins/json-model-parser.js diff --git a/test/test-cases/modelJsons/functional_memnn_babi.json b/test/test-cases/modelJsons/functional_memnn_babi.json new file mode 100644 index 0000000..d3864fb --- /dev/null +++ b/test/test-cases/modelJsons/functional_memnn_babi.json @@ -0,0 +1,493 @@ +{ + "class_name": "Model", + "keras_version": "2.3.0", + "config": { + "name": "model_2", + "layers": [ + { + "name": "input_3", + "class_name": "InputLayer", + "config": { + "name": "input_3", + "sparse": false, + "batch_input_shape": [ + null, + 68 + ], + "dtype": "float32" + }, + "inbound_nodes": [] + }, + { + "name": "input_4", + "class_name": "InputLayer", + "config": { + "name": "input_4", + "sparse": false, + "batch_input_shape": [ + null, + 4 + ], + "dtype": "float32" + }, + "inbound_nodes": [] + }, + { + "name": "sequential_1", + "class_name": "Sequential", + "config": { + "name": "sequential_1", + "layers": [ + { + "class_name": "Embedding", + "config": { + "input_dim": 22, + "input_length": null, + "embeddings_initializer": { + "class_name": "RandomUniform", + "config": { + "minval": -0.05, + "seed": null, + "maxval": 0.05 + } + }, + "activity_regularizer": null, + "dtype": "float32", + "batch_input_shape": [ + null, + null + ], + "mask_zero": false, + "name": "embedding_3", + "trainable": true, + "embeddings_constraint": null, + "embeddings_regularizer": null, + "output_dim": 64 + } + }, + { + "class_name": "Dropout", + "config": { + "seed": null, + "rate": 0.3, + "name": "dropout_1", + "noise_shape": null, + "trainable": true, + "dtype": "float32" + } + } + ] + }, + "inbound_nodes": [ + [ + [ + "input_3", + 0, + 0, + {} + ] + ] + ] + }, + { + "name": "sequential_3", + "class_name": "Sequential", + "config": { + "name": "sequential_3", + "layers": [ + { + "class_name": "Embedding", + "config": { + "input_dim": 22, + "input_length": 4, + "embeddings_initializer": { + "class_name": "RandomUniform", + "config": { + "minval": -0.05, + "seed": null, + "maxval": 0.05 + } + }, + "activity_regularizer": null, + "dtype": "float32", + "batch_input_shape": [ + null, + 4 + ], + "mask_zero": false, + "name": "embedding_5", + "trainable": true, + "embeddings_constraint": null, + "embeddings_regularizer": null, + "output_dim": 64 + } + }, + { + "class_name": "Dropout", + "config": { + "seed": null, + "rate": 0.3, + "name": "dropout_3", + "noise_shape": null, + "trainable": true, + "dtype": "float32" + } + } + ] + }, + "inbound_nodes": [ + [ + [ + "input_4", + 0, + 0, + {} + ] + ] + ] + }, + { + "name": "dot_1", + "class_name": "Dot", + "config": { + "name": "dot_1", + "trainable": true, + "normalize": false, + "axes": [ + 2, + 2 + ], + "dtype": "float32" + }, + "inbound_nodes": [ + [ + [ + "sequential_1", + 1, + 0, + {} + ], + [ + "sequential_3", + 1, + 0, + {} + ] + ] + ] + }, + { + "name": "activation_1", + "class_name": "Activation", + "config": { + "name": "activation_1", + "trainable": true, + "activation": "softmax", + "dtype": "float32" + }, + "inbound_nodes": [ + [ + [ + "dot_1", + 0, + 0, + {} + ] + ] + ] + }, + { + "name": "sequential_2", + "class_name": "Sequential", + "config": { + "name": "sequential_2", + "layers": [ + { + "class_name": "Embedding", + "config": { + "input_dim": 22, + "input_length": null, + "embeddings_initializer": { + "class_name": "RandomUniform", + "config": { + "minval": -0.05, + "seed": null, + "maxval": 0.05 + } + }, + "activity_regularizer": null, + "dtype": "float32", + "batch_input_shape": [ + null, + null + ], + "mask_zero": false, + "name": "embedding_4", + "trainable": true, + "embeddings_constraint": null, + "embeddings_regularizer": null, + "output_dim": 4 + } + }, + { + "class_name": "Dropout", + "config": { + "seed": null, + "rate": 0.3, + "name": "dropout_2", + "noise_shape": null, + "trainable": true, + "dtype": "float32" + } + } + ] + }, + "inbound_nodes": [ + [ + [ + "input_3", + 0, + 0, + {} + ] + ] + ] + }, + { + "name": "add_1", + "class_name": "Add", + "config": { + "name": "add_1", + "trainable": true, + "dtype": "float32" + }, + "inbound_nodes": [ + [ + [ + "activation_1", + 0, + 0, + {} + ], + [ + "sequential_2", + 1, + 0, + {} + ] + ] + ] + }, + { + "name": "permute_1", + "class_name": "Permute", + "config": { + "name": "permute_1", + "dims": [ + 2, + 1 + ], + "trainable": true, + "dtype": "float32" + }, + "inbound_nodes": [ + [ + [ + "add_1", + 0, + 0, + {} + ] + ] + ] + }, + { + "name": "concatenate_2", + "class_name": "Concatenate", + "config": { + "name": "concatenate_2", + "trainable": true, + "axis": -1, + "dtype": "float32" + }, + "inbound_nodes": [ + [ + [ + "permute_1", + 0, + 0, + {} + ], + [ + "sequential_3", + 1, + 0, + {} + ] + ] + ] + }, + { + "name": "lstm_3", + "class_name": "LSTM", + "config": { + "recurrent_constraint": null, + "unit_forget_bias": true, + "units": 32, + "bias_initializer": { + "class_name": "Zeros", + "config": {} + }, + "recurrent_regularizer": null, + "implementation": 2, + "stateful": false, + "bias_regularizer": null, + "unroll": false, + "dtype": "float32", + "name": "lstm_3", + "trainable": true, + "go_backwards": false, + "kernel_constraint": null, + "use_bias": true, + "return_state": false, + "recurrent_activation": "sigmoid", + "activity_regularizer": null, + "dropout": 0.0, + "bias_constraint": null, + "recurrent_initializer": { + "class_name": "Orthogonal", + "config": { + "seed": null, + "gain": 1.0 + } + }, + "activation": "tanh", + "kernel_initializer": { + "class_name": "VarianceScaling", + "config": { + "scale": 1.0, + "distribution": "uniform", + "mode": "fan_avg", + "seed": null + } + }, + "kernel_regularizer": null, + "recurrent_dropout": 0.0, + "return_sequences": false + }, + "inbound_nodes": [ + [ + [ + "concatenate_2", + 0, + 0, + {} + ] + ] + ] + }, + { + "name": "dropout_4", + "class_name": "Dropout", + "config": { + "seed": null, + "rate": 0.3, + "name": "dropout_4", + "noise_shape": null, + "trainable": true, + "dtype": "float32" + }, + "inbound_nodes": [ + [ + [ + "lstm_3", + 0, + 0, + {} + ] + ] + ] + }, + { + "name": "dense_2", + "class_name": "Dense", + "config": { + "units": 22, + "bias_constraint": null, + "activity_regularizer": null, + "kernel_regularizer": null, + "bias_initializer": { + "class_name": "Zeros", + "config": {} + }, + "dtype": "float32", + "bias_regularizer": null, + "kernel_initializer": { + "class_name": "VarianceScaling", + "config": { + "scale": 1.0, + "distribution": "uniform", + "mode": "fan_avg", + "seed": null + } + }, + "name": "dense_2", + "trainable": true, + "kernel_constraint": null, + "activation": "linear", + "use_bias": true + }, + "inbound_nodes": [ + [ + [ + "dropout_4", + 0, + 0, + {} + ] + ] + ] + }, + { + "name": "activation_2", + "class_name": "Activation", + "config": { + "name": "activation_2", + "trainable": true, + "activation": "softmax", + "dtype": "float32" + }, + "inbound_nodes": [ + [ + [ + "dense_2", + 0, + 0, + {} + ] + ] + ] + } + ], + "output_layers": [ + [ + "activation_2", + 0, + 0 + ] + ], + "input_layers": [ + [ + "input_3", + 0, + 0 + ], + [ + "input_4", + 0, + 0 + ] + ] + }, + "backend": "tensorflow" +} \ No newline at end of file diff --git a/test/test-cases/modelJsons/functional_rnn_babi.json b/test/test-cases/modelJsons/functional_rnn_babi.json new file mode 100644 index 0000000..50f6774 --- /dev/null +++ b/test/test-cases/modelJsons/functional_rnn_babi.json @@ -0,0 +1,317 @@ +{ + "class_name": "Model", + "keras_version": "2.3.0", + "config": { + "name": "model_1", + "layers": [ + { + "name": "input_1", + "class_name": "InputLayer", + "config": { + "name": "input_1", + "sparse": false, + "batch_input_shape": [ + null, + 552 + ], + "dtype": "int32" + }, + "inbound_nodes": [] + }, + { + "name": "input_2", + "class_name": "InputLayer", + "config": { + "name": "input_2", + "sparse": false, + "batch_input_shape": [ + null, + 5 + ], + "dtype": "int32" + }, + "inbound_nodes": [] + }, + { + "name": "embedding_1", + "class_name": "Embedding", + "config": { + "input_dim": 36, + "input_length": null, + "embeddings_initializer": { + "class_name": "RandomUniform", + "config": { + "minval": -0.05, + "seed": null, + "maxval": 0.05 + } + }, + "activity_regularizer": null, + "dtype": "float32", + "batch_input_shape": [ + null, + null + ], + "mask_zero": false, + "name": "embedding_1", + "trainable": true, + "output_dim": 50, + "embeddings_regularizer": null, + "embeddings_constraint": null + }, + "inbound_nodes": [ + [ + [ + "input_1", + 0, + 0, + {} + ] + ] + ] + }, + { + "name": "embedding_2", + "class_name": "Embedding", + "config": { + "input_dim": 36, + "input_length": null, + "embeddings_initializer": { + "class_name": "RandomUniform", + "config": { + "minval": -0.05, + "seed": null, + "maxval": 0.05 + } + }, + "activity_regularizer": null, + "dtype": "float32", + "batch_input_shape": [ + null, + null + ], + "mask_zero": false, + "name": "embedding_2", + "trainable": true, + "output_dim": 50, + "embeddings_regularizer": null, + "embeddings_constraint": null + }, + "inbound_nodes": [ + [ + [ + "input_2", + 0, + 0, + {} + ] + ] + ] + }, + { + "name": "lstm_1", + "class_name": "LSTM", + "config": { + "recurrent_constraint": null, + "unit_forget_bias": true, + "units": 100, + "bias_initializer": { + "class_name": "Zeros", + "config": {} + }, + "recurrent_regularizer": null, + "implementation": 2, + "stateful": false, + "bias_regularizer": null, + "unroll": false, + "dtype": "float32", + "name": "lstm_1", + "trainable": true, + "go_backwards": false, + "kernel_constraint": null, + "use_bias": true, + "return_state": false, + "recurrent_activation": "sigmoid", + "activity_regularizer": null, + "dropout": 0.0, + "bias_constraint": null, + "recurrent_initializer": { + "class_name": "Orthogonal", + "config": { + "seed": null, + "gain": 1.0 + } + }, + "activation": "tanh", + "kernel_initializer": { + "class_name": "VarianceScaling", + "config": { + "scale": 1.0, + "distribution": "uniform", + "mode": "fan_avg", + "seed": null + } + }, + "kernel_regularizer": null, + "recurrent_dropout": 0.0, + "return_sequences": false + }, + "inbound_nodes": [ + [ + [ + "embedding_1", + 0, + 0, + {} + ] + ] + ] + }, + { + "name": "lstm_2", + "class_name": "LSTM", + "config": { + "recurrent_constraint": null, + "unit_forget_bias": true, + "units": 100, + "bias_initializer": { + "class_name": "Zeros", + "config": {} + }, + "recurrent_regularizer": null, + "implementation": 2, + "stateful": false, + "bias_regularizer": null, + "unroll": false, + "dtype": "float32", + "name": "lstm_2", + "trainable": true, + "go_backwards": false, + "kernel_constraint": null, + "use_bias": true, + "return_state": false, + "recurrent_activation": "sigmoid", + "activity_regularizer": null, + "dropout": 0.0, + "bias_constraint": null, + "recurrent_initializer": { + "class_name": "Orthogonal", + "config": { + "seed": null, + "gain": 1.0 + } + }, + "activation": "tanh", + "kernel_initializer": { + "class_name": "VarianceScaling", + "config": { + "scale": 1.0, + "distribution": "uniform", + "mode": "fan_avg", + "seed": null + } + }, + "kernel_regularizer": null, + "recurrent_dropout": 0.0, + "return_sequences": false + }, + "inbound_nodes": [ + [ + [ + "embedding_2", + 0, + 0, + {} + ] + ] + ] + }, + { + "name": "concatenate_1", + "class_name": "Concatenate", + "config": { + "name": "concatenate_1", + "trainable": true, + "axis": -1, + "dtype": "float32" + }, + "inbound_nodes": [ + [ + [ + "lstm_1", + 0, + 0, + {} + ], + [ + "lstm_2", + 0, + 0, + {} + ] + ] + ] + }, + { + "name": "dense_1", + "class_name": "Dense", + "config": { + "units": 36, + "bias_constraint": null, + "activity_regularizer": null, + "kernel_regularizer": null, + "bias_initializer": { + "class_name": "Zeros", + "config": {} + }, + "dtype": "float32", + "bias_regularizer": null, + "kernel_initializer": { + "class_name": "VarianceScaling", + "config": { + "scale": 1.0, + "distribution": "uniform", + "mode": "fan_avg", + "seed": null + } + }, + "name": "dense_1", + "trainable": true, + "kernel_constraint": null, + "activation": "softmax", + "use_bias": true + }, + "inbound_nodes": [ + [ + [ + "concatenate_1", + 0, + 0, + {} + ] + ] + ] + } + ], + "output_layers": [ + [ + "dense_1", + 0, + 0 + ] + ], + "input_layers": [ + [ + "input_1", + 0, + 0 + ], + [ + "input_2", + 0, + 0 + ] + ] + }, + "backend": "tensorflow" +} \ No newline at end of file diff --git a/test/test-cases/modelJsons/redshiftModel.json b/test/test-cases/modelJsons/redshiftModel.json new file mode 100644 index 0000000..4fe617a --- /dev/null +++ b/test/test-cases/modelJsons/redshiftModel.json @@ -0,0 +1,2122 @@ +{ + "backend": "tensorflow", + "class_name": "RedShiftClassificationModel", + "config": { + "layers": [ + { + "class_name": "InputLayer", + "config": { + "batch_input_shape": [ + null, + 64, + 64, + 5 + ], + "dtype": "float32", + "name": "input_1", + "sparse": false + }, + "name": "input_1", + "inbound_nodes": [] + }, + { + "class_name": "Conv2D", + "config": { + "dilation_rate": [ + 1, + 1 + ], + "trainable": true, + "bias_initializer": { + "class_name": "Zeros", + "config": {} + }, + "data_format": "channels_last", + "name": "conv2d_1", + "strides": [ + 1, + 1 + ], + "filters": 64, + "kernel_constraint": null, + "bias_constraint": null, + "kernel_regularizer": null, + "use_bias": true, + "bias_regularizer": null, + "dtype": "float32", + "kernel_initializer": { + "class_name": "VarianceScaling", + "config": { + "seed": null, + "distribution": "uniform", + "scale": 1.0, + "mode": "fan_avg" + } + }, + "activation": "relu", + "kernel_size": [ + 5, + 5 + ], + "activity_regularizer": null, + "padding": "same" + }, + "name": "conv2d_1", + "inbound_nodes": [ + [ + [ + "input_1", + 0, + 0, + {} + ] + ] + ] + }, + { + "class_name": "AveragePooling2D", + "config": { + "trainable": true, + "data_format": "channels_last", + "dtype": "float32", + "name": "average_pooling2d_1", + "strides": [ + 2, + 2 + ], + "pool_size": [ + 2, + 2 + ], + "padding": "same" + }, + "name": "average_pooling2d_1", + "inbound_nodes": [ + [ + [ + "conv2d_1", + 0, + 0, + {} + ] + ] + ] + }, + { + "class_name": "Conv2D", + "config": { + "dilation_rate": [ + 1, + 1 + ], + "trainable": true, + "bias_initializer": { + "class_name": "Zeros", + "config": {} + }, + "data_format": "channels_last", + "name": "conv2d_2", + "strides": [ + 1, + 1 + ], + "filters": 48, + "kernel_constraint": null, + "bias_constraint": null, + "kernel_regularizer": null, + "use_bias": true, + "bias_regularizer": null, + "dtype": "float32", + "kernel_initializer": { + "class_name": "VarianceScaling", + "config": { + "seed": null, + "distribution": "uniform", + "scale": 1.0, + "mode": "fan_avg" + } + }, + "activation": "relu", + "kernel_size": [ + 1, + 1 + ], + "activity_regularizer": null, + "padding": "same" + }, + "name": "conv2d_2", + "inbound_nodes": [ + [ + [ + "average_pooling2d_1", + 0, + 0, + {} + ] + ] + ] + }, + { + "class_name": "Conv2D", + "config": { + "dilation_rate": [ + 1, + 1 + ], + "trainable": true, + "bias_initializer": { + "class_name": "Zeros", + "config": {} + }, + "data_format": "channels_last", + "name": "conv2d_3", + "strides": [ + 1, + 1 + ], + "filters": 48, + "kernel_constraint": null, + "bias_constraint": null, + "kernel_regularizer": null, + "use_bias": true, + "bias_regularizer": null, + "dtype": "float32", + "kernel_initializer": { + "class_name": "VarianceScaling", + "config": { + "seed": null, + "distribution": "uniform", + "scale": 1.0, + "mode": "fan_avg" + } + }, + "activation": "relu", + "kernel_size": [ + 1, + 1 + ], + "activity_regularizer": null, + "padding": "same" + }, + "name": "conv2d_3", + "inbound_nodes": [ + [ + [ + "average_pooling2d_1", + 0, + 0, + {} + ] + ] + ] + }, + { + "class_name": "Conv2D", + "config": { + "dilation_rate": [ + 1, + 1 + ], + "trainable": true, + "bias_initializer": { + "class_name": "Zeros", + "config": {} + }, + "data_format": "channels_last", + "name": "conv2d_4", + "strides": [ + 1, + 1 + ], + "filters": 48, + "kernel_constraint": null, + "bias_constraint": null, + "kernel_regularizer": null, + "use_bias": true, + "bias_regularizer": null, + "dtype": "float32", + "kernel_initializer": { + "class_name": "VarianceScaling", + "config": { + "seed": null, + "distribution": "uniform", + "scale": 1.0, + "mode": "fan_avg" + } + }, + "activation": "relu", + "kernel_size": [ + 1, + 1 + ], + "activity_regularizer": null, + "padding": "same" + }, + "name": "conv2d_4", + "inbound_nodes": [ + [ + [ + "average_pooling2d_1", + 0, + 0, + {} + ] + ] + ] + }, + { + "class_name": "Conv2D", + "config": { + "dilation_rate": [ + 1, + 1 + ], + "trainable": true, + "bias_initializer": { + "class_name": "Zeros", + "config": {} + }, + "data_format": "channels_last", + "name": "conv2d_5", + "strides": [ + 1, + 1 + ], + "filters": 64, + "kernel_constraint": null, + "bias_constraint": null, + "kernel_regularizer": null, + "use_bias": true, + "bias_regularizer": null, + "dtype": "float32", + "kernel_initializer": { + "class_name": "VarianceScaling", + "config": { + "seed": null, + "distribution": "uniform", + "scale": 1.0, + "mode": "fan_avg" + } + }, + "activation": "relu", + "kernel_size": [ + 1, + 1 + ], + "activity_regularizer": null, + "padding": "same" + }, + "name": "conv2d_5", + "inbound_nodes": [ + [ + [ + "average_pooling2d_1", + 0, + 0, + {} + ] + ] + ] + }, + { + "class_name": "Conv2D", + "config": { + "dilation_rate": [ + 1, + 1 + ], + "trainable": true, + "bias_initializer": { + "class_name": "Zeros", + "config": {} + }, + "data_format": "channels_last", + "name": "conv2d_6", + "strides": [ + 1, + 1 + ], + "filters": 64, + "kernel_constraint": null, + "bias_constraint": null, + "kernel_regularizer": null, + "use_bias": true, + "bias_regularizer": null, + "dtype": "float32", + "kernel_initializer": { + "class_name": "VarianceScaling", + "config": { + "seed": null, + "distribution": "uniform", + "scale": 1.0, + "mode": "fan_avg" + } + }, + "activation": "relu", + "kernel_size": [ + 3, + 3 + ], + "activity_regularizer": null, + "padding": "same" + }, + "name": "conv2d_6", + "inbound_nodes": [ + [ + [ + "conv2d_2", + 0, + 0, + {} + ] + ] + ] + }, + { + "class_name": "Conv2D", + "config": { + "dilation_rate": [ + 1, + 1 + ], + "trainable": true, + "bias_initializer": { + "class_name": "Zeros", + "config": {} + }, + "data_format": "channels_last", + "name": "conv2d_7", + "strides": [ + 1, + 1 + ], + "filters": 64, + "kernel_constraint": null, + "bias_constraint": null, + "kernel_regularizer": null, + "use_bias": true, + "bias_regularizer": null, + "dtype": "float32", + "kernel_initializer": { + "class_name": "VarianceScaling", + "config": { + "seed": null, + "distribution": "uniform", + "scale": 1.0, + "mode": "fan_avg" + } + }, + "activation": "relu", + "kernel_size": [ + 5, + 5 + ], + "activity_regularizer": null, + "padding": "same" + }, + "name": "conv2d_7", + "inbound_nodes": [ + [ + [ + "conv2d_3", + 0, + 0, + {} + ] + ] + ] + }, + { + "class_name": "AveragePooling2D", + "config": { + "trainable": true, + "data_format": "channels_last", + "dtype": "float32", + "name": "average_pooling2d_2", + "strides": [ + 1, + 1 + ], + "pool_size": [ + 2, + 2 + ], + "padding": "same" + }, + "name": "average_pooling2d_2", + "inbound_nodes": [ + [ + [ + "conv2d_4", + 0, + 0, + {} + ] + ] + ] + }, + { + "class_name": "Concatenate", + "config": { + "trainable": true, + "axis": -1, + "dtype": "float32", + "name": "concatenate_1" + }, + "name": "concatenate_1", + "inbound_nodes": [ + [ + [ + "conv2d_5", + 0, + 0, + {} + ], + [ + "conv2d_6", + 0, + 0, + {} + ], + [ + "conv2d_7", + 0, + 0, + {} + ], + [ + "average_pooling2d_2", + 0, + 0, + {} + ] + ] + ] + }, + { + "class_name": "Conv2D", + "config": { + "dilation_rate": [ + 1, + 1 + ], + "trainable": true, + "bias_initializer": { + "class_name": "Zeros", + "config": {} + }, + "data_format": "channels_last", + "name": "conv2d_8", + "strides": [ + 1, + 1 + ], + "filters": 64, + "kernel_constraint": null, + "bias_constraint": null, + "kernel_regularizer": null, + "use_bias": true, + "bias_regularizer": null, + "dtype": "float32", + "kernel_initializer": { + "class_name": "VarianceScaling", + "config": { + "seed": null, + "distribution": "uniform", + "scale": 1.0, + "mode": "fan_avg" + } + }, + "activation": "relu", + "kernel_size": [ + 1, + 1 + ], + "activity_regularizer": null, + "padding": "same" + }, + "name": "conv2d_8", + "inbound_nodes": [ + [ + [ + "concatenate_1", + 0, + 0, + {} + ] + ] + ] + }, + { + "class_name": "Conv2D", + "config": { + "dilation_rate": [ + 1, + 1 + ], + "trainable": true, + "bias_initializer": { + "class_name": "Zeros", + "config": {} + }, + "data_format": "channels_last", + "name": "conv2d_9", + "strides": [ + 1, + 1 + ], + "filters": 64, + "kernel_constraint": null, + "bias_constraint": null, + "kernel_regularizer": null, + "use_bias": true, + "bias_regularizer": null, + "dtype": "float32", + "kernel_initializer": { + "class_name": "VarianceScaling", + "config": { + "seed": null, + "distribution": "uniform", + "scale": 1.0, + "mode": "fan_avg" + } + }, + "activation": "relu", + "kernel_size": [ + 1, + 1 + ], + "activity_regularizer": null, + "padding": "same" + }, + "name": "conv2d_9", + "inbound_nodes": [ + [ + [ + "concatenate_1", + 0, + 0, + {} + ] + ] + ] + }, + { + "class_name": "Conv2D", + "config": { + "dilation_rate": [ + 1, + 1 + ], + "trainable": true, + "bias_initializer": { + "class_name": "Zeros", + "config": {} + }, + "data_format": "channels_last", + "name": "conv2d_10", + "strides": [ + 1, + 1 + ], + "filters": 64, + "kernel_constraint": null, + "bias_constraint": null, + "kernel_regularizer": null, + "use_bias": true, + "bias_regularizer": null, + "dtype": "float32", + "kernel_initializer": { + "class_name": "VarianceScaling", + "config": { + "seed": null, + "distribution": "uniform", + "scale": 1.0, + "mode": "fan_avg" + } + }, + "activation": "relu", + "kernel_size": [ + 1, + 1 + ], + "activity_regularizer": null, + "padding": "same" + }, + "name": "conv2d_10", + "inbound_nodes": [ + [ + [ + "concatenate_1", + 0, + 0, + {} + ] + ] + ] + }, + { + "class_name": "Conv2D", + "config": { + "dilation_rate": [ + 1, + 1 + ], + "trainable": true, + "bias_initializer": { + "class_name": "Zeros", + "config": {} + }, + "data_format": "channels_last", + "name": "conv2d_11", + "strides": [ + 1, + 1 + ], + "filters": 92, + "kernel_constraint": null, + "bias_constraint": null, + "kernel_regularizer": null, + "use_bias": true, + "bias_regularizer": null, + "dtype": "float32", + "kernel_initializer": { + "class_name": "VarianceScaling", + "config": { + "seed": null, + "distribution": "uniform", + "scale": 1.0, + "mode": "fan_avg" + } + }, + "activation": "relu", + "kernel_size": [ + 1, + 1 + ], + "activity_regularizer": null, + "padding": "same" + }, + "name": "conv2d_11", + "inbound_nodes": [ + [ + [ + "concatenate_1", + 0, + 0, + {} + ] + ] + ] + }, + { + "class_name": "Conv2D", + "config": { + "dilation_rate": [ + 1, + 1 + ], + "trainable": true, + "bias_initializer": { + "class_name": "Zeros", + "config": {} + }, + "data_format": "channels_last", + "name": "conv2d_12", + "strides": [ + 1, + 1 + ], + "filters": 92, + "kernel_constraint": null, + "bias_constraint": null, + "kernel_regularizer": null, + "use_bias": true, + "bias_regularizer": null, + "dtype": "float32", + "kernel_initializer": { + "class_name": "VarianceScaling", + "config": { + "seed": null, + "distribution": "uniform", + "scale": 1.0, + "mode": "fan_avg" + } + }, + "activation": "relu", + "kernel_size": [ + 3, + 3 + ], + "activity_regularizer": null, + "padding": "same" + }, + "name": "conv2d_12", + "inbound_nodes": [ + [ + [ + "conv2d_8", + 0, + 0, + {} + ] + ] + ] + }, + { + "class_name": "Conv2D", + "config": { + "dilation_rate": [ + 1, + 1 + ], + "trainable": true, + "bias_initializer": { + "class_name": "Zeros", + "config": {} + }, + "data_format": "channels_last", + "name": "conv2d_13", + "strides": [ + 1, + 1 + ], + "filters": 92, + "kernel_constraint": null, + "bias_constraint": null, + "kernel_regularizer": null, + "use_bias": true, + "bias_regularizer": null, + "dtype": "float32", + "kernel_initializer": { + "class_name": "VarianceScaling", + "config": { + "seed": null, + "distribution": "uniform", + "scale": 1.0, + "mode": "fan_avg" + } + }, + "activation": "relu", + "kernel_size": [ + 5, + 5 + ], + "activity_regularizer": null, + "padding": "same" + }, + "name": "conv2d_13", + "inbound_nodes": [ + [ + [ + "conv2d_9", + 0, + 0, + {} + ] + ] + ] + }, + { + "class_name": "AveragePooling2D", + "config": { + "trainable": true, + "data_format": "channels_last", + "dtype": "float32", + "name": "average_pooling2d_3", + "strides": [ + 1, + 1 + ], + "pool_size": [ + 2, + 2 + ], + "padding": "same" + }, + "name": "average_pooling2d_3", + "inbound_nodes": [ + [ + [ + "conv2d_10", + 0, + 0, + {} + ] + ] + ] + }, + { + "class_name": "Concatenate", + "config": { + "trainable": true, + "axis": -1, + "dtype": "float32", + "name": "concatenate_2" + }, + "name": "concatenate_2", + "inbound_nodes": [ + [ + [ + "conv2d_11", + 0, + 0, + {} + ], + [ + "conv2d_12", + 0, + 0, + {} + ], + [ + "conv2d_13", + 0, + 0, + {} + ], + [ + "average_pooling2d_3", + 0, + 0, + {} + ] + ] + ] + }, + { + "class_name": "AveragePooling2D", + "config": { + "trainable": true, + "data_format": "channels_last", + "dtype": "float32", + "name": "average_pooling2d_4", + "strides": [ + 2, + 2 + ], + "pool_size": [ + 2, + 2 + ], + "padding": "same" + }, + "name": "average_pooling2d_4", + "inbound_nodes": [ + [ + [ + "concatenate_2", + 0, + 0, + {} + ] + ] + ] + }, + { + "class_name": "Conv2D", + "config": { + "dilation_rate": [ + 1, + 1 + ], + "trainable": true, + "bias_initializer": { + "class_name": "Zeros", + "config": {} + }, + "data_format": "channels_last", + "name": "conv2d_14", + "strides": [ + 1, + 1 + ], + "filters": 92, + "kernel_constraint": null, + "bias_constraint": null, + "kernel_regularizer": null, + "use_bias": true, + "bias_regularizer": null, + "dtype": "float32", + "kernel_initializer": { + "class_name": "VarianceScaling", + "config": { + "seed": null, + "distribution": "uniform", + "scale": 1.0, + "mode": "fan_avg" + } + }, + "activation": "relu", + "kernel_size": [ + 1, + 1 + ], + "activity_regularizer": null, + "padding": "same" + }, + "name": "conv2d_14", + "inbound_nodes": [ + [ + [ + "average_pooling2d_4", + 0, + 0, + {} + ] + ] + ] + }, + { + "class_name": "Conv2D", + "config": { + "dilation_rate": [ + 1, + 1 + ], + "trainable": true, + "bias_initializer": { + "class_name": "Zeros", + "config": {} + }, + "data_format": "channels_last", + "name": "conv2d_15", + "strides": [ + 1, + 1 + ], + "filters": 92, + "kernel_constraint": null, + "bias_constraint": null, + "kernel_regularizer": null, + "use_bias": true, + "bias_regularizer": null, + "dtype": "float32", + "kernel_initializer": { + "class_name": "VarianceScaling", + "config": { + "seed": null, + "distribution": "uniform", + "scale": 1.0, + "mode": "fan_avg" + } + }, + "activation": "relu", + "kernel_size": [ + 1, + 1 + ], + "activity_regularizer": null, + "padding": "same" + }, + "name": "conv2d_15", + "inbound_nodes": [ + [ + [ + "average_pooling2d_4", + 0, + 0, + {} + ] + ] + ] + }, + { + "class_name": "Conv2D", + "config": { + "dilation_rate": [ + 1, + 1 + ], + "trainable": true, + "bias_initializer": { + "class_name": "Zeros", + "config": {} + }, + "data_format": "channels_last", + "name": "conv2d_16", + "strides": [ + 1, + 1 + ], + "filters": 92, + "kernel_constraint": null, + "bias_constraint": null, + "kernel_regularizer": null, + "use_bias": true, + "bias_regularizer": null, + "dtype": "float32", + "kernel_initializer": { + "class_name": "VarianceScaling", + "config": { + "seed": null, + "distribution": "uniform", + "scale": 1.0, + "mode": "fan_avg" + } + }, + "activation": "relu", + "kernel_size": [ + 1, + 1 + ], + "activity_regularizer": null, + "padding": "same" + }, + "name": "conv2d_16", + "inbound_nodes": [ + [ + [ + "average_pooling2d_4", + 0, + 0, + {} + ] + ] + ] + }, + { + "class_name": "Conv2D", + "config": { + "dilation_rate": [ + 1, + 1 + ], + "trainable": true, + "bias_initializer": { + "class_name": "Zeros", + "config": {} + }, + "data_format": "channels_last", + "name": "conv2d_17", + "strides": [ + 1, + 1 + ], + "filters": 128, + "kernel_constraint": null, + "bias_constraint": null, + "kernel_regularizer": null, + "use_bias": true, + "bias_regularizer": null, + "dtype": "float32", + "kernel_initializer": { + "class_name": "VarianceScaling", + "config": { + "seed": null, + "distribution": "uniform", + "scale": 1.0, + "mode": "fan_avg" + } + }, + "activation": "relu", + "kernel_size": [ + 1, + 1 + ], + "activity_regularizer": null, + "padding": "same" + }, + "name": "conv2d_17", + "inbound_nodes": [ + [ + [ + "average_pooling2d_4", + 0, + 0, + {} + ] + ] + ] + }, + { + "class_name": "Conv2D", + "config": { + "dilation_rate": [ + 1, + 1 + ], + "trainable": true, + "bias_initializer": { + "class_name": "Zeros", + "config": {} + }, + "data_format": "channels_last", + "name": "conv2d_18", + "strides": [ + 1, + 1 + ], + "filters": 128, + "kernel_constraint": null, + "bias_constraint": null, + "kernel_regularizer": null, + "use_bias": true, + "bias_regularizer": null, + "dtype": "float32", + "kernel_initializer": { + "class_name": "VarianceScaling", + "config": { + "seed": null, + "distribution": "uniform", + "scale": 1.0, + "mode": "fan_avg" + } + }, + "activation": "relu", + "kernel_size": [ + 3, + 3 + ], + "activity_regularizer": null, + "padding": "same" + }, + "name": "conv2d_18", + "inbound_nodes": [ + [ + [ + "conv2d_14", + 0, + 0, + {} + ] + ] + ] + }, + { + "class_name": "Conv2D", + "config": { + "dilation_rate": [ + 1, + 1 + ], + "trainable": true, + "bias_initializer": { + "class_name": "Zeros", + "config": {} + }, + "data_format": "channels_last", + "name": "conv2d_19", + "strides": [ + 1, + 1 + ], + "filters": 128, + "kernel_constraint": null, + "bias_constraint": null, + "kernel_regularizer": null, + "use_bias": true, + "bias_regularizer": null, + "dtype": "float32", + "kernel_initializer": { + "class_name": "VarianceScaling", + "config": { + "seed": null, + "distribution": "uniform", + "scale": 1.0, + "mode": "fan_avg" + } + }, + "activation": "relu", + "kernel_size": [ + 5, + 5 + ], + "activity_regularizer": null, + "padding": "same" + }, + "name": "conv2d_19", + "inbound_nodes": [ + [ + [ + "conv2d_15", + 0, + 0, + {} + ] + ] + ] + }, + { + "class_name": "AveragePooling2D", + "config": { + "trainable": true, + "data_format": "channels_last", + "dtype": "float32", + "name": "average_pooling2d_5", + "strides": [ + 1, + 1 + ], + "pool_size": [ + 2, + 2 + ], + "padding": "same" + }, + "name": "average_pooling2d_5", + "inbound_nodes": [ + [ + [ + "conv2d_16", + 0, + 0, + {} + ] + ] + ] + }, + { + "class_name": "Concatenate", + "config": { + "trainable": true, + "axis": -1, + "dtype": "float32", + "name": "concatenate_3" + }, + "name": "concatenate_3", + "inbound_nodes": [ + [ + [ + "conv2d_17", + 0, + 0, + {} + ], + [ + "conv2d_18", + 0, + 0, + {} + ], + [ + "conv2d_19", + 0, + 0, + {} + ], + [ + "average_pooling2d_5", + 0, + 0, + {} + ] + ] + ] + }, + { + "class_name": "Conv2D", + "config": { + "dilation_rate": [ + 1, + 1 + ], + "trainable": true, + "bias_initializer": { + "class_name": "Zeros", + "config": {} + }, + "data_format": "channels_last", + "name": "conv2d_20", + "strides": [ + 1, + 1 + ], + "filters": 92, + "kernel_constraint": null, + "bias_constraint": null, + "kernel_regularizer": null, + "use_bias": true, + "bias_regularizer": null, + "dtype": "float32", + "kernel_initializer": { + "class_name": "VarianceScaling", + "config": { + "seed": null, + "distribution": "uniform", + "scale": 1.0, + "mode": "fan_avg" + } + }, + "activation": "relu", + "kernel_size": [ + 1, + 1 + ], + "activity_regularizer": null, + "padding": "same" + }, + "name": "conv2d_20", + "inbound_nodes": [ + [ + [ + "concatenate_3", + 0, + 0, + {} + ] + ] + ] + }, + { + "class_name": "Conv2D", + "config": { + "dilation_rate": [ + 1, + 1 + ], + "trainable": true, + "bias_initializer": { + "class_name": "Zeros", + "config": {} + }, + "data_format": "channels_last", + "name": "conv2d_21", + "strides": [ + 1, + 1 + ], + "filters": 92, + "kernel_constraint": null, + "bias_constraint": null, + "kernel_regularizer": null, + "use_bias": true, + "bias_regularizer": null, + "dtype": "float32", + "kernel_initializer": { + "class_name": "VarianceScaling", + "config": { + "seed": null, + "distribution": "uniform", + "scale": 1.0, + "mode": "fan_avg" + } + }, + "activation": "relu", + "kernel_size": [ + 1, + 1 + ], + "activity_regularizer": null, + "padding": "same" + }, + "name": "conv2d_21", + "inbound_nodes": [ + [ + [ + "concatenate_3", + 0, + 0, + {} + ] + ] + ] + }, + { + "class_name": "Conv2D", + "config": { + "dilation_rate": [ + 1, + 1 + ], + "trainable": true, + "bias_initializer": { + "class_name": "Zeros", + "config": {} + }, + "data_format": "channels_last", + "name": "conv2d_22", + "strides": [ + 1, + 1 + ], + "filters": 92, + "kernel_constraint": null, + "bias_constraint": null, + "kernel_regularizer": null, + "use_bias": true, + "bias_regularizer": null, + "dtype": "float32", + "kernel_initializer": { + "class_name": "VarianceScaling", + "config": { + "seed": null, + "distribution": "uniform", + "scale": 1.0, + "mode": "fan_avg" + } + }, + "activation": "relu", + "kernel_size": [ + 1, + 1 + ], + "activity_regularizer": null, + "padding": "same" + }, + "name": "conv2d_22", + "inbound_nodes": [ + [ + [ + "concatenate_3", + 0, + 0, + {} + ] + ] + ] + }, + { + "class_name": "Conv2D", + "config": { + "dilation_rate": [ + 1, + 1 + ], + "trainable": true, + "bias_initializer": { + "class_name": "Zeros", + "config": {} + }, + "data_format": "channels_last", + "name": "conv2d_23", + "strides": [ + 1, + 1 + ], + "filters": 128, + "kernel_constraint": null, + "bias_constraint": null, + "kernel_regularizer": null, + "use_bias": true, + "bias_regularizer": null, + "dtype": "float32", + "kernel_initializer": { + "class_name": "VarianceScaling", + "config": { + "seed": null, + "distribution": "uniform", + "scale": 1.0, + "mode": "fan_avg" + } + }, + "activation": "relu", + "kernel_size": [ + 1, + 1 + ], + "activity_regularizer": null, + "padding": "same" + }, + "name": "conv2d_23", + "inbound_nodes": [ + [ + [ + "concatenate_3", + 0, + 0, + {} + ] + ] + ] + }, + { + "class_name": "Conv2D", + "config": { + "dilation_rate": [ + 1, + 1 + ], + "trainable": true, + "bias_initializer": { + "class_name": "Zeros", + "config": {} + }, + "data_format": "channels_last", + "name": "conv2d_24", + "strides": [ + 1, + 1 + ], + "filters": 128, + "kernel_constraint": null, + "bias_constraint": null, + "kernel_regularizer": null, + "use_bias": true, + "bias_regularizer": null, + "dtype": "float32", + "kernel_initializer": { + "class_name": "VarianceScaling", + "config": { + "seed": null, + "distribution": "uniform", + "scale": 1.0, + "mode": "fan_avg" + } + }, + "activation": "relu", + "kernel_size": [ + 3, + 3 + ], + "activity_regularizer": null, + "padding": "same" + }, + "name": "conv2d_24", + "inbound_nodes": [ + [ + [ + "conv2d_20", + 0, + 0, + {} + ] + ] + ] + }, + { + "class_name": "Conv2D", + "config": { + "dilation_rate": [ + 1, + 1 + ], + "trainable": true, + "bias_initializer": { + "class_name": "Zeros", + "config": {} + }, + "data_format": "channels_last", + "name": "conv2d_25", + "strides": [ + 1, + 1 + ], + "filters": 128, + "kernel_constraint": null, + "bias_constraint": null, + "kernel_regularizer": null, + "use_bias": true, + "bias_regularizer": null, + "dtype": "float32", + "kernel_initializer": { + "class_name": "VarianceScaling", + "config": { + "seed": null, + "distribution": "uniform", + "scale": 1.0, + "mode": "fan_avg" + } + }, + "activation": "relu", + "kernel_size": [ + 5, + 5 + ], + "activity_regularizer": null, + "padding": "same" + }, + "name": "conv2d_25", + "inbound_nodes": [ + [ + [ + "conv2d_21", + 0, + 0, + {} + ] + ] + ] + }, + { + "class_name": "AveragePooling2D", + "config": { + "trainable": true, + "data_format": "channels_last", + "dtype": "float32", + "name": "average_pooling2d_6", + "strides": [ + 1, + 1 + ], + "pool_size": [ + 2, + 2 + ], + "padding": "same" + }, + "name": "average_pooling2d_6", + "inbound_nodes": [ + [ + [ + "conv2d_22", + 0, + 0, + {} + ] + ] + ] + }, + { + "class_name": "Concatenate", + "config": { + "trainable": true, + "axis": -1, + "dtype": "float32", + "name": "concatenate_4" + }, + "name": "concatenate_4", + "inbound_nodes": [ + [ + [ + "conv2d_23", + 0, + 0, + {} + ], + [ + "conv2d_24", + 0, + 0, + {} + ], + [ + "conv2d_25", + 0, + 0, + {} + ], + [ + "average_pooling2d_6", + 0, + 0, + {} + ] + ] + ] + }, + { + "class_name": "AveragePooling2D", + "config": { + "trainable": true, + "data_format": "channels_last", + "dtype": "float32", + "name": "average_pooling2d_7", + "strides": [ + 2, + 2 + ], + "pool_size": [ + 2, + 2 + ], + "padding": "same" + }, + "name": "average_pooling2d_7", + "inbound_nodes": [ + [ + [ + "concatenate_4", + 0, + 0, + {} + ] + ] + ] + }, + { + "class_name": "Conv2D", + "config": { + "dilation_rate": [ + 1, + 1 + ], + "trainable": true, + "bias_initializer": { + "class_name": "Zeros", + "config": {} + }, + "data_format": "channels_last", + "name": "conv2d_26", + "strides": [ + 1, + 1 + ], + "filters": 92, + "kernel_constraint": null, + "bias_constraint": null, + "kernel_regularizer": null, + "use_bias": true, + "bias_regularizer": null, + "dtype": "float32", + "kernel_initializer": { + "class_name": "VarianceScaling", + "config": { + "seed": null, + "distribution": "uniform", + "scale": 1.0, + "mode": "fan_avg" + } + }, + "activation": "relu", + "kernel_size": [ + 1, + 1 + ], + "activity_regularizer": null, + "padding": "same" + }, + "name": "conv2d_26", + "inbound_nodes": [ + [ + [ + "average_pooling2d_7", + 0, + 0, + {} + ] + ] + ] + }, + { + "class_name": "Conv2D", + "config": { + "dilation_rate": [ + 1, + 1 + ], + "trainable": true, + "bias_initializer": { + "class_name": "Zeros", + "config": {} + }, + "data_format": "channels_last", + "name": "conv2d_27", + "strides": [ + 1, + 1 + ], + "filters": 92, + "kernel_constraint": null, + "bias_constraint": null, + "kernel_regularizer": null, + "use_bias": true, + "bias_regularizer": null, + "dtype": "float32", + "kernel_initializer": { + "class_name": "VarianceScaling", + "config": { + "seed": null, + "distribution": "uniform", + "scale": 1.0, + "mode": "fan_avg" + } + }, + "activation": "relu", + "kernel_size": [ + 1, + 1 + ], + "activity_regularizer": null, + "padding": "same" + }, + "name": "conv2d_27", + "inbound_nodes": [ + [ + [ + "average_pooling2d_7", + 0, + 0, + {} + ] + ] + ] + }, + { + "class_name": "Conv2D", + "config": { + "dilation_rate": [ + 1, + 1 + ], + "trainable": true, + "bias_initializer": { + "class_name": "Zeros", + "config": {} + }, + "data_format": "channels_last", + "name": "conv2d_28", + "strides": [ + 1, + 1 + ], + "filters": 128, + "kernel_constraint": null, + "bias_constraint": null, + "kernel_regularizer": null, + "use_bias": true, + "bias_regularizer": null, + "dtype": "float32", + "kernel_initializer": { + "class_name": "VarianceScaling", + "config": { + "seed": null, + "distribution": "uniform", + "scale": 1.0, + "mode": "fan_avg" + } + }, + "activation": "relu", + "kernel_size": [ + 1, + 1 + ], + "activity_regularizer": null, + "padding": "same" + }, + "name": "conv2d_28", + "inbound_nodes": [ + [ + [ + "average_pooling2d_7", + 0, + 0, + {} + ] + ] + ] + }, + { + "class_name": "Conv2D", + "config": { + "dilation_rate": [ + 1, + 1 + ], + "trainable": true, + "bias_initializer": { + "class_name": "Zeros", + "config": {} + }, + "data_format": "channels_last", + "name": "conv2d_29", + "strides": [ + 1, + 1 + ], + "filters": 128, + "kernel_constraint": null, + "bias_constraint": null, + "kernel_regularizer": null, + "use_bias": true, + "bias_regularizer": null, + "dtype": "float32", + "kernel_initializer": { + "class_name": "VarianceScaling", + "config": { + "seed": null, + "distribution": "uniform", + "scale": 1.0, + "mode": "fan_avg" + } + }, + "activation": "relu", + "kernel_size": [ + 3, + 3 + ], + "activity_regularizer": null, + "padding": "same" + }, + "name": "conv2d_29", + "inbound_nodes": [ + [ + [ + "conv2d_26", + 0, + 0, + {} + ] + ] + ] + }, + { + "class_name": "AveragePooling2D", + "config": { + "trainable": true, + "data_format": "channels_last", + "dtype": "float32", + "name": "average_pooling2d_8", + "strides": [ + 1, + 1 + ], + "pool_size": [ + 2, + 2 + ], + "padding": "same" + }, + "name": "average_pooling2d_8", + "inbound_nodes": [ + [ + [ + "conv2d_27", + 0, + 0, + {} + ] + ] + ] + }, + { + "class_name": "Concatenate", + "config": { + "trainable": true, + "axis": -1, + "dtype": "float32", + "name": "concatenate_5" + }, + "name": "concatenate_5", + "inbound_nodes": [ + [ + [ + "conv2d_28", + 0, + 0, + {} + ], + [ + "conv2d_29", + 0, + 0, + {} + ], + [ + "average_pooling2d_8", + 0, + 0, + {} + ] + ] + ] + }, + { + "class_name": "Flatten", + "config": { + "trainable": true, + "data_format": "channels_last", + "dtype": "float32", + "name": "flatten_1" + }, + "name": "flatten_1", + "inbound_nodes": [ + [ + [ + "concatenate_5", + 0, + 0, + {} + ] + ] + ] + }, + { + "class_name": "Dense", + "config": { + "trainable": true, + "bias_initializer": { + "class_name": "Zeros", + "config": {} + }, + "name": "dense_2", + "units": 1024, + "kernel_constraint": null, + "bias_constraint": null, + "kernel_regularizer": null, + "use_bias": true, + "bias_regularizer": null, + "dtype": "float32", + "kernel_initializer": { + "class_name": "VarianceScaling", + "config": { + "seed": null, + "distribution": "uniform", + "scale": 1.0, + "mode": "fan_avg" + } + }, + "activation": "relu", + "activity_regularizer": null + }, + "name": "dense_2", + "inbound_nodes": [ + [ + [ + "flatten_1", + 0, + 0, + {} + ] + ] + ] + }, + { + "class_name": "Dense", + "config": { + "trainable": true, + "bias_initializer": { + "class_name": "Zeros", + "config": {} + }, + "name": "dense_1", + "units": 32, + "kernel_constraint": null, + "bias_constraint": null, + "kernel_regularizer": null, + "use_bias": true, + "bias_regularizer": null, + "dtype": "float32", + "kernel_initializer": { + "class_name": "VarianceScaling", + "config": { + "seed": null, + "distribution": "uniform", + "scale": 1.0, + "mode": "fan_avg" + } + }, + "activation": "softmax", + "activity_regularizer": null + }, + "name": "dense_1", + "inbound_nodes": [ + [ + [ + "dense_2", + 0, + 0, + {} + ] + ] + ] + } + ], + "output_layers": [ + [ + "dense_1", + 0, + 0 + ] + ], + "name": "redshiftclassificationmodel_1", + "input_layers": [ + [ + "input_1", + 0, + 0 + ] + ] + }, + "keras_version": "2.3.0" +} \ No newline at end of file diff --git a/test/test-cases/modelJsons/sequential_conv_cifar.json b/test/test-cases/modelJsons/sequential_conv_cifar.json new file mode 100644 index 0000000..8b8239e --- /dev/null +++ b/test/test-cases/modelJsons/sequential_conv_cifar.json @@ -0,0 +1,378 @@ +{ + "class_name": "Sequential", + "keras_version": "2.3.0", + "config": { + "name": "sequential_6", + "layers": [ + { + "class_name": "Conv2D", + "config": { + "data_format": "channels_last", + "strides": [ + 1, + 1 + ], + "bias_constraint": null, + "activity_regularizer": null, + "kernel_regularizer": null, + "bias_initializer": { + "class_name": "Zeros", + "config": {} + }, + "padding": "same", + "dtype": "float32", + "kernel_size": [ + 3, + 3 + ], + "bias_regularizer": null, + "dilation_rate": [ + 1, + 1 + ], + "batch_input_shape": [ + null, + 64, + 64, + 3 + ], + "kernel_initializer": { + "class_name": "VarianceScaling", + "config": { + "scale": 1.0, + "distribution": "uniform", + "mode": "fan_avg", + "seed": null + } + }, + "name": "conv2d_3", + "filters": 32, + "trainable": true, + "kernel_constraint": null, + "activation": "linear", + "use_bias": true + } + }, + { + "class_name": "Activation", + "config": { + "name": "activation_4", + "trainable": true, + "activation": "relu", + "dtype": "float32" + } + }, + { + "class_name": "Conv2D", + "config": { + "data_format": "channels_last", + "strides": [ + 1, + 1 + ], + "bias_constraint": null, + "activity_regularizer": null, + "kernel_regularizer": null, + "bias_initializer": { + "class_name": "Zeros", + "config": {} + }, + "padding": "valid", + "dtype": "float32", + "kernel_size": [ + 3, + 3 + ], + "bias_regularizer": null, + "dilation_rate": [ + 1, + 1 + ], + "kernel_initializer": { + "class_name": "VarianceScaling", + "config": { + "scale": 1.0, + "distribution": "uniform", + "mode": "fan_avg", + "seed": null + } + }, + "name": "conv2d_4", + "filters": 32, + "trainable": true, + "kernel_constraint": null, + "activation": "linear", + "use_bias": true + } + }, + { + "class_name": "Activation", + "config": { + "name": "activation_5", + "trainable": true, + "activation": "relu", + "dtype": "float32" + } + }, + { + "class_name": "MaxPooling2D", + "config": { + "data_format": "channels_last", + "pool_size": [ + 2, + 2 + ], + "name": "max_pooling2d_2", + "trainable": true, + "padding": "valid", + "strides": [ + 2, + 2 + ], + "dtype": "float32" + } + }, + { + "class_name": "Dropout", + "config": { + "seed": null, + "rate": 0.25, + "name": "dropout_8", + "noise_shape": null, + "trainable": true, + "dtype": "float32" + } + }, + { + "class_name": "Conv2D", + "config": { + "data_format": "channels_last", + "strides": [ + 1, + 1 + ], + "bias_constraint": null, + "activity_regularizer": null, + "kernel_regularizer": null, + "bias_initializer": { + "class_name": "Zeros", + "config": {} + }, + "padding": "same", + "dtype": "float32", + "kernel_size": [ + 3, + 3 + ], + "bias_regularizer": null, + "dilation_rate": [ + 1, + 1 + ], + "kernel_initializer": { + "class_name": "VarianceScaling", + "config": { + "scale": 1.0, + "distribution": "uniform", + "mode": "fan_avg", + "seed": null + } + }, + "name": "conv2d_5", + "filters": 64, + "trainable": true, + "kernel_constraint": null, + "activation": "linear", + "use_bias": true + } + }, + { + "class_name": "Activation", + "config": { + "name": "activation_6", + "trainable": true, + "activation": "relu", + "dtype": "float32" + } + }, + { + "class_name": "Conv2D", + "config": { + "data_format": "channels_last", + "strides": [ + 1, + 1 + ], + "bias_constraint": null, + "activity_regularizer": null, + "kernel_regularizer": null, + "bias_initializer": { + "class_name": "Zeros", + "config": {} + }, + "padding": "valid", + "dtype": "float32", + "kernel_size": [ + 3, + 3 + ], + "bias_regularizer": null, + "dilation_rate": [ + 1, + 1 + ], + "kernel_initializer": { + "class_name": "VarianceScaling", + "config": { + "scale": 1.0, + "distribution": "uniform", + "mode": "fan_avg", + "seed": null + } + }, + "name": "conv2d_6", + "filters": 64, + "trainable": true, + "kernel_constraint": null, + "activation": "linear", + "use_bias": true + } + }, + { + "class_name": "Activation", + "config": { + "name": "activation_7", + "trainable": true, + "activation": "relu", + "dtype": "float32" + } + }, + { + "class_name": "MaxPooling2D", + "config": { + "data_format": "channels_last", + "pool_size": [ + 2, + 2 + ], + "name": "max_pooling2d_3", + "trainable": true, + "padding": "valid", + "strides": [ + 2, + 2 + ], + "dtype": "float32" + } + }, + { + "class_name": "Dropout", + "config": { + "seed": null, + "rate": 0.25, + "name": "dropout_9", + "noise_shape": null, + "trainable": true, + "dtype": "float32" + } + }, + { + "class_name": "Flatten", + "config": { + "name": "flatten_2", + "trainable": true, + "data_format": "channels_last", + "dtype": "float32" + } + }, + { + "class_name": "Dense", + "config": { + "units": 512, + "bias_constraint": null, + "activity_regularizer": null, + "kernel_regularizer": null, + "bias_initializer": { + "class_name": "Zeros", + "config": {} + }, + "dtype": "float32", + "bias_regularizer": null, + "kernel_initializer": { + "class_name": "VarianceScaling", + "config": { + "scale": 1.0, + "distribution": "uniform", + "mode": "fan_avg", + "seed": null + } + }, + "name": "dense_11", + "trainable": true, + "kernel_constraint": null, + "activation": "linear", + "use_bias": true + } + }, + { + "class_name": "Activation", + "config": { + "name": "activation_8", + "trainable": true, + "activation": "relu", + "dtype": "float32" + } + }, + { + "class_name": "Dropout", + "config": { + "seed": null, + "rate": 0.5, + "name": "dropout_10", + "noise_shape": null, + "trainable": true, + "dtype": "float32" + } + }, + { + "class_name": "Dense", + "config": { + "units": 10, + "bias_constraint": null, + "activity_regularizer": null, + "kernel_regularizer": null, + "bias_initializer": { + "class_name": "Zeros", + "config": {} + }, + "dtype": "float32", + "bias_regularizer": null, + "kernel_initializer": { + "class_name": "VarianceScaling", + "config": { + "scale": 1.0, + "distribution": "uniform", + "mode": "fan_avg", + "seed": null + } + }, + "name": "dense_12", + "trainable": true, + "kernel_constraint": null, + "activation": "linear", + "use_bias": true + } + }, + { + "class_name": "Activation", + "config": { + "name": "activation_9", + "trainable": true, + "activation": "softmax", + "dtype": "float32" + } + } + ] + }, + "backend": "tensorflow" +} \ No newline at end of file diff --git a/test/test-cases/modelJsons/sequential_conv_mnist.json b/test/test-cases/modelJsons/sequential_conv_mnist.json new file mode 100644 index 0000000..813d28a --- /dev/null +++ b/test/test-cases/modelJsons/sequential_conv_mnist.json @@ -0,0 +1,209 @@ +{ + "class_name": "Sequential", + "keras_version": "2.3.0", + "config": { + "name": "sequential_5", + "layers": [ + { + "class_name": "Conv2D", + "config": { + "data_format": "channels_last", + "strides": [ + 1, + 1 + ], + "bias_constraint": null, + "activity_regularizer": null, + "kernel_regularizer": null, + "bias_initializer": { + "class_name": "Zeros", + "config": {} + }, + "padding": "valid", + "dtype": "float32", + "kernel_size": [ + 3, + 3 + ], + "bias_regularizer": null, + "dilation_rate": [ + 1, + 1 + ], + "batch_input_shape": [ + null, + 28, + 28, + 1 + ], + "kernel_initializer": { + "class_name": "VarianceScaling", + "config": { + "scale": 1.0, + "distribution": "uniform", + "mode": "fan_avg", + "seed": null + } + }, + "name": "conv2d_1", + "filters": 32, + "trainable": true, + "kernel_constraint": null, + "activation": "relu", + "use_bias": true + } + }, + { + "class_name": "Conv2D", + "config": { + "data_format": "channels_last", + "strides": [ + 1, + 1 + ], + "bias_constraint": null, + "activity_regularizer": null, + "kernel_regularizer": null, + "bias_initializer": { + "class_name": "Zeros", + "config": {} + }, + "padding": "valid", + "dtype": "float32", + "kernel_size": [ + 3, + 3 + ], + "bias_regularizer": null, + "dilation_rate": [ + 1, + 1 + ], + "kernel_initializer": { + "class_name": "VarianceScaling", + "config": { + "scale": 1.0, + "distribution": "uniform", + "mode": "fan_avg", + "seed": null + } + }, + "name": "conv2d_2", + "filters": 64, + "trainable": true, + "kernel_constraint": null, + "activation": "relu", + "use_bias": true + } + }, + { + "class_name": "MaxPooling2D", + "config": { + "data_format": "channels_last", + "pool_size": [ + 2, + 2 + ], + "name": "max_pooling2d_1", + "trainable": true, + "padding": "valid", + "strides": [ + 2, + 2 + ], + "dtype": "float32" + } + }, + { + "class_name": "Dropout", + "config": { + "seed": null, + "rate": 0.25, + "name": "dropout_6", + "noise_shape": null, + "trainable": true, + "dtype": "float32" + } + }, + { + "class_name": "Flatten", + "config": { + "name": "flatten_1", + "trainable": true, + "data_format": "channels_last", + "dtype": "float32" + } + }, + { + "class_name": "Dense", + "config": { + "units": 128, + "bias_constraint": null, + "activity_regularizer": null, + "kernel_regularizer": null, + "bias_initializer": { + "class_name": "Zeros", + "config": {} + }, + "dtype": "float32", + "bias_regularizer": null, + "kernel_initializer": { + "class_name": "VarianceScaling", + "config": { + "scale": 1.0, + "distribution": "uniform", + "mode": "fan_avg", + "seed": null + } + }, + "name": "dense_9", + "trainable": true, + "kernel_constraint": null, + "activation": "relu", + "use_bias": true + } + }, + { + "class_name": "Dropout", + "config": { + "seed": null, + "rate": 0.5, + "name": "dropout_7", + "noise_shape": null, + "trainable": true, + "dtype": "float32" + } + }, + { + "class_name": "Dense", + "config": { + "units": 10, + "bias_constraint": null, + "activity_regularizer": null, + "kernel_regularizer": null, + "bias_initializer": { + "class_name": "Zeros", + "config": {} + }, + "dtype": "float32", + "bias_regularizer": null, + "kernel_initializer": { + "class_name": "VarianceScaling", + "config": { + "scale": 1.0, + "distribution": "uniform", + "mode": "fan_avg", + "seed": null + } + }, + "name": "dense_10", + "trainable": true, + "kernel_constraint": null, + "activation": "softmax", + "use_bias": true + } + } + ] + }, + "backend": "tensorflow" +} \ No newline at end of file diff --git a/test/test-cases/modelJsons/sequential_dense.json b/test/test-cases/modelJsons/sequential_dense.json new file mode 100644 index 0000000..b3ea262 --- /dev/null +++ b/test/test-cases/modelJsons/sequential_dense.json @@ -0,0 +1,153 @@ +{ + "class_name": "Sequential", + "keras_version": "2.3.0", + "config": { + "name": "sequential_4", + "layers": [ + { + "class_name": "Dense", + "config": { + "units": 32, + "bias_constraint": { + "class_name": "MinMaxNorm", + "config": { + "min_value": -1, + "max_value": 1, + "rate": 1.0, + "axis": 0 + } + }, + "activity_regularizer": null, + "kernel_regularizer": null, + "bias_initializer": { + "class_name": "VarianceScaling", + "config": { + "scale": 1.0, + "distribution": "normal", + "mode": "fan_avg", + "seed": 33 + } + }, + "dtype": "float32", + "bias_regularizer": null, + "batch_input_shape": [ + null, + 32 + ], + "kernel_initializer": { + "class_name": "VarianceScaling", + "config": { + "scale": 1.0, + "distribution": "uniform", + "mode": "fan_avg", + "seed": 46 + } + }, + "name": "dense_3", + "trainable": true, + "kernel_constraint": { + "class_name": "MaxNorm", + "config": { + "max_value": 1.5, + "axis": 0 + } + }, + "activation": "linear", + "use_bias": true + } + }, + { + "class_name": "Activation", + "config": { + "name": "activation_3", + "trainable": true, + "activation": "relu", + "dtype": "float32" + } + }, + { + "class_name": "Dense", + "config": { + "units": 32, + "bias_constraint": null, + "activity_regularizer": { + "class_name": "L1L2", + "config": { + "l2": 0.05000000074505806, + "l1": 0.05000000074505806 + } + }, + "kernel_regularizer": null, + "bias_initializer": { + "class_name": "Zeros", + "config": {} + }, + "dtype": "float32", + "bias_regularizer": null, + "kernel_initializer": { + "class_name": "VarianceScaling", + "config": { + "scale": 1.0, + "distribution": "uniform", + "mode": "fan_avg", + "seed": 46 + } + }, + "name": "dense_4", + "trainable": true, + "kernel_constraint": { + "class_name": "MaxNorm", + "config": { + "max_value": 1.5, + "axis": 0 + } + }, + "activation": "tanh", + "use_bias": false + } + }, + { + "class_name": "Dense", + "config": { + "units": 10, + "bias_constraint": null, + "activity_regularizer": { + "class_name": "L1L2", + "config": { + "l2": 0.05000000074505806, + "l1": 0.05000000074505806 + } + }, + "kernel_regularizer": null, + "bias_initializer": { + "class_name": "Zeros", + "config": {} + }, + "dtype": "float32", + "bias_regularizer": null, + "kernel_initializer": { + "class_name": "VarianceScaling", + "config": { + "scale": 1.0, + "distribution": "uniform", + "mode": "fan_avg", + "seed": 46 + } + }, + "name": "dense_5", + "trainable": true, + "kernel_constraint": { + "class_name": "MaxNorm", + "config": { + "max_value": 1.5, + "axis": 0 + } + }, + "activation": "softmax", + "use_bias": false + } + } + ] + }, + "backend": "tensorflow" +} \ No newline at end of file diff --git a/test/test-cases/modelJsons/sequential_nested.json b/test/test-cases/modelJsons/sequential_nested.json new file mode 100644 index 0000000..eb56d63 --- /dev/null +++ b/test/test-cases/modelJsons/sequential_nested.json @@ -0,0 +1,125 @@ +{ + "class_name": "Sequential", + "config": { + "name": "sequential_11", + "layers": [ + { + "class_name": "Dense", + "config": { + "bias_constraint": null, + "units": 32, + "kernel_initializer": { + "class_name": "VarianceScaling", + "config": { + "seed": null, + "mode": "fan_avg", + "distribution": "uniform", + "scale": 1.0 + } + }, + "activity_regularizer": null, + "dtype": "float32", + "name": "dense_16", + "kernel_regularizer": null, + "kernel_constraint": null, + "activation": "relu", + "trainable": true, + "use_bias": true, + "bias_initializer": { + "class_name": "Zeros", + "config": {} + }, + "batch_input_shape": [ + null, + 32 + ], + "bias_regularizer": null + } + }, + { + "class_name": "Sequential", + "config": { + "name": "sequential_10", + "layers": [ + { + "class_name": "Sequential", + "config": { + "name": "sequential_9", + "layers": [ + { + "class_name": "Dense", + "config": { + "bias_constraint": null, + "units": 32, + "kernel_initializer": { + "class_name": "VarianceScaling", + "config": { + "seed": null, + "mode": "fan_avg", + "distribution": "uniform", + "scale": 1.0 + } + }, + "activity_regularizer": null, + "dtype": "float32", + "name": "dense_14", + "kernel_regularizer": null, + "kernel_constraint": null, + "activation": "relu", + "trainable": true, + "use_bias": true, + "bias_initializer": { + "class_name": "Zeros", + "config": {} + }, + "batch_input_shape": [ + null, + 32 + ], + "bias_regularizer": null + } + }, + { + "class_name": "Dense", + "config": { + "bias_constraint": null, + "units": 32, + "kernel_initializer": { + "class_name": "VarianceScaling", + "config": { + "seed": null, + "mode": "fan_avg", + "distribution": "uniform", + "scale": 1.0 + } + }, + "activity_regularizer": null, + "dtype": "float32", + "name": "dense_15", + "kernel_regularizer": null, + "kernel_constraint": null, + "activation": "relu", + "trainable": true, + "use_bias": true, + "bias_initializer": { + "class_name": "Zeros", + "config": {} + }, + "batch_input_shape": [ + null, + 32 + ], + "bias_regularizer": null + } + } + ] + } + } + ] + } + } + ] + }, + "keras_version": "2.3.0", + "backend": "tensorflow" +} \ No newline at end of file diff --git a/test/test-cases/modelJsons/sequential_subclass.json b/test/test-cases/modelJsons/sequential_subclass.json new file mode 100644 index 0000000..a7f5a9b --- /dev/null +++ b/test/test-cases/modelJsons/sequential_subclass.json @@ -0,0 +1,169 @@ +{ + "class_name": "SequentialSubClass", + "keras_version": "2.3.0", + "config": { + "name": "sequentialsubclass_1", + "layers": [ + { + "class_name": "Dense", + "config": { + "units": 32, + "bias_constraint": { + "class_name": "MinMaxNorm", + "config": { + "min_value": -1, + "max_value": 1, + "rate": 1.0, + "axis": 0 + } + }, + "activity_regularizer": null, + "kernel_regularizer": null, + "bias_initializer": { + "class_name": "VarianceScaling", + "config": { + "scale": 1.0, + "distribution": "normal", + "mode": "fan_avg", + "seed": 33 + } + }, + "dtype": "float32", + "bias_regularizer": null, + "batch_input_shape": [ + null, + 32 + ], + "kernel_initializer": { + "class_name": "VarianceScaling", + "config": { + "scale": 1.0, + "distribution": "uniform", + "mode": "fan_avg", + "seed": 46 + } + }, + "name": "dense_6", + "trainable": true, + "kernel_constraint": { + "class_name": "MaxNorm", + "config": { + "max_value": 1.5, + "axis": 0 + } + }, + "activation": "linear", + "use_bias": true + } + }, + { + "class_name": "Dense", + "config": { + "units": 32, + "bias_constraint": { + "class_name": "MinMaxNorm", + "config": { + "min_value": -1, + "max_value": 1, + "rate": 1.0, + "axis": 0 + } + }, + "activity_regularizer": null, + "kernel_regularizer": null, + "bias_initializer": { + "class_name": "VarianceScaling", + "config": { + "scale": 1.0, + "distribution": "normal", + "mode": "fan_avg", + "seed": 33 + } + }, + "dtype": "float32", + "bias_regularizer": null, + "kernel_initializer": { + "class_name": "VarianceScaling", + "config": { + "scale": 1.0, + "distribution": "uniform", + "mode": "fan_avg", + "seed": 46 + } + }, + "name": "dense_7", + "trainable": true, + "kernel_constraint": { + "class_name": "MaxNorm", + "config": { + "max_value": 1.5, + "axis": 0 + } + }, + "activation": "tanh", + "use_bias": true + } + }, + { + "class_name": "Dropout", + "config": { + "seed": null, + "rate": 0.5, + "name": "dropout_5", + "noise_shape": null, + "trainable": true, + "dtype": "float32" + } + }, + { + "class_name": "Dense", + "config": { + "units": 10, + "bias_constraint": { + "class_name": "MinMaxNorm", + "config": { + "min_value": -1, + "max_value": 1, + "rate": 1.0, + "axis": 0 + } + }, + "activity_regularizer": null, + "kernel_regularizer": null, + "bias_initializer": { + "class_name": "VarianceScaling", + "config": { + "scale": 1.0, + "distribution": "normal", + "mode": "fan_avg", + "seed": 33 + } + }, + "dtype": "float32", + "bias_regularizer": null, + "kernel_initializer": { + "class_name": "VarianceScaling", + "config": { + "scale": 1.0, + "distribution": "uniform", + "mode": "fan_avg", + "seed": 46 + } + }, + "name": "dense_8", + "trainable": true, + "kernel_constraint": { + "class_name": "MaxNorm", + "config": { + "max_value": 1.5, + "axis": 0 + } + }, + "activation": "softmax", + "use_bias": true + } + } + ] + }, + "backend": "tensorflow" +} \ No newline at end of file From da00cfbce5706c639ab289295a3315fb8517d518 Mon Sep 17 00:00:00 2001 From: Umesh Date: Mon, 21 Oct 2019 19:44:20 -0500 Subject: [PATCH 06/23] Finalize Test --- test/plugins/ImportKeras/ImportKeras.spec.js | 167 +++++++++++------ test/plugins/json-model-parser.spec.js | 181 +++++++++++++++++++ 2 files changed, 291 insertions(+), 57 deletions(-) create mode 100644 test/plugins/json-model-parser.spec.js diff --git a/test/plugins/ImportKeras/ImportKeras.spec.js b/test/plugins/ImportKeras/ImportKeras.spec.js index a244a06..daa925c 100644 --- a/test/plugins/ImportKeras/ImportKeras.spec.js +++ b/test/plugins/ImportKeras/ImportKeras.spec.js @@ -4,79 +4,132 @@ */ describe('ImportKeras', function () { - var testFixture = require('../../globals'), + const testFixture = require('../../globals'), + BlobClient = require('webgme-engine/src/server/middleware/blob/BlobClientWithFSBackend'), + fs = require('fs'), + {promisify} = require('util'), + assert = require('assert'), gmeConfig = testFixture.getGmeConfig(), expect = testFixture.expect, + path = testFixture.path, logger = testFixture.logger.fork('ImportKeras'), PluginCliManager = testFixture.WebGME.PluginCliManager, + SEED_DIR = path.join(__dirname, '..', '..', '..', 'src', 'seeds'), + manager = new PluginCliManager(null, logger, gmeConfig), projectName = 'testProject', pluginName = 'ImportKeras', - project, + blobClient = new BlobClient(gmeConfig, logger), + JSON_DIR = path.join(__dirname, '../../test-cases/modelJsons/'); + + let project, gmeAuth, + awaitableLoadObject, storage, - commitHash; - - before(function (done) { - testFixture.clearDBAndGetGMEAuth(gmeConfig, projectName) - .then(function (gmeAuth_) { - gmeAuth = gmeAuth_; - // This uses in memory storage. Use testFixture.getMongoStorage to persist test to database. - storage = testFixture.getMemoryStorage(logger, gmeConfig, gmeAuth); - return storage.openDatabase(); - }) - .then(function () { - var importParam = { - projectSeed: testFixture.path.join(testFixture.SEED_DIR, 'EmptyProject.webgmex'), - projectName: projectName, - branchName: 'master', - logger: logger, - gmeConfig: gmeConfig - }; - - return testFixture.importProject(storage, importParam); - }) - .then(function (importResult) { - project = importResult.project; - commitHash = importResult.commitHash; - return project.createBranch('test', commitHash); - }) - .nodeify(done); + commitHash, + plugin; + + before(async function () { + gmeAuth = await testFixture.clearDBAndGetGMEAuth(gmeConfig, projectName); + storage = testFixture.getMemoryStorage(logger, gmeConfig, gmeAuth); + await storage.openDatabase(); + const importParam = { + projectSeed: path.join(SEED_DIR, 'keras', 'keras.webgmex'), + projectName: projectName, + branchName: 'master', + logger: logger, + gmeConfig: gmeConfig + }; + const importResult = await testFixture.importProject(storage, importParam); + project = importResult.project; + commitHash = importResult.commitHash; + // Promisify + awaitableLoadObject = promisify(project.loadObject); + + await project.createBranch('test', commitHash); + plugin = await manager.initializePlugin(pluginName); + const context = { + project: project, + commitHash: commitHash, + branchName: 'test', + activeNode: '' + }; + await manager.configurePlugin(plugin, {}, context); }); - after(function (done) { - storage.closeDatabase() - .then(function () { - return gmeAuth.unload(); - }) - .nodeify(done); + after(async function () { + await storage.closeDatabase(); + await gmeAuth.unload(); }); - it('should run plugin and update the branch', function (done) { - var manager = new PluginCliManager(null, logger, gmeConfig), - pluginConfig = { - }, + describe('without-json-file', function () { + it('should fail without a JSON file', async () => { + let manager = new PluginCliManager(null, logger, gmeConfig), + context = { + project: project, + commitHash: commitHash, + branchName: 'test', + activeNode: '' + }, + runPlugin = promisify(manager.executePlugin); + try { + let pluginResult = await runPlugin(pluginName, context, context); + assert(!pluginResult.success); + } catch (err) { + expect(err.message).to.equal('Keras Json Not Provided'); + } + + let branchHash = await project.getBranchHash('test'); + expect(branchHash).to.equal(commitHash); + }); + }); + + const runPluginTest = async (modelName) => { + let manager = new PluginCliManager(null, logger, gmeConfig), + runPlugin = promisify(manager.executePlugin), + pluginConfig = {}, context = { project: project, - commitHash: commitHash, - branchName: 'test', - activeNode: '/1', + activeNode: '', + branchName: 'test' }; - manager.executePlugin(pluginName, pluginConfig, context, function (err, pluginResult) { - try { - expect(err).to.equal(null); - expect(typeof pluginResult).to.equal('object'); - expect(pluginResult.success).to.equal(true); - } catch (e) { - done(e); - return; - } + let data = fs.readFileSync(path.join(JSON_DIR, modelName), 'utf-8'); - project.getBranchHash('test') - .then(function (branchHash) { - expect(branchHash).to.not.equal(commitHash); - }) - .nodeify(done); + assert(data != null); + + let branchHash = await project.getBranchHash('test'); + let commitObject = await awaitableLoadObject(branchHash); + let rootNode = await plugin.core.loadRoot(commitObject.root); + + assert(rootNode != null); + + let childrenPaths = (await plugin.core.loadChildren(rootNode)).map(plugin.core.getPath); + pluginConfig.srcModel = await blobClient.putFile(modelName, data); + let pluginResult = await runPlugin(pluginName, pluginConfig, context); + + assert(pluginResult != null); + assert(pluginResult.success === true); + assert(rootNode != null); + + let newBranchHash = await project.getBranchHash('test'); + commitHash = newBranchHash; + let newCommitObj = await awaitableLoadObject(newBranchHash); + + let newRootNode = await plugin.core.loadRoot(newCommitObj.root); + let newChildrenPaths = (await plugin.core.loadChildren(newRootNode)).map(plugin.core.getPath); + assert(newChildrenPaths.length === childrenPaths.length + 1); + }; + + describe('test-cases', function () { + let modelsToTest = fs.readdirSync(JSON_DIR).filter((targetFile) => { + return targetFile.endsWith('.json'); }); + + modelsToTest.forEach((model) => { + it(`should run plugin for ${model}`, async () => { + await runPluginTest(model); + }); + }); + }); -}); +}); \ No newline at end of file diff --git a/test/plugins/json-model-parser.spec.js b/test/plugins/json-model-parser.spec.js new file mode 100644 index 0000000..f0fbbc8 --- /dev/null +++ b/test/plugins/json-model-parser.spec.js @@ -0,0 +1,181 @@ +describe('json-model-parser', function () { + 'use strict'; + const requireJs = require('webgme').requirejs; + const fs = require('fs'); + const path = require('path'); + const assert = require('assert'); + const ModelParser = requireJs('../../../src/plugins/json-model-parser'); + + describe('basic-sequential', function () { + let sequentialModelConfig = null; + let layerConfigProperties = null; + before(() => { + layerConfigProperties = [ + 'bias_regularizer', 'dtype', 'bias_constraint', + 'kernel_regularizer', 'name', 'trainable', 'bias_initializer', + 'activity_regularizer', 'kernel_constraint', + ]; + let sequentialModelTxt = fs.readFileSync(path.resolve(__dirname, + '../test-cases/modelJsons/sequential_dense.json')); + sequentialModelConfig = JSON.parse(sequentialModelTxt); + }); + + it('Should find the number of models to be 1', () => { + let numModels = ModelParser.countNumberOfModels(sequentialModelConfig); + assert.equal(numModels, 1); + }); + + it('It should parse the sequential model, with no nested models', () => { + let flattenedConfig = ModelParser.flatten(sequentialModelConfig); + assert.equal(flattenedConfig.config.layers.length, + sequentialModelConfig.config.layers.length + 1); + // Test all layers are imported correctly + for (let i = 1; i < flattenedConfig.config.layers.length; i++) { + assert.equal(flattenedConfig.config.layers[i].name, + sequentialModelConfig.config.layers[i - 1].config.name); + assert(Object.prototype.hasOwnProperty.call(flattenedConfig.config.layers[i],'inbound_nodes')); + assert.deepEqual(flattenedConfig.config.layers[i].config.bias_regularizer); + } + // Check the layer config properties for equality + layerConfigProperties.forEach((prop) => { + for (let i = 1; i < flattenedConfig.config.layers.length; i++) { + if (flattenedConfig.config.layers[i].config[prop]) { + assert.deepEqual(flattenedConfig.config.layers[i].config[prop], + sequentialModelConfig.config.layers[i - 1].config[prop]); + } + } + }); + + // Check the properties of the added InputLayer + assert.equal(flattenedConfig.config.layers[0].class_name, 'InputLayer'); + assert.deepEqual(flattenedConfig.config.layers[0].config.batch_input_shape, + sequentialModelConfig.config.layers[0].config.batch_input_shape); + + }); + }); + + describe('nested-sequential', function () { + let nestedSequentialModelConfig = null; + before(() => { + let nestedSequentialModelTxt = fs.readFileSync(path.resolve(__dirname, + '../test-cases/modelJsons/sequential_nested.json')); + nestedSequentialModelConfig = JSON.parse(nestedSequentialModelTxt); + }); + + it('Should find the number of models to be 3', () => { + let numModels = ModelParser.countNumberOfModels(nestedSequentialModelConfig); + assert.equal(numModels, 3); + }); + + it('Should flatten the nested sequential model', function () { + let flattenedConfig = ModelParser.flatten(nestedSequentialModelConfig); + assert.equal(flattenedConfig.config.layers.length, 4); + assert.equal(flattenedConfig.config.layers[0].class_name, 'InputLayer'); + assert.deepEqual(flattenedConfig.config.layers[0].config.batch_input_shape, + nestedSequentialModelConfig.config.layers[0].config.batch_input_shape); + assert.deepEqual(flattenedConfig.config.layers[2].class_name, + nestedSequentialModelConfig.config.layers[1] + .config.layers[0].config.layers[0].class_name); + }); + + + }); + + describe('sequential-inside-functional', function () { + let nestedFunctionalModelConfig = null; + beforeEach(() => { + let nestedFunctionalModelTxt = fs.readFileSync(path.resolve(__dirname, + '../test-cases/modelJsons/functional_memnn_babi.json')); + nestedFunctionalModelConfig = JSON.parse(nestedFunctionalModelTxt); + }); + + it('should find the number of models to be 4', function () { + let numModels = ModelParser.countNumberOfModels(nestedFunctionalModelConfig); + assert.equal(numModels, 4); + }); + + it('should flatten the sequential inside functional model', function () { + let flattenedConfig = ModelParser.flatten(nestedFunctionalModelConfig); + + // Tests for layers + assert.deepEqual(flattenedConfig.config.layers[0], nestedFunctionalModelConfig.config.layers[0]); + assert.deepEqual(flattenedConfig.config.layers[1], nestedFunctionalModelConfig.config.layers[1]); + assert.deepEqual(flattenedConfig.config.layers[2].config, + nestedFunctionalModelConfig.config.layers[2].config.layers[0].config); + assert.deepEqual(flattenedConfig.config.layers[3].config, + nestedFunctionalModelConfig.config.layers[2].config.layers[1].config); + assert.deepEqual(flattenedConfig.config.layers[4].config, + nestedFunctionalModelConfig.config.layers[3].config.layers[0].config); + assert.deepEqual(flattenedConfig.config.layers[5].config, + nestedFunctionalModelConfig.config.layers[3].config.layers[1].config); + assert.deepEqual(flattenedConfig.config.layers[6].config, + nestedFunctionalModelConfig.config.layers[4].config); + assert.deepEqual(flattenedConfig.config.layers[7].config, + nestedFunctionalModelConfig.config.layers[5].config); + assert.deepEqual(flattenedConfig.config.layers[8].config, + nestedFunctionalModelConfig.config.layers[6].config.layers[0].config); + assert.deepEqual(flattenedConfig.config.layers[9].config, + nestedFunctionalModelConfig.config.layers[6].config.layers[1].config); + assert.deepEqual(flattenedConfig.config.layers[10].config, + nestedFunctionalModelConfig.config.layers[7].config); + assert.deepEqual(flattenedConfig.config.layers[11].config, + nestedFunctionalModelConfig.config.layers[8].config); + let i = 12, j = 9; + while (i < flattenedConfig.config.layers.length) { + assert.deepEqual(flattenedConfig.config.layers[i].config, + nestedFunctionalModelConfig.config.layers[j].config); + i++; + j++; + } + + // Tests for inbound nodes, tested using model_graph. + assert.deepEqual(flattenedConfig.config.layers[2].inbound_nodes, ['input_3']); + assert.deepEqual(flattenedConfig.config.layers[3].inbound_nodes, ['embedding_3']); + assert.deepEqual(flattenedConfig.config.layers[4].inbound_nodes, ['input_4']); + assert.deepEqual(flattenedConfig.config.layers[5].inbound_nodes, ['embedding_5']); + assert.deepEqual(flattenedConfig.config.layers[6].inbound_nodes, ['dropout_1', 'dropout_3']); + assert.deepEqual(flattenedConfig.config.layers[7].inbound_nodes, ['dot_1']); + assert.deepEqual(flattenedConfig.config.layers[8].inbound_nodes, ['input_3']); + assert.deepEqual(flattenedConfig.config.layers[9].inbound_nodes, ['embedding_4']); + assert.deepEqual(flattenedConfig.config.layers[10].inbound_nodes, ['activation_1', 'dropout_2']); + assert.deepEqual(flattenedConfig.config.layers[11].inbound_nodes, ['add_1']); + assert.deepEqual(flattenedConfig.config.layers[12].inbound_nodes, ['permute_1', 'dropout_3']); + assert.deepEqual(flattenedConfig.config.layers[13].inbound_nodes, ['concatenate_2']); + assert.deepEqual(flattenedConfig.config.layers[14].inbound_nodes, ['lstm_3']); + assert.deepEqual(flattenedConfig.config.layers[15].inbound_nodes, ['dropout_4']); + assert.deepEqual(flattenedConfig.config.layers[16].inbound_nodes, ['dense_2']); + }); + }); + + describe('redshift-convolutional-model', function () { + let redshiftModelConfig = null; + beforeEach(() => { + let redshiftModelTxt = fs.readFileSync(path.resolve(path.resolve(__dirname, + '../test-cases/modelJsons/redshiftModel.json'))); + redshiftModelConfig = JSON.parse(redshiftModelTxt); + }); + + it('find the number of models to be 1', function () { + let numModels = ModelParser.countNumberOfModels(redshiftModelConfig); + assert.equal(numModels, 1); + }); + + it('should flatten the model and change its inbound_nodes config', function () { + let flattenedRedshiftConfig = ModelParser.flatten(redshiftModelConfig); + // Test the layer config + for(let i = 0; i < flattenedRedshiftConfig.config.layers.length; i++){ + let flatLayer = flattenedRedshiftConfig.config.layers[i]; + let normalLayer = redshiftModelConfig.config.layers[i]; + // Test the layer config + assert.deepEqual(flatLayer.config, normalLayer.config); + // Tests for inbound nodes, it should be iterable, since the model is functional and not nested. + if(normalLayer.inbound_nodes.length > 0){ + normalLayer.inbound_nodes[0].forEach((node, index) =>{ + assert.deepEqual(flatLayer.inbound_nodes[index], node[0]); + }); + } + } + + }); + }); +}); \ No newline at end of file From c27bcefe0d84a0a4beaa608caee005b00bfcbd3b Mon Sep 17 00:00:00 2001 From: Brian Broll Date: Tue, 22 Oct 2019 09:33:15 -0500 Subject: [PATCH 07/23] Move helper files to utils/ in ImportKeras dir --- src/plugins/ImportKeras/ImportKeras.js | 4 ++-- src/plugins/{ => ImportKeras/utils}/JSONModelMaps.js | 0 src/plugins/{ => ImportKeras/utils}/json-model-parser.js | 0 test/plugins/json-model-parser.spec.js | 4 ++-- 4 files changed, 4 insertions(+), 4 deletions(-) rename src/plugins/{ => ImportKeras/utils}/JSONModelMaps.js (100%) rename src/plugins/{ => ImportKeras/utils}/json-model-parser.js (100%) diff --git a/src/plugins/ImportKeras/ImportKeras.js b/src/plugins/ImportKeras/ImportKeras.js index 0e7a943..83bb8f6 100644 --- a/src/plugins/ImportKeras/ImportKeras.js +++ b/src/plugins/ImportKeras/ImportKeras.js @@ -11,8 +11,8 @@ define([ 'plugin/PluginConfig', 'text!./metadata.json', 'plugin/PluginBase', - '../JSONModelMaps', - '../json-model-parser', + './utils/JSONModelMaps', + './utils/json-model-parser', ], function ( PluginConfig, pluginMetadata, diff --git a/src/plugins/JSONModelMaps.js b/src/plugins/ImportKeras/utils/JSONModelMaps.js similarity index 100% rename from src/plugins/JSONModelMaps.js rename to src/plugins/ImportKeras/utils/JSONModelMaps.js diff --git a/src/plugins/json-model-parser.js b/src/plugins/ImportKeras/utils/json-model-parser.js similarity index 100% rename from src/plugins/json-model-parser.js rename to src/plugins/ImportKeras/utils/json-model-parser.js diff --git a/test/plugins/json-model-parser.spec.js b/test/plugins/json-model-parser.spec.js index f0fbbc8..3a3246d 100644 --- a/test/plugins/json-model-parser.spec.js +++ b/test/plugins/json-model-parser.spec.js @@ -4,7 +4,7 @@ describe('json-model-parser', function () { const fs = require('fs'); const path = require('path'); const assert = require('assert'); - const ModelParser = requireJs('../../../src/plugins/json-model-parser'); + const ModelParser = requireJs('plugin/ImportKeras/ImportKeras/utils/json-model-parser'); describe('basic-sequential', function () { let sequentialModelConfig = null; @@ -178,4 +178,4 @@ describe('json-model-parser', function () { }); }); -}); \ No newline at end of file +}); From 6fcd15a582179bcf767c0f886db9fe3e1f07d141 Mon Sep 17 00:00:00 2001 From: Umesh Date: Tue, 22 Oct 2019 10:11:55 -0500 Subject: [PATCH 08/23] Remove count methods from plugin utils --- .../ImportKeras/utils/json-model-parser.js | 15 ------------ test/plugins/json-model-parser.spec.js | 23 ++----------------- 2 files changed, 2 insertions(+), 36 deletions(-) diff --git a/src/plugins/ImportKeras/utils/json-model-parser.js b/src/plugins/ImportKeras/utils/json-model-parser.js index 6228e6a..fcff5b1 100644 --- a/src/plugins/ImportKeras/utils/json-model-parser.js +++ b/src/plugins/ImportKeras/utils/json-model-parser.js @@ -1,14 +1,9 @@ /*globals define*/ define(['./JSONModelMaps'], function (JSONModelMaps) { const ModelParser = { - countNumberOfModels: countNumberOfModels, flatten: flatten }; - function countNumberOfModels(modelConfig) { - return countModels(modelConfig); - } - /* This function will flatten nested models (i.e. a Model inside a model) and create a simple array of all the layers that can be directly used by @@ -26,16 +21,6 @@ define(['./JSONModelMaps'], function (JSONModelMaps) { return alteredModelConfig; } - function countModels(modelConfig) { - if(isModel(modelConfig)){ - return modelConfig.config.layers - .filter(layer => isModel(layer)) - .map(nestedModel => countModels(nestedModel)) - .reduce((a, b) => a+b, 1); - } - return 0; - } - function flattenNestedModel(modelConfig, layersInfo = [], inboundNodeKeys = {}) { let layers = modelConfig.config.layers; if (layers) { diff --git a/test/plugins/json-model-parser.spec.js b/test/plugins/json-model-parser.spec.js index 3a3246d..7c23add 100644 --- a/test/plugins/json-model-parser.spec.js +++ b/test/plugins/json-model-parser.spec.js @@ -20,11 +20,6 @@ describe('json-model-parser', function () { sequentialModelConfig = JSON.parse(sequentialModelTxt); }); - it('Should find the number of models to be 1', () => { - let numModels = ModelParser.countNumberOfModels(sequentialModelConfig); - assert.equal(numModels, 1); - }); - it('It should parse the sequential model, with no nested models', () => { let flattenedConfig = ModelParser.flatten(sequentialModelConfig); assert.equal(flattenedConfig.config.layers.length, @@ -62,10 +57,6 @@ describe('json-model-parser', function () { nestedSequentialModelConfig = JSON.parse(nestedSequentialModelTxt); }); - it('Should find the number of models to be 3', () => { - let numModels = ModelParser.countNumberOfModels(nestedSequentialModelConfig); - assert.equal(numModels, 3); - }); it('Should flatten the nested sequential model', function () { let flattenedConfig = ModelParser.flatten(nestedSequentialModelConfig); @@ -83,17 +74,12 @@ describe('json-model-parser', function () { describe('sequential-inside-functional', function () { let nestedFunctionalModelConfig = null; - beforeEach(() => { + before(() => { let nestedFunctionalModelTxt = fs.readFileSync(path.resolve(__dirname, '../test-cases/modelJsons/functional_memnn_babi.json')); nestedFunctionalModelConfig = JSON.parse(nestedFunctionalModelTxt); }); - it('should find the number of models to be 4', function () { - let numModels = ModelParser.countNumberOfModels(nestedFunctionalModelConfig); - assert.equal(numModels, 4); - }); - it('should flatten the sequential inside functional model', function () { let flattenedConfig = ModelParser.flatten(nestedFunctionalModelConfig); @@ -149,17 +135,12 @@ describe('json-model-parser', function () { describe('redshift-convolutional-model', function () { let redshiftModelConfig = null; - beforeEach(() => { + before(() => { let redshiftModelTxt = fs.readFileSync(path.resolve(path.resolve(__dirname, '../test-cases/modelJsons/redshiftModel.json'))); redshiftModelConfig = JSON.parse(redshiftModelTxt); }); - it('find the number of models to be 1', function () { - let numModels = ModelParser.countNumberOfModels(redshiftModelConfig); - assert.equal(numModels, 1); - }); - it('should flatten the model and change its inbound_nodes config', function () { let flattenedRedshiftConfig = ModelParser.flatten(redshiftModelConfig); // Test the layer config From 112abbe2ea3c73e4898ace3bb949c6e6c685ab7c Mon Sep 17 00:00:00 2001 From: Umesh Date: Tue, 22 Oct 2019 11:55:48 -0500 Subject: [PATCH 09/23] Add Input/Output Connection Test --- test/plugins/ImportKeras/ImportKeras.spec.js | 47 +++++++++++++++++++- 1 file changed, 46 insertions(+), 1 deletion(-) diff --git a/test/plugins/ImportKeras/ImportKeras.spec.js b/test/plugins/ImportKeras/ImportKeras.spec.js index daa925c..b14b7d5 100644 --- a/test/plugins/ImportKeras/ImportKeras.spec.js +++ b/test/plugins/ImportKeras/ImportKeras.spec.js @@ -121,7 +121,7 @@ describe('ImportKeras', function () { }; describe('test-cases', function () { - let modelsToTest = fs.readdirSync(JSON_DIR).filter((targetFile) => { + const modelsToTest = fs.readdirSync(JSON_DIR).filter((targetFile) => { return targetFile.endsWith('.json'); }); @@ -132,4 +132,49 @@ describe('ImportKeras', function () { }); }); + + describe('test-connections', function () { + // For name's sake + const modelToTest = path.join(JSON_DIR, 'sequential_conv_mnist.json'); + + it('should run plugin and connect layers correctly', async () => { + const manager = new PluginCliManager(null, logger, gmeConfig), + runPlugin = promisify(manager.executePlugin), + pluginConfig = {}, + context = { + project: project, + activeNode: '', + branchName: 'test' + }; + const data = fs.readFileSync(modelToTest, 'utf-8'); + + pluginConfig.srcModel = await blobClient.putFile(modelToTest, data); + const pluginResult = await runPlugin(pluginName, pluginConfig, context); + + assert(pluginResult != null); + assert(pluginResult.success === true); + + commitHash = await project.getBranchHash('test'); + const commitObj = await awaitableLoadObject(commitHash); + + const rootNode = await plugin.core.loadRoot(commitObj.root); + const children = (await plugin.core.loadChildren(rootNode)) + .filter((node) => { + return plugin.core.getAttribute(node, 'name') + === modelToTest.replace('.json', ''); + }); + assert(children.length === 1); + const layers = await plugin.core.loadChildren(children[0]); + let layersObject = {}; + layers.map(node => { + layersObject[plugin.core.getAttribute(node, 'name')] = node; + }); + const inputSourcePort = await plugin.core.loadMembers(layersObject['conv2d_1'], 'inputs'); + const destinatioMembers = await plugin.core.loadMembers(layersObject['input'], 'outputs'); + const soruceMembers = await plugin.core.loadMembers(inputSourcePort[0], 'source'); + assert(destinatioMembers[0]); + assert(soruceMembers[0]); + assert(soruceMembers[0] === destinatioMembers[0]); + }); + }); }); \ No newline at end of file From 2c697a79548da2cc50ffd0e9df17f8210e0d2817 Mon Sep 17 00:00:00 2001 From: Umesh Date: Tue, 22 Oct 2019 12:32:10 -0500 Subject: [PATCH 10/23] Finalize connection test --- test/plugins/ImportKeras/ImportKeras.spec.js | 24 +++++++++++++------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/test/plugins/ImportKeras/ImportKeras.spec.js b/test/plugins/ImportKeras/ImportKeras.spec.js index b14b7d5..1b2dd1a 100644 --- a/test/plugins/ImportKeras/ImportKeras.spec.js +++ b/test/plugins/ImportKeras/ImportKeras.spec.js @@ -136,6 +136,8 @@ describe('ImportKeras', function () { describe('test-connections', function () { // For name's sake const modelToTest = path.join(JSON_DIR, 'sequential_conv_mnist.json'); + const layerNames = ['input', 'conv2d_1', 'conv2d_2', 'max_pooling2d_1', + 'dropout_6', 'flatten_1', 'dense_9', 'dropout_7', 'dense_10']; it('should run plugin and connect layers correctly', async () => { const manager = new PluginCliManager(null, logger, gmeConfig), @@ -165,16 +167,22 @@ describe('ImportKeras', function () { }); assert(children.length === 1); const layers = await plugin.core.loadChildren(children[0]); - let layersObject = {}; + let layersMap = {}; layers.map(node => { - layersObject[plugin.core.getAttribute(node, 'name')] = node; + layersMap[plugin.core.getAttribute(node, 'name')] = node; }); - const inputSourcePort = await plugin.core.loadMembers(layersObject['conv2d_1'], 'inputs'); - const destinatioMembers = await plugin.core.loadMembers(layersObject['input'], 'outputs'); - const soruceMembers = await plugin.core.loadMembers(inputSourcePort[0], 'source'); - assert(destinatioMembers[0]); - assert(soruceMembers[0]); - assert(soruceMembers[0] === destinatioMembers[0]); + let inputSourcePort, destinationMembers, soruceMembers; + + for(let i = 1; i < layerNames.length; i++){ + inputSourcePort = await plugin.core.loadMembers(layersMap[layerNames[i]], 'inputs'); + destinationMembers = await plugin.core.loadMembers(layersMap[layerNames[i-1]], 'outputs'); + soruceMembers = await plugin.core.loadMembers(inputSourcePort[0], 'source'); + assert(destinationMembers[0]); + assert(soruceMembers[0]); + assert(soruceMembers[0] === destinationMembers[0]); + } + + }); }); }); \ No newline at end of file From 2c93550a5f9a30de113d0c8cb7f54da76b10d657 Mon Sep 17 00:00:00 2001 From: Umesh Date: Tue, 22 Oct 2019 13:30:56 -0500 Subject: [PATCH 11/23] Fix code-climate issues for ImportKeras.js, ImportKeras.spec.js --- src/plugins/ImportKeras/ImportKeras.js | 61 +++++++++++--------- test/plugins/ImportKeras/ImportKeras.spec.js | 6 +- 2 files changed, 35 insertions(+), 32 deletions(-) diff --git a/src/plugins/ImportKeras/ImportKeras.js b/src/plugins/ImportKeras/ImportKeras.js index 83bb8f6..b47e469 100644 --- a/src/plugins/ImportKeras/ImportKeras.js +++ b/src/plugins/ImportKeras/ImportKeras.js @@ -151,37 +151,44 @@ define([ allPointerNames.filter(pointer => !!config[pointer]) .forEach((pointer) => { if (typeof config[pointer] == 'string') { - let configurableNode = this.core.createNode({ - parent: layerNode, - base: this.META[config[pointer]] - }); - // This will set the necessary pointers. - // Of things like activations and so on... - this.core.setPointer(layerNode, pointer, configurableNode); - this.logger.debug(`Added ${this.core.getAttribute(configurableNode, 'name')}` - + ` as ${pointer} to the layer ` - + `${this.core.getAttribute(layerNode, 'name')}`); + this._addStringPropertiesNode(layerNode, config, pointer); } else { - let pluggableNode = this.core.createNode({ - parent: layerNode, - base: this.META[config[pointer].class_name] - }); - this.logger.debug(`Added ${this.core.getAttribute(pluggableNode, 'name')} as ${pointer} to the layer ` + - `${this.core.getAttribute(layerNode, 'name')}`); - let validArgumentsForThisNode = this.core.getValidAttributeNames(pluggableNode); - let configForAddedNode = config[pointer].config; - if (validArgumentsForThisNode && configForAddedNode) { - validArgumentsForThisNode.forEach((arg) => { - if (configForAddedNode[arg]) { - this.core.setAttribute(pluggableNode, arg, this._toPythonIterable(configForAddedNode[arg])); - } - }); - } + this._addPluggableNodes(layerNode, config, pointer); } }); + }; + ImportKeras.prototype._addStringPropertiesNode = function(layerNode, config, pointer) { + let configurableNode = this.core.createNode({ + parent: layerNode, + base: this.META[config[pointer]] + }); + // This will set the necessary pointers. + // Of things like activations and so on... + this.core.setPointer(layerNode, pointer, configurableNode); + this.logger.debug(`Added ${this.core.getAttribute(configurableNode, 'name')}` + + ` as ${pointer} to the layer ` + + `${this.core.getAttribute(layerNode, 'name')}`); }; + ImportKeras.prototype._addPluggableNodes = function (layerNode, config, pointer){ + let pluggableNode = this.core.createNode({ + parent: layerNode, + base: this.META[config[pointer].class_name] + }); + this.logger.debug(`Added ${this.core.getAttribute(pluggableNode, 'name')} as` + + ` ${pointer} to the layer ${this.core.getAttribute(layerNode, 'name')}`); + let validArgumentsForThisNode = this.core.getValidAttributeNames(pluggableNode); + let configForAddedNode = config[pointer].config; + if (validArgumentsForThisNode && configForAddedNode) { + validArgumentsForThisNode.forEach((arg) => { + if (configForAddedNode[arg]) + this.core.setAttribute(pluggableNode, arg, + this._toPythonIterable(configForAddedNode[arg])); + }); + } + } + // This method is used to convert javascript arrays to a // tuple/ list(Python) in string Representation. Needed for // Code generation. @@ -247,7 +254,7 @@ define([ } }); - await this._updateConnections(layerInputConnections, layerOutputConnections); + await this._updateConnections(layerInputConnections); }; @@ -259,7 +266,7 @@ define([ return allLayerNames.indexOf(layerName) > -1; }; - ImportKeras.prototype._updateConnections = function (inputs, outputs) { + ImportKeras.prototype._updateConnections = function (inputs) { let allLayerNames = Object.keys(inputs); return Promise.all(allLayerNames.map((layerName) => { let dstLayer = this.layerInfo[layerName]; diff --git a/test/plugins/ImportKeras/ImportKeras.spec.js b/test/plugins/ImportKeras/ImportKeras.spec.js index 1b2dd1a..4e11206 100644 --- a/test/plugins/ImportKeras/ImportKeras.spec.js +++ b/test/plugins/ImportKeras/ImportKeras.spec.js @@ -42,7 +42,6 @@ describe('ImportKeras', function () { const importResult = await testFixture.importProject(storage, importParam); project = importResult.project; commitHash = importResult.commitHash; - // Promisify awaitableLoadObject = promisify(project.loadObject); await project.createBranch('test', commitHash); @@ -107,8 +106,7 @@ describe('ImportKeras', function () { pluginConfig.srcModel = await blobClient.putFile(modelName, data); let pluginResult = await runPlugin(pluginName, pluginConfig, context); - assert(pluginResult != null); - assert(pluginResult.success === true); + assert(pluginResult != null && pluginResult.success === true); assert(rootNode != null); let newBranchHash = await project.getBranchHash('test'); @@ -181,8 +179,6 @@ describe('ImportKeras', function () { assert(soruceMembers[0]); assert(soruceMembers[0] === destinationMembers[0]); } - - }); }); }); \ No newline at end of file From acfc2688d70d0573db3951b7f2facc7fdd9f2ea7 Mon Sep 17 00:00:00 2001 From: Umesh Date: Tue, 22 Oct 2019 13:30:56 -0500 Subject: [PATCH 12/23] Fix code-climate issues for ImportKeras.js, ImportKeras.spec.js --- src/plugins/ImportKeras/ImportKeras.js | 61 +++++++++++--------- test/plugins/ImportKeras/ImportKeras.spec.js | 6 +- 2 files changed, 35 insertions(+), 32 deletions(-) diff --git a/src/plugins/ImportKeras/ImportKeras.js b/src/plugins/ImportKeras/ImportKeras.js index 83bb8f6..c2b7b5a 100644 --- a/src/plugins/ImportKeras/ImportKeras.js +++ b/src/plugins/ImportKeras/ImportKeras.js @@ -151,35 +151,42 @@ define([ allPointerNames.filter(pointer => !!config[pointer]) .forEach((pointer) => { if (typeof config[pointer] == 'string') { - let configurableNode = this.core.createNode({ - parent: layerNode, - base: this.META[config[pointer]] - }); - // This will set the necessary pointers. - // Of things like activations and so on... - this.core.setPointer(layerNode, pointer, configurableNode); - this.logger.debug(`Added ${this.core.getAttribute(configurableNode, 'name')}` - + ` as ${pointer} to the layer ` - + `${this.core.getAttribute(layerNode, 'name')}`); + this._addStringPropertiesNode(layerNode, config, pointer); } else { - let pluggableNode = this.core.createNode({ - parent: layerNode, - base: this.META[config[pointer].class_name] - }); - this.logger.debug(`Added ${this.core.getAttribute(pluggableNode, 'name')} as ${pointer} to the layer ` + - `${this.core.getAttribute(layerNode, 'name')}`); - let validArgumentsForThisNode = this.core.getValidAttributeNames(pluggableNode); - let configForAddedNode = config[pointer].config; - if (validArgumentsForThisNode && configForAddedNode) { - validArgumentsForThisNode.forEach((arg) => { - if (configForAddedNode[arg]) { - this.core.setAttribute(pluggableNode, arg, this._toPythonIterable(configForAddedNode[arg])); - } - }); - } + this._addPluggableNodes(layerNode, config, pointer); } }); + }; + + ImportKeras.prototype._addStringPropertiesNode = function(layerNode, config, pointer) { + let configurableNode = this.core.createNode({ + parent: layerNode, + base: this.META[config[pointer]] + }); + // This will set the necessary pointers. + // Of things like activations and so on... + this.core.setPointer(layerNode, pointer, configurableNode); + this.logger.debug(`Added ${this.core.getAttribute(configurableNode, 'name')}` + + ` as ${pointer} to the layer ` + + `${this.core.getAttribute(layerNode, 'name')}`); + }; + ImportKeras.prototype._addPluggableNodes = function (layerNode, config, pointer){ + let pluggableNode = this.core.createNode({ + parent: layerNode, + base: this.META[config[pointer].class_name] + }); + this.logger.debug(`Added ${this.core.getAttribute(pluggableNode, 'name')} as` + + ` ${pointer} to the layer ${this.core.getAttribute(layerNode, 'name')}`); + let validArgumentsForThisNode = this.core.getValidAttributeNames(pluggableNode); + let configForAddedNode = config[pointer].config; + if (validArgumentsForThisNode && configForAddedNode) { + validArgumentsForThisNode.forEach((arg) => { + if (configForAddedNode[arg]) + this.core.setAttribute(pluggableNode, arg, + this._toPythonIterable(configForAddedNode[arg])); + }); + } }; // This method is used to convert javascript arrays to a @@ -247,7 +254,7 @@ define([ } }); - await this._updateConnections(layerInputConnections, layerOutputConnections); + await this._updateConnections(layerInputConnections); }; @@ -259,7 +266,7 @@ define([ return allLayerNames.indexOf(layerName) > -1; }; - ImportKeras.prototype._updateConnections = function (inputs, outputs) { + ImportKeras.prototype._updateConnections = function (inputs) { let allLayerNames = Object.keys(inputs); return Promise.all(allLayerNames.map((layerName) => { let dstLayer = this.layerInfo[layerName]; diff --git a/test/plugins/ImportKeras/ImportKeras.spec.js b/test/plugins/ImportKeras/ImportKeras.spec.js index 1b2dd1a..4e11206 100644 --- a/test/plugins/ImportKeras/ImportKeras.spec.js +++ b/test/plugins/ImportKeras/ImportKeras.spec.js @@ -42,7 +42,6 @@ describe('ImportKeras', function () { const importResult = await testFixture.importProject(storage, importParam); project = importResult.project; commitHash = importResult.commitHash; - // Promisify awaitableLoadObject = promisify(project.loadObject); await project.createBranch('test', commitHash); @@ -107,8 +106,7 @@ describe('ImportKeras', function () { pluginConfig.srcModel = await blobClient.putFile(modelName, data); let pluginResult = await runPlugin(pluginName, pluginConfig, context); - assert(pluginResult != null); - assert(pluginResult.success === true); + assert(pluginResult != null && pluginResult.success === true); assert(rootNode != null); let newBranchHash = await project.getBranchHash('test'); @@ -181,8 +179,6 @@ describe('ImportKeras', function () { assert(soruceMembers[0]); assert(soruceMembers[0] === destinationMembers[0]); } - - }); }); }); \ No newline at end of file From 8684e648bca1b7e90f4924fca421b06f95ad0446 Mon Sep 17 00:00:00 2001 From: Umesh Date: Tue, 22 Oct 2019 13:40:23 -0500 Subject: [PATCH 13/23] Merge Conflict --- src/plugins/ImportKeras/ImportKeras.js | 33 -------------------------- 1 file changed, 33 deletions(-) diff --git a/src/plugins/ImportKeras/ImportKeras.js b/src/plugins/ImportKeras/ImportKeras.js index afbe54b..c2b7b5a 100644 --- a/src/plugins/ImportKeras/ImportKeras.js +++ b/src/plugins/ImportKeras/ImportKeras.js @@ -157,7 +157,6 @@ define([ } }); }; -<<<<<<< HEAD ImportKeras.prototype._addStringPropertiesNode = function(layerNode, config, pointer) { let configurableNode = this.core.createNode({ @@ -172,38 +171,6 @@ define([ + `${this.core.getAttribute(layerNode, 'name')}`); }; - ImportKeras.prototype._addPluggableNodes = function (layerNode, config, pointer){ - let pluggableNode = this.core.createNode({ - parent: layerNode, - base: this.META[config[pointer].class_name] - }); - this.logger.debug(`Added ${this.core.getAttribute(pluggableNode, 'name')} as` + - ` ${pointer} to the layer ${this.core.getAttribute(layerNode, 'name')}`); - let validArgumentsForThisNode = this.core.getValidAttributeNames(pluggableNode); - let configForAddedNode = config[pointer].config; - if (validArgumentsForThisNode && configForAddedNode) { - validArgumentsForThisNode.forEach((arg) => { - if (configForAddedNode[arg]) - this.core.setAttribute(pluggableNode, arg, - this._toPythonIterable(configForAddedNode[arg])); - }); - } -======= - - ImportKeras.prototype._addStringPropertiesNode = function(layerNode, config, pointer) { - let configurableNode = this.core.createNode({ - parent: layerNode, - base: this.META[config[pointer]] - }); - // This will set the necessary pointers. - // Of things like activations and so on... - this.core.setPointer(layerNode, pointer, configurableNode); - this.logger.debug(`Added ${this.core.getAttribute(configurableNode, 'name')}` - + ` as ${pointer} to the layer ` - + `${this.core.getAttribute(layerNode, 'name')}`); ->>>>>>> 2c93550a5f9a30de113d0c8cb7f54da76b10d657 - }; - ImportKeras.prototype._addPluggableNodes = function (layerNode, config, pointer){ let pluggableNode = this.core.createNode({ parent: layerNode, From 1b8143c3e6fe55564cd32b30da89bd51f524dd83 Mon Sep 17 00:00:00 2001 From: Umesh Date: Wed, 23 Oct 2019 09:35:34 -0500 Subject: [PATCH 14/23] Checkout gitignore from master, remove gitattribute file --- .gitattributes | 1 - .gitignore | 8 +------- 2 files changed, 1 insertion(+), 8 deletions(-) delete mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes deleted file mode 100644 index 07764a7..0000000 --- a/.gitattributes +++ /dev/null @@ -1 +0,0 @@ -* text eol=lf \ No newline at end of file diff --git a/.gitignore b/.gitignore index 522298b..1993352 100644 --- a/.gitignore +++ b/.gitignore @@ -29,10 +29,4 @@ build/Release node_modules tmp/ blob-local-storage/ -notes/ - -#IntelliJ -.idea - -#vscode -.vscode \ No newline at end of file +notes/ \ No newline at end of file From c56b62c0fc78021f8ddcd75e632cf00ee9b79d5a Mon Sep 17 00:00:00 2001 From: Umesh Date: Wed, 23 Oct 2019 09:57:25 -0500 Subject: [PATCH 15/23] Convert boolean type of python string (Adress #120#issuecomment-545453804), change variables scope (Adress #120#discussion_r338052173) --- src/plugins/ImportKeras/ImportKeras.js | 51 +++++++++++--------------- 1 file changed, 22 insertions(+), 29 deletions(-) diff --git a/src/plugins/ImportKeras/ImportKeras.js b/src/plugins/ImportKeras/ImportKeras.js index c2b7b5a..2f840b0 100644 --- a/src/plugins/ImportKeras/ImportKeras.js +++ b/src/plugins/ImportKeras/ImportKeras.js @@ -1,12 +1,4 @@ /*globals define*/ -/*eslint-env node, browser*/ - -/** - * Generated by PluginGenerator 2.20.5 from webgme on Tue Sep 10 2019 15:28:36 GMT-0500 (Central Daylight Time). - * A plugin that inherits from the PluginBase. To see source code documentation about available - * properties and methods visit %host%/docs/source/PluginBase.html. - */ - define([ 'plugin/PluginConfig', 'text!./metadata.json', @@ -65,13 +57,13 @@ define([ return; } try { - this.archName = this.getCurrentConfig().archName; + let archName = this.getCurrentConfig().archName; let metadata = await this.blobClient.getMetadata(srcJsonHash); - this.archName = this.archName ? this.archName : metadata.name.replace('.json', ''); + archName = archName ? archName : metadata.name.replace('.json', ''); let modelJson = await this.blobClient.getObjectAsJSON(srcJsonHash); this.modelInfo = JSONLayerParser.flatten(modelJson).config; - this.addNewArchitecture(); - this.addLayers(); + let importedArchNode = this.addNewArchitecture(archName); + this.addLayers(importedArchNode); await this.addConnections(); await this.save('Completed Import Model'); this.result.setSuccess(true); @@ -82,31 +74,31 @@ define([ } }; - ImportKeras.prototype.addNewArchitecture = function () { + ImportKeras.prototype.addNewArchitecture = function (archName) { // Add Architecture - this.importedArch = this.core.createNode({ + let importedArch = this.core.createNode({ parent: this.activeNode, base: this.META.Architecture }); - const uniqueName = this.archName; - this.core.setAttribute(this.importedArch, 'name', uniqueName); - + const uniqueName = archName; + this.core.setAttribute(importedArch, 'name', uniqueName); this.logger.debug(`Added ${uniqueName} as a new architecture.`); + return importedArch; }; // This should add layers. constraints, initializers, regularizers as well as activations to the layer. - ImportKeras.prototype.addLayers = function () { + ImportKeras.prototype.addLayers = function (importedArch) { let layers = this.modelInfo.layers; let layerToCreate = null; this.layerInfo = {}; layers.forEach((layer) => { layerToCreate = this._getMetaTypeForClass(layer.class_name); let layerNode = this.core.createNode({ - parent: this.importedArch, + parent: importedArch, base: this.META[layerToCreate] }); this.logger.debug(`Added ${layerToCreate}\ - to ${this.core.getAttribute(this.importedArch, 'name')}`); + to ${this.core.getAttribute(importedArch, 'name')}`); // Add all attributes, from the model JSON, as well as from the layers schema this._addLayerAttributes(layerNode, layer); @@ -128,14 +120,13 @@ define([ validAttributeNamesForThisLayer.forEach((attribute) => { if (remainingKeys.indexOf(this._jsonConfigToNodeAttr(attribute)) > -1) { - this.core.setAttribute(layerNode, attribute, this._toPythonIterable(attrObj[this._jsonConfigToNodeAttr(attribute)])); + this.core.setAttribute(layerNode, attribute, this._toPythonType(attrObj[this._jsonConfigToNodeAttr(attribute)])); this.logger.debug(`Set ${attribute} for ${this.core.getGuid(layerNode)}` + ` to ${this.core.getAttribute(layerNode, attribute)}`); } else if (configKeys.indexOf(this._jsonConfigToNodeAttr(attribute)) > -1) { - this.core.setAttribute(layerNode, attribute, this._toPythonIterable(config[this._jsonConfigToNodeAttr(attribute)])); + this.core.setAttribute(layerNode, attribute, this._toPythonType(config[this._jsonConfigToNodeAttr(attribute)])); this.logger.debug(`Set ${attribute} for ${this.core.getGuid(layerNode)}` + ` to ${this.core.getAttribute(layerNode, attribute)}`); - } }); let layerName = this.core.getAttribute(layerNode, 'name'); @@ -184,22 +175,24 @@ define([ validArgumentsForThisNode.forEach((arg) => { if (configForAddedNode[arg]) this.core.setAttribute(pluggableNode, arg, - this._toPythonIterable(configForAddedNode[arg])); + this._toPythonType(configForAddedNode[arg])); }); } }; - // This method is used to convert javascript arrays to a - // tuple/ list(Python) in string Representation. Needed for + // This method is used to convert javascript arrays/booleans to a + // list(python)/boolean in string Representation. Needed for // Code generation. - ImportKeras.prototype._toPythonIterable = function (obj) { + ImportKeras.prototype._toPythonType = function (obj) { if (obj == null) { return 'None'; } if (obj instanceof Array) { return '[' + obj.map((val) => { - return this._toPythonIterable(val); + return this._toPythonType(val); }).join(', ') + ']'; + } else if (typeof obj === 'boolean') { + return obj ? 'True' : 'False'; } else { return obj; } @@ -296,4 +289,4 @@ define([ }; return ImportKeras; -}); +}); \ No newline at end of file From 0b1921c22068b7368c1329ad2bd4b7f316141ca5 Mon Sep 17 00:00:00 2001 From: Umesh Date: Wed, 23 Oct 2019 10:03:10 -0500 Subject: [PATCH 16/23] Fix typo in sourcemembers, remove plugin cli comments --- test/plugins/ImportKeras/ImportKeras.spec.js | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/test/plugins/ImportKeras/ImportKeras.spec.js b/test/plugins/ImportKeras/ImportKeras.spec.js index 4e11206..6fd4c4c 100644 --- a/test/plugins/ImportKeras/ImportKeras.spec.js +++ b/test/plugins/ImportKeras/ImportKeras.spec.js @@ -1,8 +1,4 @@ /*eslint-env node, mocha*/ -/** - * Generated by PluginGenerator 2.20.5 from webgme on Mon Oct 21 2019 18:17:16 GMT-0500 (Central Daylight Time). - */ - describe('ImportKeras', function () { const testFixture = require('../../globals'), BlobClient = require('webgme-engine/src/server/middleware/blob/BlobClientWithFSBackend'), @@ -169,15 +165,15 @@ describe('ImportKeras', function () { layers.map(node => { layersMap[plugin.core.getAttribute(node, 'name')] = node; }); - let inputSourcePort, destinationMembers, soruceMembers; + let inputSourcePort, destinationMembers, sourceMembers; for(let i = 1; i < layerNames.length; i++){ inputSourcePort = await plugin.core.loadMembers(layersMap[layerNames[i]], 'inputs'); destinationMembers = await plugin.core.loadMembers(layersMap[layerNames[i-1]], 'outputs'); - soruceMembers = await plugin.core.loadMembers(inputSourcePort[0], 'source'); + sourceMembers = await plugin.core.loadMembers(inputSourcePort[0], 'source'); assert(destinationMembers[0]); - assert(soruceMembers[0]); - assert(soruceMembers[0] === destinationMembers[0]); + assert(sourceMembers[0]); + assert(sourceMembers[0] === destinationMembers[0]); } }); }); From 0f5a34b7d7712798ee45920d0022e7de4886b4b4 Mon Sep 17 00:00:00 2001 From: Umesh Date: Wed, 6 Nov 2019 18:56:47 -0600 Subject: [PATCH 17/23] WIP- #130: Initial Implementation Complete ImportKeras, try to run Generate Keras --- .../ImportedArchitectureCode.spec.js | 129 ++++++++++++++++++ 1 file changed, 129 insertions(+) create mode 100644 test/integration/ImportArchitectureCode/ImportedArchitectureCode.spec.js diff --git a/test/integration/ImportArchitectureCode/ImportedArchitectureCode.spec.js b/test/integration/ImportArchitectureCode/ImportedArchitectureCode.spec.js new file mode 100644 index 0000000..d34cbbe --- /dev/null +++ b/test/integration/ImportArchitectureCode/ImportedArchitectureCode.spec.js @@ -0,0 +1,129 @@ +/*globals requireJS*/ +/*eslint-env node, mocha*/ +'use strict'; + +describe('Imported JSON to Code', function () { + const testFixture = require('../../globals'); + const requrireJs = require('webgme').requirejs; + const BlobClient = requrireJs('webgme-engine/src/server/middleware/blob/BlobClientWithFSBackend'), + fs = require('fs'), + {promisify} = require('util'), + assert = require('assert'), + gmeConfig = testFixture.getGmeConfig(), + executePython = testFixture.executePython, + expect = testFixture.expect, + path = testFixture.path, + logger = testFixture.logger.fork('ImportedArchitectureToCode'), + PluginCliManager = testFixture.WebGME.PluginCliManager, + SEED_DIR = path.join(__dirname, '..', '..', '..', 'src', 'seeds'), + manager = new PluginCliManager(null, logger, gmeConfig), + projectName = 'testProject', + ImportPluginName = 'ImportKeras', + CodeGenerationPluginName = 'GenerateKeras', + blobClient = new BlobClient(gmeConfig, logger), + JSON_DIR = path.join(__dirname, '../../test-cases/modelJsons/'); + + let project, + gmeAuth, + awaitableLoadObject, + storage, + commitHash, + importPlugin, + codeGenerationPlugin; + + before(async function () { + gmeAuth = await testFixture.clearDBAndGetGMEAuth(gmeConfig, projectName); + storage = testFixture.getMemoryStorage(logger, gmeConfig, gmeAuth); + await storage.openDatabase(); + const importParam = { + projectSeed: path.join(SEED_DIR, 'keras', 'keras.webgmex'), + projectName: projectName, + branchName: 'master', + logger: logger, + gmeConfig: gmeConfig + }; + const importResult = await testFixture.importProject(storage, importParam); + project = importResult.project; + commitHash = importResult.commitHash; + awaitableLoadObject = promisify(project.loadObject); + + await project.createBranch('test', commitHash); + importPlugin = await manager.initializePlugin(ImportPluginName); + codeGenerationPlugin = await manager.initializePlugin(CodeGenerationPluginName); + const context = { + project: project, + commitHash: commitHash, + branchName: 'test', + activeNode: '' + }; + await manager.configurePlugin(importPlugin, {}, context); + await manager.configurePlugin(codeGenerationPlugin, {}, context); + }); + + after(async function () { + await storage.closeDatabase(); + await gmeAuth.unload(); + }); + + const modelsToTest = fs.readdirSync(JSON_DIR).filter((targetFile) => { + return targetFile.endsWith('.json'); + }); + + modelsToTest.forEach((modelJSON) => { + describe(`Import JSON and generate Code For ${modelJSON}`, function () { + let generatedCode; + before(async function () { + generatedCode = await importJSONAndGetGeneratedCode(modelJSON); + }); + + it(`should run the python code generated for ${modelJSON}`, () => { + let execResult = executePython(generatedCode); + // assert(execResult.success); + }) + }) + }); + + + // This function runs ImportKeras, followed by GenerateKeras + const importJSONAndGetGeneratedCode = async function(fileName){ + let executePlugin = promisify(manager.executePlugin), + pluginConfig = {}, + context = { + project: project, + activeNode: '', + branchName: 'test', + namespace: '' + }; + let data = fs.readFileSync(path.join(JSON_DIR, fileName), 'utf-8'); + + assert(data !== null); + pluginConfig.srcModel = await blobClient.putFile(fileName, data); + let importJSONPluginResult = await executePlugin(ImportPluginName, pluginConfig, context); + + assert(importJSONPluginResult.success); + + let newBranchHash = await project.getBranchHash('test'); + commitHash = newBranchHash; + let newCommitObj = await awaitableLoadObject(newBranchHash); + + let newRootNode = await importPlugin.core.loadRoot(newCommitObj.root); + const addedArchitectureNode = (await importPlugin.core.loadChildren(newRootNode)).filter((node) => { + return importPlugin.core.getAttribute(node, 'name') === fileName.replace('.json', ''); + })[0]; + assert(addedArchitectureNode); + + assert(importPlugin.core.getBase(addedArchitectureNode), importPlugin.META['Architecuture']); + + context = { + project: project, + commitHash: commitHash, + branchName: 'test', + activeNode: importPlugin.core.getPath(addedArchitectureNode), + namespace: '' + }; + // pluginConfig = {}; + // + // let generateCodePluginResult = await executePlugin(CodeGenerationPluginName, pluginConfig, context); + // assert(generateCodePluginResult.success) + }; +}); \ No newline at end of file From 5c352232156c1438accae6ba8f5aa0e958591236 Mon Sep 17 00:00:00 2001 From: Umesh Date: Thu, 7 Nov 2019 11:33:51 -0600 Subject: [PATCH 18/23] WIP- #131: Add Correct Layer Pointers, refactor string layer addition methods, change to l1_l2 in class maps --- src/plugins/ImportKeras/ImportKeras.js | 40 +++++++------------ .../ImportKeras/utils/JSONModelMaps.js | 3 +- 2 files changed, 17 insertions(+), 26 deletions(-) diff --git a/src/plugins/ImportKeras/ImportKeras.js b/src/plugins/ImportKeras/ImportKeras.js index 2f840b0..d6380a6 100644 --- a/src/plugins/ImportKeras/ImportKeras.js +++ b/src/plugins/ImportKeras/ImportKeras.js @@ -141,43 +141,33 @@ define([ ` has following configurable attributes ${allPointerNames.join(', ')}`); allPointerNames.filter(pointer => !!config[pointer]) .forEach((pointer) => { - if (typeof config[pointer] == 'string') { - this._addStringPropertiesNode(layerNode, config, pointer); - } else { - this._addPluggableNodes(layerNode, config, pointer); - } + let node = this._addFunctionNode(layerNode, config, pointer); + this.core.setPointer(layerNode, pointer, node); + this.logger.debug(`Added ${this.core.getAttribute(node, 'name')}` + + ` as ${pointer} to the layer ` + + `${this.core.getAttribute(layerNode, 'name')}`); }); }; - ImportKeras.prototype._addStringPropertiesNode = function(layerNode, config, pointer) { - let configurableNode = this.core.createNode({ - parent: layerNode, - base: this.META[config[pointer]] - }); - // This will set the necessary pointers. - // Of things like activations and so on... - this.core.setPointer(layerNode, pointer, configurableNode); - this.logger.debug(`Added ${this.core.getAttribute(configurableNode, 'name')}` - + ` as ${pointer} to the layer ` - + `${this.core.getAttribute(layerNode, 'name')}`); - }; - ImportKeras.prototype._addPluggableNodes = function (layerNode, config, pointer){ - let pluggableNode = this.core.createNode({ + ImportKeras.prototype._addFunctionNode = function (layerNode, config, pointer){ + const baseNodeName = (typeof config[pointer] === 'string' ? config[pointer] : config[pointer].class_name); + let configurableNode = this.core.createNode({ parent: layerNode, - base: this.META[config[pointer].class_name] + base: this.META[this._getMetaTypeForClass(baseNodeName)] }); - this.logger.debug(`Added ${this.core.getAttribute(pluggableNode, 'name')} as` + + this.logger.debug(`Added ${this.core.getAttribute(configurableNode, 'name')} as` + ` ${pointer} to the layer ${this.core.getAttribute(layerNode, 'name')}`); - let validArgumentsForThisNode = this.core.getValidAttributeNames(pluggableNode); + let validArgumentsForThisNode = this.core.getValidAttributeNames(configurableNode); let configForAddedNode = config[pointer].config; if (validArgumentsForThisNode && configForAddedNode) { validArgumentsForThisNode.forEach((arg) => { if (configForAddedNode[arg]) - this.core.setAttribute(pluggableNode, arg, + this.core.setAttribute(configurableNode, arg, this._toPythonType(configForAddedNode[arg])); }); } + return configurableNode; }; // This method is used to convert javascript arrays/booleans to a @@ -188,9 +178,9 @@ define([ return 'None'; } if (obj instanceof Array) { - return '[' + obj.map((val) => { + return '(' + obj.map((val) => { return this._toPythonType(val); - }).join(', ') + ']'; + }).join(', ') + ')'; } else if (typeof obj === 'boolean') { return obj ? 'True' : 'False'; } else { diff --git a/src/plugins/ImportKeras/utils/JSONModelMaps.js b/src/plugins/ImportKeras/utils/JSONModelMaps.js index 2dacfa3..c490286 100644 --- a/src/plugins/ImportKeras/utils/JSONModelMaps.js +++ b/src/plugins/ImportKeras/utils/JSONModelMaps.js @@ -3,7 +3,8 @@ define([], function() { const ModelMaps = {}; ModelMaps.CLASS_MAP = { - InputLayer: 'Input' + InputLayer: 'Input', + L1L2: 'l1_l2' }; ModelMaps.ARGUMENTS_MAP = { From 1cd436a77e93f2dcb6ccfe82c75e9b0d4d9e48da Mon Sep 17 00:00:00 2001 From: Umesh Date: Thu, 7 Nov 2019 14:27:29 -0600 Subject: [PATCH 19/23] WIP- #131: Complete Integration Test for JSON Imports(Only works for models without dropout layers) --- .../ImportedArchitectureCode.spec.js | 37 +++++++++---------- 1 file changed, 17 insertions(+), 20 deletions(-) diff --git a/test/integration/ImportArchitectureCode/ImportedArchitectureCode.spec.js b/test/integration/ImportArchitectureCode/ImportedArchitectureCode.spec.js index d34cbbe..ea0a18a 100644 --- a/test/integration/ImportArchitectureCode/ImportedArchitectureCode.spec.js +++ b/test/integration/ImportArchitectureCode/ImportedArchitectureCode.spec.js @@ -1,4 +1,3 @@ -/*globals requireJS*/ /*eslint-env node, mocha*/ 'use strict'; @@ -11,7 +10,6 @@ describe('Imported JSON to Code', function () { assert = require('assert'), gmeConfig = testFixture.getGmeConfig(), executePython = testFixture.executePython, - expect = testFixture.expect, path = testFixture.path, logger = testFixture.logger.fork('ImportedArchitectureToCode'), PluginCliManager = testFixture.WebGME.PluginCliManager, @@ -65,9 +63,11 @@ describe('Imported JSON to Code', function () { await gmeAuth.unload(); }); - const modelsToTest = fs.readdirSync(JSON_DIR).filter((targetFile) => { - return targetFile.endsWith('.json'); - }); + // let modelsToTest = fs.readdirSync(JSON_DIR).filter((targetFile) => { + // return targetFile.endsWith('.json'); + // }); + + let modelsToTest = ['sequential_dense.json']; modelsToTest.forEach((modelJSON) => { describe(`Import JSON and generate Code For ${modelJSON}`, function () { @@ -77,9 +77,9 @@ describe('Imported JSON to Code', function () { }); it(`should run the python code generated for ${modelJSON}`, () => { - let execResult = executePython(generatedCode); - // assert(execResult.success); - }) + let execResult = executePython(generatedCode); + assert(execResult.success); + }).timeout(5000); }) }); @@ -92,15 +92,13 @@ describe('Imported JSON to Code', function () { project: project, activeNode: '', branchName: 'test', + commitHash: commitHash, namespace: '' }; let data = fs.readFileSync(path.join(JSON_DIR, fileName), 'utf-8'); - assert(data !== null); pluginConfig.srcModel = await blobClient.putFile(fileName, data); - let importJSONPluginResult = await executePlugin(ImportPluginName, pluginConfig, context); - - assert(importJSONPluginResult.success); + await executePlugin(ImportPluginName, pluginConfig, context); let newBranchHash = await project.getBranchHash('test'); commitHash = newBranchHash; @@ -108,11 +106,8 @@ describe('Imported JSON to Code', function () { let newRootNode = await importPlugin.core.loadRoot(newCommitObj.root); const addedArchitectureNode = (await importPlugin.core.loadChildren(newRootNode)).filter((node) => { - return importPlugin.core.getAttribute(node, 'name') === fileName.replace('.json', ''); + return importPlugin.core.getAttribute(node, 'name') === fileName.replace('.json', ''); })[0]; - assert(addedArchitectureNode); - - assert(importPlugin.core.getBase(addedArchitectureNode), importPlugin.META['Architecuture']); context = { project: project, @@ -121,9 +116,11 @@ describe('Imported JSON to Code', function () { activeNode: importPlugin.core.getPath(addedArchitectureNode), namespace: '' }; - // pluginConfig = {}; - // - // let generateCodePluginResult = await executePlugin(CodeGenerationPluginName, pluginConfig, context); - // assert(generateCodePluginResult.success) + pluginConfig = {}; + let generateCodePluginResult = await executePlugin(CodeGenerationPluginName, pluginConfig, context); + assert(generateCodePluginResult.success); + const codeHash = generateCodePluginResult.artifacts[0]; + const codeObj = await blobClient.getObject(codeHash); + return String.fromCharCode.apply(null, new Uint8Array(codeObj)); }; }); \ No newline at end of file From aba4e1eeeda7f7e2cef7458932ff5b80986b6767 Mon Sep 17 00:00:00 2001 From: Umesh Date: Thu, 7 Nov 2019 14:27:29 -0600 Subject: [PATCH 20/23] WIP- #130: Complete Integration Test for JSON Imports(Only works for models without dropout layers) --- .../ImportedArchitectureCode.spec.js | 43 +++++++++---------- 1 file changed, 20 insertions(+), 23 deletions(-) diff --git a/test/integration/ImportArchitectureCode/ImportedArchitectureCode.spec.js b/test/integration/ImportArchitectureCode/ImportedArchitectureCode.spec.js index d34cbbe..b5c145b 100644 --- a/test/integration/ImportArchitectureCode/ImportedArchitectureCode.spec.js +++ b/test/integration/ImportArchitectureCode/ImportedArchitectureCode.spec.js @@ -1,4 +1,3 @@ -/*globals requireJS*/ /*eslint-env node, mocha*/ 'use strict'; @@ -11,7 +10,6 @@ describe('Imported JSON to Code', function () { assert = require('assert'), gmeConfig = testFixture.getGmeConfig(), executePython = testFixture.executePython, - expect = testFixture.expect, path = testFixture.path, logger = testFixture.logger.fork('ImportedArchitectureToCode'), PluginCliManager = testFixture.WebGME.PluginCliManager, @@ -65,42 +63,42 @@ describe('Imported JSON to Code', function () { await gmeAuth.unload(); }); - const modelsToTest = fs.readdirSync(JSON_DIR).filter((targetFile) => { - return targetFile.endsWith('.json'); - }); + // let modelsToTest = fs.readdirSync(JSON_DIR).filter((targetFile) => { + // return targetFile.endsWith('.json'); + // }); + + let modelsToTest = ['sequential_dense.json']; modelsToTest.forEach((modelJSON) => { describe(`Import JSON and generate Code For ${modelJSON}`, function () { let generatedCode; before(async function () { - generatedCode = await importJSONAndGetGeneratedCode(modelJSON); + generatedCode = await ImportJSONAndGetGeneratedCode(modelJSON); }); it(`should run the python code generated for ${modelJSON}`, () => { - let execResult = executePython(generatedCode); - // assert(execResult.success); - }) - }) + let execResult = executePython(generatedCode); + assert(execResult.success); + }).timeout(5000); + }); }); // This function runs ImportKeras, followed by GenerateKeras - const importJSONAndGetGeneratedCode = async function(fileName){ + const ImportJSONAndGetGeneratedCode = async function(fileName){ let executePlugin = promisify(manager.executePlugin), pluginConfig = {}, context = { project: project, activeNode: '', branchName: 'test', + commitHash: commitHash, namespace: '' }; let data = fs.readFileSync(path.join(JSON_DIR, fileName), 'utf-8'); - assert(data !== null); pluginConfig.srcModel = await blobClient.putFile(fileName, data); - let importJSONPluginResult = await executePlugin(ImportPluginName, pluginConfig, context); - - assert(importJSONPluginResult.success); + await executePlugin(ImportPluginName, pluginConfig, context); let newBranchHash = await project.getBranchHash('test'); commitHash = newBranchHash; @@ -108,11 +106,8 @@ describe('Imported JSON to Code', function () { let newRootNode = await importPlugin.core.loadRoot(newCommitObj.root); const addedArchitectureNode = (await importPlugin.core.loadChildren(newRootNode)).filter((node) => { - return importPlugin.core.getAttribute(node, 'name') === fileName.replace('.json', ''); + return importPlugin.core.getAttribute(node, 'name') === fileName.replace('.json', ''); })[0]; - assert(addedArchitectureNode); - - assert(importPlugin.core.getBase(addedArchitectureNode), importPlugin.META['Architecuture']); context = { project: project, @@ -121,9 +116,11 @@ describe('Imported JSON to Code', function () { activeNode: importPlugin.core.getPath(addedArchitectureNode), namespace: '' }; - // pluginConfig = {}; - // - // let generateCodePluginResult = await executePlugin(CodeGenerationPluginName, pluginConfig, context); - // assert(generateCodePluginResult.success) + pluginConfig = {}; + let generateCodePluginResult = await executePlugin(CodeGenerationPluginName, pluginConfig, context); + assert(generateCodePluginResult.success); + const codeHash = generateCodePluginResult.artifacts[0]; + const codeObj = await blobClient.getObject(codeHash); + return String.fromCharCode.apply(null, new Uint8Array(codeObj)); }; }); \ No newline at end of file From aeb519a190c30315cccae4d762184a9843851a77 Mon Sep 17 00:00:00 2001 From: Umesh Date: Wed, 13 Nov 2019 15:49:30 -0600 Subject: [PATCH 21/23] WIP- #130: Add Failing tests for GenerateKeras --- .../ImportedArchitectureCode.spec.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/integration/ImportArchitectureCode/ImportedArchitectureCode.spec.js b/test/integration/ImportArchitectureCode/ImportedArchitectureCode.spec.js index 96921aa..709ad8b 100644 --- a/test/integration/ImportArchitectureCode/ImportedArchitectureCode.spec.js +++ b/test/integration/ImportArchitectureCode/ImportedArchitectureCode.spec.js @@ -63,11 +63,11 @@ describe('Imported JSON to Code', function () { await gmeAuth.unload(); }); - // let modelsToTest = fs.readdirSync(JSON_DIR).filter((targetFile) => { - // return targetFile.endsWith('.json'); - // }); + let modelsToTest = fs.readdirSync(JSON_DIR).filter((targetFile) => { + return targetFile.endsWith('.json'); + }); - let modelsToTest = ['sequential_dense.json']; + // let modelsToTest = ['sequential_dense.json']; modelsToTest.forEach((modelJSON) => { describe(`Import JSON and generate Code For ${modelJSON}`, function () { From 6b1a1d5499bdd7a8788a098c4f04ed495e1bff7d Mon Sep 17 00:00:00 2001 From: Brian Broll Date: Thu, 14 Nov 2019 09:18:01 -0600 Subject: [PATCH 22/23] WIP Add helper method for getting ordering inputs/outputs --- src/plugins/ImportKeras/ImportKeras.js | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/src/plugins/ImportKeras/ImportKeras.js b/src/plugins/ImportKeras/ImportKeras.js index d6380a6..df738e4 100644 --- a/src/plugins/ImportKeras/ImportKeras.js +++ b/src/plugins/ImportKeras/ImportKeras.js @@ -262,8 +262,9 @@ define([ ImportKeras.prototype._connectLayers = async function (srcLayer, dstLayer, index) { - let srcPort = await this.core.loadMembers(srcLayer, 'outputs'); - let dstPort = await this.core.loadMembers(dstLayer, 'inputs'); + let srcPort = await this.getOrderedMembers(srcLayer, 'outputs'); + let dstPort = await this.getOrderedMembers(dstLayer, 'inputs'); + if (dstPort && srcPort) { this.core.addMember(dstPort[0], 'source', srcPort[0]); this.core.setMemberRegistry(dstPort[0], @@ -278,5 +279,22 @@ define([ } }; + ImportKeras.prototype.getOrderedMembers = async function (node, setName) { + const members = await this.core.loadMembers(node, setName); + + members.sort((m1, m2) => { + const index1 = this.getMemberIndex(node, setName, m1); + const index2 = this.getMemberIndex(node, setName, m2); + return index1 < index2 ? -1 : 1; + }); + + return members; + }; + + ImportKeras.prototype.getMemberIndex = function (node, setName, member) { + const path = this.core.getPath(member); + return this.core.getMemberAttribute(node, setName, path, 'index'); + }; + return ImportKeras; -}); \ No newline at end of file +}); From 8a6617511d6a741306eb8dd25bd79303275ba889 Mon Sep 17 00:00:00 2001 From: Brian Broll Date: Thu, 14 Nov 2019 09:21:30 -0600 Subject: [PATCH 23/23] WIP Change src/dstPort to be a single port. Added question --- src/plugins/ImportKeras/ImportKeras.js | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/plugins/ImportKeras/ImportKeras.js b/src/plugins/ImportKeras/ImportKeras.js index df738e4..dcffba6 100644 --- a/src/plugins/ImportKeras/ImportKeras.js +++ b/src/plugins/ImportKeras/ImportKeras.js @@ -262,17 +262,18 @@ define([ ImportKeras.prototype._connectLayers = async function (srcLayer, dstLayer, index) { - let srcPort = await this.getOrderedMembers(srcLayer, 'outputs'); - let dstPort = await this.getOrderedMembers(dstLayer, 'inputs'); + // FIXME: Do we really want to always be connecting to the *first* input/output? + let srcPort = (await this.getOrderedMembers(srcLayer, 'outputs'))[0]; + let dstPort = (await this.getOrderedMembers(dstLayer, 'inputs'))[0]; if (dstPort && srcPort) { - this.core.addMember(dstPort[0], 'source', srcPort[0]); - this.core.setMemberRegistry(dstPort[0], + this.core.addMember(dstPort, 'source', srcPort); + this.core.setMemberRegistry(dstPort, 'source', - this.core.getPath(srcPort[0]), + this.core.getPath(srcPort), 'position', {x: 100, y: 100}); - this.core.setMemberAttribute(dstPort[0], 'source', - this.core.getPath(srcPort[0]), + this.core.setMemberAttribute(dstPort, 'source', + this.core.getPath(srcPort), 'index', index); this.logger.debug(`Connected ${this.core.getAttribute(srcLayer, 'name')} ` + `with ${this.core.getAttribute(dstLayer, 'name')} as input ${index}`);