1
Fork 0
solar-conflux/javascript/clever-dots/synaptic.min.js

2848 lines
91 KiB
JavaScript
Raw Permalink Normal View History

/*!
* The MIT License (MIT)
*
* Copyright (c) 2016 Juan Cazala - juancazala.com
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE
*
*
*
* ********************************************************************************************
* SYNAPTIC (v1.0.8)
* ********************************************************************************************
*
* Synaptic is a javascript neural network library for node.js and the browser, its generalized
* algorithm is architecture-free, so you can build and train basically any type of first order
* or even second order neural network architectures.
*
* http://en.wikipedia.org/wiki/Recurrent_neural_network#Second_Order_Recurrent_Neural_Network
*
* The library includes a few built-in architectures like multilayer perceptrons, multilayer
* long-short term memory networks (LSTM) or liquid state machines, and a trainer capable of
* training any given network, and includes built-in training tasks/tests like solving an XOR,
* passing a Distracted Sequence Recall test or an Embeded Reber Grammar test.
*
* The algorithm implemented by this library has been taken from Derek D. Monner's paper:
*
*
* A generalized LSTM-like training algorithm for second-order recurrent neural networks
* http://www.overcomplete.net/papers/nn2012.pdf
*
* There are references to the equations in that paper commented through the source code.
*
*/
/******/ (function(modules) { // webpackBootstrap
/******/ // The module cache
/******/ var installedModules = {};
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/ // Check if module is in cache
/******/ if(installedModules[moduleId])
/******/ return installedModules[moduleId].exports;
/******/ // Create a new module (and put it into the cache)
/******/ var module = installedModules[moduleId] = {
/******/ exports: {},
/******/ id: moduleId,
/******/ loaded: false
/******/ };
/******/ // Execute the module function
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/ // Flag the module as loaded
/******/ module.loaded = true;
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/ // expose the modules object (__webpack_modules__)
/******/ __webpack_require__.m = modules;
/******/ // expose the module cache
/******/ __webpack_require__.c = installedModules;
/******/ // __webpack_public_path__
/******/ __webpack_require__.p = "";
/******/ // Load entry module and return exports
/******/ return __webpack_require__(0);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ function(module, exports, __webpack_require__) {
var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;var Synaptic = {
Neuron: __webpack_require__(1),
Layer: __webpack_require__(3),
Network: __webpack_require__(4),
Trainer: __webpack_require__(5),
Architect: __webpack_require__(6)
};
// CommonJS & AMD
if (true)
{
!(__WEBPACK_AMD_DEFINE_ARRAY__ = [], __WEBPACK_AMD_DEFINE_RESULT__ = function(){ return Synaptic }.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));
}
// Node.js
if (typeof module !== 'undefined' && module.exports)
{
module.exports = Synaptic;
}
// Browser
if (typeof window == 'object')
{
(function(){
var oldSynaptic = window['synaptic'];
Synaptic.ninja = function(){
window['synaptic'] = oldSynaptic;
return Synaptic;
};
})();
window['synaptic'] = Synaptic;
}
/***/ },
/* 1 */
/***/ function(module, exports, __webpack_require__) {
/* WEBPACK VAR INJECTION */(function(module) {// export
if (module) module.exports = 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 = {
// activate the neuron
activate: function(input) {
// activation from enviroment (for input neurons)
if (typeof input != 'undefined') {
this.activation = input;
this.derivative = 0;
this.bias = 0;
return this.activation;
}
// old state
this.old = this.state;
// eq. 15
this.state = this.selfconnection.gain * this.selfconnection.weight *
this.state + this.bias;
for (var i in this.connections.inputs) {
var input = this.connections.inputs[i];
this.state += input.from.activation * input.weight * input.gain;
}
// eq. 16
this.activation = this.squash(this.state);
// f'(s)
this.derivative = this.squash(this.state, true);
// update traces
var influences = [];
for (var id in this.trace.extended) {
// extended elegibility trace
var neuron = this.neighboors[id];
// if gated neuron's selfconnection is gated by this unit, the influence keeps track of the neuron's old state
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
influence += this.trace.influences[neuron.ID][incoming].weight *
this.trace.influences[neuron.ID][incoming].from.activation;
}
influences[neuron.ID] = influence;
}
for (var i in this.connections.inputs) {
var input = this.connections.inputs[i];
// 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;
for (var id in this.trace.extended) {
// extended elegibility trace
var 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[
input.ID] * influence;
}
}
// update gated connection's gains
for (var connection in this.connections.gated) {
this.connections.gated[connection].gain = this.activation;
}
return this.activation;
},
// back-propagate the error
propagate: function(rate, target) {
// error accumulator
var error = 0;
// whether or not this neuron is in the output layer
var isOutput = typeof target != 'undefined';
// output neurons get their error from the enviroment
if (isOutput)
this.error.responsibility = this.error.projected = target - this.activation; // Eq. 10
else // the rest of the neuron compute their error responsibilities by backpropagation
{
// error responsibilities from all the connections projected from this neuron
for (var id in this.connections.projected) {
var connection = this.connections.projected[id];
var neuron = connection.to;
// Eq. 21
error += neuron.error.responsibility * connection.gain * connection.weight;
}
// projected error responsibility
this.error.projected = this.derivative * error;
error = 0;
// 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
// 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;
}
// eq. 22
error += neuron.error.responsibility * influence;
}
// gated error responsibility
this.error.gated = this.derivative * error;
// error responsibility - Eq. 23
this.error.responsibility = this.error.projected + this.error.gated;
}
// learning rate
rate = rate || .1;
// adjust all the neuron's incoming connections
for (var id in this.connections.inputs) {
var input = this.connections.inputs[id];
// Eq. 24
var 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];
}
input.weight += rate * gradient; // adjust weights - aka learn
}
// adjust bias
this.bias += rate * this.error.responsibility;
},
project: function(neuron, weight) {
// self-connection
if (neuron == this) {
this.selfconnection.weight = 1;
return this.selfconnection;
}
// check if connection already exists
var connected = this.connected(neuron);
if (connected && connected.type == "projected") {
// update connection
if (typeof weight != 'undefined')
connected.connection.weight = weight;
// return existing connection
return connected.connection;
} else {
// create a new connection
var connection = new Neuron.connection(this, neuron, weight);
}
// reference all the connections and traces
this.connections.projected[connection.ID] = connection;
this.neighboors[neuron.ID] = neuron;
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];
trace[connection.ID] = 0;
}
return connection;
},
gate: function(connection) {
// add connection to gated list
this.connections.gated[connection.ID] = connection;
var 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];
xtrace[input.ID] = 0;
}
}
// keep track
if (neuron.ID in this.trace.influences)
this.trace.influences[neuron.ID].push(connection);
else
this.trace.influences[neuron.ID] = [connection];
// set gater
connection.gater = this;
},
// returns true or false whether the neuron is self-connected or not
selfconnected: function() {
return this.selfconnection.weight !== 0;
},
// returns true or false whether the neuron is connected to another neuron (parameter)
connected: function(neuron) {
var result = {
type: null,
connection: false
};
if (this == neuron) {
if (this.selfconnected()) {
result.type = 'selfconnection';
result.connection = this.selfconnection;
return result;
} else
return false;
}
for (var type in this.connections) {
for (var connection in this.connections[type]) {
var connection = this.connections[type][connection];
if (connection.to == neuron) {
result.type = type;
result.connection = connection;
return result;
} else if (connection.from == neuron) {
result.type = type;
result.connection = connection;
return result;
}
}
}
return false;
},
// clears all the traces (the neuron forgets it's context, but the connections remain intact)
clear: function() {
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])
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() {
this.clear();
for (var type in this.connections)
for (var 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 };
// allocate sentences
var allocate = function(store){
var allocated = layer in layers && store[layers.__count];
if (!allocated)
{
layers.__count = store.push([]) - 1;
layers[layer] = layers.__count;
}
};
allocate(activation_sentences);
allocate(trace_sentences);
allocate(propagation_sentences);
var 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);
if (args.length == 1) {
if (args[0] == 'target') {
var id = 'target_' + targets.length;
targets.push(varID);
} else
var id = args[0];
if (id in variables)
return variables[id];
return variables[id] = {
value: 0,
id: varID++
};
} else {
var extended = args.length > 2;
if (extended)
var value = args.pop();
var unit = args.shift();
var prop = args.pop();
if (!extended)
var value = unit[prop];
var id = prop + '_';
for (var property in args)
id += args[property] + '_';
id += unit.ID;
if (id in variables)
return variables[id];
return variables[id] = {
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)
if (typeof args[i] == 'string')
sentence += args[i];
else
sentence += 'F[' + args[i].id + ']';
store.push(sentence + ';');
};
// helper to check if an object is empty
var isEmpty = function(obj) {
for (var prop in obj) {
if (obj.hasOwnProperty(prop))
return false;
}
return true;
};
// 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;
// optimize neuron's behaviour
var rate = getVar('rate');
var 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');
var bias = getVar(this, 'bias');
if (this.selfconnection.gater)
var self_gain = getVar(this.selfconnection, 'gain');
if (this.selfconnected())
var self_weight = getVar(this.selfconnection, 'weight');
buildSentence(old, ' = ', state, store_activation);
if (this.selfconnected())
if (this.selfconnection.gater)
buildSentence(state, ' = ', self_gain, ' * ', self_weight, ' * ',
state, ' + ', bias, store_activation);
else
buildSentence(state, ' = ', self_weight, ' * ', state, ' + ',
bias, store_activation);
else
buildSentence(state, ' = ', bias, store_activation);
for (var i in this.connections.inputs) {
var input = this.connections.inputs[i];
var input_activation = getVar(input.from, 'activation');
var input_weight = getVar(input, 'weight');
if (input.gater)
var input_gain = getVar(input, 'gain');
if (this.connections.inputs[i].gater)
buildSentence(state, ' += ', input_activation, ' * ',
input_weight, ' * ', input_gain, store_activation);
else
buildSentence(state, ' += ', input_activation, ' * ',
input_weight, store_activation);
}
var derivative = getVar(this, 'derivative');
switch (this.squash) {
case Neuron.squash.LOGISTIC:
buildSentence(activation, ' = (1 / (1 + Math.exp(-', state, ')))',
store_activation);
buildSentence(derivative, ' = ', activation, ' * (1 - ',
activation, ')', store_activation);
break;
case Neuron.squash.TANH:
var eP = getVar('aux');
var 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);
buildSentence(derivative, ' = 1 - (', activation, ' * ', activation, ')', store_activation);
break;
case Neuron.squash.IDENTITY:
buildSentence(activation, ' = ', state, store_activation);
buildSentence(derivative, ' = 1', store_activation);
break;
case Neuron.squash.HLIM:
buildSentence(activation, ' = +(', state, ' > 0)', store_activation);
buildSentence(derivative, ' = 1', store_activation);
case Neuron.squash.RELU:
buildSentence(activation, ' = ', state, ' > 0 ? ', state, ' : 0', store_activation);
buildSentence(derivative, ' = ', state, ' > 0 ? 1 : 0', store_activation);
break;
}
for (var id in this.trace.extended) {
// calculate extended elegibility traces in advance
var neuron = this.neighboors[id];
var influence = getVar('influences[' + neuron.ID + ']');
var neuron_old = getVar(neuron, 'old');
var 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]
[incoming], 'weight');
var incoming_activation = getVar(this.trace.influences[neuron.ID]
[incoming].from, 'activation');
if (initialized)
buildSentence(influence, ' += ', incoming_weight, ' * ', incoming_activation, store_trace);
else {
buildSentence(influence, ' = ', incoming_weight, ' * ', incoming_activation, store_trace);
initialized = true;
}
}
}
for (var i in this.connections.inputs) {
var input = this.connections.inputs[i];
if (input.gater)
var input_gain = getVar(input, 'gain');
var input_activation = getVar(input.from, 'activation');
var trace = getVar(this, 'trace', 'elegibility', input.ID, this.trace
.elegibility[input.ID]);
if (this.selfconnected()) {
if (this.selfconnection.gater) {
if (input.gater)
buildSentence(trace, ' = ', self_gain, ' * ', self_weight,
' * ', trace, ' + ', input_gain, ' * ', input_activation,
store_trace);
else
buildSentence(trace, ' = ', self_gain, ' * ', self_weight,
' * ', trace, ' + ', input_activation, store_trace);
} else {
if (input.gater)
buildSentence(trace, ' = ', self_weight, ' * ', trace, ' + ',
input_gain, ' * ', input_activation, store_trace);
else
buildSentence(trace, ' = ', self_weight, ' * ', trace, ' + ',
input_activation, store_trace);
}
} else {
if (input.gater)
buildSentence(trace, ' = ', input_gain, ' * ', input_activation,
store_trace);
else
buildSentence(trace, ' = ', input_activation, store_trace);
}
for (var id in this.trace.extended) {
// extended elegibility trace
var neuron = this.neighboors[id];
var influence = getVar('influences[' + neuron.ID + ']');
var trace = getVar(this, 'trace', 'elegibility', input.ID, this.trace
.elegibility[input.ID]);
var xtrace = getVar(this, 'trace', 'extended', neuron.ID, input.ID,
this.trace.extended[neuron.ID][input.ID]);
if (neuron.selfconnected())
var neuron_self_weight = getVar(neuron.selfconnection, 'weight');
if (neuron.selfconnection.gater)
var neuron_self_gain = getVar(neuron.selfconnection, 'gain');
if (neuron.selfconnected())
if (neuron.selfconnection.gater)
buildSentence(xtrace, ' = ', neuron_self_gain, ' * ',
neuron_self_weight, ' * ', xtrace, ' + ', derivative, ' * ',
trace, ' * ', influence, store_trace);
else
buildSentence(xtrace, ' = ', neuron_self_weight, ' * ',
xtrace, ' + ', derivative, ' * ', trace, ' * ',
influence, store_trace);
else
buildSentence(xtrace, ' = ', derivative, ' * ', trace, ' * ',
influence, store_trace);
}
}
for (var connection in this.connections.gated) {
var gated_gain = getVar(this.connections.gated[connection], 'gain');
buildSentence(gated_gain, ' = ', activation, store_activation);
}
}
if (!isInput) {
var responsibility = getVar(this, 'error', 'responsibility', this.error
.responsibility);
if (isOutput) {
var target = getVar('target');
buildSentence(responsibility, ' = ', target, ' - ', activation,
store_propagation);
for (var id in this.connections.inputs) {
var input = this.connections.inputs[id];
var trace = getVar(this, 'trace', 'elegibility', input.ID, this.trace
.elegibility[input.ID]);
var input_weight = getVar(input, 'weight');
buildSentence(input_weight, ' += ', rate, ' * (', responsibility,
' * ', trace, ')', store_propagation);
}
outputs.push(activation.id);
} else {
if (!noProjections && !noGates) {
var error = getVar('aux');
for (var id in this.connections.projected) {
var connection = this.connections.projected[id];
var neuron = connection.to;
var connection_weight = getVar(connection, 'weight');
var neuron_responsibility = getVar(neuron, 'error',
'responsibility', neuron.error.responsibility);
if (connection.gater) {
var connection_gain = getVar(connection, 'gain');
buildSentence(error, ' += ', neuron_responsibility, ' * ',
connection_gain, ' * ', connection_weight,
store_propagation);
} else
buildSentence(error, ' += ', neuron_responsibility, ' * ',
connection_weight, store_propagation);
}
var projected = getVar(this, 'error', 'projected', this.error.projected);
buildSentence(projected, ' = ', derivative, ' * ', error,
store_propagation);
buildSentence(error, ' = 0', store_propagation);
for (var id in this.trace.extended) {
var neuron = this.neighboors[id];
var influence = getVar('aux_2');
var neuron_old = getVar(neuron, 'old');
if (neuron.selfconnection.gater == this)
buildSentence(influence, ' = ', neuron_old, store_propagation);
else
buildSentence(influence, ' = 0', store_propagation);
for (var input in this.trace.influences[neuron.ID]) {
var connection = this.trace.influences[neuron.ID][input];
var connection_weight = getVar(connection, 'weight');
var neuron_activation = getVar(connection.from, 'activation');
buildSentence(influence, ' += ', connection_weight, ' * ',
neuron_activation, store_propagation);
}
var neuron_responsibility = getVar(neuron, 'error',
'responsibility', neuron.error.responsibility);
buildSentence(error, ' += ', neuron_responsibility, ' * ',
influence, store_propagation);
}
var gated = getVar(this, 'error', 'gated', this.error.gated);
buildSentence(gated, ' = ', derivative, ' * ', error,
store_propagation);
buildSentence(responsibility, ' = ', projected, ' + ', gated,
store_propagation);
for (var id in this.connections.inputs) {
var input = this.connections.inputs[id];
var gradient = getVar('aux');
var trace = getVar(this, 'trace', 'elegibility', input.ID, this
.trace.elegibility[input.ID]);
buildSentence(gradient, ' = ', projected, ' * ', trace,
store_propagation);
for (var id in this.trace.extended) {
var neuron = this.neighboors[id];
var neuron_responsibility = getVar(neuron, 'error',
'responsibility', neuron.error.responsibility);
var xtrace = getVar(this, 'trace', 'extended', neuron.ID,
input.ID, this.trace.extended[neuron.ID][input.ID]);
buildSentence(gradient, ' += ', neuron_responsibility, ' * ',
xtrace, store_propagation);
}
var input_weight = getVar(input, 'weight');
buildSentence(input_weight, ' += ', rate, ' * ', gradient,
store_propagation);
}
} else if (noGates) {
buildSentence(responsibility, ' = 0', store_propagation);
for (var id in this.connections.projected) {
var connection = this.connections.projected[id];
var neuron = connection.to;
var connection_weight = getVar(connection, 'weight');
var neuron_responsibility = getVar(neuron, 'error',
'responsibility', neuron.error.responsibility);
if (connection.gater) {
var connection_gain = getVar(connection, 'gain');
buildSentence(responsibility, ' += ', neuron_responsibility,
' * ', connection_gain, ' * ', connection_weight,
store_propagation);
} else
buildSentence(responsibility, ' += ', neuron_responsibility,
' * ', connection_weight, store_propagation);
}
buildSentence(responsibility, ' *= ', derivative,
store_propagation);
for (var id in this.connections.inputs) {
var input = this.connections.inputs[id];
var trace = getVar(this, 'trace', 'elegibility', input.ID, this
.trace.elegibility[input.ID]);
var input_weight = getVar(input, 'weight');
buildSentence(input_weight, ' += ', rate, ' * (',
responsibility, ' * ', trace, ')', store_propagation);
}
} else if (noProjections) {
buildSentence(responsibility, ' = 0', store_propagation);
for (var id in this.trace.extended) {
var neuron = this.neighboors[id];
var influence = getVar('aux');
var neuron_old = getVar(neuron, 'old');
if (neuron.selfconnection.gater == this)
buildSentence(influence, ' = ', neuron_old, store_propagation);
else
buildSentence(influence, ' = 0', store_propagation);
for (var input in this.trace.influences[neuron.ID]) {
var connection = this.trace.influences[neuron.ID][input];
var connection_weight = getVar(connection, 'weight');
var neuron_activation = getVar(connection.from, 'activation');
buildSentence(influence, ' += ', connection_weight, ' * ',
neuron_activation, store_propagation);
}
var neuron_responsibility = getVar(neuron, 'error',
'responsibility', neuron.error.responsibility);
buildSentence(responsibility, ' += ', neuron_responsibility,
' * ', influence, store_propagation);
}
buildSentence(responsibility, ' *= ', derivative,
store_propagation);
for (var id in this.connections.inputs) {
var input = this.connections.inputs[id];
var gradient = getVar('aux');
buildSentence(gradient, ' = 0', store_propagation);
for (var id in this.trace.extended) {
var neuron = this.neighboors[id];
var neuron_responsibility = getVar(neuron, 'error',
'responsibility', neuron.error.responsibility);
var xtrace = getVar(this, 'trace', 'extended', neuron.ID,
input.ID, this.trace.extended[neuron.ID][input.ID]);
buildSentence(gradient, ' += ', neuron_responsibility, ' * ',
xtrace, store_propagation);
}
var input_weight = getVar(input, 'weight');
buildSentence(input_weight, ' += ', rate, ' * ', gradient,
store_propagation);
}
}
}
buildSentence(bias, ' += ', rate, ' * ', responsibility,
store_propagation);
}
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
}
}
}
// represents a connection between two neurons
Neuron.connection = function Connection(from, to, weight) {
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;
}
// squashing functions
Neuron.squash = {};
// eq. 5 & 5'
Neuron.squash.LOGISTIC = function(x, derivate) {
if (!derivate)
return 1 / (1 + Math.exp(-x));
var fx = Neuron.squash.LOGISTIC(x);
return fx * (1 - fx);
};
Neuron.squash.TANH = function(x, derivate) {
if (derivate)
return 1 - Math.pow(Neuron.squash.TANH(x), 2);
var eP = Math.exp(x);
var 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) {
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
}
}
})();
/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(2)(module)))
/***/ },
/* 2 */
/***/ function(module, exports) {
module.exports = function(module) {
if(!module.webpackPolyfill) {
module.deprecate = function() {};
module.paths = [];
// module.parent = undefined by default
module.children = [];
module.webpackPolyfill = 1;
}
return module;
}
/***/ },
/* 3 */
/***/ function(module, exports, __webpack_require__) {
/* WEBPACK VAR INJECTION */(function(module) {// export
if (module) module.exports = Layer;
// import
var Neuron = __webpack_require__(1)
, Network = __webpack_require__(4)
/*******************************************************************************************
LAYER
*******************************************************************************************/
function Layer(size, label) {
this.size = size | 0;
this.list = [];
this.label = label || null;
this.connectedTo = [];
while (size--) {
var neuron = new Neuron();
this.list.push(neuron);
}
}
Layer.prototype = {
// activates all the neurons in the layer
activate: function(input) {
var activations = [];
if (typeof input != 'undefined') {
if (input.length != this.size)
throw new Error("INPUT size and LAYER size must be the same to activate!");
for (var id in this.list) {
var neuron = this.list[id];
var activation = neuron.activate(input[id]);
activations.push(activation);
}
} else {
for (var id in this.list) {
var neuron = this.list[id];
var activation = neuron.activate();
activations.push(activation);
}
}
return activations;
},
// propagates the error on all the neurons of the layer
propagate: function(rate, target) {
if (typeof target != 'undefined') {
if (target.length != this.size)
throw new Error("TARGET size and LAYER size must be the same to propagate!");
for (var id = this.list.length - 1; id >= 0; id--) {
var neuron = this.list[id];
neuron.propagate(rate, target[id]);
}
} else {
for (var id = this.list.length - 1; id >= 0; id--) {
var neuron = this.list[id];
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;
if (layer instanceof Layer) {
if (!this.connected(layer))
return new Layer.connection(this, layer, type, weights);
} 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) {
if (type == Layer.gateType.INPUT) {
if (connection.to.size != this.size)
throw new Error("GATER layer and CONNECTION.TO layer must be the same size in order to gate!");
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) {
var gated = neuron.connections.inputs[input];
if (gated.ID in connection.connections)
gater.gate(gated);
}
}
} else if (type == Layer.gateType.OUTPUT) {
if (connection.from.size != this.size)
throw new Error("GATER layer and CONNECTION.FROM layer must be the same size in order to gate!");
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) {
var gated = neuron.connections.projected[projected];
if (gated.ID in connection.connections)
gater.gate(gated);
}
}
} else if (type == Layer.gateType.ONE_TO_ONE) {
if (connection.size != this.size)
throw new Error("The number of GATER UNITS must be the same as the number of CONNECTIONS to gate!");
for (var id in connection.list) {
var gater = this.list[id];
var gated = connection.list[id];
gater.gate(gated);
}
}
connection.gatedfrom.push({layer: this, type: type});
},
// true or false whether the whole layer is self-connected or not
selfconnected: function() {
for (var id in this.list) {
var 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) {
// Check if ALL to ALL connection
var connections = 0;
for (var here in this.list) {
for (var there in layer.list) {
var from = this.list[here];
var to = layer.list[there];
var connected = from.connected(to);
if (connected.type == 'projected')
connections++;
}
}
if (connections == this.size * layer.size)
return Layer.connectionType.ALL_TO_ALL;
// Check if ONE to ONE connection
connections = 0;
for (var neuron in this.list) {
var from = this.list[neuron];
var to = layer.list[neuron];
var connected = from.connected(to);
if (connected.type == 'projected')
connections++;
}
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];
neuron.clear();
}
},
// resets all the neurons in the layer
reset: function() {
for (var id in this.list) {
var neuron = this.list[id];
neuron.reset();
}
},
// returns all the neurons in the layer (array)
neurons: function() {
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];
if (options.label)
neuron.label = options.label + '_' + neuron.ID;
if (options.squash)
neuron.squash = options.squash;
if (options.bias)
neuron.bias = options.bias;
}
return this;
}
}
// 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 = [];
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 ||
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;
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);
}
// types of connections
Layer.connectionType = {};
Layer.connectionType.ALL_TO_ALL = "ALL TO ALL";
Layer.connectionType.ONE_TO_ONE = "ONE TO ONE";
Layer.connectionType.ALL_TO_ELSE = "ALL TO ELSE";
// types of gates
Layer.gateType = {};
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++;
}
})();
/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(2)(module)))
/***/ },
/* 4 */
/***/ function(module, exports, __webpack_require__) {
/* WEBPACK VAR INJECTION */(function(module) {// export
if (module) module.exports = Network;
// import
var Neuron = __webpack_require__(1)
, Layer = __webpack_require__(3)
, Trainer = __webpack_require__(5)
/*******************************************************************************************
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);
};
/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(2)(module)))
/***/ },
/* 5 */
/***/ function(module, exports, __webpack_require__) {
/* WEBPACK VAR INJECTION */(function(module) {// 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 = {
// trains any given set to a network
train: function(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;
var 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)
this.error = options.error;
if (options.rate)
this.rate = options.rate;
if (options.cost)
this.cost = options.cost;
if (options.schedule)
this.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;
}
if (this.crossValidate || options.crossValidate) {
if(!this.crossValidate) this.crossValidate = {};
crossValidate = true;
if (options.crossValidate.testSize)
this.crossValidate.testSize = options.crossValidate.testSize;
if (options.crossValidate.testError)
this.crossValidate.testError = options.crossValidate.testError;
}
}
currentRate = this.rate;
if(Array.isArray(this.rate)) {
var bucketSize = Math.floor(this.iterations / this.rate.length);
}
if(crossValidate) {
var numTrain = Math.ceil((1 - this.crossValidate.testSize) * set.length);
trainSet = set.slice(0, numTrain);
testSet = set.slice(numTrain);
}
var lastError = 0;
while ((!abort && iterations < this.iterations && error > this.error)) {
if (crossValidate && error <= this.crossValidate.testError) {
break;
}
var currentSetSize = set.length;
error = 0;
iterations++;
if(bucketSize > 0) {
var currentBucket = Math.floor(iterations / bucketSize);
currentRate = this.rate[currentBucket] || currentRate;
}
if(typeof this.rate === 'function') {
currentRate = this.rate(iterations, lastError);
}
if (crossValidate) {
this._trainSet(trainSet, currentRate, cost);
error += this.test(testSet).error;
currentSetSize = 1;
} else {
error += this._trainSet(set, currentRate, cost);
currentSetSize = set.length;
}
// check error
error /= currentSetSize;
lastError = error;
if (options) {
if (this.schedule && this.schedule.every && iterations %
this.schedule.every == 0)
abort = this.schedule.do({ error: error, iterations: iterations, rate: currentRate });
else if (options.log && iterations % options.log == 0) {
console.log('iterations', iterations, 'error', error, 'rate', currentRate);
};
if (options.shuffle)
shuffle(set);
}
}
var results = {
error: error,
iterations: 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) {
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;
var 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) {
var error = 0;
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 [deprecated: use trainAsync instead]
workerTrain: function(set, callback, options, suppressWarning) {
if (!suppressWarning) {
console.warn('Deprecated: do not use `workerTrain`, use `trainAsync` instead.')
}
var that = this;
if (!this.network.optimized)
this.network.optimize();
// Create a new worker
var worker = this.network.worker(this.network.optimized.memory, set, options);
// train the worker
worker.onmessage = function(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;
that.network.optimized.ownership(e.data.memoryBuffer);
// Done callback
callback({
error: error,
iterations: iterations,
time: time
});
// Delete the worker and all its associated memory
worker.terminate();
break;
case 'log':
console.log(e.data.message);
case 'schedule':
if (options && options.schedule && typeof options.schedule.do === 'function') {
var scheduled = options.schedule.do
scheduled(e.data.message)
}
break;
}
};
// Start the worker
worker.postMessage({action: 'startTraining'});
},
// trains an XOR to the network
XOR: function(options) {
if (this.network.inputs() != 2 || this.network.outputs() != 1)
throw new Error("Incompatible network (2 inputs, 1 output)");
var defaults = {
iterations: 100000,
log: false,
shuffle: true,
cost: Trainer.cost.MSE
};
if (options)
for (var i in options)
defaults[i] = options[i];
return this.train([{
input: [0, 0],
output: [0]
}, {
input: [1, 0],
output: [1]
}, {
input: [0, 1],
output: [1]
}, {
input: [1, 1],
output: [0]
}], defaults);
},
// trains the network to pass a Distracted Sequence Recall test
DSR: function(options) {
options = options || {};
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;
trial = correct = i = j = success = 0;
var error = 1,
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)
if (number == avoid[i])
used = true;
return used ? noRepeat(range, avoid) : number;
};
var equal = function(prediction, output) {
for (var i in prediction)
if (Math.round(prediction[i]) != output[i])
return false;
return true;
};
var start = Date.now();
while (trial < iterations && (success < criterion || trial % 1000 != 0)) {
// generate sequence
var sequence = [],
sequenceLength = length - prompts.length;
for (i = 0; i < sequenceLength; i++) {
var any = Math.random() * distractors.length | 0;
sequence.push(distractors[any]);
}
var indexes = [],
positions = [];
for (i = 0; i < prompts.length; i++) {
indexes.push(Math.random() * targets.length | 0);
positions.push(noRepeat(sequenceLength, positions));
}
positions = positions.sort();
for (i = 0; i < prompts.length; i++) {
sequence[positions[i]] = targets[indexes[i]];
sequence.push(prompts[i]);
}
//train sequence
var distractorsCorrect;
var targetsCorrect = distractorsCorrect = 0;
error = 0;
for (i = 0; i < length; i++) {
// generate input from sequence
var input = [];
for (j = 0; j < symbols; j++)
input[j] = 0;
input[sequence[i]] = 1;
// generate target output
var output = [];
for (j = 0; j < targets.length; j++)
output[j] = 0;
if (i >= sequenceLength) {
var index = i - sequenceLength;
output[indexes[index]] = 1;
}
// check result
var prediction = this.network.activate(input);
if (equal(prediction, output))
if (i < sequenceLength)
distractorsCorrect++;
else
targetsCorrect++;
else {
this.network.propagate(rate, output);
}
error += cost(output, prediction);
if (distractorsCorrect + targetsCorrect == length)
correct++;
}
// calculate error
if (trial % 1000 == 0)
correct = 0;
trial++;
var divideError = trial % 1000;
divideError = divideError == 0 ? 1000 : divideError;
success = correct / divideError;
error /= length;
// log
if (log && trial % log == 0)
console.log("iterations:", trial, " success:", success, " correct:",
correct, " time:", Date.now() - start, " error:", error);
if (schedule.do && schedule.every && trial % schedule.every == 0)
schedule.do({
iterations: trial,
success: success,
error: error,
time: Date.now() - start,
correct: correct
});
}
return {
iterations: trial,
success: success,
error: 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;
// gramar node
var Node = function() {
this.paths = [];
};
Node.prototype = {
connect: function(node, value) {
this.paths.push({
node: node,
value: value
});
return this;
},
any: function() {
if (this.paths.length == 0)
return false;
var index = Math.random() * this.paths.length | 0;
return this.paths[index];
},
test: function(value) {
for (var i in this.paths)
if (this.paths[i].value == value)
return this.paths[i];
return false;
}
};
var reberGrammar = function() {
// 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");
n4.connect(n4, "S");
var 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");
return {
input: input,
output: output
}
};
// build an embeded reber grammar
var embededReberGrammar = function() {
var reber1 = reberGrammar();
var reber2 = reberGrammar();
var output = new Node();
var 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,
"T");
var input = (new Node).connect(n2, "B");
return {
input: input,
output: output
}
};
// generate an ERG sequence
var generate = function() {
var node = embededReberGrammar().input;
var next = node.any();
var str = "";
while (next) {
str += next.value;
next = next.node.any();
}
return str;
};
// 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);
while (i < str.length) {
var next = node.test(ch);
if (!next)
return false;
node = next.node;
ch = str.charAt(++i);
}
return true;
};
// 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) {
if (array1[i] > max1) {
max1 = array1[i];
i1 = i;
}
if (array2[i] > max2) {
max2 = array2[i];
i2 = i;
}
}
return i1 != i2;
};
var iteration = 0;
var error = 1;
var table = {
"B": 0,
"P": 1,
"T": 2,
"X": 3,
"S": 4,
"E": 5
};
var start = Date.now();
while (iteration < iterations && error > criterion) {
var i = 0;
error = 0;
// ERG sequence to learn
var sequence = generate();
// input
var read = sequence.charAt(i);
// target
var predict = sequence.charAt(i + 1);
// train
while (i < sequence.length - 1) {
var input = [];
var target = [];
for (var 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);
if (different(output, target))
this.network.propagate(rate, target);
read = sequence.charAt(++i);
predict = sequence.charAt(i + 1);
error += cost(target, output);
}
error /= sequence.length;
iteration++;
if (iteration % log == 0) {
console.log("iterations:", iteration, " time:", Date.now() - start,
" error:", error);
}
}
return {
iterations: iteration,
error: error,
time: Date.now() - start,
test: test,
generate: generate
}
},
timingTask: function(options){
if (this.network.inputs() != 2 || this.network.outputs() != 1)
throw new Error("Invalid Network: must have 2 inputs and one output");
if (typeof options == 'undefined')
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 < 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++)
(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)
}
}
};
// Built-in cost functions
Trainer.cost = {
// Eq. 9
CROSS_ENTROPY: function(target, output)
{
var crossentropy = 0;
for (var 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 += 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;
}
}
/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(2)(module)))
/***/ },
/* 6 */
/***/ function(module, exports, __webpack_require__) {
/* WEBPACK VAR INJECTION */(function(module) {// import
var Layer = __webpack_require__(3)
, Network = __webpack_require__(4)
, Trainer = __webpack_require__(5)
/*******************************************************************************************
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;
/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(2)(module)))
/***/ }
/******/ ]);