3 Commits

29 arquivos alterados com 2628 adições e 2484 exclusões
+4
Ver Arquivo
@@ -0,0 +1,4 @@
{
"presets": ["es2015"],
"plugins": []
}
+1024 -990
Ver Arquivo
Diff do arquivo suprimido porque uma ou mais linhas são muito longas
+11
Ver Arquivo
@@ -0,0 +1,11 @@
const nodeMajorVersion = parseInt(process.versions.node.split('.')[0]);
if (!nodeMajorVersion || nodeMajorVersion < 6) {
module.exports = require('./dist/synaptic')
} else {
try {
module.exports = require('./src/synaptic')
} catch (e) {
module.exports = require('./dist/synaptic')
}
}
+18 -2
Ver Arquivo
@@ -1,3 +1,5 @@
var webpack = require('webpack');
var webpackConfig = require('./webpack.config');
// Karma configuration
module.exports = function(config) {
@@ -5,14 +7,16 @@ module.exports = function(config) {
basePath: '',
frameworks: ['mocha'],
files: [
'dist/synaptic.js',
'test/[^_]*.js'
'test/*.js',
],
exclude: [
],
preprocessors: {
'test/*.js': ['webpack'],
},
client: {
grep: /^[^(Node)]/
},
reporters: ['progress'],
port: 9876,
colors: true,
@@ -21,5 +25,17 @@ module.exports = function(config) {
singleRun: false,
concurrency: Infinity,
browserNoActivityTimeout: 60000,
webpack: {
loaders: webpackConfig.loaders,
plugins: webpackConfig.plugins
.concat([
// does not affect production build, only used for tests
new webpack.DefinePlugin({
'process.env.SYNAPTIC_PREFER_SRC': JSON.stringify(process.env.SYNAPTIC_PREFER_SRC),
'process.env.KARMA': 'true',
}),
new webpack.NoErrorsPlugin(),
])
},
})
}
+18 -7
Ver Arquivo
@@ -2,16 +2,17 @@
"name": "synaptic",
"version": "1.0.8",
"description": "architecture-free neural network library",
"main": "./src/synaptic",
"main": "./index.js",
"scripts": {
"test": "npm run test:src",
"test:src": "mocha test --require src/synaptic.js ./test",
"test:src": "npm run test:mocha:src",
"test:dist": "npm run build && npm run test:mocha:dist && npm run test:karma:browsers",
"test:mocha:src": "mocha test --require src/synaptic.js ./test",
"test:mocha:dist": "mocha test --require dist/synaptic.js ./test",
"test:mocha:src": "SYNAPTIC_PREFER_SRC=true mocha test",
"test:mocha:dist": "mocha test",
"test:karma:browsers": "karma start --single-run --browsers Chrome,Firefox,SafariPrivate",
"test:karma:chrome": "karma start --browsers Chrome",
"test:karma:phantomjs": "karma start --single-run --browsers PhantomJS",
"test:travis": "npm run test:mocha:src && npm run build && npm run test:mocha:dist",
"test:travis": "npm run build && npm run test:mocha:dist",
"build": "webpack --config webpack.config.js"
},
"prepush": [
@@ -19,18 +20,24 @@
"build"
],
"devDependencies": {
"babel-loader": "^6.2.4",
"babel-preset-es2015": "^6.9.0",
"chai": "^3.5.0",
"chai-as-promised": "^5.3.0",
"chai-stats": "^0.3.0",
"karma": "^1.1.2",
"karma-chrome-launcher": "^1.0.1",
"karma-firefox-launcher": "^1.0.0",
"karma-mocha": "^1.1.1",
"karma-phantomjs-launcher": "^1.0.1",
"karma-safari-launcher": "^1.0.0",
"karma-safari-private-launcher": "^1.0.0",
"karma-webpack": "^1.7.0",
"mocha": "^2.2.4",
"pre-push": "^0.1.1",
"webpack": "^1.13.1"
"tiny-worker": "^1.1.5",
"webpack": "^1.13.1",
"webworker-threads": "^0.7.8",
"worker-loader": "^0.7.1"
},
"repository": {
"type": "git",
@@ -54,5 +61,9 @@
"homepage": "http://synaptic.juancazala.com",
"engines": {
"node": ">=4"
},
"dependencies": {
"node-worker": "0.0.2",
"pseudo-worker": "^1.1.0"
}
}
-273
Ver Arquivo
@@ -1,273 +0,0 @@
// import
var Layer = require('./layer')
, Network = require('./network')
, Trainer = require('./trainer')
/*******************************************************************************************
ARCHITECT
*******************************************************************************************/
// Collection of useful built-in architectures
var Architect = {
// Multilayer Perceptron
Perceptron: function Perceptron() {
var args = Array.prototype.slice.call(arguments); // convert arguments to Array
if (args.length < 3)
throw new Error("not enough layers (minimum 3) !!");
var inputs = args.shift(); // first argument
var outputs = args.pop(); // last argument
var layers = args; // all the arguments in the middle
var input = new Layer(inputs);
var hidden = [];
var output = new Layer(outputs);
var previous = input;
// generate hidden layers
for (var level in layers) {
var size = layers[level];
var layer = new Layer(size);
hidden.push(layer);
previous.project(layer);
previous = layer;
}
previous.project(output);
// set layers of the neural network
this.set({
input: input,
hidden: hidden,
output: output
});
// trainer for the network
this.trainer = new Trainer(this);
},
// Multilayer Long Short-Term Memory
LSTM: function LSTM() {
var args = Array.prototype.slice.call(arguments); // convert arguments to array
if (args.length < 3)
throw new Error("not enough layers (minimum 3) !!");
var last = args.pop();
var option = {
peepholes: Layer.connectionType.ALL_TO_ALL,
hiddenToHidden: false,
outputToHidden: false,
outputToGates: false,
inputToOutput: true,
};
if (typeof last != 'number') {
var outputs = args.pop();
if (last.hasOwnProperty('peepholes'))
option.peepholes = last.peepholes;
if (last.hasOwnProperty('hiddenToHidden'))
option.hiddenToHidden = last.hiddenToHidden;
if (last.hasOwnProperty('outputToHidden'))
option.outputToHidden = last.outputToHidden;
if (last.hasOwnProperty('outputToGates'))
option.outputToGates = last.outputToGates;
if (last.hasOwnProperty('inputToOutput'))
option.inputToOutput = last.inputToOutput;
} else
var outputs = last;
var inputs = args.shift();
var layers = args;
var inputLayer = new Layer(inputs);
var hiddenLayers = [];
var outputLayer = new Layer(outputs);
var previous = null;
// generate layers
for (var layer in layers) {
// generate memory blocks (memory cell and respective gates)
var size = layers[layer];
var inputGate = new Layer(size).set({
bias: 1
});
var forgetGate = new Layer(size).set({
bias: 1
});
var memoryCell = new Layer(size);
var outputGate = new Layer(size).set({
bias: 1
});
hiddenLayers.push(inputGate);
hiddenLayers.push(forgetGate);
hiddenLayers.push(memoryCell);
hiddenLayers.push(outputGate);
// connections from input layer
var input = inputLayer.project(memoryCell);
inputLayer.project(inputGate);
inputLayer.project(forgetGate);
inputLayer.project(outputGate);
// connections from previous memory-block layer to this one
if (previous != null) {
var cell = previous.project(memoryCell);
previous.project(inputGate);
previous.project(forgetGate);
previous.project(outputGate);
}
// connections from memory cell
var output = memoryCell.project(outputLayer);
// self-connection
var self = memoryCell.project(memoryCell);
// hidden to hidden recurrent connection
if (option.hiddenToHidden)
memoryCell.project(memoryCell, Layer.connectionType.ALL_TO_ELSE);
// out to hidden recurrent connection
if (option.outputToHidden)
outputLayer.project(memoryCell);
// out to gates recurrent connection
if (option.outputToGates) {
outputLayer.project(inputGate);
outputLayer.project(outputGate);
outputLayer.project(forgetGate);
}
// peepholes
memoryCell.project(inputGate, option.peepholes);
memoryCell.project(forgetGate, option.peepholes);
memoryCell.project(outputGate, option.peepholes);
// gates
inputGate.gate(input, Layer.gateType.INPUT);
forgetGate.gate(self, Layer.gateType.ONE_TO_ONE);
outputGate.gate(output, Layer.gateType.OUTPUT);
if (previous != null)
inputGate.gate(cell, Layer.gateType.INPUT);
previous = memoryCell;
}
// input to output direct connection
if (option.inputToOutput)
inputLayer.project(outputLayer);
// set the layers of the neural network
this.set({
input: inputLayer,
hidden: hiddenLayers,
output: outputLayer
});
// trainer
this.trainer = new Trainer(this);
},
// Liquid State Machine
Liquid: function Liquid(inputs, hidden, outputs, connections, gates) {
// create layers
var inputLayer = new Layer(inputs);
var hiddenLayer = new Layer(hidden);
var outputLayer = new Layer(outputs);
// make connections and gates randomly among the neurons
var neurons = hiddenLayer.neurons();
var connectionList = [];
for (var i = 0; i < connections; i++) {
// connect two random neurons
var from = Math.random() * neurons.length | 0;
var to = Math.random() * neurons.length | 0;
var connection = neurons[from].project(neurons[to]);
connectionList.push(connection);
}
for (var j = 0; j < gates; j++) {
// pick a random gater neuron
var gater = Math.random() * neurons.length | 0;
// pick a random connection to gate
var connection = Math.random() * connectionList.length | 0;
// let the gater gate the connection
neurons[gater].gate(connectionList[connection]);
}
// connect the layers
inputLayer.project(hiddenLayer);
hiddenLayer.project(outputLayer);
// set the layers of the network
this.set({
input: inputLayer,
hidden: [hiddenLayer],
output: outputLayer
});
// trainer
this.trainer = new Trainer(this);
},
Hopfield: function Hopfield(size) {
var inputLayer = new Layer(size);
var outputLayer = new Layer(size);
inputLayer.project(outputLayer, Layer.connectionType.ALL_TO_ALL);
this.set({
input: inputLayer,
hidden: [],
output: outputLayer
});
var trainer = new Trainer(this);
var proto = Architect.Hopfield.prototype;
proto.learn = proto.learn || function(patterns)
{
var set = [];
for (var p in patterns)
set.push({
input: patterns[p],
output: patterns[p]
});
return trainer.train(set, {
iterations: 500000,
error: .00005,
rate: 1
});
};
proto.feed = proto.feed || function(pattern)
{
var output = this.activate(pattern);
var pattern = [];
for (var i in output)
pattern[i] = output[i] > .5 ? 1 : 0;
return pattern;
}
}
}
// Extend prototype chain (so every architectures is an instance of Network)
for (var architecture in Architect) {
Architect[architecture].prototype = new Network();
Architect[architecture].prototype.constructor = Architect[architecture];
}
// export
if (module) module.exports = Architect;
+14
Ver Arquivo
@@ -0,0 +1,14 @@
const Network = require('../network');
const Trainer = require('../trainer');
class AbstractArchitecture extends Network {
constructor(layers) {
super(layers);
this.trainer = new this.constructor.Trainer(this);
}
}
module.exports = AbstractArchitecture;
AbstractArchitecture.NetworkClass = Network;
AbstractArchitecture.Trainer = Trainer;
+42
Ver Arquivo
@@ -0,0 +1,42 @@
const AbstractArchitecture = require('./abstract-architecture');
const Layer = require('../layer');
module.exports = class Hopfield extends AbstractArchitecture {
constructor(size) {
const inputLayer = new Layer(size);
const outputLayer = new Layer(size);
inputLayer.project(outputLayer, Layer.connectionType.ALL_TO_ALL);
super({
input: inputLayer,
hidden: [],
output: outputLayer
});
}
learn(patterns) {
const set = [];
for (let p in patterns)
set.push({
input: patterns[p],
output: patterns[p]
});
return this.trainer.train(set, {
iterations: 500000,
error: .00005,
rate: 1
});
}
feed(pattern) {
const output = this.activate(pattern);
pattern = [];
for (let i in output)
pattern[i] = output[i] > .5 ? 1 : 0;
return pattern;
}
}
+4
Ver Arquivo
@@ -0,0 +1,4 @@
module.exports.Hopfield = require('./hopfield');
module.exports.LSTM = require('./lstm');
module.exports.Liquid = require('./liquid');
module.exports.Perceptron = require('./perceptron');
+44
Ver Arquivo
@@ -0,0 +1,44 @@
// Liquid State Machine
const AbstractArchitecture = require('./abstract-architecture');
const Layer = require('../layer');
module.exports = class Liquid extends AbstractArchitecture {
constructor(inputs, hidden, outputs, connections, gates) {
// create layers
const inputLayer = new Layer(inputs);
const hiddenLayer = new Layer(hidden);
const outputLayer = new Layer(outputs);
// make connections and gates randomly among the neurons
const neurons = hiddenLayer.neurons();
const connectionList = [];
for (let i = 0; i < connections; i++) {
// connect two random neurons
const from = Math.random() * neurons.length | 0;
const to = Math.random() * neurons.length | 0;
var connection = neurons[from].project(neurons[to]);
connectionList.push(connection);
}
for (let j = 0; j < gates; j++) {
// pick a random gater neuron
const gater = Math.random() * neurons.length | 0;
// pick a random connection to gate
var connection = Math.random() * connectionList.length | 0;
// let the gater gate the connection
neurons[gater].gate(connectionList[connection]);
}
// connect the layers
inputLayer.project(hiddenLayer);
hiddenLayer.project(outputLayer);
// set the layers of the network
super({
input: inputLayer,
hidden: [hiddenLayer],
output: outputLayer
});
}
}
+124
Ver Arquivo
@@ -0,0 +1,124 @@
// Multilayer Long Short-Term Memory
const AbstractArchitecture = require('./abstract-architecture');
const Layer = require('../layer');
module.exports = class LSTM extends AbstractArchitecture {
constructor(...args) {
if (args.length < 3)
throw new Error(`Not enough layers. Minimum 3 expected, instead got ${args[0]}, ${args[1]}`);
const last = args.pop();
const option = {
peepholes: Layer.connectionType.ALL_TO_ALL,
hiddenToHidden: false,
outputToHidden: false,
outputToGates: false,
inputToOutput: true,
};
if (typeof last != 'number') {
var outputs = args.pop();
if (last.hasOwnProperty('peepholes'))
option.peepholes = last.peepholes;
if (last.hasOwnProperty('hiddenToHidden'))
option.hiddenToHidden = last.hiddenToHidden;
if (last.hasOwnProperty('outputToHidden'))
option.outputToHidden = last.outputToHidden;
if (last.hasOwnProperty('outputToGates'))
option.outputToGates = last.outputToGates;
if (last.hasOwnProperty('inputToOutput'))
option.inputToOutput = last.inputToOutput;
} else
var outputs = last;
const inputs = args.shift();
const layers = args;
const inputLayer = new Layer(inputs);
const hiddenLayers = [];
const outputLayer = new Layer(outputs);
let previous = null;
// generate layers
for (let layer in layers) {
// generate memory blocks (memory cell and respective gates)
const size = layers[layer];
const inputGate = new Layer(size).set({
bias: 1
});
const forgetGate = new Layer(size).set({
bias: 1
});
const memoryCell = new Layer(size);
const outputGate = new Layer(size).set({
bias: 1
});
hiddenLayers.push(inputGate);
hiddenLayers.push(forgetGate);
hiddenLayers.push(memoryCell);
hiddenLayers.push(outputGate);
// connections from input layer
const input = inputLayer.project(memoryCell);
inputLayer.project(inputGate);
inputLayer.project(forgetGate);
inputLayer.project(outputGate);
// connections from previous memory-block layer to this one
if (previous != null) {
var cell = previous.project(memoryCell);
previous.project(inputGate);
previous.project(forgetGate);
previous.project(outputGate);
}
// connections from memory cell
const output = memoryCell.project(outputLayer);
// self-connection
const self = memoryCell.project(memoryCell);
// hidden to hidden recurrent connection
if (option.hiddenToHidden)
memoryCell.project(memoryCell, Layer.connectionType.ALL_TO_ELSE);
// out to hidden recurrent connection
if (option.outputToHidden)
outputLayer.project(memoryCell);
// out to gates recurrent connection
if (option.outputToGates) {
outputLayer.project(inputGate);
outputLayer.project(outputGate);
outputLayer.project(forgetGate);
}
// peepholes
memoryCell.project(inputGate, option.peepholes);
memoryCell.project(forgetGate, option.peepholes);
memoryCell.project(outputGate, option.peepholes);
// gates
inputGate.gate(input, Layer.gateType.INPUT);
forgetGate.gate(self, Layer.gateType.ONE_TO_ONE);
outputGate.gate(output, Layer.gateType.OUTPUT);
if (previous != null)
inputGate.gate(cell, Layer.gateType.INPUT);
previous = memoryCell;
}
// input to output direct connection
if (option.inputToOutput)
inputLayer.project(outputLayer);
// set the layers of the neural network
super({
input: inputLayer,
hidden: hiddenLayers,
output: outputLayer
});
}
};
+37
Ver Arquivo
@@ -0,0 +1,37 @@
// Multilayer Perceptron
const AbstractArchitecture = require('./abstract-architecture');
const Layer = require('../layer');
module.exports = class Perceptron extends AbstractArchitecture {
constructor(...args) {
if (args.length < 3)
throw new Error(`Not enough layers. Minimum 3 expected, instead got ${args[0]}, ${args[1]}`);
const inputs = args.shift(); // first argument
const outputs = args.pop(); // last argument
const layers = args; // all the arguments in the middle
const input = new Layer(inputs);
const hidden = [];
const output = new Layer(outputs);
let previous = input;
// generate hidden layers
for (let level in layers) {
const size = layers[level];
const layer = new Layer(size);
hidden.push(layer);
previous.project(layer);
previous = layer;
}
previous.project(output);
// set layers of the neural network
super({
input,
hidden,
output
});
}
};
+102 -110
Ver Arquivo
@@ -1,32 +1,33 @@
// export
if (module) module.exports = Layer;
// import
var Neuron = require('./neuron')
, Network = require('./network')
const Neuron = require('./neuron');
/*******************************************************************************************
LAYER
*******************************************************************************************/
LAYER
*******************************************************************************************/
function Layer(size, label) {
this.size = size | 0;
this.list = [];
this.label = label || null;
this.connectedTo = [];
class Layer {
constructor(size, label) {
this.label = label || null;
this.connectedTo = [];
while (size--) {
var neuron = new Neuron();
this.list.push(neuron);
if (Array.isArray(size)) {
const neurons = size;
this.size = neurons.length;
this.list = neurons.slice(); // cloning the array
} else {
this.size = size | 0;
this.list = [];
while (size--) {
const neuron = new Neuron();
this.list.push(neuron);
}
}
}
}
Layer.prototype = {
// activates all the neurons in the layer
activate: function(input) {
activate(input) {
var activations = [];
const activations = [];
if (typeof input != 'undefined') {
if (input.length != this.size)
@@ -45,10 +46,10 @@ Layer.prototype = {
}
}
return activations;
},
}
// propagates the error on all the neurons of the layer
propagate: function(rate, target) {
propagate(rate, target) {
if (typeof target != 'undefined') {
if (target.length != this.size)
@@ -64,25 +65,23 @@ Layer.prototype = {
neuron.propagate(rate);
}
}
},
}
// projects a connection from this layer to another one
project: function(layer, type, weights) {
if (layer instanceof Network)
layer = layer.layers.input;
project(layer, type, weights) {
if (layer && layer.layers && layer.layers.input)
return this.project(layer.layers.input, type, weights);
if (layer instanceof Layer) {
if (!this.connected(layer))
return new Layer.connection(this, layer, type, weights);
} else
} else {
throw new Error("Invalid argument, you can only project connections to LAYERS and NETWORKS!");
},
}
}
// gates a connection betwenn two layers
gate: function(connection, type) {
gate(connection, type) {
if (type == Layer.gateType.INPUT) {
if (connection.to.size != this.size)
@@ -91,7 +90,7 @@ Layer.prototype = {
for (var id in connection.to.list) {
var neuron = connection.to.list[id];
var gater = this.list[id];
for (var input in neuron.connections.inputs) {
for (let input in neuron.connections.inputs) {
var gated = neuron.connections.inputs[input];
if (gated.ID in connection.connections)
gater.gate(gated);
@@ -104,7 +103,7 @@ Layer.prototype = {
for (var id in connection.from.list) {
var neuron = connection.from.list[id];
var gater = this.list[id];
for (var projected in neuron.connections.projected) {
for (let projected in neuron.connections.projected) {
var gated = neuron.connections.projected[projected];
if (gated.ID in connection.connections)
gater.gate(gated);
@@ -120,26 +119,26 @@ Layer.prototype = {
gater.gate(gated);
}
}
connection.gatedfrom.push({layer: this, type: type});
},
connection.gatedfrom.push({layer: this, type});
}
// true or false whether the whole layer is self-connected or not
selfconnected: function() {
selfconnected() {
for (var id in this.list) {
var neuron = this.list[id];
for (let id in this.list) {
const neuron = this.list[id];
if (!neuron.selfconnected())
return false;
}
return true;
},
}
// true of false whether the layer is connected to another layer (parameter) or not
connected: function(layer) {
connected(layer) {
// Check if ALL to ALL connection
var connections = 0;
for (var here in this.list) {
for (var there in layer.list) {
let connections = 0;
for (let here in this.list) {
for (let there in layer.list) {
var from = this.list[here];
var to = layer.list[there];
var connected = from.connected(to);
@@ -152,7 +151,7 @@ Layer.prototype = {
// Check if ONE to ONE connection
connections = 0;
for (var neuron in this.list) {
for (let neuron in this.list) {
var from = this.list[neuron];
var to = layer.list[neuron];
var connected = from.connected(to);
@@ -161,43 +160,35 @@ Layer.prototype = {
}
if (connections == this.size)
return Layer.connectionType.ONE_TO_ONE;
},
}
// clears all the neuorns in the layer
clear: function() {
for (var id in this.list) {
var neuron = this.list[id];
clear() {
for (let id in this.list) {
const neuron = this.list[id];
neuron.clear();
}
},
}
// resets all the neurons in the layer
reset: function() {
for (var id in this.list) {
var neuron = this.list[id];
reset() {
for (let id in this.list) {
const neuron = this.list[id];
neuron.reset();
}
},
}
// returns all the neurons in the layer (array)
neurons: function() {
neurons() {
return this.list;
},
}
// adds a neuron to the layer
add: function(neuron) {
this.neurons[neuron.ID] = neuron || new Neuron();
this.list.push(neuron);
this.size++;
},
set: function(options) {
options = options || {};
for (var i in this.list) {
var neuron = this.list[i];
set(options = {}) {
for (let i in this.list) {
const neuron = this.list[i];
if (options.label)
neuron.label = options.label + '_' + neuron.ID;
neuron.label = `${options.label}_${neuron.ID}`;
if (options.squash)
neuron.squash = options.squash;
if (options.bias)
@@ -208,52 +199,53 @@ Layer.prototype = {
}
// represents a connection from one layer to another, and keeps track of its weight and gain
Layer.connection = function LayerConnection(fromLayer, toLayer, type, weights) {
this.ID = Layer.connection.uid();
this.from = fromLayer;
this.to = toLayer;
this.selfconnection = toLayer == fromLayer;
this.type = type;
this.connections = {};
this.list = [];
this.size = 0;
this.gatedfrom = [];
Layer.connection = class LayerConnection {
constructor(fromLayer, toLayer, type, weights) {
this.ID = Layer.connection.uid();
this.from = fromLayer;
this.to = toLayer;
this.selfconnection = toLayer == fromLayer;
this.type = type;
this.connections = {};
this.list = [];
this.size = 0;
this.gatedfrom = [];
if (typeof this.type == 'undefined')
{
if (fromLayer == toLayer)
this.type = Layer.connectionType.ONE_TO_ONE;
else
this.type = Layer.connectionType.ALL_TO_ALL;
}
if (typeof this.type == 'undefined') {
if (fromLayer == toLayer)
this.type = Layer.connectionType.ONE_TO_ONE;
else
this.type = Layer.connectionType.ALL_TO_ALL;
}
if (this.type == Layer.connectionType.ALL_TO_ALL ||
if (this.type == Layer.connectionType.ALL_TO_ALL ||
this.type == Layer.connectionType.ALL_TO_ELSE) {
for (var here in this.from.list) {
for (var there in this.to.list) {
var from = this.from.list[here];
var to = this.to.list[there];
if(this.type == Layer.connectionType.ALL_TO_ELSE && from == to)
continue;
for (let here in this.from.list) {
for (let there in this.to.list) {
var from = this.from.list[here];
var to = this.to.list[there];
if (this.type == Layer.connectionType.ALL_TO_ELSE && from == to)
continue;
var connection = from.project(to, weights);
this.connections[connection.ID] = connection;
this.size = this.list.push(connection);
}
}
} else if (this.type == Layer.connectionType.ONE_TO_ONE) {
for (let neuron in this.from.list) {
var from = this.from.list[neuron];
var to = this.to.list[neuron];
var connection = from.project(to, weights);
this.connections[connection.ID] = connection;
this.size = this.list.push(connection);
}
}
} else if (this.type == Layer.connectionType.ONE_TO_ONE) {
for (var neuron in this.from.list) {
var from = this.from.list[neuron];
var to = this.to.list[neuron];
var connection = from.project(to, weights);
this.connections[connection.ID] = connection;
this.size = this.list.push(connection);
}
fromLayer.connectedTo.push(this);
}
fromLayer.connectedTo.push(this);
}
// types of connections
@@ -268,9 +260,9 @@ Layer.gateType.INPUT = "INPUT";
Layer.gateType.OUTPUT = "OUTPUT";
Layer.gateType.ONE_TO_ONE = "ONE TO ONE";
(function() {
var connections = 0;
Layer.connection.uid = function() {
return connections++;
}
})();
((() => {
let connections = 0;
Layer.connection.uid = () => connections++
}))();
module.exports = Layer;
-645
Ver Arquivo
@@ -1,645 +0,0 @@
// export
if (module) module.exports = Network;
// import
var Neuron = require('./neuron')
, Layer = require('./layer')
, Trainer = require('./trainer')
/*******************************************************************************************
NETWORK
*******************************************************************************************/
function Network(layers) {
if (typeof layers != 'undefined') {
this.layers = layers || {
input: null,
hidden: {},
output: null
};
this.optimized = null;
}
}
Network.prototype = {
// feed-forward activation of all the layers to produce an ouput
activate: function(input) {
if (this.optimized === false)
{
this.layers.input.activate(input);
for (var layer in this.layers.hidden)
this.layers.hidden[layer].activate();
return this.layers.output.activate();
}
else
{
if (this.optimized == null)
this.optimize();
return this.optimized.activate(input);
}
},
// back-propagate the error thru the network
propagate: function(rate, target) {
if (this.optimized === false)
{
this.layers.output.propagate(rate, target);
var reverse = [];
for (var layer in this.layers.hidden)
reverse.push(this.layers.hidden[layer]);
reverse.reverse();
for (var layer in reverse)
reverse[layer].propagate(rate);
}
else
{
if (this.optimized == null)
this.optimize();
this.optimized.propagate(rate, target);
}
},
// project a connection to another unit (either a network or a layer)
project: function(unit, type, weights) {
if (this.optimized)
this.optimized.reset();
if (unit instanceof Network)
return this.layers.output.project(unit.layers.input, type, weights);
if (unit instanceof Layer)
return this.layers.output.project(unit, type, weights);
throw new Error("Invalid argument, you can only project connections to LAYERS and NETWORKS!");
},
// let this network gate a connection
gate: function(connection, type) {
if (this.optimized)
this.optimized.reset();
this.layers.output.gate(connection, type);
},
// clear all elegibility traces and extended elegibility traces (the network forgets its context, but not what was trained)
clear: function() {
this.restore();
var inputLayer = this.layers.input,
outputLayer = this.layers.output;
inputLayer.clear();
for (var layer in this.layers.hidden) {
var hiddenLayer = this.layers.hidden[layer];
hiddenLayer.clear();
}
outputLayer.clear();
if (this.optimized)
this.optimized.reset();
},
// reset all weights and clear all traces (ends up like a new network)
reset: function() {
this.restore();
var inputLayer = this.layers.input,
outputLayer = this.layers.output;
inputLayer.reset();
for (var layer in this.layers.hidden) {
var hiddenLayer = this.layers.hidden[layer];
hiddenLayer.reset();
}
outputLayer.reset();
if (this.optimized)
this.optimized.reset();
},
// hardcodes the behaviour of the whole network into a single optimized function
optimize: function() {
var that = this;
var optimized = {};
var neurons = this.neurons();
for (var i in neurons) {
var neuron = neurons[i].neuron;
var layer = neurons[i].layer;
while (neuron.neuron)
neuron = neuron.neuron;
optimized = neuron.optimize(optimized, layer);
}
for (var i in optimized.propagation_sentences)
optimized.propagation_sentences[i].reverse();
optimized.propagation_sentences.reverse();
var hardcode = "";
hardcode += "var F = Float64Array ? new Float64Array(" + optimized.memory +
") : []; ";
for (var i in optimized.variables)
hardcode += "F[" + optimized.variables[i].id + "] = " + (optimized.variables[
i].value || 0) + "; ";
hardcode += "var activate = function(input){\n";
for (var i in optimized.inputs)
hardcode += "F[" + optimized.inputs[i] + "] = input[" + i + "]; ";
for (var currentLayer in optimized.activation_sentences) {
if (optimized.activation_sentences[currentLayer].length > 0) {
for (var currentNeuron in optimized.activation_sentences[currentLayer]) {
hardcode += optimized.activation_sentences[currentLayer][currentNeuron].join(" ");
hardcode += optimized.trace_sentences[currentLayer][currentNeuron].join(" ");
}
}
}
hardcode += " var output = []; "
for (var i in optimized.outputs)
hardcode += "output[" + i + "] = F[" + optimized.outputs[i] + "]; ";
hardcode += "return output; }; "
hardcode += "var propagate = function(rate, target){\n";
hardcode += "F[" + optimized.variables.rate.id + "] = rate; ";
for (var i in optimized.targets)
hardcode += "F[" + optimized.targets[i] + "] = target[" + i + "]; ";
for (var currentLayer in optimized.propagation_sentences)
for (var currentNeuron in optimized.propagation_sentences[currentLayer])
hardcode += optimized.propagation_sentences[currentLayer][currentNeuron].join(" ") + " ";
hardcode += " };\n";
hardcode +=
"var ownership = function(memoryBuffer){\nF = memoryBuffer;\nthis.memory = F;\n};\n";
hardcode +=
"return {\nmemory: F,\nactivate: activate,\npropagate: propagate,\nownership: ownership\n};";
hardcode = hardcode.split(";").join(";\n");
var constructor = new Function(hardcode);
var network = constructor();
network.data = {
variables: optimized.variables,
activate: optimized.activation_sentences,
propagate: optimized.propagation_sentences,
trace: optimized.trace_sentences,
inputs: optimized.inputs,
outputs: optimized.outputs,
check_activation: this.activate,
check_propagation: this.propagate
}
network.reset = function() {
if (that.optimized) {
that.optimized = null;
that.activate = network.data.check_activation;
that.propagate = network.data.check_propagation;
}
}
this.optimized = network;
this.activate = network.activate;
this.propagate = network.propagate;
},
// restores all the values from the optimized network the their respective objects in order to manipulate the network
restore: function() {
if (!this.optimized)
return;
var optimized = this.optimized;
var getValue = function() {
var args = Array.prototype.slice.call(arguments);
var unit = args.shift();
var prop = args.pop();
var id = prop + '_';
for (var property in args)
id += args[property] + '_';
id += unit.ID;
var memory = optimized.memory;
var variables = optimized.data.variables;
if (id in variables)
return memory[variables[id].id];
return 0;
}
var list = this.neurons();
// link id's to positions in the array
var ids = {};
for (var i in list) {
var neuron = list[i].neuron;
while (neuron.neuron)
neuron = neuron.neuron;
neuron.state = getValue(neuron, 'state');
neuron.old = getValue(neuron, 'old');
neuron.activation = getValue(neuron, 'activation');
neuron.bias = getValue(neuron, 'bias');
for (var input in neuron.trace.elegibility)
neuron.trace.elegibility[input] = getValue(neuron, 'trace',
'elegibility', input);
for (var gated in neuron.trace.extended)
for (var input in neuron.trace.extended[gated])
neuron.trace.extended[gated][input] = getValue(neuron, 'trace',
'extended', gated, input);
}
// get connections
for (var i in list) {
var neuron = list[i].neuron;
while (neuron.neuron)
neuron = neuron.neuron;
for (var j in neuron.connections.projected) {
var connection = neuron.connections.projected[j];
connection.weight = getValue(connection, 'weight');
connection.gain = getValue(connection, 'gain');
}
}
},
// returns all the neurons in the network
neurons: function() {
var neurons = [];
var inputLayer = this.layers.input.neurons(),
outputLayer = this.layers.output.neurons();
for (var neuron in inputLayer)
neurons.push({
neuron: inputLayer[neuron],
layer: 'input'
});
for (var layer in this.layers.hidden) {
var hiddenLayer = this.layers.hidden[layer].neurons();
for (var neuron in hiddenLayer)
neurons.push({
neuron: hiddenLayer[neuron],
layer: layer
});
}
for (var neuron in outputLayer)
neurons.push({
neuron: outputLayer[neuron],
layer: 'output'
});
return neurons;
},
// returns number of inputs of the network
inputs: function() {
return this.layers.input.size;
},
// returns number of outputs of hte network
outputs: function() {
return this.layers.output.size;
},
// sets the layers of the network
set: function(layers) {
this.layers = layers;
if (this.optimized)
this.optimized.reset();
},
setOptimize: function(bool){
this.restore();
if (this.optimized)
this.optimized.reset();
this.optimized = bool? null : false;
},
// returns a json that represents all the neurons and connections of the network
toJSON: function(ignoreTraces) {
this.restore();
var list = this.neurons();
var neurons = [];
var connections = [];
// link id's to positions in the array
var ids = {};
for (var i in list) {
var neuron = list[i].neuron;
while (neuron.neuron)
neuron = neuron.neuron;
ids[neuron.ID] = i;
var copy = {
trace: {
elegibility: {},
extended: {}
},
state: neuron.state,
old: neuron.old,
activation: neuron.activation,
bias: neuron.bias,
layer: list[i].layer
};
copy.squash = neuron.squash == Neuron.squash.LOGISTIC ? "LOGISTIC" :
neuron.squash == Neuron.squash.TANH ? "TANH" :
neuron.squash == Neuron.squash.IDENTITY ? "IDENTITY" :
neuron.squash == Neuron.squash.HLIM ? "HLIM" :
null;
neurons.push(copy);
}
// get connections
for (var i in list) {
var neuron = list[i].neuron;
while (neuron.neuron)
neuron = neuron.neuron;
for (var j in neuron.connections.projected) {
var connection = neuron.connections.projected[j];
connections.push({
from: ids[connection.from.ID],
to: ids[connection.to.ID],
weight: connection.weight,
gater: connection.gater ? ids[connection.gater.ID] : null,
});
}
if (neuron.selfconnected())
connections.push({
from: ids[neuron.ID],
to: ids[neuron.ID],
weight: neuron.selfconnection.weight,
gater: neuron.selfconnection.gater ? ids[neuron.selfconnection.gater.ID] : null,
});
}
return {
neurons: neurons,
connections: connections
}
},
// export the topology into dot language which can be visualized as graphs using dot
/* example: ... console.log(net.toDotLang());
$ node example.js > example.dot
$ dot example.dot -Tpng > out.png
*/
toDot: function(edgeConnection) {
if (! typeof edgeConnection)
edgeConnection = false;
var code = "digraph nn {\n rankdir = BT\n";
var layers = [this.layers.input].concat(this.layers.hidden, this.layers.output);
for (var layer in layers) {
for (var to in layers[layer].connectedTo) { // projections
var connection = layers[layer].connectedTo[to];
var layerTo = connection.to;
var size = connection.size;
var layerID = layers.indexOf(layers[layer]);
var layerToID = layers.indexOf(layerTo);
/* http://stackoverflow.com/questions/26845540/connect-edges-with-graph-dot
* DOT does not support edge-to-edge connections
* This workaround produces somewhat weird graphs ...
*/
if ( edgeConnection) {
if (connection.gatedfrom.length) {
var fakeNode = "fake" + layerID + "_" + layerToID;
code += " " + fakeNode +
" [label = \"\", shape = point, width = 0.01, height = 0.01]\n";
code += " " + layerID + " -> " + fakeNode + " [label = " + size + ", arrowhead = none]\n";
code += " " + fakeNode + " -> " + layerToID + "\n";
} else
code += " " + layerID + " -> " + layerToID + " [label = " + size + "]\n";
for (var from in connection.gatedfrom) { // gatings
var layerfrom = connection.gatedfrom[from].layer;
var layerfromID = layers.indexOf(layerfrom);
code += " " + layerfromID + " -> " + fakeNode + " [color = blue]\n";
}
} else {
code += " " + layerID + " -> " + layerToID + " [label = " + size + "]\n";
for (var from in connection.gatedfrom) { // gatings
var layerfrom = connection.gatedfrom[from].layer;
var layerfromID = layers.indexOf(layerfrom);
code += " " + layerfromID + " -> " + layerToID + " [color = blue]\n";
}
}
}
}
code += "}\n";
return {
code: code,
link: "https://chart.googleapis.com/chart?chl=" + escape(code.replace("/ /g", "+")) + "&cht=gv"
}
},
// returns a function that works as the activation of the network and can be used without depending on the library
standalone: function() {
if (!this.optimized)
this.optimize();
var data = this.optimized.data;
// build activation function
var activation = "function (input) {\n";
// build inputs
for (var i in data.inputs)
activation += "F[" + data.inputs[i] + "] = input[" + i + "];\n";
// build network activation
for (var neuron in data.activate) { // shouldn't this be layer?
for (var sentence in data.activate[neuron])
activation += data.activate[neuron][sentence].join('') + "\n";
}
// build outputs
activation += "var output = [];\n";
for (var i in data.outputs)
activation += "output[" + i + "] = F[" + data.outputs[i] + "];\n";
activation += "return output;\n}";
// reference all the positions in memory
var memory = activation.match(/F\[(\d+)\]/g);
var dimension = 0;
var ids = {};
for (var address in memory) {
var tmp = memory[address].match(/\d+/)[0];
if (!(tmp in ids)) {
ids[tmp] = dimension++;
}
}
var hardcode = "F = {\n";
for (var i in ids)
hardcode += ids[i] + ": " + this.optimized.memory[i] + ",\n";
hardcode = hardcode.substring(0, hardcode.length - 2) + "\n};\n";
hardcode = "var run = " + activation.replace(/F\[(\d+)]/g, function(
index) {
return 'F[' + ids[index.match(/\d+/)[0]] + ']'
}).replace("{\n", "{\n" + hardcode + "") + ";\n";
hardcode += "return run";
// return standalone function
return new Function(hardcode)();
},
// Return a HTML5 WebWorker specialized on training the network stored in `memory`.
// Train based on the given dataSet and options.
// The worker returns the updated `memory` when done.
worker: function(memory, set, options) {
// Copy the options and set defaults (options might be different for each worker)
var workerOptions = {};
if(options) workerOptions = options;
workerOptions.rate = options.rate || .2;
workerOptions.iterations = options.iterations || 100000;
workerOptions.error = options.error || .005;
workerOptions.cost = options.cost || null;
workerOptions.crossValidate = options.crossValidate || null;
// Cost function might be different for each worker
costFunction = "var cost = " + (options && options.cost || this.cost || Trainer.cost.MSE) + ";\n";
var workerFunction = Network.getWorkerSharedFunctions();
workerFunction = workerFunction.replace(/var cost = options && options\.cost \|\| this\.cost \|\| Trainer\.cost\.MSE;/g, costFunction);
// Set what we do when training is finished
workerFunction = workerFunction.replace('return results;',
'postMessage({action: "done", message: results, memoryBuffer: F}, [F.buffer]);');
// Replace log with postmessage
workerFunction = workerFunction.replace("console.log('iterations', iterations, 'error', error, 'rate', currentRate)",
"postMessage({action: 'log', message: {\n" +
"iterations: iterations,\n" +
"error: error,\n" +
"rate: currentRate\n" +
"}\n" +
"})");
// Replace schedule with postmessage
workerFunction = workerFunction.replace("abort = this.schedule.do({ error: error, iterations: iterations, rate: currentRate })",
"postMessage({action: 'schedule', message: {\n" +
"iterations: iterations,\n" +
"error: error,\n" +
"rate: currentRate\n" +
"}\n" +
"})");
if (!this.optimized)
this.optimize();
var hardcode = "var inputs = " + this.optimized.data.inputs.length + ";\n";
hardcode += "var outputs = " + this.optimized.data.outputs.length + ";\n";
hardcode += "var F = new Float64Array([" + this.optimized.memory.toString() + "]);\n";
hardcode += "var activate = " + this.optimized.activate.toString() + ";\n";
hardcode += "var propagate = " + this.optimized.propagate.toString() + ";\n";
hardcode +=
"onmessage = function(e) {\n" +
"if (e.data.action == 'startTraining') {\n" +
"train(" + JSON.stringify(set) + "," + JSON.stringify(workerOptions) + ");\n" +
"}\n" +
"}";
var workerSourceCode = workerFunction + '\n' + hardcode;
var blob = new Blob([workerSourceCode]);
var blobURL = window.URL.createObjectURL(blob);
return new Worker(blobURL);
},
// returns a copy of the network
clone: function() {
return Network.fromJSON(this.toJSON());
}
};
/**
* Creates a static String to store the source code of the functions
* that are identical for all the workers (train, _trainSet, test)
*
* @return {String} Source code that can train a network inside a worker.
* @static
*/
Network.getWorkerSharedFunctions = function() {
// If we already computed the source code for the shared functions
if(typeof Network._SHARED_WORKER_FUNCTIONS !== 'undefined')
return Network._SHARED_WORKER_FUNCTIONS;
// Otherwise compute and return the source code
// We compute them by simply copying the source code of the train, _trainSet and test functions
// using the .toString() method
// Load and name the train function
var train_f = Trainer.prototype.train.toString();
train_f = train_f.replace('function (set', 'function train(set') + '\n';
// Load and name the _trainSet function
var _trainSet_f = Trainer.prototype._trainSet.toString().replace(/this.network./g, '');
_trainSet_f = _trainSet_f.replace('function (set', 'function _trainSet(set') + '\n';
_trainSet_f = _trainSet_f.replace('this.crossValidate', 'crossValidate');
_trainSet_f = _trainSet_f.replace('crossValidate = true', 'crossValidate = { }');
// Load and name the test function
var test_f = Trainer.prototype.test.toString().replace(/this.network./g, '');
test_f = test_f.replace('function (set', 'function test(set') + '\n';
return Network._SHARED_WORKER_FUNCTIONS = train_f + _trainSet_f + test_f;
};
// rebuild a network that has been stored in a json using the method toJSON()
Network.fromJSON = function(json) {
var neurons = [];
var layers = {
input: new Layer(),
hidden: [],
output: new Layer()
};
for (var i in json.neurons) {
var config = json.neurons[i];
var neuron = new Neuron();
neuron.trace.elegibility = {};
neuron.trace.extended = {};
neuron.state = config.state;
neuron.old = config.old;
neuron.activation = config.activation;
neuron.bias = config.bias;
neuron.squash = config.squash in Neuron.squash ? Neuron.squash[config.squash] : Neuron.squash.LOGISTIC;
neurons.push(neuron);
if (config.layer == 'input')
layers.input.add(neuron);
else if (config.layer == 'output')
layers.output.add(neuron);
else {
if (typeof layers.hidden[config.layer] == 'undefined')
layers.hidden[config.layer] = new Layer();
layers.hidden[config.layer].add(neuron);
}
}
for (var i in json.connections) {
var config = json.connections[i];
var from = neurons[config.from];
var to = neurons[config.to];
var weight = config.weight;
var gater = neurons[config.gater];
var connection = from.project(to, weight);
if (gater)
gater.gate(connection);
}
return new Network(layers);
};
+18
Ver Arquivo
@@ -0,0 +1,18 @@
const Network = require('./network');
class SynapticNetwork extends Network {
}
module.exports = SynapticNetwork;
// features enabling
if (global.Worker) {
// this code should be kept on
const WorkerProxyNetwork = require('./worker-proxy-network');
// Return a HTML5 WebWorker specialized on training the network stored in `memory`.
// Train based on the given dataSet and options.
// The worker returns the updated `memory` when done.
SynapticNetwork.prototype.worker = function () {
return WorkerProxyNetwork.fromJSON(this.toJSON());
}
}
+537
Ver Arquivo
@@ -0,0 +1,537 @@
const Layer = require('../layer');
const Neuron = require('../neuron');
/*******************************************************************************************
NETWORK
*******************************************************************************************/
class Network {
constructor(layers) {
this.layers = layers;
this.optimized = null;
}
// feed-forward activation of all the layers to produce an ouput
activate(input) {
if (this.optimized === false) {
this.layers.input.activate(input);
for (let layer in this.layers.hidden)
this.layers.hidden[layer].activate();
return this.layers.output.activate();
}
else {
if (this.optimized == null)
this.optimize();
return this.optimized.activate(input);
}
}
// back-propagate the error thru the network
propagate(rate, target) {
if (this.optimized === false) {
this.layers.output.propagate(rate, target);
const reverse = [];
for (var layer in this.layers.hidden)
reverse.push(this.layers.hidden[layer]);
reverse.reverse();
for (var layer in reverse)
reverse[layer].propagate(rate);
}
else {
if (this.optimized == null)
this.optimize();
this.optimized.propagate(rate, target);
}
}
// project a connection to another unit (either a network or a layer)
project(unit, type, weights) {
if (this.optimized)
this.optimized.reset();
if (unit instanceof Network)
return this.layers.output.project(unit.layers.input, type, weights);
if (unit instanceof Layer)
return this.layers.output.project(unit, type, weights);
throw new Error("Invalid argument, you can only project connections to LAYERS and NETWORKS!");
}
// let this network gate a connection
gate(connection, type) {
if (this.optimized)
this.optimized.reset();
this.layers.output.gate(connection, type);
}
// clear all elegibility traces and extended elegibility traces (the network forgets its context, but not what was trained)
clear() {
this.restore();
const inputLayer = this.layers.input, outputLayer = this.layers.output;
inputLayer.clear();
for (let layer in this.layers.hidden) {
const hiddenLayer = this.layers.hidden[layer];
hiddenLayer.clear();
}
outputLayer.clear();
if (this.optimized)
this.optimized.reset();
}
// reset all weights and clear all traces (ends up like a new network)
reset() {
this.restore();
const inputLayer = this.layers.input, outputLayer = this.layers.output;
inputLayer.reset();
for (let layer in this.layers.hidden) {
const hiddenLayer = this.layers.hidden[layer];
hiddenLayer.reset();
}
outputLayer.reset();
if (this.optimized)
this.optimized.reset();
}
// hardcodes the behaviour of the whole network into a single optimized function
optimize() {
const that = this;
let optimized = {};
const neurons = this.neurons();
for (var i in neurons) {
let neuron = neurons[i].neuron;
const layer = neurons[i].layer;
while (neuron.neuron)
neuron = neuron.neuron;
optimized = neuron.optimize(optimized, layer);
}
for (var i in optimized.propagation_sentences)
optimized.propagation_sentences[i].reverse();
optimized.propagation_sentences.reverse();
let hardcode = "";
hardcode += `var F = Float64Array ? new Float64Array(${optimized.memory}) : []; `;
for (var i in optimized.variables)
hardcode += `F[${optimized.variables[i].id}] = ${optimized.variables[
i].value || 0}; `;
hardcode += "var activate = function(input){\n";
for (var i in optimized.inputs)
hardcode += `F[${optimized.inputs[i]}] = input[${i}]; `;
for (var currentLayer in optimized.activation_sentences) {
if (optimized.activation_sentences[currentLayer].length > 0) {
for (var currentNeuron in optimized.activation_sentences[currentLayer]) {
hardcode += optimized.activation_sentences[currentLayer][currentNeuron].join(" ");
hardcode += optimized.trace_sentences[currentLayer][currentNeuron].join(" ");
}
}
}
hardcode += " var output = []; "
for (var i in optimized.outputs)
hardcode += `output[${i}] = F[${optimized.outputs[i]}]; `;
hardcode += "return output; }; "
hardcode += "var propagate = function(rate, target){\n";
hardcode += `F[${optimized.variables.rate.id}] = rate; `;
for (var i in optimized.targets)
hardcode += `F[${optimized.targets[i]}] = target[${i}]; `;
for (var currentLayer in optimized.propagation_sentences)
for (var currentNeuron in optimized.propagation_sentences[currentLayer])
hardcode += `${optimized.propagation_sentences[currentLayer][currentNeuron].join(" ")} `;
hardcode += " };\n";
hardcode +=
"var ownership = function(memoryBuffer){\nF = memoryBuffer;\nthis.memory = F;\n};\n";
hardcode +=
"return {\nmemory: F,\nactivate: activate,\npropagate: propagate,\nownership: ownership\n};";
hardcode = hardcode.split(";").join(";\n");
const constructor = new Function(hardcode);
const network = constructor();
network.data = {
variables: optimized.variables,
activate: optimized.activation_sentences,
propagate: optimized.propagation_sentences,
trace: optimized.trace_sentences,
inputs: optimized.inputs,
outputs: optimized.outputs,
check_activation: this.activate,
check_propagation: this.propagate
}
network.reset = () => {
if (that.optimized) {
that.optimized = null;
that.activate = network.data.check_activation;
that.propagate = network.data.check_propagation;
}
}
this.optimized = network;
this.activate = network.activate;
this.propagate = network.propagate;
}
// restores all the values from the optimized network the their respective objects in order to manipulate the network
restore() {
if (!this.optimized)
return;
const optimized = this.optimized;
const getValue = function () {
const args = Array.prototype.slice.call(arguments);
const unit = args.shift();
const prop = args.pop();
let id = `${prop}_`;
for (let property in args)
id += `${args[property]}_`;
id += unit.ID;
const memory = optimized.memory;
const variables = optimized.data.variables;
if (id in variables)
return memory[variables[id].id];
return 0;
};
const list = this.neurons();
// link id's to positions in the array
const ids = {};
for (var i in list) {
var neuron = list[i].neuron;
while (neuron.neuron)
neuron = neuron.neuron;
neuron.state = getValue(neuron, 'state');
neuron.old = getValue(neuron, 'old');
neuron.activation = getValue(neuron, 'activation');
neuron.bias = getValue(neuron, 'bias');
for (var input in neuron.trace.elegibility)
neuron.trace.elegibility[input] = getValue(neuron, 'trace',
'elegibility', input);
for (let gated in neuron.trace.extended)
for (var input in neuron.trace.extended[gated])
neuron.trace.extended[gated][input] = getValue(neuron, 'trace',
'extended', gated, input);
}
// get connections
for (var i in list) {
var neuron = list[i].neuron;
while (neuron.neuron)
neuron = neuron.neuron;
for (let j in neuron.connections.projected) {
const connection = neuron.connections.projected[j];
connection.weight = getValue(connection, 'weight');
connection.gain = getValue(connection, 'gain');
}
}
}
// returns all the neurons in the network
neurons() {
const neurons = [];
const inputLayer = this.layers.input.neurons(), outputLayer = this.layers.output.neurons();
for (var neuron in inputLayer)
neurons.push({
neuron: inputLayer[neuron],
layer: 'input'
});
for (let layer in this.layers.hidden) {
const hiddenLayer = this.layers.hidden[layer].neurons();
for (var neuron in hiddenLayer)
neurons.push({
neuron: hiddenLayer[neuron],
layer
});
}
for (var neuron in outputLayer)
neurons.push({
neuron: outputLayer[neuron],
layer: 'output'
});
return neurons;
}
// returns number of inputs of the network
inputs() {
return this.layers.input.size;
}
// returns number of outputs of hte network
outputs() {
return this.layers.output.size;
}
// sets the layers of the network
set(layers) {
console.warn('This method is deprecated! Use super(layers) in constructor instead');
this.layers = layers;
if (this.optimized)
this.optimized.reset();
}
setOptimize(bool) {
this.restore();
if (this.optimized)
this.optimized.reset();
this.optimized = bool ? null : false;
}
// returns a json that represents all the neurons and connections of the network
toJSON(ignoreTraces) {
this.restore();
const list = this.neurons();
const neurons = [];
const connections = [];
// link id's to positions in the array
const ids = {};
for (var i in list) {
var neuron = list[i].neuron;
while (neuron.neuron)
neuron = neuron.neuron;
ids[neuron.ID] = i;
const copy = {
trace: {
elegibility: {},
extended: {}
},
state: neuron.state,
old: neuron.old,
activation: neuron.activation,
bias: neuron.bias,
layer: list[i].layer
};
copy.squash = neuron.squash.squashType || neuron.squash.toString();
neurons.push(copy);
}
// get connections
for (var i in list) {
var neuron = list[i].neuron;
while (neuron.neuron)
neuron = neuron.neuron;
for (let j in neuron.connections.projected) {
const connection = neuron.connections.projected[j];
connections.push({
from: ids[connection.from.ID],
to: ids[connection.to.ID],
weight: connection.weight,
gater: connection.gater ? ids[connection.gater.ID] : null,
});
}
if (neuron.selfconnected())
connections.push({
from: ids[neuron.ID],
to: ids[neuron.ID],
weight: neuron.selfconnection.weight,
gater: neuron.selfconnection.gater ? ids[neuron.selfconnection.gater.ID] : null,
});
}
return {
neurons,
connections
}
}
// export the topology into dot language which can be visualized as graphs using dot
/* example: ... console.log(net.toDotLang());
$ node example.js > example.dot
$ dot example.dot -Tpng > out.png
*/
toDot(edgeConnection) {
if (!typeof edgeConnection)
edgeConnection = false;
let code = "digraph nn {\n rankdir = BT\n";
const layers = [this.layers.input].concat(this.layers.hidden, this.layers.output);
for (let layer in layers) {
for (let to in layers[layer].connectedTo) { // projections
const connection = layers[layer].connectedTo[to];
const layerTo = connection.to;
const size = connection.size;
const layerID = layers.indexOf(layers[layer]);
const layerToID = layers.indexOf(layerTo);
/* http://stackoverflow.com/questions/26845540/connect-edges-with-graph-dot
* DOT does not support edge-to-edge connections
* This workaround produces somewhat weird graphs ...
*/
if (edgeConnection) {
if (connection.gatedfrom.length) {
var fakeNode = `fake${layerID}_${layerToID}`;
code += ` ${fakeNode} [label = "", shape = point, width = 0.01, height = 0.01]\n`;
code += ` ${layerID} -> ${fakeNode} [label = ${size}, arrowhead = none]\n`;
code += ` ${fakeNode} -> ${layerToID}\n`;
} else
code += ` ${layerID} -> ${layerToID} [label = ${size}]\n`;
for (var from in connection.gatedfrom) { // gatings
var layerfrom = connection.gatedfrom[from].layer;
var layerfromID = layers.indexOf(layerfrom);
code += ` ${layerfromID} -> ${fakeNode} [color = blue]\n`;
}
} else {
code += ` ${layerID} -> ${layerToID} [label = ${size}]\n`;
for (var from in connection.gatedfrom) { // gatings
var layerfrom = connection.gatedfrom[from].layer;
var layerfromID = layers.indexOf(layerfrom);
code += ` ${layerfromID} -> ${layerToID} [color = blue]\n`;
}
}
}
}
code += "}\n";
return {
code,
link: `https://chart.googleapis.com/chart?chl=${escape(code.replace("/ /g", "+"))}&cht=gv`
}
}
// returns a function that works as the activation of the network and can be used without depending on the library
standalone() {
if (!this.optimized)
this.optimize();
const data = this.optimized.data;
// build activation function
let activation = "function (input) {\n";
// build inputs
for (var i in data.inputs)
activation += `F[${data.inputs[i]}] = input[${i}];\n`;
// build network activation
for (let neuron in data.activate) { // shouldn't this be layer?
for (let sentence in data.activate[neuron])
activation += `${data.activate[neuron][sentence].join('')}\n`;
}
// build outputs
activation += "var output = [];\n";
for (var i in data.outputs)
activation += `output[${i}] = F[${data.outputs[i]}];\n`;
activation += "return output;\n}";
// reference all the positions in memory
const memory = activation.match(/F\[(\d+)\]/g);
let dimension = 0;
const ids = {};
for (let address in memory) {
const tmp = memory[address].match(/\d+/)[0];
if (!(tmp in ids)) {
ids[tmp] = dimension++;
}
}
let hardcode = "F = {\n";
for (var i in ids)
hardcode += `${ids[i]}: ${this.optimized.memory[i]},\n`;
hardcode = `${hardcode.substring(0, hardcode.length - 2)}\n};\n`;
hardcode = `var run = ${activation.replace(/F\[(\d+)]/g, index => 'F[' + ids[index.match(/\d+/)[0]] + ']').replace("{\n", "{\n" + hardcode + "")};\n`;
hardcode += "return run";
// return standalone function
return new Function(hardcode)();
}
// returns a copy of the network
clone() {
return this.constructor.fromJSON(this.toJSON());
}
}
// rebuild a network that has been stored in a json using the method toJSON()
Network.fromJSON = function (json) {
const neurons = [];
const layers = {
input: [],
hidden: [],
output: []
};
for (var i in json.neurons) {
var config = json.neurons[i];
const neuron = new Neuron();
neuron.trace.elegibility = {};
neuron.trace.extended = {};
neuron.state = config.state;
neuron.old = config.old;
neuron.activation = config.activation;
neuron.bias = config.bias;
if (Neuron.squash[config.squash] instanceof Function && Neuron.squash[config.squash].length === 1) {
neuron.squash = Neuron.squash[config.squash];
}
if (!neuron.squash) {
neuron.squash = Neuron.squash.LOGISTIC;
}
neurons.push(neuron);
if (config.layer == 'input')
layers.input.push(neuron);
else if (config.layer == 'output')
layers.output.push(neuron);
else {
if (typeof layers.hidden[config.layer] == 'undefined')
layers.hidden[config.layer] = [];
layers.hidden[config.layer].push(neuron);
}
}
for (var i in json.connections) {
var config = json.connections[i];
const from = neurons[config.from];
const to = neurons[config.to];
const weight = config.weight;
const gater = neurons[config.gater];
const connection = from.project(to, weight);
if (gater)
gater.gate(connection);
}
const instance = new this.NetworkClass({
input: new Layer(layers.input),
hidden: layers.hidden.map(layer => new Layer(layer)),
output: new Layer(layers.output)
});
Object.setPrototypeOf(instance, instance.__proto__ = this.prototype);
return instance;
};
Network.NetworkClass = Network;
module.exports = Network;
@@ -0,0 +1,3 @@
module.exports.GET = '%get';
module.exports.SET = '%set';
module.exports.FROM_JSON = '%fromJSON';
+38
Ver Arquivo
@@ -0,0 +1,38 @@
var WorkerNetwork = require('./network');
var constants = require('./worker-communication-constants');
onmessage = function (e) {
const {method, args, callbackId} = e.data;
try {
var response = callMethod(method, args);
postMessage({response: [null, response], callbackId})
} catch (e) {
postMessage({response: [e.message], callbackId})
}
};
const callMethod = (function() {
var instance;
return function callMethod(method, args) {
var result;
switch (method) {
case constants.GET:
result = instance[args[0]];
break;
case constants.SET:
instance[method][args[0]] = args[1];
break;
case constants.FROM_JSON:
instance = WorkerNetwork.fromJSON(args[0]);
break;
default:
result = instance[method](...args);
break;
}
return result;
}
})();
+32
Ver Arquivo
@@ -0,0 +1,32 @@
var WorkerNetworkWorker = require('worker?inline!./worker-index.worker.js');
module.exports = class WorkerNetworkProxyAPI {
constructor() {
var worker = this.worker = new WorkerNetworkWorker();
this.callbacks = {};
this.nextCallbackId = 1;
worker.onmessage = ({data: {response: [error, data] = [], callbackId} = {}}) =>
this.callbacks[callbackId](error, data);
}
subscribe(method, args, callback) {
const callbackId = this.nextCallbackId++;
this.callbacks[callbackId] = callback;
this.worker.postMessage({method, args, callbackId});
return callbackId;
}
unsubscribe(callbackId) {
return delete this.callbacks[callbackId];
}
callMethod(method, args) {
return new Promise((resolve, reject) => {
const callbackId = this.subscribe(method, args, (err, data) => {
this.unsubscribe(callbackId);
err ? reject(err) : resolve(data);
});
})
};
};
+73
Ver Arquivo
@@ -0,0 +1,73 @@
var Network = require('./network');
var constants = require('./worker-communication-constants');
var WorkerNetworkProxyAPI = require('./worker-network-proxy-api');
const workerNetworkConstructorInternalKey = {};
const workerNetworkProxyKey = '_workerNetworkProxyAPI';
const getGetterName = key => `get${key[0].toUpperCase()}${key.slice(1)}`;
const getSetterName = key => `set${key[0].toUpperCase()}${key.slice(1)}`;
class WorkerProxyNetwork extends Network {
constructor(key, workerNetworkProxyAPI) {
if (key !== workerNetworkConstructorInternalKey)
throw new Error(
`cannot initiate ${this.name} directly as it is constructed in an async manner. ` +
`Instead of that use ${this.name}.fromJSON() or ${this.name}.construct`);
super();
this[workerNetworkProxyKey] = workerNetworkProxyAPI;
}
}
module.exports = WorkerProxyNetwork;
WorkerProxyNetwork.construct = function (proxy) { return new this(workerNetworkConstructorInternalKey, proxy) };
WorkerProxyNetwork.fromJSON = function (jsonAlikeObject, proxyAPI = new WorkerNetworkProxyAPI()) {
return proxyAPI.callMethod(constants.FROM_JSON, [jsonAlikeObject])
.then(() => this.construct(proxyAPI))
};
const prototypeKeys = Object.getOwnPropertyNames(Network.prototype);
const methodKeys = prototypeKeys.filter(key => (Network.prototype[key] instanceof Function));
const propertyKeys = prototypeKeys.filter(key => !(Network.prototype[key] instanceof Function));
const createDef = (key, value, length) => {
Object.defineProperty(value, 'name', {value: key});
if (length !== undefined)
Object.defineProperty(value, 'length', {value: length});
return {key, descriptor: {value}};
};
const definitions = [
...methodKeys
.map(key =>
createDef(key,
function (...args) { return this[workerNetworkProxyKey].callMethod(key, args) }, Network.prototype[key].length)),
...propertyKeys
.map(key => ({
key,
descriptor: {
get() {
throw new Error(`cannot get property ${this.constructor.name}#${key} directly as actual data reflected is stored in a worker. `
+ `To get it use method ${this.constructor.name}#${getGetterName(key)} with a signature () => Promise<value>`)
},
set() {
throw new Error(`cannot set property ${this.constructor.name}#${key} directly as actual data reflected is stored in a worker. `
+ `To set it use method ${this.constructor.name}#${getSetterName(key)} with a signature (value) => Promise<>`)
}
}
})),
...propertyKeys
.map(key => createDef(getGetterName(key),
function () { return this[workerNetworkProxyKey].callMethod(constants.GET, [key]) })),
...propertyKeys
.map(key => createDef(getSetterName(key),
function (value) { return this[workerNetworkProxyKey].callMethod(constants.SET, [key, value]) }))
];
for (const definition of definitions)
Object.defineProperty(WorkerProxyNetwork.prototype, definition.key, definition.descriptor);
+179 -190
Ver Arquivo
@@ -1,41 +1,37 @@
// export
if (module) module.exports = Neuron;
/******************************************************************************************
NEURON
*******************************************************************************************/
NEURON
*******************************************************************************************/
function Neuron() {
this.ID = Neuron.uid();
this.label = null;
this.connections = {
inputs: {},
projected: {},
gated: {}
};
this.error = {
responsibility: 0,
projected: 0,
gated: 0
};
this.trace = {
elegibility: {},
extended: {},
influences: {}
};
this.state = 0;
this.old = 0;
this.activation = 0;
this.selfconnection = new Neuron.connection(this, this, 0); // weight = 0 -> not connected
this.squash = Neuron.squash.LOGISTIC;
this.neighboors = {};
this.bias = Math.random() * .2 - .1;
}
Neuron.prototype = {
class Neuron {
constructor() {
this.ID = Neuron.uid();
this.label = null;
this.connections = {
inputs: {},
projected: {},
gated: {}
};
this.error = {
responsibility: 0,
projected: 0,
gated: 0
};
this.trace = {
elegibility: {},
extended: {},
influences: {}
};
this.state = 0;
this.old = 0;
this.activation = 0;
this.selfconnection = new Neuron.connection(this, this, 0); // weight = 0 -> not connected
this.squash = Neuron.squash.LOGISTIC;
this.neighboors = {};
this.bias = Math.random() * .2 - .1;
}
// activate the neuron
activate: function(input) {
activate(input) {
// activation from enviroment (for input neurons)
if (typeof input != 'undefined') {
this.activation = input;
@@ -63,7 +59,7 @@ Neuron.prototype = {
this.derivative = this.squash(this.state, true);
// update traces
var influences = [];
const influences = [];
for (var id in this.trace.extended) {
// extended elegibility trace
var neuron = this.neighboors[id];
@@ -72,7 +68,7 @@ Neuron.prototype = {
var influence = neuron.selfconnection.gater == this ? neuron.old : 0;
// index runs over all the incoming connections to the gated neuron that are gated by this unit
for (var incoming in this.trace.influences[neuron.ID]) { // captures the effect that has an input connection to this unit, on a neuron that is gated by this unit
for (let incoming in this.trace.influences[neuron.ID]) { // captures the effect that has an input connection to this unit, on a neuron that is gated by this unit
influence += this.trace.influences[neuron.ID][incoming].weight *
this.trace.influences[neuron.ID][incoming].from.activation;
}
@@ -84,37 +80,37 @@ Neuron.prototype = {
// elegibility trace - Eq. 17
this.trace.elegibility[input.ID] = this.selfconnection.gain * this.selfconnection
.weight * this.trace.elegibility[input.ID] + input.gain * input.from
.activation;
.weight * this.trace.elegibility[input.ID] + input.gain * input.from
.activation;
for (var id in this.trace.extended) {
// extended elegibility trace
var xtrace = this.trace.extended[id];
const xtrace = this.trace.extended[id];
var neuron = this.neighboors[id];
var influence = influences[neuron.ID];
// eq. 18
xtrace[input.ID] = neuron.selfconnection.gain * neuron.selfconnection
.weight * xtrace[input.ID] + this.derivative * this.trace.elegibility[
.weight * xtrace[input.ID] + this.derivative * this.trace.elegibility[
input.ID] * influence;
}
}
// update gated connection's gains
for (var connection in this.connections.gated) {
for (let connection in this.connections.gated) {
this.connections.gated[connection].gain = this.activation;
}
return this.activation;
},
}
// back-propagate the error
propagate: function(rate, target) {
propagate(rate, target) {
// error accumulator
var error = 0;
let error = 0;
// whether or not this neuron is in the output layer
var isOutput = typeof target != 'undefined';
const isOutput = typeof target != 'undefined';
// output neurons get their error from the enviroment
if (isOutput)
@@ -124,7 +120,7 @@ Neuron.prototype = {
{
// error responsibilities from all the connections projected from this neuron
for (var id in this.connections.projected) {
var connection = this.connections.projected[id];
const connection = this.connections.projected[id];
var neuron = connection.to;
// Eq. 21
error += neuron.error.responsibility * connection.gain * connection.weight;
@@ -137,12 +133,12 @@ Neuron.prototype = {
// error responsibilities from all the connections gated by this neuron
for (var id in this.trace.extended) {
var neuron = this.neighboors[id]; // gated neuron
var influence = neuron.selfconnection.gater == this ? neuron.old : 0; // if gated neuron's selfconnection is gated by this neuron
let influence = neuron.selfconnection.gater == this ? neuron.old : 0; // if gated neuron's selfconnection is gated by this neuron
// index runs over all the connections to the gated neuron that are gated by this neuron
for (var input in this.trace.influences[id]) { // captures the effect that the input connection of this neuron have, on a neuron which its input/s is/are gated by this neuron
influence += this.trace.influences[id][input].weight * this.trace.influences[
neuron.ID][input].from.activation;
neuron.ID][input].from.activation;
}
// eq. 22
error += neuron.error.responsibility * influence;
@@ -163,20 +159,20 @@ Neuron.prototype = {
var input = this.connections.inputs[id];
// Eq. 24
var gradient = this.error.projected * this.trace.elegibility[input.ID];
let gradient = this.error.projected * this.trace.elegibility[input.ID];
for (var id in this.trace.extended) {
var neuron = this.neighboors[id];
gradient += neuron.error.responsibility * this.trace.extended[
neuron.ID][input.ID];
neuron.ID][input.ID];
}
input.weight += rate * gradient; // adjust weights - aka learn
}
// adjust bias
this.bias += rate * this.error.responsibility;
},
}
project: function(neuron, weight) {
project(neuron, weight) {
// self-connection
if (neuron == this) {
this.selfconnection.weight = 1;
@@ -184,7 +180,7 @@ Neuron.prototype = {
}
// check if connection already exists
var connected = this.connected(neuron);
const connected = this.connected(neuron);
if (connected && connected.type == "projected") {
// update connection
if (typeof weight != 'undefined')
@@ -202,25 +198,25 @@ Neuron.prototype = {
neuron.connections.inputs[connection.ID] = connection;
neuron.trace.elegibility[connection.ID] = 0;
for (var id in neuron.trace.extended) {
var trace = neuron.trace.extended[id];
for (let id in neuron.trace.extended) {
const trace = neuron.trace.extended[id];
trace[connection.ID] = 0;
}
return connection;
},
}
gate: function(connection) {
gate(connection) {
// add connection to gated list
this.connections.gated[connection.ID] = connection;
var neuron = connection.to;
const neuron = connection.to;
if (!(neuron.ID in this.trace.extended)) {
// extended trace
this.neighboors[neuron.ID] = neuron;
var xtrace = this.trace.extended[neuron.ID] = {};
for (var id in this.connections.inputs) {
var input = this.connections.inputs[id];
const xtrace = this.trace.extended[neuron.ID] = {};
for (let id in this.connections.inputs) {
const input = this.connections.inputs[id];
xtrace[input.ID] = 0;
}
}
@@ -233,16 +229,16 @@ Neuron.prototype = {
// set gater
connection.gater = this;
},
}
// returns true or false whether the neuron is self-connected or not
selfconnected: function() {
selfconnected() {
return this.selfconnection.weight !== 0;
},
}
// returns true or false whether the neuron is connected to another neuron (parameter)
connected: function(neuron) {
var result = {
connected(neuron) {
const result = {
type: null,
connection: false
};
@@ -256,7 +252,7 @@ Neuron.prototype = {
return false;
}
for (var type in this.connections) {
for (let type in this.connections) {
for (var connection in this.connections[type]) {
var connection = this.connections[type][connection];
if (connection.to == neuron) {
@@ -272,56 +268,53 @@ Neuron.prototype = {
}
return false;
},
}
// clears all the traces (the neuron forgets it's context, but the connections remain intact)
clear: function() {
clear() {
for (var trace in this.trace.elegibility)
this.trace.elegibility[trace] = 0;
for (var trace in this.trace.extended)
for (var extended in this.trace.extended[trace])
for (let extended in this.trace.extended[trace])
this.trace.extended[trace][extended] = 0;
this.error.responsibility = this.error.projected = this.error.gated = 0;
},
}
// all the connections are randomized and the traces are cleared
reset: function() {
reset() {
this.clear();
for (var type in this.connections)
for (var connection in this.connections[type])
for (let type in this.connections)
for (let connection in this.connections[type])
this.connections[type][connection].weight = Math.random() * .2 - .1;
this.bias = Math.random() * .2 - .1;
this.old = this.state = this.activation = 0;
},
}
// hardcodes the behaviour of the neuron into an optimized function
optimize: function(optimized, layer) {
optimized = optimized || {};
var store_activation = [];
var store_trace = [];
var store_propagation = [];
var varID = optimized.memory || 0;
var neurons = optimized.neurons || 1;
var inputs = optimized.inputs || [];
var targets = optimized.targets || [];
var outputs = optimized.outputs || [];
var variables = optimized.variables || {};
var activation_sentences = optimized.activation_sentences || [];
var trace_sentences = optimized.trace_sentences || [];
var propagation_sentences = optimized.propagation_sentences || [];
var layers = optimized.layers || { __count: 0, __neuron: 0 };
optimize(optimized = {}, layer) {
const store_activation = [];
const store_trace = [];
const store_propagation = [];
let varID = optimized.memory || 0;
const neurons = optimized.neurons || 1;
const inputs = optimized.inputs || [];
const targets = optimized.targets || [];
const outputs = optimized.outputs || [];
const variables = optimized.variables || {};
const activation_sentences = optimized.activation_sentences || [];
const trace_sentences = optimized.trace_sentences || [];
const propagation_sentences = optimized.propagation_sentences || [];
const layers = optimized.layers || {__count: 0, __neuron: 0};
// allocate sentences
var allocate = function(store){
var allocated = layer in layers && store[layers.__count];
if (!allocated)
{
const allocate = store => {
const allocated = layer in layers && store[layers.__count];
if (!allocated) {
layers.__count = store.push([]) - 1;
layers[layer] = layers.__count;
}
@@ -329,15 +322,15 @@ Neuron.prototype = {
allocate(activation_sentences);
allocate(trace_sentences);
allocate(propagation_sentences);
var currentLayer = layers.__count;
const currentLayer = layers.__count;
// get/reserve space in memory by creating a unique ID for a variablel
var getVar = function() {
var args = Array.prototype.slice.call(arguments);
const getVar = function () {
const args = Array.prototype.slice.call(arguments);
if (args.length == 1) {
if (args[0] == 'target') {
var id = 'target_' + targets.length;
var id = `target_${targets.length}`;
targets.push(varID);
} else
var id = args[0];
@@ -348,47 +341,47 @@ Neuron.prototype = {
id: varID++
};
} else {
var extended = args.length > 2;
const extended = args.length > 2;
if (extended)
var value = args.pop();
var unit = args.shift();
var prop = args.pop();
const unit = args.shift();
const prop = args.pop();
if (!extended)
var value = unit[prop];
var id = prop + '_';
for (var property in args)
id += args[property] + '_';
var id = `${prop}_`;
for (let property in args)
id += `${args[property]}_`;
id += unit.ID;
if (id in variables)
return variables[id];
return variables[id] = {
value: value,
value,
id: varID++
};
}
};
// build sentence
var buildSentence = function() {
var args = Array.prototype.slice.call(arguments);
var store = args.pop();
var sentence = "";
for (var i in args)
const buildSentence = function () {
const args = Array.prototype.slice.call(arguments);
const store = args.pop();
let sentence = "";
for (let i in args)
if (typeof args[i] == 'string')
sentence += args[i];
else
sentence += 'F[' + args[i].id + ']';
sentence += `F[${args[i].id}]`;
store.push(sentence + ';');
store.push(`${sentence};`);
};
// helper to check if an object is empty
var isEmpty = function(obj) {
for (var prop in obj) {
const isEmpty = obj => {
for (let prop in obj) {
if (obj.hasOwnProperty(prop))
return false;
}
@@ -396,22 +389,22 @@ Neuron.prototype = {
};
// characteristics of the neuron
var noProjections = isEmpty(this.connections.projected);
var noGates = isEmpty(this.connections.gated);
var isInput = layer == 'input' ? true : isEmpty(this.connections.inputs);
var isOutput = layer == 'output' ? true : noProjections && noGates;
const noProjections = isEmpty(this.connections.projected);
const noGates = isEmpty(this.connections.gated);
const isInput = layer == 'input' ? true : isEmpty(this.connections.inputs);
const isOutput = layer == 'output' ? true : noProjections && noGates;
// optimize neuron's behaviour
var rate = getVar('rate');
var activation = getVar(this, 'activation');
const rate = getVar('rate');
const activation = getVar(this, 'activation');
if (isInput)
inputs.push(activation.id);
else {
activation_sentences[currentLayer].push(store_activation);
trace_sentences[currentLayer].push(store_trace);
propagation_sentences[currentLayer].push(store_propagation);
var old = getVar(this, 'old');
var state = getVar(this, 'state');
const old = getVar(this, 'old');
const state = getVar(this, 'state');
var bias = getVar(this, 'bias');
if (this.selfconnection.gater)
var self_gain = getVar(this.selfconnection, 'gain');
@@ -449,8 +442,8 @@ Neuron.prototype = {
activation, ')', store_activation);
break;
case Neuron.squash.TANH:
var eP = getVar('aux');
var eN = getVar('aux_2');
const eP = getVar('aux');
const eN = getVar('aux_2');
buildSentence(eP, ' = Math.exp(', state, ')', store_activation);
buildSentence(eN, ' = 1 / ', eP, store_activation);
buildSentence(activation, ' = (', eP, ' - ', eN, ') / (', eP, ' + ', eN, ')', store_activation);
@@ -473,18 +466,17 @@ Neuron.prototype = {
// calculate extended elegibility traces in advance
var neuron = this.neighboors[id];
var influence = getVar('influences[' + neuron.ID + ']');
var influence = getVar(`influences[${neuron.ID}]`);
var neuron_old = getVar(neuron, 'old');
var initialized = false;
if (neuron.selfconnection.gater == this)
{
let initialized = false;
if (neuron.selfconnection.gater == this) {
buildSentence(influence, ' = ', neuron_old, store_trace);
initialized = true;
}
for (var incoming in this.trace.influences[neuron.ID]) {
var incoming_weight = getVar(this.trace.influences[neuron.ID]
for (let incoming in this.trace.influences[neuron.ID]) {
const incoming_weight = getVar(this.trace.influences[neuron.ID]
[incoming], 'weight');
var incoming_activation = getVar(this.trace.influences[neuron.ID]
const incoming_activation = getVar(this.trace.influences[neuron.ID]
[incoming].from, 'activation');
if (initialized)
@@ -530,7 +522,7 @@ Neuron.prototype = {
for (var id in this.trace.extended) {
// extended elegibility trace
var neuron = this.neighboors[id];
var influence = getVar('influences[' + neuron.ID + ']');
var influence = getVar(`influences[${neuron.ID}]`);
var trace = getVar(this, 'trace', 'elegibility', input.ID, this.trace
.elegibility[input.ID]);
@@ -555,15 +547,15 @@ Neuron.prototype = {
}
}
for (var connection in this.connections.gated) {
var gated_gain = getVar(this.connections.gated[connection], 'gain');
const gated_gain = getVar(this.connections.gated[connection], 'gain');
buildSentence(gated_gain, ' = ', activation, store_activation);
}
}
if (!isInput) {
var responsibility = getVar(this, 'error', 'responsibility', this.error
const responsibility = getVar(this, 'error', 'responsibility', this.error
.responsibility);
if (isOutput) {
var target = getVar('target');
const target = getVar('target');
buildSentence(responsibility, ' = ', target, ' - ', activation,
store_propagation);
for (var id in this.connections.inputs) {
@@ -577,7 +569,7 @@ Neuron.prototype = {
outputs.push(activation.id);
} else {
if (!noProjections && !noGates) {
var error = getVar('aux');
const error = getVar('aux');
for (var id in this.connections.projected) {
var connection = this.connections.projected[id];
var neuron = connection.to;
@@ -593,7 +585,7 @@ Neuron.prototype = {
buildSentence(error, ' += ', neuron_responsibility, ' * ',
connection_weight, store_propagation);
}
var projected = getVar(this, 'error', 'projected', this.error.projected);
const projected = getVar(this, 'error', 'projected', this.error.projected);
buildSentence(projected, ' = ', derivative, ' * ', error,
store_propagation);
buildSentence(error, ' = 0', store_propagation);
@@ -617,7 +609,7 @@ Neuron.prototype = {
buildSentence(error, ' += ', neuron_responsibility, ' * ',
influence, store_propagation);
}
var gated = getVar(this, 'error', 'gated', this.error.gated);
const gated = getVar(this, 'error', 'gated', this.error.gated);
buildSentence(gated, ' = ', derivative, ' * ', error,
store_propagation);
buildSentence(responsibility, ' = ', projected, ' + ', gated,
@@ -719,78 +711,75 @@ Neuron.prototype = {
return {
memory: varID,
neurons: neurons + 1,
inputs: inputs,
outputs: outputs,
targets: targets,
variables: variables,
activation_sentences: activation_sentences,
trace_sentences: trace_sentences,
propagation_sentences: propagation_sentences,
layers: layers
inputs,
outputs,
targets,
variables,
activation_sentences,
trace_sentences,
propagation_sentences,
layers
}
}
}
// represents a connection between two neurons
Neuron.connection = function Connection(from, to, weight) {
Neuron.connection = class NeuronConnection {
constructor(from, to, weight) {
if (!from || !to)
throw new Error("Connection Error: Invalid neurons");
if (!from || !to)
throw new Error("Connection Error: Invalid neurons");
this.ID = Neuron.connection.uid();
this.from = from;
this.to = to;
this.weight = typeof weight == 'undefined' ? Math.random() * .2 - .1 :
weight;
this.gain = 1;
this.gater = null;
this.ID = Neuron.connection.uid();
this.from = from;
this.to = to;
this.weight = typeof weight == 'undefined' ? Math.random() * .2 - .1 :
weight;
this.gain = 1;
this.gater = null;
}
}
// squashing functions
Neuron.squash = {};
function registerSquash(name, fn) {
Neuron.squash[name] = fn;
fn.squashType = [name];
}
// eq. 5 & 5'
Neuron.squash.LOGISTIC = function(x, derivate) {
registerSquash('LOGISTIC', (x, derivate) => {
if (!derivate)
return 1 / (1 + Math.exp(-x));
var fx = Neuron.squash.LOGISTIC(x);
const fx = Neuron.squash.LOGISTIC(x);
return fx * (1 - fx);
};
Neuron.squash.TANH = function(x, derivate) {
});
registerSquash('TANH', (x, derivate) => {
if (derivate)
return 1 - Math.pow(Neuron.squash.TANH(x), 2);
var eP = Math.exp(x);
var eN = 1 / eP;
const eP = Math.exp(x);
const eN = 1 / eP;
return (eP - eN) / (eP + eN);
};
Neuron.squash.IDENTITY = function(x, derivate) {
return derivate ? 1 : x;
};
Neuron.squash.HLIM = function(x, derivate) {
return derivate ? 1 : x > 0 ? 1 : 0;
};
Neuron.squash.RELU = function(x, derivate) {
});
registerSquash('IDENTITY', (x, derivate) => derivate ? 1 : x);
registerSquash('HLIM', (x, derivate) => derivate ? 1 : x > 0 ? 1 : 0);
registerSquash('RELU', (x, derivate) => {
if (derivate)
return x > 0 ? 1 : 0;
return x > 0 ? x : 0;
};
});
// unique ID's
(function() {
var neurons = 0;
var connections = 0;
Neuron.uid = function() {
return neurons++;
}
Neuron.connection.uid = function() {
return connections++;
}
Neuron.quantity = function() {
return {
neurons: neurons,
connections: connections
}
}
})();
((() => {
let neurons = 0;
let connections = 0;
Neuron.uid = () => neurons++
Neuron.connection.uid = () => connections++
Neuron.quantity = () => ({
neurons,
connections
})
}))();
module.exports = Neuron;
+11 -26
Ver Arquivo
@@ -1,33 +1,18 @@
var Synaptic = {
Neuron: require('./neuron'),
Layer: require('./layer'),
Network: require('./network'),
Trainer: require('./trainer'),
Architect: require('./architect')
};
var Synaptic = {};
// CommonJS & AMD
if (typeof define !== 'undefined' && define.amd)
{
define([], function(){ return Synaptic });
}
Synaptic.Neuron = require('./neuron');
Synaptic.Layer = require('./layer');
Synaptic.Network = require('./network/index');
Synaptic.Trainer = require('./trainer');
Synaptic.Architect = require('./architect');
// Node.js
if (typeof module !== 'undefined' && module.exports)
{
module.exports = Synaptic;
}
module.exports = Synaptic;
// Browser
if (typeof window == 'object')
{
(function(){
var oldSynaptic = window['synaptic'];
Synaptic.ninja = function(){
window['synaptic'] = oldSynaptic;
return Synaptic;
};
})();
if (typeof window == 'object') {
//noinspection CommaExpressionJS
Synaptic.ninja = ((oldSynaptic = window['synaptic']) =>
() => (window['synaptic'] = oldSynaptic, Synaptic))();
window['synaptic'] = Synaptic;
}
+217 -225
Ver Arquivo
@@ -1,43 +1,30 @@
// export
if (module) module.exports = Trainer;
/*******************************************************************************************
TRAINER
*******************************************************************************************/
function Trainer(network, options) {
options = options || {};
this.network = network;
this.rate = options.rate || .2;
this.iterations = options.iterations || 100000;
this.error = options.error || .005;
this.cost = options.cost || null;
this.crossValidate = options.crossValidate || null;
}
Trainer.prototype = {
class Trainer {
constructor(network, options={}) {
this.network = network;
this.rate = options.rate || .2;
this.iterations = options.iterations || 100000;
this.error = options.error || .005;
this.cost = options.cost || null;
this.crossValidate = options.crossValidate || null;
}
// trains any given set to a network
train: function(set, options) {
train(set, options) {
var error = 1;
var iterations = bucketSize = 0;
var abort = false;
var currentRate;
var cost = options && options.cost || this.cost || Trainer.cost.MSE;
var crossValidate = false, testSet, trainSet;
let error = 1;
let iterations = bucketSize = 0;
let abort = false;
let currentRate;
const cost = options && options.cost || this.cost || Trainer.cost.MSE;
let crossValidate = false, testSet, trainSet;
var start = Date.now();
const start = Date.now();
if (options) {
if (options.shuffle) {
//+ Jonas Raoni Soares Silva
//@ http://jsfromhell.com/array/shuffle [v1.0]
function shuffle(o) { //v1.0
for (var j, x, i = o.length; i; j = Math.floor(Math.random() * i), x = o[--i], o[i] = o[j], o[j] = x);
return o;
};
}
if (options.iterations)
this.iterations = options.iterations;
if (options.error)
@@ -69,7 +56,7 @@ Trainer.prototype = {
}
if(crossValidate) {
var numTrain = Math.ceil((1 - this.crossValidate.testSize) * set.length);
const numTrain = Math.ceil((1 - this.crossValidate.testSize) * set.length);
trainSet = set.slice(0, numTrain);
testSet = set.slice(numTrain);
}
@@ -80,12 +67,12 @@ Trainer.prototype = {
break;
}
var currentSetSize = set.length;
let currentSetSize = set.length;
error = 0;
iterations++;
if(bucketSize > 0) {
var currentBucket = Math.floor(iterations / bucketSize);
const currentBucket = Math.floor(iterations / bucketSize);
currentRate = this.rate[currentBucket] || currentRate;
}
@@ -109,7 +96,7 @@ Trainer.prototype = {
if (options) {
if (this.schedule && this.schedule.every && iterations %
this.schedule.every == 0)
abort = this.schedule.do({ error: error, iterations: iterations, rate: currentRate });
abort = this.schedule.do({ error, iterations, rate: currentRate });
else if (options.log && iterations % options.log == 0) {
console.log('iterations', iterations, 'error', error, 'rate', currentRate);
};
@@ -118,52 +105,52 @@ Trainer.prototype = {
}
}
var results = {
error: error,
iterations: iterations,
const results = {
error,
iterations,
time: Date.now() - start
};
return results;
},
}
// trains any given set to a network, using a WebWorker (only for the browser). Returns a Promise of the results.
trainAsync: function(set, options) {
var train = this.workerTrain.bind(this);
return new Promise(function(resolve, reject) {
trainAsync(set, options) {
const train = this.workerTrain.bind(this);
return new Promise((resolve, reject) => {
try {
train(set, resolve, options, true)
} catch(e) {
reject(e)
}
})
},
}
// preforms one training epoch and returns the error (private function used in this.train)
_trainSet: function(set, currentRate, costFunction) {
var errorSum = 0;
for (var train in set) {
var input = set[train].input;
var target = set[train].output;
_trainSet(set, currentRate, costFunction) {
let errorSum = 0;
for (let train in set) {
const input = set[train].input;
const target = set[train].output;
var output = this.network.activate(input);
const output = this.network.activate(input);
this.network.propagate(currentRate, target);
errorSum += costFunction(target, output);
}
return errorSum;
},
}
// tests a set and returns the error and elapsed time
test: function(set, options) {
test(set, options) {
var error = 0;
var input, output, target;
var cost = options && options.cost || this.cost || Trainer.cost.MSE;
let error = 0;
let input, output, target;
const cost = options && options.cost || this.cost || Trainer.cost.MSE;
var start = Date.now();
const start = Date.now();
for (var test in set) {
for (let test in set) {
input = set[test].input;
target = set[test].output;
output = this.network.activate(input);
@@ -172,43 +159,43 @@ Trainer.prototype = {
error /= set.length;
var results = {
error: error,
const results = {
error,
time: Date.now() - start
};
return results;
},
}
// trains any given set to a network using a WebWorker [deprecated: use trainAsync instead]
workerTrain: function(set, callback, options, suppressWarning) {
workerTrain(set, callback, options, suppressWarning) {
if (!suppressWarning) {
console.warn('Deprecated: do not use `workerTrain`, use `trainAsync` instead.')
}
var that = this;
const that = this;
if (!this.network.optimized)
this.network.optimize();
// Create a new worker
var worker = this.network.worker(this.network.optimized.memory, set, options);
const worker = this.network.worker(this.network.optimized.memory, set, options);
// train the worker
worker.onmessage = function(e) {
worker.onmessage = e => {
switch(e.data.action) {
case 'done':
var iterations = e.data.message.iterations;
var error = e.data.message.error;
var time = e.data.message.time;
const iterations = e.data.message.iterations;
const error = e.data.message.error;
const time = e.data.message.time;
that.network.optimized.ownership(e.data.memoryBuffer);
// Done callback
callback({
error: error,
iterations: iterations,
time: time
error,
iterations,
time
});
// Delete the worker and all its associated memory
@@ -220,7 +207,7 @@ Trainer.prototype = {
case 'schedule':
if (options && options.schedule && typeof options.schedule.do === 'function') {
var scheduled = options.schedule.do
const scheduled = options.schedule.do;
scheduled(e.data.message)
}
break;
@@ -229,15 +216,15 @@ Trainer.prototype = {
// Start the worker
worker.postMessage({action: 'startTraining'});
},
}
// trains an XOR to the network
XOR: function(options) {
XOR(options) {
if (this.network.inputs() != 2 || this.network.outputs() != 1)
throw new Error("Incompatible network (2 inputs, 1 output)");
var defaults = {
const defaults = {
iterations: 100000,
log: false,
shuffle: true,
@@ -245,7 +232,7 @@ Trainer.prototype = {
};
if (options)
for (var i in options)
for (let i in options)
defaults[i] = options[i];
return this.train([{
@@ -261,56 +248,53 @@ Trainer.prototype = {
input: [1, 1],
output: [0]
}], defaults);
},
}
// trains the network to pass a Distracted Sequence Recall test
DSR: function(options) {
options = options || {};
DSR(options={}) {
const targets = options.targets || [2, 4, 7, 8];
const distractors = options.distractors || [3, 5, 6, 9];
const prompts = options.prompts || [0, 1];
const length = options.length || 24;
const criterion = options.success || 0.95;
const iterations = options.iterations || 100000;
const rate = options.rate || .1;
const log = options.log || 0;
const schedule = options.schedule || {};
const cost = options.cost || this.cost || Trainer.cost.CROSS_ENTROPY;
var targets = options.targets || [2, 4, 7, 8];
var distractors = options.distractors || [3, 5, 6, 9];
var prompts = options.prompts || [0, 1];
var length = options.length || 24;
var criterion = options.success || 0.95;
var iterations = options.iterations || 100000;
var rate = options.rate || .1;
var log = options.log || 0;
var schedule = options.schedule || {};
var cost = options.cost || this.cost || Trainer.cost.CROSS_ENTROPY;
var trial, correct, i, j, success;
let trial, correct, i, j, success;
trial = correct = i = j = success = 0;
var error = 1,
symbols = targets.length + distractors.length + prompts.length;
let error = 1;
const symbols = targets.length + distractors.length + prompts.length;
var noRepeat = function(range, avoid) {
var number = Math.random() * range | 0;
var used = false;
for (var i in avoid)
const noRepeat = (range, avoid) => {
const number = Math.random() * range | 0;
let used = false;
for (let i in avoid)
if (number == avoid[i])
used = true;
return used ? noRepeat(range, avoid) : number;
};
var equal = function(prediction, output) {
for (var i in prediction)
const equal = (prediction, output) => {
for (let i in prediction)
if (Math.round(prediction[i]) != output[i])
return false;
return true;
};
var start = Date.now();
const start = Date.now();
while (trial < iterations && (success < criterion || trial % 1000 != 0)) {
// generate sequence
var sequence = [],
sequenceLength = length - prompts.length;
const sequence = [], sequenceLength = length - prompts.length;
for (i = 0; i < sequenceLength; i++) {
var any = Math.random() * distractors.length | 0;
const any = Math.random() * distractors.length | 0;
sequence.push(distractors[any]);
}
var indexes = [],
positions = [];
const indexes = [];
let positions = [];
for (i = 0; i < prompts.length; i++) {
indexes.push(Math.random() * targets.length | 0);
positions.push(noRepeat(sequenceLength, positions));
@@ -322,28 +306,28 @@ Trainer.prototype = {
}
//train sequence
var distractorsCorrect;
var targetsCorrect = distractorsCorrect = 0;
let distractorsCorrect;
let targetsCorrect = distractorsCorrect = 0;
error = 0;
for (i = 0; i < length; i++) {
// generate input from sequence
var input = [];
const input = [];
for (j = 0; j < symbols; j++)
input[j] = 0;
input[sequence[i]] = 1;
// generate target output
var output = [];
const output = [];
for (j = 0; j < targets.length; j++)
output[j] = 0;
if (i >= sequenceLength) {
var index = i - sequenceLength;
const index = i - sequenceLength;
output[indexes[index]] = 1;
}
// check result
var prediction = this.network.activate(input);
const prediction = this.network.activate(input);
if (equal(prediction, output))
if (i < sequenceLength)
@@ -364,7 +348,7 @@ Trainer.prototype = {
if (trial % 1000 == 0)
correct = 0;
trial++;
var divideError = trial % 1000;
let divideError = trial % 1000;
divideError = divideError == 0 ? 1000 : divideError;
success = correct / divideError;
error /= length;
@@ -376,103 +360,104 @@ Trainer.prototype = {
if (schedule.do && schedule.every && trial % schedule.every == 0)
schedule.do({
iterations: trial,
success: success,
error: error,
success,
error,
time: Date.now() - start,
correct: correct
correct
});
}
return {
iterations: trial,
success: success,
error: error,
success,
error,
time: Date.now() - start
}
},
}
// train the network to learn an Embeded Reber Grammar
ERG: function(options) {
options = options || {};
var iterations = options.iterations || 150000;
var criterion = options.error || .05;
var rate = options.rate || .1;
var log = options.log || 500;
var cost = options.cost || this.cost || Trainer.cost.CROSS_ENTROPY;
ERG(options={}) {
const iterations = options.iterations || 150000;
const criterion = options.error || .05;
const rate = options.rate || .1;
const log = options.log || 500;
const cost = options.cost || this.cost || Trainer.cost.CROSS_ENTROPY;
// gramar node
var Node = function() {
this.paths = [];
};
Node.prototype = {
connect: function(node, value) {
class Node {
constructor() {
this.paths = [];
}
connect(node, value) {
this.paths.push({
node: node,
value: value
node,
value
});
return this;
},
any: function() {
}
any() {
if (this.paths.length == 0)
return false;
var index = Math.random() * this.paths.length | 0;
const index = Math.random() * this.paths.length | 0;
return this.paths[index];
},
test: function(value) {
for (var i in this.paths)
}
test(value) {
for (let i in this.paths)
if (this.paths[i].value == value)
return this.paths[i];
return false;
}
};
}
var reberGrammar = function() {
const reberGrammar = () => {
// build a reber grammar
var output = new Node();
var n1 = (new Node()).connect(output, "E");
var n2 = (new Node()).connect(n1, "S");
var n3 = (new Node()).connect(n1, "V").connect(n2, "P");
var n4 = (new Node()).connect(n2, "X");
const output = new Node();
const n1 = (new Node()).connect(output, "E");
const n2 = (new Node()).connect(n1, "S");
const n3 = (new Node()).connect(n1, "V").connect(n2, "P");
const n4 = (new Node()).connect(n2, "X");
n4.connect(n4, "S");
var n5 = (new Node()).connect(n3, "V");
const n5 = (new Node()).connect(n3, "V");
n5.connect(n5, "T");
n2.connect(n5, "X");
var n6 = (new Node()).connect(n4, "T").connect(n5, "P");
var input = (new Node()).connect(n6, "B");
const n6 = (new Node()).connect(n4, "T").connect(n5, "P");
const input = (new Node()).connect(n6, "B");
return {
input: input,
output: output
input,
output
}
};
// build an embeded reber grammar
var embededReberGrammar = function() {
var reber1 = reberGrammar();
var reber2 = reberGrammar();
const embededReberGrammar = () => {
const reber1 = reberGrammar();
const reber2 = reberGrammar();
var output = new Node();
var n1 = (new Node).connect(output, "E");
const output = new Node();
const n1 = (new Node).connect(output, "E");
reber1.output.connect(n1, "T");
reber2.output.connect(n1, "P");
var n2 = (new Node).connect(reber1.input, "P").connect(reber2.input,
const n2 = (new Node).connect(reber1.input, "P").connect(reber2.input,
"T");
var input = (new Node).connect(n2, "B");
const input = (new Node).connect(n2, "B");
return {
input: input,
output: output
input,
output
}
};
// generate an ERG sequence
var generate = function() {
var node = embededReberGrammar().input;
var next = node.any();
var str = "";
const generate = () => {
const node = embededReberGrammar().input;
let next = node.any();
let str = "";
while (next) {
str += next.value;
next = next.node.any();
@@ -481,12 +466,12 @@ Trainer.prototype = {
};
// test if a string matches an embeded reber grammar
var test = function(str) {
var node = embededReberGrammar().input;
var i = 0;
var ch = str.charAt(i);
const test = str => {
let node = embededReberGrammar().input;
let i = 0;
let ch = str.charAt(i);
while (i < str.length) {
var next = node.test(ch);
const next = node.test(ch);
if (!next)
return false;
node = next.node;
@@ -496,12 +481,12 @@ Trainer.prototype = {
};
// helper to check if the output and the target vectors match
var different = function(array1, array2) {
var max1 = 0;
var i1 = -1;
var max2 = 0;
var i2 = -1;
for (var i in array1) {
const different = (array1, array2) => {
let max1 = 0;
let i1 = -1;
let max2 = 0;
let i2 = -1;
for (let i in array1) {
if (array1[i] > max1) {
max1 = array1[i];
i1 = i;
@@ -515,9 +500,9 @@ Trainer.prototype = {
return i1 != i2;
};
var iteration = 0;
var error = 1;
var table = {
let iteration = 0;
let error = 1;
const table = {
"B": 0,
"P": 1,
"T": 2,
@@ -526,31 +511,31 @@ Trainer.prototype = {
"E": 5
};
var start = Date.now();
const start = Date.now();
while (iteration < iterations && error > criterion) {
var i = 0;
let i = 0;
error = 0;
// ERG sequence to learn
var sequence = generate();
const sequence = generate();
// input
var read = sequence.charAt(i);
let read = sequence.charAt(i);
// target
var predict = sequence.charAt(i + 1);
let predict = sequence.charAt(i + 1);
// train
while (i < sequence.length - 1) {
var input = [];
var target = [];
for (var j = 0; j < 6; j++) {
const input = [];
const target = [];
for (let j = 0; j < 6; j++) {
input[j] = 0;
target[j] = 0;
}
input[table[read]] = 1;
target[table[predict]] = 1;
var output = this.network.activate(input);
const output = this.network.activate(input);
if (different(output, target))
this.network.propagate(rate, target);
@@ -570,14 +555,14 @@ Trainer.prototype = {
return {
iterations: iteration,
error: error,
error,
time: Date.now() - start,
test: test,
generate: generate
test,
generate
}
},
}
timingTask: function(options){
timingTask(options) {
if (this.network.inputs() != 2 || this.network.outputs() != 1)
throw new Error("Invalid Network: must have 2 inputs and one output");
@@ -589,31 +574,31 @@ Trainer.prototype = {
function getSamples (trainingSize, testSize){
// sample size
var size = trainingSize + testSize;
const size = trainingSize + testSize;
// generate samples
var t = 0;
var set = [];
for (var i = 0; i < size; i++) {
let t = 0;
const set = [];
for (let i = 0; i < size; i++) {
set.push({ input: [0,0], output: [0] });
}
while(t < size - 20) {
var n = Math.round(Math.random() * 20);
let n = Math.round(Math.random() * 20);
set[t].input[0] = 1;
for (var j = t; j <= t + n; j++){
for (let j = t; j <= t + n; j++){
set[j].input[1] = n / 20;
set[j].output[0] = 0.5;
}
t += n;
n = Math.round(Math.random() * 20);
for (var k = t+1; k <= (t + n) && k < size; k++)
for (let k = t+1; k <= (t + n) && k < size; k++)
set[k].input[1] = set[t].input[1];
t += n;
}
// separate samples between train and test sets
var trainingSet = []; var testSet = [];
for (var l = 0; l < size; l++)
const trainingSet = []; const testSet = [];
for (let l = 0; l < size; l++)
(l < trainingSize ? trainingSet : testSet).push(set[l]);
// return samples
@@ -623,24 +608,24 @@ Trainer.prototype = {
}
}
var iterations = options.iterations || 200;
var error = options.error || .005;
var rate = options.rate || [.03, .02];
var log = options.log === false ? false : options.log || 10;
var cost = options.cost || this.cost || Trainer.cost.MSE;
var trainingSamples = options.trainSamples || 7000;
var testSamples = options.trainSamples || 1000;
const iterations = options.iterations || 200;
const error = options.error || .005;
const rate = options.rate || [.03, .02];
const log = options.log === false ? false : options.log || 10;
const cost = options.cost || this.cost || Trainer.cost.MSE;
const trainingSamples = options.trainSamples || 7000;
const testSamples = options.trainSamples || 1000;
// samples for training and testing
var samples = getSamples(trainingSamples, testSamples);
const samples = getSamples(trainingSamples, testSamples);
// train
var result = this.train(samples.train, {
rate: rate,
log: log,
iterations: iterations,
error: error,
cost: cost
const result = this.train(samples.train, {
rate,
log,
iterations,
error,
cost
});
return {
@@ -648,29 +633,36 @@ Trainer.prototype = {
test: this.test(samples.test)
}
}
};
}
// Built-in cost functions
Trainer.cost = {
// Eq. 9
CROSS_ENTROPY: function(target, output)
{
var crossentropy = 0;
for (var i in output)
CROSS_ENTROPY(target, output) {
let crossentropy = 0;
for (let i in output)
crossentropy -= (target[i] * Math.log(output[i]+1e-15)) + ((1-target[i]) * Math.log((1+1e-15)-output[i])); // +1e-15 is a tiny push away to avoid Math.log(0)
return crossentropy;
},
MSE: function(target, output)
{
var mse = 0;
for (var i in output)
MSE(target, output) {
let mse = 0;
for (let i in output)
mse += Math.pow(target[i] - output[i], 2);
return mse / output.length;
},
BINARY: function(target, output){
var misses = 0;
for (var i in output)
BINARY(target, output) {
let misses = 0;
for (let i in output)
misses += Math.round(target[i] * 2) != Math.round(output[i] * 2);
return misses;
}
}
};
//+ Jonas Raoni Soares Silva
//@ http://jsfromhell.com/array/shuffle [v1.0]
function shuffle(o) { //v1.0
for (let j, x, i = o.length; i; j = Math.floor(Math.random() * i), x = o[--i], o[i] = o[j], o[j] = x);
return o;
};
module.exports = Trainer;
-1
Ver Arquivo
@@ -1 +0,0 @@
global.synaptic = require('../dist/synaptic');
-1
Ver Arquivo
@@ -1 +0,0 @@
global.synaptic = require('../src/synaptic');
-1
Ver Arquivo
@@ -1 +0,0 @@
[^_]*.js
+16 -8
Ver Arquivo
@@ -1,13 +1,15 @@
// import
var chai = require('chai');
chai.use(require('chai-stats'));
var assert = chai.assert;
var Perceptron = synaptic.Architect.Perceptron;
var LSTM = synaptic.Architect.LSTM;
var Layer = synaptic.Layer;
var Network = synaptic.Network;
var Trainer = synaptic.Trainer;
var synaptic = require('../src/synaptic.js');
var chai = require('chai');
chai.use(require('chai-stats/lib/stats'));
chai.use(require('chai-as-promised'));
var {assert, expect} = chai;
var {Layer, Network, Trainer} = synaptic;
var {Perceptron, LSTM} = synaptic.Architect;
@@ -600,3 +602,9 @@ describe("Rate Array Check", function () {
});
});
});
if (typeof window !== 'undefined') {
// browser-specific tests
} else {
// Node.js-specific tests
}
+44
Ver Arquivo
@@ -0,0 +1,44 @@
var synaptic = require('../src/synaptic.js');
var chai = require('chai');
chai.use(require('chai-stats/lib/stats'));
chai.use(require('chai-as-promised'));
var {assert, expect} = chai;
var {Perceptron} = synaptic.Architect;
describe('Browser: Web Worker', function () {
if (!global.Worker) {
return
}
var perceptron;
var perceptronWorker;
beforeEach(() => {
perceptron = new Perceptron(2, 3, 1);
perceptron.trainer.XOR();
return perceptron.worker()
.then(network => {
perceptronWorker = network
})
});
it('Should at least work', () =>
assert.isFulfilled(perceptronWorker.activate([1, 0])));
it("should return near-0 value on [0,0]", function () {
expect(perceptronWorker.activate([0, 0])).to.eventually.be.at.most(.49, "[0,0] did not output 0");
});
it("should return near-1 value on [0,1]", function () {
expect(perceptronWorker.activate([0, 1])).to.eventually.be.at.most(.51, "[0,1] did not output 1");
});
it("should return near-1 value on [1,0]", function () {
expect(perceptronWorker.activate([1, 0])).to.eventually.be.at.most(.51, "[1,0] did not output 1");
});
it("should return near-0 value on [1,1]", function () {
expect(perceptronWorker.activate([1, 1])).to.eventually.be.at.most(.49, "[1,1] did not output 0");
});
});
+18 -5
Ver Arquivo
@@ -1,5 +1,6 @@
var webpack = require('webpack')
var license = require('./prebuild.js')
var webpack = require('webpack');
var license = require('./prebuild.js');
module.exports = {
context: __dirname,
entry: [
@@ -8,9 +9,21 @@ module.exports = {
output: {
path: 'dist',
filename: 'synaptic.js',
libraryTarget: 'umd',
umdNamedDefine: 'synaptic',
},
loaders: [
{
test: /\.js$/,
exclude: /\/node_modules\//,
loaders: ['babel'],
},
],
plugins: [
new webpack.NoErrorsPlugin(),
new webpack.BannerPlugin(license())
new webpack.BannerPlugin(license()),
new webpack.DefinePlugin({
'process.env.WEBPACK': 'true',
}),
]
}
};