Serving models on the Web with JS/Wasm#

The JavaScript API makes it possible to run an Yggdrasil Decision Forests model or a TensorFlow Decision Forests model in a webpage.

Usage example#

The following example shows how to run a Yggdrasil model in a webpage. The result of this example is viewable here. The source code of the example is available here.

Step 1: Prepare the model#

The JavaScript API requires a Zipped Yggdrasil model.

Optionally, the meta-data of the model can be removed to make the model smaller with the edit_model or the pure_serving_model=True argument.

Train and prepare a model for JS with the CLI API

# Install the CLI API. See "CLI API / Quick Start" or " CLI API / Install" sections for more details.
wget https://github.com/google/yggdrasil-decision-forests/releases/download/1.0.0/cli_linux.zip
unzip cli_linux.zip

# Train the model (see Quick Start)
./train ... --output=model

# Remove the meta-data from the model (makes the model smaller)
./edit_model --input=/tmp/model --output=/tmp/model_pure --pure_serving=true

# Zip the model.
zip -j /tmp/model.zip /tmp/model_pure/*

# /tmp/model.zip can be used directly in Javascript.

Train and prepare a model for JS with TensorFlow Decision Forests

# Load TF-DF (see TF-DF tutorial)
import tensorflow_decision_forests as tfdf

# Load a dataset in a Pandas dataframe.
train_df = pd.read_csv("project/train.csv")

# Convert the dataset into a TensorFlow dataset.
train_ds = tfdf.keras.pd_dataframe_to_tf_dataset(train_df, label="my_label")

# Train a Random Forest model.
model = tfdf.keras.GradientBoostedTreesModel(pure_serving_model=True)
model.fit(train_ds)

# Export the model to a SavedModel.
model.save("/tmp/tfdf_model")

# Zip the model.
zip -j /tmp/model.zip /tmp/tfdf_model/assets/*

# /tmp/model.zip can be used directly in Javascript.

Note

See the convert model page on how to import models from other formats.

Note

Warning: The model zip file should be a flat zip file: The model files should be located at the root of the zip file. If using the zip tool, a flat file can be created with the -j option.

Step 2: Install the YDF Javascript library#

Download the latest version of the YDF Javascript library on the Yggdrasil release page. For example, this is the YDF Javascript library for YDF 0.2.5.

The files of your webpage can be structured as follows:

  • index.html : The webpage running the model (see step 3).

  • model.zip : The model created in step 1.

  • ydf/ :The content of the zip file downloaded above.

    • inference.js

    • inference.wasm

Step 3: Create the webpage#

Add the YDF Javascript wrapper to your html:

<!-- Yggdrasil Decision Forests -->
<script src="ydf/inference.js"></script>

<!-- JSZip -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/jszip/3.10.0/jszip.min.js"></script>

Then, also in the HTML header or in the body, load the YDF library:

<script>
let ydf = null;
YggdrasilDecisionForests().then(function (m) {
  ydf = m;
  console.log("The library is loaded and ready to be used!");
});
</script>

Once the library is loaded, load the model:

Note

In practice, you will likely load the model next to the console.log("The library... call above.

let model = null;
ydf.loadModelFromUrl("model.zip").then((loadedModel) => {
  model = loadedModel;
  console.log("The model is loaded");
});

Once, the model is loaded, you can make predictions:

let examples = {
  feature_1: [1, null, 3], // "null" represents a missing value.
  feature_2: ["cat", "dog", "tiger"],
  };
let predictions = model.predict(examples);

Note

The input of the predict function should be the same as the input features used to train the model.

Finally, when you are done using the model, unload the model:

model.unload();
model = null;

Testing the inference#

You case use Yggdrasil pre-configured https server to test your code before deployment. Look at build_and_run_local_server.sh for an example.

Compile YDF Javascript library from source#

To compile, the YDF Javascript library from the source, install the dependencies required to compile YDF (see CLI / Compile from source), and then run the following command:

# Download YDF
git clone https://github.com/google/yggdrasil-decision-forests.git
cd yggdrasil-decision-forests


# Compile Yggdrasil Decision Forest WebAssembly inference engine
# The result is available at "bazel-bin/yggdrasil_decision_forests/port/javascript/ydf.zip"
# Note: You can skip the "--config=lto --config=size" flags to speed-up compilation at the expense of a larger binary.
bazel build -c opt --config=lto --config=size --config=wasm //yggdrasil_decision_forests/port/javascript:create_release

# The YDF library is available at: bazel-bin/yggdrasil_decision_forests/port/javascript/ydf.zip

If the compilation fails with the error Exception: FROZEN_CACHE is set, disable the Emscripten frozen cache. This is done by setting FROZEN_CACHE=False in the emscripten_config Emscripten configuration file located in the Bazel cache ~/.cache/bazel/.../external/emsdk/emscripten_toolchain. See here here for more details.

You can also compile YDF from within a Docker as follow:

docker run -it -v ${PWD}/..:/working_dir -w /working_dir/yggdrasil-decision-forests ubuntu:18.04

apt-get update
apt-get -y --no-install-recommends install \
  ca-certificates \
  build-essential \
  g++-10 \
  clang-10 \
  git \
  python3 \
  python3-pip \
  zip \
  wget \
  unzip

python3 -m pip install numpy

wget -O bazelisk https://github.com/bazelbuild/bazelisk/releases/download/v1.14.0/bazelisk-linux-amd64
chmod +x bazelisk
ln -s ${PWD}/bazelisk /usr/bin/bazel

yggdrasil_decision_forests/port/javascript/tools/build_zipped_library.sh