Uncaught (in promise) TypeError: Cannot read property 'length' of null

Viewed 1507

enter image description here

I was following the TensorFlow.js - Audio recognition using transfer learning tutorial. When I called the train() function by pressing the 'train' button, the error came as above. Was it me or was there a mistake in the tutorial?

(I did follow the guide step by step...and test it with a localhost in the latest version of chrome)

The first snippet is from index.js and the second one is from index.html.

let recognizer;

function predictWord() {
 // Array of words that the recognizer is trained to recognize.
 const words = recognizer.wordLabels();
 recognizer.listen(({scores}) => {
   // Turn scores into a list of (score,word) pairs.
   scores = Array.from(scores).map((s, i) => ({score: s, word: words[i]}));
   // Find the most probable word.
   scores.sort((s1, s2) => s2.score - s1.score);
   document.querySelector('#console').textContent = scores[0].word;
 }, {probabilityThreshold: 0.75});
}

async function app() {
 recognizer = speechCommands.create('BROWSER_FFT');
 await recognizer.ensureModelLoaded();
 console.log("The pre-trained model is loaded.");
 //predictWord();
 buildModel();
 console.log("The model is built.");
}

app();

// One frame is ~23ms of audio.
const NUM_FRAMES = 3;
let examples = [];

function collect(label) {
 if (recognizer.isListening()) {
   return recognizer.stopListening();
 }
 if (label == null) {
   return;
 }
 recognizer.listen(async ({spectrogram: {frameSize, data}}) => {
   let vals = normalize(data.subarray(-frameSize * NUM_FRAMES));
   examples.push({vals, label});
   document.querySelector('#console').textContent =
       `${examples.length} examples collected`;
 }, {
   overlapFactor: 0.999,
   includeSpectrogram: true,
   invokeCallbackOnNoiseAndUnknown: true
 });
}

function normalize(x) {
 const mean = -100;
 const std = 10;
 return x.map(x => (x - mean) / std);
}

const INPUT_SHAPE = [NUM_FRAMES, 232, 1];
let model;

async function train() {
 toggleButtons(false);
 const ys = tf.oneHot(examples.map(e => e.label), 3);
 console.log("line one in train() is executed successfully.");
 const xsShape = [examples.length, ...INPUT_SHAPE];
 console.log("line two in train() is executed successfully.");
 const xs = tf.tensor(flatten(examples.map(e => e.vals)), xsShape);
 console.log("line three in train() is executed sucessfully.");

 console.log(examples);

 await model.fit(xs, ys, {
   batchSize: 16,
   epochs: 10,
   callbacks: {
     onEpochEnd: (epoch, logs) => {
       document.querySelector('#console').textContent =
           `Accuracy: ${(logs.acc * 100).toFixed(1)}% Epoch: ${epoch + 1}`;
     }
   }
 });

console.log("The training is done !");

 tf.dispose([xs, ys]);
 toggleButtons(true);
}

function buildModel() {
 model = tf.sequential();
 model.add(tf.layers.depthwiseConv2d({
   depthMultiplier: 8,
   kernelSize: [NUM_FRAMES, 3],
   activation: 'relu',
   inputShape: INPUT_SHAPE
 }));
 model.add(tf.layers.maxPooling2d({poolSize: [1, 2], strides: [2, 2]}));
 model.add(tf.layers.flatten());
 model.add(tf.layers.dense({units: 3, activation: 'softmax'}));
 const optimizer = tf.train.adam(0.01);
 model.compile({
   optimizer,
   loss: 'categoricalCrossentropy',
   metrics: ['accuracy']
 });
}

function toggleButtons(enable) {
 document.querySelectorAll('button').forEach(b => b.disabled = !enable);
}

function flatten(tensors) {
 const size = tensors[0].length;
 const result = new Float32Array(tensors.length * size);
 tensors.forEach((arr, i) => result.set(arr, i * size));
 return result;
}

//Call buildModel() when the app loads:

async function moveSlider(labelTensor) {
 const label = (await labelTensor.data())[0];
 document.getElementById('console').textContent = label;
 if (label == 2) {
   return;
 }
 let delta = 0.1;
 const prevValue = +document.getElementById('output').value;
 document.getElementById('output').value =
     prevValue + (label === 0 ? -delta : delta);
}

function listen() {
 if (recognizer.isListening()) {
   recognizer.stopListening();
   toggleButtons(true);
   document.getElementById('listen').textContent = 'Listen';
   return;
 }
 toggleButtons(false);
 document.getElementById('listen').textContent = 'Stop';
 document.getElementById('listen').disabled = false;

 recognizer.listen(async ({spectrogram: {frameSize, data}}) => {
   const vals = normalize(data.subarray(-frameSize * NUM_FRAMES));
   const input = tf.tensor(vals, [1, ...INPUT_SHAPE]);
   const probs = model.predict(input);
   const predLabel = probs.argMax(1);
   await moveSlider(predLabel);
   tf.dispose([input, probs, predLabel]);
 }, {
   overlapFactor: 0.999,
   includeSpectrogram: true,
   invokeCallbackOnNoiseAndUnknown: true
 });
}
<html>
  <head>
    <script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs"></script>
    <script src="https://cdn.jsdelivr.net/npm/@tensorflow-models/speech-commands">             </script>
  </head>
  <body>
    <button id="left" onmousedown="collect(0)" onmouseup="collect(null)">a</button>
    <button id="right" onmousedown="collect(1)" onmouseup="collect(null)">o</button>
    <button id="noise" onmousedown="collect(2)" onmouseup="collect(null)">Noise</button>
    <br/><br/>
    <button id="train" onclick="train()">Train</button>
    <br/><br/>
    <button id="listen" onclick="listen()">Listen</button>
    <input type="range" id="output" min="0" max="10" step="0.1">
    <div id="console"></div>
    <script src="index.js"></script>
  </body>
</html>

2 Answers

It seems the tutorial was written before TensorFlow.js 2.0 came out, but since the version isn't specified in the jsDelivr link it's loading the latest 2.x version.

It seems to work if you specify a 1.x version, so for example you could replace:

<script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs"></script>

with:

<script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs@1.7.4"></script>

The error printed to console should indicate some lines of the code you've run, but if not there's not much information to go off of.

As a first guess, it looks like you need to run buildModel() first to initialize the model variable. Currently it's initialized to undefined.

Related