UNPKG

deept.js

Version:

Eine TypeScript/JavaScript Library für ML im Browser

379 lines (378 loc) 17.2 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k]; result["default"] = mod; return result; }; Object.defineProperty(exports, "__esModule", { value: true }); var chalk_1 = __importDefault(require("chalk")); var matrix_1 = __importDefault(require("../Matrix/matrix")); var comps = __importStar(require("./neuralnet.lib")); /** * @class Neurales Netzwerk für Machine-Learning. * @constructs NeuralNet: Baut ein leeres Netzwerk auf. * @method addLayers: Layer zum Netzwerk hinzufügen. * @method predict: Feed-Forward Algorythmus. * @method train: Trainiert das Netzwerk. * @method test: Testet das Netzwerk. * @method mutate: Mutate-Funktion für gen. Algorythmen. * @method static log: Loggt ein gegebenes Netzwerk zur Konsole. * @method log: Loggt dieses Netzwerk zur Konsole. * @method emit: Wandelt das Netzwerk in JSON Format um. */ var NeuralNet = /** @class */ (function () { /** * Leeres Neurales Netzwerk aufbauen. * @returns Ein leeres Neurales Netzwerk. */ function NeuralNet() { this.inputNodes = 0; // anzahl der Input-nodes this.hiddenLayerNodes = []; // Array welches die anzahl der nodes für jd. hL speichert this.hiddenLayers = []; // init für das array mit den hiddenLayer objekten this.numHiddenLayers = 0; // anzahl der hiddenLayer this.outputNodes = 0; // anzahl der outputNodes this.outputLayer = new comps.OutputLayer(0, 0); this.learningRate = 0.1; // Preset für die learningRate this.totalError = 0; } /** * Funktion die es für den User einfacher macht sein Network aufzustellen. * Eine Art redirect zu den spezifischeren Funktionen. * @param layerArray Ein Array aus Objekten welche informationen über die Struktur des Netzwerkes * enthalten */ NeuralNet.prototype.addLayers = function (layerArray) { for (var _i = 0, layerArray_1 = layerArray; _i < layerArray_1.length; _i++) { var layer = layerArray_1[_i]; // let layer = layerArray[i]; if (layer.type === 'input') { this.inputNodes = layer.nodes; } else if (layer.type === 'hidden') { comps.addHiddenLayer(layer, this); } else if (layer.type === 'output') { comps.addOutputLayer(layer, this); } else { console.log(chalk_1.default.redBright('Invalid layertype: ' + layer.type)); } } }; /** * Diese Funktion ist ein feed-forward Algorythmus durch das Netzwerk. * @param inputArray Ein Array aus Zahlen mit denen das Netzwerk sein Ergebnis berechnet. * @callback callback Eine Callback-Funktion welche nach dem Durchlauf aufgerufen wird und der das Ergebnis * übergeben wird. * @returns Ein Array mit den Ergebnissen des Netzwerkes. */ NeuralNet.prototype.predict = function (inputArray, callback) { if (inputArray.length !== this.inputNodes) { var errorText = 'Number of given inputs (' + inputArray.length + ') does not' + 'match up with number of input nodes ' + this.inputNodes + ').'; console.log(chalk_1.default.redBright(errorText)); return; } var inputs = matrix_1.default.fromArray(inputArray); var hidden = matrix_1.default.multiply(this.hiddenLayers[0].W_F_T, inputs); hidden = matrix_1.default.add(hidden, this.hiddenLayers[0].biases); hidden.map(comps.sigmoid); for (var i = 1; i < this.hiddenLayers.length; i++) { hidden = matrix_1.default.multiply(this.hiddenLayers[i].W_F_T, hidden); hidden = matrix_1.default.add(hidden, this.hiddenLayers[i].biases); hidden.map(comps.sigmoid); } var output = matrix_1.default.multiply(this.outputLayer.W_F_T, hidden); output = matrix_1.default.add(output, this.outputLayer.biases); output.map(comps.sigmoid); /** * Gibt den Output als Array zurück, z.B: * <- [1, 0] * -> [0.996183321413] */ if (callback) { callback(matrix_1.default.toArray(output)); } else { return matrix_1.default.toArray(output); } }; /** * Ein backwards-propagation Algorythmus der einen Input und ein gewünschtes Ergebnis als Array * nimmt und über eine optionale Anzahl (default 10000) an iterations das Training durchführt. * @param trainingData Array mit Objekten welche als Eigenschaften die Informationen zum Training enthalten. Das * Netzwerk benutzt zufällig eines aus diesem Array. * @param ops Ein Objekt mit Optionen für den Ablauf des trainings. */ NeuralNet.prototype.train = function (trainingData, ops) { if (ops === undefined) { ops = { iterations: 1000000, print: true, test: true }; } if (!ops.iterations) { ops.iterations = 1000000; } if (ops.print === undefined) { ops.print = true; } if (ops.test === undefined) { ops.test = true; } if (ops.print === true) { console.clear(); console.log(chalk_1.default.yellowBright('Training NeuralNet ...')); } for (var x = 0; x < ops.iterations; x++) { var data = comps.pickRandomFromArray(trainingData); if (data.input.length !== this.inputNodes) { var errorText = 'Number of given inputs (' + data.input.length + ') does not' + 'match up with number of input nodes ' + this.inputNodes + ').'; console.log(chalk_1.default.redBright(errorText)); return; } else if (data.target.length !== this.outputNodes) { var errorText = 'Number of given targets (' + data.target.length + ') does not' + 'match up with number of input nodes ' + this.inputNodes + ').'; console.log(chalk_1.default.redBright(errorText)); } var inputs = matrix_1.default.fromArray(data.input); var hidden = matrix_1.default.multiply(this.hiddenLayers[0].W_F_T, inputs); hidden = matrix_1.default.add(hidden, this.hiddenLayers[0].biases); hidden.map(comps.sigmoid); this.hiddenLayers[0].values = hidden; for (var i = 1; i < this.hiddenLayers.length; i++) { hidden = matrix_1.default.multiply(this.hiddenLayers[i].W_F_T, hidden); hidden = matrix_1.default.add(hidden, this.hiddenLayers[i].biases); hidden.map(comps.sigmoid); this.hiddenLayers[i].values = hidden; } var outputs = matrix_1.default.multiply(this.outputLayer.W_F_T, hidden); outputs = matrix_1.default.add(outputs, this.outputLayer.biases); outputs.map(comps.sigmoid); var targets = matrix_1.default.fromArray(data.target); // TRAINING // Generate Output-Error var outputError = matrix_1.default.subtract(targets, outputs); // Gradient for last weights var gradient = matrix_1.default.map(outputs, comps.dsigmoid); gradient.multiply(outputError); gradient.multiply(this.learningRate); var hiddenT = matrix_1.default.transpose(this.hiddenLayers[this.numHiddenLayers - 1].values); var weightsHODeltas = matrix_1.default.multiply(gradient, hiddenT); this.outputLayer.W_F_T.add(weightsHODeltas); this.outputLayer.biases.add(gradient); var wHOT = matrix_1.default.transpose(this.outputLayer.W_F_T); var hiddenErrors = matrix_1.default.multiply(wHOT, outputError); for (var i = this.numHiddenLayers - 1; i >= 0; i--) { var hiddenGradient = matrix_1.default.map(this.hiddenLayers[i].values, comps.dsigmoid); hiddenGradient.multiply(hiddenErrors); hiddenGradient.multiply(this.learningRate); // If not first hidden Layer if (i >= 1) { hiddenT = matrix_1.default.transpose(this.hiddenLayers[i - 1].values); var weightsHHDeltas = matrix_1.default.multiply(hiddenGradient, hiddenT); this.hiddenLayers[i].W_F_T.add(weightsHHDeltas); this.hiddenLayers[i].biases.add(hiddenGradient); } else if (i === 0) { var inputsT = matrix_1.default.transpose(inputs); var weightsIHDeltas = matrix_1.default.multiply(hiddenGradient, inputsT); this.hiddenLayers[i].W_F_T.add(weightsIHDeltas); this.hiddenLayers[i].biases.add(hiddenGradient); } } if (ops.print === true) { for (var i = 0; i < 20; i++) { if (x === i * ops.iterations / 10 && x !== 0) { var percent = i * 10; console.clear(); console.log(chalk_1.default.yellowBright('Training NeuralNet ...')); console.log(percent + "% (" + x + ")"); } } } } if (ops.print === true) { console.clear(); console.log(chalk_1.default.yellowBright('Training NeuralNet ...')); console.log(chalk_1.default.greenBright('Done training.')); } if (ops.test === true) { if (ops.print === true) { console.log(chalk_1.default.yellowBright('Testing ...')); } var testRes = this.test(trainingData, { iterations: ops.iterations / 2, passWhen: 60, print: false }); this.totalError = testRes.val; var state = testRes.stat; if (ops.print === true) { console.log(chalk_1.default.greenBright('Done testing')); if (state === 'FAILED') { console.log('Status: ' + chalk_1.default.redBright(state) + ' ' + this.totalError + '%'); } else { console.log('Status: ' + chalk_1.default.greenBright(state) + ' ' + this.totalError + '%'); } } } }; /** * Diese Methode testet das Netzwerk indem es Richtige und falsche Antworten vergleicht. * @param testingData Array aus Objekten wie die Trainingsdaten. * @param ops Optionen für den Ablauf des Testings. * @returns Den berechneten Prozentsatz der korrekten Antworten. */ NeuralNet.prototype.test = function (testingData, ops) { var correct = 0; if (!ops) { ops = { iterations: 1000000, print: true }; } if (!ops.iterations) { ops.iterations = 1000000; } if (ops.print === undefined) { ops.print = true; } if (ops.print === true) { console.log(chalk_1.default.yellowBright('Testing ...')); } for (var x = 0; x < ops.iterations; x++) { var data = comps.pickRandomFromArray(testingData); var inputs = matrix_1.default.fromArray(data.input); var hidden = matrix_1.default.multiply(this.hiddenLayers[0].W_F_T, inputs); hidden = matrix_1.default.add(hidden, this.hiddenLayers[0].biases); hidden.map(comps.sigmoid); this.hiddenLayers[0].values = hidden; for (var i = 1; i < this.hiddenLayers.length; i++) { hidden = matrix_1.default.multiply(this.hiddenLayers[i].W_F_T, hidden); hidden = matrix_1.default.add(hidden, this.hiddenLayers[i].biases); hidden.map(comps.sigmoid); this.hiddenLayers[i].values = hidden; } var outputs = matrix_1.default.multiply(this.outputLayer.W_F_T, hidden); outputs = matrix_1.default.add(outputs, this.outputLayer.biases); outputs.map(comps.sigmoid); var targets = matrix_1.default.fromArray(data.target); var shouldBeBiggest = { val: 0, index: 0 }; var isBiggest = { val: 0, index: 0 }; var outArr = matrix_1.default.toArray(outputs); var tarArr = matrix_1.default.toArray(targets); for (var f = 0; f < outArr.length; f++) { if (outArr[f] > isBiggest.val) { isBiggest.val = outArr[f]; isBiggest.index = f; } } for (var f = 0; f < tarArr.length; f++) { if (tarArr[f] > shouldBeBiggest.val) { shouldBeBiggest.val = tarArr[f]; shouldBeBiggest.index = f; } } if (isBiggest.index === shouldBeBiggest.index) { correct++; } } var status; var CORRECT_PERCENT = correct / ops.iterations * 100; if (ops.print !== true) { if (ops.passWhen === undefined) { status = 'PASSED'; } else { if (CORRECT_PERCENT >= ops.passWhen) { status = 'PASSED'; } else { status = 'FAILED'; } } } else { if (ops.passWhen === undefined) { console.log(chalk_1.default.greenBright('Done testing'), 'Testing Results: ' + chalk_1.default.blue(CORRECT_PERCENT + "%") + ' correct'); } else { if (CORRECT_PERCENT >= ops.passWhen) { status = 'PASSED'; console.log(chalk_1.default.greenBright('Done testing')); console.log('Status: ' + chalk_1.default.greenBright(status) + ' ' + CORRECT_PERCENT + '%'); } else { status = 'FAILED'; console.log(chalk_1.default.greenBright('Done testing')); console.log('Status: ' + chalk_1.default.redBright(status) + ' ' + CORRECT_PERCENT + '%'); } } } if (status === undefined) { status = 'FAILED'; } return { stat: status, val: CORRECT_PERCENT }; }; /** * Ein genetischer Algorythmus soll eine Population über Generationen mutieren und trainieren. * @param rate Eine Zahl an der die Population mutieren soll. */ NeuralNet.prototype.mutate = function (rate) { console.log(chalk_1.default.yellowBright('Feature not implemented yet')); return; }; /** * Loggt das gegebene Netzwerk in die Konsole. * @param net Neurales Netzwerk welches ausgegeben werden soll. */ NeuralNet.log = function (net) { var numHiddenNodes = 0; for (var i = 0; i < net.numHiddenLayers; i++) { numHiddenNodes += net.hiddenLayerNodes[i]; } var resultText = "\nNeural Network: \n"; resultText += " NODES:\n"; resultText += " Input nodes: " + net.inputNodes + "\n"; resultText += " Hidden nodes: " + numHiddenNodes + "\n"; resultText += " Output nodes: " + net.outputNodes + "\n"; resultText += "\n"; resultText += " HIDDEN LAYERS:\n"; for (var i = 0; i < net.numHiddenLayers; i++) { resultText += " Hidden Layer " + (i + 1) + ":\n"; resultText += " Nodes: " + net.hiddenLayerNodes[i] + "\n"; } resultText += " LEARNING RATE: " + net.learningRate; console.log(resultText); }; /** * Loggt das Netzwerk in die Konsole. */ NeuralNet.prototype.log = function () { NeuralNet.log(this); }; /** * Gibt das Netzwerk als String zurück um es z.B. zu speichern. * @returns Einen String welcher z.B. in JSON Format gespeichert werden kann. */ NeuralNet.prototype.emit = function () { var res = JSON.stringify(this, undefined, 1); return res; }; return NeuralNet; }()); exports.default = NeuralNet;