Comparar commits
15 Commits
typescript
...
1.0.0
| Autor | SHA1 | Data | |
|---|---|---|---|
| e0f20561c2 | |||
| ec8cd5f29b | |||
| f4eed6a396 | |||
| 70b974ddcd | |||
| 892aa1b349 | |||
| 52fe07fc95 | |||
| d4aa1d23ac | |||
| ed5e287a40 | |||
| 8be394c4c7 | |||
| 26b75f9542 | |||
| e4b1639bf4 | |||
| 8c4e3db35c | |||
| cbfe912e54 | |||
| f1c5d7c9e2 | |||
| a8e7ca02df |
@@ -14,3 +14,5 @@ node_modules
|
||||
# Demo.
|
||||
demo.js
|
||||
|
||||
# Degub
|
||||
debug.html
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
{
|
||||
"name": "synaptic",
|
||||
"version": "1.0.0",
|
||||
"homepage": "https://github.com/cazala/synaptic",
|
||||
"authors": [
|
||||
"Juan Cazala <juancazala@gmail.com>"
|
||||
],
|
||||
"description": "architecture-free neural network library for node.js and the browser",
|
||||
"main": "./dist/synaptic.min.js",
|
||||
"moduleType": [
|
||||
"amd",
|
||||
"globals",
|
||||
"node"
|
||||
],
|
||||
"keywords": [
|
||||
"neural",
|
||||
"network",
|
||||
"deep",
|
||||
"learning",
|
||||
"machine",
|
||||
"learning",
|
||||
"lstm",
|
||||
"perceptron"
|
||||
],
|
||||
"license": "MIT",
|
||||
"ignore": [
|
||||
"**/.*",
|
||||
"node_modules",
|
||||
"bower_components",
|
||||
"test",
|
||||
"tests"
|
||||
]
|
||||
}
|
||||
externo
+183
-103
@@ -145,23 +145,23 @@ var Architect = {
|
||||
var last = args.pop();
|
||||
var option = {
|
||||
peepholes: Layer.connectionType.ALL_TO_ALL,
|
||||
hiddentohidden: false,
|
||||
outtohidden: false,
|
||||
outtogates: false,
|
||||
intoout: true,
|
||||
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('outtohidden'))
|
||||
option.outtohidden = last.outtohidden;
|
||||
if (last.hasOwnProperty('outtogates'))
|
||||
option.outtogates = last.outtogates;
|
||||
if (last.hasOwnProperty('intoout'))
|
||||
option.intoout = last.intoout;
|
||||
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;
|
||||
|
||||
@@ -216,15 +216,15 @@ var Architect = {
|
||||
var self = memoryCell.project(memoryCell);
|
||||
|
||||
// hidden to hidden recurrent connection
|
||||
if (option.hiddentohidden)
|
||||
if (option.hiddenToHidden)
|
||||
memoryCell.project(memoryCell, Layer.connectionType.ALL_TO_ELSE);
|
||||
|
||||
// out to hidden recurrent connection
|
||||
if (option.outtohidden)
|
||||
if (option.outputToHidden)
|
||||
outputLayer.project(memoryCell);
|
||||
|
||||
// out to gates recurrent connection
|
||||
if (option.outtogates) {
|
||||
if (option.outputToGates) {
|
||||
outputLayer.project(inputGate);
|
||||
outputLayer.project(outputGate);
|
||||
outputLayer.project(forgetGate);
|
||||
@@ -246,7 +246,7 @@ var Architect = {
|
||||
}
|
||||
|
||||
// input to output direct connection
|
||||
if (option.intoout)
|
||||
if (option.inputToOutput)
|
||||
inputLayer.project(outputLayer);
|
||||
|
||||
// set the layers of the neural network
|
||||
@@ -372,7 +372,7 @@ function Layer(size, label) {
|
||||
this.size = size | 0;
|
||||
this.list = [];
|
||||
this.label = label || null;
|
||||
this.connectedto = [];
|
||||
this.connectedTo = [];
|
||||
|
||||
while (size--) {
|
||||
var neuron = new Neuron();
|
||||
@@ -612,7 +612,7 @@ Layer.connection = function LayerConnection(fromLayer, toLayer, type, weights) {
|
||||
}
|
||||
}
|
||||
|
||||
fromLayer.connectedto.push(this);
|
||||
fromLayer.connectedTo.push(this);
|
||||
}
|
||||
|
||||
// types of connections
|
||||
@@ -783,7 +783,6 @@ Network.prototype = {
|
||||
hardcode += "F[" + optimized.variables[i].id + "] = " + (optimized.variables[
|
||||
i].value || 0) + "; ";
|
||||
hardcode += "var activate = function(input){\n";
|
||||
hardcode += "influences = [];";
|
||||
for (var i in optimized.inputs)
|
||||
hardcode += "F[" + optimized.inputs[i] + "] = input[" + i + "]; ";
|
||||
for (var currentLayer in optimized.activation_sentences) {
|
||||
@@ -997,21 +996,6 @@ Network.prototype = {
|
||||
neurons.push(copy);
|
||||
}
|
||||
|
||||
if (!ignoreTraces)
|
||||
for (var i in neurons) {
|
||||
var copy = neurons[i];
|
||||
|
||||
for (var input in neuron.trace.elegibility)
|
||||
copy.trace.elegibility[input] = neuron.trace.elegibility[input];
|
||||
|
||||
for (var gated in neuron.trace.extended) {
|
||||
copy.trace.extended[gated] = {};
|
||||
for (var input in neuron.trace.extended[gated])
|
||||
copy.trace.extended[ids[gated]][input] = neuron.trace.extended[
|
||||
gated][input];
|
||||
}
|
||||
}
|
||||
|
||||
// get connections
|
||||
for (var i in list) {
|
||||
var neuron = list[i].neuron;
|
||||
@@ -1032,8 +1016,7 @@ Network.prototype = {
|
||||
from: ids[neuron.ID],
|
||||
to: ids[neuron.ID],
|
||||
weight: neuron.selfconnection.weight,
|
||||
gater: neuron.selfconnection.gater ? ids[neuron.selfconnection.gater
|
||||
.ID] : null,
|
||||
gater: neuron.selfconnection.gater ? ids[neuron.selfconnection.gater.ID] : null,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1048,31 +1031,31 @@ Network.prototype = {
|
||||
$ node example.js > example.dot
|
||||
$ dot example.dot -Tpng > out.png
|
||||
*/
|
||||
toDot: function(edgeconnection) {
|
||||
if (! typeof edgeconnection)
|
||||
edgeconnection = false;
|
||||
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;
|
||||
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);
|
||||
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 ( edgeConnection) {
|
||||
if (connection.gatedfrom.length) {
|
||||
var fakeNode = "fake" + layerID + "_" + layertoID;
|
||||
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";
|
||||
code += " " + fakeNode + " -> " + layerToID + "\n";
|
||||
} else
|
||||
code += " " + layerID + " -> " + layertoID + " [label = " + size + "]\n";
|
||||
code += " " + layerID + " -> " + layerToID + " [label = " + size + "]\n";
|
||||
for (var from in connection.gatedfrom) { // gatings
|
||||
var layerfrom = connection.gatedfrom[from].layer;
|
||||
var type = connection.gatedfrom[from].type;
|
||||
@@ -1080,12 +1063,12 @@ Network.prototype = {
|
||||
code += " " + layerfromID + " -> " + fakeNode + " [color = blue]\n";
|
||||
}
|
||||
} else {
|
||||
code += " " + layerID + " -> " + layertoID + " [label = " + size + "]\n";
|
||||
code += " " + layerID + " -> " + layerToID + " [label = " + size + "]\n";
|
||||
for (var from in connection.gatedfrom) { // gatings
|
||||
var layerfrom = connection.gatedfrom[from].layer;
|
||||
var type = connection.gatedfrom[from].type;
|
||||
var layerfromID = layers.indexOf(layerfrom);
|
||||
code += " " + layerfromID + " -> " + layertoID + " [color = blue]\n";
|
||||
code += " " + layerfromID + " -> " + layerToID + " [color = blue]\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1179,12 +1162,12 @@ Network.prototype = {
|
||||
},
|
||||
|
||||
// returns a copy of the network
|
||||
clone: function(ignoreTraces) {
|
||||
return Network.fromJSON(this.toJSON(ignoreTraces));
|
||||
clone: function() {
|
||||
return Network.fromJSON(this.toJSON());
|
||||
}
|
||||
}
|
||||
|
||||
// rebuild a network that has been stored in a json using the method toJson()
|
||||
// rebuild a network that has been stored in a json using the method toJSON()
|
||||
Network.fromJSON = function(json) {
|
||||
|
||||
var neurons = [];
|
||||
@@ -1199,14 +1182,13 @@ Network.fromJSON = function(json) {
|
||||
var config = json.neurons[i];
|
||||
|
||||
var neuron = new Neuron();
|
||||
neuron.trace.elegibility = config.trace.elegibility;
|
||||
neuron.trace.extended = config.trace.extended;
|
||||
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;
|
||||
neuron.squash = config.squash in Neuron.squash ? Neuron.squash[config.squash] : Neuron.squash.LOGISTIC;
|
||||
neurons.push(neuron);
|
||||
|
||||
if (config.layer == 'input')
|
||||
@@ -1707,14 +1689,13 @@ Neuron.prototype = {
|
||||
buildSentence(derivative, ' = 1', store_activation);
|
||||
break;
|
||||
}
|
||||
|
||||
influences = [];
|
||||
|
||||
for (var id in this.trace.extended) {
|
||||
// calculate extended elegibility traces in advance
|
||||
|
||||
var xtrace = this.trace.extended[id];
|
||||
var neuron = this.neighboors[id];
|
||||
var influence = getVar('aux');
|
||||
var influence = getVar('influences[' + neuron.ID + ']');
|
||||
var neuron_old = getVar(neuron, 'old');
|
||||
var initialized = false;
|
||||
if (neuron.selfconnection.gater == this)
|
||||
@@ -1729,17 +1710,12 @@ Neuron.prototype = {
|
||||
[incoming].from, 'activation');
|
||||
|
||||
if (initialized)
|
||||
buildSentence(influence, ' += ', incoming_weight, ' * ',
|
||||
incoming_activation, store_trace);
|
||||
buildSentence(influence, ' += ', incoming_weight, ' * ', incoming_activation, store_trace);
|
||||
else {
|
||||
buildSentence(influence, ' = ', incoming_weight, ' * ',
|
||||
incoming_activation, store_trace);
|
||||
buildSentence(influence, ' = ', incoming_weight, ' * ', incoming_activation, store_trace);
|
||||
initialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
influences.push(neuron.ID);
|
||||
buildSentence("influences[" + (influences.length - 1) + "] = ", influence, store_trace);
|
||||
}
|
||||
|
||||
for (var i in this.connections.inputs) {
|
||||
@@ -1777,7 +1753,7 @@ Neuron.prototype = {
|
||||
// extended elegibility trace
|
||||
var xtrace = this.trace.extended[id];
|
||||
var neuron = this.neighboors[id];
|
||||
var influence = getVar('aux');
|
||||
var influence = getVar('influences[' + neuron.ID + ']');
|
||||
var neuron_old = getVar(neuron, 'old');
|
||||
|
||||
var trace = getVar(this, 'trace', 'elegibility', input.ID, this.trace
|
||||
@@ -1792,14 +1768,14 @@ Neuron.prototype = {
|
||||
if (neuron.selfconnection.gater)
|
||||
buildSentence(xtrace, ' = ', neuron_self_gain, ' * ',
|
||||
neuron_self_weight, ' * ', xtrace, ' + ', derivative, ' * ',
|
||||
trace, ' * ', "influences[" + influences.indexOf(neuron.ID) + "]", store_trace);
|
||||
trace, ' * ', influence, store_trace);
|
||||
else
|
||||
buildSentence(xtrace, ' = ', neuron_self_weight, ' * ',
|
||||
xtrace, ' + ', derivative, ' * ', trace, ' * ',
|
||||
"influences[" + influences.indexOf(neuron.ID) + "]", store_trace);
|
||||
influence, store_trace);
|
||||
else
|
||||
buildSentence(xtrace, ' = ', derivative, ' * ', trace, ' * ',
|
||||
"influences[" + influences.indexOf(neuron.ID) + "]", store_trace);
|
||||
influence, store_trace);
|
||||
}
|
||||
}
|
||||
for (var connection in this.connections.gated) {
|
||||
@@ -2053,7 +2029,7 @@ function Trainer(network, options) {
|
||||
this.rate = options.rate || .2;
|
||||
this.iterations = options.iterations || 100000;
|
||||
this.error = options.error || .005
|
||||
this.cost = options.cost || Trainer.cost.CROSS_ENTROPY;
|
||||
this.cost = options.cost || null;
|
||||
}
|
||||
|
||||
Trainer.prototype = {
|
||||
@@ -2063,8 +2039,9 @@ Trainer.prototype = {
|
||||
|
||||
var error = 1;
|
||||
var iterations = bucketSize = 0;
|
||||
var abort_training = false;
|
||||
var abort = false;
|
||||
var input, output, target, currentRate;
|
||||
var cost = options && options.cost || this.cost || Trainer.cost.MSE;
|
||||
|
||||
var start = Date.now();
|
||||
|
||||
@@ -2100,12 +2077,12 @@ Trainer.prototype = {
|
||||
}
|
||||
|
||||
|
||||
while (!abort_training && iterations < this.iterations && error > this.error) {
|
||||
while (!abort && iterations < this.iterations && error > this.error) {
|
||||
error = 0;
|
||||
|
||||
if(bucketSize > 0) {
|
||||
var currentBucket = Math.floor(iterations / bucketSize);
|
||||
currentRate = this.rate[currentBucket];
|
||||
currentRate = this.rate[currentBucket] || currentRate;
|
||||
}
|
||||
|
||||
for (var train in set) {
|
||||
@@ -2115,7 +2092,7 @@ Trainer.prototype = {
|
||||
output = this.network.activate(input);
|
||||
this.network.propagate(currentRate, target);
|
||||
|
||||
error += this.cost(target, output);
|
||||
error += cost(target, output);
|
||||
}
|
||||
|
||||
// check error
|
||||
@@ -2125,7 +2102,7 @@ Trainer.prototype = {
|
||||
if (options) {
|
||||
if (this.schedule && this.schedule.every && iterations %
|
||||
this.schedule.every == 0)
|
||||
abort_training = this.schedule.do({
|
||||
abort = this.schedule.do({
|
||||
error: error,
|
||||
iterations: iterations,
|
||||
rate: currentRate
|
||||
@@ -2147,6 +2124,33 @@ Trainer.prototype = {
|
||||
return results;
|
||||
},
|
||||
|
||||
// tests a set and returns the error and elapsed time
|
||||
test: function(set, options){
|
||||
|
||||
var error = 0;
|
||||
var abort = false;
|
||||
var input, output, target;
|
||||
var cost = options && options.cost || this.cost || Trainer.cost.MSE;
|
||||
|
||||
var start = Date.now();
|
||||
|
||||
for (var test in set) {
|
||||
input = set[test].input;
|
||||
target = set[test].output;
|
||||
output = this.network.activate(input);
|
||||
error += cost(target, output);
|
||||
}
|
||||
|
||||
error /= set.length;
|
||||
|
||||
var results = {
|
||||
error: error,
|
||||
time: Date.now() - start
|
||||
}
|
||||
|
||||
return results;
|
||||
},
|
||||
|
||||
// trains any given set to a network using a WebWorker
|
||||
workerTrain: function(set, callback, options) {
|
||||
|
||||
@@ -2155,7 +2159,8 @@ Trainer.prototype = {
|
||||
var iterations = bucketSize = 0;
|
||||
var input, output, target, currentRate;
|
||||
var length = set.length;
|
||||
var abort_training = false;
|
||||
var abort = false;
|
||||
var cost = options && options.cost || that.cost || Trainer.cost.MSE;
|
||||
|
||||
var start = Date.now();
|
||||
|
||||
@@ -2170,29 +2175,31 @@ Trainer.prototype = {
|
||||
};
|
||||
}
|
||||
if (options.iterations)
|
||||
this.iterations = options.iterations;
|
||||
that.iterations = options.iterations;
|
||||
if (options.error)
|
||||
this.error = options.error;
|
||||
that.error = options.error;
|
||||
if (options.rate)
|
||||
this.rate = options.rate;
|
||||
that.rate = options.rate;
|
||||
if (options.cost)
|
||||
this.cost = options.cost;
|
||||
that.cost = options.cost;
|
||||
if (options.schedule)
|
||||
this.schedule = options.schedule;
|
||||
that.schedule = options.schedule;
|
||||
if (options.customLog)
|
||||
{
|
||||
// for backward compatibility with code that used customLog
|
||||
console.log('Deprecated: use schedule instead of customLog')
|
||||
this.schedule = options.customLog;
|
||||
that.schedule = options.customLog;
|
||||
}
|
||||
}
|
||||
|
||||
// dynamic learning rate
|
||||
currentRate = this.rate;
|
||||
if(Array.isArray(this.rate)) {
|
||||
bucketSize = Math.floor(this.iterations / this.rate.length);
|
||||
currentRate = that.rate;
|
||||
if(Array.isArray(that.rate)) {
|
||||
bucketSize = Math.floor(that.iterations / that.rate.length);
|
||||
}
|
||||
|
||||
// create a worker
|
||||
var worker = this.network.worker();
|
||||
var worker = that.network.worker();
|
||||
|
||||
// activate the network
|
||||
function activateWorker(input)
|
||||
@@ -2208,7 +2215,7 @@ Trainer.prototype = {
|
||||
function propagateWorker(target){
|
||||
if(bucketSize > 0) {
|
||||
var currentBucket = Math.floor(iterations / bucketSize);
|
||||
currentRate = this.rate[currentBucket];
|
||||
currentRate = that.rate[currentBucket] || currentRate;
|
||||
}
|
||||
worker.postMessage({
|
||||
action: "propagate",
|
||||
@@ -2233,10 +2240,11 @@ Trainer.prototype = {
|
||||
|
||||
// log
|
||||
if (options) {
|
||||
if (this.schedule && this.schedule.every && iterations % this.schedule.every == 0)
|
||||
abort_training = this.schedule.do({
|
||||
if (that.schedule && that.schedule.every && iterations % that.schedule.every == 0)
|
||||
abort = that.schedule.do({
|
||||
error: error,
|
||||
iterations: iterations
|
||||
iterations: iterations,
|
||||
rate: currentRate
|
||||
});
|
||||
else if (options.log && iterations % options.log == 0) {
|
||||
console.log('iterations', iterations, 'error', error);
|
||||
@@ -2245,7 +2253,7 @@ Trainer.prototype = {
|
||||
shuffle(set);
|
||||
}
|
||||
|
||||
if (!abort_training && iterations < that.iterations && error > that.error)
|
||||
if (!abort && iterations < that.iterations && error > that.error)
|
||||
{
|
||||
activateWorker(set[index].input);
|
||||
} else {
|
||||
@@ -2264,7 +2272,7 @@ Trainer.prototype = {
|
||||
|
||||
if (e.data.action == "activate")
|
||||
{
|
||||
error += that.cost(set[index].output, e.data.output);
|
||||
error += cost(set[index].output, e.data.output);
|
||||
propagateWorker(set[index].output);
|
||||
index++;
|
||||
}
|
||||
@@ -2321,6 +2329,7 @@ Trainer.prototype = {
|
||||
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 = 0,
|
||||
error = 1,
|
||||
@@ -2396,10 +2405,7 @@ Trainer.prototype = {
|
||||
this.network.propagate(rate, output);
|
||||
}
|
||||
|
||||
var delta = 0;
|
||||
for (var j in prediction)
|
||||
delta += Math.pow(output[j] - prediction[j], 2);
|
||||
error += delta / this.network.outputs();
|
||||
error += cost(output, prediction);
|
||||
|
||||
if (distractorsCorrect + targetsCorrect == length)
|
||||
correct++;
|
||||
@@ -2444,6 +2450,7 @@ Trainer.prototype = {
|
||||
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;
|
||||
|
||||
// gramar node
|
||||
var Node = function() {
|
||||
@@ -2602,12 +2609,7 @@ Trainer.prototype = {
|
||||
read = sequence.charAt(++i);
|
||||
predict = sequence.charAt(i + 1);
|
||||
|
||||
var delta = 0;
|
||||
for (var k in output)
|
||||
delta += Math.pow(target[k] - output[k], 2)
|
||||
delta /= output.length;
|
||||
|
||||
error += delta;
|
||||
error += cost(target, output);
|
||||
}
|
||||
error /= sequence.length;
|
||||
iteration++;
|
||||
@@ -2624,6 +2626,78 @@ Trainer.prototype = {
|
||||
test: test,
|
||||
generate: generate
|
||||
}
|
||||
},
|
||||
|
||||
timingTask: function(options){
|
||||
|
||||
if (this.network.inputs() != 2 || this.network.outputs() != 1)
|
||||
throw "Invalid Network: must have 2 inputs and one output";
|
||||
|
||||
if (typeof options == 'undefined')
|
||||
var options = {};
|
||||
|
||||
// helper
|
||||
function getSamples (trainingSize, testSize){
|
||||
|
||||
// sample size
|
||||
var size = trainingSize + testSize;
|
||||
|
||||
// generate samples
|
||||
var t = 0;
|
||||
var set = [];
|
||||
for (var i = 0; i < size; i++) {
|
||||
set.push({ input: [0,0], output: [0] });
|
||||
}
|
||||
while(t < size - 20) {
|
||||
var n = Math.round(Math.random() * 20);
|
||||
set[t].input[0] = 1;
|
||||
for (var 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++)
|
||||
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++)
|
||||
(l < trainingSize ? trainingSet : testSet).push(set[l]);
|
||||
|
||||
// return samples
|
||||
return {
|
||||
train: trainingSet,
|
||||
test: testSet
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
// samples for training and testing
|
||||
var samples = getSamples(trainingSamples, testSamples);
|
||||
|
||||
// train
|
||||
var result = this.train(samples.train, {
|
||||
rate: rate,
|
||||
log: log,
|
||||
iterations: iterations,
|
||||
error: error,
|
||||
cost: cost
|
||||
});
|
||||
|
||||
return {
|
||||
train: result,
|
||||
test: this.test(samples.test)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -2643,6 +2717,12 @@ Trainer.cost = {
|
||||
for (var 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)
|
||||
misses += Math.round(target[i] * 2) != Math.round(output[i] * 2);
|
||||
return misses;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
externo
+3
-2
Diff do arquivo suprimido porque uma ou mais linhas são muito longas
@@ -44,7 +44,6 @@ gulp.task('debug', function () {
|
||||
.bundle()
|
||||
.pipe(source('synaptic.js'))
|
||||
.pipe(buffer())
|
||||
.pipe(prepend(license))
|
||||
.pipe(append(globals))
|
||||
.pipe(gulp.dest('./dist'));
|
||||
});
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "synaptic",
|
||||
"version": "0.1.7",
|
||||
"version": "1.0.0",
|
||||
"description": "Architecture-free neural network library",
|
||||
"main": "./src/synaptic",
|
||||
"scripts": {
|
||||
|
||||
+16
-16
@@ -58,23 +58,23 @@ var Architect = {
|
||||
var last = args.pop();
|
||||
var option = {
|
||||
peepholes: Layer.connectionType.ALL_TO_ALL,
|
||||
hiddentohidden: false,
|
||||
outtohidden: false,
|
||||
outtogates: false,
|
||||
intoout: true,
|
||||
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('outtohidden'))
|
||||
option.outtohidden = last.outtohidden;
|
||||
if (last.hasOwnProperty('outtogates'))
|
||||
option.outtogates = last.outtogates;
|
||||
if (last.hasOwnProperty('intoout'))
|
||||
option.intoout = last.intoout;
|
||||
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;
|
||||
|
||||
@@ -129,15 +129,15 @@ var Architect = {
|
||||
var self = memoryCell.project(memoryCell);
|
||||
|
||||
// hidden to hidden recurrent connection
|
||||
if (option.hiddentohidden)
|
||||
if (option.hiddenToHidden)
|
||||
memoryCell.project(memoryCell, Layer.connectionType.ALL_TO_ELSE);
|
||||
|
||||
// out to hidden recurrent connection
|
||||
if (option.outtohidden)
|
||||
if (option.outputToHidden)
|
||||
outputLayer.project(memoryCell);
|
||||
|
||||
// out to gates recurrent connection
|
||||
if (option.outtogates) {
|
||||
if (option.outputToGates) {
|
||||
outputLayer.project(inputGate);
|
||||
outputLayer.project(outputGate);
|
||||
outputLayer.project(forgetGate);
|
||||
@@ -159,7 +159,7 @@ var Architect = {
|
||||
}
|
||||
|
||||
// input to output direct connection
|
||||
if (option.intoout)
|
||||
if (option.inputToOutput)
|
||||
inputLayer.project(outputLayer);
|
||||
|
||||
// set the layers of the neural network
|
||||
|
||||
+2
-2
@@ -9,7 +9,7 @@ function Layer(size, label) {
|
||||
this.size = size | 0;
|
||||
this.list = [];
|
||||
this.label = label || null;
|
||||
this.connectedto = [];
|
||||
this.connectedTo = [];
|
||||
|
||||
while (size--) {
|
||||
var neuron = new Neuron();
|
||||
@@ -249,7 +249,7 @@ Layer.connection = function LayerConnection(fromLayer, toLayer, type, weights) {
|
||||
}
|
||||
}
|
||||
|
||||
fromLayer.connectedto.push(this);
|
||||
fromLayer.connectedTo.push(this);
|
||||
}
|
||||
|
||||
// types of connections
|
||||
|
||||
+20
-38
@@ -142,7 +142,6 @@ Network.prototype = {
|
||||
hardcode += "F[" + optimized.variables[i].id + "] = " + (optimized.variables[
|
||||
i].value || 0) + "; ";
|
||||
hardcode += "var activate = function(input){\n";
|
||||
hardcode += "influences = [];";
|
||||
for (var i in optimized.inputs)
|
||||
hardcode += "F[" + optimized.inputs[i] + "] = input[" + i + "]; ";
|
||||
for (var currentLayer in optimized.activation_sentences) {
|
||||
@@ -356,21 +355,6 @@ Network.prototype = {
|
||||
neurons.push(copy);
|
||||
}
|
||||
|
||||
if (!ignoreTraces)
|
||||
for (var i in neurons) {
|
||||
var copy = neurons[i];
|
||||
|
||||
for (var input in neuron.trace.elegibility)
|
||||
copy.trace.elegibility[input] = neuron.trace.elegibility[input];
|
||||
|
||||
for (var gated in neuron.trace.extended) {
|
||||
copy.trace.extended[gated] = {};
|
||||
for (var input in neuron.trace.extended[gated])
|
||||
copy.trace.extended[ids[gated]][input] = neuron.trace.extended[
|
||||
gated][input];
|
||||
}
|
||||
}
|
||||
|
||||
// get connections
|
||||
for (var i in list) {
|
||||
var neuron = list[i].neuron;
|
||||
@@ -391,8 +375,7 @@ Network.prototype = {
|
||||
from: ids[neuron.ID],
|
||||
to: ids[neuron.ID],
|
||||
weight: neuron.selfconnection.weight,
|
||||
gater: neuron.selfconnection.gater ? ids[neuron.selfconnection.gater
|
||||
.ID] : null,
|
||||
gater: neuron.selfconnection.gater ? ids[neuron.selfconnection.gater.ID] : null,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -407,31 +390,31 @@ Network.prototype = {
|
||||
$ node example.js > example.dot
|
||||
$ dot example.dot -Tpng > out.png
|
||||
*/
|
||||
toDot: function(edgeconnection) {
|
||||
if (! typeof edgeconnection)
|
||||
edgeconnection = false;
|
||||
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;
|
||||
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);
|
||||
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 ( edgeConnection) {
|
||||
if (connection.gatedfrom.length) {
|
||||
var fakeNode = "fake" + layerID + "_" + layertoID;
|
||||
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";
|
||||
code += " " + fakeNode + " -> " + layerToID + "\n";
|
||||
} else
|
||||
code += " " + layerID + " -> " + layertoID + " [label = " + size + "]\n";
|
||||
code += " " + layerID + " -> " + layerToID + " [label = " + size + "]\n";
|
||||
for (var from in connection.gatedfrom) { // gatings
|
||||
var layerfrom = connection.gatedfrom[from].layer;
|
||||
var type = connection.gatedfrom[from].type;
|
||||
@@ -439,12 +422,12 @@ Network.prototype = {
|
||||
code += " " + layerfromID + " -> " + fakeNode + " [color = blue]\n";
|
||||
}
|
||||
} else {
|
||||
code += " " + layerID + " -> " + layertoID + " [label = " + size + "]\n";
|
||||
code += " " + layerID + " -> " + layerToID + " [label = " + size + "]\n";
|
||||
for (var from in connection.gatedfrom) { // gatings
|
||||
var layerfrom = connection.gatedfrom[from].layer;
|
||||
var type = connection.gatedfrom[from].type;
|
||||
var layerfromID = layers.indexOf(layerfrom);
|
||||
code += " " + layerfromID + " -> " + layertoID + " [color = blue]\n";
|
||||
code += " " + layerfromID + " -> " + layerToID + " [color = blue]\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -538,12 +521,12 @@ Network.prototype = {
|
||||
},
|
||||
|
||||
// returns a copy of the network
|
||||
clone: function(ignoreTraces) {
|
||||
return Network.fromJSON(this.toJSON(ignoreTraces));
|
||||
clone: function() {
|
||||
return Network.fromJSON(this.toJSON());
|
||||
}
|
||||
}
|
||||
|
||||
// rebuild a network that has been stored in a json using the method toJson()
|
||||
// rebuild a network that has been stored in a json using the method toJSON()
|
||||
Network.fromJSON = function(json) {
|
||||
|
||||
var neurons = [];
|
||||
@@ -558,14 +541,13 @@ Network.fromJSON = function(json) {
|
||||
var config = json.neurons[i];
|
||||
|
||||
var neuron = new Neuron();
|
||||
neuron.trace.elegibility = config.trace.elegibility;
|
||||
neuron.trace.extended = config.trace.extended;
|
||||
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;
|
||||
neuron.squash = config.squash in Neuron.squash ? Neuron.squash[config.squash] : Neuron.squash.LOGISTIC;
|
||||
neurons.push(neuron);
|
||||
|
||||
if (config.layer == 'input')
|
||||
|
||||
+8
-14
@@ -465,14 +465,13 @@ Neuron.prototype = {
|
||||
buildSentence(derivative, ' = 1', store_activation);
|
||||
break;
|
||||
}
|
||||
|
||||
influences = [];
|
||||
|
||||
for (var id in this.trace.extended) {
|
||||
// calculate extended elegibility traces in advance
|
||||
|
||||
var xtrace = this.trace.extended[id];
|
||||
var neuron = this.neighboors[id];
|
||||
var influence = getVar('aux');
|
||||
var influence = getVar('influences[' + neuron.ID + ']');
|
||||
var neuron_old = getVar(neuron, 'old');
|
||||
var initialized = false;
|
||||
if (neuron.selfconnection.gater == this)
|
||||
@@ -487,17 +486,12 @@ Neuron.prototype = {
|
||||
[incoming].from, 'activation');
|
||||
|
||||
if (initialized)
|
||||
buildSentence(influence, ' += ', incoming_weight, ' * ',
|
||||
incoming_activation, store_trace);
|
||||
buildSentence(influence, ' += ', incoming_weight, ' * ', incoming_activation, store_trace);
|
||||
else {
|
||||
buildSentence(influence, ' = ', incoming_weight, ' * ',
|
||||
incoming_activation, store_trace);
|
||||
buildSentence(influence, ' = ', incoming_weight, ' * ', incoming_activation, store_trace);
|
||||
initialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
influences.push(neuron.ID);
|
||||
buildSentence("influences[" + (influences.length - 1) + "] = ", influence, store_trace);
|
||||
}
|
||||
|
||||
for (var i in this.connections.inputs) {
|
||||
@@ -535,7 +529,7 @@ Neuron.prototype = {
|
||||
// extended elegibility trace
|
||||
var xtrace = this.trace.extended[id];
|
||||
var neuron = this.neighboors[id];
|
||||
var influence = getVar('aux');
|
||||
var influence = getVar('influences[' + neuron.ID + ']');
|
||||
var neuron_old = getVar(neuron, 'old');
|
||||
|
||||
var trace = getVar(this, 'trace', 'elegibility', input.ID, this.trace
|
||||
@@ -550,14 +544,14 @@ Neuron.prototype = {
|
||||
if (neuron.selfconnection.gater)
|
||||
buildSentence(xtrace, ' = ', neuron_self_gain, ' * ',
|
||||
neuron_self_weight, ' * ', xtrace, ' + ', derivative, ' * ',
|
||||
trace, ' * ', "influences[" + influences.indexOf(neuron.ID) + "]", store_trace);
|
||||
trace, ' * ', influence, store_trace);
|
||||
else
|
||||
buildSentence(xtrace, ' = ', neuron_self_weight, ' * ',
|
||||
xtrace, ' + ', derivative, ' * ', trace, ' * ',
|
||||
"influences[" + influences.indexOf(neuron.ID) + "]", store_trace);
|
||||
influence, store_trace);
|
||||
else
|
||||
buildSentence(xtrace, ' = ', derivative, ' * ', trace, ' * ',
|
||||
"influences[" + influences.indexOf(neuron.ID) + "]", store_trace);
|
||||
influence, store_trace);
|
||||
}
|
||||
}
|
||||
for (var connection in this.connections.gated) {
|
||||
|
||||
+137
-33
@@ -8,7 +8,7 @@ function Trainer(network, options) {
|
||||
this.rate = options.rate || .2;
|
||||
this.iterations = options.iterations || 100000;
|
||||
this.error = options.error || .005
|
||||
this.cost = options.cost || Trainer.cost.CROSS_ENTROPY;
|
||||
this.cost = options.cost || null;
|
||||
}
|
||||
|
||||
Trainer.prototype = {
|
||||
@@ -18,8 +18,9 @@ Trainer.prototype = {
|
||||
|
||||
var error = 1;
|
||||
var iterations = bucketSize = 0;
|
||||
var abort_training = false;
|
||||
var abort = false;
|
||||
var input, output, target, currentRate;
|
||||
var cost = options && options.cost || this.cost || Trainer.cost.MSE;
|
||||
|
||||
var start = Date.now();
|
||||
|
||||
@@ -55,12 +56,12 @@ Trainer.prototype = {
|
||||
}
|
||||
|
||||
|
||||
while (!abort_training && iterations < this.iterations && error > this.error) {
|
||||
while (!abort && iterations < this.iterations && error > this.error) {
|
||||
error = 0;
|
||||
|
||||
if(bucketSize > 0) {
|
||||
var currentBucket = Math.floor(iterations / bucketSize);
|
||||
currentRate = this.rate[currentBucket];
|
||||
currentRate = this.rate[currentBucket] || currentRate;
|
||||
}
|
||||
|
||||
for (var train in set) {
|
||||
@@ -70,7 +71,7 @@ Trainer.prototype = {
|
||||
output = this.network.activate(input);
|
||||
this.network.propagate(currentRate, target);
|
||||
|
||||
error += this.cost(target, output);
|
||||
error += cost(target, output);
|
||||
}
|
||||
|
||||
// check error
|
||||
@@ -80,7 +81,7 @@ Trainer.prototype = {
|
||||
if (options) {
|
||||
if (this.schedule && this.schedule.every && iterations %
|
||||
this.schedule.every == 0)
|
||||
abort_training = this.schedule.do({
|
||||
abort = this.schedule.do({
|
||||
error: error,
|
||||
iterations: iterations,
|
||||
rate: currentRate
|
||||
@@ -102,6 +103,33 @@ Trainer.prototype = {
|
||||
return results;
|
||||
},
|
||||
|
||||
// tests a set and returns the error and elapsed time
|
||||
test: function(set, options){
|
||||
|
||||
var error = 0;
|
||||
var abort = false;
|
||||
var input, output, target;
|
||||
var cost = options && options.cost || this.cost || Trainer.cost.MSE;
|
||||
|
||||
var start = Date.now();
|
||||
|
||||
for (var test in set) {
|
||||
input = set[test].input;
|
||||
target = set[test].output;
|
||||
output = this.network.activate(input);
|
||||
error += cost(target, output);
|
||||
}
|
||||
|
||||
error /= set.length;
|
||||
|
||||
var results = {
|
||||
error: error,
|
||||
time: Date.now() - start
|
||||
}
|
||||
|
||||
return results;
|
||||
},
|
||||
|
||||
// trains any given set to a network using a WebWorker
|
||||
workerTrain: function(set, callback, options) {
|
||||
|
||||
@@ -110,7 +138,8 @@ Trainer.prototype = {
|
||||
var iterations = bucketSize = 0;
|
||||
var input, output, target, currentRate;
|
||||
var length = set.length;
|
||||
var abort_training = false;
|
||||
var abort = false;
|
||||
var cost = options && options.cost || that.cost || Trainer.cost.MSE;
|
||||
|
||||
var start = Date.now();
|
||||
|
||||
@@ -125,29 +154,31 @@ Trainer.prototype = {
|
||||
};
|
||||
}
|
||||
if (options.iterations)
|
||||
this.iterations = options.iterations;
|
||||
that.iterations = options.iterations;
|
||||
if (options.error)
|
||||
this.error = options.error;
|
||||
that.error = options.error;
|
||||
if (options.rate)
|
||||
this.rate = options.rate;
|
||||
that.rate = options.rate;
|
||||
if (options.cost)
|
||||
this.cost = options.cost;
|
||||
that.cost = options.cost;
|
||||
if (options.schedule)
|
||||
this.schedule = options.schedule;
|
||||
that.schedule = options.schedule;
|
||||
if (options.customLog)
|
||||
{
|
||||
// for backward compatibility with code that used customLog
|
||||
console.log('Deprecated: use schedule instead of customLog')
|
||||
this.schedule = options.customLog;
|
||||
that.schedule = options.customLog;
|
||||
}
|
||||
}
|
||||
|
||||
// dynamic learning rate
|
||||
currentRate = this.rate;
|
||||
if(Array.isArray(this.rate)) {
|
||||
bucketSize = Math.floor(this.iterations / this.rate.length);
|
||||
currentRate = that.rate;
|
||||
if(Array.isArray(that.rate)) {
|
||||
bucketSize = Math.floor(that.iterations / that.rate.length);
|
||||
}
|
||||
|
||||
// create a worker
|
||||
var worker = this.network.worker();
|
||||
var worker = that.network.worker();
|
||||
|
||||
// activate the network
|
||||
function activateWorker(input)
|
||||
@@ -163,7 +194,7 @@ Trainer.prototype = {
|
||||
function propagateWorker(target){
|
||||
if(bucketSize > 0) {
|
||||
var currentBucket = Math.floor(iterations / bucketSize);
|
||||
currentRate = this.rate[currentBucket];
|
||||
currentRate = that.rate[currentBucket] || currentRate;
|
||||
}
|
||||
worker.postMessage({
|
||||
action: "propagate",
|
||||
@@ -188,10 +219,11 @@ Trainer.prototype = {
|
||||
|
||||
// log
|
||||
if (options) {
|
||||
if (this.schedule && this.schedule.every && iterations % this.schedule.every == 0)
|
||||
abort_training = this.schedule.do({
|
||||
if (that.schedule && that.schedule.every && iterations % that.schedule.every == 0)
|
||||
abort = that.schedule.do({
|
||||
error: error,
|
||||
iterations: iterations
|
||||
iterations: iterations,
|
||||
rate: currentRate
|
||||
});
|
||||
else if (options.log && iterations % options.log == 0) {
|
||||
console.log('iterations', iterations, 'error', error);
|
||||
@@ -200,7 +232,7 @@ Trainer.prototype = {
|
||||
shuffle(set);
|
||||
}
|
||||
|
||||
if (!abort_training && iterations < that.iterations && error > that.error)
|
||||
if (!abort && iterations < that.iterations && error > that.error)
|
||||
{
|
||||
activateWorker(set[index].input);
|
||||
} else {
|
||||
@@ -219,7 +251,7 @@ Trainer.prototype = {
|
||||
|
||||
if (e.data.action == "activate")
|
||||
{
|
||||
error += that.cost(set[index].output, e.data.output);
|
||||
error += cost(set[index].output, e.data.output);
|
||||
propagateWorker(set[index].output);
|
||||
index++;
|
||||
}
|
||||
@@ -276,6 +308,7 @@ Trainer.prototype = {
|
||||
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 = 0,
|
||||
error = 1,
|
||||
@@ -351,10 +384,7 @@ Trainer.prototype = {
|
||||
this.network.propagate(rate, output);
|
||||
}
|
||||
|
||||
var delta = 0;
|
||||
for (var j in prediction)
|
||||
delta += Math.pow(output[j] - prediction[j], 2);
|
||||
error += delta / this.network.outputs();
|
||||
error += cost(output, prediction);
|
||||
|
||||
if (distractorsCorrect + targetsCorrect == length)
|
||||
correct++;
|
||||
@@ -399,6 +429,7 @@ Trainer.prototype = {
|
||||
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;
|
||||
|
||||
// gramar node
|
||||
var Node = function() {
|
||||
@@ -557,12 +588,7 @@ Trainer.prototype = {
|
||||
read = sequence.charAt(++i);
|
||||
predict = sequence.charAt(i + 1);
|
||||
|
||||
var delta = 0;
|
||||
for (var k in output)
|
||||
delta += Math.pow(target[k] - output[k], 2)
|
||||
delta /= output.length;
|
||||
|
||||
error += delta;
|
||||
error += cost(target, output);
|
||||
}
|
||||
error /= sequence.length;
|
||||
iteration++;
|
||||
@@ -579,6 +605,78 @@ Trainer.prototype = {
|
||||
test: test,
|
||||
generate: generate
|
||||
}
|
||||
},
|
||||
|
||||
timingTask: function(options){
|
||||
|
||||
if (this.network.inputs() != 2 || this.network.outputs() != 1)
|
||||
throw "Invalid Network: must have 2 inputs and one output";
|
||||
|
||||
if (typeof options == 'undefined')
|
||||
var options = {};
|
||||
|
||||
// helper
|
||||
function getSamples (trainingSize, testSize){
|
||||
|
||||
// sample size
|
||||
var size = trainingSize + testSize;
|
||||
|
||||
// generate samples
|
||||
var t = 0;
|
||||
var set = [];
|
||||
for (var i = 0; i < size; i++) {
|
||||
set.push({ input: [0,0], output: [0] });
|
||||
}
|
||||
while(t < size - 20) {
|
||||
var n = Math.round(Math.random() * 20);
|
||||
set[t].input[0] = 1;
|
||||
for (var 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++)
|
||||
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++)
|
||||
(l < trainingSize ? trainingSet : testSet).push(set[l]);
|
||||
|
||||
// return samples
|
||||
return {
|
||||
train: trainingSet,
|
||||
test: testSet
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
// samples for training and testing
|
||||
var samples = getSamples(trainingSamples, testSamples);
|
||||
|
||||
// train
|
||||
var result = this.train(samples.train, {
|
||||
rate: rate,
|
||||
log: log,
|
||||
iterations: iterations,
|
||||
error: error,
|
||||
cost: cost
|
||||
});
|
||||
|
||||
return {
|
||||
train: result,
|
||||
test: this.test(samples.test)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -598,6 +696,12 @@ Trainer.cost = {
|
||||
for (var 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)
|
||||
misses += Math.round(target[i] * 2) != Math.round(output[i] * 2);
|
||||
return misses;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+53
-50
@@ -12,29 +12,39 @@ var Perceptron = synaptic.Architect.Perceptron,
|
||||
|
||||
// utils
|
||||
|
||||
var noRepeat = function(range, avoid) {
|
||||
function noRepeat (range, avoid) {
|
||||
var number = Math.random() * range | 0;
|
||||
var used = false;
|
||||
for (var i in avoid)
|
||||
if (number == avoid[i])
|
||||
used = true;
|
||||
return used ? noRepeat(range, avoid) : number;
|
||||
for (var i in avoid){
|
||||
if (number == avoid[i]){
|
||||
return noRepeat(range,avoid);
|
||||
}
|
||||
}
|
||||
return number;
|
||||
};
|
||||
|
||||
var equal = function(prediction, output) {
|
||||
function equal (prediction, output) {
|
||||
for (var i in prediction)
|
||||
if (Math.round(prediction[i]) != output[i])
|
||||
return false;
|
||||
return true;
|
||||
};
|
||||
|
||||
var generateRandomArray = function(size){
|
||||
function generateRandomArray (size){
|
||||
var array = [];
|
||||
for (var j = 0; j < size; j++)
|
||||
array.push(Math.random() + .5 | 0);
|
||||
return array;
|
||||
}
|
||||
|
||||
function compare (a, b) {
|
||||
var mse = 0;
|
||||
for (var k in a)
|
||||
mse += Math.pow(a[k] - b[k], 2);
|
||||
mse /= a.length;
|
||||
|
||||
return mse < 1e-10;
|
||||
}
|
||||
|
||||
// specs
|
||||
|
||||
describe('Basic Neural Network', function() {
|
||||
@@ -84,7 +94,7 @@ describe('Basic Neural Network', function() {
|
||||
var test11 = Math.round(network.activate([1, 1]));
|
||||
assert.equal(test11, 1, "[1,1] did not output 1");
|
||||
});
|
||||
|
||||
|
||||
it("trains an OR gate", function() {
|
||||
|
||||
var inputLayer = new Layer(2),
|
||||
@@ -293,8 +303,25 @@ describe("LSTM - Discrete Sequence Recall", function() {
|
||||
}
|
||||
});
|
||||
|
||||
describe("LSTM - Timing Task", function() {
|
||||
var network = new synaptic.Architect.LSTM(2,7,1);
|
||||
var result = network.trainer.timingTask({
|
||||
log: false,
|
||||
trainSamples: 4000,
|
||||
testSamples: 500
|
||||
});
|
||||
|
||||
it("should complete the training in less than 200 iterations", function() {
|
||||
assert(result.train.iterations <= 200);
|
||||
});
|
||||
|
||||
it("should pass the test with an error smaller than 0.05", function() {
|
||||
assert(result.test.error < .05);
|
||||
});
|
||||
});
|
||||
|
||||
describe("Optimized and Unoptimized Networks Equivalency", function() {
|
||||
var optimized = new Perceptron(10,15,5);
|
||||
var optimized = new LSTM(2,1,1)
|
||||
|
||||
var unoptimized = optimized.clone();
|
||||
unoptimized.setOptimize(false);
|
||||
@@ -305,23 +332,19 @@ describe("Optimized and Unoptimized Networks Equivalency", function() {
|
||||
for (var i = 1; i <= iterations; i++)
|
||||
{
|
||||
//random input
|
||||
var input = generateRandomArray(10);
|
||||
var input = generateRandomArray(2);
|
||||
|
||||
// activate networks
|
||||
var output1 = optimized.activate(input);
|
||||
var output2 = unoptimized.activate(input);
|
||||
|
||||
if (i % 100 == 0)
|
||||
it(' same output for both networks after ' + i + ' iterations', function(){
|
||||
var diff = false;
|
||||
for (var k in output1)
|
||||
if (output1[k] - output2[k] != 0)
|
||||
diff = true;
|
||||
assert(!diff);
|
||||
it('should produce the same output for both networks after ' + i + ' iterations', function(){
|
||||
assert(compare(output1, output2));
|
||||
});
|
||||
|
||||
// random target
|
||||
var target = generateRandomArray(5);
|
||||
var target = generateRandomArray(1);
|
||||
|
||||
// propagate networks
|
||||
optimized.propagate(learningRate, target);
|
||||
@@ -330,7 +353,7 @@ describe("Optimized and Unoptimized Networks Equivalency", function() {
|
||||
});
|
||||
|
||||
describe("toJSON/fromJSON Networks Equivalency", function() {
|
||||
var original = new Perceptron(10,15,5);
|
||||
var original = new LSTM(10,5,5);
|
||||
|
||||
var exported = original.toJSON();
|
||||
var imported = Network.fromJSON(exported);
|
||||
@@ -348,12 +371,8 @@ describe("toJSON/fromJSON Networks Equivalency", function() {
|
||||
var output2 = imported.activate(input);
|
||||
|
||||
if (i % 100 == 0)
|
||||
it(' same output for both networks after ' + i + ' iterations', function(){
|
||||
var diff = false;
|
||||
for (var k in output1)
|
||||
if (output1[k] - output2[k] != 0)
|
||||
diff = true;
|
||||
assert(!diff);
|
||||
it('should produce the same output for both networks after ' + i + ' iterations', function(){
|
||||
assert(compare(output1, output2));
|
||||
});
|
||||
|
||||
// random target
|
||||
@@ -366,7 +385,9 @@ describe("toJSON/fromJSON Networks Equivalency", function() {
|
||||
});
|
||||
|
||||
describe("Cloned Networks Equivalency", function() {
|
||||
var original = new Perceptron(10,15,5);
|
||||
|
||||
var original = new LSTM(10,5,5);
|
||||
|
||||
var cloned = original.clone();
|
||||
|
||||
var learningRate = .5;
|
||||
@@ -382,12 +403,8 @@ describe("Cloned Networks Equivalency", function() {
|
||||
var output2 = cloned.activate(input);
|
||||
|
||||
if (i % 100 == 0)
|
||||
it(' same output for both networks after ' + i + ' iterations', function(){
|
||||
var diff = false;
|
||||
for (var k in output1)
|
||||
if (output1[k] - output2[k] != 0)
|
||||
diff = true;
|
||||
assert(!diff);
|
||||
it('should produce the same output for both networks after ' + i + ' iterations', function(){
|
||||
assert(compare(output1, output2));
|
||||
});
|
||||
|
||||
// random target
|
||||
@@ -399,10 +416,10 @@ describe("Cloned Networks Equivalency", function() {
|
||||
}
|
||||
});
|
||||
|
||||
describe("Manual Override", function() {
|
||||
describe("Scheduled Tasks", function() {
|
||||
var perceptron = new Perceptron(2, 3, 1);
|
||||
|
||||
it('iterations ended at full 3000', function(){
|
||||
it('should stop training at 3000 iterations', function(){
|
||||
var final_stats = perceptron.trainer.XOR({
|
||||
iterations: 3000,
|
||||
rate: 0.000001,
|
||||
@@ -419,7 +436,7 @@ describe("Manual Override", function() {
|
||||
assert.equal( final_stats.iterations, 3000 )
|
||||
});
|
||||
|
||||
it('iterations ended at 2000, not full 3000', function(){
|
||||
it('should abort the training at 2000 iterations', function(){
|
||||
var final_stats = perceptron.trainer.XOR({
|
||||
iterations: 3000,
|
||||
rate: 0.000001,
|
||||
@@ -436,7 +453,7 @@ describe("Manual Override", function() {
|
||||
assert.equal( final_stats.iterations, 2000 )
|
||||
});
|
||||
|
||||
it('training works even when schedule() has no return value', function(){
|
||||
it('should work even if shedule.do() returns no value', function(){
|
||||
var final_stats = perceptron.trainer.XOR({
|
||||
iterations: 3000,
|
||||
rate: 0.000001,
|
||||
@@ -449,18 +466,4 @@ describe("Manual Override", function() {
|
||||
assert.equal( final_stats.iterations, 3000 )
|
||||
});
|
||||
|
||||
it('using depreciated customLog still works', function(){
|
||||
var counter = 0
|
||||
var final_stats = perceptron.trainer.XOR({
|
||||
iterations: 3000,
|
||||
rate: 0.000001,
|
||||
error: 0.000001,
|
||||
customLog: {
|
||||
every: 1000,
|
||||
do: function(data) { counter++ }
|
||||
}
|
||||
});
|
||||
assert.equal( counter, 3 )
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>test</title>
|
||||
<script src="../dist/synaptic.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
</body>
|
||||
<script type="text/javascript">
|
||||
var Architect = synaptic.Architect;
|
||||
|
||||
var myPerceptron = new Architect.Perceptron(2,3,1);
|
||||
document.body.innerHTML += JSON.stringify(myPerceptron.trainer.XOR())+'<br>'
|
||||
document.body.innerHTML += JSON.stringify(myPerceptron.trainer.XOR())+'<br>'
|
||||
|
||||
if(document.body.innerHTML.length > 0){
|
||||
document.body.innerHTML += '<br>if you see this, then synaptic works'
|
||||
}
|
||||
|
||||
</script>
|
||||
</html>
|
||||
Referência em uma Nova Issue
Bloquear um usuário