Files
Superalgos/Projects/TensorFlow/TS/Bot-Modules/Learning-Bot/Low-Frequency-Learning/TensorFlowModelsManager.js
T

375 lines
19 KiB
JavaScript

/*
* File: TensorFlowModelsManager.js
* TensorFlow Library Model types currently supported by SuperAlgos:
* 1.) Sequential Model
*
* History:
* 7/6/21: Start
* 8/24/21: Pushing as beta
*
* Contents:
* newTensorFlowModelsManager.thisObject,
* Model Manager Initialization ()=>{},
* sequentialModelSetup(),
* compileModel(),
* trainModel(),
* trainSequentialModel(),
* fitArgsConstructor()
*
*/
exports.newTensorFlowModelsManager = function (processIndex, learningSystem) {
const MODULE_NAME = 'Tensorflow Models Manager';
/* ModelsManager Variables/Objects/Dependencies Sections: */
const _tfjsCore = require("@tensorflow/tfjs-core");
var _tensorFlowAPI;
var _learningSystem;
var _tensorFlowModel;
let isTrained;
let modelType;
let thisObject = {
sequentialModelSetup: sequentialModelSetup,
compileModel: compileModel,
trainModel: trainModel,
get api() {return _tensorFlowAPI; },
get model() { return _tensorFlowModel; },
set model(value) {
_tensorFlowModel = value;
},
get isTrained() { return isTrained; },
};
// Model Manager Initialization:
( () => {
if (
learningSystem.machineLearningLibrary.typeOfLearning !== undefined &&
learningSystem.machineLearningLibrary.typeOfLearning.typeOfModel !== undefined &&
learningSystem.machineLearningLibrary.typeOfLearning.typeOfModel.model !== undefined &&
learningSystem.machineLearningLibrary.typeOfLearning.typeOfModel.model.api !== undefined
) {
_learningSystem = learningSystem; // Record locally
isTrained = false;
switch (learningSystem.machineLearningLibrary.typeOfLearning.typeOfModel.model.api.type) {
case 'Layers API': {
_tensorFlowAPI = require("@tensorflow/tfjs-layers")
if (learningSystem.machineLearningLibrary.typeOfLearning.typeOfModel.model.api.layersModel === undefined) { return }
switch (learningSystem.machineLearningLibrary.typeOfLearning.typeOfModel.model.api.layersModel.type) {
case 'Sequential Model': {
modelType = 'Sequential Model';
sequentialModelSetup();
break
}
case 'Functional Model': {
modelType = 'FunctionalModel';
// To be implemented some day...
break
}
}
break
}
case 'Core API': {
_tensorFlowAPI = require("@tensorflow/tfjs-core")
// To be implemented some day...
break
}
}
}
compileModel();
})();
return thisObject;
/* Model specific setup section: */
function sequentialModelSetup() {
let layersModel = _learningSystem.machineLearningLibrary.typeOfLearning.typeOfModel.model.api.layersModel;
/* Load the Model: */
_tensorFlowModel = _tensorFlowAPI.sequential();
/* Construct the layers and add to the model: */
for (let i = 0; i < layersModel.layers.length; i++) {
let layerNode = layersModel.layers[i];
let layerFunction; // The layer function.
let layerArgsObject = {}; // layerFunction's arguments.
let layerObject; // The completed layer.
if (layerNode.typeOfLayer === undefined) { continue }
if (layerNode.typeOfLayer.layer === undefined) { continue }
/* Assign tensorFlowAPI layer type to the layer function: */
switch (layerNode.typeOfLayer.layer.type) {
case 'Dense Layer': {
layerFunction = _tensorFlowAPI.layers.dense
break
}
/*case ' ': {
break
}
case ' ': {
break
}*/
default: {
TS.projects.foundations.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
'[WARN] Layer of type ' + layerNode.typeOfLayer.layer.type + ' not implemented yet.')
continue
}
}
/* Construct layerArgsObject for this layerFunction via collecting the children of the layerNode: */
if (layerNode.typeOfLayer.layer.dimensionalityUnits !== undefined) {
layerArgsObject.units = layerNode.typeOfLayer.layer.dimensionalityUnits.config.value
} else {
layerArgsObject.units = 1
}
if (layerNode.typeOfLayer.layer.activationFunction !== undefined) {
layerArgsObject.activation = layerNode.typeOfLayer.layer.activationFunction.config.value
}
if (layerNode.typeOfLayer.layer.bias !== undefined) {
layerArgsObject.useBias = true
} else {
layerArgsObject.useBias = false
}
if (layerNode.typeOfLayer.layer.kernel !== undefined) {
if (layerNode.typeOfLayer.layer.kernel.kernelInitializer !== undefined) {
layerArgsObject.kernelInitializer = layerNode.typeOfLayer.layer.kernel.kernelInitializer.config.value
}
if (layerNode.typeOfLayer.layer.kernel.kernelConstraint !== undefined) {
layerArgsObject.kernelConstraint = layerNode.typeOfLayer.layer.kernel.kernelConstraint.config.value
}
if (layerNode.typeOfLayer.layer.kernel.kernelRegularizer !== undefined) {
layerArgsObject.kernelRegularizer = layerNode.typeOfLayer.layer.kernel.kernelRegularizer.config.value
}
}
if (layerNode.typeOfLayer.layer.bias !== undefined) {
if (layerNode.typeOfLayer.layer.bias.biasInitializer !== undefined) {
layerArgsObject.biasInitializer = layerNode.typeOfLayer.layer.bias.biasInitializer.config.value
}
if (layerNode.typeOfLayer.layer.bias.biasConstraint !== undefined) {
layerArgsObject.biasConstraint = layerNode.typeOfLayer.layer.bias.biasConstraint.config.value
}
if (layerNode.typeOfLayer.layer.bias.biasRegularizer !== undefined) {
layerArgsObject.biasRegularizer = layerNode.typeOfLayer.layer.bias.biasRegularizer.config.value
}
}
if (layerNode.typeOfLayer.layer.batchSize !== undefined) {
layerArgsObject.batchSize = layerNode.typeOfLayer.layer.batchSize.config.value
}
if (layerNode.typeOfLayer.layer.dtype !== undefined) {
layerArgsObject.dtype = layerNode.typeOfLayer.layer.dtype.config.value
}
if (layerNode.typeOfLayer.layer.trainable !== undefined) {
layerArgsObject.trainable = layerNode.typeOfLayer.layer.trainable.config.value
}
if (layerNode.typeOfLayer.layer.weights !== undefined) {
if (layerNode.typeOfLayer.layer.weights.tensor !== undefined) {
layerArgsObject.weights = layerNode.typeOfLayer.layer.weights.tensor.config.value
}
}
layerArgsObject.name = layerNode.typeOfLayer.layer.config.codeName;
/* Check if this is the input layer: */
if (layersModel.inputLayer.referenceParent.id === layerNode.id) {
/*
* If there is a explicitly defined Input Shape, then we will take it,
* otherwise, we will count the amount of features declared.
*/
if (layersModel.inputLayer.inputShape !== undefined) {
if (layerArgsObject.inputShape = layersModel.inputLayer.inputShape !== undefined) {
if (layerArgsObject.inputShape = layersModel.inputLayer.inputShape.config.value !== undefined) {
layerArgsObject.inputShape = layersModel.inputLayer.inputShape.config.value
}
}
}
if (layerArgsObject.inputShape === undefined) {
layerArgsObject.inputShape = [];
layerArgsObject.inputShape.push(layersModel.inputLayer.inputFeatures.dataFeatures.length);
}
}
/* Call the layerFunction and add layer to the model: */
layerObject = layerFunction(layerArgsObject);
_tensorFlowModel.add(layerObject);
}
}
/* Compile Models Section: */
function compileModel() {
/* optimizerBuilder() : builds and returns either a string or an tf.train.Optimizer:
* TF Docs: optimizer (string|tf.train.Optimizer) An instance of tf.train.Optimizer or a string name for an Optimizer.
*/
let optimizerBuilder = () => {
let optimizerType = _learningSystem.machineLearningLibrary.typeOfLearning.typeOfModel.model.compile.optimizer.optimizerType.type;
let learningRate, momentum, useNesterov, initAccVal, rho, decay, epsilon, beta1, beta2, centered;
switch(optimizerType) {
case 'Optimizer by Name': // For specifying string name as optimizer.
// This ensures acceptable type entered:
return _learningSystem.machineLearningLibrary.typeOfLearning.typeOfModel.model.compile.optimizer.optimizerType.config.value;
// Following Cases for building instance of tf.train.Optimizer:
case 'SGD Instance':
learningRate = _learningSystem.machineLearningLibrary.typeOfLearning.typeOfModel.model.compile.optimizer.optimizerType.config.learningRate;
return _tfjsCore.train.sgd(learningRate);
case 'Momentum Instance':
learningRate = _learningSystem.machineLearningLibrary.typeOfLearning.typeOfModel.model.compile.optimizer.optimizerType.config.learningRate;
momentum = _learningSystem.machineLearningLibrary.typeOfLearning.typeOfModel.model.compile.optimizer.optimizerType.config.momentum;
useNesterov = _learningSystem.machineLearningLibrary.typeOfLearning.typeOfModel.model.compile.optimizer.optimizerType.config.useNesterov;
return _tfjsCore.train.momentum(learningRate, momentum, useNesterov);
case 'Adagrad Instance':
learningRate = _learningSystem.machineLearningLibrary.typeOfLearning.typeOfModel.model.compile.optimizer.optimizerType.config.learningRate;
initAccVal = _learningSystem.machineLearningLibrary.typeOfLearning.typeOfModel.model.compile.optimizer.optimizerType.config.initialAccumulatorValue;
return _tfjsCore.train.adagrad(learningRate, initAccVal);
case 'Adadelta Instance':
learningRate = _learningSystem.machineLearningLibrary.typeOfLearning.typeOfModel.model.compile.optimizer.optimizerType.config.learningRate;
rho = _learningSystem.machineLearningLibrary.typeOfLearning.typeOfModel.model.compile.optimizer.optimizerType.config.rho;
epsilon = _learningSystem.machineLearningLibrary.typeOfLearning.typeOfModel.model.compile.optimizer.optimizerType.config.epsilon;
return _tfjsCore.train.adadelta(learningRate, rho, epsilon);
case 'Adam Instance':
learningRate = _learningSystem.machineLearningLibrary.typeOfLearning.typeOfModel.model.compile.optimizer.optimizerType.config.learningRate;
beta1 = _learningSystem.machineLearningLibrary.typeOfLearning.typeOfModel.model.compile.optimizer.optimizerType.config.beta1;
beta2 = _learningSystem.machineLearningLibrary.typeOfLearning.typeOfModel.model.compile.optimizer.optimizerType.config.beta2;
epsilon = _learningSystem.machineLearningLibrary.typeOfLearning.typeOfModel.model.compile.optimizer.optimizerType.config.epsilon;
return _tfjsCore.train.adam(learningRate, beta1, beta2, epsilon);
case 'Rmsprop Instance':
learningRate = _learningSystem.machineLearningLibrary.typeOfLearning.typeOfModel.model.compile.optimizer.optimizerType.config.learningRate;
decay = _learningSystem.machineLearningLibrary.typeOfLearning.typeOfModel.model.compile.optimizer.optimizerType.config.decay;
momentum = _learningSystem.machineLearningLibrary.typeOfLearning.typeOfModel.model.compile.optimizer.optimizerType.config.momentum;
epsilon = _learningSystem.machineLearningLibrary.typeOfLearning.typeOfModel.model.compile.optimizer.optimizerType.config.epsilon;
centered = _learningSystem.machineLearningLibrary.typeOfLearning.typeOfModel.model.compile.optimizer.optimizerType.config.centered;
return _tfjsCore.train.rmsprop(learningRate, decay, momentum, epsilon, centered);
default:
return false;
}
}
let compileMetrics;
if (_learningSystem.machineLearningLibrary.typeOfLearning.typeOfModel.model.compile.metrics !== undefined &&
_learningSystem.machineLearningLibrary.typeOfLearning.typeOfModel.model.compile.metrics.config.value !== undefined) {
compileMetrics = _learningSystem.machineLearningLibrary.typeOfLearning.typeOfModel.model.compile.metrics.config.value;
}
// Collect, build and process optimizer type:
let optimizerBuild = optimizerBuilder();
let compileArgs = {
optimizer: optimizerBuild,
/* Loss Types: see: @tensorflow -> tf-layers.node.js:
* categoricalCrossentropy, sparseCategoricalCrossentropy, binaryCrossentropy, meanSquaredError ...
*/
loss: _learningSystem.machineLearningLibrary.typeOfLearning.typeOfModel.model.compile.lossFunction.config.value,
metrics: compileMetrics
}
_tensorFlowModel.compile(compileArgs);
}
/* Fit Model Section: */
async function trainModel(mlDataObj) {
switch(modelType) {
case 'Sequential Model':
_tensorFlowModel.summary();
await trainSequentialModel(mlDataObj);
break;
case 'Functional Model':
// To be implemented some day...
TS.projects.foundations.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
'[WARN] Model of type ' + modelType + ' not yet implemented.');
throw new Error('Model Type: >' + modelType + '< not yet implemented');
default: {
TS.projects.foundations.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME,
'[WARN] Model of type ' + modelType + ' not yet implemented.');
throw new Error('Model Type: >' + modelType + '< not yet implemented');
}
}
}
async function trainSequentialModel(mlDataObj) {
// Should we print state of mlDataObj:
let dataReporting = (learningSystem.machineLearningLibrary.typeOfLearning.typeOfModel.model.dataReporting !== undefined &&
learningSystem.machineLearningLibrary.typeOfLearning.typeOfModel.model.dataReporting.config.printDataReports === true) ? true : false;
if (dataReporting) {
mlDataObj.print();
}
let xTensor = mlDataObj.featuresTensor();
let yTensor = mlDataObj.labelsTensor();
let modelFitDatasetArgs = {};
// Should we collect any fitArgs:
if (_learningSystem.machineLearningLibrary.typeOfLearning.typeOfModel.model.fitDataset !== undefined) {
modelFitDatasetArgs = fitArgsConstructor();
if (dataReporting) {
let fitKeys = Object.keys(modelFitDatasetArgs);
let fitVals = Object.values(modelFitDatasetArgs);
console.log("__________________________________________________\nModel Fitting Arguments:\n==================================================");
fitKeys.forEach((e, i) => console.log(i + ": \"" + e + "\"=>[" + fitVals[i] + "]"));
}
}
console.log("__________________________________________________\nTraining " +
_learningSystem.machineLearningLibrary.typeOfLearning.typeOfModel.model.name +
":\n==================================================");
const history = await _tensorFlowModel.fit(xTensor, yTensor, modelFitDatasetArgs);
isTrained = true;
if (dataReporting) {
console.log("\n__________________________________________________\nModel Fitting Report:\n==================================================");
console.log("First Epoch #1:\n\tacc=>" + history.history.acc[0] + "\n\tloss=>" + history.history.loss[0]);
let totalEpochs = history.epoch.length;
console.log("Final Epoch #" + totalEpochs + ":\n\tacc=>" + history.history.acc[totalEpochs - 1] + "\n\tloss=>" + history.history.loss[totalEpochs - 1] + "\n__________________________________________________");
}
}
/* Construct modelFitDatasetArgs by collecting available arguments: */
function fitArgsConstructor() {
let modelFitDatasetArgs = {};
let fitDatasetNode = _learningSystem.machineLearningLibrary.typeOfLearning.typeOfModel.model.fitDataset;
// verbose- Verbosity level:
if (fitDatasetNode.verbose !== undefined) {
if (fitDatasetNode.verbose.config.value !== undefined) {
modelFitDatasetArgs.verbose = fitDatasetNode.verbose.config.value
}
}
// epochs- Integer number of times to iterate over the training data arrays:
if (fitDatasetNode.epochs !== undefined) {
if (fitDatasetNode.epochs.config.value !== undefined) {
modelFitDatasetArgs.epochs = fitDatasetNode.epochs.config.value
}
}
// batchSize- Number of samples per gradient update. If unspecified, it will default to 32 per tf.org:
if (fitDatasetNode.datasetArgs !== undefined) {
if (fitDatasetNode.datasetArgs.batchesPerGradientUpdate !== undefined &&
fitDatasetNode.datasetArgs.batchesPerGradientUpdate.config.value !== undefined) {
modelFitDatasetArgs.batchSize = fitDatasetNode.datasetArgs.batchesPerGradientUpdate.config.value;
}
if (fitDatasetNode.datasetArgs.shuffle !== undefined &&
fitDatasetNode.datasetArgs.shuffle.config.booleanValue !== undefined) {
modelFitDatasetArgs.shuffle = fitDatasetNode.datasetArgs.shuffle.config.booleanValue;
}
// Future Add validation split:
}
// This callback hard coded for now:
modelFitDatasetArgs.callbacks = {
onEpochEnd: (epoch, logs) => console.log("callbacks:\tEpoch:" + epoch + " | Loss=>" + logs.loss)
}
return modelFitDatasetArgs;
}
}