-
Notifications
You must be signed in to change notification settings - Fork 665
Networks
Networks are basically an array of layers. They have an input layer, a number of hidden layers, and an output layer. Networks can project and gate connections, activate and propagate in the same fashion as Layers do. Networks can also be optimized, extended, exported to JSON, converted to Workers or standalone Functions, and cloned.
var inputLayer = new Layer(4);
var hiddenLayer = new Layer(6);
var outputLayer = new Layer(2);
inputLayer.project(hiddenLayer);
hiddenLayer.project(outputLayer);
var myNetwork = new Network({
input: inputLayer,
hidden: [hiddenLayer],
output: outputLayer
});
A network can project a connection to another, or gate a connection between two others networks in the same way Layers do.
You have to provide the network that you want to connect to and the connectionType
:
myNetwork.project(otherNetwork, Layer.connectionType.ALL_TO_ALL);
/*
All the neurons in myNetwork's output layer now project a connection
to all the neurons in otherNetwork's input layer.
*/
There are two connectionType
s:
-
Layer.connectionType.ALL_TO_ALL
: It connects every neuron from layer A, to every neuron in layer B. -
Layer.connectionType.ONE_TO_ONE
: It connects each neuron from layer A, to one neuron in layer B. Both layers must be the same size in order to work. -
Layer.connectionType.ALL_TO_ELSE
: Useful only in self-connections. It connects every neuron from a layer to all the other neurons in that same layer, except with itself. If thisconnectionType
is used in a connection between different layers, it produces the same result asALL_TO_ALL
.
If not specified, the connection type is always Layer.connectionType.ALL_TO_ALL
.
The method project returns a LayerConnection
object, that can be gated by another network or layer.
A Network can gate a connection between two other Networks or Layers, or a Layers's self-connection.
var connection = A.project(B);
C.gate(connection, Layer.gateType.INPUT_GATE); // now C's output layer gates the connection between A's output layer and B's input layer (input gate)
There are three gateType
s:
-
Layer.gateType.INPUT_GATE
: If network C is gating connections between network A and B, all the neurons from C's output layer gate all the input connections to B's input layer. -
Layer.gateType.OUTPUT_GATE
: If network C is gating connections between network A and B, all the neurons from C's output layer gate all the output connections from A's output layer. -
Layer.gateType.ONE_TO_ONE
: If network C is gating connections between network A and B, each neuron from C's output layer gates one connection from A's output layer to B's input layer. To use this kind of gateType, A's output layer, B's input layer and C's output layer must be the same size.
When a network is activated, an input must be provided to activate the input layer, then all the hidden layers are activated in order, and finally the output layer is activated and its activation is returned.
var inputLayer = new Layer(4);
var hiddenLayer = new Layer(6);
var outputLayer = new Layer(2);
inputLayer.project(hiddenLayer);
hiddenLayer.project(outputLayer);
var myNetwork = new Network({
input: inputLayer,
hidden: [hiddenLayer],
output: outputLayer
});
myNetwork.activate([1,0,1,0]); // [0.5200553602396137, 0.4792707231811006]
You can provide a target value and a learning rate to a network and backpropagate the error from the output layer to all the hidden layers in reverse order until reaching the input layer. For example, this is how you train a network how to solve an XOR:
// create the network
var inputLayer = new Layer(2);
var hiddenLayer = new Layer(3);
var outputLayer = new Layer(1);
inputLayer.project(hiddenLayer);
hiddenLayer.project(outputLayer);
var myNetwork = new Network({
input: inputLayer,
hidden: [hiddenLayer],
output: outputLayer
});
// train the network
var learningRate = .3;
for (var i = 0; i < 20000; i++)
{
// 0,0 => 0
myNetwork.activate([0,0]);
myNetwork.propagate(learningRate, [0]);
// 0,1 => 1
myNetwork.activate([0,1]);
myNetwork.propagate(learningRate, [1]);
// 1,0 => 1
myNetwork.activate([1,0]);
myNetwork.propagate(learningRate, [1]);
// 1,1 => 0
myNetwork.activate([1,1]);
myNetwork.propagate(learningRate, [0]);
}
// test the network
myNetwork.activate([0,0]); // [0.015020775950893527]
myNetwork.activate([0,1]); // [0.9815816381088985]
myNetwork.activate([1,0]); // [0.9871822457132193]
myNetwork.activate([1,1]); // [0.012950087641929467]
Networks get optimized automatically on the fly after its first activation, if you print in the console the activate
or propagate
methods of your Network instance after activating it, it will look something like this:
function (input){
F[1] = input[0];
F[2] = input[1];
F[3] = input[2];
F[4] = input[3];
F[6] = F[7];
F[7] = F[8];
F[7] += F[1] * F[9];
F[7] += F[2] * F[10];
F[7] += F[3] * F[11];
F[7] += F[4] * F[12];
F[5] = (1 / (1 + Math.exp(-F[7])));
F[13] = F[5] * (1 - F[5]);
...
This improves the performance of the network dramatically.
You can see how to extend a network in the Examples section.
Networks can be stored as JSON's and then restored back:
var exported = myNetwork.toJSON();
var imported = Network.fromJSON(exported);
imported
will be a new instance of Network
that is an exact clone of myNetwork
The network can be converted into a WebWorker. This feature doesn't work in node.js, and it's not supported on every browser (it must support Blob).
// training set
var learningRate = .3;
var trainingSet = [
{
input: [0,0],
output: [0]
},
{
input: [0,1],
output: [1]
},
{
input: [1,0],
output: [1]
},
{
input: [1,1],
output: [0]
},
];
// create a network
var inputLayer = new Layer(2);
var hiddenLayer = new Layer(3);
var outputLayer = new Layer(1);
inputLayer.project(hiddenLayer);
hiddenLayer.project(outputLayer);
var myNetwork = new Network({
input: inputLayer,
hidden: [hiddenLayer],
output: outputLayer
});
// create a worker
var myWorker = myNetwork.worker();
// activate the network
function activateWorker(input)
{
myWorker.postMessage({
action: "activate",
input: input,
memoryBuffer: myNetwork.optimized.memory
}, [myNetwork.optimized.memory.buffer]);
}
// backpropagate the network
function propagateWorker(target){
myWorker.postMessage({
action: "propagate",
target: target,
rate: learningRate,
memoryBuffer: myNetwork.optimized.memory
}, [myNetwork.optimized.memory.buffer]);
}
// train the worker
myWorker.onmessage = function(e){
// give control of the memory back to the network - this is mandatory!
myNetwork.optimized.ownership(e.data.memoryBuffer);
if (e.data.action == "propagate")
{
if (index >= 4)
{
index = 0;
iterations++;
if (iterations % 100 == 0)
{
var output00 = myNetwork.activate([0,0]);
var output01 = myNetwork.activate([0,1]);
var output10 = myNetwork.activate([1,0]);
var output11 = myNetwork.activate([1,1]);
console.log("0,0 => ", output00);
console.log("0,1 => ", output01);
console.log("1,0 => ", output10);
console.log("1,1 => ", output11, "\n");
}
}
activateWorker(trainingSet[index].input);
}
if (e.data.action == "activate")
{
propagateWorker(trainingSet[index].output);
index++;
}
}
// kick it
var index = 0;
var iterations = 0;
activateWorker(trainingSet[index].input);
The network can be exported to a single javascript Function. This can be useful when your network is already trained and you just need to use it, since the standalone functions is just one javascript function with an array and operations within, with no dependencies on Synaptic or any other library.
var inputLayer = new Layer(4);
var hiddenLayer = new Layer(6);
var outputLayer = new Layer(2);
inputLayer.project(hiddenLayer);
hiddenLayer.project(outputLayer);
var myNetwork = new Network({
input: inputLayer,
hidden: [hiddenLayer],
output: outputLayer
});
var standalone = myNetwork.standalone();
myNetwork.activate([1,0,1,0]); // [0.5466397925108878, 0.5121246668637663]
standalone([1,0,1,0]); // [0.5466397925108878, 0.5121246668637663]
A network can be cloned to a completely new instance, with the same connections and traces.
var inputLayer = new Layer(4);
var hiddenLayer = new Layer(6);
var outputLayer = new Layer(2);
inputLayer.project(hiddenLayer);
hiddenLayer.project(outputLayer);
var myNetwork = new Network({
input: inputLayer,
hidden: [hiddenLayer],
output: outputLayer
});
var clone = myNetwork.clone();
myNetwork.activate([1,0,1,0]); // [0.5466397925108878, 0.5121246668637663]
clone.activate([1,0,1,0]); // [0.5466397925108878, 0.5121246668637663]
The method neurons()
return an array with all the neurons in the network, in activation order.
The method set(layers)
receives an object with layers in the same format as the constructor of Network
and sets them as the layers of the Network, this is useful when you are extending the Network
class to create your own architectures. See the examples section.
var inputLayer = new Layer(4);
var hiddenLayer = new Layer(6);
var outputLayer = new Layer(2);
inputLayer.project(hiddenLayer);
hiddenLayer.project(outputLayer);
var myNetwork = new Network();
myNetwork.set({
input: inputLayer,
hidden: [hiddenLayer],
output: outputLayer
});
The method clear()
clears the networks contextual memory, while leaving the network's weights unmodified.
This can be useful in LSTM's when the network needs to be activated with a new sequence of data that should not use context from previous activations.