Table of Contents
- Module 1 – Course Overview
- Module 2 – Introduction
- Module 3 – Setting up TensorFlow.js Environment
- Module 4 – Understanding TensorFlow.js Core Concepts
- Module 5 – Preparing Data for Machine Learning Model: Part 1
- Module 6 – Preparing Data for Machine Learning Model: Part 2
- Module 7 – Building, Training, and Evaluating Machine Learning Model
- Neural Network Overview
- Building Neural Network Using Layers API
- Demo: Building Neural Network Using Layers API
- Training Model Using TensorFlow.js
- Demo: Training Neural Network Model
- Demo: Visualizing Training Performance
- Demo: Evaluating Model Performance
- Model Performance Metrics
- Demo: Visualizing Model Performance Metrics
- Demo: Running Training in Node.js
- Summary
- Module 8 – Saving and Loading Machine Learning Model
- Module 9 – Predicting Using Trained Machine Learning Model
- Module 10 – Using Pre-trained Models with TensorFlow.js
- Module 11 – What’s Next?
- Architecture Diagrams
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:
| Backend | Mechanism | Performance | Coverage |
|---|---|---|---|
| CPU | Plain vanilla JavaScript | Baseline | 100% |
| WebGL | GPU-accelerated via WebGL API | Up to 100× over CPU | 97%+ of devices globally |
| WebAssembly (Wasm) | CPU acceleration with portable assembly | 10×–30× over CPU | 90%+ of devices globally |
Server-side backend:
| Backend | Mechanism | Performance |
|---|---|---|
| Node | Low-level TensorFlow C/C++ API bindings | Comparable 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:
- Server-side — Comments are sent to a server where ML frameworks detect toxicity.
- 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:
- Custom TF.js model with TF-IDF features
- Model trained in Python and converted to TF.js format
- Custom TF.js model with Universal Sentence Encoder features (transfer learning)
- Fully pre-trained TF.js toxicity model
Course Structure
The course follows an incremental approach:
- Module 3 — Set up TensorFlow.js environment (browser and Node.js)
- Module 4 — Core concepts: tensors, operations, memory management
- Modules 5–6 — Data preparation and feature engineering for toxicity detection
- Module 7 — Build, train, and evaluate a neural network model
- Module 8 — Save and load the trained model
- Module 9 — Predict with the trained model, wire to a UI, use a Python-exported model
- Module 10 — Use pre-trained models, transfer learning with Universal Sentence Encoder
- 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.jsonneeds to be version-controlled;node_modulesis reproducible
If you come from Python, think of
package.jsonasrequirements.txtand npm aspip.
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()returnsundefinedbefore any TensorFlow operation has run because the backend initializes lazily. Usetf.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
| Setup | How | Best For |
|---|---|---|
| Script tag (CDN) | <script src="https://cdn.jsdelivr.net/..."> | Quick prototyping |
| npm + Parcel | npm install @tensorflow/tfjs | Medium/large apps |
| npm + Webpack | npm install @tensorflow/tfjs | Production builds |
| Node.js | require('@tensorflow/tfjs-node') | Server-side execution |
Key functions covered:
tf.getBackend()— returns the currently active backendtf.setBackend('cpu' | 'webgl' | 'wasm')— switch backendstf.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:
| Concept | Description |
|---|---|
| Rank | Number of dimensions (0 = scalar, 1 = vector, 2 = matrix, …) |
| Shape | Size along each dimension, e.g. [2, 3] means 2 rows × 3 columns |
| Data type (dtype) | float32 (default), int32, bool, string |
| Immutability | Tensors cannot be changed; operations create new tensors |
Tensor types:
tf.scalar(value)— rank 0, single valuetf.tensor1d([...])— rank 1 vectortf.tensor2d([[...],[...]])— rank 2 matrixtf.tensor(data, shape, dtype)— general form for any ranktf.variable(tensor)— mutable wrapper, supportsassign()
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 tofloat32by 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:
| Function | Purpose |
|---|---|
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().numTensors | Inspect the current number of tensors in memory |
tf.tidyis 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(), andkeep()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]
- Problem: Understand the business requirement; formulate the ML approach.
- Data: Extract, clean, explore, and visualize the data.
- Modeling: Feature engineering → build model → train → evaluate → iterate.
- 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
toxiccolumn is used as the output label (binary classification) - The
comment_textcolumn 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:
| Source | API |
|---|---|
| In-memory array | tf.data.array(array) |
| CSV file | tf.data.csv(url, options) |
| JavaScript generator function | tf.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:
| Operation | Purpose |
|---|---|
.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: line1 → line2 → line4 → 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-copyplugin and place data files in astaticfolder that gets copied to thedistoutput 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.csvto read CSV data and create atf.data.Dataset - Learned async/await programming patterns essential for TensorFlow.js operations
- Created a simple bar chart using
tfvis.render.barchartto 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
SEEDfor 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.generatorandtf.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:
| Function | Formula | Use 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:
- Forward pass: Feed input through the network layer by layer to get predictions
- Loss calculation: Compute error between predictions and actual labels (e.g., binary cross-entropy for binary classification)
- Backpropagation: Compute gradients of the loss with respect to each weight
- Weight update: Adjust weights using an optimizer (e.g., Adam) in the direction that reduces loss
- 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 withlogs.loss,logs.val_loss,logs.acc,logs.val_acconBatchEnd(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()andtf.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]
| Storage | Path Prefix | Notes |
|---|---|---|
| localStorage | localstorage://model-name | String-based storage, small models (<10 MB) |
| IndexedDB | indexeddb://model-name | Object storage, larger models, no expiry |
| Download | downloads://model-name | Downloads model.json + weights.bin |
| HTTP server | http://server/upload | Requires 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: Application → Local 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');
| Method | Use 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) andloadGraphModel(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.jsonandidfs.jsondirectly 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 withdataSync() - 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):
- Feature extraction: Use the pre-trained model as a frozen feature extractor, only train a new classification head on top
- 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:
| Aspect | TF-IDF | USE Embeddings |
|---|---|---|
| Vector size | Up to thousands (vocabulary size) | Fixed 512 |
| Context preserved | No | Yes |
| Training required | No (rule-based) | Pre-trained model load |
| Semantics | Word count-based | Meaning-based |
| Memory footprint | Larger (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:
| # | Model | Features | Training |
|---|---|---|---|
| 1 | Custom TF.js model | TF-IDF | In-browser training |
| 2 | Python-exported model | TF-IDF (Python) | Pre-trained in Python |
| 3 | Transfer learning (USE) | 512-dim USE embeddings | In-browser fine-tuning |
| 4 | Pre-trained toxicity model | USE (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/toxicitymodel 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:
| Domain | Pre-built Models |
|---|---|
| Computer Vision | MobileNet (classification), COCO-SSD (object detection), PoseNet / MoveNet (pose estimation), DeepLab (segmentation) |
| Audio | Speech commands recognition |
| Generative | Models via Magenta.js |
| Face | face-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
- ml5.js: ml5js.org
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