UNPKG

nanonet

Version:

A lightweight neural network class for JavaScript.

1 lines 3.27 kB
module.exports = class NanoNet{constructor(t){this.structure=t,this.layers=[],this.learningRate=.1,this.layers[0]={activations:[]};for(let e=0;e<t[0];e++)this.layers[0].activations.push(0);for(let e=1;e<t.length;e++){let i={activations:[],weights:[],biases:[]};for(let s=0;s<t[e];s++){i.activations.push(0),i.biases.push(0),i.weights.push([]);for(let r=0;r<t[e-1];r++)i.weights[s].push(NanoNet.random(-1,1))}this.layers[e]=i}}get input(){return this.layers[0].activations}get output(){return this.layers[this.layers.length-1].activations}train(t){for(let e in t){let i=t[e],s=i[0],r=i[1];this.feedForward(s),this.propagateBackwards(r)}return this}feedForward(t){for(let e in t)this.layers[0].activations[e]=t[e];return this.feed()}feed(){for(let t=1;t<this.layers.length;t++){let e=this.getWeightedInputs(t),i=this.applyActivationFunction(e);this.layers[t].activations=i}return this}applyActivationFunction(t){let e=[];for(let i in t)e[i]=NanoNet.sigmoid(t[i]);return e}applyActivationFunctionDerivative(t){let e=[];for(let i in t)e[i]=NanoNet.sigmoidDerivative(t[i]);return e}propagateBackwards(t){let e=this.getOutputGradient(t),i=[];i[0]=this.getOutputDeltas(e);for(let t=1;t<this.layers.length-1;t++)i[t]=this.getDeltas(i[t-1],this.layers.length-1-t);return i.reverse(),this.updateWeightsAndBiases(i),this}updateWeightsAndBiases(t){for(let e=1;e<this.layers.length;e++){let i=this.layers[e],s=this.layers[e-1],r=t[e-1],a=i.weights,n=i.biases,l=s.activations,o=[];for(let t in r){o[t]=[];for(let e in l)o[t][e]=l[e]*r[t]}let h=[];for(let t in r)h[t]=r[t];for(let t in a){let e=a[t];for(let i in e)a[t][i]-=o[t][i]*this.learningRate}for(let t in n)n[t]-=h[t]*this.learningRate}return this}getOutputGradient(t){let e=[],i=this.output;for(let s in i)e[s]=i[s]-t[s];return e}getOutputDeltas(t){let e=[],i=this.getWeightedInputs(this.layers.length-1),s=this.applyActivationFunctionDerivative(i);for(let i in t)e[i]=t[i]*s[i];return e}getWeightedInputs(t){let e=this.layers[t],i=this.layers[t-1].activations,s=e.weights,r=e.biases,a=[];for(let t in r){let e=[];for(let r in i)e[r]=s[t][r]*i[r];let n=NanoNet.sum(e);a[t]=n+r[t]}return a}getWeightedDeltas(t,e){let i=[];for(let s in t){let r=[];for(let i in e)r[i]=t[s][i]*e[i];let a=NanoNet.sum(r);i[s]=a}return i}getDeltas(t,e){let i=this.layers[e+1],s=[],r=this.getWeightedInputs(e),a=this.applyActivationFunctionDerivative(r),n=this.transposeWeights(i.weights),l=this.getWeightedDeltas(n,t);for(let t in l)s[t]=l[t]*a[t];return s}transposeWeights(t){let e=[];for(let i in t){let s=t[i];for(let r in s)e[r]||(e[r]=[]),e[r][i]=t[i][r]}return e}static random(t=1,e=0){if("number"==typeof t&&"number"==typeof e){if(t!==e){let i=t<e?t:e,s=t<e?e:t;return Math.random()*(s-i)+i}throw"Range must be non-zero."}throw"random expects numbers."}static sigmoid(t){if("number"==typeof t){let e=Math.exp(t),i=e/(1+e);return i<0?0:i>1?1:Number.isNaN(i)?0===t?.5:t>0?1:0:i}throw"sigmoid expects a number."}static sigmoidDerivative(t){if("number"==typeof t){let e=NanoNet.sigmoid(t);return e*(1-e)}throw"sigmoidDerivative expects a number."}static sum(t){let e="Sum expects an array of numbers.";if(Array.isArray(t)&&t.length){let i=0;for(let s=0;s<t.length;s++){let r=t[s];if("number"!=typeof r||Number.isNaN(r))throw e;i+=r}return i}throw e}}