From 8956352977551d662e745d977c19b3263cad58fe Mon Sep 17 00:00:00 2001 From: KEMBL Date: Sun, 30 Aug 2020 19:56:26 +0200 Subject: [PATCH 1/5] feature: first not fully implemented code example --- .prettierignore | 1 + .prettierrc.js | 3 +- package.json | 4 +- src/index.ts | 48 ++- src/neuron/Layer.ts | 103 ++++++- src/neuron/Network.Old.ts | 315 ++++++++++++++++++++ src/neuron/Network.ts | 249 +++++----------- src/neuron/Neuron.ts | 26 +- src/neuron/README.md | 43 +++ src/neuron/configuration/Configuration.ts | 74 ----- src/neuron/configuration/SharedFunctions.ts | 93 ++++++ src/neuron/configuration/index.ts | 2 + src/neuron/index.ts | 6 + src/neuron/utilities/StringFunctions.ts | 15 +- src/neuron/utilities/index.ts | 1 + 15 files changed, 716 insertions(+), 267 deletions(-) create mode 100644 .prettierignore create mode 100644 src/neuron/Network.Old.ts create mode 100644 src/neuron/README.md create mode 100644 src/neuron/configuration/SharedFunctions.ts create mode 100644 src/neuron/configuration/index.ts create mode 100644 src/neuron/index.ts create mode 100644 src/neuron/utilities/index.ts diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..a4a7373 --- /dev/null +++ b/.prettierignore @@ -0,0 +1 @@ +src/**/*.d.ts \ No newline at end of file diff --git a/.prettierrc.js b/.prettierrc.js index ce3eebd..f8e311f 100644 --- a/.prettierrc.js +++ b/.prettierrc.js @@ -1,8 +1,7 @@ module.exports = { - printWidth: 180, bracketSpacing: false, jsxBracketSameLine: true, singleQuote: true, - trailingComma: false, + trailingComma: 'none', bracketSpacing: true }; diff --git a/package.json b/package.json index 2ccee30..e22815e 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,9 @@ "compile": "tsc --build --verbose", "purge": "yarn clean:all && yarn && yarn prepare:all", "lint": "eslint -c .eslintrc.js --ext .ts --ignore-pattern *.d.ts ./src", - "lint:fix": "eslint -c .eslintrc.js --ext .ts --ignore-pattern *.d.ts --fix ./src" + "lint:fix": "eslint -c .eslintrc.js --ext .ts --ignore-pattern *.d.ts --fix ./src", + "format": "prettier --write src/**/*.ts", + "pre-commit": "yarn lint:fix && yarn format" }, "bugs": { "url": "https://github.com/KEMBL/machine-learning/issues" diff --git a/src/index.ts b/src/index.ts index b2d265f..efdbf52 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,13 +1,55 @@ -import { Network } from './neuron/Network'; +import { Configuration, Network } from './neuron'; class Program { constructor() { console.log('Programm started'); - new Network().testNeuron(); + Configuration.bias = 0; + Configuration.activationType = 'None'; + Configuration.useCostFunction = 'None'; + const inputs = [1]; + const targetOutputs = [40]; - console.log('Programm finished'); + const error = 0.0001; + const maxSteps = 2000; + const ldelta = 0.01; + + const neuronsCount = inputs.length; + const network = new Network(maxSteps, error, ldelta); // error, ldelta, maxSteps + network.debug = false; + network.addLayer(neuronsCount); // make neurons / init them / etc + network.learn(inputs, targetOutputs); // propagate / errorcost / weig\hts correction (back propagation) + //new Network().testNeuron(); + const result = network.result(); + console.log('Programm finished', result); } } new Program(); + +// class Program1 { +// constructor() { +// console.log('Programm started'); + +// const inputs: number[] = [1, 50, 10, 0.3, 1, 2, 10, 45]; +// const outputs: number[] = [50, 1, 10, 20, 100, 33, 1, 15]; +// const results: LearningResult[] = []; + +// for (let i = 0; i < inputs.length; i++) { +// const network = new Network(inputs[i], outputs[i]); +// network.debug = false; +// results.push(network.testNeuron(3)); +// } + +// for (let i = 0; i < inputs.length; i++) { +// console.log( +// `${i}. steps ${results[i].steps}, cost ${results[i].cost} input ${inputs[i]} * w ${results[i].weight} = ${outputs[i]} `, +// inputs[i] * results[i].weight, +// inputs[i] * results[i].weight === outputs[i], +// Math.round(inputs[i] * results[i].weight) === outputs[i] +// ); +// } + +// console.log('Programm finished'); +// } +// } diff --git a/src/neuron/Layer.ts b/src/neuron/Layer.ts index ff89bad..6857c39 100644 --- a/src/neuron/Layer.ts +++ b/src/neuron/Layer.ts @@ -1 +1,102 @@ -export class Layer {} +import { Neuron } from './'; + +/** + * One neurons layer + */ +export class Layer { + public debug = false; + + public get isFirst(): boolean { + return this.layerId === 0; + } + private neurons: Neuron[] = []; + + constructor(private layerId: number, private neuronsAmount: number) { + this.init(); + } + + private init = (): void => { + this.neurons = []; + for (let i = 0; i < this.neuronsAmount; i++) { + const neuron = new Neuron(); + neuron.debug = this.debug; + neuron.init(this.layerId, i); + this.neurons.push(neuron); + } + }; + + /** + * Init layer, used to set output vars in the first layer + * @param sourceLayer + */ + public setOutput = (inputVariables: number[]): void => { + if (this.layerId !== 0) { + console.warn(`Init: Current layer ${this.layerId} is nor input layer!`); + } + for (let i = 0; i <= this.neurons.length; i++) { + this.neurons[i].output = inputVariables[i]; + } + }; + + /** + * Propagate previous layer neurons to all current layer neurons + * @param sourceLayer + */ + public propagate = (sourceLayer: Layer): void => { + if (this.layerId === 0) { + return; + } + for (let i = 0; i < sourceLayer.neurons.length; i++) { + this.propagateNeuron(sourceLayer.neurons[i]); + } + }; + + /** + * Takes source neuron and propagate it to all current layer neurons + * @param sourceNeuron + */ + private propagateNeuron = (sourceNeuron: Neuron): void => { + for (let i = 0; i < this.neurons.length; i++) { + const neuron = this.neurons[i]; + neuron.input = sourceNeuron.output; + } + }; + + result = (): number[] => { + const resultsList: number[] = []; + for (let i = 0; i < this.neurons.length; i++) { + resultsList.push(this.neurons[i].output); + } + return resultsList; + }; + + cost = (outputArray: number[]): number => { + let cost = 0; + for (let i = 0; i < this.neurons.length; i++) { + cost += this.neurons[i].cost(outputArray[i]); + } + return cost; + }; + + backPropagate = (nextLayer: Layer): void => { + // + + // // new weight + // const newWeight = weight + this.learningDelta * cost; + + // const arrow = cost > 0 ? 'i' : 'v'; + // const deltaWeight = weight - newWeight; + + // this.log( + // `i:${i}, ${arrow}, cost:${fnz(cost)}, w:${fnz(newWeight)}, dw:${fnz( + // deltaWeight + // )}, v:${fnz(neuron.output)}, gv:${fnz(prediction)}` + // ); + // if (Math.abs(cost) < this.error) { + // break; + // } + + // weight = newWeight; + + } +} diff --git a/src/neuron/Network.Old.ts b/src/neuron/Network.Old.ts new file mode 100644 index 0000000..5d78bf0 --- /dev/null +++ b/src/neuron/Network.Old.ts @@ -0,0 +1,315 @@ +import { Neuron } from './Neuron'; +import { SharedFunctions } from './configuration/SharedFunctions'; +import { StringFunctions } from './utilities/StringFunctions'; +import { Configuration } from './configuration/Configuration'; + +const fnz = StringFunctions.fnz; + +export interface LearningResult { + weight: number; + steps: number; + cost: number; +} + +export class NetworkOld { + public debug = false; + + /** input value */ + private input = 1.0; + /** value which we want to achieve for given input */ + private target = 40.0; + private error = 0.0001; + /** Decreases speed of network adaptation to a given task, makes that process more predictable */ + private learningDelta = 0.01; + private maxSteps = 1000; + // private neurons: Neuron[] = [];8 + + //private i: number[]; + + constructor(input?: number, target?: number) { + //this.i = [0,1,2]; + if (!!input || input === 0) { + this.input = input; + } + + if (!!target || target === 0) { + this.target = target; + } + } + /* + private init = (inNum:number, oNum) => { + this.neurons = []; + for(let i = 0; i < inNum; i++) + { + this.neurons.push(new Neuron); + this.neurons[i].init(); + } + } +*/ + + public testNeuron = (type: number): LearningResult => { + switch (type) { + case 1: + return this.testNeuronGw(); + break; + case 2: + return this.testNeuronReal(); + break; + case 3: + this.maxSteps = 2000; + Configuration.bias = 0; + Configuration.activationType = 'None'; + Configuration.useCostFunction = 'None'; + return this.testNeuronSimple(); + break; + default: + return this.testNeuronUnbounded(); + break; + } + }; + + /** simple way to count a new weight as weight + cost * learning_delta */ + public testNeuronSimple = (): LearningResult => { + const neuron = new Neuron(); + neuron.debug = this.debug; + + let weight = 1; //Math.random()*5.0; + this.log( + `init o:${this.target}, w:${weight}, ld:${this.learningDelta}, e:${this.error}` + ); + + let i = 0; + let cost = 0; + for (i = 0; i < this.maxSteps; i++) { + // forward propagation + const prediction = neuron.prediction(weight, this.input); + + // error find + cost = neuron.cost(this.target); + + // new weight + const newWeight = weight + this.learningDelta * cost; + + const arrow = cost > 0 ? 'i' : 'v'; + const deltaWeight = weight - newWeight; + + this.log( + `i:${i}, ${arrow}, cost:${fnz(cost)}, w:${fnz(newWeight)}, dw:${fnz( + deltaWeight + )}, v:${fnz(neuron.output)}, gv:${fnz(prediction)}` + ); + if (Math.abs(cost) < this.error) { + break; + } + + weight = newWeight; + // value = newValue; + } + + return { weight, steps: i, cost }; + }; + + public testNeuronReal = (): LearningResult => { + const neuron = new Neuron(); + neuron.debug = this.debug; + //neuron.init(1, 1); + + // console.log(`0.12356 :${fnz(0.12356)}`); + // console.log(`1.00165 :${fnz(1.00165)}`); + // console.log(`0.000123 :${fnz(0.000123)}`); + // console.log(`0.0000017 :${fnz(0.0000017)}`); + // console.log(`10.03001 :${fnz(10.03001)}`); + + // return; + + //let value = 0; + this.target = SharedFunctions.activationFunction(this.target); + //this.output = Math.random()*100; + let weight = 2; //Math.random()*5.0; + this.log( + `init o:${this.target}, w:${weight}, ld:${this.learningDelta}, e:${this.error}` + ); + + let i = 0; + let cost = 0; + for (i = 0; i < this.maxSteps; i++) { + // forward propagation + const prediction = neuron.prediction(weight, this.input); + + // error find + cost = neuron.cost(this.target); + + // new weight + //const dy = 1.0; + const dy = SharedFunctions.activationFunctionPrime(prediction); // why ????? + //const pD = this.ldelta * cost; + //const mDelta = Math.abs(pD) > this.ldelta ? pD : cost; + + //const pD2 = mDelta * dy; + //const pDelta = Math.abs(pD2) > dy ? pD2 : mDelta; + + const ccost = Math.abs(cost) > 1 ? cost : Math.sign(cost); + + //const newWeight = weight + pDelta; // this.ldelta * delta * dy * this.input; + //const newWeight = weight + this.ldelta * cost * dy * this.input; + const newWeight = weight + this.learningDelta * ccost; + const arrow = cost > 0 ? 'i' : 'v'; + const deltaWeight = weight - newWeight; + + this.log( + `i:${i}, ${arrow}, cost:${fnz(cost)}, w:${fnz(newWeight)}, dw:${fnz( + deltaWeight + )}, v:${fnz(neuron.output)}, gv:${fnz(prediction)}, dy:${fnz(dy)}` + ); + if (Math.abs(cost) < this.error) { + break; + } + + weight = newWeight; + // value = newValue; + } + return { weight, steps: i, cost }; + }; + + public testNeuronUnbounded = (): LearningResult => { + //let value = 0; + this.target = SharedFunctions.activationFunction(this.target); + //this.output = Math.random()*100; + let weight = 2; //Math.random()*5.0; + this.log( + `init o:${this.target}, w:${weight}, ld:${this.learningDelta}, e:${this.error}` + ); + + let i = 0; + let cost = 0; + for (i = 0; i < this.maxSteps; i++) { + // forward propagation + let value = this.input * weight; + //const newValue = value; + const newValue = SharedFunctions.activationFunction(value); + + // error find + cost = this.target - newValue; // take sign only? + + // new weight + //const dy = 1.0; + const dy = SharedFunctions.activationFunctionPrime(newValue); + const pD = this.learningDelta * cost; + const mDelta = Math.abs(pD) > this.learningDelta ? pD : cost; + + const pD2 = mDelta * dy; + const pDelta = Math.abs(pD2) > dy ? pD2 : mDelta; + + const newWeight = weight + pDelta; // this.ldelta * delta * dy * this.input; + const arrow = cost > 0 ? 'i' : 'v'; + const deltaWeight = weight - newWeight; + this.log( + `i:${i}, ${arrow}, d:${cost.toFixed(3)}, w:${newWeight.toFixed( + 3 + )}, dw:${deltaWeight.toFixed(3)}, v:${value.toFixed( + 3 + )}, gv:${newValue.toFixed(3)}, dy:${dy.toFixed(3)}, pD:${pD.toFixed( + 3 + )}, pD2:${pD2.toFixed(3)}` + ); + if (Math.abs(cost) < this.error) { + break; + } + + weight = newWeight; + value = newValue; + } + return { weight, steps: i, cost }; + }; + + public testNeuronGw = (): LearningResult => { + //let value = 0; + this.target = SharedFunctions.activationFunction(this.target); + //this.output = Math.random()*100; + let weight = 2; //Math.random()*5.0; + this.log( + `init o:${this.target}, w:${weight}, ld:${this.learningDelta}, e:${this.error}` + ); + + let i = 0; + let cost = 0; + for (i = 0; i < this.maxSteps; i++) { + // forward propagation + let value = this.input * weight; + //const newValue = value; + const newValue = SharedFunctions.activationFunction(value); + + // error find + cost = this.target - newValue; // take sign only? + + // new weight + //const dy = 1.0; + const dy = SharedFunctions.activationFunctionPrime(newValue); + const pD = this.learningDelta * cost; + const mDelta = Math.abs(pD) > this.learningDelta ? pD : cost; + + const pD2 = mDelta * dy; + const pDelta = Math.abs(pD2) > dy ? pD2 : mDelta; + + const newWeight = weight + pDelta; // this.ldelta * delta * dy * this.input; + const arrow = cost > 0 ? 'i' : 'v'; + const deltaWeight = weight - newWeight; + this.log( + `i:${i}, ${arrow}, d:${cost.toFixed(3)}, w:${newWeight.toFixed( + 3 + )}, dw:${deltaWeight.toFixed(3)}, v:${value.toFixed( + 3 + )}, gv:${newValue.toFixed(3)}, dy:${dy.toFixed(3)}, pD:${pD.toFixed( + 3 + )}, pD2:${pD2.toFixed(3)}` + ); + if (Math.abs(cost) < this.error) { + break; + } + + weight = newWeight; + value = newValue; + } + return { weight, steps: i, cost }; + }; + /* +public testNeuronOld = () => { + const n = new Neuron(); + n.init(); + + for (let i = 0; i < 100; i++) { + n.value = this.input; + + const deltaOut = n.value - this.output; + const df = Configuration.activationFunctionDf(n.value); + const newWeight = + n.weight + this.delta * deltaOut * this.input * df; + + console.log(`nw correction i:${i}, d:${delta}, e:${this.error}, ow:${n.weight}, nw:${newWeight}`); + if (delta < this.error) { + break; + } + + n.weight = newWeight; + } + }; + + private countValues = () => + { + + + }*/ + + private log = (message?: unknown, optionalParams?: unknown[]): void => { + if (!this.debug) { + return; + } + + if (optionalParams) { + console.log(message, ...optionalParams); + return; + } + + console.log(message); + }; +} diff --git a/src/neuron/Network.ts b/src/neuron/Network.ts index 92993f4..154f5f1 100644 --- a/src/neuron/Network.ts +++ b/src/neuron/Network.ts @@ -1,209 +1,106 @@ -import { Neuron } from './Neuron'; -import { Configuration } from './configuration/Configuration'; -import { StringFunctions } from './utilities/StringFunctions'; - -const fnz = StringFunctions.fnz; +import { Layer } from './'; export class Network { - /** input value */ - public input = 1.0; - /** value which we want to achieve for given input */ - public target = 40.0; + public debug = false; + + /** criteria to end learning */ public error = 0.0001; + + /** maximum learn steps to learn */ + public maxSteps = 2000; + + /** learning step */ public ldelta = 0.01; - public maxSteps = 1000; - // private neurons: Neuron[] = [];8 - //private i: number[]; + private layers: Layer[] = []; - constructor() { - //this.i = [0,1,2]; + private get lastLayer(): Layer { + return this.layers[this.layers.length - 1]; } - /* - private init = (inNum:number, oNum) => { - this.neurons = []; - for(let i = 0; i < inNum; i++) - { - this.neurons.push(new Neuron); - this.neurons[i].init(); - } + + constructor(maxSteps: number, error: number, ldelta: number) { + this.maxSteps = maxSteps; + this.error = error; + this.ldelta = ldelta; } -*/ - public testNeuron = (type = 2): void => { - switch (type) { - case 1: - this.testNeuronGw(); - break; - case 2: - this.testNeuronReal(); - break; - default: - this.testNeuronUnbounded(); - break; - } + /** Adds new layer */ + addLayer = (neuronsCount: number): void => { + const layerId = this.layers.length + 1; + const layer = new Layer(layerId, neuronsCount); + this.layers.push(layer); }; - public testNeuronReal = (): void => { - const neuron = new Neuron(); - //neuron.init(1, 1); - - // console.log(`0.12356 :${fnz(0.12356)}`); - // console.log(`1.00165 :${fnz(1.00165)}`); - // console.log(`0.000123 :${fnz(0.000123)}`); - // console.log(`0.0000017 :${fnz(0.0000017)}`); - // console.log(`10.03001 :${fnz(10.03001)}`); - - // return; - - //let value = 0; - this.target = Configuration.activationFunction(this.target); - //this.output = Math.random()*100; - let weight = 2; //Math.random()*5.0; - console.log(`init o:${this.target}, w:${weight}, ld:${this.ldelta}, e:${this.error}`); - - for (let i = 0; i < this.maxSteps; i++) { - // forward propagation - const prediction = neuron.prediction(weight, this.input); - - // error find - const cost = neuron.cost(this.target); - - // new weight - //const dy = 1.0; - const dy = Configuration.activationFunctionPrime(prediction); - //const pD = this.ldelta * cost; - //const mDelta = Math.abs(pD) > this.ldelta ? pD : cost; - - //const pD2 = mDelta * dy; - //const pDelta = Math.abs(pD2) > dy ? pD2 : mDelta; - - const ccost = Math.abs(cost) > 1 ? cost : Math.sign(cost); - - //const newWeight = weight + pDelta; // this.ldelta * delta * dy * this.input; - //const newWeight = weight + this.ldelta * cost * dy * this.input; - const newWeight = weight + this.ldelta * ccost; - const arrow = cost > 0 ? 'i' : 'v'; - const deltaWeight = weight - newWeight; + /** Returns output of the last layer */ + result = (): number[] => { + const lastLayer = this.layers[this.layers.length - 1]; + return lastLayer.result(); + }; - console.log(`i:${i}, ${arrow}, cost:${fnz(cost)}, w:${fnz(newWeight)}, dw:${fnz(deltaWeight)}, v:${fnz(neuron.output)}, gv:${fnz(prediction)}, dy:${fnz(dy)}`); - if (Math.abs(cost) < this.error) { + /** Makes learning cycles */ + learn = (inputArray: number[], outputArray: number[]): void => { + this.layers[0].setOutput(inputArray); + for (let i = 0; i <= this.maxSteps; i++) { + const error = this.learnStep(outputArray); + if (error <= this.error) { break; } - - weight = newWeight; - // value = newValue; } }; - public testNeuronUnbounded = (): void => { - //let value = 0; - this.target = Configuration.activationFunction(this.target); - //this.output = Math.random()*100; - let weight = 2; //Math.random()*5.0; - console.log(`init o:${this.target}, w:${weight}, ld:${this.ldelta}, e:${this.error}`); - - for (let i = 0; i < this.maxSteps; i++) { - // forward propagation - let value = this.input * weight; - //const newValue = value; - const newValue = Configuration.activationFunction(value); + /** + * Performs one learning step + */ + private learnStep = (outputArray: number[]): number => { + let error = 1; + for (let i = 0; i <= this.maxSteps; i++) { + this.propagate(); // error find - const delta = this.target - newValue; // take sign only? - - // new weight - //const dy = 1.0; - const dy = Configuration.activationFunctionPrime(newValue); - const pD = this.ldelta * delta; - const mDelta = Math.abs(pD) > this.ldelta ? pD : delta; - - const pD2 = mDelta * dy; - const pDelta = Math.abs(pD2) > dy ? pD2 : mDelta; - - const newWeight = weight + pDelta; // this.ldelta * delta * dy * this.input; - const arrow = delta > 0 ? 'i' : 'v'; - const deltaWeight = weight - newWeight; - console.log( - `i:${i}, ${arrow}, d:${delta.toFixed(3)}, w:${newWeight.toFixed(3)}, dw:${deltaWeight.toFixed(3)}, v:${value.toFixed(3)}, gv:${newValue.toFixed(3)}, dy:${dy.toFixed( - 3 - )}, pD:${pD.toFixed(3)}, pD2:${pD2.toFixed(3)}` - ); - if (Math.abs(delta) < this.error) { - break; + error = this.findStepError(outputArray); + if (error <= this.error) { + return error; } - weight = newWeight; - value = newValue; + // new weights count + this.backPropagation(); } - }; - - public testNeuronGw = (): void => { - //let value = 0; - this.target = Configuration.activationFunction(this.target); - //this.output = Math.random()*100; - let weight = 2; //Math.random()*5.0; - console.log(`init o:${this.target}, w:${weight}, ld:${this.ldelta}, e:${this.error}`); - for (let i = 0; i < this.maxSteps; i++) { - // forward propagation - let value = this.input * weight; - //const newValue = value; - const newValue = Configuration.activationFunction(value); + return error; + }; - // error find - const delta = this.target - newValue; // take sign only? - - // new weight - //const dy = 1.0; - const dy = Configuration.activationFunctionPrime(newValue); - const pD = this.ldelta * delta; - const mDelta = Math.abs(pD) > this.ldelta ? pD : delta; - - const pD2 = mDelta * dy; - const pDelta = Math.abs(pD2) > dy ? pD2 : mDelta; - - const newWeight = weight + pDelta; // this.ldelta * delta * dy * this.input; - const arrow = delta > 0 ? 'i' : 'v'; - const deltaWeight = weight - newWeight; - console.log( - `i:${i}, ${arrow}, d:${delta.toFixed(3)}, w:${newWeight.toFixed(3)}, dw:${deltaWeight.toFixed(3)}, v:${value.toFixed(3)}, gv:${newValue.toFixed(3)}, dy:${dy.toFixed( - 3 - )}, pD:${pD.toFixed(3)}, pD2:${pD2.toFixed(3)}` - ); - if (Math.abs(delta) < this.error) { - break; + /** + * Propagate input values through all network + */ + private propagate = (): void => { + let previousLayer: Layer; + for (const layer of this.layers) { + if (!layer.isFirst) { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + layer.propagate(previousLayer!); } - - weight = newWeight; - value = newValue; + previousLayer = layer; } }; - /* -public testNeuronOld = () => { - const n = new Neuron(); - n.init(); - - for (let i = 0; i < 100; i++) { - n.value = this.input; - const deltaOut = n.value - this.output; - const df = Configuration.activationFunctionDf(n.value); - const newWeight = - n.weight + this.delta * deltaOut * this.input * df; + /** + * Searches of how big network result error is + */ + private findStepError = (outputArray: number[]): number => { + return this.lastLayer.cost(outputArray); + }; - console.log(`nw correction i:${i}, d:${delta}, e:${this.error}, ow:${n.weight}, nw:${newWeight}`); - if (delta < this.error) { - break; + /** + * Count new weights + */ + private backPropagation = (): void => { + let previousLayer: Layer; + for (const layer of this.layers.reverse()) { + if (!layer.isFirst) { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + layer.backPropagate(previousLayer!); } - - n.weight = newWeight; + previousLayer = layer; } }; - - private countValues = () => - { - - - }*/ } diff --git a/src/neuron/Neuron.ts b/src/neuron/Neuron.ts index 9c94cf4..c7fb87a 100644 --- a/src/neuron/Neuron.ts +++ b/src/neuron/Neuron.ts @@ -1,6 +1,10 @@ -import { Configuration } from './configuration/Configuration'; +import { Configuration, SharedFunctions } from './configuration'; +/** + * One neuron logic + */ export class Neuron { + public debug = false; private name = ''; private inputValue = 0; private activatedValue = 0; @@ -9,10 +13,16 @@ export class Neuron { return this.activatedValue; } + public set output(value: number) { + this.activatedValue = value; + } + public set input(x: number) { this.inputValue = x + Configuration.bias; - this.activatedValue = Configuration.activationFunction(this.inputValue); - console.log(`nr ${this.name} v: ${this.inputValue} -> ${this.activatedValue}, in: ${x}`); + this.activatedValue = SharedFunctions.activationFunction(this.inputValue); + this.log( + `nr ${this.name} v: ${this.inputValue} -> ${this.activatedValue}, in: ${x}` + ); } public init(layerId: number, index: number): void { @@ -20,11 +30,19 @@ export class Neuron { } public cost(expected: number): number { - return Configuration.costFunction(expected, this.activatedValue); + return SharedFunctions.costFunction(expected, this.activatedValue); } public prediction(weight: number, x: number): number { this.input = weight * x; return this.output; } + + private log = (logLine: string, args: string[] = []): void => { + if (!this.debug) { + return; + } + + console.log(logLine, ...args); + }; } diff --git a/src/neuron/README.md b/src/neuron/README.md new file mode 100644 index 0000000..1f0dd3e --- /dev/null +++ b/src/neuron/README.md @@ -0,0 +1,43 @@ +# Neuron + + +## Neuron logic. For one neuron only + +One neuron solves an equation: y = weight * input + bias +weight? bias? => min cost (Ytarget - Ynow) + + +1) take input +2) add bias to input +3) apply activation function to 2) +4) make prediction i.e weight * input +5) count cost function +6) change weight (change slope, so we need to know should be increase or decrease the prime slope) + + +outputReal AF(outputTarget) +--- + +cost rises = weight falls +cost falls = weight rises +cost c = weight cost i.e. weight + deltaW where deltaW = 0 as cost close to 0 +so weight + cost * learning_delta + + +### Possible variables values + +input [0,inf) +y [0,inf) + +degrees of prime (0, 90) = degrees [0, 45] = w[0,1] + degrees (45, 90) = w (1, inf) +slope from 0 to +y [0,inf) +cost [0,inf] + +------------ +Q&A + +1) Define relation between cost function and activation function. How result of the cost function changes weight? +2) how to normalize w random selection? taking in account that P to select w [0,1] should be = P to select w (1,inf]? 0 -> 45 - > 90 degrees +any random value close to 1 would be ok? +3) why if input is greater then output it takes less steps to find a result? \ No newline at end of file diff --git a/src/neuron/configuration/Configuration.ts b/src/neuron/configuration/Configuration.ts index b605e9a..032acd5 100644 --- a/src/neuron/configuration/Configuration.ts +++ b/src/neuron/configuration/Configuration.ts @@ -7,80 +7,6 @@ class Configuration { public activationType = 'Identity'; /** Cost function */ public useCostFunction = 'Squared'; - - // more https://rohanvarma.me/Loss-Functions/ - // more https://github.com/trekhleb/nano-neuron - public costFunction = (expected: number, prediction: number): number => { - switch (this.useCostFunction) { - case 'Squared': { - const v = expected * expected - 2 * expected * prediction + prediction * prediction; - return v * 0.5; - } - default: - return expected - prediction; - } - }; - - public activationFunction = (x: number): number => { - switch (this.activationType) { - case 'ReLU': - return this.activationFunctionReLU(x); - case 'LeakyReLU': - return this.activationFunctionLeakyReLU(x); - case 'Sigmoid': - return this.activationFunctionSigmoid(x); - default: - return this.activationFunctionIdentity(x); - } - }; - - public activationFunctionPrime = (x: number): number => { - switch (this.activationType) { - case 'ReLU': - return this.activationFunctionReLUPrime(x); - case 'LeakyReLU': - return this.activationFunctionLeakyReLUPrime(x); - case 'Sigmoid': - return this.activationFunctionSigmoidPrime(x); - default: - return this.activationFunctionIdentityPrime(x); - } - }; - - // more: https://en.wikipedia.org/wiki/Rectifier_(neural_networks) - private activationFunctionReLU = (x: number): number => { - return x > 0 ? x : 0; - }; - - private activationFunctionReLUPrime = (x: number): number => { - return x > 0 ? 1 : 0; - }; - - private activationFunctionLeakyReLU = (x: number): number => { - return x > 0 ? x : 0.01 * x; - }; - - private activationFunctionLeakyReLUPrime = (x: number): number => { - return x > 0 ? 1 : 0.01; - }; - - // more: https://towardsdatascience.com/derivative-of-the-sigmoid-function-536880cf918e - private activationFunctionSigmoid = (x: number): number => { - return 1 / (1 + Math.pow(Math.E, -x)); - }; - - private activationFunctionSigmoidPrime = (x: number): number => { - return this.activationFunctionSigmoid(x) * (1 - this.activationFunctionSigmoid(x)); - }; - - private activationFunctionIdentity = (x: number): number => { - return x; - }; - - // eslint-disable-next-line @typescript-eslint/no-unused-vars, no-unused-vars - private activationFunctionIdentityPrime = (_x: number): number => { - return 1; - }; } const configuration = new Configuration(); diff --git a/src/neuron/configuration/SharedFunctions.ts b/src/neuron/configuration/SharedFunctions.ts new file mode 100644 index 0000000..40cc0cc --- /dev/null +++ b/src/neuron/configuration/SharedFunctions.ts @@ -0,0 +1,93 @@ +import { Configuration } from './Configuration'; + +/** + * Shared functions + */ +class SharedFunctions { + // more https://rohanvarma.me/Loss-Functions/ + // more https://github.com/trekhleb/nano-neuron + public costFunction = (expected: number, prediction: number): number => { + switch (Configuration.useCostFunction) { + case 'Squared': { + // every time returns a result in interval [0, +inf) + const v = + expected * expected - + 2 * expected * prediction + + prediction * prediction; + return v * 0.5; + } + default: + return expected - prediction; + } + }; + + public activationFunction = (x: number): number => { + switch (Configuration.activationType) { + case 'ReLU': + return this.activationFunctionReLU(x); + case 'LeakyReLU': + return this.activationFunctionLeakyReLU(x); + case 'Sigmoid': + return this.activationFunctionSigmoid(x); + default: + return this.activationFunctionIdentity(x); + } + }; + + public activationFunctionPrime = (x: number): number => { + switch (Configuration.activationType) { + case 'ReLU': + return this.activationFunctionReLUPrime(x); + case 'LeakyReLU': + return this.activationFunctionLeakyReLUPrime(x); + case 'Sigmoid': + return this.activationFunctionSigmoidPrime(x); + default: + return this.activationFunctionIdentityPrime(x); + } + }; + + // more: https://en.wikipedia.org/wiki/Rectifier_(neural_networks) + private activationFunctionReLU = (x: number): number => { + return x > 0 ? x : 0; + }; + + /** + * Streats slope as 45 degress when x > 0 and 0 degress for all other cases + */ + private activationFunctionReLUPrime = (x: number): number => { + return x > 0 ? 1 : 0; + }; + + private activationFunctionLeakyReLU = (x: number): number => { + return x > 0 ? x : 0.01 * x; + }; + + private activationFunctionLeakyReLUPrime = (x: number): number => { + return x > 0 ? 1 : 0.01; + }; + + // more: https://towardsdatascience.com/derivative-of-the-sigmoid-function-536880cf918e + private activationFunctionSigmoid = (x: number): number => { + return 1 / (1 + Math.pow(Math.E, -x)); + }; + + private activationFunctionSigmoidPrime = (x: number): number => { + return ( + this.activationFunctionSigmoid(x) * + (1 - this.activationFunctionSigmoid(x)) + ); + }; + + private activationFunctionIdentity = (x: number): number => { + return x; + }; + + // eslint-disable-next-line @typescript-eslint/no-unused-vars, no-unused-vars + private activationFunctionIdentityPrime = (_x: number): number => { + return 1; + }; +} + +const sharedFunctions = new SharedFunctions(); +export { sharedFunctions as SharedFunctions }; diff --git a/src/neuron/configuration/index.ts b/src/neuron/configuration/index.ts new file mode 100644 index 0000000..da288a1 --- /dev/null +++ b/src/neuron/configuration/index.ts @@ -0,0 +1,2 @@ +export * from './Configuration'; +export * from './SharedFunctions'; diff --git a/src/neuron/index.ts b/src/neuron/index.ts new file mode 100644 index 0000000..4d658aa --- /dev/null +++ b/src/neuron/index.ts @@ -0,0 +1,6 @@ +export * from './configuration'; +export * from './utilities'; + +export * from './Layer'; +export * from './Neuron'; +export * from './Network'; diff --git a/src/neuron/utilities/StringFunctions.ts b/src/neuron/utilities/StringFunctions.ts index e856cc5..0de9232 100644 --- a/src/neuron/utilities/StringFunctions.ts +++ b/src/neuron/utilities/StringFunctions.ts @@ -1,15 +1,18 @@ /** * Useful string functions */ -class StringFunctions { - public fnz = (num: number, fixedVals = 3): string => { +export class StringFunctions { + public static fnz = (num: number, fixedVals = 3): string => { + if (num === 0) { + return num.toString(); + } + if (Math.abs(num) >= 1) { return num.toFixed(fixedVals); } - return num.toFixed(1 - Math.floor(Math.log(Math.abs(num % 1)) / Math.log(10))); + return num.toFixed( + 1 - Math.floor(Math.log(Math.abs(num % 1)) / Math.log(10)) + ); }; } - -const stringFunctions = new StringFunctions(); -export { stringFunctions as StringFunctions }; diff --git a/src/neuron/utilities/index.ts b/src/neuron/utilities/index.ts new file mode 100644 index 0000000..42a1a3b --- /dev/null +++ b/src/neuron/utilities/index.ts @@ -0,0 +1 @@ +export * from './StringFunctions'; From 5e562b8adcb167729fcfb9a0bc8c284393c64a35 Mon Sep 17 00:00:00 2001 From: KEMBL Date: Sun, 30 Aug 2020 21:00:50 +0200 Subject: [PATCH 2/5] minor fixes, still does not work --- src/neuron/Layer.ts | 37 ++++++++++++++++--------------------- src/neuron/Network.ts | 2 +- src/neuron/Neuron.ts | 16 +++++++++++++--- 3 files changed, 30 insertions(+), 25 deletions(-) diff --git a/src/neuron/Layer.ts b/src/neuron/Layer.ts index 6857c39..944cb4f 100644 --- a/src/neuron/Layer.ts +++ b/src/neuron/Layer.ts @@ -58,7 +58,7 @@ export class Layer { private propagateNeuron = (sourceNeuron: Neuron): void => { for (let i = 0; i < this.neurons.length; i++) { const neuron = this.neurons[i]; - neuron.input = sourceNeuron.output; + neuron.prediction(sourceNeuron.output); } }; @@ -75,28 +75,23 @@ export class Layer { for (let i = 0; i < this.neurons.length; i++) { cost += this.neurons[i].cost(outputArray[i]); } - return cost; + return cost / (2 * this.neurons.length); }; backPropagate = (nextLayer: Layer): void => { // - - // // new weight - // const newWeight = weight + this.learningDelta * cost; - - // const arrow = cost > 0 ? 'i' : 'v'; - // const deltaWeight = weight - newWeight; - - // this.log( - // `i:${i}, ${arrow}, cost:${fnz(cost)}, w:${fnz(newWeight)}, dw:${fnz( - // deltaWeight - // )}, v:${fnz(neuron.output)}, gv:${fnz(prediction)}` - // ); - // if (Math.abs(cost) < this.error) { - // break; - // } - - // weight = newWeight; - - } + // // new weight + // const newWeight = weight + this.learningDelta * cost; + // const arrow = cost > 0 ? 'i' : 'v'; + // const deltaWeight = weight - newWeight; + // this.log( + // `i:${i}, ${arrow}, cost:${fnz(cost)}, w:${fnz(newWeight)}, dw:${fnz( + // deltaWeight + // )}, v:${fnz(neuron.output)}, gv:${fnz(prediction)}` + // ); + // if (Math.abs(cost) < this.error) { + // break; + // } + // weight = newWeight; + }; } diff --git a/src/neuron/Network.ts b/src/neuron/Network.ts index 154f5f1..0dde4e4 100644 --- a/src/neuron/Network.ts +++ b/src/neuron/Network.ts @@ -87,7 +87,7 @@ export class Network { * Searches of how big network result error is */ private findStepError = (outputArray: number[]): number => { - return this.lastLayer.cost(outputArray); + return this.lastLayer.cost(outputArray); }; /** diff --git a/src/neuron/Neuron.ts b/src/neuron/Neuron.ts index c7fb87a..6ef42ef 100644 --- a/src/neuron/Neuron.ts +++ b/src/neuron/Neuron.ts @@ -8,6 +8,7 @@ export class Neuron { private name = ''; private inputValue = 0; private activatedValue = 0; + private _weight = 0; public get output(): number { return this.activatedValue; @@ -17,7 +18,15 @@ export class Neuron { this.activatedValue = value; } - public set input(x: number) { + public get weight(): number { + return this._weight; + } + + public set weight(value: number) { + this._weight = value; + } + + private set input(x: number) { this.inputValue = x + Configuration.bias; this.activatedValue = SharedFunctions.activationFunction(this.inputValue); this.log( @@ -27,14 +36,15 @@ export class Neuron { public init(layerId: number, index: number): void { this.name = `${layerId}_${index}`; + this._weight = Math.random(); } public cost(expected: number): number { return SharedFunctions.costFunction(expected, this.activatedValue); } - public prediction(weight: number, x: number): number { - this.input = weight * x; + public prediction(x: number): number { + this.input = this._weight * x; return this.output; } From b9d824d246ec2637a2106c136ffd84e92c1408b3 Mon Sep 17 00:00:00 2001 From: KEMBL Date: Sun, 30 Aug 2020 22:48:51 +0200 Subject: [PATCH 3/5] minor changes --- src/neuron/Layer.ts | 12 +- src/neuron/Network.Old.ts | 629 +++++++++++++++++++------------------- 2 files changed, 325 insertions(+), 316 deletions(-) diff --git a/src/neuron/Layer.ts b/src/neuron/Layer.ts index 944cb4f..0c1f1d1 100644 --- a/src/neuron/Layer.ts +++ b/src/neuron/Layer.ts @@ -9,7 +9,8 @@ export class Layer { public get isFirst(): boolean { return this.layerId === 0; } - private neurons: Neuron[] = []; + + public neurons: Neuron[] = []; constructor(private layerId: number, private neuronsAmount: number) { this.init(); @@ -79,6 +80,15 @@ export class Layer { }; backPropagate = (nextLayer: Layer): void => { + for (let i = 0; i < this.neurons.length; i++) { + for (let j = 0; j < nextLayer.neurons.length; j++) { + // cost += this.neurons[i].cost(outputArray[i]); + } + } + + //const dy = SharedFunctions.activationFunctionPrime(newValue); + //const newWeight = weight + this.ldelta * cost * dy * this.input; + // // // new weight // const newWeight = weight + this.learningDelta * cost; diff --git a/src/neuron/Network.Old.ts b/src/neuron/Network.Old.ts index 5d78bf0..a203dc1 100644 --- a/src/neuron/Network.Old.ts +++ b/src/neuron/Network.Old.ts @@ -1,315 +1,314 @@ -import { Neuron } from './Neuron'; -import { SharedFunctions } from './configuration/SharedFunctions'; -import { StringFunctions } from './utilities/StringFunctions'; -import { Configuration } from './configuration/Configuration'; - -const fnz = StringFunctions.fnz; - -export interface LearningResult { - weight: number; - steps: number; - cost: number; -} - -export class NetworkOld { - public debug = false; - - /** input value */ - private input = 1.0; - /** value which we want to achieve for given input */ - private target = 40.0; - private error = 0.0001; - /** Decreases speed of network adaptation to a given task, makes that process more predictable */ - private learningDelta = 0.01; - private maxSteps = 1000; - // private neurons: Neuron[] = [];8 - - //private i: number[]; - - constructor(input?: number, target?: number) { - //this.i = [0,1,2]; - if (!!input || input === 0) { - this.input = input; - } - - if (!!target || target === 0) { - this.target = target; - } - } - /* - private init = (inNum:number, oNum) => { - this.neurons = []; - for(let i = 0; i < inNum; i++) - { - this.neurons.push(new Neuron); - this.neurons[i].init(); - } - } -*/ - - public testNeuron = (type: number): LearningResult => { - switch (type) { - case 1: - return this.testNeuronGw(); - break; - case 2: - return this.testNeuronReal(); - break; - case 3: - this.maxSteps = 2000; - Configuration.bias = 0; - Configuration.activationType = 'None'; - Configuration.useCostFunction = 'None'; - return this.testNeuronSimple(); - break; - default: - return this.testNeuronUnbounded(); - break; - } - }; - - /** simple way to count a new weight as weight + cost * learning_delta */ - public testNeuronSimple = (): LearningResult => { - const neuron = new Neuron(); - neuron.debug = this.debug; - - let weight = 1; //Math.random()*5.0; - this.log( - `init o:${this.target}, w:${weight}, ld:${this.learningDelta}, e:${this.error}` - ); - - let i = 0; - let cost = 0; - for (i = 0; i < this.maxSteps; i++) { - // forward propagation - const prediction = neuron.prediction(weight, this.input); - - // error find - cost = neuron.cost(this.target); - - // new weight - const newWeight = weight + this.learningDelta * cost; - - const arrow = cost > 0 ? 'i' : 'v'; - const deltaWeight = weight - newWeight; - - this.log( - `i:${i}, ${arrow}, cost:${fnz(cost)}, w:${fnz(newWeight)}, dw:${fnz( - deltaWeight - )}, v:${fnz(neuron.output)}, gv:${fnz(prediction)}` - ); - if (Math.abs(cost) < this.error) { - break; - } - - weight = newWeight; - // value = newValue; - } - - return { weight, steps: i, cost }; - }; - - public testNeuronReal = (): LearningResult => { - const neuron = new Neuron(); - neuron.debug = this.debug; - //neuron.init(1, 1); - - // console.log(`0.12356 :${fnz(0.12356)}`); - // console.log(`1.00165 :${fnz(1.00165)}`); - // console.log(`0.000123 :${fnz(0.000123)}`); - // console.log(`0.0000017 :${fnz(0.0000017)}`); - // console.log(`10.03001 :${fnz(10.03001)}`); - - // return; - - //let value = 0; - this.target = SharedFunctions.activationFunction(this.target); - //this.output = Math.random()*100; - let weight = 2; //Math.random()*5.0; - this.log( - `init o:${this.target}, w:${weight}, ld:${this.learningDelta}, e:${this.error}` - ); - - let i = 0; - let cost = 0; - for (i = 0; i < this.maxSteps; i++) { - // forward propagation - const prediction = neuron.prediction(weight, this.input); - - // error find - cost = neuron.cost(this.target); - - // new weight - //const dy = 1.0; - const dy = SharedFunctions.activationFunctionPrime(prediction); // why ????? - //const pD = this.ldelta * cost; - //const mDelta = Math.abs(pD) > this.ldelta ? pD : cost; - - //const pD2 = mDelta * dy; - //const pDelta = Math.abs(pD2) > dy ? pD2 : mDelta; - - const ccost = Math.abs(cost) > 1 ? cost : Math.sign(cost); - - //const newWeight = weight + pDelta; // this.ldelta * delta * dy * this.input; - //const newWeight = weight + this.ldelta * cost * dy * this.input; - const newWeight = weight + this.learningDelta * ccost; - const arrow = cost > 0 ? 'i' : 'v'; - const deltaWeight = weight - newWeight; - - this.log( - `i:${i}, ${arrow}, cost:${fnz(cost)}, w:${fnz(newWeight)}, dw:${fnz( - deltaWeight - )}, v:${fnz(neuron.output)}, gv:${fnz(prediction)}, dy:${fnz(dy)}` - ); - if (Math.abs(cost) < this.error) { - break; - } - - weight = newWeight; - // value = newValue; - } - return { weight, steps: i, cost }; - }; - - public testNeuronUnbounded = (): LearningResult => { - //let value = 0; - this.target = SharedFunctions.activationFunction(this.target); - //this.output = Math.random()*100; - let weight = 2; //Math.random()*5.0; - this.log( - `init o:${this.target}, w:${weight}, ld:${this.learningDelta}, e:${this.error}` - ); - - let i = 0; - let cost = 0; - for (i = 0; i < this.maxSteps; i++) { - // forward propagation - let value = this.input * weight; - //const newValue = value; - const newValue = SharedFunctions.activationFunction(value); - - // error find - cost = this.target - newValue; // take sign only? - - // new weight - //const dy = 1.0; - const dy = SharedFunctions.activationFunctionPrime(newValue); - const pD = this.learningDelta * cost; - const mDelta = Math.abs(pD) > this.learningDelta ? pD : cost; - - const pD2 = mDelta * dy; - const pDelta = Math.abs(pD2) > dy ? pD2 : mDelta; - - const newWeight = weight + pDelta; // this.ldelta * delta * dy * this.input; - const arrow = cost > 0 ? 'i' : 'v'; - const deltaWeight = weight - newWeight; - this.log( - `i:${i}, ${arrow}, d:${cost.toFixed(3)}, w:${newWeight.toFixed( - 3 - )}, dw:${deltaWeight.toFixed(3)}, v:${value.toFixed( - 3 - )}, gv:${newValue.toFixed(3)}, dy:${dy.toFixed(3)}, pD:${pD.toFixed( - 3 - )}, pD2:${pD2.toFixed(3)}` - ); - if (Math.abs(cost) < this.error) { - break; - } - - weight = newWeight; - value = newValue; - } - return { weight, steps: i, cost }; - }; - - public testNeuronGw = (): LearningResult => { - //let value = 0; - this.target = SharedFunctions.activationFunction(this.target); - //this.output = Math.random()*100; - let weight = 2; //Math.random()*5.0; - this.log( - `init o:${this.target}, w:${weight}, ld:${this.learningDelta}, e:${this.error}` - ); - - let i = 0; - let cost = 0; - for (i = 0; i < this.maxSteps; i++) { - // forward propagation - let value = this.input * weight; - //const newValue = value; - const newValue = SharedFunctions.activationFunction(value); - - // error find - cost = this.target - newValue; // take sign only? - - // new weight - //const dy = 1.0; - const dy = SharedFunctions.activationFunctionPrime(newValue); - const pD = this.learningDelta * cost; - const mDelta = Math.abs(pD) > this.learningDelta ? pD : cost; - - const pD2 = mDelta * dy; - const pDelta = Math.abs(pD2) > dy ? pD2 : mDelta; - - const newWeight = weight + pDelta; // this.ldelta * delta * dy * this.input; - const arrow = cost > 0 ? 'i' : 'v'; - const deltaWeight = weight - newWeight; - this.log( - `i:${i}, ${arrow}, d:${cost.toFixed(3)}, w:${newWeight.toFixed( - 3 - )}, dw:${deltaWeight.toFixed(3)}, v:${value.toFixed( - 3 - )}, gv:${newValue.toFixed(3)}, dy:${dy.toFixed(3)}, pD:${pD.toFixed( - 3 - )}, pD2:${pD2.toFixed(3)}` - ); - if (Math.abs(cost) < this.error) { - break; - } - - weight = newWeight; - value = newValue; - } - return { weight, steps: i, cost }; - }; - /* -public testNeuronOld = () => { - const n = new Neuron(); - n.init(); - - for (let i = 0; i < 100; i++) { - n.value = this.input; - - const deltaOut = n.value - this.output; - const df = Configuration.activationFunctionDf(n.value); - const newWeight = - n.weight + this.delta * deltaOut * this.input * df; - - console.log(`nw correction i:${i}, d:${delta}, e:${this.error}, ow:${n.weight}, nw:${newWeight}`); - if (delta < this.error) { - break; - } - - n.weight = newWeight; - } - }; - - private countValues = () => - { - - - }*/ - - private log = (message?: unknown, optionalParams?: unknown[]): void => { - if (!this.debug) { - return; - } - - if (optionalParams) { - console.log(message, ...optionalParams); - return; - } - - console.log(message); - }; -} +// import { Neuron } from './Neuron'; +// import { SharedFunctions } from './configuration/SharedFunctions'; +// import { StringFunctions } from './utilities/StringFunctions'; +// import { Configuration } from './configuration/Configuration'; + +// const fnz = StringFunctions.fnz; + +// export interface LearningResult { +// weight: number; +// steps: number; +// cost: number; +// } + +// export class NetworkOld { +// public debug = false; + +// /** input value */ +// private input = 1.0; +// /** value which we want to achieve for given input */ +// private target = 40.0; +// private error = 0.0001; +// /** Decreases speed of network adaptation to a given task, makes that process more predictable */ +// private learningDelta = 0.01; +// private maxSteps = 1000; +// // private neurons: Neuron[] = [];8 + +// //private i: number[]; + +// constructor(input?: number, target?: number) { +// //this.i = [0,1,2]; +// if (!!input || input === 0) { +// this.input = input; +// } + +// if (!!target || target === 0) { +// this.target = target; +// } +// } +// /* +// private init = (inNum:number, oNum) => { +// this.neurons = []; +// for(let i = 0; i < inNum; i++) +// { +// this.neurons.push(new Neuron); +// this.neurons[i].init(); +// } +// } +// */ + +// public testNeuron = (type: number): LearningResult => { +// switch (type) { +// case 1: +// return this.testNeuronGw(); +// break; +// case 2: +// return this.testNeuronReal(); +// break; +// case 3: +// this.maxSteps = 2000; +// Configuration.bias = 0; +// Configuration.activationType = 'None'; +// Configuration.useCostFunction = 'None'; +// return this.testNeuronSimple(); +// break; +// default: +// return this.testNeuronUnbounded(); +// break; +// } +// }; + +// /** simple way to count a new weight as weight + cost * learning_delta */ +// public testNeuronSimple = (): LearningResult => { +// const neuron = new Neuron(); +// neuron.debug = this.debug; + +// let weight = 1; //Math.random()*5.0; +// this.log( +// `init o:${this.target}, w:${weight}, ld:${this.learningDelta}, e:${this.error}` +// ); + +// let i = 0; +// let cost = 0; +// for (i = 0; i < this.maxSteps; i++) { +// // forward propagation +// const prediction = neuron.prediction(weight, this.input); + +// // error find +// cost = neuron.cost(this.target); + +// // new weight +// const newWeight = weight + this.learningDelta * cost; + +// const arrow = cost > 0 ? 'i' : 'v'; +// const deltaWeight = weight - newWeight; + +// this.log( +// `i:${i}, ${arrow}, cost:${fnz(cost)}, w:${fnz(newWeight)}, dw:${fnz( +// deltaWeight +// )}, v:${fnz(neuron.output)}, gv:${fnz(prediction)}` +// ); +// if (Math.abs(cost) < this.error) { +// break; +// } + +// weight = newWeight; +// // value = newValue; +// } + +// return { weight, steps: i, cost }; +// }; + +// public testNeuronReal = (): LearningResult => { +// const neuron = new Neuron(); +// neuron.debug = this.debug; +// //neuron.init(1, 1); + +// // console.log(`0.12356 :${fnz(0.12356)}`); +// // console.log(`1.00165 :${fnz(1.00165)}`); +// // console.log(`0.000123 :${fnz(0.000123)}`); +// // console.log(`0.0000017 :${fnz(0.0000017)}`); +// // console.log(`10.03001 :${fnz(10.03001)}`); + +// // return; + +// //let value = 0; +// this.target = SharedFunctions.activationFunction(this.target); +// //this.output = Math.random()*100; +// let weight = 2; //Math.random()*5.0; +// this.log( +// `init o:${this.target}, w:${weight}, ld:${this.learningDelta}, e:${this.error}` +// ); + +// let i = 0; +// let cost = 0; +// for (i = 0; i < this.maxSteps; i++) { +// // forward propagation +// const prediction = neuron.prediction(weight, this.input); + +// // error find +// cost = neuron.cost(this.target); + +// // new weight +// //const dy = 1.0; +// const dy = SharedFunctions.activationFunctionPrime(prediction); // why ????? +// //const pD = this.ldelta * cost; +// //const mDelta = Math.abs(pD) > this.ldelta ? pD : cost; + +// //const pD2 = mDelta * dy; +// //const pDelta = Math.abs(pD2) > dy ? pD2 : mDelta; + +// const ccost = Math.abs(cost) > 1 ? cost : Math.sign(cost); + +// //const newWeight = weight + pDelta; // this.ldelta * delta * dy * this.input; +// //const newWeight = weight + this.ldelta * cost * dy * this.input; +// const newWeight = weight + this.learningDelta * ccost; +// const arrow = cost > 0 ? 'i' : 'v'; +// const deltaWeight = weight - newWeight; + +// this.log( +// `i:${i}, ${arrow}, cost:${fnz(cost)}, w:${fnz(newWeight)}, dw:${fnz( +// deltaWeight +// )}, v:${fnz(neuron.output)}, gv:${fnz(prediction)}, dy:${fnz(dy)}` +// ); +// if (Math.abs(cost) < this.error) { +// break; +// } + +// weight = newWeight; +// // value = newValue; +// } +// return { weight, steps: i, cost }; +// }; + +// public testNeuronUnbounded = (): LearningResult => { +// //let value = 0; +// this.target = SharedFunctions.activationFunction(this.target); +// //this.output = Math.random()*100; +// let weight = 2; //Math.random()*5.0; +// this.log( +// `init o:${this.target}, w:${weight}, ld:${this.learningDelta}, e:${this.error}` +// ); + +// let i = 0; +// let cost = 0; +// for (i = 0; i < this.maxSteps; i++) { +// // forward propagation +// let value = this.input * weight; +// //const newValue = value; +// const newValue = SharedFunctions.activationFunction(value); + +// // error find +// cost = this.target - newValue; // take sign only? + +// // new weight +// //const dy = 1.0; +// const dy = SharedFunctions.activationFunctionPrime(newValue); +// const pD = this.learningDelta * cost; +// const mDelta = Math.abs(pD) > this.learningDelta ? pD : cost; + +// const pD2 = mDelta * dy; +// const pDelta = Math.abs(pD2) > dy ? pD2 : mDelta; + +// const newWeight = weight + pDelta; // this.ldelta * delta * dy * this.input; +// const arrow = cost > 0 ? 'i' : 'v'; +// const deltaWeight = weight - newWeight; +// this.log( +// `i:${i}, ${arrow}, d:${cost.toFixed(3)}, w:${newWeight.toFixed( +// 3 +// )}, dw:${deltaWeight.toFixed(3)}, v:${value.toFixed( +// 3 +// )}, gv:${newValue.toFixed(3)}, dy:${dy.toFixed(3)}, pD:${pD.toFixed( +// 3 +// )}, pD2:${pD2.toFixed(3)}` +// ); +// if (Math.abs(cost) < this.error) { +// break; +// } + +// weight = newWeight; +// value = newValue; +// } +// return { weight, steps: i, cost }; +// }; + +// public testNeuronGw = (): LearningResult => { +// //let value = 0; +// this.target = SharedFunctions.activationFunction(this.target); +// //this.output = Math.random()*100; +// let weight = 2; //Math.random()*5.0; +// this.log( +// `init o:${this.target}, w:${weight}, ld:${this.learningDelta}, e:${this.error}` +// ); + +// let i = 0; +// let cost = 0; +// for (i = 0; i < this.maxSteps; i++) { +// // forward propagation +// let value = this.input * weight; +// //const newValue = value; +// const newValue = SharedFunctions.activationFunction(value); + +// // error find +// cost = this.target - newValue; // take sign only? + +// // new weight +// //const dy = 1.0; +// const dy = SharedFunctions.activationFunctionPrime(newValue); +// const pD = this.learningDelta * cost; +// const mDelta = Math.abs(pD) > this.learningDelta ? pD : cost; + +// const pD2 = mDelta * dy; +// const pDelta = Math.abs(pD2) > dy ? pD2 : mDelta; + +// const newWeight = weight + pDelta; // this.ldelta * delta * dy * this.input; +// const arrow = cost > 0 ? 'i' : 'v'; +// const deltaWeight = weight - newWeight; +// this.log( +// `i:${i}, ${arrow}, d:${cost.toFixed(3)}, w:${newWeight.toFixed( +// 3 +// )}, dw:${deltaWeight.toFixed(3)}, v:${value.toFixed( +// 3 +// )}, gv:${newValue.toFixed(3)}, dy:${dy.toFixed(3)}, pD:${pD.toFixed( +// 3 +// )}, pD2:${pD2.toFixed(3)}` +// ); +// if (Math.abs(cost) < this.error) { +// break; +// } + +// weight = newWeight; +// value = newValue; +// } +// return { weight, steps: i, cost }; +// }; +// /* +// public testNeuronOld = () => { +// const n = new Neuron(); +// n.init(); + +// for (let i = 0; i < 100; i++) { +// n.value = this.input; + +// const deltaOut = n.value - this.output; +// const df = Configuration.activationFunctionDf(n.value); +// const newWeight = +// n.weight + this.delta * deltaOut * this.input * df; + +// console.log(`nw correction i:${i}, d:${delta}, e:${this.error}, ow:${n.weight}, nw:${newWeight}`); +// if (delta < this.error) { +// break; +// } + +// n.weight = newWeight; +// } +// }; + +// private countValues = () => +// { + +// }*/ + +// private log = (message?: unknown, optionalParams?: unknown[]): void => { +// if (!this.debug) { +// return; +// } + +// if (optionalParams) { +// console.log(message, ...optionalParams); +// return; +// } + +// console.log(message); +// }; +// } From b57c8bd1802d41962a595ae02d8325817e74671f Mon Sep 17 00:00:00 2001 From: KEMBL Date: Mon, 31 Aug 2020 06:31:33 +0200 Subject: [PATCH 4/5] feature: #2 code is finished, not tested --- src/neuron/Layer.ts | 83 ++++++++++++--------- src/neuron/Network.ts | 32 ++++---- src/neuron/Neuron.ts | 63 +++++++++++----- src/neuron/configuration/SharedFunctions.ts | 23 ++++++ 4 files changed, 133 insertions(+), 68 deletions(-) diff --git a/src/neuron/Layer.ts b/src/neuron/Layer.ts index 0c1f1d1..4b3e9e3 100644 --- a/src/neuron/Layer.ts +++ b/src/neuron/Layer.ts @@ -6,13 +6,13 @@ import { Neuron } from './'; export class Layer { public debug = false; - public get isFirst(): boolean { - return this.layerId === 0; - } + // public get isFirst(): boolean { + // return this.layerId === 0; + // } public neurons: Neuron[] = []; - constructor(private layerId: number, private neuronsAmount: number) { + constructor(public layerId: number, private neuronsAmount: number) { this.init(); } @@ -44,22 +44,19 @@ export class Layer { * @param sourceLayer */ public propagate = (sourceLayer: Layer): void => { - if (this.layerId === 0) { - return; - } - for (let i = 0; i < sourceLayer.neurons.length; i++) { - this.propagateNeuron(sourceLayer.neurons[i]); + for (let i = 0; i < this.neurons.length; i++) { + this.propagateNeuron(this.neurons[i], sourceLayer); + this.neurons[i].prediction(); } }; /** - * Takes source neuron and propagate it to all current layer neurons - * @param sourceNeuron + * Takes layer's neuron and feed it with all income signals + * @param neuron */ - private propagateNeuron = (sourceNeuron: Neuron): void => { - for (let i = 0; i < this.neurons.length; i++) { - const neuron = this.neurons[i]; - neuron.prediction(sourceNeuron.output); + private propagateNeuron = (neuron: Neuron, sourceLayer: Layer): void => { + for (let i = 0; i < sourceLayer.neurons.length; i++) { + neuron.propagate(i, neuron.output); } }; @@ -79,29 +76,45 @@ export class Layer { return cost / (2 * this.neurons.length); }; - backPropagate = (nextLayer: Layer): void => { + /** Receives values of errors on the next layer neurons */ + countErrors = ( + nextLayerOutputArray: number[], + nextLayer?: Layer + ): number[] => { + if (this.layerId === 0) { + return []; + } + + const errorWeights: number[] = []; for (let i = 0; i < this.neurons.length; i++) { - for (let j = 0; j < nextLayer.neurons.length; j++) { - // cost += this.neurons[i].cost(outputArray[i]); + if (nextLayer === undefined) { + this.neurons[i].propagationError = this.neurons[i].cost( + nextLayerOutputArray[i] + ); + } else { + this.neurons[i].propagationError = nextLayer.getWeightError(i); } + + errorWeights[i] = this.neurons[i].propagationError; } - //const dy = SharedFunctions.activationFunctionPrime(newValue); - //const newWeight = weight + this.ldelta * cost * dy * this.input; - - // - // // new weight - // const newWeight = weight + this.learningDelta * cost; - // const arrow = cost > 0 ? 'i' : 'v'; - // const deltaWeight = weight - newWeight; - // this.log( - // `i:${i}, ${arrow}, cost:${fnz(cost)}, w:${fnz(newWeight)}, dw:${fnz( - // deltaWeight - // )}, v:${fnz(neuron.output)}, gv:${fnz(prediction)}` - // ); - // if (Math.abs(cost) < this.error) { - // break; - // } - // weight = newWeight; + return errorWeights; + }; + + /** + * Collects sum of all errors on the given weight index + */ + private getWeightError = (inputId: number): number => { + let error = 0; + for (let i = 0; i < this.neurons.length; i++) { + error += this.neurons[i].weightError(inputId); + } + return error; + }; + + correctWeights = (learningDelta: number): void => { + for (let i = 0; i < this.neurons.length; i++) { + this.neurons[i].correctWeights(learningDelta); + } }; } diff --git a/src/neuron/Network.ts b/src/neuron/Network.ts index 0dde4e4..6302934 100644 --- a/src/neuron/Network.ts +++ b/src/neuron/Network.ts @@ -14,9 +14,7 @@ export class Network { private layers: Layer[] = []; - private get lastLayer(): Layer { - return this.layers[this.layers.length - 1]; - } + private lastLayer!: Layer; constructor(maxSteps: number, error: number, ldelta: number) { this.maxSteps = maxSteps; @@ -46,6 +44,8 @@ export class Network { break; } } + + this.lastLayer = this.layers[this.layers.length - 1]; }; /** @@ -63,7 +63,7 @@ export class Network { } // new weights count - this.backPropagation(); + this.backPropagation(outputArray); } return error; @@ -73,11 +73,10 @@ export class Network { * Propagate input values through all network */ private propagate = (): void => { - let previousLayer: Layer; + let previousLayer: Layer | undefined; for (const layer of this.layers) { - if (!layer.isFirst) { - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - layer.propagate(previousLayer!); + if (previousLayer !== undefined) { + layer.propagate(previousLayer); } previousLayer = layer; } @@ -93,14 +92,19 @@ export class Network { /** * Count new weights */ - private backPropagation = (): void => { - let previousLayer: Layer; + private backPropagation = (outputArray: number[]): void => { + let previousLayer: Layer | undefined = undefined; + let nextLayerOutputArray = outputArray; for (const layer of this.layers.reverse()) { - if (!layer.isFirst) { - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - layer.backPropagate(previousLayer!); - } + nextLayerOutputArray = layer.countErrors( + nextLayerOutputArray, + previousLayer + ); previousLayer = layer; } + + for (const layer of this.layers) { + layer.correctWeights(this.ldelta); + } }; } diff --git a/src/neuron/Neuron.ts b/src/neuron/Neuron.ts index 6ef42ef..ad318c7 100644 --- a/src/neuron/Neuron.ts +++ b/src/neuron/Neuron.ts @@ -4,11 +4,14 @@ import { Configuration, SharedFunctions } from './configuration'; * One neuron logic */ export class Neuron { + public neuronId = 0; public debug = false; + public propagationError = 0; + private name = ''; - private inputValue = 0; private activatedValue = 0; - private _weight = 0; + private propagationSum = 0; + private weights: number[] = []; public get output(): number { return this.activatedValue; @@ -18,36 +21,58 @@ export class Neuron { this.activatedValue = value; } - public get weight(): number { - return this._weight; - } - - public set weight(value: number) { - this._weight = value; - } - - private set input(x: number) { - this.inputValue = x + Configuration.bias; - this.activatedValue = SharedFunctions.activationFunction(this.inputValue); + private set input(value: number) { + this.activatedValue = SharedFunctions.activationFunction(value); this.log( - `nr ${this.name} v: ${this.inputValue} -> ${this.activatedValue}, in: ${x}` + `nr ${this.name} v: ${value} -> ${this.activatedValue}, in: ${value}` ); } - public init(layerId: number, index: number): void { - this.name = `${layerId}_${index}`; - this._weight = Math.random(); + public init(layerId: number, neuronId: number): void { + this.neuronId = neuronId; + this.name = `${layerId}_${neuronId}`; } public cost(expected: number): number { return SharedFunctions.costFunction(expected, this.activatedValue); } - public prediction(x: number): number { - this.input = this._weight * x; + public propagate(linkIndex: number, linkValue: number): number { + let weight = this.weights[linkIndex]; + if (weight === undefined) { + weight = SharedFunctions.initialWeight(); // TODO: in order to optimize this we could prefill weights oat the init step + this.weights[linkIndex] = weight; + } + + this.propagationSum += weight * linkValue; return this.output; } + /** + * Makes neuron prediction according to signals on inputs + */ + public prediction(): void { + // this.state = NeuronState.Prediction; + this.input = this.propagationSum; + this.propagationSum = Configuration.bias; + } + + public weightError = (inputId: number): number => { + return this.weights[inputId] * this.propagationError; + }; + + correctWeights = (learningDelta: number): void => { + for (let i = 0; i < this.weights.length; i++) { + const newWeight = + this.weights[i] + + this.propagationError * + SharedFunctions.activationFunctionPrime(this.output) * + this.propagationSum * + learningDelta; + this.weights[i] = newWeight; + } + }; + private log = (logLine: string, args: string[] = []): void => { if (!this.debug) { return; diff --git a/src/neuron/configuration/SharedFunctions.ts b/src/neuron/configuration/SharedFunctions.ts index 40cc0cc..689b865 100644 --- a/src/neuron/configuration/SharedFunctions.ts +++ b/src/neuron/configuration/SharedFunctions.ts @@ -4,18 +4,41 @@ import { Configuration } from './Configuration'; * Shared functions */ class SharedFunctions { + /** + * Initial random weight interval is probably depends on elcted activation function + * TODO: !check that + */ + public initialWeight = (): number => { + switch (Configuration.activationType) { + case 'ReLU': + return Math.random(); // [0, 1] + case 'LeakyReLU': + return Math.random(); // [0, 1] + case 'Sigmoid': + return Math.random() - 1; // [-0.5, 0.5] + default: { + console.warn( + `Define initial weight function for ${Configuration.activationType} actiovation type` + ); + return Math.random(); // [0, 1] + } + } + }; + // more https://rohanvarma.me/Loss-Functions/ // more https://github.com/trekhleb/nano-neuron public costFunction = (expected: number, prediction: number): number => { switch (Configuration.useCostFunction) { case 'Squared': { // every time returns a result in interval [0, +inf) + // usually used for regression const v = expected * expected - 2 * expected * prediction + prediction * prediction; return v * 0.5; } + // case 'CrossEntropy': // TODO: needs for classification default: return expected - prediction; } From 091495c02b7131f42e97c5ca9016c0c65c8a790e Mon Sep 17 00:00:00 2001 From: KEMBL Date: Mon, 31 Aug 2020 23:23:35 +0200 Subject: [PATCH 5/5] feature: #2 network with backpropagation is ready --- README.md | 6 + docs/9999.steps.error.txt | 101 +++++++ package.json | 4 +- src/index.ts | 101 ++++--- src/neuron/Layer.ts | 82 +++-- src/neuron/Network.ts | 103 +++++-- src/neuron/Neuron.ts | 90 ++++-- src/neuron/configuration/Configuration.ts | 11 +- src/neuron/configuration/SharedFunctions.ts | 55 ++-- src/neuron/utilities/StringFunctions.ts | 8 +- yarn.lock | 313 ++++++++++---------- 11 files changed, 576 insertions(+), 298 deletions(-) create mode 100644 docs/9999.steps.error.txt diff --git a/README.md b/README.md index 7f9153a..8508115 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,9 @@ # machine-learning A set of code for testing machine learning related ideas in node enviroment + +Feedforward neural network with backpropagation + +- More: http://galaxy.agh.edu.pl/~vlsi/AI/backp_t_en/backprop.html + +- More: https://www.youtube.com/watch?v=t-Jpm1axBko diff --git a/docs/9999.steps.error.txt b/docs/9999.steps.error.txt new file mode 100644 index 0000000..a00ca88 --- /dev/null +++ b/docs/9999.steps.error.txt @@ -0,0 +1,101 @@ + + + + +Cannot solve network in 9999 steps +interesting bug - newtork passed propagation one time in forward direction and nex time in back direction + +in + + Configuration.bias = 1; + Configuration.activationType = 'Sigmoid'; + Configuration.useCostFunction = 'Identity'; + const inputs = [1, 0]; + const targetOutputs = [1]; + + const error = 0.0001; + const maxSteps = 10000; + const ldelta = 0.1; + const debug = true; + const layers = [2, 2, 1]; + + // Neurons: XYZ X - source output, Y - layer row Z - input Layer + // Debug. prefill weights + // [ [layer1], [layer2], ..., [[neuron1], [neuron2], ... ], [[[weight1, weight2, ...]], [[weight1, weight2, ...]], ...], [neuron2], ... ] ] + const weights = [ + [ + [0.13, -0.42], // w111, w211 + [-0.34, 0.38] // w121, w221 + ], + [ + [0.25, -0.2], // w112, w212 + [0.07, 0.32] // w122, 2222 + ], + [[-0.41, 0.12]] // w113, w213 + ]; + + +OUT: + +Nt : Learn step 9999 +Nt : Propagation +Nr 12: Out: act(2.7451) -> 0.94 +Nr 12: prediction 0.94 +Nr 22: Out: act(3.9656) -> 0.98 +Nr 22: prediction 0.98 +Nr 11: Out: act(7.1340) -> 1.00 +Nr 11: prediction 1.00 +Nr 21: Out: act(7.8411) -> 1.00 +Nr 21: prediction 1.00 +Nr 10: Out: act(0.028) -> 0.51 +Nr 10: prediction 0.51 +Nr 20: Out: act(-0.19) -> 0.45 +Nr 20: prediction 0.45 +Nr 13: costf expec: 1.0000, act: 0.99 +Lr 3: Lec: 0 0.0050 +Lr 3: Lec: 0.0050 +Nt : Cost error search 0.0050 +Nt : Res1 0.0050 <=? 0.0001 +Nt : Back propagation +Lr 0: CountErrors +Lr 1: CountErrors +Nr 10: weightError W110 = 0 +Nr 20: weightError W120 = 0 +Nr 10: weightError W210 = 0 +Nr 20: weightError W220 = 0 +Lr 1: PropagationError [ 0, 0 ] +Lr 2: CountErrors +Nr 11: weightError W111 = 0 +Nr 21: weightError W121 = 0 +Nr 11: weightError W211 = 0 +Nr 21: weightError W221 = 0 +Lr 2: PropagationError [ 0, 0 ] +Lr 3: CountErrors +Nr 12: weightError W112 = 0 +Nr 22: weightError W122 = 0 +Lr 3: PropagationError [ 0 ] +Step weights [ + [ + [ 3.6557519074579687, 2.750120828433372 ], + [ 3.3842892480045124, 3.730466980473381 ] + ], + [ + [ 1.7539241589012051, 1.3473803347581663 ], + [ 2.9805490674365664, 3.333285567708932 ] + ], + [ [ 1.7626923588248318, 2.5608905458477635 ] ] +] +Programm finished [ 0.9949911111648337 ] [ 1 ] +Result weights [ + [ + [ 3.6557519074579687, 2.750120828433372 ], + [ 3.3842892480045124, 3.730466980473381 ] + ], + [ + [ 1.7539241589012051, 1.3473803347581663 ], + [ 2.9805490674365664, 3.333285567708932 ] + ], + [ [ 1.7626923588248318, 2.5608905458477635 ] ] +] +Done in 194.45s. + diff --git a/package.json b/package.json index e22815e..e28d199 100644 --- a/package.json +++ b/package.json @@ -46,7 +46,7 @@ "eslint-plugin-prefer-arrow": "^1.2.1", "prettier": "^1.19.1", "prettier-eslint": "^9.0.1", - "ts-node": "^8.10.1", - "typescript": "^3.8.3" + "ts-node": "^9.0.0", + "typescript": "^4.0.2" } } diff --git a/src/index.ts b/src/index.ts index efdbf52..fa8b16b 100644 --- a/src/index.ts +++ b/src/index.ts @@ -4,52 +4,67 @@ class Program { constructor() { console.log('Programm started'); - Configuration.bias = 0; - Configuration.activationType = 'None'; - Configuration.useCostFunction = 'None'; - const inputs = [1]; - const targetOutputs = [40]; - - const error = 0.0001; - const maxSteps = 2000; - const ldelta = 0.01; - - const neuronsCount = inputs.length; - const network = new Network(maxSteps, error, ldelta); // error, ldelta, maxSteps - network.debug = false; - network.addLayer(neuronsCount); // make neurons / init them / etc + Configuration.bias = 1; + Configuration.activationType = 'Sigmoid'; + Configuration.useCostFunction = 'Identity'; + const inputs = [1, 0]; + const targetOutputs = [1]; + + const maximumCostError = 0.0001; + const maxSteps = 1000; + const ldelta = 0.1; + const debug = true; + const layers = [2, 2, 1]; + + // Fill in arrays if want to start not from random weights + // Neurons: XYZ X - source output, Y - layer row Z - input Layer + // Debug. prefill weights + // [ [layer1], [layer2], ..., [[neuron1], [neuron2], ... ], [[[weight1, weight2, ...]], [[weight1, weight2, ...]], ...], [neuron2], ... ] ] + const weights: number[][][] = []; + // const weights: number[] = [ + // [ + // [0.13, -0.42], // w111, w211 + // [-0.34, 0.38] // w121, w221 + // ], + // [ + // [0.25, -0.2], // w112, w212 + // [0.07, 0.32] // w122, 2222 + // ], + // [[-0.41, 0.12]] // w113, w213 + // ]; + + // const weights = [ + // [ [ 12.073027175758078, -0.42 ], [ 11.29143338568982, 0.38 ] ], + // [ + // [ 2.5379574472175412, 2.060681210357274 ], + // [ 4.114487335508431, 4.26457245636459 ] + // ], + // [ [ 2.11694803045532, 2.897016751994774 ] ] + // ]; + + const network = new Network( + layers[0], + maxSteps, + maximumCostError, + ldelta, + debug + ); // error, ldelta, maxSteps + for (const neuronsCount of layers) { + network.addLayer(neuronsCount); // make neurons / init them / etc + } + + if (weights.length > 0) { + network.initWeights(weights); + } + network.learn(inputs, targetOutputs); // propagate / errorcost / weig\hts correction (back propagation) //new Network().testNeuron(); - const result = network.result(); - console.log('Programm finished', result); + const result = network.output(); + console.log('Programm finished', result, targetOutputs); + console.log('Result weights', network.getWeights()); + console.log('Last step', Network.currentStep + 1); + console.log('Error cost', network.findStepError(targetOutputs)); } } new Program(); - -// class Program1 { -// constructor() { -// console.log('Programm started'); - -// const inputs: number[] = [1, 50, 10, 0.3, 1, 2, 10, 45]; -// const outputs: number[] = [50, 1, 10, 20, 100, 33, 1, 15]; -// const results: LearningResult[] = []; - -// for (let i = 0; i < inputs.length; i++) { -// const network = new Network(inputs[i], outputs[i]); -// network.debug = false; -// results.push(network.testNeuron(3)); -// } - -// for (let i = 0; i < inputs.length; i++) { -// console.log( -// `${i}. steps ${results[i].steps}, cost ${results[i].cost} input ${inputs[i]} * w ${results[i].weight} = ${outputs[i]} `, -// inputs[i] * results[i].weight, -// inputs[i] * results[i].weight === outputs[i], -// Math.round(inputs[i] * results[i].weight) === outputs[i] -// ); -// } - -// console.log('Programm finished'); -// } -// } diff --git a/src/neuron/Layer.ts b/src/neuron/Layer.ts index 4b3e9e3..2261572 100644 --- a/src/neuron/Layer.ts +++ b/src/neuron/Layer.ts @@ -1,40 +1,65 @@ -import { Neuron } from './'; +import { Neuron, StringFunctions } from './'; + +// shortcut to rounding function +// eslint-disable-next-line no-unused-vars, @typescript-eslint/no-unused-vars +// const _fnz = StringFunctions.fnz; /** * One neurons layer */ export class Layer { - public debug = false; - - // public get isFirst(): boolean { - // return this.layerId === 0; - // } - + private debug = false; + private name = ''; public neurons: Neuron[] = []; - constructor(public layerId: number, private neuronsAmount: number) { + constructor( + public layerId: number, + private neuronsAmount: number, + debug?: boolean + ) { + this.debug = !!debug; this.init(); } private init = (): void => { this.neurons = []; + this.name = `Lr ${this.layerId}`; for (let i = 0; i < this.neuronsAmount; i++) { - const neuron = new Neuron(); - neuron.debug = this.debug; - neuron.init(this.layerId, i); + const neuronId = i + 1; + const neuron = new Neuron(this.layerId, neuronId, this.debug); this.neurons.push(neuron); } }; + /** Allows to modify weighs of neurons for debug purposes */ + public initWeights = (weights: number[][]): void => { + // this.log('Lw', weights); + for (let i = 0; i < this.neurons.length; i++) { + const neuron = this.neurons[i]; + neuron.initWeights(weights[i]); + } + }; + + /** Debug method. Allows to set weights directly */ + public getWeights = (): number[][] => { + // this.log('GNe', weights); + const weights: number[][] = []; + for (let i = 0; i < this.neurons.length; i++) { + const neuron = this.neurons[i]; + weights.push(neuron.getWeights()); + } + return weights; + }; + /** * Init layer, used to set output vars in the first layer * @param sourceLayer */ public setOutput = (inputVariables: number[]): void => { if (this.layerId !== 0) { - console.warn(`Init: Current layer ${this.layerId} is nor input layer!`); + this.log(`WARN: Current layer ${this.layerId} is not an input layer!`); } - for (let i = 0; i <= this.neurons.length; i++) { + for (let i = 0; i < this.neurons.length; i++) { this.neurons[i].output = inputVariables[i]; } }; @@ -44,6 +69,10 @@ export class Layer { * @param sourceLayer */ public propagate = (sourceLayer: Layer): void => { + // this.log( + // `Propagate layer ${this.layerId} from layer ${sourceLayer.layerId}`, + // this.neurons.length + // ); for (let i = 0; i < this.neurons.length; i++) { this.propagateNeuron(this.neurons[i], sourceLayer); this.neurons[i].prediction(); @@ -55,12 +84,14 @@ export class Layer { * @param neuron */ private propagateNeuron = (neuron: Neuron, sourceLayer: Layer): void => { + // this.log(`propagateNeuron`, sourceLayer.neurons.length); for (let i = 0; i < sourceLayer.neurons.length; i++) { - neuron.propagate(i, neuron.output); + neuron.propagate(i, sourceLayer.neurons[i].output); + // neuron.propagate(0, sourceLayer.neurons[i].output); } }; - result = (): number[] => { + public output = (): number[] => { const resultsList: number[] = []; for (let i = 0; i < this.neurons.length; i++) { resultsList.push(this.neurons[i].output); @@ -68,19 +99,22 @@ export class Layer { return resultsList; }; - cost = (outputArray: number[]): number => { + public cost = (outputArray: number[]): number => { let cost = 0; for (let i = 0; i < this.neurons.length; i++) { cost += this.neurons[i].cost(outputArray[i]); } - return cost / (2 * this.neurons.length); + const layerErrorCost = cost / (2 * this.neurons.length); // TODO: ? what is the purpose of division by 2*... ? + // this.log(`Lec: ${fnz(layerErrorCost)}`); + return layerErrorCost; }; /** Receives values of errors on the next layer neurons */ - countErrors = ( + public countErrors = ( nextLayerOutputArray: number[], nextLayer?: Layer ): number[] => { + this.log(`CountErrors`); if (this.layerId === 0) { return []; } @@ -97,7 +131,7 @@ export class Layer { errorWeights[i] = this.neurons[i].propagationError; } - + this.log(`PropagationError`, errorWeights); return errorWeights; }; @@ -112,9 +146,17 @@ export class Layer { return error; }; - correctWeights = (learningDelta: number): void => { + public correctWeights = (learningDelta: number): void => { for (let i = 0; i < this.neurons.length; i++) { this.neurons[i].correctWeights(learningDelta); } }; + + private log = (logLine: string, ...args: unknown[]): void => { + if (!this.debug) { + return; + } + + StringFunctions.log(`${this.name}: ${logLine}`, ...args); + }; } diff --git a/src/neuron/Network.ts b/src/neuron/Network.ts index 6302934..9418506 100644 --- a/src/neuron/Network.ts +++ b/src/neuron/Network.ts @@ -1,10 +1,20 @@ -import { Layer } from './'; +import { Layer, StringFunctions } from './'; +// shortcut to rounding function +// eslint-disable-next-line no-unused-vars, @typescript-eslint/no-unused-vars +const fnz = StringFunctions.fnz; + +/** + * Feedforward network with back propagation + */ export class Network { - public debug = false; + public static currentStep = 0; + + private debug = false; + private name = 'Nt '; /** criteria to end learning */ - public error = 0.0001; + public maxError = 0.0001; /** maximum learn steps to learn */ public maxSteps = 2000; @@ -16,36 +26,45 @@ export class Network { private lastLayer!: Layer; - constructor(maxSteps: number, error: number, ldelta: number) { + constructor( + inputs: number, + maxSteps: number, + maxCostError: number, + ldelta: number, + debug?: boolean + ) { this.maxSteps = maxSteps; - this.error = error; + this.maxError = maxCostError; this.ldelta = ldelta; + this.debug = !!debug; + + this.addLayer(inputs); // inuts } /** Adds new layer */ addLayer = (neuronsCount: number): void => { - const layerId = this.layers.length + 1; - const layer = new Layer(layerId, neuronsCount); + const layerId = this.layers.length; + const layer = new Layer(layerId, neuronsCount, this.debug); this.layers.push(layer); + this.lastLayer = layer; }; /** Returns output of the last layer */ - result = (): number[] => { - const lastLayer = this.layers[this.layers.length - 1]; - return lastLayer.result(); + output = (): number[] => { + return this.lastLayer.output(); }; /** Makes learning cycles */ learn = (inputArray: number[], outputArray: number[]): void => { this.layers[0].setOutput(inputArray); - for (let i = 0; i <= this.maxSteps; i++) { + for (let i = 0; i < this.maxSteps; i++) { + this.log(`Learn step ${i}`); + Network.currentStep = i; const error = this.learnStep(outputArray); - if (error <= this.error) { + if (error <= this.maxError) { break; } } - - this.lastLayer = this.layers[this.layers.length - 1]; }; /** @@ -53,18 +72,20 @@ export class Network { */ private learnStep = (outputArray: number[]): number => { let error = 1; - for (let i = 0; i <= this.maxSteps; i++) { - this.propagate(); + this.propagate(); + + // look at error value + error = this.findStepError(outputArray); + this.log(`Res1`, fnz(error), '<=?', this.maxError); + if (error <= this.maxError) { + this.log(`Res: ${fnz(error)} < ${this.maxError}`); + return error; + } - // error find - error = this.findStepError(outputArray); - if (error <= this.error) { - return error; - } + // new weights count + this.backPropagation(outputArray); - // new weights count - this.backPropagation(outputArray); - } + console.log('Step weights', this.getWeights()); return error; }; @@ -85,17 +106,20 @@ export class Network { /** * Searches of how big network result error is */ - private findStepError = (outputArray: number[]): number => { - return this.lastLayer.cost(outputArray); + public findStepError = (outputArray: number[]): number => { + const cost = this.lastLayer.cost(outputArray); + this.log(`Cost error search`, fnz(cost)); + return cost; }; /** * Count new weights */ private backPropagation = (outputArray: number[]): void => { + this.log(`Back propagation`); let previousLayer: Layer | undefined = undefined; let nextLayerOutputArray = outputArray; - for (const layer of this.layers.reverse()) { + for (const layer of this.layers.slice().reverse()) { nextLayerOutputArray = layer.countErrors( nextLayerOutputArray, previousLayer @@ -107,4 +131,29 @@ export class Network { layer.correctWeights(this.ldelta); } }; + + public initWeights = (weights: number[][][]): void => { + this.log('Nw', weights); + for (let i = 1; i < this.layers.length; i++) { + this.layers[i].initWeights(weights[i - 1]); + } + }; + + /** Debug method. Allows to set weights directly */ + public getWeights = (): number[][][] => { + // this.log('GNe', weights); + const weights: number[][][] = []; + for (let i = 1; i < this.layers.length; i++) { + weights.push(this.layers[i].getWeights()); + } + return weights; + }; + + private log = (logLine: string, ...args: unknown[]): void => { + if (!this.debug) { + return; + } + + StringFunctions.log(`${this.name}: ${logLine}`, ...args); + }; } diff --git a/src/neuron/Neuron.ts b/src/neuron/Neuron.ts index ad318c7..052ae5f 100644 --- a/src/neuron/Neuron.ts +++ b/src/neuron/Neuron.ts @@ -1,16 +1,23 @@ import { Configuration, SharedFunctions } from './configuration'; +import { StringFunctions } from '.'; + +// shortcut to rounding function +// eslint-disable-next-line no-unused-vars, @typescript-eslint/no-unused-vars +const fnz = StringFunctions.fnz; /** * One neuron logic */ export class Neuron { public neuronId = 0; - public debug = false; + public layerId = 0; public propagationError = 0; + private debug = false; private name = ''; private activatedValue = 0; - private propagationSum = 0; + private propagationSum = Configuration.bias; + private inputs: number[] = []; private weights: number[] = []; public get output(): number { @@ -23,29 +30,46 @@ export class Neuron { private set input(value: number) { this.activatedValue = SharedFunctions.activationFunction(value); - this.log( - `nr ${this.name} v: ${value} -> ${this.activatedValue}, in: ${value}` - ); + this.log(`Out: act(${fnz(value)}) -> ${fnz(this.activatedValue)}`); } - public init(layerId: number, neuronId: number): void { + constructor(layerId: number, neuronId: number, debug?: boolean) { this.neuronId = neuronId; - this.name = `${layerId}_${neuronId}`; + this.layerId = layerId; + this.name = `Nr ${neuronId}${layerId}`; + this.debug = !!debug; } public cost(expected: number): number { - return SharedFunctions.costFunction(expected, this.activatedValue); + //this.log(`costf expec: ${fnz(expected)}, act: ${fnz(this.output)}`); + this.log(`costf expec: ${expected}, act: ${this.output}`); + const errorCost = SharedFunctions.costFunction(expected, this.output); + return errorCost; } - public propagate(linkIndex: number, linkValue: number): number { - let weight = this.weights[linkIndex]; + /** + * Propagate method + * @param inputId Link index + * @param linkValue value + */ + public propagate(inputId: number, linkValue: number): void { + let weight = this.weights[inputId]; if (weight === undefined) { weight = SharedFunctions.initialWeight(); // TODO: in order to optimize this we could prefill weights oat the init step - this.weights[linkIndex] = weight; + this.weights[inputId] = weight; } + // this.log(`${this.wIndex(inputId)} = ${fnz(weight)}`); + this.inputs[inputId] = linkValue; + + // const pSum = this.propagationSum; this.propagationSum += weight * linkValue; - return this.output; + // this.log( + // `Sum: ${fnz(pSum)} -> ${fnz(this.propagationSum)} `, + // fnz(weight), + // '*', + // fnz(linkValue) + // ); } /** @@ -55,29 +79,49 @@ export class Neuron { // this.state = NeuronState.Prediction; this.input = this.propagationSum; this.propagationSum = Configuration.bias; + + this.log(`prediction ${fnz(this.output)}`); } public weightError = (inputId: number): number => { - return this.weights[inputId] * this.propagationError; + const error = this.weights[inputId] * this.propagationError; + this.log(`weightError ${this.wIndex(inputId)} = ${fnz(error)}`); + return error; }; - correctWeights = (learningDelta: number): void => { + public correctWeights = (learningDelta: number): void => { + const weightAdditionMultiplayer = + this.propagationError * + SharedFunctions.activationFunctionPrime(this.output) * + learningDelta; + for (let i = 0; i < this.weights.length; i++) { - const newWeight = - this.weights[i] + - this.propagationError * - SharedFunctions.activationFunctionPrime(this.output) * - this.propagationSum * - learningDelta; - this.weights[i] = newWeight; + const weightAddition = weightAdditionMultiplayer * this.inputs[i]; + this.weights[i] += weightAddition; + // this.log(`weightError ${this.wIndex(i)} = ${fnz(this.weights[i])}, added ${fnz(weightAddition)}`); } }; - private log = (logLine: string, args: string[] = []): void => { + /** Debug method. Allows to set weights directly */ + public initWeights = (weights: number[]): void => { + // this.log('Ne', weights); + this.weights = weights; + }; + + /** Debug method. Allows to set weights directly */ + public getWeights = (): number[] => { + // this.log('GNe', weights); + return this.weights; + }; + + private wIndex = (inputId: number): string => + `W${inputId + 1}${this.neuronId}${this.layerId}`; + + private log = (logLine: string, ...args: unknown[]): void => { if (!this.debug) { return; } - console.log(logLine, ...args); + StringFunctions.log(`${this.name}: ${logLine}`, ...args); }; } diff --git a/src/neuron/configuration/Configuration.ts b/src/neuron/configuration/Configuration.ts index 032acd5..089e617 100644 --- a/src/neuron/configuration/Configuration.ts +++ b/src/neuron/configuration/Configuration.ts @@ -1,13 +1,10 @@ /** * Main settings fo the ML system */ -class Configuration { - public bias = 1; +export class Configuration { + public static bias = 1; /** Activation function */ - public activationType = 'Identity'; + public static activationType = 'Identity'; /** Cost function */ - public useCostFunction = 'Squared'; + public static useCostFunction = 'Squared'; } - -const configuration = new Configuration(); -export { configuration as Configuration }; diff --git a/src/neuron/configuration/SharedFunctions.ts b/src/neuron/configuration/SharedFunctions.ts index 689b865..6a249b9 100644 --- a/src/neuron/configuration/SharedFunctions.ts +++ b/src/neuron/configuration/SharedFunctions.ts @@ -3,12 +3,12 @@ import { Configuration } from './Configuration'; /** * Shared functions */ -class SharedFunctions { +export class SharedFunctions { /** * Initial random weight interval is probably depends on elcted activation function * TODO: !check that */ - public initialWeight = (): number => { + public static initialWeight = (): number => { switch (Configuration.activationType) { case 'ReLU': return Math.random(); // [0, 1] @@ -27,7 +27,10 @@ class SharedFunctions { // more https://rohanvarma.me/Loss-Functions/ // more https://github.com/trekhleb/nano-neuron - public costFunction = (expected: number, prediction: number): number => { + public static costFunction = ( + expected: number, + prediction: number + ): number => { switch (Configuration.useCostFunction) { case 'Squared': { // every time returns a result in interval [0, +inf) @@ -40,77 +43,77 @@ class SharedFunctions { } // case 'CrossEntropy': // TODO: needs for classification default: + // Identity return expected - prediction; } }; - public activationFunction = (x: number): number => { + public static activationFunction = (x: number): number => { switch (Configuration.activationType) { case 'ReLU': - return this.activationFunctionReLU(x); + return SharedFunctions.activationFunctionReLU(x); case 'LeakyReLU': - return this.activationFunctionLeakyReLU(x); + return SharedFunctions.activationFunctionLeakyReLU(x); case 'Sigmoid': - return this.activationFunctionSigmoid(x); + return SharedFunctions.activationFunctionSigmoid(x); default: - return this.activationFunctionIdentity(x); + // Identity + return SharedFunctions.activationFunctionIdentity(x); } }; - public activationFunctionPrime = (x: number): number => { + public static activationFunctionPrime = (x: number): number => { switch (Configuration.activationType) { case 'ReLU': - return this.activationFunctionReLUPrime(x); + return SharedFunctions.activationFunctionReLUPrime(x); case 'LeakyReLU': - return this.activationFunctionLeakyReLUPrime(x); + return SharedFunctions.activationFunctionLeakyReLUPrime(x); case 'Sigmoid': - return this.activationFunctionSigmoidPrime(x); + return SharedFunctions.activationFunctionSigmoidPrime(x); default: - return this.activationFunctionIdentityPrime(x); + // Identity + return SharedFunctions.activationFunctionIdentityPrime(x); } }; // more: https://en.wikipedia.org/wiki/Rectifier_(neural_networks) - private activationFunctionReLU = (x: number): number => { + private static activationFunctionReLU = (x: number): number => { return x > 0 ? x : 0; }; /** * Streats slope as 45 degress when x > 0 and 0 degress for all other cases */ - private activationFunctionReLUPrime = (x: number): number => { + private static activationFunctionReLUPrime = (x: number): number => { return x > 0 ? 1 : 0; }; - private activationFunctionLeakyReLU = (x: number): number => { + private static activationFunctionLeakyReLU = (x: number): number => { return x > 0 ? x : 0.01 * x; }; - private activationFunctionLeakyReLUPrime = (x: number): number => { + private static activationFunctionLeakyReLUPrime = (x: number): number => { return x > 0 ? 1 : 0.01; }; // more: https://towardsdatascience.com/derivative-of-the-sigmoid-function-536880cf918e - private activationFunctionSigmoid = (x: number): number => { + private static activationFunctionSigmoid = (x: number): number => { return 1 / (1 + Math.pow(Math.E, -x)); }; - private activationFunctionSigmoidPrime = (x: number): number => { + private static activationFunctionSigmoidPrime = (x: number): number => { return ( - this.activationFunctionSigmoid(x) * - (1 - this.activationFunctionSigmoid(x)) + SharedFunctions.activationFunctionSigmoid(x) * + (1 - SharedFunctions.activationFunctionSigmoid(x)) ); }; - private activationFunctionIdentity = (x: number): number => { + private static activationFunctionIdentity = (x: number): number => { return x; }; // eslint-disable-next-line @typescript-eslint/no-unused-vars, no-unused-vars - private activationFunctionIdentityPrime = (_x: number): number => { + private static activationFunctionIdentityPrime = (_x: number): number => { return 1; }; } - -const sharedFunctions = new SharedFunctions(); -export { sharedFunctions as SharedFunctions }; diff --git a/src/neuron/utilities/StringFunctions.ts b/src/neuron/utilities/StringFunctions.ts index 0de9232..13e71d8 100644 --- a/src/neuron/utilities/StringFunctions.ts +++ b/src/neuron/utilities/StringFunctions.ts @@ -1,8 +1,10 @@ +import { Network } from '..'; + /** * Useful string functions */ export class StringFunctions { - public static fnz = (num: number, fixedVals = 3): string => { + public static fnz = (num: number, fixedVals = 4): string => { if (num === 0) { return num.toString(); } @@ -15,4 +17,8 @@ export class StringFunctions { 1 - Math.floor(Math.log(Math.abs(num % 1)) / Math.log(10)) ); }; + + public static log = (logLine: string, ...args: unknown[]): void => { + console.log(`${Network.currentStep}> ${logLine}`, ...args); + }; } diff --git a/yarn.lock b/yarn.lock index 9a3bfcc..75ccb25 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3,23 +3,23 @@ "@babel/code-frame@^7.0.0": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.8.3.tgz#33e25903d7481181534e12ec0a25f16b6fcf419e" - integrity sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g== + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.10.4.tgz#168da1a36e90da68ae8d49c0f1b48c7c6249213a" + integrity sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg== dependencies: - "@babel/highlight" "^7.8.3" + "@babel/highlight" "^7.10.4" -"@babel/helper-validator-identifier@^7.9.0": - version "7.9.5" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.9.5.tgz#90977a8e6fbf6b431a7dc31752eee233bf052d80" - integrity sha512-/8arLKUFq882w4tWGj9JYzRpAlZgiWUJ+dtteNTDqrRBz9Iguck9Rn3ykuBDoUwh2TO4tSAJlrxDUOXWklJe4g== +"@babel/helper-validator-identifier@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz#a78c7a7251e01f616512d31b10adcf52ada5e0d2" + integrity sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw== -"@babel/highlight@^7.8.3": - version "7.9.0" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.9.0.tgz#4e9b45ccb82b79607271b2979ad82c7b68163079" - integrity sha512-lJZPilxX7Op3Nv/2cvFdnlepPXDxi29wxteT57Q965oc5R9v86ztx0jfxVrTcBk8C2kcPkkDa2Z4T3ZsPPVWsQ== +"@babel/highlight@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.10.4.tgz#7d1bdfd65753538fabe6c38596cdb76d9ac60143" + integrity sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA== dependencies: - "@babel/helper-validator-identifier" "^7.9.0" + "@babel/helper-validator-identifier" "^7.10.4" chalk "^2.0.0" js-tokens "^4.0.0" @@ -34,14 +34,19 @@ integrity sha512-OCutwjDZ4aFS6PB1UZ988C4YgwlBHJd6wCeQqaLdmadZ/7e+w79+hbMUFC1QXDNCmdyoRfAFdm0RypzwR+Qpag== "@types/json-schema@^7.0.3": - version "7.0.4" - resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.4.tgz#38fd73ddfd9b55abb1e1b2ed578cb55bd7b7d339" - integrity sha512-8+KAKzEvSUdeo+kmqnKrqgeE+LcA0tjYWFY7RPProVYwnqDjukzO+3b6dLD56rYX5TdWejnEOLJYOIeh4CXKuA== + version "7.0.5" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.5.tgz#dcce4430e64b443ba8945f0290fb564ad5bac6dd" + integrity sha512-7+2BITlgjgDhH0vvwZU/HZJVyk+2XUlvxXe8dFMedNX/aMkaOq++rMAFXc0tM7ij15QaWlbdQASBR9dihi+bDQ== + +"@types/json5@^0.0.29": + version "0.0.29" + resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" + integrity sha1-7ihweulOEdK4J7y+UnC86n8+ce4= "@types/node@^11.11.3": - version "11.15.14" - resolved "https://registry.yarnpkg.com/@types/node/-/node-11.15.14.tgz#0b837e6d31cde5a2eb7c8f66f506d46f062f1838" - integrity sha512-5R/ADTMiAxv+4GAwzsupaUO51T1e0UYdPc8StO/t6a+Fv1o2sbY9MWgmP2QxtKGwge8HqTjq058L360ai5RDug== + version "11.15.20" + resolved "https://registry.yarnpkg.com/@types/node/-/node-11.15.20.tgz#dacd63b282c1a0d40fa0ad216b7ca128afded614" + integrity sha512-DY2QwdrBqNlsxdMehwzUtSsWHgYYPLVCAuXvOcu3wkzYmchbRunQ7OEZFOrmFoBLfA1ysz2Ypr6vtNP9WQkUaQ== "@typescript-eslint/eslint-plugin-tslint@^2.30.0": version "2.34.0" @@ -149,14 +154,14 @@ acorn@^6.0.7: integrity sha512-ZVA9k326Nwrj3Cj9jlh3wGFutC2ZornPNARZwsNYqQYgN0EsV2d53w5RN/co65Ohn4sUAUtb1rSUAOD6XN9idA== acorn@^7.1.1: - version "7.2.0" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.2.0.tgz#17ea7e40d7c8640ff54a694c889c26f31704effe" - integrity sha512-apwXVmYVpQ34m/i71vrApRrRKCWQnZZF1+npOD0WV5xZFfwWOmKGQ2RWlfdy9vWITsenisM8M0Qeq8agcFHNiQ== + version "7.4.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.0.tgz#e1ad486e6c54501634c6c397c5c121daa383607c" + integrity sha512-+G7P8jJmCHr+S+cLfQxygbWhXy+8YTVGzAkpEbcLo2mLoL7tij/VG41QSHACSf5QgYRhMZYHuNc6drJaO0Da+w== ajv@^6.10.0, ajv@^6.10.2, ajv@^6.9.1: - version "6.12.2" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.2.tgz#c629c5eced17baf314437918d2da88c99d5958cd" - integrity sha512-k+V+hzjm5q/Mr8ef/1Y9goCmlsK4I6Sm74teeyGvFk1XrOsbsKLjEdrvny42CZ+a8sXbk8KWpY/bDwS+FLL2UQ== + version "6.12.4" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.4.tgz#0614facc4522127fa713445c6bfd3ebd376e2234" + integrity sha512-eienB2c9qVQs2KWexhkrdMLVDoIQCz5KSeLxwg9Lzk4DOfBtIK9PQwwufcsn1jjGuf9WZmqPMbGxOzfcuphJCQ== dependencies: fast-deep-equal "^3.1.1" fast-json-stable-stringify "^2.0.0" @@ -227,7 +232,7 @@ argparse@^1.0.7: dependencies: sprintf-js "~1.0.2" -array-includes@^3.0.3: +array-includes@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.1.tgz#cdd67e6852bdf9c1215460786732255ed2459348" integrity sha512-c2VXaCHl7zPsvpkFsw4nxvFie4fh1ur9bpcgsVkIjqn0H/Xwdg+7fv3n2r/isyS8EBj5b06M9kHyZuIr4El6WQ== @@ -236,7 +241,7 @@ array-includes@^3.0.3: es-abstract "^1.17.0" is-string "^1.0.5" -array.prototype.flat@^1.2.1: +array.prototype.flat@^1.2.3: version "1.2.3" resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.2.3.tgz#0de82b426b0318dbfdb940089e38b043d37f6c7b" integrity sha512-gBlRZV0VSmfPIeWfuuy56XZMvbVfbEUnOXUvt3F/eUUUSyzlgLxhEX4YAEpxNAogRGehPSnfXyPtYyKAhkzQhQ== @@ -292,10 +297,10 @@ chalk@^2.0.0, chalk@^2.1.0, chalk@^2.4.2: escape-string-regexp "^1.0.5" supports-color "^5.3.0" -chalk@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-3.0.0.tgz#3f73c2bf526591f574cc492c51e2456349f844e4" - integrity sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg== +chalk@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.0.tgz#4e14870a618d9e2edd97dd8345fd9d9dc315646a" + integrity sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A== dependencies: ansi-styles "^4.1.0" supports-color "^7.1.0" @@ -324,6 +329,11 @@ cli-width@^2.0.0: resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.1.tgz#b0433d0b4e9c847ef18868a4ef16fd5fc8271c48" integrity sha512-GRMWDxpOB6Dgk2E5Uo+3eEBvtOOlimMmpbFiKuLFnQzYDavtLFY3K5ona41jgN/WdRZtG7utuVSVTL4HbZHGkw== +cli-width@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-3.0.0.tgz#a2f48437a2caa9a22436e794bf071ec9e61cedf6" + integrity sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw== + color-convert@^1.9.0: version "1.9.3" resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" @@ -349,9 +359,9 @@ color-name@~1.1.4: integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== comment-parser@^0.7.4: - version "0.7.4" - resolved "https://registry.yarnpkg.com/comment-parser/-/comment-parser-0.7.4.tgz#f5eb83cbae323cae6533c057f41d52692361c83a" - integrity sha512-Nnl77/mt6sj1BiYSVMeMWzvD0183F2MFOJyFRmZHimUVDYS9J40AvXpiFA7RpU5pQH+HkvYc0dnsHpwW2xmbyQ== + version "0.7.6" + resolved "https://registry.yarnpkg.com/comment-parser/-/comment-parser-0.7.6.tgz#0e743a53c8e646c899a1323db31f6cd337b10f12" + integrity sha512-GKNxVA7/iuTnAqGADlTWX4tkhzxZKXp5fLJqKTlQLHkE65XDUKutZ3BHaJC5IGcper2tT3QRD1xr4o3jNpgXXg== common-tags@^1.4.0: version "1.8.0" @@ -460,21 +470,21 @@ error-ex@^1.2.0: is-arrayish "^0.2.1" es-abstract@^1.17.0, es-abstract@^1.17.0-next.1, es-abstract@^1.17.5: - version "1.17.5" - resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.17.5.tgz#d8c9d1d66c8981fb9200e2251d799eee92774ae9" - integrity sha512-BR9auzDbySxOcfog0tLECW8l28eRGpDpU3Dm3Hp4q/N+VtLTmyj4EUN088XZWQDW/hzj6sYRDXeOFsaAODKvpg== + version "1.17.6" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.17.6.tgz#9142071707857b2cacc7b89ecb670316c3e2d52a" + integrity sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw== dependencies: es-to-primitive "^1.2.1" function-bind "^1.1.1" has "^1.0.3" has-symbols "^1.0.1" - is-callable "^1.1.5" - is-regex "^1.0.5" + is-callable "^1.2.0" + is-regex "^1.1.0" object-inspect "^1.7.0" object-keys "^1.1.1" object.assign "^4.1.0" - string.prototype.trimleft "^2.1.1" - string.prototype.trimright "^2.1.1" + string.prototype.trimend "^1.0.1" + string.prototype.trimstart "^1.0.1" es-to-primitive@^1.2.1: version "1.2.1" @@ -497,15 +507,15 @@ eslint-config-prettier@^6.11.0: dependencies: get-stdin "^6.0.0" -eslint-import-resolver-node@^0.3.2: - version "0.3.3" - resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.3.tgz#dbaa52b6b2816b50bc6711af75422de808e98404" - integrity sha512-b8crLDo0M5RSe5YG8Pu2DYBj71tSB6OvXkfzwbJU2w7y8P4/yo0MyF8jU26IEuEuHF2K5/gcAJE3LhQGqBBbVg== +eslint-import-resolver-node@^0.3.3: + version "0.3.4" + resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.4.tgz#85ffa81942c25012d8231096ddf679c03042c717" + integrity sha512-ogtf+5AB/O+nM6DIeBUNr2fuT7ot9Qg/1harBfBtaP13ekEWFQEEMP94BCB7zaNW3gyY+8SHYF00rnqYwXKWOA== dependencies: debug "^2.6.9" resolve "^1.13.1" -eslint-module-utils@^2.4.1: +eslint-module-utils@^2.6.0: version "2.6.0" resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.6.0.tgz#579ebd094f56af7797d19c9866c9c9486629bfa6" integrity sha512-6j9xxegbqe8/kZY8cYpcp0xhbK0EgJlg3g9mib3/miLaExuuwc3n5UEfSnU6hWMbT0FAYVvDbL9RrRgpUeQIvA== @@ -514,22 +524,23 @@ eslint-module-utils@^2.4.1: pkg-dir "^2.0.0" eslint-plugin-import@^2.20.2: - version "2.20.2" - resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.20.2.tgz#91fc3807ce08be4837141272c8b99073906e588d" - integrity sha512-FObidqpXrR8OnCh4iNsxy+WACztJLXAHBO5hK79T1Hc77PgQZkyDGA5Ag9xAvRpglvLNxhH/zSmZ70/pZ31dHg== + version "2.22.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.22.0.tgz#92f7736fe1fde3e2de77623c838dd992ff5ffb7e" + integrity sha512-66Fpf1Ln6aIS5Gr/55ts19eUuoDhAbZgnr6UxK5hbDx6l/QgQgx61AePq+BV4PP2uXQFClgMVzep5zZ94qqsxg== dependencies: - array-includes "^3.0.3" - array.prototype.flat "^1.2.1" + array-includes "^3.1.1" + array.prototype.flat "^1.2.3" contains-path "^0.1.0" debug "^2.6.9" doctrine "1.5.0" - eslint-import-resolver-node "^0.3.2" - eslint-module-utils "^2.4.1" + eslint-import-resolver-node "^0.3.3" + eslint-module-utils "^2.6.0" has "^1.0.3" minimatch "^3.0.4" - object.values "^1.1.0" + object.values "^1.1.1" read-pkg-up "^2.0.0" - resolve "^1.12.0" + resolve "^1.17.0" + tsconfig-paths "^3.9.0" eslint-plugin-jsdoc@^24.0.0: version "24.0.6" @@ -545,9 +556,9 @@ eslint-plugin-jsdoc@^24.0.0: spdx-expression-parse "^3.0.0" eslint-plugin-prefer-arrow@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/eslint-plugin-prefer-arrow/-/eslint-plugin-prefer-arrow-1.2.1.tgz#9e2943cdae4476e41f94f50dd7a250f267db6865" - integrity sha512-CPAvdTGG0YbFAJrUKdRBrOJ0X1I7jTtF5VIM4m2Bw1/A2jrhfUeUAcPy4pAEB5DNaUuDqc59f3pKTeiVeamS1A== + version "1.2.2" + resolved "https://registry.yarnpkg.com/eslint-plugin-prefer-arrow/-/eslint-plugin-prefer-arrow-1.2.2.tgz#0c6d25a6b94cb3e0110a23d129760af5860edb6e" + integrity sha512-C8YMhL+r8RMeMdYAw/rQtE6xNdMulj+zGWud/qIGnlmomiPRaLDGLMeskZ3alN6uMBojmooRimtdrXebLN4svQ== eslint-scope@^3.7.1: version "3.7.3" @@ -566,9 +577,9 @@ eslint-scope@^4.0.0, eslint-scope@^4.0.3: estraverse "^4.1.1" eslint-scope@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.0.0.tgz#e87c8887c73e8d1ec84f1ca591645c358bfc8fb9" - integrity sha512-oYrhJW7S0bxAFDvWqzvMPRm6pcgcnWc4QnofCAqRTRfQC0JcwenzGglTtsLyIuuWFfkqDG9vz67cnttSd53djw== + version "5.1.0" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.0.tgz#d0f971dfe59c69e0cada684b23d49dbf82600ce5" + integrity sha512-iiGRvtxWqgtx5m8EyQUJihBloE4EnYeGE/bz1wSPwJE6tZuJUtHlhqDM4Xj2ukE8Dyy1+HCZ4hE0fzIVMzb58w== dependencies: esrecurse "^4.1.0" estraverse "^4.1.1" @@ -581,16 +592,16 @@ eslint-utils@^1.3.1, eslint-utils@^1.4.3: eslint-visitor-keys "^1.1.0" eslint-utils@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-2.0.0.tgz#7be1cc70f27a72a76cd14aa698bcabed6890e1cd" - integrity sha512-0HCPuJv+7Wv1bACm8y5/ECVfYdfsAm9xmVb7saeFlxjPYALefjhbYoCkBjPdPzGH8wWyTpAez82Fh3VKYEZ8OA== + version "2.1.0" + resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-2.1.0.tgz#d2de5e03424e707dc10c74068ddedae708741b27" + integrity sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg== dependencies: eslint-visitor-keys "^1.1.0" eslint-visitor-keys@^1.0.0, eslint-visitor-keys@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.1.0.tgz#e2a82cea84ff246ad6fb57f9bde5b46621459ec2" - integrity sha512-8y9YjtM1JBJU/A9Kc+SbaOV4y29sSWckBwMHa+FGtVj5gN/sbnKDf6xJUl+8g7FAij9LVaP8C24DUiH/f/2Z9A== + version "1.3.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz#30ebd1ef7c2fdff01c3a4f151044af25fab0523e" + integrity sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ== eslint@^5.0.0: version "5.16.0" @@ -728,9 +739,9 @@ estraverse@^4.1.0, estraverse@^4.1.1: integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== estraverse@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.1.0.tgz#374309d39fd935ae500e7b92e8a6b4c720e59642" - integrity sha512-FyohXK+R0vE+y1nHLoBM7ZTyqRpqAlhdZHCWIWEviFLiGB8b04H6bQs8G+XTthacvT8VuwvteiP7RJSxMs8UEw== + version "5.2.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.2.0.tgz#307df42547e6cc7324d3cf03c155d5cdb8c53880" + integrity sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ== esutils@^2.0.2: version "2.0.3" @@ -747,9 +758,9 @@ external-editor@^3.0.3: tmp "^0.0.33" fast-deep-equal@^3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz#545145077c501491e33b15ec408c294376e94ae4" - integrity sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA== + version "3.1.3" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" + integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== fast-json-stable-stringify@^2.0.0: version "2.1.0" @@ -956,20 +967,20 @@ inquirer@^6.2.2: through "^2.3.6" inquirer@^7.0.0: - version "7.1.0" - resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-7.1.0.tgz#1298a01859883e17c7264b82870ae1034f92dd29" - integrity sha512-5fJMWEmikSYu0nv/flMc475MhGbB7TSPd/2IpFV4I4rMklboCH2rQjYY5kKiYGHqUF9gvaambupcJFFG9dvReg== + version "7.3.3" + resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-7.3.3.tgz#04d176b2af04afc157a83fd7c100e98ee0aad003" + integrity sha512-JG3eIAj5V9CwcGvuOmoo6LB9kbAYT8HXffUl6memuszlwDC/qvFAJw49XJ5NROSFNPxp3iQg1GqkFhaY/CR0IA== dependencies: ansi-escapes "^4.2.1" - chalk "^3.0.0" + chalk "^4.1.0" cli-cursor "^3.1.0" - cli-width "^2.0.0" + cli-width "^3.0.0" external-editor "^3.0.3" figures "^3.0.0" - lodash "^4.17.15" + lodash "^4.17.19" mute-stream "0.0.8" run-async "^2.4.0" - rxjs "^6.5.3" + rxjs "^6.6.0" string-width "^4.1.0" strip-ansi "^6.0.0" through "^2.3.6" @@ -979,10 +990,10 @@ is-arrayish@^0.2.1: resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0= -is-callable@^1.1.4, is-callable@^1.1.5: - version "1.1.5" - resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.1.5.tgz#f7e46b596890456db74e7f6e976cb3273d06faab" - integrity sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q== +is-callable@^1.1.4, is-callable@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.0.tgz#83336560b54a38e35e3a2df7afd0454d691468bb" + integrity sha512-pyVD9AaGLxtg6srb2Ng6ynWJqkHU9bEM087AKck0w8QwDarTfNcpIYoU8x8Hv2Icm8u6kFJM18Dag8lyqGkviw== is-date-object@^1.0.1: version "1.0.2" @@ -1011,12 +1022,12 @@ is-glob@^4.0.0, is-glob@^4.0.1: dependencies: is-extglob "^2.1.1" -is-regex@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.0.5.tgz#39d589a358bf18967f726967120b8fc1aed74eae" - integrity sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ== +is-regex@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.1.tgz#c6f98aacc546f6cec5468a07b7b153ab564a57b9" + integrity sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg== dependencies: - has "^1.0.3" + has-symbols "^1.0.1" is-string@^1.0.5: version "1.0.5" @@ -1068,6 +1079,13 @@ json-stable-stringify-without-jsonify@^1.0.1: resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" integrity sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE= +json5@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.1.tgz#779fb0018604fa854eacbf6252180d83543e3dbe" + integrity sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow== + dependencies: + minimist "^1.2.0" + levn@^0.3.0, levn@~0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" @@ -1104,10 +1122,10 @@ lodash.unescape@4.0.1: resolved "https://registry.yarnpkg.com/lodash.unescape/-/lodash.unescape-4.0.1.tgz#bf2249886ce514cda112fae9218cdc065211fc9c" integrity sha1-vyJJiGzlFM2hEvrpIYzcBlIR/Jw= -lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.4: - version "4.17.15" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548" - integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A== +lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.4: + version "4.17.20" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52" + integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA== loglevel-colored-level-prefix@^1.0.0: version "1.0.0" @@ -1118,9 +1136,9 @@ loglevel-colored-level-prefix@^1.0.0: loglevel "^1.4.1" loglevel@^1.4.1: - version "1.6.8" - resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.6.8.tgz#8a25fb75d092230ecd4457270d80b54e28011171" - integrity sha512-bsU7+gc9AJ2SqpzxwU3+1fedl8zAntbtC5XYlt3s2j1hJcn2PsXSmgN8TaLG/J1/2mod4+cE/3vNL70/c1RNCA== + version "1.7.0" + resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.7.0.tgz#728166855a740d59d38db01cf46f042caa041bb0" + integrity sha512-i2sY04nal5jDcagM3FMfG++T69GEEM8CYuOfeOIvmXzOIcwE9a/CJPR0MFM97pYMj/u10lzz7/zd7+qwhrBTqQ== make-error@^1.1.1: version "1.3.6" @@ -1144,7 +1162,7 @@ minimatch@^3.0.4: dependencies: brace-expansion "^1.1.7" -minimist@^1.2.5: +minimist@^1.2.0, minimist@^1.2.5: version "1.2.5" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== @@ -1197,9 +1215,9 @@ normalize-package-data@^2.3.2: validate-npm-package-license "^3.0.1" object-inspect@^1.7.0: - version "1.7.0" - resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.7.0.tgz#f4f6bd181ad77f006b5ece60bd0b6f398ff74a67" - integrity sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw== + version "1.8.0" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.8.0.tgz#df807e5ecf53a609cc6bfe93eac3cc7be5b3a9d0" + integrity sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA== object-keys@^1.0.11, object-keys@^1.0.12, object-keys@^1.1.1: version "1.1.1" @@ -1216,7 +1234,7 @@ object.assign@^4.1.0: has-symbols "^1.0.0" object-keys "^1.0.11" -object.values@^1.1.0: +object.values@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.1.tgz#68a99ecde356b7e9295a3c5e0ce31dc8c953de5e" integrity sha512-WTa54g2K8iu0kmS/us18jEmdv1a4Wi//BZ/DTVYEcH0XhLM5NYdpDHja3gt57VrZLcNAO2WGA+KpWsDBaHt6eA== @@ -1241,9 +1259,9 @@ onetime@^2.0.0: mimic-fn "^1.0.0" onetime@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.0.tgz#fff0f3c91617fe62bb50189636e99ac8a6df7be5" - integrity sha512-5NcSkPHhwTVFIQN+TUqXoS5+dlElHXdpAWu9I0HP20YOtIi+aZ0Ct82jdlILDxjLEAWwvm+qj1m6aEtsDVmm6Q== + version "5.1.2" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" + integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== dependencies: mimic-fn "^2.1.0" @@ -1430,7 +1448,7 @@ resolve-from@^4.0.0: resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== -resolve@^1.10.0, resolve@^1.12.0, resolve@^1.13.1: +resolve@^1.10.0, resolve@^1.13.1, resolve@^1.17.0: version "1.17.0" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.17.0.tgz#b25941b54968231cc2d1bb76a79cb7f2c0bf8444" integrity sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w== @@ -1465,10 +1483,10 @@ run-async@^2.2.0, run-async@^2.4.0: resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455" integrity sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ== -rxjs@^6.4.0, rxjs@^6.5.3: - version "6.5.5" - resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.5.5.tgz#c5c884e3094c8cfee31bf27eb87e54ccfc87f9ec" - integrity sha512-WfQI+1gohdf0Dai/Bbmk5L5ItH5tYqm3ki2c5GdWhKjalzjg93N3avFjVStyZZz+A2Em+ZxKH5bNghw9UeylGQ== +rxjs@^6.4.0, rxjs@^6.6.0: + version "6.6.2" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.2.tgz#8096a7ac03f2cc4fe5860ef6e572810d9e01c0d2" + integrity sha512-BHdBMVoWC2sL26w//BCu3YzKT4s2jip/WhwsGEDmeKYBhKDZeYezVUnHatYB7L85v5xs0BAQmg6BEYJEKxBabg== dependencies: tslib "^1.9.0" @@ -1593,7 +1611,7 @@ string-width@^4.1.0: is-fullwidth-code-point "^3.0.0" strip-ansi "^6.0.0" -string.prototype.trimend@^1.0.0: +string.prototype.trimend@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.1.tgz#85812a6b847ac002270f5808146064c995fb6913" integrity sha512-LRPxFUaTtpqYsTeNKaFOw3R4bxIzWOnbQ837QfBylo8jIxtcbK/A/sMV7Q+OAV/vWo+7s25pOE10KYSjaSO06g== @@ -1601,25 +1619,7 @@ string.prototype.trimend@^1.0.0: define-properties "^1.1.3" es-abstract "^1.17.5" -string.prototype.trimleft@^2.1.1: - version "2.1.2" - resolved "https://registry.yarnpkg.com/string.prototype.trimleft/-/string.prototype.trimleft-2.1.2.tgz#4408aa2e5d6ddd0c9a80739b087fbc067c03b3cc" - integrity sha512-gCA0tza1JBvqr3bfAIFJGqfdRTyPae82+KTnm3coDXkZN9wnuW3HjGgN386D7hfv5CHQYCI022/rJPVlqXyHSw== - dependencies: - define-properties "^1.1.3" - es-abstract "^1.17.5" - string.prototype.trimstart "^1.0.0" - -string.prototype.trimright@^2.1.1: - version "2.1.2" - resolved "https://registry.yarnpkg.com/string.prototype.trimright/-/string.prototype.trimright-2.1.2.tgz#c76f1cef30f21bbad8afeb8db1511496cfb0f2a3" - integrity sha512-ZNRQ7sY3KroTaYjRS6EbNiiHrOkjihL9aQE/8gfQ4DtAC/aEBRHFJa44OmoWxGGqXuJlfKkZW4WcXErGr+9ZFg== - dependencies: - define-properties "^1.1.3" - es-abstract "^1.17.5" - string.prototype.trimend "^1.0.0" - -string.prototype.trimstart@^1.0.0: +string.prototype.trimstart@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz#14af6d9f34b053f7cfc89b72f8f2ee14b9039a54" integrity sha512-XxZn+QpvrBI1FOcg6dIpxUPgWCPuNXvMD72aaRaUQv1eD4e/Qy8i/hFTe0BUmD60p/QA6bh1avmuPTfNjqVWRw== @@ -1666,9 +1666,9 @@ strip-json-comments@^2.0.1: integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= strip-json-comments@^3.0.1: - version "3.1.0" - resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.0.tgz#7638d31422129ecf4457440009fba03f9f9ac180" - integrity sha512-e6/d0eBu7gHtdCqFt0xJr642LdToM5/cN4Qb9DbHjVx1CP5RyeM+zH7pbecEmDv/lBqb0QH+6Uqq75rxFPkM0w== + version "3.1.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" + integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== supports-color@^2.0.0: version "2.0.0" @@ -1683,9 +1683,9 @@ supports-color@^5.3.0: has-flag "^3.0.0" supports-color@^7.1.0: - version "7.1.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.1.0.tgz#68e32591df73e25ad1c4b49108a2ec507962bfd1" - integrity sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g== + version "7.2.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" + integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== dependencies: has-flag "^4.0.0" @@ -1716,10 +1716,10 @@ tmp@^0.0.33: dependencies: os-tmpdir "~1.0.2" -ts-node@^8.10.1: - version "8.10.1" - resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-8.10.1.tgz#77da0366ff8afbe733596361d2df9a60fc9c9bd3" - integrity sha512-bdNz1L4ekHiJul6SHtZWs1ujEKERJnHs4HxN7rjTyyVOFf3HaJ6sLqe6aPG62XTzAB/63pKRh5jTSWL0D7bsvw== +ts-node@^9.0.0: + version "9.0.0" + resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-9.0.0.tgz#e7699d2a110cc8c0d3b831715e417688683460b3" + integrity sha512-/TqB4SnererCDR/vb4S/QvSZvzQMJN8daAslg7MeaiHvD8rDZsSfXmNeNumyZZzMned72Xoq/isQljYSt8Ynfg== dependencies: arg "^4.1.0" diff "^4.0.1" @@ -1727,6 +1727,16 @@ ts-node@^8.10.1: source-map-support "^0.5.17" yn "3.1.1" +tsconfig-paths@^3.9.0: + version "3.9.0" + resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.9.0.tgz#098547a6c4448807e8fcb8eae081064ee9a3c90b" + integrity sha512-dRcuzokWhajtZWkQsDVKbWyY+jgcLC5sqJhg2PSgf4ZkH2aHPvaOY8YWGhmjb68b5qqTfasSsDO9k7RUiEmZAw== + dependencies: + "@types/json5" "^0.0.29" + json5 "^1.0.1" + minimist "^1.2.0" + strip-bom "^3.0.0" + tslib@^1.8.1, tslib@^1.9.0: version "1.13.0" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.13.0.tgz#c881e13cc7015894ed914862d276436fa9a47043" @@ -1756,22 +1766,27 @@ type-fest@^0.8.1: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== -typescript@^3.2.1, typescript@^3.8.3: - version "3.9.3" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.3.tgz#d3ac8883a97c26139e42df5e93eeece33d610b8a" - integrity sha512-D/wqnB2xzNFIcoBG9FG8cXRDjiqSTbG2wd8DMZeQyJlP1vfTkIxH4GKveWaEBYySKIg+USu+E+EDIR47SqnaMQ== +typescript@^3.2.1: + version "3.9.7" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.7.tgz#98d600a5ebdc38f40cb277522f12dc800e9e25fa" + integrity sha512-BLbiRkiBzAwsjut4x/dsibSTB6yWpwT5qWmC2OfuCg3GgVQCSgMs4vEctYPhsaGtd0AeuuHMkjZ2h2WG8MSzRw== + +typescript@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.0.2.tgz#7ea7c88777c723c681e33bf7988be5d008d05ac2" + integrity sha512-e4ERvRV2wb+rRZ/IQeb3jm2VxBsirQLpQhdxplZ2MEzGvDkkMmPglecnNDfSUBivMjP93vRbngYYDQqQ/78bcQ== uri-js@^4.2.2: - version "4.2.2" - resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.2.2.tgz#94c540e1ff772956e2299507c010aea6c8838eb0" - integrity sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ== + version "4.4.0" + resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.0.tgz#aa714261de793e8a82347a7bcc9ce74e86f28602" + integrity sha512-B0yRTzYdUCCn9n+F4+Gh4yIDtMQcaJsmYBDsTSG8g/OejKBodLQ2IHfN3bM7jUsRXndopT7OIXWdYqc1fjmV6g== dependencies: punycode "^2.1.0" v8-compile-cache@^2.0.3: - version "2.1.0" - resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.1.0.tgz#e14de37b31a6d194f5690d67efc4e7f6fc6ab30e" - integrity sha512-usZBT3PW+LOjM25wbqIlZwPeJV+3OSz3M1k1Ws8snlW39dZyYL9lOGC5FgPVHfk0jKmjiDV8Z0mIbVQPiwFs7g== + version "2.1.1" + resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.1.1.tgz#54bc3cdd43317bca91e35dcaf305b1a7237de745" + integrity sha512-8OQ9CL+VWyt3JStj7HX7/ciTL2V3Rl1Wf5OL+SNTm0yK1KvtReVulksyeRnCANHHuUxHlQig+JJDlUhBt1NQDQ== validate-npm-package-license@^3.0.1: version "3.0.4"