Thursday, 24 December 2020

Tensorflow automl model in react

I'm trying to move a tensorflow model from its original html into a react app (built with create-react-app).

My App.js looks like this:

import logo from './logo.svg';
import * as tf from "@tensorflow/tfjs";
// import { loadImageclassification } from "@tensorflow/tfjs";
import './App.css';
import * as automl from "@tensorflow/tfjs-automl";
// import model from './model.json';
// 'model.json'

function App() {

var loadFile = function(event) {
    var image = document.getElementById('output');
    image.src = URL.createObjectURL(event.target.files[0]);
  run();
};

async function run() {

      // const model = await tf.loadImageclassification('model.json');
      const model = await tf.automl.loadImageclassification('model.json');
        const image = document.getElementById('output');
        const predictions = await model.classify(image);
        //const predictions = predictions.sort(function(a, b){return a-b});
        console.log(predictions);

        // Show the resulting object on the page.
        const pre = document.getElementById('result');
        pre.textContent = JSON.stringify(predictions, null, 2);;
}

  return (
  <div className="App">
    <div className="hero-text">
      <h1>classifier</h1>
      <h3>Upload a picture to see what type it is! </h3>
      <p>
        <input type="file"  accept="image/*" name="image" id="file"  onChange={loadFile} />
      </p>
      <div id="demobox">
        <p>
          <label for="file">Upload your image</label>
        </p>
      </div> 
      <p><img id="output" width="200" alt="output" /></p>
      <div className="result" id="result">
      </div>
    </div>
  </div>
  );
}

export default App;

My index.html looks like this:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <meta name="theme-color" content="#000000" />
    <meta
      name="description"
      content="Web site created using create-react-app"
    />
    <link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
    <link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
  </head>
  <body>
    <noscript>You need to enable JavaScript to run this app.</noscript>
    <div id="root"></div>
    <script src="https://unpkg.com/@tensorflow/tfjs"></script>
    <script src="https://unpkg.com/@tensorflow/tfjs-automl"></script>
  </body>
</html>

However, when I try to npm start the app, I get some variation of this error:

Attempted import error: 'automl' is not exported from '@tensorflow/tfjs' (imported as 'tf').

I've tried changing around my import structure, to no avail, but I assume it's something obvious and basic that I'm doing wrong, so I figured there was a good shot that someone here might see the problem within a few seconds.

Edit:

I've hacked my way through a couple of the errors I was getting, and my App.js file currently looks like this (the only difference in my index.html file is that I removed the two <script src="...unpkg..."></script>s):

import logo from './logo.svg';
import * as tf from "@tensorflow/tfjs";
// import { loadImageclassification } from "@tensorflow/tfjs";
import './App.css';
import * as automl from "@tensorflow/tfjs-automl";
import * as modelJSON from './model.json';

function App() {

var loadFile = function(event) {
    var image = document.getElementById('output');
    image.src = URL.createObjectURL(event.target.files[0]);
  run();
};

async function run() {
  console.log(modelJSON);
        // const model = await tf.loadImageclassification('model.json');
        const model = await automl.loadImageClassification(modelJSON);
        const image = document.getElementById('output');
        const predictions = await model.classify(image);
        console.log(predictions);

        const pre = document.getElementById('result');
        pre.textContent = JSON.stringify(predictions, null, 2);
}

  return (
  <div className="App">
    <div className="hero-text">
      <h1>classifier</h1>
      <h3>Upload a picture to see what type it is! </h3>
      <p>
        <input type="file"  accept="image/*" name="image" id="file"  onChange={loadFile} />
      </p>
      <div id="demobox">
        <p>
          <label htmlFor="file">Upload your image</label>
        </p>
      </div> 
      <p><img id="output" width="200" alt="output" /></p>
      <div className="result" id="result">
      </div>
    </div>
  </div>
  );
}

export default App;

However, I am now getting the following error, which seems to be issuing from somewhere in the loadImageClassification method:

Unhandled Rejection (TypeError): modelUrl.lastIndexOf is not a function

And in the "view source" section of the error message is this:

  24 | 
  25 | /** Loads and parses the dictionary. */
  26 | export async function loadDictionary(modelUrl: string): Promise<string[]> {
> 27 |   const lastIndexOfSlash = modelUrl.lastIndexOf('/');
  28 |   const prefixUrl =
  29 |       lastIndexOfSlash >= 0 ? modelUrl.slice(0, lastIndexOfSlash + 1) : '';
  30 |   const dictUrl = `${prefixUrl}dict.txt`;

I'll keep blundering around, but maybe one of you will have mercy on me.

Edit 2:

Changed my run function to this:

async function run() {
  const modelUrl = "<MY_GITHUB_PAGES_URL_LINKING_TO_MODEL.JSSON>";
  const model = await automl.loadImageClassification(modelUrl);
  ...

Based on the information that loadImageClassification uses a fetch request under the hood and so requires a remote file (which is strange, because it seemed to work fine in the static index.html original version of this same project).

However, I am now getting the following 404 for the GET request to the https://myusername.github.io/model.json page:

404

Edit 3:

It appears github pages won't work for that, so I am now trying it just with a localhost express server, which at present looks like this:

const modelJSON = require('./model.json');

const express = require("express");
const bodyParser = require("body-parser");
const CORS = require("cors");

const app = express();

app.use(bodyParser.json());
app.use(CORS());

let modelObj = modelJSON;

app.get("/", (req, res) => {
  // console.log(modelObj);
  res.send(modelObj);
});

app.listen(5000, () => {
  console.log("Server listening on port 5000");
});

I can see the correct data when I navigate to localhost5000, but when I change

async function run() {
  const modelUrl = "<MY_GITHUB_PAGES_URL_LINKING_TO_MODEL.JSSON>";
  const model = await automl.loadImageClassification(modelUrl);
  ...

to

async function run() {
  const modelUrl = "http://localhost:5000/";
  const model = await automl.loadImageClassification(modelUrl);

I get these errors:

enter image description here

Edit 4:

Now I am getting these errors:

enter image description here

My server.js file looks like this:

enter image description here

So presumably the problem now has to do with the fact that loadImageClassification assumes that the ...shard__of6.bin and dict files are in the same directory as the model.json file.

Now the question is probably: how to simulate the file structure that it (i.e., loadImageClassification) is expecting with an express js server.

Fundamental confusion:

I'm just don't understand why, when loadImageClassification is in the original static html, it does not seem to require a remote url from which to fetch model.json — but then when I put it in my react app, it suddenly gives me this error: "Fetch API cannot load file:///Users///client/src/model.json. URL scheme must be 'http' or 'https' for CORS request."



from Tensorflow automl model in react

No comments:

Post a Comment