1

I'm using Tensorflow.js to train and predict foods, the Web API to access my notebook webcam and Vue.js to create a simple page.

I have an infinite loop inside the addExample() method, the while(true), which is responsible for asking the model to predict what is in front of my webcam every frame and re-render the result.

I'd like to know if there is a problem in having an infinite loop inside a method in my Vue instance.

async addExample() {
  const selector = document.getElementById('classSelector');
  const player = document.getElementById('player');

  // Get the intermediate activation of MobileNet 'conv_preds' and pass that
  // to the KNN classifier.
  const activation = this.net.infer(player, 'conv_preds');

  // Pass the intermediate activation to the classifier
  this.classifier.addExample(activation, selector.selectedIndex);

  console.log("Class '" + selector.value + "' added to the model");

  while (true) {
    if (this.classifier.getNumClasses() > 0) {
      const activation = this.net.infer(player, 'conv_preds');

      const result = await this.classifier.predictClass(activation);
      this.confidence = result.confidences[result.classIndex];
      this.label = this.foodClasses[result.classIndex].name;
    }
    await tf.nextFrame();
  }
}

This method is triggered when I click the training button, but inside the method it stays in infinite loop. But everytime I need to train the same object or a new one, I have to trigger the method again, which results in entering again in the same loop - but I think the old one keeps running.

<button type="button" @click="addExample()">Train class</button>

In case you want to see the code in action, here is a fiddle

Edited: Thank you for the answers. I was able to solve my problem in the way I was expecting. Now, when I trigger the addExample() function, I have only one loop running, instead of having the old ones. I'm saying this based on a very shallow analysis of my GPU utilization percentage in the Task Manager.

In the old way, when I triggered addExample() more than one time, I could see the GPU percentage utilization raising more and more.

enter image description here

Now, the percentage utilization increases only one time.

enter image description here

This is the final code:

async addExample() {
  const selector = document.getElementById('classSelector');
  const player = document.getElementById('player');
  this.infiniteLoopControler += 1;
  const internalLoopControler = this.infiniteLoopControler;

  // Get the intermediate activation of MobileNet 'conv_preds' and pass that
  // to the KNN classifier.
  const activation = this.net.infer(player, 'conv_preds');

  // Pass the intermediate activation to the classifier
  this.classifier.addExample(activation, selector.selectedIndex);

  console.log("Class '" + selector.value + "' added to the model");

  while (internalLoopControler === this.infiniteLoopControler) {
    if (this.classifier.getNumClasses() > 0) {
      const activation = this.net.infer(player, 'conv_preds');

      const result = await this.classifier.predictClass(activation);
      this.confidence = result.confidences[result.classIndex];
      this.label = this.foodClasses[result.classIndex].name;
    }
    await tf.nextFrame();
  }
}

Thank you again for helping me!

  • You should try to focus your question on one specific problem. Right now, your text contains ~3 questions, the primary question is opinion-based and the "how to improve the code" is also off-topic, better suited for [Code Review](https://codereview.stackexchange.com/). – Thomas Dondorf Aug 01 '19 at 17:29
  • Yes, you are right. I actually have many questions and I tried to put everything in the same post, which is not ideal. Do you know if I can move a post from one Stack to another? – Arthur Brant Aug 01 '19 at 20:32
  • Modified my post to be more specific and concise about my concerns in my code. – Arthur Brant Aug 01 '19 at 20:54
  • 1
    Nice, it's a good question now! :) – Thomas Dondorf Aug 02 '19 at 08:21

2 Answers2

1

A simple way to remove the while(true) is to use recursion instead.

async addExample() {
  const selector = document.getElementById('classSelector');
  const player = document.getElementById('player');

  // Get the intermediate activation of MobileNet 'conv_preds' and pass that
  // to the KNN classifier.
  const activation = this.net.infer(player, 'conv_preds');

  // Pass the intermediate activation to the classifier
  this.classifier.addExample(activation, selector.selectedIndex);

  console.log("Class '" + selector.value + "' added to the model");


  const cb = () => {
    if (this.classifier.getNumClasses() > 0) {
      const activation = this.net.infer(player, 'conv_preds');

      const result = await this.classifier.predictClass(activation);
      this.confidence = result.confidences[result.classIndex];
      this.label = this.foodClasses[result.classIndex].name;
    }
    tf.nextFrame().then(cb);
  }

  cb();
}
Sebastian Speitel
  • 7,166
  • 2
  • 19
  • 38
  • What would be the advantage of this? IMHO your changes make the code less readable and do not improve it in any way. Maybe I'm missing something. – Thomas Dondorf Aug 01 '19 at 17:32
  • It's not necessarily better, just another way to do it. – Sebastian Speitel Aug 01 '19 at 18:05
  • My post was a bit confusing and it may have led you to misunderstand me - so I editted. I'm not sure if it's a good idea to use recursion (which is normally used to solve finite problems) in this case, because I really need to stay in an infinite loop. I think that staying in an infinite recursion can cause problems in stack memory. – Arthur Brant Aug 01 '19 at 21:02
  • As far as I could test right now, Promises don't recursively increase the call stack. Each `.then()` callback is invoked on the same depth. – Sebastian Speitel Aug 01 '19 at 21:23
  • 1
    I started trying your approach, but as soon as some issues related to accessing variables from outside the ```cb() ``` function - such as ```this.net``` and ```this.classifier``` - were raised, I decided to abort. And after thinking for a bit, your approach would still fall into the same hole that I was: when I trigger the ```addExample()``` multiple times to train the same object or a new one, I'm creating new infinite recursions while the old ones (that were activated in older trainings) are problably still running. – Arthur Brant Aug 04 '19 at 02:29
1

There is nothing wrong with using await inside a (infinite) loop. It is even better to use await than using .then as the stack trace does not need to be collected by the engine. For more information check out these topics:


If you need a way to stop the loop after it was started (when the button is clicked again), you could simply change your loop to check which iteration is currently active.

Code Sample

let iteration = 0;

async function addExample() {
  iteration += 1;
  const myIteration = iteration;
  while (myIteration === iteration) {
    // ...
  }
}

This is a (very much) simplified example. Each time addExample is called the iteration variable is increased and the current iteration run is stored. When the button is clicked a second time, the condition of the first iteration will not be met anymore and the (first) loop will stop.

Thomas Dondorf
  • 23,416
  • 6
  • 84
  • 105
  • Thank you @thomas! It solved my problem. As I was suspecting, my code was not performant. Your approach fixed that. – Arthur Brant Aug 04 '19 at 03:08