deept.js
Version:
Eine TypeScript/JavaScript Library für ML im Browser
379 lines (378 loc) • 17.2 kB
JavaScript
"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;