GPU-JS: Error: too many arguments for kernel - javascript

I was trying to make simple script that uses GPU for multiplying arrays, but when I turn on the code, it shows error as in title. I don't know if it's my fault and I didn't installed every library or its a bug.
Code is from gpu-js github example:
const { GPU } = require('gpu.js');
const gpu = new GPU();
const multiplyMatrix = gpu.createKernel(function(a, b) {
let sum = 0;
for (let i = 0; i < 512; i++) {
sum += a[this.thread.y][i] * b[i][this.thread.x];
}
return sum;
}).setOutput([512, 512]);
const c = multiplyMatrix(a, b);
Thanks in advance.

A and B are not defined, you need to define your matrices first and then call the function. Here's the full example from their website, comments mine:
// Function to create the 512x512 matrix
const generateMatrices = () => {
const matrices = [[], []]
for (let y = 0; y < 512; y++){
matrices[0].push([])
matrices[1].push([])
for (let x = 0; x < 512; x++){
matrices[0][y].push(Math.random())
matrices[1][y].push(Math.random())
}
}
return matrices
}
//Define the function to be ran on GPU
const gpu = new GPU();
const multiplyMatrix = gpu.createKernel(function(a, b) {
let sum = 0;
for (let i = 0; i < 512; i++) {
sum += a[this.thread.y][i] * b[i][this.thread.x];
}
return sum;
}).setOutput([512, 512])
// Create the matrices
const matrices = generateMatrices()
// Run multiplyMatrix using the 2 matrices created.
const out = multiplyMatrix(matrices[0], matrices[1])

Related

matrix with different colors javascript

I need to build 2d matrix 50x50 representing boxes with random colors, but if the boxes which are close to each other have the same colors, they should get different random color from each other, until it's different and then continue building.
Here I made matrix with boxes inside it works fine, but colors sometimes do match:
function onLoad(evt)
{
var matrix = [];
for (var i = 0; i < 50; i++) {
var row = [];
for (var j = 0; j < 50; j++) {
var randColor = Math.floor(Math.random()*16777215).toString(16);
row.push(MyComponent(randColor));
}
matrix.push(row);
}
var newData = matrix.map(function(row) {
return row.map(function(x) {
return x;
})})
}
You need a way to determine whether a particular color is too close to another. One way to do this is with rgb-lab (or, less accurately, euclidean distance). Say you use rgb-lab's deltaE function, which takes two arguments, where each argument is a 3-item array of RGB amounts.
Generate your random colors such that you can get their components' decimal values easily, and so that you can also get their hex string representation easily. Then iterate over the filled adjacent indicies and compare the colors. If they're too similar, try again. Something along the lines of:
const MINIMUM_DISTANCE = 25;
const getColor = () => {
const r = Math.floor(Math.random() * 256);
const g = Math.floor(Math.random() * 256);
const b = Math.floor(Math.random() * 256);
const str = r.toString(16) + g.toString(16) + b.toString(16);
return { rgb: [r, g, b], str };
};
const matrix = [];
for (let i = 0; i < 50; i++) {
const row = [];
for (let j = 0; j < 50; j++) {
let color;
let tooClose = false;
do {
color = getColor();
tooClose =
(row[j - 1] && deltaE(color.rgb, row[j - 1].rgb) < 25) ||
(matrix[i - 1] && deltaE(color.rgb, row[i - 1][j].rgb < 25));
} while (tooClose);
row.push(color);
}
}
Change the MINIMUM_DISTANCE as desired. See here for an explanation of the numbers.
Then you'll need to turn the color objects into an array of components with color strings at the end.
const arrayOfComponents = matrix.map(
row => row.map(
({ str }) => MyComponent(str)
)
);

How to make k-means algorithm functional

I have a very basic implementation of k-means in javascript (I know but it needs to run in the browser). What I would like to understand is - how could one make this more functional?
It is currently full of loops, and extremely difficult to follow / reason about, code below:
export default class KMeans {
constructor(vectors, k) {
this.vectors = vectors;
this.numOfVectors = vectors.length;
this.k = k || bestGuessK(this.numOfVectors);
this.centroids = randomCentroids(this.vectors, this.k);
}
classify(vector, distance) {
let min = Infinity;
let index = 0;
for (let i = 0; i < this.centroids.length; i++) {
const dist = distance(vector, this.centroids[i]);
if (dist < min) {
min = dist;
index = i;
}
}
return index;
}
cluster() {
const assigment = new Array(this.numOfVectors);
const clusters = new Array(this.k);
let movement = true;
while (movement) {
// update vector to centroid assignments
for (let i = 0; i < this.numOfVectors; i++) {
assigment[i] = this.classify(this.vectors[i], euclidean);
}
// update location of each centroid
movement = false;
for (let j = 0; j < this.k; j++) {
const assigned = [];
for (let i = 0; i < assigment.length; i++) {
if (assigment[i] === j) assigned.push(this.vectors[i]);
}
if (!assigned.length) continue;
const centroid = this.centroids[j];
const newCentroid = new Array(centroid.length);
for (let g = 0; g < centroid.length; g++) {
let sum = 0;
for (let i = 0; i < assigned.length; i++) {
sum += assigned[i][g];
}
newCentroid[g] = sum / assigned.length;
if (newCentroid[g] !== centroid[g]) {
movement = true;
}
}
this.centroids[j] = newCentroid;
clusters[j] = assigned;
}
}
return clusters;
}
}
It certainly can.
You could start with this:
classify(vector, distance) {
let min = Infinity;
let index = 0;
for (let i = 0; i < this.centroids.length; i++) {
const dist = distance(vector, this.centroids[i]);
if (dist < min) {
min = dist;
index = i;
}
}
return index;
}
Why is this a member function? Wouldn't a pure function const classify = (centroids, vector, distance) => {...} be cleaner?
Then for an implementation, let's change the distance signature a bit. If we curry it to const distance = (vector) => (centroid) => {...}, we can then write
const classify = (centroids, vector, distance) =>
minIndex (centroids .map (distance (vector)))
And if that distance API is out of our control, it's not much harder:
const classify = (centroids, vector, distance) =>
minIndex (centroids .map (centroid => distance (vector, centroid)))
Granted, we haven't written minIndex yet, but we've already broken the problem down to use a more meaningful abstraction. And minIndex isn't hard to write. You can do it imperatively as the original classify function did, or with something like this:
const minIndex = (xs) => xs.indexOf (Math.min (...xs))
Note that distance is a slightly misleading name here. I had to read it more carefully because I assumed a name like that would represent..., well a distance. Instead it's a function used to calculate distance. Perhaps the name metric or something like distanceFunction, distanceFn, or distanceImpl would be more obvious.
Now let's move on to this bit:
const newCentroid = new Array(centroid.length);
for (let g = 0; g < centroid.length; g++) {
let sum = 0;
for (let i = 0; i < assigned.length; i++) {
sum += assigned[i][g];
}
newCentroid[g] = sum / assigned.length;
if (newCentroid[g] !== centroid[g]) {
movement = true;
}
}
This code has two responsibilities: creating the newCentroid array, and updating the value of movement if any value has changed.
Let's separate those two.
First, creating the new centroid. We can clean up that nested for-loop to something like this:
const makeNewCentroid = (centroid, assigned) =>
centroid .map ((c, g) => mean (assigned .map ((a) => a[g])))
This depends on a mean function, which we'll write along with its required sum function like this:
const sum = (ns) => ns .reduce ((t, n) => t + n, 0)
const mean = xs => sum (xs) / xs.length
Then we need to update movement. We can do that easily based on centroids and newCentroids:
movement = centroids.some((c, i) => c !== newCentroids[i])
Obviously, you can continue in this manner. Each for loop should have a fundamental purpose. Find that purpose and see if one of the Array.prototype methods could better express it. For the second section we worked with above, we found two purposes, and just split them into two separate blocks.
This should give you a good start on making this more functional. There is no magic bullet. But if you think in terms of pure functions on immutable data, and on strong separation of concerns, you can usually move in a functional direction.

Code that creates matrix with random 0 and 1 values

Can you please write a js code that fills empty matrix randomly with 0 or 1? I need to use Random() function.
I wrote this code and I got an error Random() is not defined
var matrix = [];
for(var y = 0; y<5; y++){
for(var x = 0; x<5; x++){
let arr = [0,1]
matrix[y][x]= random(arr)
matrix.push(matrix[y][x])
}
}
You should Math.random() and then use Math.round() to get 0 or 1.
Secondly you should set matrix[y] to an empty array otherwise code will throw error.
var matrix = [];
for(var y = 0; y<5; y++){
matrix[y] = [];
for(var x = 0; x<5; x++){
matrix[y][x]= Math.round(Math.random())
matrix.push(matrix[y][x])
}
}
console.log(matrix)
An easier to create a matrix of any length you can use map(). Create an array of given length and map it to a another array with same length have random values from 0 or 1
const getMatrix = len => [...Array(len)].map(x => [...Array(len)].map(b => Math.round(Math.random())));
let res = getMatrix(5);
console.log(res)
For different length and width use two parameters.
const getMatrix = (l,w) => [...Array(l)].map(x => [...Array(w)].map(b => Math.round(Math.random())));
let res = getMatrix(2,3);
console.log(res)
You should use Math.round(Math.random()).
An easy way to do it using ES6:
const arr = new Array(5).fill().map(() => new Array(5).fill().map(() => Math.round(Math.random())));
console.log(arr);
You have to use the fill() method before map(), otherwise you will get undefined values.
The "classical" way to do it using your snippet of code will be similar to what you tried, with the addition of the standard built-in object Math which has the random() method and also round() to get integer values. If you want a matrix (2D array) then you will need to push an array into each row, otherwise you will get a simple array.
var matrix = [];
for(var y = 0; y < 5; y++) {
const row = [];
for(var x = 0; x < 5; x++) {
row.push(Math.round(Math.random()));
}
matrix.push(row);
}
console.log(matrix);

Simple Feedforward Neural Network in JavaScript

I'm new to this site, so I apologize in advance if I'm doing anything wrong in this post.
I'm currently trying out machine learning, and I'm learning neural networks. I'm currently using http://neuralnetworksanddeeplearning.com/. However, I don't fully understand everything, and all of the code is written in Python (I'm more comfortable with JavaScript).
I've created a program that works for simple data. However, for more complicated data (handwritten digits recognition with MNIST data), the accuracy rate isn't nearly as high as the website above says it will be, by using a neural network of 784 input neurons, 10-400 hidden neurons in the hidden layer (only one hidden layer and tried several possible number of neurons), and 10 output neurons with hundreds of iterations. I think that there is an error with my back propagation step (i.e. the train step, I'm including the other functions here as reference) that prevents it from learning fast enough (BTW, I'm using the cross-entropy as my cost function). I would really appreciate if anyone can help me find the error. Thanks in advance.
Below is the code. The weights are arranged in an array of arrays of arrays (weight[i][j][k] is the weight between the jth neurons in the ith layer and the kth neuron in the (i+1)th layer). Similarly, bias[i][j] is the bias of the (i+1)th layer for the jth neuron. The training data is formatted as an array of objects with keys of inputs and outputs (see example below).
class NeuralNetwork {
constructor(layers) {
// Check if layers is a valid argument
// Initialize neural network
if (!Array.isArray(layers) || layers.length < 2) {
throw Error("Layers must be specified as an array of length at least 2");
}
this.weights = [];
this.biases = [];
for (let i = 0, l = layers.length; i < l; ++i) {
let currentLayer = layers[i];
if (typeof currentLayer === "number" && Number.isInteger(currentLayer) && currentLayer > 0) {
let numWeights = layers[i + 1];
if (i < l - 1) {
this.weights.push([]);
}
if (i) {
this.biases.push([]);
}
// Seed weights and biases
for (let j = 0; j < currentLayer; ++j) {
if (i < l - 1) {
let weights = [];
for (let k = 0; k < numWeights; ++k) {
weights.push(Math.random() * 2 - 1);
}
this.weights[i].push(weights);
}
if (i) {
this.biases[i - 1].push(Math.random() * 2 - 1);
}
}
} else {
throw Error("Array used to specify NeuralNetwork layers must consist solely of positive integers");
}
}
this.activation = (x) => 1 / (1 + Math.exp(-x));
this.activationDerivative = (x) => this.activation(x) * (1 - this.activation(x));
Object.freeze(this);
console.log("Successfully initialized NeuralNetwork");
return this;
}
run(input, training) {
// Forward propagation
let currentInput;
if (training) {
currentInput = [input.map((a) => {return {before: a, after: a}})];
} else {
currentInput = [...input];
}
for (let i = 0, l = this.weights.length; i < l; ++i) {
let newInput = [];
for (let j = 0, m = this.weights[i][0].length, n = (training ? currentInput[i] : currentInput).length; j < m; ++j) {
let sum = this.biases[i][j];
for (let k = 0; k < n; ++k) {
sum += (training ? currentInput[i][k].after : currentInput[k]) * this.weights[i][k][j];
}
if (training) {
newInput.push({
before: sum,
after: this.activation(sum)
});
} else {
newInput.push(this.activation(sum));
}
}
if (training) {
currentInput.push(newInput);
} else {
currentInput = newInput;
}
}
return currentInput;
}
train(data, learningRate = 0.1, batch = 50, iterations = 10000) {
// Backward propagation
console.log("Initialized training");
let length = data.length,
totalCost = 0,
learningRateFunction = typeof learningRate === "function",
batchCount = 0,
weightChanges = [],
biasChanges = [];
for (let i = 0; i < iterations; ++i) {
let rate = learningRateFunction ? learningRate(i, totalCost) : learningRate;
totalCost = 0;
for (let j = 0, l = length; j < l; ++j) {
let currentData = data[j],
result = this.run(currentData.input, true),
outputLayer = result[result.length - 1],
outputLayerError = [],
errors = [];
for (let k = 0, m = outputLayer.length; k < m; ++k) {
let currentOutputNeuron = outputLayer[k];
outputLayerError.push(currentOutputNeuron.after - currentData.output[k]);
totalCost -= Math.log(currentOutputNeuron.after) * currentData.output[k] + Math.log(1 - currentOutputNeuron.after) * (1 - currentData.output[k]);
}
errors.push(outputLayerError);
for (let k = result.length - 1; k > 1; --k) {
let previousErrors = errors[0],
newErrors = [],
currentLayerWeights = this.weights[k - 1],
previousResult = result[k - 1];
for (let i = 0, n = currentLayerWeights.length; i < n; ++i) {
let sum = 0,
currentNeuronWeights = currentLayerWeights[i];
for (let j = 0, o = currentNeuronWeights.length; j < o; ++j) {
sum += currentNeuronWeights[j] * previousErrors[j];
}
newErrors.push(sum * this.activationDerivative(previousResult[i].before));
}
errors.unshift(newErrors);
}
for (let k = 0, n = this.biases.length; k < n; ++k) {
if (!weightChanges[k]) weightChanges[k] = [];
if (!biasChanges[k]) biasChanges[k] = [];
let currentLayerWeights = this.weights[k],
currentLayerBiases = this.biases[k],
currentLayerErrors = errors[k],
currentLayerResults = result[k],
currentLayerWeightChanges = weightChanges[k],
currentLayerBiasChanges = biasChanges[k];
for (let i = 0, o = currentLayerBiases.length; i < o; ++i) {
let change = rate * currentLayerErrors[i];
for (let j = 0, p = currentLayerWeights.length; j < p; ++j) {
if (!currentLayerWeightChanges[j]) currentLayerWeightChanges[j] = [];
currentLayerWeightChanges[j][i] = (currentLayerWeightChanges[j][i] || 0) - change * currentLayerResults[j].after;
}
currentLayerBiasChanges[i] = (currentLayerBiasChanges[i] || 0) - change;
}
}
++batchCount;
if (batchCount % batch === 0 || i === iterations - 1 && j === l - 1) {
for (let k = 0, n = this.weights.length; k < n; ++k) {
let currentLayerWeights = this.weights[k],
currentLayerBiases = this.biases[k],
currentLayerWeightChanges = weightChanges[k],
currentLayerBiasChanges = biasChanges[k];
for (let i = 0, o = currentLayerWeights.length; i < o; ++i) {
let currentNeuronWeights = currentLayerWeights[i],
currentNeuronWeightChanges = currentLayerWeightChanges[i];
for (let j = 0, p = currentNeuronWeights.length; j < p; ++j) {
currentNeuronWeights[j] += currentNeuronWeightChanges[j] / batch;
}
currentLayerBiases[i] += currentLayerBiasChanges[i] / batch;
}
}
weightChanges = [];
biasChanges = [];
}
}
totalCost /= length;
}
console.log(`Training ended due to iterations reached\nIterations: ${iterations} times\nTime spent: ${(new Date).getTime() - startTime} ms`);
return this;
}
}
Example
Tests if a point is inside a circle. For this example, the neural network performs well. However, for more complicated examples such as handwriting recognition, the neural network performs really badly (best I can get for a single neural network is 70% accuracy, compared to the 96% accuracy stated in the website even when using similar parameters).
let trainingData = [];
for (let i = 0; i < 1000; ++i) {
let [x, y] = [Math.random(), Math.random()];
trainingData.push({input: [x, y], output: [Number(Math.hypot(x,y) < 1)]});
}
let brain = new NeuralNetwork([2, 5, 5, 1]);
brain.train(trainingData.slice(0,700), 0.1, 10, 500); // Accuracy rate 95.33% on the remaining 300 entries in trainingData
Ok, I guess I'm going to answer my own question. So, I don't think there is an error in my code and it's perfectly fine to use (albeit really, really inefficient) if anyone wants to.
The reason why my runs on the MNIST data did not give accurate answers come from the fact that I did not process the data at first. The raw data gave the darkness of the 28*28 pixels in the range of [0, 255], which I used directly as the input for each of the training data. The correct procedure here would be to convert this into the range of [0, 1] or [-1, 1].
The reason that the [0, 255] range does not work as well is due to the fact that the second hidden layer of neurons will receive really positive or negative inputs.
When the backpropagation algorithm computes the gradient, the change computed for each weight will be really small as it is proportional to the slope of the activation function at the input to the neuron (the derivative of the logistic function is exp(-x)/(1+exp(-x)), which is close to 0 for really positive and negative values of x). Thus, the neural network will take really long to train and, in my case, was not able to learn the data well.
With the correct method, I am able to achieve around 90% accuracy for a 784*200*10 neural network in a fairly short time, though it still is not nearly as accurate as what the author says he is able to achieve using an even simpler algorinthm in the link mentioned in the question.

create a grid array in JavaScript

I want to setup a grid containing m * n objects. This grid got a width of m rows and n columns.
I tried this code first
let map = [][]; // Create an array that takes a x and y index
function createMap() {
for (let x = 0; x < columnCount; x++) {
for (let y = 0; y < rowCount; y++) {
addCell(x, y);
}
}
}
function addCell(x, y) {
map[x][y] = cell(); // create a new object on x and y
}
Obviously this is a wrong syntax. The initialization of map is wrong. How can I create the array that I can access a object by passing in the x and y coordinate to the array?
Let's say I want to access the object on (3|7) I want to go for map[3][7].
Is that possible?
You cant initialize a 2d array, as there are no real 2d arrays in js. However you could setup a regular array, and add arrays to it:
function createMap(columnCount, rowCount) {
const map = [];
for (let x = 0; x < columnCount; x++) {
map[x] = []; // set up inner array
for (let y = 0; y < rowCount; y++) {
addCell(map, x, y);
}
}
return map;
}
function addCell(map, x, y) {
map[x][y] = cell(); // create a new object on x and y
}
const map = createMap(10, 10);
You aren't actually that far off with your solution. You're right, though, you cannot initialize a two-dimensional array like let a = [][]. If you add just one line to your for-loops, your solution also produces a map-like structure:
In your createMap() function, you just need to initialize every field of the the array with an array, after that you can fill the fields of this array:
function createMap() {
for (let x = 0; x < 10; x++) {
map[x] = []; // initialize map[x] as an array
for (let y = 0; y < 10; y++) {
addCell(x, y);
}
}
}
And initialize map as a simple array.
Here is a working example:
let map = [];
createMap();
console.log(map);
function createMap() {
for (let x = 0; x < 5; x++) {
map[x] = [];
for (let y = 0; y < 5; y++) {
addCell(x, y);
}
}
}
function addCell(x, y) {
map[x][y] = cell(x,y); // create a new object on x and y
}
function cell(x,y) {
return (x+1)+":"+(y+1);
}
You need a single array as value and a check if one row does not exist.
function createMap(rowCount, columnCount) {
for (let x = 0; x < rowCount; x++) {
for (let y = 0; y < columnCount; y++) {
addCell(x, y);
}
}
}
function addCell(x, y) {
map[x] = map[x] || [];
map[x][y] = x + '|' + y;
}
var map = [];
createMap(4, 8);
console.log(map[3][7]);
console.log(map);
An approach by using Array.from.
function createMap(rowCount, columnCount) {
map = Array.from(
{ length: rowCount }, // take rowCount as length
(_, i) => Array.from( // fill with new array
{ length: columnCount }, // take columnCount for every row
(_, j) => [i, j].join('|') // initialize cell with some value
)
);
}
var map;
createMap(4, 8);
console.log(map[3][7]);
console.log(map);
Not sure if you are having trouble creating the grid or displaying it.
Here is yet another way to create it:
const grid = Array.from(new Array(5),(_,x)=>Array.from(new Array(5),(_,y)=>addCell(x,y)));
Here are 2 ways to show the grid:
const grid = Array.from(new Array(5),()=>Array.from(new Array(5),()=>"-"));
const rotate = grid =>
grid[0].map(
(_,y)=>grid.map(
(_,x)=>[y,x]
)
).map(
row=>row.map(([x,y])=>grid[y][x])
);
const format = grid => grid.map(x=>x.join(" ")).join("\n");
//set some values of grid
[[0,2],[1,2],[2,2],[3,2],[4,2]].forEach(
([x,y])=>grid[x][y]="X"
);
//you can map the grid to columns first, it'll look like it's rotated
// unless you generate the columns in div float lefts
console.log("map grid columns first:")
console.log(format(grid));
//you can rotate the grid to build each row and then each column like html table
console.log("map grid rows first:")
console.log(format(rotate(grid)));
var grid=[];
var grid_length=10; //mathematical length
var grid_width=10; //mathematical width
function pos(x,y){
return (y*grid_length)-grid_length-1+x-2;
}
function replaceItem(x,y,item){
grid[pos(x,y)]=item;
}
var itemsRequested=[];
function iRequest(x,y){ // get Item on grid.
itemsRequested.push(grid[pos(x,y)]); // this both adds the Object to a list and returns it
return grid[pos(x,y)];
}
This method only makes a mathematical grid, with which you can reference with the pos() function.
Then to answer your question to get the object on 3,7 you would simply say
var a=iRequest(3,7);
//currently would return undefined because there are no objects in the array.
When using this method, 1,1 is the top left corner, and pos(1,1) would return 0.
Here's a functional method that doesn't rely on any reassignment or mutation:
const lengthX = 5;
const lengthY = 2;
const map = Array.from({ length: lengthX }, (_, colIndex) => (
Array.from({ length: lengthY }, (_, rowIndex) => (
// some element to put in each, for example
{ foo: 'bar'}
))
));
console.log(map);
The colIndex and rowIndex are optional, they're not used in this snippet, but if you need to create elements based on their location in the grid, they're the variables to use.
try this:
var x = new Array(10);
for (var i = 0; i < 10; i++) {
x[i] = new Array(20);
}
x[5][12] = 3.0;

Categories

Resources