How to Use TensorFlow.js for Multi-variable Regression - javascript

I want to fit a nonlinear multivariable equation using TensorFlow. The equation is given below. The parameters to fit are a0, a1, and a2. The independent variables are S and R, while F is the dependent variable. The corresponding data for S,R,F are provided in the code below, as Sdata, Rdata, and Fdata respectively.
F = a0 + a1*S + a2*R
I
const Sdata = tf.tensor1d([13.8,13.8,20.2,12.1,14.1,29.4,13.7,16.6,18.9,15.5]);
const Fdata = tf.tensor1d([46.7,130.7,78.1,72.2,40.1,78.6,57.4,170.7,80.2,45.2]);
const Rdata = tf.tensor1d([1.5,4.5,2.5,3.0,3.5,3.0,2.5,3.0,3.0,2.5])
const a0 = tf.scalar(Math.random()).variable();
const a1 = tf.scalar(Math.random()).variable();
const a2 = tf.scalar(Math.random()).variable();
const fun = (r,s) => a2.mul(r).add(a1.mul(s)).add(a0)
const cost = (pred, label) => pred.sub(label).square().mean();
const learningRate = 0.01;
const optimizer = tf.train.sgd(learningRate);
// Train the model.
for (let i = 0; i < 800; i++) {
optimizer.minimize(() => cost(fun(Rdata,Sdata), Fdata));
}
As shown in my code, I assumed the function "fun" can take two independent variable. Instead of getting a0 = -6.6986, a1 = 0.8005, and a2 = 25.2523, I am getting NaNs.
Does it mean it is not possible to fit multivariable functions in tensorflow.js? I don't think so. I will appreciate any insight to this.

Because of the learning rate, the model is oscillating to find the best parameters. Actually, the parameters keep on increasing to Infinity.
Tuning the learning rate will allow the model to find the best parameters. In this case 0.001 seems to give good result. If you want to improve the accuracy of the model, you can consider normalizing all your input data to be of the same magnitude order - between 0 and 1
const Sdata = tf.tensor1d([13.8,13.8,20.2,12.1,14.1,29.4,13.7,16.6,18.9,15.5]);
const Fdata = tf.tensor1d([46.7,130.7,78.1,72.2,40.1,78.6,57.4,170.7,80.2,45.2]);
const Rdata = tf.tensor1d([1.5,4.5,2.5,3.0,3.5,3.0,2.5,3.0,3.0,2.5])
const a0 = tf.scalar(Math.random()).variable();
const a1 = tf.scalar(Math.random()).variable();
const a2 = tf.scalar(Math.random()).variable();
const fun = (r,s) => a2.mul(r).add(a1.mul(s)).add(a0)
const cost = (pred, label) => pred.sub(label).square().mean();
const learningRate = 0.001;
const optimizer = tf.train.sgd(learningRate);
// Train the model.
for (let i = 0; i < 800; i++) {
console.log("training")
optimizer.minimize(() => cost(fun(Rdata,Sdata), Fdata));
}
console.log(`a: ${a0.dataSync()}, b: ${a1.dataSync()}, c: ${a2.dataSync()}`);
const preds = fun(Rdata,Sdata).dataSync();
preds.forEach((pred, i) => {
console.log(`x: ${i}, pred: ${pred}`);
});
<html>
<head>
<!-- Load TensorFlow.js -->
<script src="https://cdn.jsdelivr.net/npm/#tensorflow/tfjs#latest"> </script>
</head>
<body>
</body>
</html>

Related

Get pseudo-random item with given probability

I want to give the user a prize when he signs in;
but it needs to be there some rare prizes so I want to appear prizes with different chances to appear using percents
i want to display one of these
[50 : 'flower'], [30 : 'book'], [20 : 'mobile'];
using percents they have
if there any way using Node.js or just javascript functions it would be great
You can create a function to get weighted random results, something like this:
const weightedSample = (items) => {
// cache if necessary; in Chrome, seems to make little difference
const total = Object.values(items).reduce((sum, weight) => sum + weight, 0)
const rnd = Math.random() * total
let accumulator = 0
for (const [item, weight] of Object.entries(items)) {
accumulator += weight
if (rnd < accumulator) {
return item
}
}
}
// check frequencies of each result
const prizes = { flower: 50, book: 30, mobile: 20 }
const results = Object.fromEntries(Object.keys(prizes).map(k => [k, 0]))
for (let i = 0; i < 1e6; ++i) {
const prize = weightedSample(prizes)
++results[prize]
}
// sample results: { flower: 500287, book: 299478, mobile: 200235 }
console.log(results)
This will work regardless of whether the weights add up to 100, whether they're integers, and so on.
'Right off the top of my head'-approach would be to prepare an array where each source item occurs the number of times that corresponds to respective probability and pick random item out of that array (assuming probability value has no more than 2 decimal places):
// main function
const getPseudoRandom = items => {
const {min, random} = Math,
commonMultiplier = 100,
itemBox = []
for(item in items){
for(let i = 0; i < items[item]*commonMultiplier; i++){
const randomPosition = 0|random()*itemBox.length
itemBox.splice(randomPosition, 0, item)
}
}
return itemBox[0|random()*itemBox.length]
}
// test of random outcomes distribution
const outcomes = Array(1000)
.fill()
.map(_ => getPseudoRandom({'flower': 0.5, 'book': 0.3, 'mobile': 0.2})),
distribution = outcomes.reduce((acc, item, _, s) =>
(acc[item] = (acc[item]||0)+100/s.length, acc), {})
console.log(distribution)
.as-console-wrapper{min-height:100%;}
While above approach may seem easy to comprehend and deploy, you may consider another one - build up the sort of probability ranges of respective width and have your random value falling into one of those - the wider the range, the greater probability:
const items = {'flower': 0.5, 'book': 0.2, 'mobile': 0.2, '1mUSD': 0.1},
// main function
getPseudoRandom = items => {
let totalWeight = 0,
ranges = [],
rnd = Math.random()
for(const itemName in items){
ranges.push({
itemName,
max: totalWeight += items[itemName]
})
}
return ranges
.find(({max}) => max > rnd*totalWeight)
.itemName
},
// test of random outcomes distribution
outcomes = Array(1000)
.fill()
.map(_ => getPseudoRandom(items)),
distribution = outcomes.reduce((acc, item, _, s) =>
(acc[item] = (acc[item]||0)+100/s.length, acc), {})
console.log(distribution)
"Certain probability" and "random" could lead to different approaches!
If you want random each time, something like:
let chances = [[0.2,'mobile'],[0.5,'book'],[1.0,'flower']]
let val = Math.random() // floating number from 0 to 1.0
let result = chances.find( c => c[0] <= val )[1]
This will give a random result each time. It could be possible to get 'mobile' 100 times in a row! Rare, of course, but a good random number generate will let that happen.
But perhaps you want to ensure that, in 100 results, you only hand out 20 mobiles, 30 books, and 50 flowers. Then you might want a "random array" for each user. Pre-fill the all the slots and remove them as they are used. Something like:
// when setting up a new user
let userArray = []
let chances = [[20,'mobile'],[30,'book'],[50,'flower']]
changes.forEach( c => {
for(let i = 0; i < c[0]; i++) userArray.push(c[1])
})
// save userArray, which has exactly 100 values
// then, when picking a random value for a user, find an index in the current length
let index = Math.floor(Math.random() * userArray.length)
let result = userArray[index]
userArray.splice(index,1) // modify and save userArray for next login
if(userArray.length === 0) reinitializeUserArray()
There are different approaches to this, but just some ideas to get you started.

Can you replicate tf.random_crop in TensorFlow JS efficiently?

Random cropping is not implemented in TensorFlow JS, but is it possible to replicate it? My idea was to use Tensor.slice() with tensors generated from tf.randomUniform as parameters, but it only accepts "numbers". So it seems to me, that in order to get random cropping working, I'd have to reconstruct that part of the computation graph in each iteration with newly generated random numbers (e.g. from Math.random()) as slice parameters. Or is there another way?
Here is my code. My understanding is, that the inner function will create the random offset rx and ry only once, and I'd need a tensorflow operation to continuously get random values in each iteration.
export function jitter (d) {
const inner = (tImage) => {
const tShp = tImage.shape;
const cropShape = [
tShp[0], tShp[1]-d,
tShp[2]-d, tShp[3]];
const rx = Math.floor(Math.random() * d + 0.5);
const ry = Math.floor(Math.random() * d + 0.5);
const crop = tImage.slice(
[0, rx, ry, 0],
[cropShape[0], cropShape[1], cropShape[2], cropShape[3]]);
}
return inner;
}
Link to doc for Tensor.slice()
slice will allow to slice or crop a part of the input. Using gatherND will allow on the other hand to slice multiples times if one wants to avoid using slice repeatedly. But the indices at where to slice should be given. Below, the function g generate the indices from the random coordinates and try to calculate the indices of all the z * z elements that will be included in the crop.
const g = (r, s, z, n) => {
const arr = []
for (let i = 0; i < n; i++) {
const c = Math.floor(Math.random() * r)
const d = Math.floor(Math.random() * s)
const p = Array.from({length: z}, (_, k) => k + c)
const q = Array.from({length: z}, (_, k) => k + d)
arr.push(p.map( e => q.map(f => ([e, f]))).flat())
}
return arr
}
const n = 3
const crop = 4
const hsize = 2 // maximum of the height where to start cropping
const wsize = 2 // maximum of the width where to start cropping
// hsize = length_of_height_dimension - crop_size_over_height
// wsize = length_of_width_dimension - crop_size_over_width
const indices = tf.tensor( g(hsize, wsize, crop, n)).toInt()
const input = tf.tensor(Array.from({length: 64 * 3}, (_, k) => k +1), [8, 8, 3]);
tf.gatherND(input, indices).reshape([n, crop, crop, 3]).print()
<html>
<head>
<!-- Load TensorFlow.js -->
<script src="https://cdn.jsdelivr.net/npm/#tensorflow/tfjs#latest"> </script>
</head>
<body>
</body>
</html>

Computing the gradient of the loss using Tensorflow.js

I am trying to compute the gradient of a loss, with relation to a network's trainable weights using Tensorflow.js in order to apply these gradients to my network's weight. In python, this is easily done using the tf.gradients() functions, which takes two minimum inputs representing dx and dy.
However, I am not able to reproduce the behavior in Tensorflow.js. I am not sure wether my understanding of the gradient of the loss w.r.t the weights is wrong, or if my code contains mistakes.
I have spent some time analysing the core code of the tfjs-node package to understand how it is done when we call the function tf.model.fit(), but with little success so far.
let model = build_model(); //Two stacked dense layers followed by two parallel dense layers for the output
let loss = compute_loss(...); //This function returns a tf.Tensor of shape [1] containing the mean loss for the batch.
const f = () => loss;
const grad = tf.variableGrads(f);
grad(model.getWeights());
The model.getWeights() function returns an array of tf.variable(), so I assumed the function would compute dL/dW for each layer, which I could apply later to my network's weights, however, that's not quite the case as I get this error :
Error: Cannot compute gradient of y=f(x) with respect to x. Make sure that the f you passed encloses all operations that lead from x to y.
I don't quite understand what does this error means.
How am I supposed to compute the gradient (analog to tf.gradients() in Python) of the loss using Tensorflow.js then ?
Edit :
This is the function computing the loss :
function compute_loss(done, new_state, memory, agent, gamma=0.99) {
let reward_sum = 0.;
if(done) {
reward_sum = 0.;
} else {
reward_sum = agent.call(tf.oneHot(new_state, 12).reshape([1, 9, 12]))
.values.flatten().get(0);
}
let discounted_rewards = [];
let memory_reward_rev = memory.rewards;
for(let reward of memory_reward_rev.reverse()) {
reward_sum = reward + gamma * reward_sum;
discounted_rewards.push(reward_sum);
}
discounted_rewards.reverse();
let onehot_states = [];
for(let state of memory.states) {
onehot_states.push(tf.oneHot(state, 12));
}
let init_onehot = onehot_states[0];
for(let i=1; i<onehot_states.length;i++) {
init_onehot = init_onehot.concat(onehot_states[i]);
}
let log_val = agent.call(
init_onehot.reshape([memory.states.length, 9, 12])
);
let disc_reward_tensor = tf.tensor(discounted_rewards);
let advantage = disc_reward_tensor.reshapeAs(log_val.values).sub(log_val.values);
let value_loss = advantage.square();
log_val.values.print();
let policy = tf.softmax(log_val.logits);
let logits_cpy = log_val.logits.clone();
let entropy = policy.mul(logits_cpy.mul(tf.scalar(-1)));
entropy = entropy.sum();
let memory_actions = [];
for(let i=0; i< memory.actions.length; i++) {
memory_actions.push(new Array(2000).fill(0));
memory_actions[i][memory.actions[i]] = 1;
}
memory_actions = tf.tensor(memory_actions);
let policy_loss = tf.losses.softmaxCrossEntropy(memory_actions.reshape([memory.actions.length, 2000]), log_val.logits);
let value_loss_copy = value_loss.clone();
let entropy_mul = (entropy.mul(tf.scalar(0.01))).mul(tf.scalar(-1));
let total_loss_1 = value_loss_copy.mul(tf.scalar(0.5, dtype='float32'));
let total_loss_2 = total_loss_1.add(policy_loss);
let total_loss = total_loss_2.add(entropy_mul);
total_loss.print();
return total_loss.mean();
}
EDIT 2:
I managed to use the compute_loss as the loss function specified on model.compile(). But then, it is required that it takes only two inputs (predictions, labels), so it's not working out for me, as I want to input multiple parameters.
I am trully lost on the matter.
The error says it all.
Your issue has to do with tf.variableGrads. loss should be a scalar computed using all available tf tensors operators. loss should not return a tensor as indicated in your question.
Here is an example of what loss should be:
const a = tf.variable(tf.tensor1d([3, 4]));
const b = tf.variable(tf.tensor1d([5, 6]));
const x = tf.tensor1d([1, 2]);
const f = () => a.mul(x.square()).add(b.mul(x)).sum(); // f is a function
// df/da = x ^ 2, df/db = x
const {value, grads} = tf.variableGrads(f); // gradient of f as respect of each variable
Object.keys(grads).forEach(varName => grads[varName].print());
/!\ Notice that the gradient is calculated as respect of variables created using tf.variable
Update:
You're not computing the gradients as it should be. Here is the fix.
function compute_loss(done, new_state, memory, agent, gamma=0.99) {
const f = () => { let reward_sum = 0.;
if(done) {
reward_sum = 0.;
} else {
reward_sum = agent.call(tf.oneHot(new_state, 12).reshape([1, 9, 12]))
.values.flatten().get(0);
}
let discounted_rewards = [];
let memory_reward_rev = memory.rewards;
for(let reward of memory_reward_rev.reverse()) {
reward_sum = reward + gamma * reward_sum;
discounted_rewards.push(reward_sum);
}
discounted_rewards.reverse();
let onehot_states = [];
for(let state of memory.states) {
onehot_states.push(tf.oneHot(state, 12));
}
let init_onehot = onehot_states[0];
for(let i=1; i<onehot_states.length;i++) {
init_onehot = init_onehot.concat(onehot_states[i]);
}
let log_val = agent.call(
init_onehot.reshape([memory.states.length, 9, 12])
);
let disc_reward_tensor = tf.tensor(discounted_rewards);
let advantage = disc_reward_tensor.reshapeAs(log_val.values).sub(log_val.values);
let value_loss = advantage.square();
log_val.values.print();
let policy = tf.softmax(log_val.logits);
let logits_cpy = log_val.logits.clone();
let entropy = policy.mul(logits_cpy.mul(tf.scalar(-1)));
entropy = entropy.sum();
let memory_actions = [];
for(let i=0; i< memory.actions.length; i++) {
memory_actions.push(new Array(2000).fill(0));
memory_actions[i][memory.actions[i]] = 1;
}
memory_actions = tf.tensor(memory_actions);
let policy_loss = tf.losses.softmaxCrossEntropy(memory_actions.reshape([memory.actions.length, 2000]), log_val.logits);
let value_loss_copy = value_loss.clone();
let entropy_mul = (entropy.mul(tf.scalar(0.01))).mul(tf.scalar(-1));
let total_loss_1 = value_loss_copy.mul(tf.scalar(0.5, dtype='float32'));
let total_loss_2 = total_loss_1.add(policy_loss);
let total_loss = total_loss_2.add(entropy_mul);
total_loss.print();
return total_loss.mean().asScalar();
}
return tf.variableGrads(f);
}
Notice that you can quickly run into a memory consumption issue. It will advisable to surround the function differentiated with tf.tidy to dispose of the tensors.

How to search an input tag value from a series of arrays

I am trying to pull information from an array that consists of a number of other arrays.
The problem I am trying to solve is I am trying to pull a number from a range located in a variety of arrays and am having trouble doing so.
Here is my JS code
I made a range() function with Javascript that lists the (start, end) of the parameters included
function rangeSCORE(start, end) {
return Array(end - start + 1).fill().map((_, idx) => start + idx)
}
const score = document.getElementById('score').value;
const levelOneScore = rangeSCORE(486, 489);
const levelTwoScore = rangeSCORE(490, 493);
const levelThreeScore = rangeSCORE(494, 497);
const levelFourScore = rangeSCORE(498, 501);
const levelFiveScore = rangeSCORE(502, 505);
const levelSixthScore = rangeSCORE(506, 509);
const levelSeventhScore = rangeSCORE(510, 513);
const levelEightScore = rangeSCORE(514, 517);
const levelNinthScore = rangeSCORE(518, 528);
const ScoreValue = [levelOneScore, levelTwoScore, levelThreeScore, levelFourScore, levelFiveScore, levelSixthScore, levelSeventhScore, levelEightScore, levelNinthScore];
function showChance() {
if(ScoreValue.includes(score)) {
console.log(score)
}
}
here is my HTML code
<input type="number" class="transparentBar" min="472" max="528" id="score" placeholder="SCORE">
for example, if I input value and click a button I want to know how to search a list of arrays
You would benefit from a different data structure. You want to map a score to a level, so the easiest would be to create an array of which the indexes represent scores and the corresponding array value represents the level.
You could create such an array like this:
const scoreToLevel = [].concat(...
[485, 489, 493, 497, 501, 505, 509, 513, 517, 528].map((end, i, ends) =>
Array(end - (ends[i-1] || -1)).fill(i)
)
);
// I/O handling:
const scoreInput = document.getElementById('score');
const levelOutput = document.getElementById('level');
scoreInput.addEventListener("input", function showChance() {
levelOutput.textContent = scoreToLevel[this.value];
});
Score: <input type="number" class="transparentBar" min="472" max="528" id="score" placeholder="SCORE"><br>
Level: <span id="level"></span>
Note that here the start of the ranges is not specified as that seems to always be adjacent to the end of the previous range.
You define the rangeSCORE as 2D array. So try this and it will search for any score inside rangeSCORE.
function rangeSCORE(start, end) {
return Array(end - start + 1).fill().map((_, idx) => start + idx)
}
const levelOneScore = rangeSCORE(486, 489);
const levelTwoScore = rangeSCORE(490, 493);
const levelThreeScore = rangeSCORE(494, 497);
const levelFourScore = rangeSCORE(498, 501);
const levelFiveScore = rangeSCORE(502, 505);
const levelSixthScore = rangeSCORE(506, 509);
const levelSeventhScore = rangeSCORE(510, 513);
const levelEightScore = rangeSCORE(514, 517);
const levelNinthScore = rangeSCORE(518, 528);
const ScoreValue = [levelOneScore, levelTwoScore, levelThreeScore, levelFourScore, levelFiveScore, levelSixthScore, levelSeventhScore, levelEightScore, levelNinthScore];
function showChance() {
const score = document.getElementById('score').value;
for(var i=0;i<ScoreValue.length;i++)
{
for(var j=0;j<ScoreValue[i].length;j++){
if (ScoreValue[i][j]==score){
console.log(score);
console.log("level",i);
}
}
}
}
<input type="number" class="transparentBar" min="472" max="528" id="score" placeholder="SCORE" onchange=showChance()>

Constantly increasing memory usage when passing around huge arrays to webworker

I am currently doing some 3d modeling using babylonjs. I need to create a pressure map from given pressure at specific points. I am doing that using IDW. However this means that even with my map being a size of 70x90 grid requires me to have an array of 25200 (4 rgba values for each pixel) entries. Then this buffer is passed to a RawTexture for assigning it to a material, that is overlaid on the object
I am using a web worker, because I have to update the pressure values every 100ms and I don't want to block the main thread.The issue occurs when I am return that array (created in calculate function) from a service worker.
For some reason the memory usage just keeps going up, without stopping. It eventually goes up to around 1.5 gigabytes and I have to kill it.
The question : Is there any way to prevent this and what could be causing such high memory usage?
Worker:
// #flow
import { find, propEq, both } from 'ramda';
import { colorFromValue } from './color';
import { inverseDistance, distanceValues } from './math';
const findPoint = (x: number, y: number) =>
find(both(propEq('x', x), propEq('y', y)));
const distanceDict = {};
/* eslint-disable */
function calculate(options: Object, pList: Array<*>) {
const points = pList || [];
const { height, width } = options;
const gridWidth = width * 4;
const grid = new Uint8Array(options.width * options.height * 4);
for (let y = 0; y < height; y += 1) {
const rW = y * gridWidth;
for (let i = 0; i < gridWidth; i += 4) {
const index = i + rW;
const x = i / 4;
const dictKey = `${x}--${y}`;
let bottoms = distanceDict[dictKey];
if (bottoms === undefined) {
bottoms = distanceValues(points, x, y);
distanceDict[dictKey] = bottoms;
}
const point = findPoint(x, y)(points);
const value = point !== undefined && point !== null ?
point.value : inverseDistance(points, bottoms);
const color = colorFromValue(value);
grid[index] = color[0];
grid[index + 1] = color[1];
grid[index + 2] = color[2];
grid[index + 3] = 255;
}
}
return grid;
}
self.onmessage = (e) => {
const { points, options } = e.data;
const grid = calculate(options, points);
self.postMessage(grid.buffer, [grid.buffer]);
};
Painting:
modifyNodes = (points: Array<*>) => new Promise((res, rej) => {
this.worker.onmessage = (e) => {
this._texture.update(new Uint8Array(e.data));
res();
}
const data = {
options: this._options,
points,
};
this.worker.postMessage(data);
})
So it seems the issue was in the colorFromValue function that was memoized. Because the values had quite few decimal points it could create up to 9! new entries into cache, so it drove up the memory usage...

Categories

Resources