I've tried to rewrite neural network found here to javascript. My javascript code looks like this.
function NeuralFactor(weight) {
var self = this;
this.weight = weight;
this.delta = 0;
}
function Sigmoid(value) {
return 1 / (1 + Math.exp(-value));
}
function Neuron(isInput) {
var self = this;
this.pulse = function() {
self.output = 0;
self.input.forEach(function(item) {
self.output += item.signal.output * item.factor.weight;
});
self.output += self.bias.weight;
self.output = Sigmoid(self.output);
};
this.bias = new NeuralFactor(isInput ? 0 : Math.random());
this.error = 0;
this.input = [];
this.output = 0;
this.findInput = function(signal) {
var input = self.input.filter(function(input) {
return signal == input.signal;
})[0];
return input;
};
}
function NeuralLayer() {
var self = this;
this.pulse = function() {
self.neurons.forEach(function(neuron) {
neuron.pulse();
});
};
this.neurons = [];
this.train = function(learningRate) {
self.neurons.forEach(function(neuron) {
neuron.bias.weight += neuron.bias.delta * learningRate;
neuron.bias.delta = 0;
neuron.input.forEach(function(input) {
input.factor.weight += input.factor.delta * learningRate;
input.factor.delta = 0;
})
})
}
}
function NeuralNet(inputCount, hiddenCount, outputCount) {
var self = this;
this.inputLayer = new NeuralLayer();
this.hiddenLayer = new NeuralLayer();
this.outputLayer = new NeuralLayer();
this.learningRate = 0.5;
for(var i = 0; i < inputCount; i++)
self.inputLayer.neurons.push(new Neuron(true));
for(var i = 0; i < hiddenCount; i++)
self.hiddenLayer.neurons.push(new Neuron());
for(var i = 0; i < outputCount; i++)
self.outputLayer.neurons.push(new Neuron());
for (var i = 0; i < hiddenCount; i++)
for (var j = 0; j < inputCount; j++)
self.hiddenLayer.neurons[i].input.push({
signal: self.inputLayer.neurons[j],
factor: new NeuralFactor(Math.random())
});
for (var i = 0; i < outputCount; i++)
for (var j = 0; j < hiddenCount; j++)
self.outputLayer.neurons[i].input.push({
signal: self.hiddenLayer.neurons[j],
factor: new NeuralFactor(Math.random())
});
this.pulse = function() {
self.hiddenLayer.pulse();
self.outputLayer.pulse();
};
this.backPropagation = function(desiredResults) {
for(var i = 0; i < self.outputLayer.neurons.length; i++) {
var outputNeuron = self.outputLayer.neurons[i];
var output = outputNeuron.output;
outputNeuron.error = (desiredResults[i] - output) * output * (1.0 - output);
}
for(var i = 0; i < self.hiddenLayer.neurons.length; i++) {
var hiddenNeuron = self.hiddenLayer.neurons[i];
var error = 0;
for(var j = 0; j < self.outputLayer.neurons.length; j++) {
var outputNeuron = self.outputLayer.neurons[j];
error += outputNeuron.error * outputNeuron.findInput(hiddenNeuron).factor.weight * hiddenNeuron.output * (1.0 - hiddenNeuron.output);
}
hiddenNeuron.error = error;
}
for(var j = 0; j < self.outputLayer.neurons.length; j++) {
var outputNeuron = self.outputLayer.neurons[j];
for(var i = 0; i < self.hiddenLayer.neurons.length; i++) {
var hiddenNeuron = self.hiddenLayer.neurons[i];
outputNeuron.findInput(hiddenNeuron).factor.delta += outputNeuron.error * hiddenNeuron.output;
}
outputNeuron.bias.delta += outputNeuron.error * outputNeuron.bias.weight;
}
for(var j = 0; j < self.hiddenLayer.neurons.length; j++) {
var hiddenNeuron = self.hiddenLayer.neurons[j];
for(var i = 0; i < self.inputLayer.neurons.length; i++) {
var inputNeuron = self.inputLayer.neurons[i];
hiddenNeuron.findInput(inputNeuron).factor.delta += hiddenNeuron.error * inputNeuron.output;
}
hiddenNeuron.bias.delta += hiddenNeuron.error * hiddenNeuron.bias.weight;
}
};
this.train = function(input, desiredResults) {
for(var i = 0; i < self.inputLayer.neurons.length; i++) {
var neuron = self.inputLayer.neurons[i];
neuron.output = input[i];
}
self.pulse();
self.backPropagation(desiredResults);
self.hiddenLayer.train(self.learningRate);
self.outputLayer.train(self.learningRate);
};
}
Now I'm trying to learn it how to resolve XOR problem. I'm teaching it like this:
var net = new NeuralNet(2,2,1);
var testInputs = [[0,0], [0,1], [1,0], [1,1]];
var testOutputs = [[1],[0],[0],[1]];
for (var i = 0; i < 1000; i++)
for(var j = 0; j < 4; j++)
net.train(testInputs[j], testOutputs[j]);
function UseNet(a, b) {
net.inputLayer.neurons[0].output = a;
net.inputLayer.neurons[1].output = b;
net.pulse();
return net.outputLayer.neurons[0].output;
}
The problem is that all results that I get is close to 0.5 and pretty random, no matter what arguments I use. For example:
UseNet(0,0) => 0.5107701166677714
UseNet(0,1) => 0.4801498747476413
UseNet(1,0) => 0.5142463167153447
UseNet(1,1) => 0.4881829364416052
What can be wrong with my code?
Neural networks CAN fail to learn a function; this is most often caused by employing a network topology which is too simple to model the necessary function.
It really does not have to be Python to get started with deep learning. JavaScript equips you with everything you need to create your own neural network from the scratch.
Just like every other supervised machine learning model, neural networks learn relationships between input variables and output variables. In fact, we can even see how it's related to the most iconic model of all, linear regression.
This network is big enough for the XOR problem and I can't see any obvious mistakes, so I suspect it's getting stuck in a local minimum.
Try going through the training set 10,000 times instead of 1000; this gives it a better chance of breaking out of any minima and converging. You can also increase convergence a lot by upping the number of hidden neurons, tweaking η (the learning rate) or adding momentum. To implement the latter, try using this as your training function:
this.train = function(learningRate) {
var momentum = 0 /* Some value, probably fairly small. */;
self.neurons.forEach(function(neuron) {
neuron.bias.weight += neuron.bias.delta * learningRate;
neuron.bias.delta = 0;
neuron.input.forEach(function(input) {
input.factor.weight += (input.factor.delta * learningRate) + (input.factor.weight * momentum);
input.factor.delta = 0;
})
})
}
I've had good results changing the learning rate to 1.5 (which is pretty high) and momentum to 0.000001 (which is pretty small).
(Incidentally, have you tried running the .NET implementation with a few different seeds? It can take quite a while to converge too!)
This system uses fuzzy logic. As it says in the article don't use integers instead use "close" real numbers as the article suggests -- try
UseNet(0.1,0.1) =>
UseNet(0.1,0.9) =>
UseNet(0.9,0.1) =>
UseNet(0.9,0.9) =>
For the results anything above 0.5 is a 1 and below is 0
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With