Tensorflow.js predictClass 未返回 classIndex (NaN)

Tensorflow.js predictClass not returning classIndex (NaN)

我正在尝试将 "Teachable machine" 用于自己的目的。我想将它用作训练模型的样板,该模型对来自两个 类 的图像进行分类。开始使用 github 中的示例 - https://github.com/googlecreativelab/teachable-machine-boilerplate

更改了一些内容以在 nodeJS 上使用本地图像。解决了随之而来的一些问题和错误。现在它似乎没有错误地工作,但它没有预测任何东西。

来自 git 的示例非常容易理解,我几乎按原样使用它(除了加载图像不是来自网络摄像头,而是来自文件)。

部分在训练每张图像后立即输出:

Processed image cat.10.jpg
{ classIndex: -1, confidences: { '1': NaN } }

来到另一个目录后:

Processed image dog.1.jpg
{ classIndex: -1, confidences: { '1': 0, '2': NaN } }

当涉及到预测时,它输出这个:

EXAMPLES:
{ '1': 14, '2': 14 }
PREDICTING
{ classIndex: -1, confidences: { '1': 0, '2': NaN } }
{ classIndex: -1, confidences: { '1': 0, '2': NaN } }

图像大小调整为 227x227 玩过更大的火车数据集(最多 50 张照片),但这没有任何意义,示例甚至从 1 张照片开始工作。 尝试更改 TOPK 值 - 没有成功。

getOffsets - 这只是一个函数,如果图像有其他尺寸,则仅使用图像的中心 227x227 部分。也不是问题。

setTimeout - 只是一个蹩脚的解决方案,它帮助我在训练后进行 运行 预测。不知道为什么它不等待它,但它有帮助。至于它说的例子数是正确的 - 我假设训练是完整的。

你们知道我做错了什么吗?

const tf = require("@tensorflow/tfjs");
require("@tensorflow/tfjs-node");
const mobilenetModule = require("@tensorflow-models/mobilenet");
const knnClassifier = require("@tensorflow-models/knn-classifier");
const fs = require("fs");
const { createCanvas, Image } = require("canvas");

global.fetch = require("node-fetch");

class NN {
  constructor() {
    this.IMAGE_SIZE = 227;
    this.TOPK = 10;

    this.loadModel();
  }
  async loadModel() {
    this.knn = await knnClassifier.create();
    this.mobilenet = await mobilenetModule.load();

    await this.trainData();
    await this.predictData();
  }

  async predictData() {
    setTimeout(() => {
      const exampleCount = this.knn.getClassExampleCount();
      console.log(`EXAMPLES:`);
      console.log(exampleCount);

      console.log("PREDICTING");
      this.predictPath("./dataset/test/a");
    }, 0);
  }

  async trainData() {
    await this.trainPath("./dataset/train/a", 1);
    console.log("TRAINED A");
    await this.trainPath("./dataset/train/b", 2);
    console.log("TRAINED B");
  }

  async trainPath(path, idx) {
    await fs.readdir(path, async (err, imageNames) => {
      for (let i = 0; i < imageNames.length; i++) {
        const img = await this.processImage(`${path}/${imageNames[i]}`);
        const imgTf = tf.fromPixels(img);

        const inferLocal = img => this.mobilenet.infer(img, "conv_preds");
        const logits = inferLocal(imgTf);

        this.knn.addExample(logits, idx);

        imgTf.dispose();
        if (logits != null) {
          logits.dispose();
        }
        console.log(`Processed image ${imageNames[i]}`);

        // Try to predict after adding
        const numClasses = this.knn.getNumClasses();
        if (numClasses > 0) {
          const prediction = await this.predictImage(
            `${path}/${imageNames[i]}`
          );
          console.log(prediction);
        }
      }
    });
  }

  async predictPath(path) {
    fs.readdir(path, async (err, imageNames) => {
      for (let i = 0; i < imageNames.length; i++) {
        const prediction = await this.predictImage(`${path}/${imageNames[i]}`);
        console.log(prediction);
      }
    });
  }

  async predictImage(imagePath) {
    const img = await this.processImage(imagePath);
    const imgTf = tf.fromPixels(img);
    const inferLocal = () => this.mobilenet.infer(imgTf, "conv_preds");
    const logits = inferLocal();
    const prediction = await this.knn.predictClass(logits, this.TOPK);
    imgTf.dispose();
    if (logits != null) {
      logits.dispose();
    }
    return prediction;
  }

  async processImage(imagePath) {
    const canvas = createCanvas(this.IMAGE_SIZE, this.IMAGE_SIZE);
    const ctx = canvas.getContext("2d");
    const img = new Image();

    const promise = new Promise((resolve, reject) => {
      img.crossOrigin = "";
      img.onload = () => {
        const { x, y } = this.getOffsets(img);
        ctx.drawImage(img, x, y);
        resolve(canvas);
      };
    });

    img.src = `${imagePath}`;
    return promise;
  }

  getOffsets(img) {
    let x, y;
    if (Number(img.width) > this.IMAGE_SIZE) {
      x = -(img.width - this.IMAGE_SIZE) / 2;
    } else {
      x = (this.IMAGE_SIZE - img.width) / 2;
    }

    if (Number(img.height) > this.IMAGE_SIZE) {
      y = -(img.height - this.IMAGE_SIZE) / 2;
    } else {
      y = (this.IMAGE_SIZE - img.height) / 2;
    }

    return { x, y };
  }
}

new NN();

啊啊啊啊。看起来我解决了它。我们不能使用 1 中的 class 索引(我使用了 1 和 2) 他们需要从0开始。

await this.trainPath("./dataset/train/a", 0);
await this.trainPath("./dataset/train/b", 1);