Getting started
Callable functions
Preparing data
Using sequentialBuild()
Saving and Loading models
Evaluation metrics
Get started with examples
Getting Started
To get started, you would need to have NodeJS install on your machine. You can install NodeJS here.
Neurex installation
Install Neurex via NPM
npm install neurex
Neurex is a trainable neural network library for NodeJS. Using this library, you will be able to train neural networks in javascript!
Note: As of the library's current capabilities, it is limited to build ANN models only.
Callable functions
Importing Neurex is centralized. You can import it's components through destructure method or direct import. For destructure method, you simply extract the exported functions and classes. While direct import, you need to specify the individual components as properties of that import variable.
For destructure method of importing neurex
:
const {CsvDataHandler, Neurex, split_dataset, RegressionMetrics} = require('neurex');
Direct import of neurex:
const neurex = require('neurex');
But you have to call individual components as properties of the neurex
import variable.
const neurex = require('neurex');
const model = new neurex.Neurex();
const csv = new neurex.CsvDataHandler();
So, it is recommended to use the destructure method for cleaner and readable code.
Neurex provides ready to use functions, classes and their methods for data preprocessing, cleaning, training, and predicting. These are the functions, classes and their methods you can use:
Neurex
- (class) is the main core of the library. Methods are:
configure()
- allows you to configure the networks initialize parameter range (-0.1 to 0.1 or -1 to 1, etc.), learning rate and optimizer to usemodelSummary()
- Shows the model architecturesequentialBuild()
- Allows you to build layers sequentially.build()
- use aftersequentialBuild()
. This is where weights and biases are initialized.saveModel()
- saves the trained parameters in.nrx
file.train()
- starts the training loop.predict()
- produces predictions based on the input data
Layers
- (class) Stacking layers will return the layer's information such as the layer_name, activation_function, layer_size, and etc. Methods are:
inputShape()
- the inputShape() method allows you to get the shape of your input.connectedLayer()
- Allows you to build a layer with number of neurons and the activation function to use in a layer. Stacking more layers will build fully connected layers.
CsvDataHandler
- (class) is a utility tool for that allows you to extract and manipulate data from your .csv dataset. Methods are:
read_csv()
- Opens and reads the provided CSV file and maps its contents into an array of arrays. The first row is treated as column names and stored separately.rowsToInt()
- Converts all elements in every row of the provided data array to numerical values. Ensure that all elements are numeric, otherwise, they will result in `NaN`.getRowElements()
- Selects a range of elements from each row of the provided array.removeColumns()
- Removes specified columns from the dataset and updates the column names.extractColumn()
- Extracts a column as a 1D array and removes that column from the dataset and column names.normalize()
- Normalizes the provided data using the specified method.trimRows()
- Returns rows from row 1 to the specified range and removes the rest.tabularize()
- Displays the provided data in a tabular format, including column names.exportCSV()
- Export the loaded data to CSV.
Interpreter
- (class) allows you to run inference predictions on your applications. You can load your trained model and run predictions. Methods are:
loadSavedModel()
- allows you to load the trained model. The model is typically in a.nrx
file format which contains the learned parameters of the model during training.predict()
- produces predictions based on the input datamodelSummary()
- Shows the model architecture
MinMaxScaler
- (class) Scales input features (array of arrays) to [0, 1] based on feature-wise min/max. Requires fitting on training data first. Methods are:
fit()
- Calculates min and max for each feature from the input data.transform()
- Transforms the input data using the fitted min and max values.inverseTransform()
- Inverse transforms the normalized data back to original scale.
split_dataset()
- (function) Splits a dataset into training and testing sets.
RegressionMetrics()
- (function) Computes evaluation metrics for regression tasks given test features and labels.
ClassificationMetrics()
- (function) Computes evaluation metrics for classification tasks given test features and labels.
OneHotEncoded()
- (function) Converts a column of categorical labels into one-hot encoded vectors.
IntegerLabeling()
- (function) Converts labels that cannot be converted to interger labels (example: words). If your labels already integer-labeled (ex: 0, 1, 2, 3, ...), no need to use this function
BinaryLabeling()
- (function) Converts labels that cannot be converted to binary labels (example: words). If your labels already 0s and 1s, no need to use this function
Preparing data and preprocessing
Preparing data to be use for training can be done using CsvDataHandler
. Using this utility tool, can you import your CSV dataset. To start, you would need to import CsvDataHandler
from Neurex and create an instance of it:
const {CsvDataHandler} = require('neurex');
const csv = new CsvDataHandler();
To load you CSV dataset, use read_csv();
const {CsvDataHandler} = require('neurex');
const csv = new CsvDataHandler();
const dataset = csv.read_csv('awesome-dataset.csv');
To view your loaded dataset, use tabularize(). This is very useful especially if you want to keep track what is the current structure of your dataset along the process.
const {CsvDataHandler} = require('neurex');
const csv = new CsvDataHandler();
const dataset = csv.read_csv('awesome-dataset.csv');
csv.tabularize(dataset);
If your working with numerical data, consider using rowsToInt()
method. This is because when you loaded your CSV dataset, all cell elements on all rows are strings.
You can view the changes by logging the first result of the process and after converting all rows to Numbers.
Ensure that all rows contains elements that can be converted to numerical data, othewise, elements that cannot be converted to int (like words) will be represented as NaN.
const {CsvDataHandler} = require('neurex');
const csv = new CsvDataHandler();
const dataset = csv.read_csv('awesome-dataset.csv');
const formatted_dataset = csv.rowsToInt(dataset);
If you're going to feed your dataset to your model, consider normalizing your dataset first. Your dataset must not contain NaNs and all rows are already formatted to numbers. You can do this using normalize() method. As for now, the only available normalization method is MinMax, which normalize all values between 0 to 1.
const {CsvDataHandler} = require('neurex');
const csv = new CsvDataHandler();
const dataset = csv.read_csv('awesome-dataset.csv');
const formatted_dataset = csv.rowsToInt(dataset);
const normalized_dataset = csv.normalize("MinMax",formatted_dataset);
csv.tabularize(normalized_dataset);
If you want to extract data under the column to be use as your labels or trainY, use extractColumn()
. Note that this will alter the structure of your dataset.
const {CsvDataHandler} = require('neurex');
const csv = new CsvDataHandler();
const dataset = csv.read_csv('awesome-dataset.csv');
const formatted_dataset = csv.rowsToInt(dataset);
const normalized_dataset = csv.normalize("MinMax",formatted_dataset);
const labels = csv.extractColumn("Column_Name", normalized_dataset);
You may have unwanted columns in your current dataset (or columns that contains NaN values) that cannot be used as features, considering dropping them using removeColumns()
method. When removing columns, this will alter the structure of your dataset.
const {CsvDataHandler} = require('neurex');
const csv = new CsvDataHandler();
const dataset = csv.read_csv('awesome-dataset.csv');
const formatted_dataset = csv.rowsToInt(dataset);
const normalized_dataset = csv.normalize("MinMax",formatted_dataset);
const labels = csv.extractColumn("Column_Name", normalized_dataset);
const train_dataset = csv.removeColumns(["column_name1","column_name2", /* other column names */], normalized_dataset);
csv.tabularize(train_dataset);
If you need to get only specific number of features from a row, you can use getRowElements()
. It will get the elements from the first element in the row up to the specified the range. Note that this will alter the structure of your dataset
const {CsvDataHandler} = require('neurex');
const csv = new CsvDataHandler();
const dataset = csv.read_csv('awesome-dataset.csv');
const formatted_dataset = csv.rowsToInt(dataset);
const normalized_dataset = csv.normalize("MinMax",formatted_dataset);
const labels = csv.extractColumn("Column_Name", normalized_dataset);
const train_dataset = csv.removeColumns(["column_name1","column_name2", /* other column names */], normalized_dataset);
const new_train_dataset = csv.getRowElements(5, train_dataset);
csv.tabularize(new_train_dataset); // e.g: from 7 features now return with 5 features only
Using MinMaxScaler
The use of MinMaxScaler
is to normalize and denormalize you data. It is being used by CsvDataHandler
internally in the normalize()
method. You can still do manual normalization on your dataset using the transform()
and to denormalized, you would need to use inverseTransform()
To use MinMaxScaler
, you need to import it from neurex
module.
const {MinMaxScaler} = require('neurex');
const scaler = new MinMaxScaler();
To normalize and denormalize, you must use fit()
first. Here, you can pass your X_train, Y_train, X_test or Y_test to get their minimum and maximum values. Then you can use transform()
and inverseTransform()
. The code below is how to use the MinMaxScaler.
const {MinMaxScaler} = require('neurex');
const scaler = new MinMaxScaler();
scaler.fit(X_train);
const normalized_Xtrain = scaler.transform(X_train);
/** though, not all cases you would need to normalize
* Y_train.
* example: doing binary or multiclass-classification task
*/
scaler.fit(labels);
const normalize_Ytrain = scaler.transform(labels);
/**
* Works great if you're doing regression tasks
*/
scaler.fit(Y_test);
const denormalize = scaler.inverseTransform(predictions);
Using split_dataset
Using this function, you can split your training and testing sets to evaluate your model's performance
const {split_dataset} = require('neurex');
const {X_train, Y_train, X_test, Y_test} = split_dataset(new_train_dataset, labels, 0.2);
Using sequentialBuild()
The sequentialBuild()
method in Neurex is a core function for defining the architecture of a neural network.
It allows you to stack different types of layers, such as input and connected layers, in a specific order to create the network's structure.
This method sets up the layer details, including the number of neurons and the activation functions for each layer, but it does not initialize
the weights and biases. This separation of building the architecture from initializing the parameters is by design, with the build()
method
being responsible for the latter. A key aspect of sequentialBuild() is its ability to extract the input size from the first layer you define,
which is crucial for setting up the subsequent layers correctly.
Setting it up
Using the sequentialBuild() is simple. Think of it as the Keras' Sequential() but a simplified version and it's the interface for stacking layers sequentially.
To start, you would need first to import Neurex
and Layers
classes and create instances for the two.
const {Neurex, Layers} = require('neurex');
const model = new Neurex();
const layer = new Layers();
Then, we call sequentialBuild()
from Neurex. You can add layers from the Layers
class to the array
const {Neurex, Layers} = require('neurex');
const model = new Neurex();
const layer = new Layers();
model.sequentialBuild([
layer.inputShape({features: 2}),
layer.connectedLayer("relu", 3),
layer.connectedLayer("relu", 3),
layer.connectedLayer("softmax", 2),
]);
The sequentialBuild() will take the layer's definitions and other properties and register them internally. And after
building your layers, use build()
method. This is where your network will initialize random trainable
parameters like weights and biases.
const {Neurex, Layers} = require('neurex');
const model = new Neurex();
const layer = new Layers();
model.sequentialBuild([
layer.inputShape({features: 2}),
layer.connectedLayer("relu", 3),
layer.connectedLayer("relu", 3),
layer.connectedLayer("softmax", 2),
]);
model.build();
After which, you can now train your model by calling train()
method.
Note: You cannot initiate training, without building and you cannot use build()
if there are no layers passed in the sequentialBuild()
array. Using layer.inputShape()
doesn't count.
const {Neurex, Layers} = require('neurex');
const model = new Neurex();
const layer = new Layers();
model.sequentialBuild([
layer.inputShape({features: 2}),
layer.connectedLayer("relu", 3),
layer.connectedLayer("relu", 3),
layer.connectedLayer("softmax", 2),
]);
model.build();
model.train(X_train, Y_train, 'categorical_cross_entropy', 1000, 12);
Saving and Loading models
You can save your model after training using saveModel()
method. This will save your layer's definitions and properties,
as well as it's trained parameters, which are the weights and biases.
Note: .nrx
models are exclusive only to Neurex.
const {Neurex, Layers} = require('neurex');
const model = new Neurex();
const layer = new Layers();
model.sequentialBuild([
layer.inputShape({features: 2}),
layer.connectedLayer("relu", 3),
layer.connectedLayer("relu", 3),
layer.connectedLayer("softmax", 2),
]);
model.build();
model.train(X_train, Y_train, 'categorical_cross_entropy', 1000, 12);
model.saveModel('ANN');
After saving your model, you can load it for inference.
You can load your trained models using the Interpreter
. This will allow you to make predictions on new unseen data on your backend applications.
Import the Interpreter
from neurex
and create an instance of it.
const {Interpreter} = require('neurex');
const interpreter = new Interpreter();
Use loadSavedModel()
to load your trained model. Ensure that the model and the script that uses the Neurex module is in the same root project directory. You can akso view your model's architecture by using the modelSummary()
.
You can also load trained models that are in .nrx
format which is the only supported file format when loading models in Neurex. nrx
model files are files created when saving and exporting model after training using Neurex.
const {Interpreter} = require('neurex');
const interpreter = new Interpreter();
interpreter.loadSavedModel('ANN.nrx');
interpreter.modelSummary();
And finally, run predictions using predict()
.
const {Interpreter} = require('neurex');
const interpreter = new Interpreter();
interpreter.loadSavedModel('ANN.nrx');
const predictions = interpreter.predict(your_data_here);
/* your application's logic how wll you make the use of your data */
Evaluation metrics
Neurex has evaluation metrics you can use to evaluate your model's performance depends on the tasks your model is trained on.
First we have RegressionMetrics
for regression tasks and ClassificationMetrics
for classification task
To use RegressionMetrics
, import it from neurex
:
const {RegressionMetrics} = require('neurex');
Then simply call RegressionMetrics()
and pass the predictions produced by your model and the actuals
const {RegressionMetrics} = require('neurex');
RegressionMetrics(preds, acts);
It will display the output somewhat like this:
Output:
Predicted: [ 0.20411132470278437 ] | Actual: [ 0.24951892238614495 ]
Predicted: [ 0.7559041099891297 ] | Actual: [ 0.7498396407953817 ]
Predicted: [ 0.23807888917102038 ] | Actual: [ 0.2783835792174471 ]
Predicted: [ 0.3060140181074933 ] | Actual: [ 0.3361128928800513 ]
Predicted: [ 0.22675636768160834 ] | Actual: [ 0.2687620269403464 ]
Predicted: [ 0.10163621068193374 ] | Actual: [ 0.05067350865939705 ]
Predicted: [ 0.11974644487530517 ] | Actual: [ 0.09557408595253368 ]
Predicted: [ 0.192788803213372 ] | Actual: [ 0.23989737010904427 ]
Predicted: [ 0.4664129798707005 ] | Actual: [ 0.4804361770365619 ]
Predicted: [ 0.1394012369108757 ] | Actual: [ 0.1597177677998717 ]
Predicted: [ 0.29469149661808136 ] | Actual: [ 0.3264913406029506 ]
Predicted: [ 0.4250571041394965 ] | Actual: [ 0.44194996792815905 ]
Predicted: [ 0.10665654167355221 ] | Actual: [ 0.06350224502886466 ]
Predicted: [ 0.15203553869301223 ] | Actual: [ 0.19820397690827454 ]
Predicted: [ 0.10576763596022898 ] | Actual: [ 0.01860166773572803 ]
Predicted: [ 0.15201716139758148 ] | Actual: [ 0.21103271327774215 ]
Predicted: [ 0.13181993433755224 ] | Actual: [ 0.1340602950609365 ]
Predicted: [ 0.07440597928632409 ] | Actual: [ 0 ]
Predicted: [ 0.09559946595081015 ] | Actual: [ 0.03143040410519564 ]
Predicted: [ 0.2607239321498447 ] | Actual: [ 0.29762668377164847 ]
Predicted: [ 0.09150986110250392 ] | Actual: [ 0.012187299550994226 ]
======= Evaluation Metrics =======
MSE: 0.0021946485498809463
MAE: 0.04103344402940233
r2: 0.932582919965781
rMSE: 0.04684707621486048
Pretty cool, right? Those are some of the predictions made by my trained model which outputs continuous numerical values based on the given inputs. Notice that the output size is 1 (each rows has only one element) is because, my model is trained with one output size (one output neuron) in output layer. If you trained your model to do multi-output regression, the result will looks like this:
Output:
Predicted: [ 1.0090798753393537, 1.0101907423961312 ] | Actual: [ 1, 1 ]
Predicted: [ -0.00023412077778860695, -0.0000052853467176622315 ] | Actual: [ 0, 0 ]
======= Evaluation Metrics =======
MSE: 0.000046587551808997814
MAE: 0.004877505964997789
r2: 0.999813649792764
rMSE: 0.00965272519126053
Just ensure your output size also has the same number of target values, like in the example I trained a model that outputs 2 two values because there are two target values
As for classification tasks, we have ClassificationMetrics
. You would also need to import it from neurex
:
const {ClassificationMetrics} = require('neurex');
Then to evaluate your trained model for classifcation tasks, you would need to pass the output predictions, actuals, classifcation type and optionally, an array that contains class labels.
const {ClassificationMetrics} = require('neurex');
// passing class labels is optional
// specify the classification type as 'binary' for binary classification, 'categorical'
// if the model is trained on multi-class with one-hot encoded labels and 'sparse_categorical',
// if the model is trained on multi-class with integer-labeled encoding
ClassificationMetrics(pred, acts, 'binary', ["class1, class2"]);
Note: here on Neurex, when you train your model for a classification task (whether binary or multi-class classifcation),
the first occurring class when you preprocess your dataset, especially labels using Binarylabeling()
, and
IntegerLabeling()
functions, they always starts on 0.
For binary classification type, the output should look like this:
Accuracy: 90.00%
Correct: 63
Misclassified: 7
Misclassified Examples:
Predicted: 1 | Actual: 0 | Predicted Class: class2 | Actual Class: class1
Predicted: 0 | Actual: 1 | Predicted Class: class1 | Actual Class: class2
Predicted: 1 | Actual: 0 | Predicted Class: class2 | Actual Class: class1
Predicted: 0 | Actual: 1 | Predicted Class: class1 | Actual Class: class2
Predicted: 0 | Actual: 1 | Predicted Class: class1 | Actual Class: class2
Predicted: 0 | Actual: 1 | Predicted Class: class1 | Actual Class: class2
Predicted: 1 | Actual: 0 | Predicted Class: class2 | Actual Class: class1
======= Classification Report =======
precision recall f1-score support
class1 0.91 0.93 0.92 46
class2 0.87 0.83 0.85 24
accuracy 0.90 70
macro avg 0.89 0.88 0.89 70
weighted avg 0.90 0.90 0.90 70
The report for multi-class classification will still be the same, but there are more classes displayed in the report