var AI = (function () { var AI = {}; var isArrayOf = function (a, type, length) { if ((typeof a !== "object") || (!Array.isArray()) || ((typeof length === "number") && (a.length !== length)) || (((typeof type === "string") && (typeof a[0] !== type)) || ((typeof type === "function") && type(a))) ) { return false; } return true; }; var sigmoid = function (x) { return 1 / (1 + Math.pow(Math.E, -x)); }; AI.Neuron = (function () { var Neuron = function (inputLength, func) { if (typeof func !== "function") { func = function (x) { return sigmoid(x); }; } if (typeof length !== "number") { inputLength = 1; } inputLength = Math.max(Math.floor(inputLength), 1); this.func = func; this.bias = Math.random(); this.inputWeights = []; for (var i = 0; i < inputLength; ++i) { this.inputWeights.push(Math.random()); } }; Neuron.prototype.compute = function (input) { if (isArrayOf(input, "number", this.inputWeights.length)) { // FAIL, do something when it fails. return; } var total = this.bias; for (var i = 0; i < this.inputWeights.length; ++i) { total += input[i] * this.inputWeights[i]; } this.prevWeightedSum = total; this.prevOutput = this.func(total); return this.prevOutput; }; return Neuron; })(); AI.NeuronLayer = (function () { var NeuronLayer = function (inputLength, neuronLength, func) { var correctArgs = function (n) { if (typeof n !== "number") { n = 1; } return Math.max(Math.floor(n), 1); }; inputLength = correctArgs(inputLength); neuronLength = correctArgs(neuronLength); this.inputLength = inputLength; this.neurons = []; for (var i = 0; i < neuronLength; ++i) { this.neurons.push(new AI.Neuron(inputLength, func)); } }; NeuronLayer.prototype.compute = function (input) { if (isArrayOf(input, "number", this.inputLength)) { // FAIL, do something when it fails. return; } var outputs = []; for (var i = 0; i < this.neurons.length; ++i) { outputs.push(this.neurons[i].compute.apply(this.neurons[i], [input])); } return outputs; }; return NeuronLayer; })(); AI.NeuronNetwork = (function () { var NeuronNetwork = function (input) { if (isArrayOf(input, "number") || (input.length <= 1)) { // FAIL, do something when it fails. return; } this.inputLength = input[0]; this.neuronLayers = []; for (var i = 1; i < (input.length-1); ++i) { this.neuronLayers.push(new AI.NeuronLayer(input[i-1], input[i])); } this.neuronLayers.push(new AI.NeuronLayer(input[input.length-2], input[input.length-1], function (x) { return x; })); }; NeuronNetwork.prototype.compute = function (input) { if (isArrayOf(input, "number", this.inputLength)) { // FAIL, do something when it fails. return; } var output = input; for (var i = 0; i < this.neuronLayers.length; ++i) { output = this.neuronLayers[i].compute(output); } return output; }; NeuronNetwork.prototype.train = function (input, desiredOutput, learningRate) { if (typeof learningRate !== "number") { learningRate = 0.25; } if (typeof momentum !== "number") { momentum = 0.04; } var output = this.compute(input); /*var gradients = []; for (var i = 0; i < this.neuronLayers.length; ++i) { gradients.push([]); for (var j = 0; j < this.neuronLayers[i].neurons.length; ++j) { gradients[i].push(0); } } for (var i = 0; i < this.neuronLayers[this.neuronLayers.length-1].neurons.length; ++i) { gradients[this.neuronLayers.length-1][i] = ((desiredOutput[i] - output[i]) * (1 - output[i]) * (1 + output[i])); } for (var i = (this.neuronLayers.length-2); i >= 0; --i) { var inputs = []; if (i === 0) { for (var j = 0; j < input.length; ++j) { inputs.push(input[i]); } } else { for (var j = 0; j < input.length; ++j) { inputs.push(this.neuronLayers[i-1]); } } for (var j = 0; j < this.neuronLayers[i].neurons.length; ++j) { var neuronOutput = this.neuronLayers[i].neurons[j].prevOutput; var prevNeuronLayer = this.neuronLayers[i+1]; var outputSum = 0; for (var k = 0; k < prevNeuronLayer.neurons.length; ++k) { //console.log(gradients[i+1][k], prevNeuronLayer.neurons[k].inputWeights[j]); outputSum += (gradients[i+1][k] * prevNeuronLayer.neurons[k].inputWeights[j]); } //console.log("neuron delta", i, j, neuronOutput, outputSum); gradients[i][j] = (neuronOutput * (1 - neuronOutput) * outputSum); } } //console.log("gradients", gradients); for (var i = 0; i < this.neuronLayers[0].neurons.length; ++i) { var currNeuron = this.neuronLayers[0].neurons[i]; for (var j = 0; j < input.length; ++j) { currNeuron.inputWeights[j] += -(learningRate * gradients[0][i] * input[j]); //console.log("input delta", (learningRate * gradients[0][i] * input[j])); } } for (var i = 1; i < this.neuronLayers.length; ++i) { var currNeuronLayer = this.neuronLayers[i]; for (var j = 0; j < currNeuronLayer.length; ++j) { var currNeuron = currNeuronLayer.neurons[j]; currNeuron.bias += -(learningRate * gradients[i][j]); for (var k = 0; k < currNeuron.inputWeights.length; ++k) { currNeuron.inputWeights[k] += -(learningRate * gradients[i][j] * this.neuronLayers[i-1].neurons[k].prevOutput); //console.log("neuron delta", -(learningRate * gradients[i][j] * this.neuronLayers[i-1].neurons[k].prevOutput)); } } }*/ var errors = []; for (var i = 0; i < this.neuronLayers.length; ++i) { errors.push([]); for (var j = 0; j < this.neuronLayers[i].neurons.length; ++j) { errors[i].push(0); } } for (var i = 0, neuronLayer = this.neuronLayers[this.neuronLayers.length-1]; i < neuronLayer.neurons.length; ++i) { errors[this.neuronLayers.length-1][i] = (desiredOutput[i] - output[i]) * 1; } for (var i = (this.neuronLayers.length-2); i >= 1; --i) { var inputs = []; if (i === 1) { for (var j = 0; j < input.length; ++j) { inputs.push(input[i]); } } else { for (var j = 0; j < input.length; ++j) { inputs.push(this.neuronLayers[i-1].neurons[j].prevOutput); } } for (var j = 0; j < this.neuronLayers[i].neurons.length; ++j) { var neuronOutput = this.neuronLayers[i].neurons[j].prevOutput; var prevNeuronLayer = this.neuronLayers[i+1]; var outputSum = 0; for (var k = 0; k < prevNeuronLayer.neurons.length; ++k) { //console.log(errors[i+1][k], prevNeuronLayer.neurons[k].inputWeights[j]); outputSum += (errors[i+1][k] * prevNeuronLayer.neurons[k].inputWeights[j]); } //console.log("neuron delta", i, j, neuronOutput, outputSum); errors[i][j] = (neuronOutput * (1 - neuronOutput) * outputSum); } } //console.log("gradients", gradients); for (var i = 0; i < this.neuronLayers[0].neurons.length; ++i) { var currNeuron = this.neuronLayers[0].neurons[i]; for (var j = 0; j < input.length; ++j) { currNeuron.inputWeights[j] += (learningRate * errors[0][i] * input[j]); //console.log("input delta", (learningRate * errors[0][i] * input[j])); } } for (var i = 1; i < this.neuronLayers.length; ++i) { var currNeuronLayer = this.neuronLayers[i]; for (var j = 0; j < currNeuronLayer.neurons.length; ++j) { var currNeuron = currNeuronLayer.neurons[j]; currNeuron.bias += (learningRate * errors[i][j]); for (var k = 0; k < currNeuron.inputWeights.length; ++k) { currNeuron.inputWeights[k] += (learningRate * errors[i][j] * this.neuronLayers[i-1].neurons[k].prevOutput); //console.log("neuron delta", (learningRate * errors[i][j] * this.neuronLayers[i-1].neurons[k].prevOutput)); } } } return output; }; return NeuronNetwork; })(); AI.canvasTest = function (neuronLayout) { if (typeof neuronLayout === "undefined") { neuronLayout = [10, 64]; } window.test = { network: new AI.NeuronNetwork(neuronLayout), training: [ { input: [], output: [ 0, 255, 255, 255, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 255, 0, 255, 255, 0, 255, 255, 255, 255, 0, 255, 255, 255, 255, 0, 255, 255, 0, 255, 255, 255, 0, 0, 255, 255, 0, 255, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ] }, { input: [], output: [ 0, 0, 0, 255, 255, 0, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0 ] }, { input: [], output: [ 0, 255, 255, 255, 255, 255, 0, 0, 255, 255, 0, 0, 0, 255, 255, 0, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 255, 255, 255, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 255, 255, 0, 0, 0, 255, 255, 0, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0 ] }, { input: [], output: [ 0, 255, 255, 255, 255, 255, 0, 0, 255, 255, 0, 0, 0, 255, 255, 0, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 255, 255, 0, 255, 255, 0, 0, 0, 255, 255, 0, 0, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ] }, { input: [], output: [ 0, 0, 0, 255, 255, 255, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 255, 255, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0 ] }, { input: [], output: [ 255, 255, 255, 255, 255, 255, 255, 0, 255, 255, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 0, 0, 255, 255, 0, 255, 255, 0, 0, 0, 255, 255, 0, 0, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ] }, { input: [], output: [ 0, 255, 255, 255, 255, 255, 0, 0, 255, 255, 0, 0, 0, 255, 255, 0, 255, 255, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 0, 0, 255, 255, 0, 0, 0, 255, 255, 0, 255, 255, 0, 0, 0, 255, 255, 0, 0, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ] }, { input: [], output: [ 255, 255, 255, 255, 255, 255, 255, 0, 255, 255, 0, 0, 0, 255, 255, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ] }, { input: [], output: [ 0, 255, 255, 255, 255, 255, 0, 0, 255, 255, 0, 0, 0, 255, 255, 0, 255, 255, 0, 0, 0, 255, 255, 0, 0, 255, 255, 255, 255, 255, 0, 0, 255, 255, 0, 0, 0, 255, 255, 0, 255, 255, 0, 0, 0, 255, 255, 0, 0, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ] }, { input: [], output: [ 0, 255, 255, 255, 255, 255, 0, 0, 255, 255, 0, 0, 0, 255, 255, 0, 255, 255, 0, 0, 0, 255, 255, 0, 0, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 255, 255, 0, 255, 255, 0, 0, 0, 255, 255, 0, 0, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ] } ], draw: function (result) { { var canvas = $("#canvas")[0]; var ctx = canvas.getContext("2d"); ctx.clearRect(0, 0, canvas.width, canvas.height); var pixel = ctx.createImageData(1, 1); var data = pixel.data; for (var i = 0; i < result.length; ++i) { data[0] = 255-result[i]; data[1] = 255-result[i]; data[2] = 255-result[i]; data[3] = 255; //ctx.putImageData(pixel, (i % 8), Math.floor(i / 8)); //console.log((i % 8), Math.floor(i / 8), data, "rgba("+data[0]+","+data[1]+","+data[2]+","+(data[3]/255)+")"); ctx.fillStyle = "rgba("+data[0]+","+data[1]+","+data[2]+","+(data[3]/255)+")"; ctx.fillRect((i % 8)*canvas.width/8, Math.floor(i / 8)*canvas.height/8, canvas.width/8, canvas.height/8); } } for (var i = 0; i < 10; ++i) { var canvasx = $("#canvas"+i)[0]; var ctxx = canvasx.getContext("2d"); ctxx.clearRect(0, 0, canvasx.width, canvasx.height); var pic = this.compute(i); var pixel = ctxx.createImageData(1, 1); var data = pixel.data; for (var j = 0; j < pic.length; ++j) { data[0] = 255-pic[j]; data[1] = 255-pic[j]; data[2] = 255-pic[j]; data[3] = 255; ctxx.fillStyle = "rgba("+data[0]+","+data[1]+","+data[2]+","+(data[3]/255)+")"; ctxx.fillRect((j % 8)*canvasx.width/8, Math.floor(j / 8)*canvasx.height/8, canvasx.width/8, canvasx.height/8); } } }, compute: function (n, draw) { var result = this.network.compute(this.training[n].input); if (draw) { this.draw(result); } return result; }, train: function (n, draw, learningRate) { var result = this.network.train(this.training[n].input, this.training[n].output, learningRate); if (draw) { this.draw(result); } return result; }, trainAll: function (amount, draw, learningRate) { if (typeof amount !== "number") { amount = 1; } for (var i = 0; i < amount; ++i) { for (var j = 0; j < 10; ++j) { this.train(j, draw); } } }, trainRand: function (learningRate) { this.train(Math.floor(Math.random()*10), true, learningRate); (function (self) { window.setTimeout(function () { self.trainRand(); }, 200); })(this); } }; for (var i = 0; i < 10; ++i) { var currInput = window.test.training[i].input; for (var j = 0; j < 10; ++j) { currInput.push((i === j) ? 1 : 0); } } $("body").append("
"); for (var i = 0; i < 5; ++i) { $("body").append(""); } $("body").append("