Coverage

95%
188
179
9

lib/costfunctions/crossentropy.js

100%
10
10
0
LineHitsSource
11var CostFunction = require('./interface');
21var util = require('util');
3
4/**
5The cross-entropy cost function for a single pair of values.
6wiki: http://en.wikipedia.org/wiki/Cross_entropy
7
8@class
9@implements CostFunction
10*/
11function CrossEntropy () {
121 return CostFunction.apply(this, arguments);
13}
14
151util.inherits(CrossEntropy, CostFunction);
16
171CrossEntropy.prototype.fn = function (output, target) {
181 var result = -target * Math.log(output) - (1 - target) * Math.log(1 - output);
191 return isNaN(result) ? 0 : result;
20};
21
221CrossEntropy.prototype.delta = function (inputs, output, target) {
232 return (output - target);
24};
25
261module.exports = CrossEntropy;
27

lib/costfunctions/index.js

100%
3
3
0
LineHitsSource
1/**
2Cost functions for neural networks.
3
4@file
5*/
61exports.CrossEntropy = require('./crossentropy');
71exports.MeanSquaredError = require('./meansquarederror');
81exports.LogLikelihood = require('./loglikelihood');
9

lib/costfunctions/interface.js

50%
4
2
2
LineHitsSource
1/**
2A generic interface for cost functions.
3
4@class
5@interface
6*/
7function CostFunction () {}
8
91CostFunction.prototype = {
10 /**
11 TODO doc CostFunction.fn
12
13 @function
14 @param {number} output - the actual output value
15 @param {number} target - the desired output value
16 @returns {number} the cost associated with the output and target
17 */
18 fn: function (output, target) {
190 throw new Error("Not Implemented");
20 },
21 /**
22 TODO doc CostFunction.delta
23
24 @function
25 @param {array} input - the array of input values
26 @param {number} output - the actual output value
27 @param {number} target - the desired output value
28 @returns {number} the error delta associated with the output and target
29 */
30 delta: function (input, output, target) {
310 throw new Error("Not Implemented");
32 }
33};
34
351module.exports = CostFunction;
36

lib/costfunctions/loglikelihood.js

100%
8
8
0
LineHitsSource
11var CostFunction = require('./interface');
21var util = require('util');
3
4/**
5TODO doc LogLikelihood
6
7@class
8@implements CostFunction
9*/
10function LogLikelihood () {}
11
121util.inherits(LogLikelihood, CostFunction);
13
14/**
15TODO doc LogLikelihood.fn
16
17@function
18*/
191LogLikelihood.prototype.fn = function (output, target) {
201 return -1 * Math.log(1 - Math.abs(output - target));
21};
22
23
24/**
25TODO doc LogLikelihood.delta
26
27@function
28*/
291LogLikelihood.prototype.delta = function (input, output, target) {
302 return output - target;
31};
32
331module.exports = LogLikelihood;
34

lib/costfunctions/meansquarederror.js

100%
14
14
0
LineHitsSource
11var math = require('mathjs');
21var CostFunction = require('./interface');
31var util = require('util');
4
5/**
6The mean squared error cost function for a single pair of values.
7wiki: http://en.wikipedia.org/wiki/Mean_squared_error
8
9@class
10@implements CostFunction
11*/
12function MeanSquaredError () {
133 return CostFunction.apply(this, arguments);
14}
15
161util.inherits(MeanSquaredError, CostFunction);
17
181MeanSquaredError.prototype.fn = function (output, target) {
192170 return 0.5 * Math.pow(math.norm(output - target), 2);
20};
21
221MeanSquaredError.prototype.delta = function (inputs, output, target) {
232174 var error = output - target;
242174 return inputs.map(function (input) {
254350 var z = 1 / (1 + Math.exp(-input)); // sigmoid prime value
264350 return z * (1 - z) * error;
27 }).reduce(function (a, b) {
284350 return a + b;
29 }, 0);
30};
31
321module.exports = MeanSquaredError;
33

lib/layers/basic.js

100%
17
17
0
LineHitsSource
11var neurons = require('../neurons');
21var _ = require('underscore');
3
4/**
5A single layer of neurons. Networks process input through multiple layers.
6
7@class
8@param {number} size - Number of neurons in the layer.
9@param {number} input_size - Length of input arrays.
10@param {object} Neuron - (optional) Class used to initialize neurons. Defaults to SigmoidNeuron.
11*/
12function Layer (size, input_size, Neuron) {
1311 Neuron = Neuron || neurons.SigmoidNeuron;
1411 this._neurons = _.range(size).map(function (i) {
1541 var weights, bias;
16 if (i === 0) {
1748 weights = _.range(input_size).map(function () { return 0; });
1811 bias = 0;
19 } else {
20126 weights = _.range(input_size).map(function () { return _.random(-1, 1); });
2130 bias = _.random(-1, 1);
22 }
2341 return new Neuron(weights, bias);
24 });
25}
26
27/**
28Getter / setter for the layer's neurons.
29
30@function
31@param {array} neurons - (optional) A list of neurons to replace the layer's current neurons. If undefined, returns the list of current neurons.
32*/
331Layer.prototype.neurons = function (neurons) {
34 if (neurons === undefined)
35 return this._neurons;
36 else
37 this._neurons = neurons;
38};
39
40/**
41Getter / setter for the layer's neurons, individually.
42
43@function
44@param {number} i - Index of the neuron to get or set.
45@param {object} neuron - (optional) A single neuron, which replaces the i'th neuron. If undefined, returns the i'th neuron.
46*/
471Layer.prototype.neuron = function (i, neuron) {
48 if (neuron === undefined)
49 return this._neurons[i];
50 else
51 this._neurons[i] = neuron;
52};
53
54/**
55Given an array of input bits, returns the layer's output
56when the input is applied to each neuron.
57
58@function
59@param {array} input - array of input bits
60*/
611Layer.prototype.process = function (input) {
622193 var self = this;
632193 return self.neurons().map(function (neuron) {
647311 return neuron.process(input);
65 });
66};
67
681module.exports = Layer;
69

lib/layers/index.js

100%
2
2
0
LineHitsSource
11exports.Layer = require('./basic');
21exports.SoftmaxLayer = require('./softmax');
3

lib/layers/softmax.js

100%
12
12
0
LineHitsSource
11var util = require('util');
21var SoftmaxNeuron = require('../neurons').SoftmaxNeuron;
31var Layer = require('./basic');
41var _ = require('underscore');
5
6/**
7TODO doc SoftmaxLayer
8
9@class
10@implements Layer
11*/
12function SoftmaxLayer (size, input_size) {
132 return Layer.call(this, size, input_size, SoftmaxNeuron);
14}
15
161util.inherits(SoftmaxLayer, Layer);
17
18
19/**
20TODO doc SoftmaxLayer.process
21
22@function
23@param {array} inputs - an array of input bits
24@returns {array} array of processed output bits
25*/
261SoftmaxLayer.prototype.process = function (inputs) {
272 var results = this.neurons().map(function (neuron, j) {
2810 return neuron.process(inputs, j);
29 });
3010 var sum = results.reduce(function (a, b) { return a + b; });
3112 return results.map(function (result) { return result / sum; });
32};
33
341module.exports = SoftmaxLayer;
35

lib/networks/index.js

100%
2
2
0
LineHitsSource
11exports.Network = require('./interface');
21exports.StochasticNetwork = require('./stochastic');
3

lib/networks/interface.js

100%
75
75
0
LineHitsSource
11var _ = require('underscore');
21var neurons = require('../neurons');
31var costfunctions = require('../costfunctions');
41var layers = require('../layers');
5
6/**
7Generic neural network. Subclasses must implement a `_train` method
8to adjust weights and biases based on training data and a learning rate.
9
10@class
11@interface
12@param {array} sizes - list of sizes for each layer, ex: [2, 3, 1] -> 2-neuron layer, 3-neuron layer, 1-neuron layer.
13@param {object} layer - (optional) layer class to use. Defaults to the basic Layer.
14@param {object} cost - (optional) cost function to use. Defaults to MeanSquaredError.
15*/
16function Network (sizes, layer, cost) {
172 var self = this;
182 this._layer = layer || layers.Layer;
192 this._cost = cost ? new cost() : new costfunctions.MeanSquaredError();
202 this._layers = sizes.map(function (size, i) {
216 return new self._layer(size, sizes[i-1] || size);
22 });
23}
24
25/**
26Getter / setter for the network's neural layers.
27
28@function
29@param {array} layers - (optional) A list of layers to replace the network's current layers. If undefined, returns the list of current layers.
30*/
311Network.prototype.layers = function (layers) {
32 if (layers === undefined)
33 return this._layers;
34 else
35 this._layers = layers;
36};
37
38/**
39Getter / setter for the network's neural layers, individually.
40
41@function
42@param {number} i - Index of the layer to get or set.
43@param {object} layer - (optional) A single layer, which replaces the i'th layer. If undefined, returns the i'th layer.
44*/
451Network.prototype.layer = function (i, layer) {
46 if (layer === undefined)
47 return this._layers[i];
48 else
49 this._layers[i] = layer;
50};
51
52/**
53Applies an array of input bits to each layer,
54feeding its output into the next layer,
55and returning the final layer's output.
56
57@function
58@param {array} input - array of input bits.
59@returns {array} array of output bits
60*/
611Network.prototype.process = function (input) {
625 return this.layers().reduce(function (input, layer) {
6315 return layer.process(input);
64 }, input);
65};
66
67/**
68Calculates the network's output at each layer
69for the given array of input values.
70
71@function
72@param {array} input - array of input bits
73@returns {array} array of arrays of output bits for each layer
74*/
751Network.prototype._feedforward = function (input) {
76725 var outputs = [];
77725 var layers = this.layers();
78 for (var i = 0; i < layers.length; i++) {
792175 output = layers[i].process(input);
802175 outputs.push(output);
812175 input = output;
82 }
83725 return outputs;
84};
85
86/*
87Given a learning rate, a matrix of inputs, and a matrix of deltas,
88adjusts weights and biases for every neuron in every layer of the network.
89
90@function
91@param {number} learning_rate - A small, positive number.
92@param {array} outputs - [i][j] matrix of outputs for every jth neuron of every ith layers
93@param {array} deltas - [i][j] matrix of deltas for every jth neuron of every ith layer
94**/
951Network.prototype._adjust_network = function (learning_rate, outputs, deltas) {
96723 var self = this;
97723 var num_layers = this.layers().length;
98 for (var i = 1; i < num_layers; i++) {
99 // input size for the current layer === # of neurons in previous layer
1001446 var layer = this.layer(i);
1011446 var input_size = this.layer(i-1).neurons().length;
1021446 var layer_size = layer.neurons().length;
103 for (var j = 0; j < layer_size; j++) {
1045784 var neuron = layer.neuron(j);
1055784 var delta = deltas[i-1][j];
1065784 var output = outputs[i][j];
107 for (var k = 0; k < input_size; k++) {
10818075 var change = learning_rate * delta * output;
10918075 neuron.weight(k, neuron.weight(k) + change);
110 }
1115784 var bias = neuron.bias() + (learning_rate * delta);
1125784 neuron.bias(bias);
113 }
114 }
115};
116
117/**
118Given outputs for each layer and the desired network output,
119the network calculates modifications for its weights and biases
120that will ensure it yields the proper output for the given input.
121
122@function
123@param {array} outputs - [i][j] matrix of output bits for every jth neuron of every ith layer
124@param {array} target - array of bits representing desired network output
125@returns {array} [i][j] matrix of deltas for every jth neuron of every ith layer
126*/
1271Network.prototype._calculate_deltas = function (input, outputs, target) {
128724 var self = this;
129 // 1. output errors for the output layer and
130 // 2. backpropogate the errors through prior layers
131724 var errors = [];
132724 var deltas = [];
133724 var num_layers = this.layers().length - 1;
134 for (var i = num_layers; i > 0; i--) {
1351448 var layer = this.layer(i);
1361448 var layer_size = layer.neurons().length;
137 // add a new layer to errors and deltas for every layer we deal with
1381448 errors.unshift([]);
1391448 deltas.unshift([]);
140 // calculate errors for each neuron in each layer
141 for (var j = 0; j < layer_size; j++) {
1425792 var neuron = layer.neuron(j);
1435792 var output = outputs[i][j];
1445792 var error = 0;
145 if (i === num_layers) {
146 // output layer
1472172 error -= self._cost.delta(input, output, target[j]);
148 } else {
149 // hidden layers
150 for (var k = 0; k < deltas[1].length; k++) {
151 // compute the delta for each weight of the next layer
152 // corresponding to each bit of this layer's output
15310860 error -= self.layer(i+1).neuron(k).weight(j) * deltas[1][k];
154 }
155 }
1565792 errors[0][j] = error;
1575792 deltas[0][j] = error * output * (1 - output);
158 }
159 }
160724 return [errors, deltas];
161};
162
163/**
164The dreaded backpropogation algorithm.
165First, calculates output from each layer of the network.
166Then, calculates error rates for each neuron of each layer.
167Lastly, uses those outputs, deltas, and a specified learning rate to adjust the weights and biases of the network.
168
169@function
170@param {array} input - array of input bits to the network
171@param {array} target - array of desired output bits from the network for the given input
172@param {number} learning_rate - (optional) value between 0 and 1 representing how quickly the network learns. Defaults to 0.3.
173@returns {array} deltas for each neuron of each layer in the network, besides the input layer
174*/
1751Network.prototype._backpropogate = function (input, target, learning_rate) {
176 // 1. feedforward
177723 var outputs = this._feedforward(input);
178 // 2. calculate deltas
179723 var results = this._calculate_deltas(input, outputs, target);
180723 var errors = results[0];
181723 var deltas = results[1];
182 // 3. adjust network
183723 this._adjust_network(learning_rate, outputs, deltas);
184
185723 return [outputs, errors];
186};
187
188/**
189Given training data, a number of epochs, and a learning rate,
190trains the network to more accurately predict
191correct outputs for given inputs.
192
193The second parameter accepts an object containing custom training settings, specifically:
194* epochs: number of rounds to train against the data. Defaults to 20,000.
195* learning_rate: value between 0 and 1 representing how quickly the network learns. Defaults to 0.3.
196* threshold: error threshold. if the network attains an error rate under this threshold, it stops training early. Defaults to 0.005.
197
198@function
199@param {array} training_data - array of [input, correct_output] pairs used to train the network
200@param {object} options - options object.
201@returns {array} [i, error] where i is the number of iterations it took to reach the returned error rate
202*/
2031Network.prototype.train = function (training_data, opts) {
2041 opts = opts || {};
2051 var self = this;
2061 var error_threshold = opts.threshold || Math.pow(10, -3) * 5;
2071 var epochs = opts.epochs || Math.pow(10, 4) * 2;
2081 var learning_rate = opts.learning_rate || 0.3;
2091 var error = 1;
210
211 for (var i = 0; i < epochs && error > error_threshold; i++) {
212723 error = 0;
213 for (var j = 0; j < training_data.length; j++) {
214723 var input = training_data[j][0];
215723 var target = training_data[j][1];
216723 var result = this._backpropogate(input, target, learning_rate);
217723 var outputs = result[0];
218723 var network_output = outputs[outputs.length - 1];
219 // aggregate errors only from the output layer
220 for (var k = 0; k < network_output.length; k++) {
2212169 error += this._cost.fn(network_output[k], target[k]);
222 }
223 }
224723 error = error / training_data.length;
225 }
226
2271 return [i, error];
228};
229
2301module.exports = Network;
231

lib/networks/stochastic.js

46%
13
6
7
LineHitsSource
11var _ = require('underscore');
21var Network = require('./interface');
31var util = require('util');
4
5/**
6Network that samples training data
7into mini batches, in order to learn
8more quickly with less processing
9without sacrificing significant accuracy.
10
11@class
12@implements Network
13*/
14function StochasticNetwork () {
150 return Network.apply(this, arguments);
16}
17
181util.inherits(StochasticNetwork, Network);
19
20/*
21Stochastic training function.
22Breaks training data into small batches
23and learns from them one batch at a time.
24
25@function
26@param {array} training_data - [input, output] pairs used to guide learning.
27@param {number} epochs - Number of rounds to train against the data.
28@param {number} learning_rate - Small, positive number (ex: 0.3) indicating the rate of learning.
29@param {number} batch_size - Size of batches of training data to train against at a time.
30**/
311StochasticNetwork.prototype.train = function (training_data, epochs, learning_rate, batch_size) {
320 epochs = epochs || 300;
330 learning_rate = learning_rate || 0.3;
340 batch_size = batch_size || 10;
35
36 for (var i = 0; i < epochs; i++) {
370 var batch = _.sample(training_data, batch_size);
38 for (var j = 0; j < batch.length; j++) {
390 var data = batch[j];
400 this._backpropogate(data[0], data[1], learning_rate);
41 }
42 }
43};
44
451module.exports = StochasticNetwork;
46

lib/neurons/index.js

100%
2
2
0
LineHitsSource
11exports.Perceptron = require('./perceptron');
21exports.SigmoidNeuron = require('./sigmoid');
3

lib/neurons/interface.js

100%
4
4
0
LineHitsSource
1/**
2A generic interface for Neurons.
3It includes getters and setters for a neuron's weight and biases.
4
5Classes inheriting from this interface should implement a `_process`
6function that takes an array of bits of size equal to the neuron's
7list of weights.
8
9@class
10@interface
11@param {array} weights - An array of weights to apply to input bits.
12@param {number} bias - A number added to the product of input bits and their weights.
13*/
14function Neuron (weights, bias) {
15149 this._weights = weights;
16149 this._bias = bias;
17}
18
191Neuron.prototype = {
20 /**
21 Get or set the entire array of the neuron's weights.
22 @function
23 @param {array} weights - The new list of weights. If undefined, returns the current list of weights.
24 */
25 weights: function (weights) {
26 if (weights === undefined)
27 return this._weights;
28 else
29 this._weights = weights;
30 },
31 /**
32 Get or set the neuron's weight value for a given input bit.
33 @function
34 @param {number} i - The index of the weight to get or set.
35 @param {number} n - The new value for the specified weight. If undefined, returns the current weight value.
36 */
37 weight: function (i, n) {
38 if (n === undefined)
39 return this._weights[i];
40 else
41 this._weights[i] = n;
42 },
43 /**
44 Get or set the Perceptron's bias value.
45 @function
46 @param {number} n - The new bias value. If undefined, returns the current bias value.
47 */
48 bias: function (n) {
49 if (n === undefined)
50 return this._bias;
51 else
52 this._bias = n;
53 },
54 /**
55 Given an array of input bits, return the neuron's output.
56 If the array of input bits is a different length than
57 the neuron's list of weights, it will throw an error.
58 If `_process` is unimplemented, it will also throw an error.
59
60 @function
61 @param {array} input - An array of input bits.
62 */
63 process: function (input) {
64 if (input.length !== this.weights().length)
65 throw new Error("input.length does not match weights.length: " + input.length + ' !== ' + this.weights().length);
66 else if (!this._process)
67 throw new Error("Not Implemented");
68 else
69 return this._process.apply(this, arguments);
70 }
71};
72
731module.exports = Neuron;
74

lib/neurons/perceptron.js

100%
10
10
0
LineHitsSource
11var util = require('util');
21var Neuron = require('./interface');
3
4/**
5The Perceptron is a type of neuron that
6sums the product of input values and
7their respective weights. If that sum,
8plus the Perceptron's "bias", is
9greater than 0, it outputs 1.
10Else, it outputs 0.
11
12@class
13@implements Neuron
14*/
15function Perceptron () {
167 return Neuron.apply(this, arguments);
17}
18
191util.inherits(Perceptron, Neuron);
20
21/**
22Given an array of input bits,
23return 0 if
24the product of input bits and weights
25plus the neuron's bias
26if greater than 0.
27otherwise, returns 1.
28
29@function
30@param {array} input - An array of input bits.
31*/
321Perceptron.prototype._process = function (input) {
3344 var self = this;
3444 var result = input.map(function (x, i) {
35412 return (self.weight(i) * x);
36 }).reduce(function (a, b) {
37412 return a + b;
38 }, self.bias());
39
40 if (result > 0)
41 return 1;
42 else
43 return 0;
44};
45
461module.exports = Perceptron;
47

lib/neurons/sigmoid.js

100%
12
12
0
LineHitsSource
11var util = require('util');
21var Neuron = require('./interface');
3
4/**
5The SigmoidNeuron is a neuron that,
6given an array of input bits,
7takes the product of those bits
8and their weights, plus the neuron's "bias",
9and returns the negative inverse of that value.
10
11Unlike a Perceptron, the SigmoidNeuron
12returns values between 0 and 1.
13
14@class
15@implements Neuron
16*/
17function SigmoidNeuron () {
18142 return Neuron.apply(this, arguments);
19}
20
211util.inherits(SigmoidNeuron, Neuron);
22
23/**
24Given an array of input bits,
25takes the product of those bits
26and their weights, plus the neuron's "bias",
27and returns the negative inverse of that value.
28
29@function
30@param {array} input - An array of input bits.
31*/
321SigmoidNeuron.prototype._process = function (input) {
337421 var self = this;
347421 var result = input.map(function (x, i) {
3521545 return (self.weight(i) * x);
36 }).reduce(function (a, b) {
3721545 return a + b;
38 }, self.bias());
39
407421 var inverse = 1 / (1 + Math.exp(-result));
41
427421 return inverse;
43};
44
451module.exports = SigmoidNeuron;
46