UNPKG

layerganza

Version:

A feed-forward neural network with injectable layers, activation functions, and optimizers.

80 lines (79 loc) 3.97 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); class OutputLayer { constructor(nodeCount, activationFunction, optimizer) { this.nodeCount = nodeCount; this.outputs = new Float64Array(nodeCount); this.activationFunction = activationFunction.xToY; this.activationFunctionDerivative = activationFunction.yToSlope; this.errorGradients = new Float64Array(nodeCount); this.optimizer = optimizer; this.inputs = new Float64Array(0); //@TODO avoid unneeded empty array?; this.inputLayer = null; this.inputCount = 0; //@TODO avoid this? this.inputNodeCount = 0; //@TODO avoid this? this.weights = new Float64Array(0); //@TODO avoid unneeded empty array? this.weightErrorGradients = new Float64Array(0); //@TODO avoid unneeded empty array? } setInputLayer(inputLayer) { this.inputLayer = inputLayer; this.inputCount = inputLayer.nodeCount; this.inputNodeCount = this.inputCount + 1; //Add 1 for the bias node this.weights = new Float64Array(this.nodeCount * this.inputNodeCount); this.weightErrorGradients = new Float64Array(this.nodeCount * this.inputNodeCount); for (let weightI = 0, weightLen = this.weights.length; weightI < weightLen; weightI++) { this.weights[weightI] = Math.random() - 0.5; //@TODO would a gaussian distribution work better? } this.optimizer.init(this.weights.length); } feedForward() { this.inputs = this.inputLayer.outputs; //@TODO is this needed? //Defining these locally speeds up the loop below by reducing object property access let inputNodeCount = this.inputNodeCount; let weights = this.weights; let inputs = this.inputs; let activationFunction = this.activationFunction; let outputs = this.outputs; let nodeCount = this.nodeCount; let inputCount = this.inputCount; // Defining these here ideally speeds up the loop below let inputI; let sum; for (let neuronI = 0; neuronI < nodeCount; neuronI++) { sum = 0; for (inputI = 0; inputI < inputCount; inputI++) { sum += inputs[inputI] * weights[neuronI * inputNodeCount + inputI]; } sum += weights[neuronI * inputNodeCount + inputCount]; //Bias node that always inputs "1" outputs[neuronI] = activationFunction(sum); } return outputs; } backPropagateCalculateErrorGradient(targetOutputs) { //Defining these locally speeds up the loop below by reducing object property access let nodeCount = this.nodeCount; let errorGradients = this.errorGradients; let outputs = this.outputs; let activationFunctionDerivative = this.activationFunctionDerivative; let inputNodeCount = this.inputNodeCount; let inputCount = this.inputCount; let inputs = this.inputs; let weightErrorGradients = this.weightErrorGradients; // Defining these here ideally speeds up the loop below let inputI; let activationErrorGradient; for (let neuronI = 0; neuronI < nodeCount; neuronI++) { activationErrorGradient = (outputs[neuronI] - targetOutputs[neuronI]) * activationFunctionDerivative(outputs[neuronI]); errorGradients[neuronI] = activationErrorGradient; for (inputI = 0; inputI < inputCount; inputI++) { weightErrorGradients[neuronI * inputNodeCount + inputI] = inputs[inputI] * activationErrorGradient; } weightErrorGradients[neuronI * inputNodeCount + inputCount] = activationErrorGradient; //Bias node } } backPropagateOptimize(learningTimeStep) { this.optimizer.optimizeWeights(this.weights, this.weightErrorGradients, learningTimeStep); } } exports.default = OutputLayer;