Tensorflow.js предиката класса не возвращает classIndex (NaN) - PullRequest
0 голосов
/ 06 января 2019

Я пытаюсь использовать «Обучаемую машину» для собственных целей. Я хочу использовать его как шаблон для модели обучения, которая классифицирует изображения из двух классов. Начал играть на примере с 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 Ответ

0 голосов
/ 07 января 2019

Daaaamn. Похоже, я решил это. Мы не можем использовать классовые индексы от 1 (я использовал 1 и 2) Их нужно начинать с 0.

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