Code viewer for World: Assignment-2 (clone by Dev...
const len = 784;
const totalData = 1000;

const CAT = 0;
const RAINBOW = 3;
const TRAIN = 1;
const CAR = 4;
const ALARM = 2;
const EFIL = 5;

let catsData;
let trainsData;
let rainbowsData;
let carData;
let effilData;
let alarmData;

let cats = {};
let trains = {};
let rainbows = {};
let car = {};
let effil = {};
let alarm = {};
let nn;

function preload() {
  catsData = loadBytes('uploads/ozad2/cats1000.bin');
  trainsData = loadBytes('uploads/ozad2/trains1000.bin');
  //rainbowsData = loadBytes('/uploads/ozad2/rainbows1000.bin');
  //carData = loadBytes('data/car.bin');
  //effilData = loadBytes('uploads/ozad2/effil.bin');
  alarmData = loadBytes('uploads/ozad2/alarm.bin');
}


function setup() {
  createCanvas(500, 500);
	
  thehtml = "<button id='train'>train</button> <button id='test'>test</button> <button id='guess'>guess</button> <button id='clear'>clear</button><br>"+
  "<div>Draw either Alarm, Cat, Train.</div><span>Guess output is:=</span><div id = 'output'></div></br><div>Epoch:=<span id= 'epoch'></span></div><div>Test percentage:=<span id= 'testpercentage'></span></div>";
   AB.msg ( thehtml, 7 );
   
  background('gray');
    
   // console.log(catsData);
    //console.log(trainsData);
  // Preparing the data
  prepareData(cats, catsData, CAT);
  //prepareData(rainbows, rainbowsData, RAINBOW);
  prepareData(trains, trainsData, TRAIN);

  //prepareData(car, carData, CAR);
  
  //prepareData(effil, effilData, CAR);
  prepareData(alarm, alarmData, ALARM);


  // Making the neural network
  nn = new NeuralNetwork(784, 64, 3);

  // Randomizing the data
  let training = [];
  training = training.concat(cats.training);
  //training = training.concat(rainbows.training);
  training = training.concat(trains.training);
  //training = training.concat(effil.training);
  //training = training.concat(car.training);
  training = training.concat(alarm.training);

  let testing = [];
  testing = testing.concat(cats.testing);
  //testing = testing.concat(rainbows.testing);
  testing = testing.concat(trains.testing);
  //testing = testing.concat(car.testing);
  testing = testing.concat(alarm.testing);
  //testing = testing.concat(effil.testing);



let id = document.getElementById("train");
let epochCounter = 0;
$(id).click(function (event) {
    
    trainEpoch(training);
    epochCounter++;
    $("#epoch").text(epochCounter);
    console.log("Epoch: " + epochCounter);
    AB.removeLoading();
});

  /*let trainButton = select('#train');
  
  trainButton.mousePressed(function() {
   // console.log('training');
    trainEpoch(training);
    epochCounter++;
    console.log("Epoch: " + epochCounter);
  });*/

let id_test = document.getElementById("test");
$(id_test).click(function (event) {
    let percent = testAll(testing);
    console.log("Percent: " + nf(percent, 2, 2) + "%");
    $('#testpercentage').text(nf(percent, 2, 2)+"%");
});

/*  let testButton = select('#test');
  testButton.mousePressed(function() {
    
  });*/
  
  let id_guess = document.getElementById("guess");
$(id_guess).click(function (event) {
        let inputs = [];
    let img = get();
    img.resize(28, 28);
    img.loadPixels();
    for (let i = 0; i < len; i++) {
      let bright = img.pixels[i * 4];
      inputs[i] = (255 - bright) / 255.0;
    }

    let guess = nn.predict(inputs);
     console.log(guess);
    let m = max(guess);
    let classification = guess.indexOf(m);
    console.log(classification);
    if (classification === CAT) {
      console.log("cat");
      $("#output").text("Cat");
    } else if (classification === RAINBOW) {
      console.log("rainbow");
    } else if (classification === TRAIN) {
      console.log("train");
      $("#output").text("Train");
    }  else if (classification === CAR) {
      console.log("car");
    }  else if (classification === EFIL) {
      console.log("effil");
    } else if (classification === ALARM) {
      console.log("alarm");
      $("#output").text("Alarm Clock");
    }
});

  /*let guessButton = select('#guess');
  guessButton.mousePressed(function() {

    //image(img, 0, 0);
  });*/

  //let clearButton = select('#clear');
  
  let id_clear = document.getElementById("clear");
$(id_clear).click(function (event) {
    //background(255);
    background('gray');
});

  // for (let i = 1; i < 6; i++) {
  //   trainEpoch(training);
  //   console.log("Epoch: " + i);
  //   let percent = testAll(testing);
  //   console.log("% Correct: " + percent);
  // }
}


function draw() {
  strokeWeight(8);
  stroke(0);
  if (mouseIsPressed) {
    line(pmouseX, pmouseY, mouseX, mouseY);
  }
}


function trainEpoch(training) {
  shuffle(training, true);
  console.log(training);
  // Train for one epoch
  for (let i = 0; i < training.length; i++) {
    console.log(training[i]);
    //return;
    let data = training[i];
    console.log('data');
    console.log(data);
    let inputs = Array.from(data).map(x => x / 255);
    let label = training[i].label;
    let targets = [0, 0, 0];
    targets[label] = 1;
    // console.log(inputs);
    // console.log(targets);
    nn.train(inputs, targets);
  }
}

function testAll(testing) {

  let correct = 0;
  // Train for one epoch
  for (let i = 0; i < testing.length; i++) {
    // for (let i = 0; i < 1; i++) {
    let data = testing[i];
    let inputs = Array.from(data).map(x => x / 255);
    let label = testing[i].label;
    let guess = nn.predict(inputs);

    let m = max(guess);
    let classification = guess.indexOf(m);
    // console.log(guess);
    // console.log(classification);
    // console.log(label);

    if (classification === label) {
      correct++;
    }
  }
  let percent = 100 * correct / testing.length;
  return percent;

}



// Other techniques for learning

class ActivationFunction {
  constructor(func, dfunc) {
    this.func = func;
    this.dfunc = dfunc;
  }
}

let sigmoid = new ActivationFunction(
  x => 1 / (1 + Math.exp(-x)),
  y => y * (1 - y)
);

let tanh = new ActivationFunction(
  x => Math.tanh(x),
  y => 1 - (y * y)
);


class NeuralNetwork {
  /*
  * if first argument is a NeuralNetwork the constructor clones it
  * USAGE: cloned_nn = new NeuralNetwork(to_clone_nn);
  */
  constructor(in_nodes, hid_nodes, out_nodes) {
    if (in_nodes instanceof NeuralNetwork) {
      let a = in_nodes;
      this.input_nodes = a.input_nodes;
      this.hidden_nodes = a.hidden_nodes;
      this.output_nodes = a.output_nodes;

      this.weights_ih = a.weights_ih.copy();
      this.weights_ho = a.weights_ho.copy();

      this.bias_h = a.bias_h.copy();
      this.bias_o = a.bias_o.copy();
    } else {
      this.input_nodes = in_nodes;
      this.hidden_nodes = hid_nodes;
      this.output_nodes = out_nodes;

      this.weights_ih = new Matrix(this.hidden_nodes, this.input_nodes);
      this.weights_ho = new Matrix(this.output_nodes, this.hidden_nodes);
      this.weights_ih.randomize();
      this.weights_ho.randomize();

      this.bias_h = new Matrix(this.hidden_nodes, 1);
      this.bias_o = new Matrix(this.output_nodes, 1);
      this.bias_h.randomize();
      this.bias_o.randomize();
    }

    // TODO: copy these as well
    this.setLearningRate();
    this.setActivationFunction();

  }

  predict(input_array) {
    //console.log(input_array);
    // Generating the Hidden Outputs
    let inputs = Matrix.fromArray(input_array);
    //console.log(inputs);
    //console.log('weights');
   // console.log(this.weights_ih);
    let hidden = Matrix.multiply(this.weights_ih, inputs);
    
    //console.log('bias_h');
    //console.log(this.bias_h);
    hidden.add(this.bias_h);
    // activation function!
    hidden.map(this.activation_function.func);
    //console.log('hidden');
    //console.log(hidden);
    // Generating the output's output!
    let output = Matrix.multiply(this.weights_ho, hidden);
    //console.log('output');
    output.add(this.bias_o);
    output.map(this.activation_function.func);

    //console.log(output);

    // Sending back to the caller!
    return output.toArray();
  }

  setLearningRate(learning_rate = 0.01) {
    this.learning_rate = learning_rate;
  }

  setActivationFunction(func = sigmoid) {
    this.activation_function = func;
  }
  /*setActivationFunction(func = tanh) {
    this.activation_function = func;
  }*/

  train(input_array, target_array) {
    // Generating the Hidden Outputs
    //console.log(input_array);
    //console.log(target_array);
    let inputs = Matrix.fromArray(input_array);
    let hidden = Matrix.multiply(this.weights_ih, inputs);
    hidden.add(this.bias_h);
    // activation function!
    hidden.map(this.activation_function.func);

    // Generating the output's output!
    let outputs = Matrix.multiply(this.weights_ho, hidden);
    outputs.add(this.bias_o);
    outputs.map(this.activation_function.func);

    // Convert array to matrix object
    let targets = Matrix.fromArray(target_array);

    // Calculate the error
    // ERROR = TARGETS - OUTPUTS
    let output_errors = Matrix.subtract(targets, outputs);

    // let gradient = outputs * (1 - outputs);
    // Calculate gradient
    let gradients = Matrix.map(outputs, this.activation_function.dfunc);
    gradients.multiply(output_errors);
    gradients.multiply(this.learning_rate);


    // Calculate deltas
    let hidden_T = Matrix.transpose(hidden);
    let weight_ho_deltas = Matrix.multiply(gradients, hidden_T);

    // Adjust the weights by deltas
    this.weights_ho.add(weight_ho_deltas);
    // Adjust the bias by its deltas (which is just the gradients)
    this.bias_o.add(gradients);

    // Calculate the hidden layer errors
    let who_t = Matrix.transpose(this.weights_ho);
    let hidden_errors = Matrix.multiply(who_t, output_errors);

    // Calculate hidden gradient
    let hidden_gradient = Matrix.map(hidden, this.activation_function.dfunc);
    hidden_gradient.multiply(hidden_errors);
    hidden_gradient.multiply(this.learning_rate);

    // Calcuate input->hidden deltas
    let inputs_T = Matrix.transpose(inputs);
    let weight_ih_deltas = Matrix.multiply(hidden_gradient, inputs_T);

    this.weights_ih.add(weight_ih_deltas);
    // Adjust the bias by its deltas (which is just the gradients)
    this.bias_h.add(hidden_gradient);
  }

  serialize() {
    return JSON.stringify(this);
  }

  static deserialize(data) {
    if (typeof data == 'string') {
      data = JSON.parse(data);
    }
    let nn = new NeuralNetwork(data.input_nodes, data.hidden_nodes, data.output_nodes);
    nn.weights_ih = Matrix.deserialize(data.weights_ih);
    nn.weights_ho = Matrix.deserialize(data.weights_ho);
    nn.bias_h = Matrix.deserialize(data.bias_h);
    nn.bias_o = Matrix.deserialize(data.bias_o);
    nn.learning_rate = data.learning_rate;
    return nn;
  }


  // Adding function for neuro-evolution
  copy() {
    return new NeuralNetwork(this);
  }

  // Accept an arbitrary function for mutation
  mutate(func) {
    this.weights_ih.map(func);
    this.weights_ho.map(func);
    this.bias_h.map(func);
    this.bias_o.map(func);
  }



}


// let m = new Matrix(3,2);


class Matrix {
  constructor(rows, cols) {
    this.rows = rows;
    this.cols = cols;
    this.data = Array(this.rows).fill().map(() => Array(this.cols).fill(0));
  }

  copy() {
    let m = new Matrix(this.rows, this.cols);
    for (let i = 0; i < this.rows; i++) {
      for (let j = 0; j < this.cols; j++) {
        m.data[i][j] = this.data[i][j];
      }
    }
    return m;
  }

  static fromArray(arr) {
    return new Matrix(arr.length, 1).map((e, i) => arr[i]);
  }

  static subtract(a, b) {
    if (a.rows !== b.rows || a.cols !== b.cols) {
      console.log('Columns and Rows of A must match Columns and Rows of B.');
      return;
    }

    // Return a new Matrix a-b
    return new Matrix(a.rows, a.cols)
      .map((_, i, j) => a.data[i][j] - b.data[i][j]);
  }

  toArray() {
    let arr = [];
    for (let i = 0; i < this.rows; i++) {
      for (let j = 0; j < this.cols; j++) {
        arr.push(this.data[i][j]);
      }
    }
    return arr;
  }

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

  add(n) {
    if (n instanceof Matrix) {
      if (this.rows !== n.rows || this.cols !== n.cols) {
        console.log('Columns and Rows of A must match Columns and Rows of B.');
        return;
      }
      return this.map((e, i, j) => e + n.data[i][j]);
    } else {
      return this.map(e => e + n);
    }
  }

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

  static multiply(a, b) {
    //console.log('static multiply');
    console.log(a);
    console.log(b);
    // Matrix product
    if (a.cols !== b.rows) {
      console.log('Columns of A must match rows of B.');
      return;
    }

    return new Matrix(a.rows, b.cols)
      .map((e, i, j) => {
        // Dot product of values in col
        let sum = 0;
        for (let k = 0; k < a.cols; k++) {
          sum += a.data[i][k] * b.data[k][j];
        }
        return sum;
      });
  }

  multiply(n) {
    //console.log('multiply');
    if (n instanceof Matrix) {
      if (this.rows !== n.rows || this.cols !== n.cols) {
        console.log('Columns and Rows of A must match Columns and Rows of B.');
        return;
      }

      // hadamard product
      return this.map((e, i, j) => e * n.data[i][j]);
    } else {
      // Scalar product
      return this.map(e => e * n);
    }
  }

  map(func) {
    // Apply a function to every element of matrix
    for (let i = 0; i < this.rows; i++) {
      for (let j = 0; j < this.cols; j++) {
        let val = this.data[i][j];
        this.data[i][j] = func(val, i, j);
      }
    }
    return this;
  }

  static map(matrix, func) {
    // Apply a function to every element of matrix
    return new Matrix(matrix.rows, matrix.cols)
      .map((e, i, j) => func(matrix.data[i][j], i, j));
  }

  print() {
    console.table(this.data);
    return this;
  }

  serialize() {
    return JSON.stringify(this);
  }

  static deserialize(data) {
    if (typeof data == 'string') {
      data = JSON.parse(data);
    }
    let matrix = new Matrix(data.rows, data.cols);
    matrix.data = data.data;
    return matrix;
  }
}

if (typeof module !== 'undefined') {
  module.exports = Matrix;
}

function loadByte(file, callback)
{
    var self = this;
  var data = {};
  var oReq = new XMLHttpRequest();
  oReq.open("GET", file, true);
  oReq.responseType = "arraybuffer";
  oReq.onload = function(oEvent) {
    var arrayBuffer = oReq.response;
    if (arrayBuffer) {
      data.bytes = new Uint8Array(arrayBuffer);
      if (callback) {
        callback(data);
      }
      self._decrementPreload();
    }
  }
  oReq.send(null);
 // console.log(data);
  return data;
}

function prepareData(category, data, label) {
  category.training = [];
  category.testing = [];
  for (let i = 0; i < totalData; i++) {
    let offset = i * len;
    let threshold = floor(0.8 * totalData);
    if (i < threshold) {
      category.training[i] = data.bytes.subarray(offset, offset + len);
      category.training[i].label = label;
    } else {
      category.testing[i - threshold] = data.bytes.subarray(offset, offset + len);
      category.testing[i - threshold].label = label;
    }
  }
  //console.log(category);
}