Intermediate

Building Machine Learning Solutions with TensorFlow.js 2

Machine learning and deep learning are powering some of the most groundbreaking applications of the current era. JavaScript was not traditionally considered the go-to language for machine...

Table of Contents


Module 1 – Course Overview

Machine learning and deep learning are powering some of the most groundbreaking applications of the current era. JavaScript was not traditionally considered the go-to language for machine learning model development and deployment, despite being one of the most popular programming languages in the world.

TensorFlow.js is an open-source framework that now allows JavaScript developers to extend their skills to build machine-learning-powered applications that can solve key challenges around:

  • Data privacy — running inference locally without sending sensitive data to a server
  • Network latency — eliminating the round-trip to a remote API
  • Application availability — working offline or in intermittent-connectivity environments
  • Compute cost — offloading computation to the client device

TensorFlow.js can run on:

  • Client browsers (desktop and mobile)
  • Mobile native applications (React Native)
  • IoT edge devices
  • Server-side with Node.js

In this course, you will learn to:

  • Build and train models from scratch using TensorFlow.js
  • Use existing pre-trained models
  • Retrain pre-trained models using transfer learning
  • Run TensorFlow.js on both the client side and the server side

Prerequisites: Basic JavaScript knowledge, familiarity with machine learning and deep learning concepts.


Module 2 – Introduction

Why TensorFlow.js?

Most machine learning applications follow a standard client-server architecture: client devices send data to a server, the server runs inference using deployed models, and predictions are returned to the client. This setup has well-known limitations:

  • Privacy: Every prediction requires sending potentially sensitive data over the network.
  • Latency: Each request incurs round-trip network overhead.
  • Availability: Servers can be unavailable or unreachable.
  • Cost: Scaling server-side ML APIs for large consumer bases is expensive.

TensorFlow.js changes this paradigm by enabling inference directly in the browser — and even training new models or retraining pre-trained models locally, without sending any data over the network.

Real-world example: Companies like Airbnb use TensorFlow.js models on the client side to detect sensitive or personal information while users upload pictures, without that data ever leaving the device.

Client-side use cases that benefit from TensorFlow.js:

  • Health diagnosis from user images or text — eliminates privacy concerns
  • Processing sensitive legal documents
  • Applications requiring near-real-time inference from live audio or video feeds
  • Interactive and personalized experiences that update based on user behavior
graph LR
    A[User Device / Browser] -->|Standard architecture| B[Remote Server]
    B -->|Prediction result| A
    A -->|TensorFlow.js| C[Local ML Inference]
    C -->|Instant result| A

TensorFlow.js Performance

TensorFlow.js has grown rapidly since its inception in 2017 and supports multiple backends to maximize performance:

Client-side backends:

BackendMechanismPerformanceCoverage
CPUPlain vanilla JavaScriptBaseline100%
WebGLGPU-accelerated via WebGL APIUp to 100× over CPU97%+ of devices globally
WebAssembly (Wasm)CPU acceleration with portable assembly10×–30× over CPU90%+ of devices globally

Server-side backend:

BackendMechanismPerformance
NodeLow-level TensorFlow C/C++ API bindingsComparable to Python TensorFlow

WebGPU is under active development and promises near-zero overhead execution, which will further improve client-side performance.

TensorFlow.js automatically falls back to a less capable backend if the preferred one is not available. For example, if WebGL is unavailable, it falls back to Wasm or CPU.

TensorFlow.js Overview

TensorFlow.js is not just a library — it is a complete ecosystem:

graph TD
    A[TensorFlow.js Ecosystem]
    A --> B[Pre-built Models]
    A --> C[Layers API]
    A --> D[Ops / Core API]
    A --> E[TFJS Data]
    A --> F[TFJS Vis]
    A --> G[Community Libraries]
    B --> B1[Image Classification]
    B --> B2[Object Detection]
    B --> B3[Pose Detection]
    B --> B4[Speech Commands]
    B --> B5[Toxicity Detection]
    B --> B6[Question Answering]
    C --> C1[Dense Layers]
    C --> C2[Conv2D, LSTM, etc.]
    G --> G1[ml5.js]
    G --> G2[Magenta.js]
    G --> G3[face-api.js]

Pre-built models are available for common tasks such as:

  • Image classification (MobileNet)
  • Object detection in images and video
  • Human pose detection
  • Speech command recognition
  • Question answering
  • Toxicity detection in text

These can be integrated into an application with just a few lines of code.

APIs:

  • Layers API — High-level, like building blocks (equivalent to Keras in Python TensorFlow).
  • Ops / Core API — Lower-level mathematical operations.
  • TFJS Data — Utilities for loading and preprocessing data from CSV, arrays, generators, webcams, microphones.
  • TFJS Vis (tfvis) — Visualization utilities for charts, model summaries, training history, confusion matrices.

Community libraries:

  • ml5.js — Very high-level abstractions for rapid prototyping
  • Magenta.js — Music and art generation
  • face-api.js — Face detection and recognition

Course Demo: Toxicity Detector

The demo project built throughout this course tackles one of the key challenges of online communication platforms: toxic comment detection.

Most platforms allow users to post free-form text comments. This creates a risk of abusive, offensive, or toxic content that harms communities and platform reputation. Traditional approaches use human moderators or simple word-filter rules, both of which have clear limitations.

The solution explored here uses Natural Language Processing (NLP) and machine learning to identify toxic comments that violate platform guidelines.

Two approaches are built:

  1. Server-side — Comments are sent to a server where ML frameworks detect toxicity.
  2. Client-side — Toxicity detection happens entirely in the browser. Users receive immediate feedback before publishing, without any privacy concerns, without infrastructure to maintain, and without scaling challenges.

The final application will support four model flavors:

  1. Custom TF.js model with TF-IDF features
  2. Model trained in Python and converted to TF.js format
  3. Custom TF.js model with Universal Sentence Encoder features (transfer learning)
  4. Fully pre-trained TF.js toxicity model

Course Structure

The course follows an incremental approach:

  1. Module 3 — Set up TensorFlow.js environment (browser and Node.js)
  2. Module 4 — Core concepts: tensors, operations, memory management
  3. Modules 5–6 — Data preparation and feature engineering for toxicity detection
  4. Module 7 — Build, train, and evaluate a neural network model
  5. Module 8 — Save and load the trained model
  6. Module 9 — Predict with the trained model, wire to a UI, use a Python-exported model
  7. Module 10 — Use pre-trained models, transfer learning with Universal Sentence Encoder
  8. Module 11 — Next steps and resources

Module 3 – Setting up TensorFlow.js Environment

TensorFlow.js can be set up on:

  • Client-side browsers (the primary focus of this course)
  • Mobile native apps (React Native)
  • Desktop and IoT edge devices
  • Server-side with Node.js

TensorFlow.js in Browser Using Script Tag

The simplest way to include TensorFlow.js in a browser application is via a <script> tag pointing to the CDN:

<!DOCTYPE html>
<html>
    <head>
        <script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs/dist/tf.min.js"></script>
        <script src="script.js"></script>
    </head>
    <body>
    </body>
</html>

Note: You can remove the version tag from the CDN URL to always get the latest version. This approach is quick to get started with but becomes harder to manage for medium to large applications due to dependency management issues.

Demo: Running TensorFlow.js in Browser with Script Tag

Once the script tag is added, TensorFlow.js is available globally via the tf object:

// script.js
console.log(tf.version);
console.log(tf.getBackend());

For local development, a local web server is required to serve HTML pages. VS Code Live Server extension is a popular choice — it acts as a development server with live reload support.

TensorFlow.js in Browser Using Package Managers

For medium and large-scale applications, using a package manager (npm or Yarn) with a web application bundler (Parcel or Webpack) is the recommended approach.

Benefits of package managers:

  • All dependencies declared in package.json
  • Only package.json needs to be version-controlled; node_modules is reproducible

If you come from Python, think of package.json as requirements.txt and npm as pip.

Benefits of bundlers:

  • Combine JavaScript, HTML, CSS, and other dependencies
  • Resolve file ordering for dependency management
  • Linting to catch code issues
  • Transpilation (make ES6 code compatible with older browsers)
  • Minification to reduce bundle size for faster load times

Install TensorFlow.js via npm:

npm install @tensorflow/tfjs

Demo: Running TensorFlow.js in Browser Using NPM and Parcel

Project setup steps:

# Initialize npm project
npm init

# Install TensorFlow.js
npm install @tensorflow/tfjs

# Install Parcel bundler (globally)
npm install -g parcel-bundler

# Install WebAssembly backend (optional)
npm install @tensorflow/tfjs-backend-wasm

Project structure:

toxicity-detector/
├── package.json
└── src/
    ├── index.html
    └── client.js

src/index.html:

<!DOCTYPE html>
<html>
    <body>
        <script src="client.js"></script>
    </body>
</html>

src/client.js:

import * as tf from '@tensorflow/tfjs';
import '@tensorflow/tfjs-backend-wasm';

console.log(tf.version);
tf.setBackend('wasm');
tf.ready().then(() => {
    console.log(tf.getBackend());
});

Add a dev script to package.json:

{
  "scripts": {
    "dev": "parcel src/index.html"
  }
}

Run with:

npm run dev

Demo: Exploring TensorFlow.js Backends

You can programmatically check and switch TensorFlow.js backends:

import * as tf from '@tensorflow/tfjs';
import '@tensorflow/tfjs-backend-wasm';

// Check current backend (after a TF operation has run)
tf.ready().then(() => {
    console.log(tf.getBackend()); // 'webgl', 'wasm', or 'cpu'
});

// Force a specific backend
tf.setBackend('cpu');

// For WebAssembly backend (needs the wasm package installed and imported)
tf.setBackend('wasm');

Tip: tf.getBackend() returns undefined before any TensorFlow operation has run because the backend initializes lazily. Use tf.ready() to wait for initialization.

To verify WebGL support in a browser, visit get.webgl.org — if you see a spinning cube, WebGL is supported.

Demo: Running TensorFlow.js in Node.js

TensorFlow.js on Node.js uses low-level C/C++ bindings for performance comparable to Python TensorFlow.

Install Node backend:

npm install @tensorflow/tfjs-node
# For GPU support (requires NVIDIA GPU):
npm install @tensorflow/tfjs-node-gpu

src/server.js:

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

const express = require('express');
const app = express();

app.get('/train', function (req, res) {
    console.log(tf.version);
    tf.ready().then(() => {
        const message = "Loaded TensorFlow.js - version: " + tf.version.tfjs
                      + " \n with backend " + tf.getBackend();
        console.log(message);
        // training code goes here
        res.send(message);
    });
});

app.listen(9000, function (req, res) {
    console.log('Running server on port 9000 ...');
});

Run the server:

node src/server.js

Then call the endpoint: http://localhost:9000/train

Module 3 Summary

SetupHowBest For
Script tag (CDN)<script src="https://cdn.jsdelivr.net/...">Quick prototyping
npm + Parcelnpm install @tensorflow/tfjsMedium/large apps
npm + Webpacknpm install @tensorflow/tfjsProduction builds
Node.jsrequire('@tensorflow/tfjs-node')Server-side execution

Key functions covered:

  • tf.getBackend() — returns the currently active backend
  • tf.setBackend('cpu' | 'webgl' | 'wasm') — switch backends
  • tf.ready() — returns a Promise that resolves when the backend is initialized

Module 4 – Understanding TensorFlow.js Core Concepts

TensorFlow.js provides multiple API levels. This module covers the Core / Ops API — the lower-level building blocks that underpin everything else.

Tensor Overview

A tensor is the most basic and important building block in TensorFlow. In the simplest terms, a tensor is a set of values — an array that could be multi-dimensional.

graph LR
    A[Scalar<br/>rank 0] --> B[1D Tensor<br/>rank 1<br/>shape: n]
    B --> C[2D Tensor<br/>rank 2<br/>shape: n×m]
    C --> D[3D Tensor<br/>rank 3<br/>shape: n×m×p]
    D --> E[nD Tensor<br/>rank n]

Key tensor concepts:

ConceptDescription
RankNumber of dimensions (0 = scalar, 1 = vector, 2 = matrix, …)
ShapeSize along each dimension, e.g. [2, 3] means 2 rows × 3 columns
Data type (dtype)float32 (default), int32, bool, string
ImmutabilityTensors cannot be changed; operations create new tensors

Tensor types:

  • tf.scalar(value) — rank 0, single value
  • tf.tensor1d([...]) — rank 1 vector
  • tf.tensor2d([[...],[...]]) — rank 2 matrix
  • tf.tensor(data, shape, dtype) — general form for any rank
  • tf.variable(tensor) — mutable wrapper, supports assign()

Examples of real-world shapes:

  • Grayscale 28×28 image → 2D tensor, shape [28, 28]
  • RGB 28×28 image → 3D tensor, shape [28, 28, 3]
  • Batch of 64 RGB 28×28 images → 4D tensor, shape [64, 28, 28, 3]

Demo: Working with Tensors

import * as tf from '@tensorflow/tfjs';

// 1D tensor (rank 1)
const age = tf.tensor1d([30, 25], 'int32');
age.print();            // Tensor [30, 25]
tf.print(age);          // same
console.log(age.shape); // [2]
console.log(age.dtype); // int32

// 2D tensor (rank 2)
const age_income_height = tf.tensor2d([[30, 1000, 170], [25, 2000, 168]]);
age_income_height.print();
console.log(age_income_height.shape); // [2, 3]
console.log(age_income_height.dtype); // float32

// Scalar (rank 0)
const multiplier = tf.scalar(10);
multiplier.print(); // Tensor 10

Note: If you do not specify dtype, TensorFlow.js automatically infers it. Integer values are converted to float32 by default. Explicitly set 'int32' if integer precision is required.

Basic Tensor Operations

TensorFlow.js provides a rich set of operations via the Ops API:

  • Arithmetic: tf.add, tf.sub, tf.mul, tf.div
  • Math: tf.abs, tf.exp, tf.log, tf.sqrt, tf.square, tf.round
  • Matrix: tf.matMul, tf.transpose, tf.dot
  • Reduction: tf.sum, tf.mean, tf.min, tf.max, tf.argMax, tf.argMin
  • Advanced: convolutions, pooling, activation functions

Key principle — immutability:

Tensors are immutable. Any operation on a tensor creates a new tensor in memory. This is important to keep in mind for memory management.

Variables are mutable wrappers around tensors and are used when values need to be updated (e.g., during model training):

const var_1 = tf.variable(tf.tensor1d([1, 2, 3]));
var_1.assign(tf.tensor1d([4, 5, 6])); // Overwrites the variable

Demo: Performing Basic Tensor Operations

import * as tf from '@tensorflow/tfjs';

// Tensor addition
const income_source_1 = tf.tensor1d([100, 200, 300, 150]);
const income_source_2 = tf.tensor1d([50, 70, 30, 20]);

// Method 1: tf.add()
const total_income = tf.add(income_source_1, income_source_2);
total_income.print(); // [150, 270, 330, 170]

// Method 2: chaining
const total_income2 = income_source_1.add(income_source_2);
total_income2.print();

// Variables
const var_1 = tf.variable(income_source_1);
tf.print('var_1 before assignment : ' + var_1);
var_1.assign(income_source_2);
tf.print('var_1 after assignment : ' + var_1);

Managing Memory with TensorFlow.js

Memory management is critical in TensorFlow.js, particularly when using the WebGL backend.

Why it matters:

In most programming languages, a garbage collector automatically frees memory from unused variables. JavaScript has a garbage collector too — but it does not automatically clean up TensorFlow.js WebGL tensors. Under the hood, WebGL stores tensors as GPU textures, and the browser’s GC does not manage GPU memory. If tensors are not manually disposed, the application will leak GPU memory and eventually crash.

Memory management functions:

FunctionPurpose
tensor.dispose()Free memory for a specific tensor
tf.tidy(fn)Run fn, then automatically dispose all tensors created inside it (except those returned)
tf.keep(tensor)Mark a tensor to be kept alive even inside tf.tidy
tf.memory().numTensorsInspect the current number of tensors in memory

tf.tidy is the recommended approach for most scenarios. Wrap any code block that creates tensors to ensure automatic cleanup.

Demo: Managing Memory with TensorFlow.js

import * as tf from '@tensorflow/tfjs';

const y = tf.tidy(() => {
    const income_source_1 = tf.tensor1d([100, 200, 300, 150]);
    const income_source_2 = tf.tensor1d([50, 70, 30, 20]);

    // tf.keep prevents tidy from disposing this tensor
    const total_income = tf.keep(income_source_1.add(income_source_2));
    total_income.print();

    const var_1 = tf.variable(income_source_1);
    var_1.assign(income_source_2);

    console.log('Tensors inside tidy: ' + tf.memory().numTensors);
    // tidy will dispose all tensors created here except total_income (kept) and y (returned)
});

console.log('Number of tensors after cleanup: ' + tf.memory().numTensors);

// Manual disposal
const t1 = tf.tensor1d([1, 2, 3]);
const t2 = tf.tensor1d([4, 5, 6]);
t1.dispose(); // Free t1 from memory
t2.dispose(); // Free t2 from memory

Module 4 Summary

Key concepts covered:

  • Tensors are the fundamental data structure — multi-dimensional arrays with a dtype and shape
  • Scalars are rank-0 tensors; variables are mutable wrappers
  • Immutability: all operations create new tensors
  • Ops API provides arithmetic, math, matrix, and advanced operations
  • Memory management with dispose(), tidy(), and keep() is essential when using the WebGL backend

Module 5 – Preparing Data for Machine Learning Model: Part 1

Machine Learning Workflow

Any machine learning project follows an iterative workflow:

graph LR
    A[Problem Understanding] --> B[Data]
    B --> C[Modeling]
    C --> D[Deployment]
    D --> A
    B --> B1[Extract → Explore → Visualize]
    C --> C1[Feature Engineering → Build → Train → Evaluate]
    D --> D1[Model Persistence → Scoring / Inference → Feedback]
  1. Problem: Understand the business requirement; formulate the ML approach.
  2. Data: Extract, clean, explore, and visualize the data.
  3. Modeling: Feature engineering → build model → train → evaluate → iterate.
  4. Deployment: Persist the trained model → load → predict (score/inference) → collect feedback → retrain.

This course follows this workflow end-to-end for a toxicity detection use case.

Toxicity Detection Use Case

The dataset used in this course is based on the Kaggle Toxic Comment Classification Challenge (Conversation AI team — Jigsaw + Google).

Dataset details:

  • ~159,000 Wikipedia comments
  • Labeled by human experts
  • Columns: id, comment_text, toxic, severe_toxic, obscene, threat, insult, identity_hate
  • Binary labels: 0 = absent, 1 = present
  • A comment can have more than one toxicity type

For this course:

  • A cleaned, sample version of the training file is used
  • Only the toxic column is used as the output label (binary classification)
  • The comment_text column is the input feature

Disclaimer: The dataset contains text that may be considered profane, vulgar, or offensive, as it was built for toxicity detection.

Working with TFJS Data

TensorFlow.js provides a dedicated TFJS Data module (tf.data) for loading and preprocessing data — the JavaScript equivalent of tf.data in Python TensorFlow.

Supported data sources:

SourceAPI
In-memory arraytf.data.array(array)
CSV filetf.data.csv(url, options)
JavaScript generator functiontf.data.generator(generatorFn)
Webcam (video feed)tf.data.webcam(videoElement)
Microphone (audio feed)tf.data.microphone()

All sources are converted to a tf.data.Dataset object, which exposes chainable operations:

OperationPurpose
.shuffle(bufferSize, seed)Randomize order
.filter(predicate)Keep rows matching a condition
.map(transformFn)Transform each row
.batch(batchSize)Group rows into batches
.take(n)Take the first n rows
.skip(n)Skip the first n rows
.forEachAsync(fn)Iterate asynchronously over each row

Async JavaScript Programming

Many TensorFlow.js operations are asynchronous — they return a Promise rather than blocking the main thread. This is critical for browser applications where blocking the main thread would freeze the UI.

The problem without async:

Task 1 → Task 2 → Task 3 (long-running, blocks UI) → Task 4

The solution with async/await:

Task 1 → Task 2 → Promise returned → Task 4 runs immediately
                      ↓
              Task 3 runs in background
                      ↓
              Main thread receives result

async/await pattern:

import "regenerator-runtime/runtime"; // Required for Parcel bundler

const init = async () => {
    await tf.ready(); // Wait for backend to initialize
    console.log(tf.getBackend());
};

console.log("line1");
console.log("line2");
init(); // non-blocking — returns a Promise immediately
console.log("line4"); // runs before init() finishes

Console output order: line1line2line4 → backend name (after tf.ready resolves).

Generator-based iteration with TFJS Data:

// forEachAsync takes an async callback and awaits it properly
await dataset.forEachAsync(async (row) => {
    console.log(row);
});

Demo: Reading Data Using TFJS Data

import * as tf from '@tensorflow/tfjs';
import "regenerator-runtime/runtime";

const csvUrl = 'data/toxic_data_sample.csv';

const readRawData = () => {
    const readData = tf.data.csv(csvUrl, {
        columnConfigs: {
            toxic: {
                isLabel: true  // marks 'toxic' as the output column
            }
        }
    });
    return readData;
};

const run = async () => {
    await tf.ready();

    const rawDataResult = readRawData();
    const labels = [];
    const comments = [];

    await rawDataResult.forEachAsync((row) => {
        // row.xs contains input features (all columns except labels)
        // row.ys contains output labels
        comments.push(row['xs']['comment_text']);
        labels.push(row['ys']['toxic']);
    });

    console.log(`Loaded ${comments.length} comments`);
    console.log(`Sample comment: ${comments[0]}`);
    console.log(`Sample label: ${labels[0]}`);
};

run();

To serve static files (like CSV data) with Parcel, use the parcel-plugin-static-files-copy plugin and place data files in a static folder that gets copied to the dist output directory.

Working with TFVis

tfvis (@tensorflow/tfjs-vis) is the visualization component of the TensorFlow.js ecosystem. It provides:

  • Common charts: bar chart, line chart, scatter chart
  • Model inspection: layer summaries, model topology
  • Training monitoring: history (loss, accuracy over epochs)
  • Evaluation: confusion matrix, heat maps, per-class accuracy

tfvis includes a Visor — an overlay panel that can be toggled to display all visualizations side by side while the application runs.

Install:

npm install @tensorflow/tfjs-vis

Demo: Visualizing Data Using TFVis

import * as tfvis from '@tensorflow/tfjs-vis';

const plotOutputLabelCounts = (labels) => {
    // Count occurrences of each label (0 or 1)
    const labelCounts = labels.reduce((acc, label) => {
        acc[label] = acc[label] === undefined ? 1 : acc[label] += 1;
        return acc;
    }, {});

    // Transform into tfvis-compatible format: [{index, value}]
    const barChartData = [];
    Object.keys(labelCounts).forEach((key) => {
        barChartData.push({
            index: key,
            value: labelCounts[key]
        });
    });

    // Render bar chart in the Visor
    tfvis.render.barchart({
        tab: 'Exploration',
        name: 'Toxic output labels'
    }, barChartData);
};

Module 5 Summary

  • Covered the machine learning workflow: problem → data → modeling → deployment (iterative)
  • Explored the toxicity detection use case and dataset
  • Used tf.data.csv to read CSV data and create a tf.data.Dataset
  • Learned async/await programming patterns essential for TensorFlow.js operations
  • Created a simple bar chart using tfvis.render.barchart to visualize label distribution

Module 6 – Preparing Data for Machine Learning Model: Part 2

Generating Features from Text

Machine learning models process numeric values — raw text cannot be fed directly. Text must be converted into a numeric representation first.

Approach 1: Vector Space Model (VSM) / TF-IDF

The pipeline has two steps:

Step 1 — Tokenization (Build Document Vectors)

  • Split each text into individual words (tokens)
  • Optionally remove stop words (words like I, the, was that carry no modeling value)

Step 2 — Convert to Numbers using TF-IDF

$$\text{TF-IDF}(t, d) = \text{TF}(t, d) \times \text{IDF}(t)$$

Where:

$$\text{TF}(t, d) = \frac{\text{count of term } t \text{ in document } d}{\text{total terms in } d}$$

$$\text{IDF}(t) = 1 + \ln\left(\frac{N}{\text{number of documents containing } t}\right)$$

  • TF (Term Frequency): How often a term appears in a document. Common terms in a document get higher weight.
  • IDF (Inverse Document Frequency): Penalizes terms that appear in many documents (common words). Rare, distinctive terms get higher weight.

The result is a Term-Document Matrix where each row is a document and each column is a word in the vocabulary (dictionary).

Limitations of TF-IDF:

  • Dictionary size grows with vocabulary — large feature vectors
  • Context is not preserved — word order ignored
  • Synonyms and semantic relationships not captured

Approach 2: Predictive Encoders / Embeddings (Module 10)

  • Use pre-trained models (e.g., Universal Sentence Encoder) to produce dense, context-aware embeddings
  • Each sentence maps to a fixed-size vector (e.g., 512 dimensions)

Demo: Generating TF-IDF Features

import * as tf from '@tensorflow/tfjs';

const stopwords = ['i', 'me', 'my', 'we', 'our', 'you', 'your', 'he', 'him',
    'she', 'her', 'it', 'they', 'them', 'what', 'which', 'this', 'that',
    'am', 'is', 'are', 'was', 'were', 'be', 'been', 'have', 'has', 'do',
    'does', 'did', 'a', 'an', 'the', 'and', 'but', 'if', 'or', 'of', 'at',
    'by', 'for', 'with', 'to', 'from', 'in', 'out', 'on', 'over', 'not',
    'so', 'than', 'too', 'very', 's', 't', 'can', 'will', 'just', 'now'];

let tmpDictionary = {};

// Step 1: Tokenize a sentence, optionally building the dictionary
const tokenize = (sentence, isCreateDict = false) => {
    const tmpTokens = sentence.split(/\s+/g);
    const tokens = tmpTokens.filter(
        (token) => !stopwords.includes(token) && token.length > 0
    );

    if (isCreateDict) {
        tokens.reduce((acc, token) => {
            acc[token] = acc[token] === undefined ? 1 : acc[token] += 1;
            return acc;
        }, tmpDictionary);
    }
    return tmpTokens;
};

// Sort dictionary by frequency (descending)
const sortDictionaryByValue = (dict) => {
    return Object.keys(dict)
        .map((key) => [key, dict[key]])
        .sort((a, b) => b[1] - a[1]);
};

// Step 2: Compute Inverse Document Frequency
const getInverseDocumentFrequency = (documentTokens, dictionary) => {
    return dictionary.map((token) =>
        1 + Math.log(
            documentTokens.length /
            documentTokens.reduce(
                (acc, curr) => curr.includes(token) ? acc + 1 : acc, 0
            )
        )
    );
};

// Step 3: Compute Term Frequency for a single document
const getTermFrequency = (tokens, dictionary) => {
    return dictionary.map((token) =>
        tokens.reduce((acc, curr) => curr === token ? acc + 1 : acc, 0)
    );
};

// Step 4: Compute TF-IDF
const getTfIdf = (tfs, idfs) => {
    return tfs.map((element, index) => element * idfs[index]);
};

// Full encoder: sentence → TF-IDF feature vector
const encoder = (sentence, dictionary, idfs) => {
    const tokens = tokenize(sentence);
    const tfs = getTermFrequency(tokens, dictionary);
    return getTfIdf(tfs, idfs);
};

Function Generators

A function generator is a special kind of function (marked with function*) that uses yield to produce values lazily. Each call to next() on the resulting iterator runs the function until the next yield and then pauses.

function* myGenerator() {
    yield 1;
    yield 2;
    yield 3;
}

const gen = myGenerator();
console.log(gen.next().value); // 1
console.log(gen.next().value); // 2
console.log(gen.next().value); // 3

Why generators matter for ML data pipelines:

When reading large datasets (e.g., thousands of images from disk), loading everything into memory at once would be prohibitive. Generators allow lazy, on-demand loading — only the data needed for the current step is loaded.

sequenceDiagram
    participant DataPipeline
    participant Generator
    participant Model
    DataPipeline->>Generator: Call next()
    Generator-->>DataPipeline: yield batch 1
    DataPipeline->>Model: Train on batch 1
    DataPipeline->>Generator: Call next()
    Generator-->>DataPipeline: yield batch 2
    DataPipeline->>Model: Train on batch 2
    Note over Generator: Each yield loads only one chunk

Demo: Creating Feature Dataset Using Generators

import * as tf from '@tensorflow/tfjs';

const prepareDataUsingGenerator = (comments, labels, dictionary, idfs) => {
    // Generator for features (xs)
    function* getFeatures() {
        for (let i = 0; i < comments.length; i++) {
            const encoded = encoder(comments[i], dictionary, idfs);
            yield tf.tensor2d([encoded], [1, dictionary.length]);
        }
    }

    // Generator for labels (ys)
    function* getLabels() {
        for (let i = 0; i < labels.length; i++) {
            yield tf.tensor2d([labels[i]], [1, 1]);
        }
    }

    // Create datasets from generators
    const xs = tf.data.generator(getFeatures);
    const ys = tf.data.generator(getLabels);

    // Zip features and labels together
    const ds = tf.data.zip({ xs, ys });
    return ds;
};

Train / Validation / Test Split

Splitting data into distinct sets is a best practice in any ML project:

graph LR
    A[Full Dataset 100%]
    A --> B[Training 60%]
    A --> C[Validation 10%]
    A --> D[Test 30%]
    B --> E[Update model weights]
    C --> F[Monitor for overfitting]
    D --> G[Final evaluation only]
  • Training set (60%): Used to update model weights during training.
  • Validation set (10%): Used during training to monitor performance on unseen data and detect overfitting. The model never trains on this data.
  • Test set (30%): Used only once, after training is complete, to get an unbiased estimate of real-world performance.

Overfitting occurs when a model memorizes the training data and performs poorly on unseen data. Monitoring validation loss during training helps detect and prevent this.

The test set must never influence any training or model selection decision. It represents the “real world.”

Demo: Splitting Data into Train, Validation, and Test Datasets

const BATCH_SIZE = 16;
const SEED = 7687547;

const trainValTestSplit = (ds, nrows) => {
    const trainingValidationCount = Math.round(nrows * 0.7); // 70%
    const trainingCount = Math.round(nrows * 0.6);           // 60%

    // 70% for training+validation
    const trainingValidationData = ds
        .shuffle(nrows, SEED)
        .take(trainingValidationCount);

    // 30% for testing
    const testDataset = ds
        .shuffle(nrows, SEED)
        .skip(trainingValidationCount)
        .batch(BATCH_SIZE);

    // 60% for training
    const trainingDataset = trainingValidationData
        .take(trainingCount)
        .batch(BATCH_SIZE);

    // 10% for validation
    const validationDataset = trainingValidationData
        .skip(trainingCount)
        .batch(BATCH_SIZE);

    return { trainingDataset, validationDataset, testDataset };
};

Using the same SEED for shuffling ensures that training/validation and test splits are reproducible and non-overlapping.

Module 6 Summary

  • Explored feature engineering from text using TF-IDF
  • Built a complete tokenization → dictionary creation → IDF → encoder pipeline
  • Learned JavaScript function generators for memory-efficient data loading
  • Created datasets from generators using tf.data.generator and tf.data.zip
  • Implemented train/validation/test split using .shuffle(), .take(), .skip(), and .batch()

Module 7 – Building, Training, and Evaluating Machine Learning Model

Neural Network Overview

A neural network is composed of interconnected neurons arranged in layers.

What a neuron does:

$$\text{output} = \text{activation}\left(\sum_{i} w_i \cdot x_i + b\right)$$

Where $w_i$ are weights, $x_i$ are inputs, and $b$ is a bias term.

Common activation functions:

FunctionFormulaUse Case
Sigmoid$\sigma(x) = \frac{1}{1 + e^{-x}}$Binary classification output (outputs 0–1)
ReLU$\text{ReLU}(x) = \max(0, x)$Hidden layers (prevents vanishing gradients)
Softmax$\frac{e^{x_i}}{\sum e^{x_j}}$Multi-class classification output

Feed-forward neural network architecture:

graph LR
    I1((x1)) --> H1((h1))
    I1 --> H2((h2))
    I1 --> H3((h3))
    I2((x2)) --> H1
    I2 --> H2
    I2 --> H3
    I3((x3)) --> H1
    I3 --> H2
    I3 --> H3
    H1 --> O((output))
    H2 --> O
    H3 --> O
    subgraph Input Layer
        I1
        I2
        I3
    end
    subgraph Hidden Layer
        H1
        H2
        H3
    end
    subgraph Output Layer
        O
    end

Training process:

  1. Forward pass: Feed input through the network layer by layer to get predictions
  2. Loss calculation: Compute error between predictions and actual labels (e.g., binary cross-entropy for binary classification)
  3. Backpropagation: Compute gradients of the loss with respect to each weight
  4. Weight update: Adjust weights using an optimizer (e.g., Adam) in the direction that reduces loss
  5. Repeat for multiple epochs (iterations over the training data)

Overfitting vs. Underfitting:

graph LR
    A[Underfitting] --> B[Optimal Zone]
    B --> C[Overfitting]
    A:::bad
    C:::bad
    B:::good
  • Underfitting: Model has not learned enough — low training and validation accuracy.
  • Optimal: Good balance — low training loss, low validation loss.
  • Overfitting: Model has memorized training data — low training loss, rising validation loss.

Building Neural Network Using Layers API

The TensorFlow.js Layers API provides a high-level interface for building neural networks:

// Build a sequential (feed-forward) model
const model = tf.sequential();

// Add hidden layer: 5 neurons, ReLU activation, input shape = dictionary size
model.add(tf.layers.dense({
    inputShape: [EMBEDDING_SIZE], // N = number of features
    activation: "relu",
    units: 5
}));

// Add output layer: 1 neuron, Sigmoid activation (binary classification)
model.add(tf.layers.dense({
    activation: "sigmoid",
    units: 1
}));

// Compile: specify loss, optimizer, and metrics
model.compile({
    loss: "binaryCrossentropy",
    optimizer: tf.train.adam(0.06), // learning rate = 0.06
    metrics: ["accuracy"]
});

model.summary();

Model summary example (EMBEDDING_SIZE = 1000):

Layer (type)         Output shape    Param #
dense_Dense1         [null, 5]       5005     (1000×5 + 5 biases)
dense_Dense2         [null, 1]       6        (5×1 + 1 bias)
Total params: 5011
Trainable params: 5011

Integrate model summary into Visor:

tfvis.show.modelSummary({ name: 'Model Summary', tab: 'Model' }, model);

Demo: Building Neural Network Using Layers API

import * as tf from '@tensorflow/tfjs';
import * as tfvis from '@tensorflow/tfjs-vis';

let EMBEDDING_SIZE = 1000;
const render = true;

const buildModel = () => {
    const model = tf.sequential();
    model.add(tf.layers.dense({
        inputShape: [EMBEDDING_SIZE],
        activation: "relu",
        units: 5
    }));
    model.add(tf.layers.dense({
        activation: "sigmoid",
        units: 1
    }));
    model.compile({
        loss: "binaryCrossentropy",
        optimizer: tf.train.adam(0.06),
        metrics: ["accuracy"]
    });
    model.summary();

    if (render) {
        tfvis.show.modelSummary({ name: 'Model Summary', tab: 'Model' }, model);
    }

    return model;
};

Training Model Using TensorFlow.js

The key method for training on a tf.data.Dataset is model.fitDataset():

const trainResult = await model.fitDataset(trainingDataset, {
    epochs: TRAINING_EPOCHS,
    validationData: validationDataset,
    callbacks: [myCallback]
});

Callbacks allow you to hook into training events:

  • onEpochEnd(epoch, logs) — called after each epoch with logs.loss, logs.val_loss, logs.acc, logs.val_acc
  • onBatchEnd(batch, logs) — called after each mini-batch update

Early stopping prevents overfitting by halting training when the monitored metric stops improving:

const earlyStoppingCallback = tf.callbacks.earlyStopping({
    monitor: 'val_acc',
    minDelta: 0.3,   // minimum change to qualify as improvement
    patience: 5,     // stop after 5 epochs with no improvement
    verbose: 1
});

Demo: Training Neural Network Model

const TRAINING_EPOCHS = 10;
const render = true;

const trainModel = async (model, trainingDataset, validationDataset) => {
    const history = [];
    const surface = { name: 'onEpochEnd Performance', tab: 'Training' };

    const batchHistory = [];
    const batchSurface = { name: 'onBatchEnd Performance', tab: 'Training' };

    const messageCallback = new tf.CustomCallback({
        onEpochEnd: async (epoch, logs) => {
            history.push(logs);
            console.log("Epoch: " + epoch + " loss: " + logs.loss);
            if (render) {
                tfvis.show.history(surface, history, ['loss', 'val_loss', 'acc', 'val_acc']);
            }
        },
        onBatchEnd: async (batch, logs) => {
            batchHistory.push(logs);
            if (render) {
                tfvis.show.history(batchSurface, batchHistory, ['loss', 'acc']);
            }
        }
    });

    // Uncomment to enable early stopping:
    // const earlyStoppingCallback = tf.callbacks.earlyStopping({
    //     monitor: 'val_acc',
    //     minDelta: 0.3,
    //     patience: 5,
    //     verbose: 1
    // });

    const trainResult = await model.fitDataset(trainingDataset, {
        epochs: TRAINING_EPOCHS,
        validationData: validationDataset,
        callbacks: [messageCallback]
    });

    return model;
};

Demo: Visualizing Training Performance

Training history can be visualized using tfvis.show.history:

// Inside onEpochEnd callback:
history.push(logs);
tfvis.show.history(
    { name: 'onEpochEnd Performance', tab: 'Training' },
    history,
    ['loss', 'val_loss', 'acc', 'val_acc']
);

The Visor panel will show real-time charts of loss and accuracy over epochs, both for training and validation sets. Rising validation loss while training loss continues to decrease is a clear indicator of overfitting.

Demo: Evaluating Model Performance

After training, evaluate on the held-out test set using model.evaluateDataset():

const evaluateModel = async (model, testDataset) => {
    // evaluateDataset returns [loss, accuracy] tensors
    const modeResult = await model.evaluateDataset(testDataset);

    const testLoss = modeResult[0].dataSync()[0];
    const testAcc  = modeResult[1].dataSync()[0];

    console.log(`Loss on Test Dataset: ${testLoss.toFixed(4)}`);
    console.log(`Accuracy on Test Dataset: ${testAcc.toFixed(4)}`);
};

dataSync() is a synchronous method that extracts a raw JavaScript typed array from a tensor. Use it when you need the actual number values.

Model Performance Metrics

Accuracy tells you what fraction of predictions were correct, but does not distinguish where the model succeeds or fails.

Confusion Matrix (binary classification):

Predicted: 0 (Non-toxic)Predicted: 1 (Toxic)
Actual: 0 (Non-toxic)True Negatives (TN)False Positives (FP)
Actual: 1 (Toxic)False Negatives (FN)True Positives (TP)
  • True Positives (TP): Toxic comments correctly identified as toxic
  • True Negatives (TN): Non-toxic comments correctly identified as non-toxic
  • False Positives (FP): Non-toxic comments incorrectly flagged as toxic (Type I error)
  • False Negatives (FN): Toxic comments missed (Type II error)

Depending on the use case, the cost of FP vs. FN differs. In toxicity detection, a missed toxic comment (FN) may be more costly than a false alarm (FP).

Demo: Visualizing Model Performance Metrics

const getMoreEvaluationSummaries = async (model, testDataset) => {
    const allActualLabels = [];
    const allPredictedLabels = [];

    await testDataset.forEachAsync((row) => {
        // Collect actual labels
        const actualLabels = row['ys'].dataSync();
        actualLabels.forEach((x) => allActualLabels.push(x));

        // Make predictions and round to 0 or 1 (threshold = 0.5)
        const features = row['xs'];
        const predict = model.predictOnBatch(tf.squeeze(features, 1));
        const predictLabels = tf.round(predict).dataSync();
        predictLabels.forEach((x) => allPredictedLabels.push(x));
    });

    const actualTensor = tf.tensor1d(allActualLabels);
    const predictedTensor = tf.tensor1d(allPredictedLabels);

    // Accuracy
    const accuracyResult = await tfvis.metrics.accuracy(actualTensor, predictedTensor);
    console.log(`Accuracy: ${accuracyResult}`);

    // Per-class accuracy
    const perClassAccuracy = await tfvis.metrics.perClassAccuracy(actualTensor, predictedTensor);
    console.log(`Per Class Accuracy: ${JSON.stringify(perClassAccuracy, null, 2)}`);

    // Confusion matrix
    const confusionMatrix = await tfvis.metrics.confusionMatrix(actualTensor, predictedTensor);
    const confusionMatrixData = { values: confusionMatrix };
    console.log(`Confusion Matrix:\n${JSON.stringify(confusionMatrixData, null, 2)}`);

    if (render) {
        tfvis.render.confusionMatrix({ tab: 'Evaluation', name: 'Confusion Matrix' }, confusionMatrixData);
    }
};

Demo: Running Training in Node.js

A compelling feature of TensorFlow.js is that the same code runs on both the client (browser) and server (Node.js) with minimal changes.

// server.js
const tf = require('@tensorflow/tfjs');
require('@tensorflow/tfjs-node');
const express = require('express');
const app = express();

// Set static path for Express to serve files
app.use('/data', express.static('src/assets/data'));

app.get('/train', function (req, res) {
    run(); // same run() function as client.js
    res.send('Training started');
});

app.listen(9000, () => {
    console.log('Running server on port 9000 ...');
});

Key difference: For Node.js, use a file path or HTTP URL for CSV loading:

// Client-side (relative URL):
const csvUrl = 'data/toxic_data_sample.csv';

// Server-side (file protocol or HTTP):
const csvUrl = 'file:///path/to/data/toxic_data_sample.csv';
// or: const csvUrl = 'http://localhost:9000/data/toxic_data_sample.csv';

Module 7 Summary

  • Introduced neural networks: neurons, activation functions, feed-forward architecture, training via backpropagation
  • Built a model using tf.sequential() and tf.layers.dense() (Layers API)
  • Trained the model using model.fitDataset() with custom callbacks for logging and visualization
  • Implemented early stopping to prevent overfitting
  • Visualized training history (loss and accuracy) using tfvis.show.history
  • Evaluated on test data using model.evaluateDataset()
  • Extracted confusion matrix and per-class accuracy using tfvis.metrics
  • Demonstrated running the same client-side code on Node.js with minimal changes

Module 8 – Saving and Loading Machine Learning Model

Model Export Options

TensorFlow.js supports multiple persistence mechanisms depending on the deployment target:

graph TD
    M[Trained Model] --> A[Browser: localStorage]
    M --> B[Browser: IndexedDB]
    M --> C[Local: Download as files]
    M --> D[Remote: HTTP server upload]
    A --> A1[Small models < 10 MB]
    B --> B1[Larger models]
    C --> C1[model.json + weights.bin]
StoragePath PrefixNotes
localStoragelocalstorage://model-nameString-based storage, small models (<10 MB)
IndexedDBindexeddb://model-nameObject storage, larger models, no expiry
Downloaddownloads://model-nameDownloads model.json + weights.bin
HTTP serverhttp://server/uploadRequires PUT endpoint on server

Downloading a model produces two files:

  • model.json — network topology (architecture)
  • weights.bin — binary file with trained weight values

Demo: Exporting Trained Model

const MODEL_ID = 'toxicity-detector-tfidf';
const IDF_STORAGE_ID = 'toxicity-idfs';
const DICTIONARY_STORAGE_ID = 'toxicity-tfidf-dictionary';

const exportModel = async (model, modelID, idfs, idfsStorageID, dictionary, dictionaryStorageID) => {
    const modelPath = `localstorage://${modelID}`;
    // Alternative: const modelPath = `downloads://${modelID}`;

    const saveModelResults = await model.save(modelPath);
    console.log('Model exported');

    // Also save preprocessing artifacts needed for inference
    localStorage.setItem(dictionaryStorageID, JSON.stringify(dictionary));
    localStorage.setItem(idfsStorageID, JSON.stringify(idfs));
    console.log('Dictionary and IDFs exported');

    return saveModelResults;
};

Important: Along with the model, save any preprocessing artifacts (dictionary, IDFs) that are needed to transform new inputs before passing them to the model. Without them, inference is impossible.

You can inspect the saved model in Chrome DevTools: ApplicationLocal Storage → your dev server URL. You’ll see entries for model_info, topology, weight_data, weight_specs, and your custom items.

Loading a Model

// Load a Layers API model
const model = await tf.loadLayersModel('localstorage://model-name');

// Check if model exists before loading
const models = await tf.io.listModels();
if (models['localstorage://model-name']) {
    const model = await tf.loadLayersModel('localstorage://model-name');
}

// Load a graph model (e.g., converted from Python TensorFlow SavedModel)
const graphModel = await tf.loadGraphModel('http://server/model/model.json');
MethodUse Case
tf.loadLayersModel(path)Models created with the Layers API
tf.loadGraphModel(path)Models converted from Python TensorFlow (SavedModel/Keras)

Demo: Loading Trained TensorFlow.js Model

const loadModel = async () => {
    const modelPath = `localstorage://${MODEL_ID}`;

    const models = await tf.io.listModels();
    if (models[modelPath]) {
        console.log('Model found, loading...');
        const model_loaded = await tf.loadLayersModel(modelPath);
        return model_loaded;
    } else {
        console.log('No model available');
        return null;
    }
};

// In the main run function:
const model_loaded = await loadModel();
if (model_loaded) {
    model_loaded.summary();
    // Ready to make predictions...
}

Module 8 Summary

  • Reviewed all model export/persistence options: localStorage, IndexedDB, file download, HTTP server
  • Used model.save(modelPath) to persist the trained model topology and weights
  • Saved preprocessing artifacts (dictionary and IDFs) alongside the model in localStorage
  • Used tf.loadLayersModel(path) to reload the model from localStorage
  • Distinguished between loadLayersModel (Layers API models) and loadGraphModel (converted Python models)

Module 9 – Predicting Using Trained Machine Learning Model

Model Scoring

Model scoring (also called inference or prediction) is the final step of the ML deployment pipeline. Given a new input, the model outputs a prediction.

graph LR
    A[Raw Text Input] --> B[Preprocessing<br/>Same as training]
    B --> C[Numeric Feature Vector]
    C --> D[Trained Model<br/>model.predict]
    D --> E[Probability Score<br/>0 to 1]
    E --> F{≥ 0.5?}
    F -->|Yes| G[Toxic]
    F -->|No| H[Non-toxic]

Critical principle: The exact same preprocessing applied during training must be applied to new inputs during inference. If TF-IDF features were used for training, the same vocabulary (dictionary) and IDFs must be used when encoding new text.

Demo: Predicting Using Trained TensorFlow.js Model

const predictResults = async (sentence, model) => {
    console.log('Prediction started');

    // Load preprocessing artifacts from localStorage
    const idfObject = localStorage.getItem(IDF_STORAGE_ID);
    const idfs = JSON.parse(idfObject);

    const dictionaryObject = localStorage.getItem(DICTIONARY_STORAGE_ID);
    const dictionary = JSON.parse(dictionaryObject);

    // Apply the same preprocessing as training
    const encoded = encoder(sentence.toLowerCase().trim(), dictionary, idfs);
    const encodedTensor = tf.tensor2d([encoded], [1, dictionary.length]);

    // Make prediction
    const predictionResult = model.predict(encodedTensor);
    const predictionScore = predictionResult.dataSync(); // Extract value from tensor

    // Apply threshold
    const predictedClass = (predictionScore >= 0.5) ? "toxic" : "non-toxic";
    const resultMessage = `Probability: ${predictionScore.toFixed(4)}, Class: ${predictedClass}`;
    console.log(resultMessage);

    return predictedClass;
};

Materialize UI

The application UI is built using the Materialize CSS framework — a responsive front-end framework based on Google’s Material Design principles.

Key UI elements:

  • Navigation bar with logo and title
  • TensorFlow.js version and backend display
  • Model flavor selector (4 options)
  • Visor toggle switch (show/hide tfvis panel)
  • Text area for comment input
  • Train / Load / Predict buttons
  • Result display area (Material Design chips)

Install:

npm install materialize-css
npm install material-icons jquery

Demo: Setting up Materialize UI

// client.js
import * as tf from '@tensorflow/tfjs';
import * as tfvis from '@tensorflow/tfjs-vis';
import '@tensorflow/tfjs-backend-wasm';
import "regenerator-runtime/runtime";
import * as model from './model';
import * as modelPython from './model_python';

import $ from "jquery";
import "materialize-css";
import "material-icons";
import "./main.scss";

M.AutoInit(); // Initialize all Materialize components

main.scss:

@import '~materialize-css/sass/materialize';

Demo: Integrating Steps with UI

// Initialize: show TF version and backend
const init = async () => {
    await tf.ready();
    const init_message = "Powered by TensorFlow.js - version: "
        + tf.version.tfjs + " with backend: " + tf.getBackend();
    $('#init').text(init_message);
};
init();

// Track selected model option
let modelOption = 1;
$('select').on('change', (e) => {
    modelOption = parseInt(e.target.value);
});

// Toggle Visor
$("#toggle_visor").on('change', () => {
    tfvis.visor().toggle();
});

// Train button
$('#btn_train').on('click', async () => {
    switch (modelOption) {
        case 1:
            $('#btn_train').addClass("disabled");
            M.toast({ html: 'Training Started!' });
            await model.train();
            break;
        case 2:
            M.toast({ html: 'No training needed for Python exported model!' });
            break;
    }
});

// Load button
let model_loaded;
$('#btn_load').on('click', async () => {
    switch (modelOption) {
        case 1:
            model_loaded = await model.load();
            break;
        case 2:
            model_loaded = await modelPython.load();
            break;
    }
    $('#btn_load').addClass("disabled");
    $('#btn_predict').removeClass("disabled");
});

// Predict button
$('#btn_predict').on('click', async () => {
    const message = $('#textarea-message').val();
    if (message.trim().length <= 0) {
        M.toast({ html: 'Empty message. Nothing to predict!' });
        return;
    }

    $("#chip_result").empty();
    let predictedClass;

    switch (modelOption) {
        case 1:
            predictedClass = await model.predict(message, model_loaded);
            break;
        case 2:
            predictedClass = await modelPython.predict(message, model_loaded);
            break;
    }

    $("#chip_result").append(
        `Predicted Label: <div class="chip pink-text">${predictedClass}</div>`
    );
    $('#btn_predict').removeClass("disabled");
});

TensorFlow.js Converter

A common production pattern is to train a model in Python TensorFlow (where mature data science tooling exists) and then deploy it to JavaScript environments using the TensorFlow.js converter.

graph LR
    A[Python TensorFlow<br/>Train model] --> B[SavedModel / Keras .h5]
    B --> C[tfjs_converter CLI]
    C --> D[TF.js format<br/>model.json + weights.bin]
    D --> E[Browser<br/>tf.loadGraphModel]
    D --> F[Node.js<br/>tf.loadGraphModel]
    D --> G[Mobile Native<br/>React Native]

Use cases for this pattern:

  • Your organization has existing Python ML pipelines and models
  • You want client-side inference (no API exposure, low latency, privacy)
  • You need near-real-time inference on live audio or video feeds
  • Optionally: retrain the converted model in TF.js using additional data

Conversion workflow:

# Install the converter
pip install tensorflowjs

# Convert a Keras H5 model
tensorflowjs_converter \
    --input_format keras \
    /path/to/model.h5 \
    /path/to/output/tfjs_model

This produces a model.json (architecture + weight manifest) and one or more .bin weight files.

Demo: Predicting Using Python Exported Model

Python setup:

# Create conda environment
conda create --name pstoxicity python=3.6
conda activate pstoxicity
pip install -r requirements.txt
# requirements.txt: tensorflow, pandas, jupyterlab, scikit-learn, tensorflowjs

model_python.js — TF.js model that loads the converted Python model:

import * as tf from '@tensorflow/tfjs';
import * as tfvis from '@tensorflow/tfjs-vis';
import dictionary from './assets/models/tfjs_python_toxicity/dictionary.json';
import idfs from './assets/models/tfjs_python_toxicity/idfs.json';

const MODEL_PATH = 'models/tfjs_python_toxicity/model.json';

export const load = () => loadModel();
export const predict = (sentence, model) => predictResults(sentence, model);

const loadModel = async () => {
    // Load graph model converted from Python TensorFlow
    const model_python = await tf.loadLayersModel(MODEL_PATH);
    return model_python;
};

const predictResults = async (sentence, model) => {
    console.log('Prediction started (Python model)');

    // Preprocess using the same dictionary and IDFs from Python training
    const encoded = encoder(sentence.toLowerCase().trim(), dictionary, idfs);
    const encodedTensor = tf.tensor2d([encoded], [1, dictionary.length]);

    // Make prediction
    const predictionResult = model.predict(encodedTensor);
    const predictionScore = predictionResult.dataSync();

    const predictedClass = (predictionScore >= 0.5) ? "toxic" : "non-toxic";
    const resultMessage = `Probability: ${predictionScore}, Class: ${predictedClass}`;
    console.log(resultMessage);
    return predictedClass;
};

When using a Python-trained model, import the dictionary.json and idfs.json directly from the model assets folder — no need to store them in localStorage.

Module 9 Summary

  • Implemented model scoring: applied the same preprocessing as training, then used model.predict() and extracted results with dataSync()
  • Built a responsive UI using Materialize CSS wired with jQuery event handlers
  • Connected all ML workflow steps to the UI: train → load → predict
  • Used the TensorFlow.js converter to convert a Python-trained Keras model to TF.js format
  • Loaded and used the converted model with tf.loadLayersModel() and the pre-exported dictionary and IDFs

Module 10 – Using Pre-trained Models with TensorFlow.js

Transfer Learning with TensorFlow.js

Transfer learning is one of the most impactful techniques in modern deep learning. The key idea: knowledge learned by a model on one task can be applied to a different (but related) task.

graph LR
    A[Huge dataset<br/>+ Compute power] --> B[Pre-trained Model<br/>e.g. Universal Sentence Encoder]
    B --> C[Extract embeddings<br/>Knowledge transfer]
    C --> D[Fine-tune / retrain<br/>on small domain dataset]
    D --> E[Task-specific model<br/>Toxicity detection]

Why transfer learning is powerful:

  • Pre-trained models encode rich representations learned from massive datasets that would be impractical to recreate
  • You only need a small domain-specific dataset to fine-tune
  • Significantly reduces training time and data requirements

Types of transfer learning (simplified):

  1. Feature extraction: Use the pre-trained model as a frozen feature extractor, only train a new classification head on top
  2. Fine-tuning: Unfreeze some layers of the pre-trained model and continue training on domain data

Universal Sentence Encoder (USE)

The Universal Sentence Encoder (USE) is a pre-trained model developed by Google that transforms text into dense, context-aware 512-dimensional embeddings. It was trained on a massive corpus and uses the Transformer architecture — the same family of models behind modern large language models.

Key properties:

  • Each sentence → 512 floating-point numbers
  • Context is preserved (unlike TF-IDF)
  • Vocabulary of 8,000 words
  • The Lite version is optimized for use in the browser

Comparison: TF-IDF vs. USE Embeddings:

AspectTF-IDFUSE Embeddings
Vector sizeUp to thousands (vocabulary size)Fixed 512
Context preservedNoYes
Training requiredNo (rule-based)Pre-trained model load
SemanticsWord count-basedMeaning-based
Memory footprintLarger (sparse)Smaller (dense)

Demo: Creating Features from Universal Sentence Encoder

import * as tf from '@tensorflow/tfjs';
import * as use from '@tensorflow-models/universal-sentence-encoder';

// Install:
// npm install @tensorflow-models/universal-sentence-encoder

let EMBEDDING_SIZE = 512; // USE Lite produces 512-dimensional embeddings
let use_encoder;

// Load the USE model
const loadUSEModel = async () => {
    use_encoder = await use.load();
    console.log('Universal Sentence Encoder loaded');
    return use_encoder;
};

// Updated prepareData that uses USE instead of TF-IDF
const prepareData = (encoder) => {
    const preprocess = async ({ xs, ys }) => {
        const comment = xs['comment_text'];
        const trimmedComment = comment.toLowerCase().trim();

        // Embed the comment using USE
        const encodedTensor = await encoder.embed(trimmedComment);
        // embed() returns a 2D tensor [1, 512]

        return {
            xs: encodedTensor,
            ys: tf.tensor2d([ys['toxic']], [1, 1])
        };
    };

    const readData = tf.data.csv(csvUrl, {
        columnConfigs: { toxic: { isLabel: true } }
    }).map(preprocess);

    return readData;
};

Demo: Performing Transfer Learning on USE Encoded Features

The model architecture stays the same, but EMBEDDING_SIZE changes from 1000 (TF-IDF vocabulary) to 512 (USE output size):

const MODEL_ID = 'toxicity-detector-use';
let EMBEDDING_SIZE = 512;
let use_encoder; // Global reference to loaded USE model

const buildModel = () => {
    const model = tf.sequential();
    model.add(tf.layers.dense({
        inputShape: [EMBEDDING_SIZE], // 512 for USE
        activation: "relu",
        units: 5
    }));
    model.add(tf.layers.dense({
        activation: "sigmoid",
        units: 1
    }));
    model.compile({
        loss: "binaryCrossentropy",
        optimizer: tf.train.adam(0.06),
        metrics: ["accuracy"]
    });
    return model;
};

// Export: no need to save dictionary or IDFs
const exportModel = async (model, modelID) => {
    const modelPath = `localstorage://${modelID}`;
    const saveModelResults = await model.save(modelPath);
    console.log('USE-based model exported');
    return saveModelResults;
};

// Load: also reload the USE encoder
const loadModel = async () => {
    const modelPath = `localstorage://${MODEL_ID}`;
    const models = await tf.io.listModels();
    if (models[modelPath]) {
        const model_loaded = await tf.loadLayersModel(modelPath);
        // Also load USE for inference
        use_encoder = await use.load();
        return model_loaded;
    }
    return null;
};

// Predict: use USE encoder instead of TF-IDF
const predictResults = async (sentence, model) => {
    console.log('Prediction started (USE model)');

    // Encode using Universal Sentence Encoder
    const encodedTensor = await use_encoder.embed(sentence.toLowerCase().trim());

    // Make prediction
    const predictionResult = model.predict(encodedTensor);
    const predictionScore = predictionResult.dataSync();

    const predictedClass = (predictionScore >= 0.5) ? "toxic" : "non-toxic";
    console.log(`Probability: ${predictionScore}, Class: ${predictedClass}`);
    return predictedClass;
};

Toxicity Detection Model

In addition to transfer learning with USE, TensorFlow.js provides a fully pre-trained, ready-to-use Toxicity Detection model from the @tensorflow-models/toxicity package.

Key facts:

  • Multi-headed: detects multiple toxicity categories simultaneously (toxic, severe_toxic, obscene, threat, insult, identity_attack)
  • Built on top of Universal Sentence Encoder
  • Trained on the Civil Comments dataset (~2 million comments labeled by multiple human experts)
  • Threshold parameter: controls confidence required to label a comment as matching a category

Output format:

[
    {
        label: 'toxic',
        results: [
            { probabilities: [0.95, 0.05], match: false },  // non-toxic sentence 1
            { probabilities: [0.1, 0.9], match: true }       // toxic sentence 2
        ]
    },
    {
        label: 'insult',
        results: [...]
    },
    // ... other categories
]

Where match: true means the probability for class 1 exceeds the threshold.

Demo: Using the TensorFlow.js Toxicity Detection Model

import * as tf from '@tensorflow/tfjs';
import * as toxicity from '@tensorflow-models/toxicity';

// Install:
// npm install @tensorflow-models/toxicity

const THRESHOLD = 0.9;
let toxicityModel;

export const load = async () => {
    toxicityModel = await toxicity.load(THRESHOLD);
    return toxicityModel;
};

export const predict = async (sentence, model) => {
    console.log('Toxicity detection started');

    // classify() accepts an array of sentences
    const predictions = await model.classify([sentence.toLowerCase().trim()]);
    console.log(predictions);

    const predictedClasses = [];

    predictions.forEach((category) => {
        // results[0] is for our first (and only) sentence
        if (category.results[0].match) {
            predictedClasses.push(category.label);
        }
    });

    if (predictedClasses.length === 0) {
        predictedClasses.push('non-toxic');
    }

    console.log('Predicted classes:', predictedClasses);
    return predictedClasses;
};

Wire to UI in client.js:

import * as modelToxicity from './model_toxicity';

// Train button (no training needed for pre-trained model)
// case 4: M.toast({ html: 'No training needed!' }); break;

// Load button
case 4:
    model_loaded = await modelToxicity.load();
    break;

// Predict button
case 4:
    predictedClasses = await modelToxicity.predict(message, model_loaded);
    predictedClasses.forEach((cls) => {
        $("#chip_result").append(
            `<div class="chip pink-text">${cls}</div>`
        );
    });
    break;

Module 10 Summary

The complete application now supports four model flavors:

#ModelFeaturesTraining
1Custom TF.js modelTF-IDFIn-browser training
2Python-exported modelTF-IDF (Python)Pre-trained in Python
3Transfer learning (USE)512-dim USE embeddingsIn-browser fine-tuning
4Pre-trained toxicity modelUSE (internal)No training needed
  • Learned transfer learning and why it is so effective
  • Used Universal Sentence Encoder to generate rich 512-dimensional text embeddings
  • Built and fine-tuned a classification head on USE embeddings
  • Used the fully pre-trained @tensorflow-models/toxicity model for multi-class toxicity detection

Module 11 – What’s Next?

The toxicity detection application demonstrates TensorFlow.js across the complete ML workflow. Here are directions to take the journey further:

Extend the Existing Use Case

  • Refine the neural network architecture — add more hidden layers, try different neuron counts, experiment with Dropout for regularization
  • Add more visualizations — tfvis or external libraries like Plotly.js or Chart.js
  • Train on the full dataset — the exercise files include the complete Kaggle dataset
  • Multi-class toxicity — extend the model to predict all six toxicity categories, not just toxic
  • Sentiment analysis — apply the same NLP pipeline to predict positive/negative sentiment
  • Text highlighting — highlight toxic spans in the input text
  • Emoji recommendation — learn the tone of a message and suggest relevant emojis

Explore Other ML Domains with TensorFlow.js

TensorFlow.js has rich pre-built models for domains beyond NLP:

DomainPre-built Models
Computer VisionMobileNet (classification), COCO-SSD (object detection), PoseNet / MoveNet (pose estimation), DeepLab (segmentation)
AudioSpeech commands recognition
GenerativeModels via Magenta.js
Faceface-api.js for detection and recognition

Explore Other Platforms

  • React Native — Deploy TensorFlow.js models in mobile native applications
  • Electron — Desktop applications
  • IoT Edge devices — Raspberry Pi and similar hardware

Go Deeper into TensorFlow.js

  • Explore the TensorFlow.js official API documentation for the full Layers and Ops API reference
  • Experiment with Recurrent Neural Networks (LSTM, GRU) for sequence modeling
  • Try Convolutional Neural Networks (CNN) for image classification from scratch
  • Explore WebGL shaders for custom backend operations
  • Contribute to or use ml5.js for higher-level abstractions

Key Resources


Architecture Diagrams

TensorFlow.js Backend Architecture

graph TD
    App[TensorFlow.js Application]
    App --> LA[Layers API<br/>tf.sequential, tf.layers.dense]
    App --> OA[Ops / Core API<br/>tf.add, tf.matMul, ...]
    App --> DA[TFJS Data<br/>tf.data.csv, tf.data.generator]
    App --> VA[TFJS Vis<br/>tfvis.show, tfvis.render]
    LA --> OA
    OA --> BE[Backend Selector]
    BE --> WebGL[WebGL Backend<br/>GPU — 100× CPU<br/>97%+ devices]
    BE --> Wasm[WebAssembly Backend<br/>CPU SIMD — 10–30× CPU<br/>90%+ devices]
    BE --> CPU[CPU Backend<br/>Vanilla JS<br/>100% devices]
    BE --> Node[Node.js Backend<br/>TF C++ Bindings<br/>GPU/TPU support]

Model Training Pipeline

flowchart TD
    A[Raw CSV Data] --> B[tf.data.csv]
    B --> C[forEach - Extract comments + labels]
    C --> D[Tokenize - Remove stopwords]
    D --> E{Feature Method}
    E --> E1[TF-IDF Encoding<br/>Dictionary + IDF]
    E --> E2[USE Embeddings<br/>use.load + embed]
    E1 --> F[tf.data.Dataset]
    E2 --> F
    F --> G[shuffle + take/skip + batch]
    G --> H[Train / Val / Test Split]
    H --> I[buildModel<br/>tf.sequential + dense layers]
    I --> J[model.compile<br/>binaryCrossentropy + adam]
    J --> K[model.fitDataset<br/>trainingDataset + validationDataset]
    K --> L{Overfitting?}
    L -->|Yes| M[EarlyStopping callback]
    L -->|No| N[evaluateDataset<br/>testDataset]
    M --> N
    N --> O[Accuracy + Confusion Matrix]
    O --> P[model.save<br/>localStorage / downloads]

Client-Server ML Deployment Patterns

graph LR
    subgraph Traditional Server-Side ML
        C1[Browser] -->|Raw data| S1[ML Server]
        S1 -->|Prediction| C1
    end
    subgraph TensorFlow.js Client-Side
        C2[Browser] -->|Local inference| M2[TF.js Model<br/>in Browser]
        M2 -->|Instant result| C2
        note2[No data leaves device]
    end
    subgraph TensorFlow.js Server-Side
        C3[Browser] -->|Request| S3[Node.js + TF.js]
        S3 -->|Prediction| C3
        note3[TF C++ bindings<br/>GPU/TPU support]
    end

Full Application Architecture

graph TD
    UI[Materialize CSS UI]
    UI --> CB[client.js<br/>jQuery event handlers]
    CB --> M1[model.js<br/>TF-IDF custom model]
    CB --> M2[model_python.js<br/>Python-converted model]
    CB --> M3[model_transfer.js<br/>USE transfer learning]
    CB --> M4[model_toxicity.js<br/>Pre-trained toxicity model]
    M1 --> TF[TensorFlow.js]
    M2 --> TF
    M3 --> TF
    M4 --> TF
    TF --> VIS[tfvis Visor<br/>Charts + Model Summary]
    M1 --> LS[localStorage<br/>model + dict + idfs]
    M2 --> AS[Static Assets<br/>model.json + weights.bin]
    M3 --> LS
    M3 --> USE[Universal Sentence Encoder<br/>@tensorflow-models/use]
    M4 --> TOX[Toxicity Model<br/>@tensorflow-models/toxicity]

The ML Workflow for Toxicity Detection

flowchart LR
    P[Problem<br/>Toxic comment detection] --> D
    D[Data<br/>Kaggle dataset<br/>159K Wikipedia comments] --> M
    M[Modeling<br/>TF-IDF or USE features<br/>Dense neural network<br/>Binary cross-entropy loss] --> DEP
    DEP[Deployment<br/>localStorage or HTTP server<br/>Client browser inference<br/>4 model flavors] --> P

This documentation covers the complete course content from environment setup through model deployment, including all code examples from the demo files.


Search Terms

machine · tensorflow.js · deep · neural · networks · data · science · model · performance · toxicity · browser · detection · features · network · running · trained · architecture · loading · predicting · tensor · visualizing · api · case · encoder

Interested in this course?

Contact us to book it or get a custom training plan for your team.