MACHINE LEARNING

How to Implement Collaborative Filtering Using Matrix Factorization in JavaScript

Learn how to use matrix factorization and collaborative filtering to build a more personalized, scalable recommendation engine in JavaScript with TensorFlow.js.

🧠 Introduction

While content-based filtering relies on item features, collaborative filtering uses user-item interactions—ratings, views, clicks—to predict preferences.

One of the most powerful collaborative filtering techniques is Matrix Factorization, where we decompose a user-item matrix into latent factors that describe preferences and attributes.

We’ll use TensorFlow.js to implement a lightweight version directly in JavaScript.


🧾 What is Matrix Factorization?

You start with a user-item matrix:

Hobbit1984DuneBNW
UserA5?3?
UserB?4?5

Your goal is to predict the missing values (the “?”) using:

R ≈ U × Vᵗ
Where:

  • R is the user-item matrix
  • U is the user-factor matrix (preferences)
  • V is the item-factor matrix (attributes)

🛠️ Step-by-Step: Collaborative Filtering in JavaScript


🔢 Step 1: Define Ratings Data

const ratings = [
  { user: 0, item: 0, rating: 5 },
  { user: 0, item: 2, rating: 3 },
  { user: 1, item: 1, rating: 4 },
  { user: 1, item: 3, rating: 5 }
];

const numUsers = 2;
const numItems = 4;
const numFactors = 3;

🧠 Step 2: Initialize User and Item Matrices

const tf = require('@tensorflow/tfjs-node'); // or use CDN in browser

let userMatrix = tf.variable(tf.randomNormal([numUsers, numFactors]));
let itemMatrix = tf.variable(tf.randomNormal([numItems, numFactors]));

🔁 Step 3: Training with Gradient Descent

const learningRate = 0.01;
const optimizer = tf.train.adam(learningRate);
const epochs = 200;

function loss() {
  return tf.tidy(() => {
    const predictions = tf.gather(userMatrix, ratings.map(r => r.user))
      .mul(tf.gather(itemMatrix, ratings.map(r => r.item)))
      .sum(1);
    const labels = tf.tensor1d(ratings.map(r => r.rating));
    return predictions.sub(labels).square().mean();
  });
}

async function train() {
  for (let i = 0; i < epochs; i++) {
    optimizer.minimize(loss);
    if (i % 50 === 0) {
      const l = loss().dataSync()[0];
      console.log(`Epoch ${i}: Loss = ${l.toFixed(4)}`);
    }
  }
}

🔍 Step 4: Make Predictions

async function recommend(userId, topN = 2) {
  await train(); // ensure training is done
  const userVec = userMatrix.slice([userId, 0], [1, numFactors]);
  const scores = tf.matMul(userVec, itemMatrix.transpose()).dataSync();

  const results = scores
    .map((score, i) => ({ item: i, score }))
    .sort((a, b) => b.score - a.score)
    .slice(0, topN);

  console.log(`Recommendations for User ${userId}:`, results);
}

📊 Sample Output

Epoch 0: Loss = 12.8423
Epoch 50: Loss = 1.9382
...
Recommendations for User 0:
[ { item: 1, score: 4.72 }, { item: 3, score: 3.96 } ]

✅ Benefits of Matrix Factorization

  • Captures hidden relationships (e.g., “likes sci-fi” or “enjoys drama”)
  • Learns user taste implicitly
  • Scales to large datasets

🚀 Advanced Ideas

  • 🧠 Add regularization to prevent overfitting
  • 🗃️ Use real datasets like MovieLens
  • 💾 Store model weights in browser localStorage or server
  • 🖼️ Visualize learned latent factors
  • 🌐 Convert to TensorFlow Lite for edge deployment

🧩 Summary

By adding collaborative filtering with matrix factorization, your JavaScript recommendation engine becomes much smarter and more personalized. It’s a solid stepping stone toward full production-grade systems.

You’ve now built:

  • A content-based recommender
  • A feedback loop for personalization
  • A collaborative filtering engine with real ML training

Leave a Reply

Your email address will not be published. Required fields are marked *