tensorflow / tfjs

A WebGL accelerated JavaScript library for training and deploying ML models.

Home Page:https://js.tensorflow.org

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

model.executeAsync fails when loading multiple converted TF-DF models

akshaan opened this issue · comments

System information

  • Have I written custom code (as opposed to using a stock example script provided in TensorFlow.js): Yes
  • OS Platform and Distribution (e.g., Linux Ubuntu 16.04): MacOS / Linux
  • TensorFlow.js installed from (npm or script link): npm
  • TensorFlow.js version (use command below): 4.17.0
  • Tensorflow.js Converter Version: 4.17.0

Describe the current behavior

Loading two decision forest models (trained in Python and converted) yields the following error when calling model.executeAsync:

Error: Unexpected numerical input feature shape
 ❯ eo.predictTFDFSignature node_modules/@tensorflow/tfjs-tfdf/dist/tf-tfdf.fesm.js:38:11327
    321|         throw new Error(`modelUrl IO Handler ${modelSource} has no loa…
    322|     }
    323|     const graphModelSource = { load: modelSource.loadModel };
       |                              ^
    324|     const assets = modelSource.loadAssets();
    325|     const graphModel = loadGraphModelSync(graphModelSource);
 ❯ Object.customExecutor tfjs-tfdf/src/register_tfdf_ops.ts:64:9
 ❯ tfjs-converter/src/operations/operation_executor.ts:108:31
 ❯ executeOp tfjs-converter/src/operations/operation_executor.ts:119:8
 ❯ _loop_1 tfjs-converter/src/executor/graph_executor.ts:630:13
 ❯ GraphExecutor.processStack node_modules/@tensorflow/tfjs-converter/dist/tf-converter.node.js:31391:13
 ❯ GraphExecutor.<anonymous> tfjs-converter/src/executor/graph_executor.ts:576:29
 ❯ step node_modules/tslib/tslib.es6.js:102:23
 ❯ Object.next node_modules/tslib/tslib.es6.js:83:53
 ❯ node_modules/tslib/tslib.es6.js:76:71
  • The models are loaded from the file system using a custom sync loader (code below)
  • We are using the pure-JS (browser) version of tfjs in a nodeJS server (we cannot use the nodeJS version due to incompatibilities with our linux distro)
  • The two models have many overlapping input names

Describe the expected behavior
Running predictions should work for multiple models loaded from files.

Standalone code to reproduce the issue

export class TFDFFileSystemLoadHandlerSync {
  static loadModel(path: string): tf.io.ModelArtifacts {
    const modelPath = join(path, 'model.json');
    try {
      const info = statSync(modelPath);

      // `path` can be either a directory or a file. If it is a file, assume
      // it is model.json file.
      if (info.isFile()) {
        const modelJSON = JSON.parse(readFileSync(modelPath, 'utf8'));
        const [weightsManifest, weightData] = this.loadWeights(modelJSON.weightsManifest, modelPath);
        return tf.io.getModelArtifactsForJSONSync(modelJSON, weightsManifest, weightData);
      } else {
        throw new Error('The path to load from must be a file. Loading from a directory ' + 'is not supported.');
      }
    } catch (e) {
      doesNotExistHandler('Path')(e as NodeJS.ErrnoException);
      // This line is never reached. It is added to pacify the TypeScript compiler.
      return {} as tf.io.ModelArtifacts;
    }
  }

  static loadAssets(path: string): Blob {
    const assetsPath = join(path, 'assets.zip');
    const buffer = readFileSync(assetsPath);
    return toArrayBuffer(buffer) as unknown as Blob;
  }

  static loadWeights(
    weightsManifest: tf.io.WeightsManifestConfig,
    path: string
  ): [tf.io.WeightsManifestEntry[], ArrayBuffer] {
    const dirName = dirname(path);
    const buffers: Buffer[] = [];
    const weightSpecs: tf.io.WeightsManifestEntry[] = [];
    for (const group of weightsManifest) {
      for (const path of group.paths) {
        const weightFilePath = join(dirName, path);
        try {
          const buffer = readFileSync(weightFilePath);
          buffers.push(buffer);
        } catch (e) {
          doesNotExistHandler('Weight file')(e as NodeJS.ErrnoException);
        }
      }
      weightSpecs.push(...group.weights);
    }
    return [weightSpecs, toArrayBuffer(buffers)];
  }
}

Loading model:

    return tfdf.loadTFDFModelSync({
      loadModel: () => TFDFFileSystemLoadHandlerSync.loadModel(modelPath),
      loadAssets: () => TFDFFileSystemLoadHandlerSync.loadAssets(modelPath),
    });