UNPKG

neataptic

Version:

Architecture-free neural network library with genetic algorithm implementations

370 lines (302 loc) 11.1 kB
/* Import */ var methods = require('../methods/methods'); var Network = require('./network'); var Group = require('./group'); var Layer = require('./layer'); var Node = require('./node'); /******************************************************************************* architect *******************************************************************************/ var architect = { /** * Constructs a network from a given array of connected nodes */ Construct: function (list) { // Create a network var network = new Network(0, 0); // Transform all groups into nodes var nodes = []; var i; for (i = 0; i < list.length; i++) { let j; if (list[i] instanceof Group) { for (j = 0; j < list[i].nodes.length; j++) { nodes.push(list[i].nodes[j]); } } else if (list[i] instanceof Layer) { for (j = 0; j < list[i].nodes.length; j++) { for (var k = 0; k < list[i].nodes[j].nodes.length; k++) { nodes.push(list[i].nodes[j].nodes[k]); } } } else if (list[i] instanceof Node) { nodes.push(list[i]); } } // Determine input and output nodes var inputs = []; var outputs = []; for (i = nodes.length - 1; i >= 0; i--) { if (nodes[i].type === 'output' || nodes[i].connections.out.length + nodes[i].connections.gated.length === 0) { nodes[i].type = 'output'; network.output++; outputs.push(nodes[i]); nodes.splice(i, 1); } else if (nodes[i].type === 'input' || !nodes[i].connections.in.length) { nodes[i].type = 'input'; network.input++; inputs.push(nodes[i]); nodes.splice(i, 1); } } // Input nodes are always first, output nodes are always last nodes = inputs.concat(nodes).concat(outputs); if (network.input === 0 || network.output === 0) { throw new Error('Given nodes have no clear input/output node!'); } for (i = 0; i < nodes.length; i++) { let j; for (j = 0; j < nodes[i].connections.out.length; j++) { network.connections.push(nodes[i].connections.out[j]); } for (j = 0; j < nodes[i].connections.gated.length; j++) { network.gates.push(nodes[i].connections.gated[j]); } if (nodes[i].connections.self.weight !== 0) { network.selfconns.push(nodes[i].connections.self); } } network.nodes = nodes; return network; }, /** * Creates a multilayer perceptron (MLP) */ Perceptron: function () { // Convert arguments to Array var layers = Array.prototype.slice.call(arguments); if (layers.length < 3) { throw new Error('You have to specify at least 3 layers'); } // Create a list of nodes/groups var nodes = []; nodes.push(new Group(layers[0])); for (var i = 1; i < layers.length; i++) { var layer = layers[i]; layer = new Group(layer); nodes.push(layer); nodes[i - 1].connect(nodes[i], methods.connection.ALL_TO_ALL); } // Construct the network return architect.Construct(nodes); }, /** * Creates a randomly connected network */ Random: function (input, hidden, output, options) { options = options || {}; var connections = options.connections || hidden * 2; var backconnections = options.backconnections || 0; var selfconnections = options.selfconnections || 0; var gates = options.gates || 0; var network = new Network(input, output); var i; for (i = 0; i < hidden; i++) { network.mutate(methods.mutation.ADD_NODE); } for (i = 0; i < connections - hidden; i++) { network.mutate(methods.mutation.ADD_CONN); } for (i = 0; i < backconnections; i++) { network.mutate(methods.mutation.ADD_BACK_CONN); } for (i = 0; i < selfconnections; i++) { network.mutate(methods.mutation.ADD_SELF_CONN); } for (i = 0; i < gates; i++) { network.mutate(methods.mutation.ADD_GATE); } return network; }, /** * Creates a long short-term memory network */ LSTM: function () { var args = Array.prototype.slice.call(arguments); if (args.length < 3) { throw new Error('You have to specify at least 3 layers'); } var last = args.pop(); var outputLayer; if (typeof last === 'number') { outputLayer = new Group(last); last = {}; } else { outputLayer = new Group(args.pop()); // last argument } outputLayer.set({ type: 'output' }); var options = {}; options.memoryToMemory = last.memoryToMemory || false; options.outputToMemory = last.outputToMemory || false; options.outputToGates = last.outputToGates || false; options.inputToOutput = last.inputToOutput === undefined ? true : last.inputToOutput; options.inputToDeep = last.inputToDeep === undefined ? true : last.inputToDeep; var inputLayer = new Group(args.shift()); // first argument inputLayer.set({ type: 'input' }); var blocks = args; // all the arguments in the middle var nodes = []; nodes.push(inputLayer); var previous = inputLayer; for (var i = 0; i < blocks.length; i++) { var block = blocks[i]; // Init required nodes (in activation order) var inputGate = new Group(block); var forgetGate = new Group(block); var memoryCell = new Group(block); var outputGate = new Group(block); var outputBlock = i === blocks.length - 1 ? outputLayer : new Group(block); inputGate.set({ bias: 1 }); forgetGate.set({ bias: 1 }); outputGate.set({ bias: 1 }); // Connect the input with all the nodes var input = previous.connect(memoryCell, methods.connection.ALL_TO_ALL); previous.connect(inputGate, methods.connection.ALL_TO_ALL); previous.connect(outputGate, methods.connection.ALL_TO_ALL); previous.connect(forgetGate, methods.connection.ALL_TO_ALL); // Set up internal connections memoryCell.connect(inputGate, methods.connection.ALL_TO_ALL); memoryCell.connect(forgetGate, methods.connection.ALL_TO_ALL); memoryCell.connect(outputGate, methods.connection.ALL_TO_ALL); var forget = memoryCell.connect(memoryCell, methods.connection.ONE_TO_ONE); var output = memoryCell.connect(outputBlock, methods.connection.ALL_TO_ALL); // Set up gates inputGate.gate(input, methods.gating.INPUT); forgetGate.gate(forget, methods.gating.SELF); outputGate.gate(output, methods.gating.OUTPUT); // Input to all memory cells if (options.inputToDeep && i > 0) { let input = inputLayer.connect(memoryCell, methods.connection.ALL_TO_ALL); inputGate.gate(input, methods.gating.INPUT); } // Optional connections if (options.memoryToMemory) { let input = memoryCell.connect(memoryCell, methods.connection.ALL_TO_ELSE); inputGate.gate(input, methods.gating.INPUT); } if (options.outputToMemory) { let input = outputLayer.connect(memoryCell, methods.connection.ALL_TO_ALL); inputGate.gate(input, methods.gating.INPUT); } if (options.outputToGates) { outputLayer.connect(inputGate, methods.connection.ALL_TO_ALL); outputLayer.connect(forgetGate, methods.connection.ALL_TO_ALL); outputLayer.connect(outputGate, methods.connection.ALL_TO_ALL); } // Add to array nodes.push(inputGate); nodes.push(forgetGate); nodes.push(memoryCell); nodes.push(outputGate); if (i !== blocks.length - 1) nodes.push(outputBlock); previous = outputBlock; } // input to output direct connection if (options.inputToOutput) { inputLayer.connect(outputLayer, methods.connection.ALL_TO_ALL); } nodes.push(outputLayer); return architect.Construct(nodes); }, /** * Creates a gated recurrent unit network */ GRU: function () { var args = Array.prototype.slice.call(arguments); if (args.length < 3) { throw new Error('not enough layers (minimum 3) !!'); } var inputLayer = new Group(args.shift()); // first argument var outputLayer = new Group(args.pop()); // last argument var blocks = args; // all the arguments in the middle var nodes = []; nodes.push(inputLayer); var previous = inputLayer; for (var i = 0; i < blocks.length; i++) { var layer = new Layer.GRU(blocks[i]); previous.connect(layer); previous = layer; nodes.push(layer); } previous.connect(outputLayer); nodes.push(outputLayer); return architect.Construct(nodes); }, /** * Creates a hopfield network of the given size */ Hopfield: function (size) { var input = new Group(size); var output = new Group(size); input.connect(output, methods.connection.ALL_TO_ALL); input.set({ type: 'input' }); output.set({ squash: methods.activation.STEP, type: 'output' }); var network = new architect.Construct([input, output]); return network; }, /** * Creates a NARX network (remember previous inputs/outputs) */ NARX: function (inputSize, hiddenLayers, outputSize, previousInput, previousOutput) { if (!Array.isArray(hiddenLayers)) { hiddenLayers = [hiddenLayers]; } var nodes = []; var input = new Layer.Dense(inputSize); var inputMemory = new Layer.Memory(inputSize, previousInput); var hidden = []; var output = new Layer.Dense(outputSize); var outputMemory = new Layer.Memory(outputSize, previousOutput); nodes.push(input); nodes.push(outputMemory); for (var i = 0; i < hiddenLayers.length; i++) { var hiddenLayer = new Layer.Dense(hiddenLayers[i]); hidden.push(hiddenLayer); nodes.push(hiddenLayer); if (typeof hidden[i - 1] !== 'undefined') { hidden[i - 1].connect(hiddenLayer, methods.connection.ALL_TO_ALL); } } nodes.push(inputMemory); nodes.push(output); input.connect(hidden[0], methods.connection.ALL_TO_ALL); input.connect(inputMemory, methods.connection.ONE_TO_ONE, 1); inputMemory.connect(hidden[0], methods.connection.ALL_TO_ALL); hidden[hidden.length - 1].connect(output, methods.connection.ALL_TO_ALL); output.connect(outputMemory, methods.connection.ONE_TO_ONE, 1); outputMemory.connect(hidden[0], methods.connection.ALL_TO_ALL); input.set({ type: 'input' }); output.set({ type: 'output' }); return architect.Construct(nodes); } }; /* Export */ module.exports = architect;