neuralnetwork
Version:
Rudimentary Neural Network in Typescript
171 lines (169 loc) • 7.35 kB
JavaScript
/*global module, require*/
"use strict";
function meanSquaredError(errors) {
var mean = errors.reduce(function (prev, curr, index) {
return prev + Math.pow(curr, 2);
}, 0);
return mean / errors.length;
}
exports.meanSquaredError = meanSquaredError;
var Network = (function () {
function Network() {
this.layers = {}; //new Map<string, Layer>();
this.forwardConnections = {}; //new Map<string, Array<Connection>>();
this.backwardConnections = {}; //new Map<string, Array<Connection>>();
this.rootLayer = null;
this.outputLayer = null;
}
Network.prototype.setRootLayer = function (layer) {
this.rootLayer = layer;
this.layers[layer.name] = layer;
};
Network.prototype.setOutputLayer = function (layer) {
this.outputLayer = layer;
this.layers[layer.name] = layer;
};
Network.prototype.addLayer = function (layer) {
this.layers[layer.name] = layer;
};
Network.prototype.addConnection = function (con) {
this.forwardConnections[con.inputLayer.name] = this.forwardConnections[con.inputLayer.name] || [];
this.forwardConnections[con.inputLayer.name].push(con);
this.backwardConnections[con.outputLayer.name] = this.backwardConnections[con.outputLayer.name] || [];
this.backwardConnections[con.outputLayer.name].push(con);
this.layers[con.inputLayer.name] = con.inputLayer;
this.layers[con.outputLayer.name] = con.outputLayer;
};
Network.prototype.resetLayers = function () {
for (var prop in this.layers) {
if (this.layers.hasOwnProperty(prop)) {
this.layers[prop].resetBuffers();
}
}
};
Network.prototype.forwardPropogate = function (input) {
var self = this, currentLayer = null, connections = null, openSet = [], outputLayerReached = false;
// Located outside the for loop: see <http://jsperf.com/closure-vs-name-function-in-a-loop/2>. jslint also complains otherwise
function forwardEachConnectionIn(connections) {
connections.forEach(function (connection) {
connection.forward(); // If there is a connection from output layer to another, there is a final 'push'
openSet.push(connection.outputLayer);
});
}
this.rootLayer.inputBuffer = input;
// Initial condition for Breadth First exploration
openSet.push(this.rootLayer);
while (openSet.length > 0) {
currentLayer = openSet.shift();
currentLayer.forward();
connections = this.forwardConnections[currentLayer.name] || [];
forwardEachConnectionIn(connections);
if (currentLayer === this.outputLayer) {
outputLayerReached = true;
break;
}
}
return this.outputLayer.outputBuffer;
};
Network.prototype.backwardPropogate = function (errorVector) {
var self = this, currentLayer = null, connections = null, openSet = [];
function backwardEachConnectionIn(connections) {
connections.forEach(function (connection) {
connection.backward();
openSet.push(connection.inputLayer);
});
}
this.forEachConnection(function (connection) {
connection.resetDerivatives();
});
this.outputLayer.outputError = errorVector;
// Initial condition for Breadth First exploration
openSet.push(self.outputLayer);
while (openSet.length > 0) {
currentLayer = openSet.shift();
currentLayer.backward();
connections = self.backwardConnections[currentLayer.name] || [];
backwardEachConnectionIn(connections);
// Unless recurrent...
// TODO: Non 1 to 1 typescript conversion. inputLayer changed to rootLayer
if (currentLayer === this.rootLayer) {
break;
}
}
};
Network.prototype.forEachConnection = function (callback) {
for (var i in this.forwardConnections) {
if (this.forwardConnections.hasOwnProperty(i)) {
this.forwardConnections[i].forEach(function (el, index) {
callback(el);
});
}
}
};
// TODO
Network.prototype.exploreForward = function (layerCallback, connectionsCallback) {
'use strict';
var self = this, currentOpenSet = [], nextOpenSet = [], currentLayer, connections;
// TODO: Non 1-1 typescript conversion, inputLayer to rootLayer
currentOpenSet.push(this.rootLayer);
while (currentOpenSet.length > 0 || nextOpenSet.length > 0) {
// tick tock
if (currentOpenSet.length === 0) {
currentOpenSet = nextOpenSet;
nextOpenSet = [];
}
currentLayer = currentOpenSet.shift();
layerCallback(currentLayer);
connections = this.forwardConnections[currentLayer.name] || [];
nextOpenSet.push(connectionsCallback(connections));
// Unless recurrent...
// TODO: Non 1-1 typescript conversion, inputLayer to rootLayer
if (currentLayer === this.rootLayer) {
break;
}
}
};
Network.prototype.train = function (input, targetVector, options) {
if (input.length !== this.rootLayer.inputBuffer.length) {
throw new Error('Wrong input dimensions');
}
if (targetVector.length !== this.outputLayer.outputBuffer.length) {
throw new Error('Wrong output dimensions');
}
var self = this, connections, errorVector = [], options = options || {}, step = options.step || 0.21, momentum = options.momentum || 0.1, dropout = options.dropout || 0.2;
this.forEachConnection(function (con) {
con.resetDerivatives();
// TODO: Make type friendly
if (!con._lastStep) {
con._lastStep = con.parameters.map(function (el, index) {
return 0;
});
}
});
this.resetLayers();
this.forwardPropogate(input);
errorVector = targetVector.map(function (target, index) {
return target - self.outputLayer.outputBuffer[index];
});
this.backwardPropogate(errorVector);
this.forEachConnection(function (connection) {
if (connection.parameters.length !== connection.derivatives.length) {
throw new Error('Wrong derivative dims');
}
connection.parameters = connection.parameters.map(function (el, index) {
var calculatedStep, newParam;
if (Math.random() < dropout) {
return el;
}
calculatedStep = step * connection.derivatives[index] + momentum * connection._lastStep[index];
newParam = el + calculatedStep;
connection._lastStep[index] = calculatedStep;
return newParam;
});
});
return meanSquaredError(errorVector);
};
return Network;
}());
exports.Network = Network;
//# sourceMappingURL=../maps/lib/network.js.map