Created
February 29, 2020 21:34
-
-
Save GuilhermeRossato/dcc28716fa4700d24b7ac862d36e5dde to your computer and use it in GitHub Desktop.
Simple Artificial Network with Javascript
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var sigmoid_lookup_size = 128; | |
var sigmoid_lookup = (new Float32Array(sigmoid_lookup_size+1)).map((a,i) => 1/(1+Math.exp(-(i-(sigmoid_lookup_size/2))/(sigmoid_lookup_size/32)))); | |
function real_sigmoid(x) { | |
return 1 / (1 + Math.exp(-x)); | |
} | |
function cached_sigmoid(x) { | |
if (x < -16) { | |
return -16; | |
} else if (x >= 16) { | |
return 16; | |
} | |
const position = x * (sigmoid_lookup_size/32) + sigmoid_lookup_size / 2; | |
const i = position|0; | |
if (i+1 > sigmoid_lookup_size) { | |
return 1; | |
} else if (i < 0) { | |
return 0; | |
} | |
const diff = position - i; | |
return sigmoid_lookup[i] * (1 - diff) + sigmoid_lookup[i+1] * diff; | |
} | |
class NeuralNetwork { | |
constructor(inputs, hidden_layers, hidden, outputs) { | |
if (inputs < 1 || hidden_layers < 0 || outputs < 1) { | |
throw new Error("Invalid parameters"); | |
} | |
const hidden_weights = hidden_layers <= 0 ? 0 : (inputs+1) * hidden + (hidden_layers-1) * (hidden+1) * hidden; | |
const output_weights = (hidden_layers ? (hidden+1) : (inputs+1)) * outputs; | |
const total_weights = (hidden_weights + output_weights); | |
const total_neurons = (inputs + hidden * hidden_layers + outputs); | |
let size = 4 + 2 + (total_weights + total_neurons + total_neurons - inputs); | |
size -= outputs; | |
const data = this.data = new Float32Array(size); | |
data[0] = this.inputs = inputs; | |
data[1] = this.hidden_layers = hidden_layers; | |
data[2] = this.hidden = hidden; | |
data[3] = this.outputs = outputs; | |
data[4] = this.total_weights = total_weights; | |
data[5] = this.total_neurons = total_neurons; | |
this.weight = 6; | |
this.output = this.weight + total_weights; | |
this.delta = this.output + total_neurons; | |
this.random = Math.random; | |
this.randomize(); | |
this.sigmoid = cached_sigmoid; | |
} | |
randomize() { | |
for (let i = 0; i < this.total_weights; i++) { | |
this.data[this.weight + i] = this.random() - 0.5; | |
} | |
} | |
act_output(sum) { | |
return this.sigmoid(sum); | |
} | |
run(inputs) { | |
if (inputs.length !== this.inputs) { | |
throw new Error("Input doest not match, expected "+this.inputs+", got "+inputs.length); | |
} | |
let w = this.weight; | |
let o = this.output + this.inputs; | |
let i = this.output; | |
let h, j, k; | |
const result = new Float32Array(this.outputs); | |
// Treat first layer the same way we treat other layers for consistency | |
for (h = 0; h < inputs.length; h++) { | |
this.data[this.output + h] = inputs[h]; | |
} | |
// Simple case where there are no hidden layers | |
if (!this.hidden_layers) { | |
for (j = 0; j < this.outputs; j++) { | |
let sum = this.data[w] * -1.0; | |
w++; | |
for (k = 0; k < this.inputs; k++) { | |
sum += this.data[w] * this.data[i + k]; | |
w++; | |
} | |
result[j] = this.act_output(sum); | |
this.data[o] = this.act_output(sum); | |
o++; | |
} | |
return result; | |
} | |
/* Figure input layer */ | |
for (j = 0; j < this.hidden; j++) { | |
let sum = this.data[w] * -1.0; | |
w++; | |
for (k = 0; k < this.inputs; k++) { | |
sum += this.data[w] * this.data[i + k]; | |
w++; | |
} | |
this.data[o] = this.act_output(sum); | |
o++; | |
} | |
i += this.inputs; | |
/* Calculate hidden layers. */ | |
for (h = 1; h < this.hidden_layers; h++) { | |
for (j = 0; j < this.hidden; j++) { | |
let sum = this.data[w] * -1.0; | |
w++; | |
for (k = 0; k < this.hidden; k++) { | |
sum += this.data[w] * this.data[i + k]; | |
w++; | |
} | |
this.data[o] = this.sigmoid(sum); | |
o++; | |
} | |
i += this.hidden; | |
} | |
let ret = o; | |
/* Calculate output layer. */ | |
for (j = 0; j < this.outputs; j++) { | |
let sum = this.data[w] * -1.0; | |
w++; | |
for (k = 0; k < this.hidden; k++) { | |
sum += this.data[w] * this.data[i + k]; | |
w++; | |
} | |
result[j] = this.data[o] = this.sigmoid(sum); | |
o++; | |
} | |
/* Sanity check that we used all weights and wrote all outputs. */ | |
if (w - this.weight != this.total_weights) { | |
console.log("Failed test 1"); | |
} | |
if (o - this.output != this.total_neurons) { | |
console.log("Failed test 2", o, this.output, this.total_neurons); | |
} | |
return result; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const ann = new NeuralNetwork(3, 1, 1, 1); | |
console.log(ann.total_weights, "weights"); | |
const input = [1, 1, 1]; | |
console.log("generated", ann.run(input)[0]); | |
const w0 = ann.data[ann.weight + 0]; | |
const w1 = ann.data[ann.weight + 1]; | |
const w2 = ann.data[ann.weight + 2]; | |
const w3 = ann.data[ann.weight + 3]; | |
const w4 = ann.data[ann.weight + 4]; | |
const w5 = ann.data[ann.weight + 5]; | |
const hiddenLayer = 1 - cached_sigmoid(w0 - input[0] * w1 - input[1] * w2 - input[2] * w3); | |
const outputLayer = 1 - cached_sigmoid(w4 - hiddenLayer * w5); | |
console.log("calculated", outputLayer); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment