Comparar commits
3 Commits
| Autor | SHA1 | Data | |
|---|---|---|---|
| 1b2f31330e | |||
| ec2a393158 | |||
| 5df6e5cb17 |
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"presets": ["es2015"],
|
||||
"plugins": []
|
||||
}
|
||||
externo
+1024
-990
Diff do arquivo suprimido porque uma ou mais linhas são muito longas
+11
@@ -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
@@ -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
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
module.exports.Hopfield = require('./hopfield');
|
||||
module.exports.LSTM = require('./lstm');
|
||||
module.exports.Liquid = require('./liquid');
|
||||
module.exports.Perceptron = require('./perceptron');
|
||||
@@ -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
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -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
@@ -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;
|
||||
@@ -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);
|
||||
};
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -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';
|
||||
@@ -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;
|
||||
}
|
||||
})();
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
})
|
||||
};
|
||||
};
|
||||
@@ -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
@@ -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
@@ -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
@@ -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 +0,0 @@
|
||||
global.synaptic = require('../dist/synaptic');
|
||||
@@ -1 +0,0 @@
|
||||
global.synaptic = require('../src/synaptic');
|
||||
@@ -1 +0,0 @@
|
||||
[^_]*.js
|
||||
+16
-8
@@ -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
|
||||
}
|
||||
@@ -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
@@ -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',
|
||||
}),
|
||||
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
Referência em uma Nova Issue
Bloquear um usuário