TensorflowJS with earlyStopping and Training Logs does not work - tensorflow.js

TensorflowJS does not seem to work when we define early stopping and a training log function at the same time. The examples above is taken from the TensorflowJS docs, I just added the onTrainBegin callback -- but it fails.
const model = tf.sequential();
model.add(tf.layers.dense({
units: 3,
activation: 'softmax',
kernelInitializer: 'ones',
inputShape: [2]
}));
const xs = tf.tensor2d([1, 2, 3, 4], [2, 2]);
const ys = tf.tensor2d([[1, 0, 0], [0, 1, 0]], [2, 3]);
const xsVal = tf.tensor2d([4, 3, 2, 1], [2, 2]);
const ysVal = tf.tensor2d([[0, 0, 1], [0, 1, 0]], [2, 3]);
model.compile(
{loss: 'categoricalCrossentropy', optimizer: 'sgd', metrics: ['acc']});
const onTrainBegin = function onTrainBegin(logs){
console.log("onTrainBegin");
}
// Without the EarlyStopping callback, the val_acc value would be:
// 0.5, 0.5, 0.5, 0.5, ...
// With val_acc being monitored, training should stop after the 2nd epoch.
const history = await model.fit(xs, ys, {
epochs: 10,
validationData: [xsVal, ysVal],
callbacks: [onTrainBegin, tf.callbacks.earlyStopping({monitor: 'val_acc'})]
});
// Expect to see a length-2 array.
console.log(history.history.val_acc);
This code yields the error message:
An error occured this.getMonitorValue is not a function
https://js.tensorflow.org/api/latest/#callbacks.earlyStopping

You are mixing different things. OntrainBegin specifies when the callback function is to be executed and tf.callbacks.earlyStopping({monitor: 'val_acc'}) is a function
(async() => {
const model = tf.sequential();
model.add(tf.layers.dense({
units: 3,
activation: 'softmax',
kernelInitializer: 'ones',
inputShape: [2]
}));
const xs = tf.tensor2d([1, 2, 3, 4], [2, 2]);
const ys = tf.tensor2d([[1, 0, 0], [0, 1, 0]], [2, 3]);
const xsVal = tf.tensor2d([4, 3, 2, 1], [2, 2]);
const ysVal = tf.tensor2d([[0, 0, 1], [0, 1, 0]], [2, 3]);
model.compile(
{loss: 'categoricalCrossentropy', optimizer: 'sgd', metrics: ['acc']});
const onTrainBegin = logs => {
console.log("onTrainBegin");
}
// Without the EarlyStopping callback, the val_acc value would be:
// 0.5, 0.5, 0.5, 0.5, ...
// With val_acc being monitored, training should stop after the 2nd epoch.
const history = await model.fit(xs, ys, {
epochs: 10,
validationData: [xsVal, ysVal],
callbacks: [
tf.callbacks.earlyStopping({monitor: 'val_acc'}), new tf.CustomCallback({
onEpochEnd: onTrainBegin()}),
]
});
// Expect to see a length-2 array.
console.log(history.history.val_acc);
})()
<html>
<head>
<!-- Load TensorFlow.js -->
<script src="https://cdn.jsdelivr.net/npm/#tensorflow/tfjs#latest"> </script>
</head>
<body>
</body>
</html>

Below an example of the working code for using earlyStopping and tf.CustomCallback in model training in tfjs.
await model.fitDataset(convertedTrainingData,
{epochs: 50,
validationData: convertedTestingData,
callbacks:[
new tf.CustomCallback({
onEpochEnd: async(epoch, logs) =>{
acc = logs.acc;
console.log("Epoch: " + epoch
+ " Loss: " + logs.loss.toFixed(4)
+ " Accuracy: " + logs.acc.toFixed(4)
+ " Val Loss: " + logs.val_loss.toFixed(4)
+ " Val Accuracy: " + logs.val_acc.toFixed(4));
},
onTrainEnd: async() =>{
console.log("training done");
if (acc>0.4) {
repeat = false;
console.log(repeat);
}
}
}),
tf.callbacks.earlyStopping({monitor: 'loss'})
]});

Related

Checking if any element in one array exists in another

I am trying to create a filtering system for some items by checking if the selected items exist in the presented values.
So my selectable array can range from 0 to 6, and each item contains an array of Ints that it is associated with:
let items = [
Item(cat: [1, 2, 3]),
Item(cat: [0, 6]),
Item(cat: []),
Item(cat: [0, 1])
]
I wanted to create a function that would check if any of the cat values were in the selected Ints:
#Published var filteredCats: [Int] = []
func filterByInt(array: [Item]) -> [Item] {
let output = array.filter({
guard let cats = $0.cats else { return true }
for cat in cats {
return filteredCats.contains(cat)
}
})
return output
}
But I'm having issue with the above since it returns in the loop on the first iteration, so if I was searching for 1 in the above items then Item(cat: [0, 1]) exits at false as the first looped check is 0==1.
Essentially I want to be able to do the following (in expanded terms):
let filter = [0, 3, 4]
let items = [
[1, 2, 3],
[2, 3],
[],
[5, 6, 7]
]
items.contains(filter) // --> return the arrays
Sorry if this is basic but I've been trying to find a good solution.
Checking uniqueness is where a Set can help
struct Item {
var cat: Set<Int>
}
let items = [
Item(cat: [1, 2, 3]),
Item(cat: [0, 6]),
Item(cat: []),
Item(cat: [0, 1])
]
let wanted: Set<Int> = [0, 3, 4]
let filtered = items.filter { !$0.cat.isDisjoint(with: wanted) }
I'd suggest someSatisfy (or any in Kotlin):
We can utilize allSatisfy with double negations (one for allSatisfy, the other for contains) for that:
struct Item: CustomDebugStringConvertible {
let cat: [Int]
var debugDescription: String {
"[" + cat.map { String($0) }.joined(separator: ", ") + "]"
}
}
func filterByInt(items: [Item], filter: [Int]) -> [Item] {
// Early return might make sense if the filter is empty.
// guard !filter.isEmpty else { return items }
items.filter { item in
!filter.allSatisfy {
filterItem in !item.cat.contains(filterItem)
}
}
}
let filter = [0, 3, 4]
let items: [Item] = [
.init(cat: [1, 2, 3]),
.init(cat: [2, 3]),
.init(cat: []),
.init(cat: [5, 6, 7])
]
print(filterByInt(items: items, filter: filter))
// [[1, 2, 3], [2, 3]]
print(filterByInt(items: items, filter: [5]))
// [[5, 6, 7]]
print(filterByInt(items: items, filter: []))
// []

Filter an object of objects by key

I'm having an object like this
{
"GroupA": {
"Parent1": [1, 2, 3],
"Parent2": [1, 2, 3],
"Parent12": [1, 2, 3]
},
"GroupB": {
"Parent13": [1, 2, 3],
"Parent5": [1, 2, 3]
},
"GroupC": {
"Parent7": [1, 2, 3]
}
}
Now i want to filter this object by searching the name of the Parent
For example when I search parent1 the result should be
{
"GroupA": {
"Parent1": [1, 2, 3],
"Parent12": [1, 2, 3]
},
"GroupB": {
"Parent13": [1, 2, 3],
}
}
Here is my solution but it's not working correctly if a Group has many similar Parent name it only return the first one. And when I try to set state it filter like all wrong value
let newData = []
let catalogArr = Object.entries(catalogList)
const handleSearchElement = (e) => {
const value = e.target.value
catalogArr.forEach(catalog => {
let filteredKeys = Object.keys(catalog[1]).filter(name => name.toLowerCase().includes(value.toLowerCase()))
let valuesOfKey
if(filteredKeys[0]) {
valuesOfKey = {
[filteredKeys[0]]: Object.values(catalog[1][filteredKeys[0]])
}
newData.push([catalog[0], {...valuesOfKey}])
}
})
console.log(Object.fromEntries(newData));
setCatalogList(Object.fromEntries(newData))
// console.log(catalogList);
}
You can use Array#reduce to accomplish this pretty easily, however, all of the packing and unpacking of objects using Object.entries and Object.fromEntries to essentially treat them as arrays suggests you may be using the wrong data structure.
If you need to do this repeatedly, look into doing a one-off transformation that arranges the data for O(1) access, for example, by grouping on inner keys rather than outer keys (hard to say since I don't know the data or use case). Or, if you're mostly iterating, consider using arrays.
const data = {
"GroupA": {
"Parent1": [1, 2, 3],
"Parent2": [1, 2, 3],
"Parent12": [1, 2, 3]
},
"GroupB": {
"Parent13": [1, 2, 3],
"Parent5": [1, 2, 3]
},
"GroupC": {
"Parent7": [1, 2, 3]
}
};
const targetKey = "parent1";
const res = Object.entries(data).reduce((a, [k, v]) => {
const filtered = Object.entries(v).filter(([k, ]) =>
k.toLowerCase().includes(targetKey.toLowerCase())
);
if (filtered.length) {
a[k] = Object.fromEntries(filtered);
}
return a;
}, {});
console.log(res);

Group array items using underscore

I have six items in my array: [1, 2, 3, 4, 5, 6]. I would like to group array items per 3 items like that: [[1, 2, 3], [4, 5, 6]]. Is it possible with underscore?
You can use array#reduce to group your array element.
const arr = [1, 2, 3, 4, 5, 6],
group = 3,
result = arr.reduce((r,v,i) => {
let index = Math.floor(i/group);
(r[index] = r[index] || [])[i%group] = v;
return r;
},[]);
console.log(result);

How to find segments of close values in an array?

How to implement an algorithm that finds segments of continous close values in an array?
Let's say that we start with the following array:
array = [5, 6, 4, 0, 5, 10, 12, 10, 11, 4, 3, 2, 3, 2, 1, 3, 1, 0, 2]
Let's also assume that we will tolerate a "closeness" in each segment of maximum value of 3.
The segments produced would be something like the following:
seg[0] = [5, 6, 4]
seg[1] = [0]
seg[2] = [5]
seg[3] = [10, 12, 10, 11]
seg[4] = [4, 3, 2, 3, 2]
seg[5] = [1, 3, 1]
seg[6] = [0, 2]
Also, we could allow a "dropout" of, say, maximum one continous value that would fall out of the current segment but would be included if the following values fit in the segment. This would produce something like:
seg[0] = [5, 6, 4, 0, 5] //include 0 as a "dropout"
seg[1] = [10, 12, 10, 11]
seg[3] = [4, 3, 2, 3, 2, 1, 3] // include 1 as a "dropout"
seg[4] = [1, 3, 1, 0, 2] // include 0 as a "droput"
Ideas on how to implement such an algorithm?
Here's my own take on it. Written in Haxe (haxe.org).
The checkDropout() method checks for "dropouts" of n length, and "normalizes" them away to an average number of the current segment.
using Lambda;
class Example {
static function main() {
var a:Array<Float> = [5, 6, 4, 0, 5, 10, 12, 10, 11, 4, 3, 2, 3, 2, 1, 3, 1, 0, 2];
// Segment value tolerance of 2
// Dropout max length of 3
var segmentor = new Segmentor(2, 3);
// Callback for displaying result
segmentor.displayResult = (r, v) -> {
trace('-------------------------------------');
trace('value added: ' + v);
trace('result: ' + r);
}
// Feed the segmentor with the example values
a.map(i -> segmentor.addNumber(i));
// var result = [
// [5, 6, 4, 5, 5],
// [10, 12, 10, 11],
// [4, 3, 2, 3, 2],
// [1, 3, 1],
// [0, 2]
// ];
}
}
class Segmentor {
public function new(valueSpan:Int = 2, dropoutLength:Int = 1) {
this.valueSpan = valueSpan;
this.dropoutLength = dropoutLength;
this.result = [];
this.result.push([]);
}
var dropoutLength:Int = null;
var valueSpan:Int = null;
public var result(default, null):Array<Array<Float>>;
public function addNumber(v:Float) {
this.processNumber(v);
}
function processNumber(v:Float) {
var current = this.result[this.result.length - 1];
if (current.length == 0) {
current.push(v);
} else if (fitIn(current, v) || this.checkDropout(v)) {
var current2 = this.result[this.result.length - 1];
current2.push(v);
} else {
this.result.push([v]);
}
this.displayResult(this.result, v);
}
dynamic public function displayResult(result:Array<Array<Float>>, valueAdded:Float) {}
function checkDropout(v:Float):Bool {
if (this.result.length <= 1)
return false;
var last = this.result[this.result.length - 1];
if (last.length <= this.dropoutLength) {
var prev = this.result[this.result.length - 2];
if (fitIn(prev, v)) {
var mid = mid(prev);
var newLast = [for (i in 0...last.length) mid];
newLast.map(n -> prev.push(n));
this.result.pop();
return true;
}
}
return false;
}
function fitIn(a:Array<Float>, v:Float):Bool {
var max = a.fold((c, v) -> Math.max(v, c), a[0]);
var min = a.fold((c, v) -> Math.min(v, c), a[0]);
var fit = ((max - v <= this.valueSpan) && (v - min <= this.valueSpan));
return fit;
}
function mid(a:Array<Float>) {
var max = a.fold((c, v) -> Math.max(v, c), a[0]);
var min = a.fold((c, v) -> Math.min(v, c), a[0]);
return min + (max - min) / 2;
}
}

How to get the value of the element i, j of a tensor

I have a 2d tensor and I would like to get the value of the element of index i,j value.
There are many ways one can retrieve the value of the element [i,j] of a tensor2d
Consider the following:
Using slice to retrieve directly the tensor2d starting at the coordinate [i, j] that has the size [1, 1]
h.slice([i, j], 1).as1D().print()
Get the row i as a tensor2d with gather and then the element j with slice
h.gather(tf.tensor1d([i], 'int32')).slice([0, j], [1, 1]).as1D().print()
Using stack to retrieve the row i as tensor1d and slice to retrieve the desired element
h.unstack()[i].slice([j], [1]).print()
const h = tf.tensor2d([45, 48, 45, 54, 5, 7, 8, 10, 54], [3, 3]);
// get the element of index [1, 2]
h.print()
h.gather(tf.tensor1d([1], 'int32')).slice([0, 2], [1, 1]).as1D().print()
h.slice([1, 2], 1).as1D().print()
h.unstack()[1].slice([2], [1]).print()
<html>
<head>
<!-- Load TensorFlow.js -->
<script src="https://cdn.jsdelivr.net/npm/#tensorflow/tfjs#0.12.0"> </script>
</head>
<body>
</body>
</html>
If the goal is to get the element [i, j] in order to use it in other tensor computation like divide/multiply the matrix by the element, you will need to convert the element to a scalar.
h.slice([i, j], 1).as1D().asScalar()
If you want to return that value to a javascript variable (of type number), then you will need dataSync() or data() as described in this answer
h.slice([i, j], 1).as1D().dataSync()[0]
// or
const data = await h.slice([i, j], 1).as1D().data()
const h = tf.tensor2d([45, 48, 45, 54, 5, 7, 8, 10, 54], [3, 3]);
// get the element of index [1, 2]
h.print()
// sync method
const val = h.unstack()[1].slice([2], [1]).dataSync()
console.log(val[0]);
// async method
(async () => {
const val = await h.slice([1, 2], 1).as1D().data()
console.log(val[0])
})()
<html>
<head>
<!-- Load TensorFlow.js -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/tensorflow/0.12.4/tf.js"> </script>
</head>
<body>
</body>
</html>
You can use .dataSync() or if you can wait for it .data() to retrieve an 1d array containing all values of a tensor.
Now we only have to calculate an 1d-index from the 2d-coordinates using the formula:
index = rowlength * rownumber + columnnumber
The following code shows how to use each version.
Notice the async and await in the asynchronous method: async makes the function async, so we can use await to wait for another promise to resolve (.data() retuns a promise). Because an async function returns a promise we have to wait for it before logging it using .then()
function getValSync(t, i, j) {
const data = t.dataSync();
return data[t.shape[0] * j + i]; //Or *i+j, depending on what the dimension order is
}
async function getValAsync(t, i, j) {
const data = await t.data();
return data[t.shape[0] * j + i];
}
const t2d = tf.tensor2d([1, 2, 3, 4], [2, 2]);
t2d.print();
console.log("1,0:", getValSync(t2d, 1, 0));
console.log("1,1:", getValSync(t2d, 1, 1));
getValAsync(t2d, 0, 0).then(v => console.log("0,0:", v));
<script src="https://cdn.jsdelivr.net/npm/#tensorflow/tfjs#0.12.0">
</script>

Resources