Hot questions for Using Neural networks in node.js

Question:

I'm trying to decode the dataset from this source: http://yann.lecun.com/exdb/mnist/

There is a description of the "very simple" IDX file type in the bottom, but I cannot figure it out.

What I'm trying to achieve is something like:

var imagesFileBuffer = fs.readFileSync(__dirname + '/train-images-idx3-ubyte');
var labelFileBuffer  = fs.readFileSync(__dirname + '/train-labels-idx1-ubyte');
var pixelValues      = {};

Do magic

pixelValues are now like:

// {
//   "0": [0,0,200,190,79,0... for all 784 pixels ... ],
//   "4": [0,0,200,190,79,0... for all 784 pixels ... ],

etc for all image entries in the dataset. I've tried to figure out the structure of the binary files, but failed.


Answer:

I realized there would be duplicate keys in my structure of the pixelValues object, so I made an array of objects of it instaed. The following code will create the structure I'm after:

var dataFileBuffer  = fs.readFileSync(__dirname + '/train-images-idx3-ubyte');
var labelFileBuffer = fs.readFileSync(__dirname + '/train-labels-idx1-ubyte');
var pixelValues     = [];

// It would be nice with a checker instead of a hard coded 60000 limit here
for (var image = 0; image <= 59999; image++) { 
    var pixels = [];

    for (var x = 0; x <= 27; x++) {
        for (var y = 0; y <= 27; y++) {
            pixels.push(dataFileBuffer[(image * 28 * 28) + (x + (y * 28)) + 15]);
        }
    }

    var imageData  = {};
    imageData[JSON.stringify(labelFileBuffer[image + 8])] = pixels;

    pixelValues.push(imageData);
}

The structure of pixelValues is now something like this:

[
    {5: [28,0,0,0,0,0,0,0,0,0...]},
    {0: [0,0,0,0,0,0,0,0,0,0...]},
    ...
]

There are 28x28=784 pixel values, all varying from 0 to 255.

To render the pixels, use my for loops like I did above, rendering the first pixel in the upper left corner, then working towards the right.

Question:

Impressed by the possibilities of neural networks, I've decided that before using any library I want to understand how they work. So I wrote a simple training app, which used 3 layer network with 2 neurons each. There was a canvas 400x400. Given the coordinates of x,y of the mouse over the canvas <0;399> it was supposed to give as the result coordinate/400 <0;1> (So for 100,300 it is supposed to give 0.25,0.75).

The training looked reasonable.

But when I switch to the prediction mode the network gives the same result all the time for each training batch. It gives the same results no matter what the input is. Then after more training the output changes, but it’s still the same for each input.

It's written in TypeScript. Instead of pasting the whole web training page I just made the training script so you can see more clearly what's going on.

index.ts
let sigmoid: ActivationFunction = {
    func: (x: number) => (1 / (1 + Math.exp(-x))),
    derivative: (z: number) => {
        return sigmoid.func(z) * (1 - sigmoid.func(z));
    }
};

import Matrix from './matrix';

class NeutralNetwork {
    layers: Array<number>;
    weights: Matrix[];
    biases: Matrix[];
    activation_function: ActivationFunction;
    learning_rate: number;

    constructor(...layers: Array<number>) {
        this.layers = layers;
        this.activation_function = sigmoid;

        //Initialize neural network with random weigths and biases [-1;1]
        this.weights = [];
        for(let i=0; i<this.layers.length - 1; i++){
            this.weights.push(new Matrix(this.layers[i+1], this.layers[i]));
            this.weights[i].randomize();
        }
        this.biases = [];
        for(let i=1; i<this.layers.length; i++){
            this.biases.push(new Matrix(this.layers[i], 1));
            this.biases[i-1].randomize();
        }

        this.setActivationFunction();
        this.setLearningRate();
    }

    feedForward(originalInput: Array<number>): Array<number> {
        if(originalInput.length != this.layers[0]) throw new Error("corrupt input data");

        let input : Matrix = Matrix.createFromArray(originalInput);
        for(let i = 0; i < this.layers.length - 1; i++){
            let output = Matrix.multiply(this.weights[i], input);
            output.add(this.biases[i]);
            output.map(this.activation_function.func);
            input = output;
        }

        return input.toArray();
    }

    train(originalInput: Array<number>, originalTarget: Array<number>) {
        if(originalInput.length != this.layers[0]) throw new Error("corrupt training data");
        if(originalTarget.length != this.layers[this.layers.length - 1]) throw new Error("corrupt training data");

        let outputs : Matrix[] = [];
        let input : Matrix = Matrix.createFromArray(originalInput);
        for(let i = 0; i < this.layers.length - 1; i++){
            let output = Matrix.multiply(this.weights[i], input);
            output.add(this.biases[i]);
            output.map(this.activation_function.func);
            input = output;
            outputs.push(output);
        }

        let target = Matrix.createFromArray(originalTarget);
        let errors = Matrix.subtract(target, outputs[this.layers.length - 2]);


        for(let i = this.layers.length - 2; i>=0; i--){
            let gradients = Matrix.map(outputs[i], this.activation_function.derivative);
            gradients.multiply(errors);
            gradients.multiply(this.learning_rate);

            let outputsOfLayerBeforeTransposed = Matrix.transpose(i > 0 ? outputs[i-1] : Matrix.createFromArray(originalInput));
            let deltas = Matrix.multiply(gradients, outputsOfLayerBeforeTransposed);

            this.weights[i].add(deltas);
            this.biases[i].add(gradients);

            let weightsTransposed = Matrix.transpose(this.weights[i]);
            errors = Matrix.multiply(weightsTransposed, errors);
        }

        return outputs[outputs.length - 1].toArray();

    }



    setActivationFunction(activationFunction = sigmoid) {
        this.activation_function = activationFunction;
    }
    setLearningRate(learning_rate = 0.1) {
        this.learning_rate = learning_rate;
    }
}


interface ActivationFunction {
    func(x: number): number;
    derivative(x: number): number;
}

export = NeutralNetwork;
training.ts
let NN = require('./index');

let n = new NN(2,2,2);

let data = generateTrainingData();
data.forEach(d => n.train(d.i, d.o));

//check how well is it trained
let index = 0
let t = setInterval(()=>{
    let pred = n.feedForward(data[index].i);
    console.log(`PREDICTED - ${pred} EXPECTED = ${data[index].o} COST - ${Math.pow(pred[0]-data[index].o[0],2)+Math.pow(pred[1]-data[index].o[1],2)}`)
    if(index++ == 1000) clearInterval(t);
}, 500);

function generateTrainingData(){
    let data = [];
    for(let i=0;i<1000;i++){
        let x = Math.floor(Math.random() * 400);
        let y = Math.floor(Math.random() * 400);
        data.push({
            i : [x,y],
            o : [x/400, y/400]
        })
    }

    return data;
}
matrix.ts
export default class Matrix {
    rows;
    columns;
    data: Array<Array<number>>;

    constructor(rows, columns) {
        this.rows = rows;
        this.columns = columns;
        this.data = new Array(this.rows).fill().map(() => Array(this.columns).fill(0));
    }

    static map(matrix, f) : Matrix{
        let m = new Matrix(matrix.rows, matrix.columns);
        m.map((v,i,j) => f(matrix.data[i][j], i, j));
        return m;
    }

    map(f) {
        for (let i = 0; i < this.rows; i++) {
            for (let j = 0; j < this.columns; j++) {
                this.data[i][j] = f(this.data[i][j], i, j);
            }
        }
    }

    randomize() {
        this.map(() => Math.random() * 2 - 1);
    }

    add(n) {
        if (n instanceof Matrix) {
            if (this.rows !== n.rows || this.columns !== n.columns) {
                throw new Error('Size of both matrices must match!');
            }
            return this.map((v, i, j) => v + n.data[i][j]);
        } else {
            return this.map(v => v + n);
        }
    }



    static subtract(a, b) : Matrix{
        if (a.rows !== b.rows || a.columns !== b.columns) {
            throw new Error('Size of both matrices must match!');
        }

        let m = new Matrix(a.rows, a.columns);
        m.map((_, i, j) => a.data[i][j] - b.data[i][j]);
        return m;
    }

    static multiply(a, b) {

        if (a.columns !== b.rows) {
            throw new Error('a.columns !== b.rows');
        }

        let m = new Matrix(a.rows, b.columns)
        m.map((_, i, j) => {
            let sum = 0;
            for (let k = 0; k < a.cols; k++) {
                sum += a.data[i][k] * b.data[k][j];
            }
            return sum;
        });

        return m;
    }
    multiply(n) {
        if (n instanceof Matrix) {
            if (this.rows !== n.rows || this.columns !== n.columns) {
                throw new Error('Size of both matrices must match!');
            }
            return this.map((v, i, j) => v * n.data[i][j]);
        } else {
            return this.map(v => v * n);
        }
    }
    toArray() {
        let arr = [];
        for (let i = 0; i < this.rows; i++) {
            for (let j = 0; j < this.columns; j++) {
                arr.push(this.data[i][j]);
            }
        }
        return arr;
    }

    static transpose(matrix) : Matrix {
        let m = new Matrix(matrix.columns, matrix.rows)
        m.map((_, i, j) => matrix.data[j][i]);
        return m;
    }

    static createFromArray(arr): Matrix {
        let m = new Matrix(arr.length, 1);
        m.map((v, i) => arr[i]);
        return m;
    }
}

I'm not really sure what the cause of that. I've been trying to debug this for days now, but I think my lack of experience doesn't let me see the issue here. Thank you so much for all of your help.


Answer:

There is a mistake in Matrix.multiply class method. It should be a.columns rather than a.cols. Because of this, gradients and deltas are not updating properly.

Question:

I'm using Brain.js in a node.js project. Training now takes too long to run every time i run the program to update things so i'm saving the network in a json file, using the intended method mentioned in the Github page. it seems to save properly but when i try to load it it says "node_modules/brain/lib/neuralnetwork.js:341 var size = json.layers.length; " i'm probably doing something wrong as i haven't found this problem on Github or here, any help would be appreciated. not put the entire file as most of it is repeats of code to load in the files. this all works properly not had any errors until loading the file. If more code is required i'll post it

main file

function run(){
    console.log("training network...");
    var trainingNet0 = net0.train([
        {input: pixels0, output: [1]},
        {input: pixels1, output: [1]},
        {input: pixels2, output: [1]},
        {input: pixels3, output: [1]},
        {input: pixels6, output: [0]},
        {input: pixels7, output: [0]},
        {input: pixels8, output: [0]},
        {input: pixels9, output: [0]}],
        {
            errorThresh: 5,  // error threshold to reach
            iterations: 1000,   // maximum training iterations
            log: false,           // console.log() progress periodically
            logPeriod: 10,       // number of iterations between logging
            learningRate: 0.003    // learning rate);
    })

    console.log(net0.run(pixels4))
    console.log(net0.run(pixels5))
    console.log(net0.run(pixels6))
    console.log(net0.run(pixels3))
    saveFile();

}

function saveFile(){
    fs.writeFile("network.json", net0.toJSON(), function(err) {
        if(err)
            return console.log(err);

        console.log("The file was saved!");
        loadFile()
    });
    console.log(net0.toJSON())
}

function loadFile(){    
    fs.readFile('network.json', function (err, data) {
        if (err)
            throw err; 

        console.log(data.layers);
        net0.fromJSON(data);
        console.log("file loaded");
    });
}

the contents of the json file that is being saved too

[object Object]

Answer:

I believe you need to pass a string for the data you want to write into fs.writeFile. So JSON.stringify() the data you pass in.

function saveFile(){
    fs.writeFile("network.json", JSON.stringify(net0.toJSON()), function(err) {
        if(err)
            return console.log(err);

        console.log("The file was saved!");
        loadFile()
    });
}

Don't forget, you're going to have to JSON.parse the data after you fs.readFile()

var obj = JSON.parse(fs.readFileSync('network.json', 'utf8'));

or you can use this node module to save json to files: https://github.com/jprichardson/node-jsonfile

Question:

Hello i'm following a browser brain.js script's example and this is the example code:

<script src="brain-controller.js"></script>
<script>
net = new brain.recurrent.LSTM()

net.train([
  'doe, a deer, a female deer',
  'ray, a drop of golden sun',
  'me, a name I call myself',
])

output = net.run('doe') // ', a deer, a female deer'
console.log(output)
</script>

brain-controller.js is the brain.js script updated, so please, DON'T USE CDNJS SCRIPT!!! It is bad! So this is bad too and doesn't works well. I can't run brainjs script in nodejs it gives me this error:

TypeError: Cannot read property 'LSTM' of undefined
    at Object.<anonymous> (C:\Users\alumno_ciclo\bot1.js:1:46)
[90m    at Module._compile (internal/modules/cjs/loader.js:1121:30)[39m
[90m    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1160:10)[39m
[90m    at Module.load (internal/modules/cjs/loader.js:976:32)[39m
[90m    at Function.Module._load (internal/modules/cjs/loader.js:884:14)[39m
[90m    at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:67:12)[39m
[90m    at internal/main/run_main_module.js:17:47[39m

When I do npm install brain.js it gives me those errors:

MSBUILD : error MSB3428: No se pudo cargar el componente "VCBuild.exe" de Visual C++. Para corregir este problema, 1) i
nstale .NET Framework 2.0 SDK, 2) instale Microsoft Visual Studio 2005 o 3) agregue la ubicación del componente a la ru
ta de acceso del sistema si está instalado en otro lugar.  [C:\Users\usuario\node_modules\gl\build\binding.sln]
gyp ERR! build error
gyp ERR! stack Error: `C:\Windows\Microsoft.NET\Framework\v4.0.30319\msbuild.exe` failed with exit code: 1
gyp ERR! stack     at ChildProcess.onExit (C:\Program Files\nodejs\node_modules\npm\node_modules\node-gyp\lib\build.js:262:23)
gyp ERR! stack     at ChildProcess.emit (events.js:198:13)
gyp ERR! stack     at Process.ChildProcess._handle.onexit (internal/child_process.js:248:12)
gyp ERR! System Windows_NT 10.0.17763
gyp ERR! command "C:\\Program Files\\nodejs\\node.exe" "C:\\Program Files\\nodejs\\node_modules\\npm\\node_modules\\node-gyp\\bin\\node-gyp.js" "rebuild"
gyp ERR! cwd C:\Users\usuario\node_modules\gl
gyp ERR! node -v v10.16.3
gyp ERR! node-gyp -v v3.8.0
gyp ERR! not ok
npm WARN mygamename@0.0.1 No description
npm WARN mygamename@0.0.1 No repository field.
npm WARN mygamename@0.0.1 No license field.

npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! gl@4.4.1 install: `prebuild-install || node-gyp rebuild`
npm ERR! Exit status 1
npm ERR!
npm ERR! Failed at the gl@4.4.1 install script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.

npm ERR! A complete log of this run can be found in:
npm ERR!     C:\Users\usuario\AppData\Roaming\npm-cache\_logs\2020-02-07T14_09_02_546Z-debug.log

What I should do???


Answer:

You must have the library imported incorrectly. You can see here your code works using the CDN. Specify the number of iterations (the default is 20,000 (see here)) and/or error threshold for faster performance.

function test() {
  net = new brain.recurrent.LSTM()

  net.train([
    'doe, a deer, a female deer',
    'ray, a drop of golden sun',
    'me, a name I call myself',
  ], {
    iterations: 1500,
    log: details => console.log(details),
    errorThresh: 0.011
  });

  output = net.run('doe') // ', a deer, a female deer'
  const myOutput = document.querySelector('#myOutput');
  myOutput.innerHTML = output;
  console.log(output)
}

test();
<script src="//unpkg.com/brain.js"></script>
<div id=myOutput></div>

Question:

I Use library brain.js. https://github.com/BrainJS/brain.js I dont understand.

For example I start neural network use "data 1" and get "answer 1". How to do forget this data and start teaching use new "data 2" for new "answer 2"? The first, what i think, it is restart program, but how use javascript code. Or me need ask question in github?


Answer:

Use:

var net1 = new brain.NeuralNetwork();
var res1 = net1.train(arr1);

var net2 = new brain.NeuralNetwork();
var res2 = net2.train(arr2);

may be