From 42a6a9378ce4ac395ea42953485e41cd5cffc345 Mon Sep 17 00:00:00 2001 From: Eric Date: Wed, 25 Nov 2020 21:09:49 -0600 Subject: [PATCH] Add ANN --- src/ai/ann/Layer.js | 16 ++++++++++ src/ai/ann/Network.js | 25 ++++++++++++++++ src/ai/ann/Neuron.js | 38 ++++++++++++++++++++++++ src/ai/ann/math.js | 69 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 148 insertions(+) create mode 100644 src/ai/ann/Layer.js create mode 100644 src/ai/ann/Network.js create mode 100644 src/ai/ann/Neuron.js create mode 100644 src/ai/ann/math.js diff --git a/src/ai/ann/Layer.js b/src/ai/ann/Layer.js new file mode 100644 index 0000000..8babc70 --- /dev/null +++ b/src/ai/ann/Layer.js @@ -0,0 +1,16 @@ +import { Neuron } from "./../utils/Neuron"; + +function Layer([size, activation]) { + let layer = { + neurons: Array.from(Array(size)).map((_) => Neuron(1, activation)), + error: Infinity, + predict, + }; + function predict(inputs) { + layer.output = layer.neurons.map((neuron) => neuron.predict(inputs)); + return layer.output; + } + return layer; +} + +export { Layer }; diff --git a/src/ai/ann/Network.js b/src/ai/ann/Network.js new file mode 100644 index 0000000..a3b92b2 --- /dev/null +++ b/src/ai/ann/Network.js @@ -0,0 +1,25 @@ +import { Layer } from './Layer'; +import { delta, mse } from './math'; +import { sum } from 'lodash'; + +function Network(layerDescriptors, learningRate = 0.5) { + let layers = []; + layers = layerDescriptors.map(Layer); + + const network = { + layers, + forward, + }; + + function forward(inputs) { + const layerOutputs = [layers[0].predict(inputs)]; + layers.slice(1).forEach(layer => { + layerOutputs.push(layer.predict(layerOutputs.slice(-1)[0])); + }); + return layerOutputs.slice(-1)[0]; + } + + return network; +} + +export { Network }; diff --git a/src/ai/ann/Neuron.js b/src/ai/ann/Neuron.js new file mode 100644 index 0000000..9c0862b --- /dev/null +++ b/src/ai/ann/Neuron.js @@ -0,0 +1,38 @@ +import { dot, activations } from "./math"; +import { times } from "lodash"; + +function Neuron(inputQuantity = 1, type = "linear") { + let weights = times(inputQuantity + 1, Math.random); + let neuron = { + weights, + predict, + adjust, + inputs: null, + output: null, + deltaFunction: activations[type].delta, + }; + + function _predict(inputs) { + return dot(neuron.weights, inputs); + } + + function predict(inputs) { + inputs = [...inputs, -1]; + while (inputs.length > neuron.weights.length) + neuron.weights.push(Math.random()); + neuron.inputs = inputs; + neuron.output = activations[type].function(_predict(inputs)); + + return neuron.output; + } + + function adjust(delta) { + for (let i = 0; i < neuron.weights.length; i++) { + neuron.weights[i] += delta * neuron.inputs[i]; + } + } + + return neuron; +} + +export { Neuron }; diff --git a/src/ai/ann/math.js b/src/ai/ann/math.js new file mode 100644 index 0000000..79a657a --- /dev/null +++ b/src/ai/ann/math.js @@ -0,0 +1,69 @@ +import { sumBy } from "lodash"; + +function dot(v1, v2) { + let result = 0; + for (let i = 0; i < v1.length; i++) { + result += v1[i] * v2[i]; + } + return result; +} + +function generateLine(neuron) { + const [w1, w2] = neuron.weights; + const bias = neuron.bias; + // y = b/w2 - w1x1/w2 + let leftLimit = -2, + rightLimit = 3; + + let p1 = { x: leftLimit, y: bias / w2 - (w1 * leftLimit) / w2 }; + let p2 = { x: rightLimit, y: bias / w2 - (w1 * rightLimit) / w2 }; + return [p1, p2]; +} + +function mse(arr) { + return sumBy(arr, (x) => Math.pow(x, 2)) / arr.length; +} + +const activations = { + step: { + function: function (output) { + return output > 0 ? 1 : 0; + }, + delta: null, + }, + sigmoid: { + function: function (output) { + const ex = Math.exp(-output); + return 1 / (ex + 1); + }, + delta: function (output, error) { + return output * (1 - output) * error; + }, + }, + relu: { + function: function (output) { + return Math.max(0, output); + }, + delta: function (output, error) { + return (output < 0 ? 0 : 1) * error; + }, + }, + lrelu: { + function: function (output) { + return Math.max(0.1 * output, output); + }, + delta: function (output, error) { + return (output < 0 ? 0.1 : 1) * error; + }, + }, + linear: { + function: function (output) { + return output; + }, + delta: function (output, error) { + return 1 * error; + }, + }, +}; + +export { dot, activations, mse };