tensorflow / tfjs

A WebGL accelerated JavaScript library for training and deploying ML models.
https://js.tensorflow.org
Apache License 2.0
18.52k stars 1.94k forks source link

TensorFlow JS Decision Forests inside Node.js #7805

Open fernandozet opened 1 year ago

fernandozet commented 1 year ago

Hello,

I'm new with TFDF. I'm trying to run the test model inside Node.js, but I keep getting the following error. Is there something I'm missing? Thank you.

Error: Can't read the data of 'the loaded zip file'. Is it in a supported JavaScript type (String, Blob, ArrayBuffer, etc) ? at \node_modules\jszip\dist\jszip.min.js:13:35888 at processTicksAndRejections (node:internal/process/task_queues:95:5)

const tf = require('@tensorflow/tfjs-node');
const tfdf = require('@tensorflow/tfjs-tfdf');
async getPrediction() {
  const tfdfModel = await tfdf.loadTFDFModel(
        'https://storage.googleapis.com/tfjs-testing/adult_gbt_no_regex/model.json',
      );
      const inputs = {
        age: tf.tensor1d([39, 40, 40, 35], 'int32'),
        workclass: tf.tensor1d(
          ['State-gov', 'Private', 'Private', 'Federal-gov'],
          'string',
        ),
        fnlwgt: tf.tensor1d([77516, 121772, 193524, 76845], 'int32'),
        education: tf.tensor1d(
          ['Bachelors', 'Assoc-voc', 'Doctorate', '9th'],
          'string',
        ),
        education_num: tf.tensor1d([13, 11, 16, 5], 'int32'),
        marital_status: tf.tensor1d(
          [
            'Never-married',
            'Married-civ-spouse',
            'Married-civ-spouse',
            'Married-civ-spouse',
          ],
          'string',
        ),
        occupation: tf.tensor1d(
          ['Adm-clerical', 'Craft-repair', 'Prof-specialty', 'Farming-fishing'],
          'string',
        ),
        relationship: tf.tensor1d(
          ['Not-in-family', 'Husband', 'Husband', 'Husband'],
          'string',
        ),
        race: tf.tensor1d(
          ['White', 'Asian-Pac-Islander', 'White', 'Black'],
          'string',
        ),
        sex: tf.tensor1d(['Male', 'Male', 'Male', 'Male'], 'string'),
        capital_gain: tf.tensor1d([2174, 0, 0, 0], 'int32'),
        capital_loss: tf.tensor1d([0, 0, 0, 0], 'int32'),
        hours_per_week: tf.tensor1d([40, 40, 60, 40], 'int32'),
        native_country: tf.tensor1d(
          ['United-States', '', 'United-States', 'United-States'],
          'string',
        ),
      };
      const densePredictions = await tfdfModel.executeAsync(inputs);
      console.log(densePredictions);
}
gaikwadrahul8 commented 1 year ago

Hi, @fernandozet

Thank you for bringing this issue to our attention, Could you please help us with package.json file which you're using currently to run your above code because it seems like there is some package dependancies compatibility issue at the moment ?

It downloads the converted TFJS model as a zip file.. Unzip this file before using it. An unzipped Tensorflow.js model consists of a number of files. The example model contains the following, I hope you have unzipped, if not please give it try by unzipping the files. Thank you!

assets.zip
group1-shard1of1.bin
model.json
fernandozet commented 1 year ago

Thank you for the quick response. This is my package.json: { "name": "tdf-test", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "start": "node index.js", "test": "echo \"Error: no test specified\" && exit 1" }, "author": "", "license": "ISC", "dependencies": { "@tensorflow/tfjs-node": "^4.8.0", "@tensorflow/tfjs-tfdf": "^0.0.1-alpha.8" } }

And my index.js :

const tf = require('@tensorflow/tfjs-node'); const tfdf = require('@tensorflow/tfjs-tfdf');

const getPrediction = async ()=> { const tfdfModel = await tfdf.loadTFDFModel( 'https://storage.googleapis.com/tfjs-testing/adult_gbt_no_regex/model.json', ); const inputs = { age: tf.tensor1d([39, 40, 40, 35], 'int32'), workclass: tf.tensor1d( ['State-gov', 'Private', 'Private', 'Federal-gov'], 'string', ), fnlwgt: tf.tensor1d([77516, 121772, 193524, 76845], 'int32'), education: tf.tensor1d( ['Bachelors', 'Assoc-voc', 'Doctorate', '9th'], 'string', ), education_num: tf.tensor1d([13, 11, 16, 5], 'int32'), marital_status: tf.tensor1d( [ 'Never-married', 'Married-civ-spouse', 'Married-civ-spouse', 'Married-civ-spouse', ], 'string', ), occupation: tf.tensor1d( ['Adm-clerical', 'Craft-repair', 'Prof-specialty', 'Farming-fishing'], 'string', ), relationship: tf.tensor1d( ['Not-in-family', 'Husband', 'Husband', 'Husband'], 'string', ), race: tf.tensor1d( ['White', 'Asian-Pac-Islander', 'White', 'Black'], 'string', ), sex: tf.tensor1d(['Male', 'Male', 'Male', 'Male'], 'string'), capital_gain: tf.tensor1d([2174, 0, 0, 0], 'int32'), capital_loss: tf.tensor1d([0, 0, 0, 0], 'int32'), hours_per_week: tf.tensor1d([40, 40, 60, 40], 'int32'), native_country: tf.tensor1d( ['United-States', '', 'United-States', 'United-States'], 'string', ), }; const densePredictions = await tfdfModel.executeAsync(inputs); console.log(densePredictions); }

getPrediction()

The current error message is: Error: Can't read the data of 'the loaded zip file'. Is it in a supported JavaScript type (String, Blob, ArrayBuffer, etc) ? at TFDF\node_modules\@tensorflow\tfjs-tfdf\dist\tf-tfdf.node.js:551:893 at process.processTicksAndRejections (node:internal/process/task_queues:95:5)

Please let me know if you need anything else. Thank you!

fernandozet commented 1 year ago

Hello @gaikwadrahul8,

Just wanted to double check if you needed anything else to test this. Thank you.

gaikwadrahul8 commented 1 year ago

Hi, @fernandozet

Apologize for the delayed response and I tried to replicate the same issue from my end and I'm getting the same error message and we'll dig more into this issue, will update you soon

Thank you for noticing this issue, I really appreciate your efforts and time. Thank you!

Here is error log for your reference :

gaikwadrahul-macbookpro:test-7805 gaikwadrahul$ node index.js
/Users/gaikwadrahul/Desktop/TFJS/test-7805/node_modules/@tensorflow/tfjs-tfdf/dist/tf-tfdf.node.js:1650
                throw e; }), process.on("unhandledRejection", function (e) { throw e; }), D = function (e, t) { throw process.exitCode = e, t; }, l.inspect = function () { return "[Emscripten Module object]"; }) : (S || T) && (T ? k = self.location.href : typeof document != "undefined" && document.currentScript && (k = document.currentScript.src), A && (k = A), k.indexOf("blob:") !== 0 ? k = k.substr(0, k.replace(/[?#].*/, "").lastIndexOf("/") + 1) : k = "", g = function (e) { var t = new XMLHttpRequest; return t.open("GET", e, !1), t.send(null), t.responseText; }, T && (E = function (e) { var t = new XMLHttpRequest; return t.open("GET", e, !1), t.responseType = "arraybuffer", t.send(null), new Uint8Array(t.response); }), _ = function (e, t, r) { var n = new XMLHttpRequest; n.open("GET", e, !0), n.responseType = "arraybuffer", n.onload = function () { if (n.status == 200 || n.status == 0 && n.response) {
                ^

Error: Can't read the data of 'the loaded zip file'. Is it in a supported JavaScript type (String, Blob, ArrayBuffer, etc) ?
    at /Users/gaikwadrahul/Desktop/TFJS/test-7805/node_modules/@tensorflow/tfjs-tfdf/dist/tf-tfdf.node.js:551:893
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)

Node.js v18.15.0
gaikwadrahul-macbookpro:test-7805 gaikwadrahul$ 
fernandozet commented 1 year ago

Hi @gaikwadrahul8 @mattsoulanille,

Just wanted to touch base with you to see if you were able to find a workaround for this issue. Thank you.

lucasc896 commented 1 year ago

I'm having a similar issue (report here]. Are there any updates on this? I was able to execute the example code in raw html (importing tfdf from the CDN)? cc @gaikwadrahul8 @mattsoulanille @fernandozet

benmag commented 1 year ago

Unfortunately, I appear to be having the exact same issue

Was able to execute the raw html version but nodejs fails

If I use a file:// the error message I get is


TypeError: fetch failed
    at Object.fetch (node:internal/deps/undici/undici:11576:11) {
  cause: Error: not implemented... yet...
      at makeNetworkError (node:internal/deps/undici/undici:6893:35)
      at schemeFetch (node:internal/deps/undici/undici:11029:18)
      at node:internal/deps/undici/undici:10909:26
      at mainFetch (node:internal/deps/undici/undici:10926:11)
      at fetching (node:internal/deps/undici/undici:10883:7)
      at fetch2 (node:internal/deps/undici/undici:10761:20)
      at Object.fetch (node:internal/deps/undici/undici:11574:18)
      at fetch (node:internal/process/pre_execution:252:25)
      at Object.<anonymous> (/Users/ben/Documents/git/src/cli/dist/tf-tfdf.node.js:3335:54)
      at step (/Users/ben/Documents/git/src/cli/dist/tf-tfdf.node.js:110:27)
}

And if I use a URL the error I get is the same

Error: Can't read the data of 'the loaded zip file'. Is it in a supported JavaScript type (String, Blob, ArrayBuffer, etc) ?
    at /Users/ben/Documents/git/src/cli/dist/tf-tfdf.node.js:551:893
    at processTicksAndRejections (node:internal/process/task_queues:95:5)

Am using "@tensorflow/tfjs-node": "^4.13.0". Attempted to npm install @tensorflow/tfjs-tfdf via npm but that threw peer dependency issues, so attempted to just download and use the package from here: https://www.jsdelivr.com/package/npm/@tensorflow/tfjs-tfdf?tab=files&path=dist

Unfortunately no luck finding any non-browser workaround yet

benmag commented 1 year ago

I've come up with a very hacky workaround in the mean time.

It's not pretty but sharing it in case it helps someone else who is desperate to get TFDF and Nodejs to play together

Basically the hack is to run it through the browser with puppeteer and extract the result.

1. expose the model e.g. my model is available at: http://localhost:8000/my-model/model.json

2. define the predict function in index.html this is the standard example from the docs. I've put it in a function called tftdPredict which will let us call it through puppeteer in the next step

<html>
<head>
    <script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs@4.5.0/dist/tf.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs-tfdf/dist/tf-tfdf.min.js"></script>
</head>
<body>
<script>
    async function tfdfPredict(inputs) {
        const model = await tfdf.loadTFDFModel("http://localhost:8000/my-model/model.json");

        // Map the inputs into tensors
        const outputObject = Object.fromEntries(
            Object.entries(inputs).map(([key, value]) => [key, tf.tensor(value)])
        );

        const result = await model.executeAsync(outputObject);
        return result.dataSync();
    }
</script>
</body>
</html>

3. call and extract the result with puppeteer this is the hacky solution. Puppeteer opens a browser, loads index.html and calls our function. The function will return the result from the model which we can then use in our nodejs code.

For me, I wanted my inputs to come from the nodejs side I've defined them in the inputs var and pass that through to the tfdfPredict which then maps them into tf.tensor's.

    // Launch the browser
    const browser = await puppeteer.launch();

    // Create a page
    const page = await browser.newPage();

    // Go to your site
    await page.goto('http://localhost:8000/index.html', {
        waitUntil: 'networkidle0'
    });

    // Create a mock function (evaluate will replace this with the actual function)
    const tfdfPredict = (inputs: any) => {};

    // Prepare inputs
    const inputs = {
        field1: [48.32207078227017],
        field2: [34.79555606459756],
        field3: [23.765891650440974],
        field4: [28.141106402473888],
        field5: [38.80426068033459],
        field6: [100],
        field7: [20.02061619516668]
    };

    // Call the predict function inside the page
    const result = await page.evaluate((inputs) => {
        return tfdfPredict(inputs);
    }, inputs);

    console.log(result); // We now have our result which we can use in node
gaikwadrahul8 commented 10 months ago

Hi, @fernandozet

I apologize for the delayed response and to confirm, did you follow the exact this Running TensorFlow Decision Forests models with TensorFlow.js tutorial ? if so I would suggest you to please follow this colab notebook Gist file to convert your saved TF-DF model to Tensorflow.js with tf.saved_model.save() format and see is it resolving your issue or not ?

If issue still persists please help us with error log and your Github repo with complete steps to replicate the same behavior from our end and investigate this issue further to solve it as soon as possible.

Thank you for your cooperation and patience.

benmag commented 10 months ago

Hi @gaikwadrahul8

I've just run the tutorial again with tf.saved_model.save() and am still getting an error. This error only happens when running it with nodejs. Running in the browser works fine

Error message

node index.js
/Users/ben/tfdf/node_modules/@tensorflow/tfjs-tfdf/dist/tf-tfdf.node.js:1650
                throw e; }), process.on("unhandledRejection", function (e) { throw e; }), D = function (e, t) { throw process.exitCode = e, t; }, l.inspect = function () { return "[Emscripten Module object]"; }) : (S || T) && (T ? k = self.location.href : typeof document != "undefined" && document.currentScript && (k = document.currentScript.src), A && (k = A), k.indexOf("blob:") !== 0 ? k = k.substr(0, k.replace(/[?#].*/, "").lastIndexOf("/") + 1) : k = "", g = function (e) { var t = new XMLHttpRequest; return t.open("GET", e, !1), t.send(null), t.responseText; }, T && (E = function (e) { var t = new XMLHttpRequest; return t.open("GET", e, !1), t.responseType = "arraybuffer", t.send(null), new Uint8Array(t.response); }), _ = function (e, t, r) { var n = new XMLHttpRequest; n.open("GET", e, !0), n.responseType = "arraybuffer", n.onload = function () { if (n.status == 200 || n.status == 0 && n.response) {
                ^

Error: Can't read the data of 'the loaded zip file'. Is it in a supported JavaScript type (String, Blob, ArrayBuffer, etc) ?
    at /Users/ben/tfdf/node_modules/@tensorflow/tfjs-tfdf/dist/tf-tfdf.node.js:551:893
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)

Node.js v20.4.0

Code

const tf = require('@tensorflow/tfjs-node');
const tfdf = require('@tensorflow/tfjs-tfdf');

async function run() {
    await tfdfPredict();
}

async function tfdfPredict() {
    const model = await tfdf.loadTFDFModel('http://localhost:8000/test_model/model.json');

    // Perform an inference
    const result = await model.executeAsync({
        "island": tf.tensor(["Torgersen"]),
        "bill_length_mm": tf.tensor([39.1]),
        "bill_depth_mm": tf.tensor([17.3]),
        "flipper_length_mm": tf.tensor([3.1]),
        "body_mass_g": tf.tensor([1000.0]),
        "sex": tf.tensor(["Female"]),
        "year": tf.tensor([2007], [1], 'int32'),
    });
    console.log(result);
}

run();
gaikwadrahul8 commented 10 months ago

Hi, @benmag

I tried to replicate the same behavior from my end in Node.js project with Node.js v20.11.0 LTS and v19.9.0 LTS versions and I'm getting same error message which you've mentioned above in the issue template so we'll have to dig more into this issue and will update you soon but it's working as expected with browser.

  1. Here is output error log for reference with Node.js v19.9.0 LTS :
gaikwadrahul@gaikwadrahul-macbookpro test-7805-with-node-js % node -v
v19.9.0
gaikwadrahul@gaikwadrahul-macbookpro test-7805-with-node-js % npm -v
9.6.3
gaikwadrahul@gaikwadrahul-macbookpro test-7805-with-node-js % node index.js 
/Users/gaikwadrahul/Desktop/TFJS/test-7805-with-node-js/node_modules/@tensorflow/tfjs-tfdf/dist/tf-tfdf.node.js:1650
                throw e; }), process.on("unhandledRejection", function (e) { throw e; }), D = function (e, t) { throw process.exitCode = e, t; }, l.inspect = function () { return "[Emscripten Module object]"; }) : (S || T) && (T ? k = self.location.href : typeof document != "undefined" && document.currentScript && (k = document.currentScript.src), A && (k = A), k.indexOf("blob:") !== 0 ? k = k.substr(0, k.replace(/[?#].*/, "").lastIndexOf("/") + 1) : k = "", g = function (e) { var t = new XMLHttpRequest; return t.open("GET", e, !1), t.send(null), t.responseText; }, T && (E = function (e) { var t = new XMLHttpRequest; return t.open("GET", e, !1), t.responseType = "arraybuffer", t.send(null), new Uint8Array(t.response); }), _ = function (e, t, r) { var n = new XMLHttpRequest; n.open("GET", e, !0), n.responseType = "arraybuffer", n.onload = function () { if (n.status == 200 || n.status == 0 && n.response) {
                ^

Error: Can't read the data of 'the loaded zip file'. Is it in a supported JavaScript type (String, Blob, ArrayBuffer, etc) ?
    at /Users/gaikwadrahul/Desktop/TFJS/test-7805-with-node-js/node_modules/@tensorflow/tfjs-tfdf/dist/tf-tfdf.node.js:551:893
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)

Node.js v19.9.0
gaikwadrahul@gaikwadrahul-macbookpro test-7805-with-node-js % 
  1. Here is output error log for reference with Node.js v20.11.0 LTS : :
gaikwadrahul@gaikwadrahul-macbookpro test-7805-with-node-js % node -v
v20.11.0
gaikwadrahul@gaikwadrahul-macbookpro test-7805-with-node-js % npm -v
10.2.4
gaikwadrahul@gaikwadrahul-macbookpro test-7805-with-node-js % node index.js 
/Users/gaikwadrahul/Desktop/TFJS/test-7805-with-node-js/node_modules/@tensorflow/tfjs-tfdf/dist/tf-tfdf.node.js:1650
                throw e; }), process.on("unhandledRejection", function (e) { throw e; }), D = function (e, t) { throw process.exitCode = e, t; }, l.inspect = function () { return "[Emscripten Module object]"; }) : (S || T) && (T ? k = self.location.href : typeof document != "undefined" && document.currentScript && (k = document.currentScript.src), A && (k = A), k.indexOf("blob:") !== 0 ? k = k.substr(0, k.replace(/[?#].*/, "").lastIndexOf("/") + 1) : k = "", g = function (e) { var t = new XMLHttpRequest; return t.open("GET", e, !1), t.send(null), t.responseText; }, T && (E = function (e) { var t = new XMLHttpRequest; return t.open("GET", e, !1), t.responseType = "arraybuffer", t.send(null), new Uint8Array(t.response); }), _ = function (e, t, r) { var n = new XMLHttpRequest; n.open("GET", e, !0), n.responseType = "arraybuffer", n.onload = function () { if (n.status == 200 || n.status == 0 && n.response) {
                ^

Error: Can't read the data of 'the loaded zip file'. Is it in a supported JavaScript type (String, Blob, ArrayBuffer, etc) ?
    at /Users/gaikwadrahul/Desktop/TFJS/test-7805-with-node-js/node_modules/@tensorflow/tfjs-tfdf/dist/tf-tfdf.node.js:551:893
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)

Node.js v20.11.0
gaikwadrahul@gaikwadrahul-macbookpro test-7805-with-node-js % 
sulmanen commented 5 months ago

👋 managed to solve this problem via

import * as tf from '@tensorflow/tfjs-node';
import { loadTFDFModel, TFDFModel } from '@tensorflow/tfjs-tfdf';
import { io } from '@tensorflow/tfjs-core';
import { WeightData, WeightsManifestConfig } from '@tensorflow/tfjs-core/dist/io/types';

async loadModel(): Promise<TFDFModel> {
    return await loadTFDFModel({
      loadModel: () =>
        fetch(`${BUCKET_URL}/model.json`)
          .then((response: Response) => response.json())
          .then((modelJson) =>
            io.getModelArtifactsForJSON(modelJson, (weightsManifest: WeightsManifestConfig) =>
              fetch(`${BUCKET_URL}/group1-shard1of1.bin`)
                .then((response: Response) => response.arrayBuffer())
                .then((weightData: WeightData) => [io.getWeightSpecs(weightsManifest), weightData])
            )
          ) as Promise<io.ModelArtifacts>,
      loadAssets: () =>
        fetch(`${BUCKET_URL}/assets.zip`).then((response: Response) => response.arrayBuffer()) as Promise<Blob>,
    });
  }

On node instead of response.blob() use response.arrayBuffer() for assets.zip loading