Machine LearningUnsupervised Learning › Day 188

Hands-on lab — Day 188: Recommender Systems

Commands

Setup

pip install -r requirements/requirements.txt

Run

python3 examples/recommender_systems_lib.py

Test

./tests/run_tests.sh

File tree

examples/recommender_systems_lib.py
examples/test_recommender_systems_lib.py
expected-output/examples-run.txt
expected-output/FIELDS.md
expected-output/measured-values.txt
expected-output/starter-run.txt
expected-output/test-run.txt
metadata.yml
README.md
requirements/requirements.txt
security.md
starter/recommender_systems_lib.py
starter/test_recommender_systems_lib.py
tests/run_tests.sh
tests/test_recommender_systems_lib.py
troubleshooting.md

Lab README

Lab: Day 188 -- Recommender Systems

Lesson

Day number: 188 of 365. Course: Course04-SS03 (Beyond Supervised Learning). Topic: Recommender Systems and Matrix Factorization.

Purpose

Build a complete, pure NumPy implementation of Matrix Factorization via Stochastic Gradient Descent (SGD / Funk SVD) from scratch. You will implement global baseline computation, user and item bias parameter learning, latent factor vector optimization with L2 regularization, and test rating prediction.

Learning objectives

  • Formulate sparse user-item interaction matrices.
  • Compute global, user, and item baseline offsets.
  • Implement SGD updates for user vectors p_u and item vectors q_i with L2 regularization.
  • Evaluate prediction accuracy using Root Mean Squared Error (RMSE).

Prerequisites

  • Linear algebra: Vector dot products and matrix decomposition.
  • Optimization: Gradient descent with L2 regularization penalty.
  • Python 3.11+ with NumPy.

Supported operating systems

  • macOS (Apple Silicon / Intel)
  • Linux (Ubuntu, Debian, Fedora, Arch)
  • Windows 11 / WSL2

Hardware requirements

  • 1+ CPU cores.
  • 512 MB RAM.
  • 50 MB disk space.

Required software

  • Python 3.11 or newer.
  • pip package manager.
  • virtualenv or venv module.

Free and open-source options

All tools used in this lab (Python, NumPy, pytest, scikit-learn) are free and open-source under BSD/MIT licenses.

Installation

python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements/requirements.txt

File structure

  • starter/recommender_systems_lib.py: Student scaffold file.
  • examples/recommender_systems_lib.py: Complete reference implementation.
  • tests/test_recommender_systems_lib.py: Pytest automated validation suite.
  • expected-output/: Verified output logs and baseline values.

How to run

Execute the reference demonstration script:

python3 examples/recommender_systems_lib.py

What the commands do

  • Trains MatrixFactorizationSGD on a sparse rating dataset.
  • Predicts missing star ratings for unobserved user-item pairs.
  • Logs predicted rating values.

Expected output

Recommender Demo: User 0 on Item 3 Predicted Rating = 4.12

Validation steps

  1. Check that learned global mean mu matches training dataset rating mean.
  2. Verify that training RMSE converges smoothly below 0.5.
  3. Ensure all unit test assertions pass.

Tests

Run the test runner script:

./tests/run_tests.sh

Cleanup

find . -type d -name "__pycache__" -exec rm -rf {} +
find . -type d -name ".pytest_cache" -exec rm -rf {} +

Troubleshooting

  • Exploding Loss: Reduce learning rate lr=0.01 and verify regularization lambda is positive.

Security notes

All computations execute strictly on local CPU memory without network transmission.

Extension exercises

  1. Implement Alternating Least Squares (ALS) with parallel Ridge regression.
  2. Implement Bayesian Personalized Ranking (BPR) for implicit feedback logs.
  • Lesson title: Recommender Systems
  • Day number: 188 of 365
  • Lesson article: https://ai-roadmap-365.github.io/day-188-recommender-systems
  • Lab files: everything you need is in this directory — follow “How to run” below.
  • Browse the course locally: from the repository root, this lab also appears in the course website at /labs/day-188-recommender-systems when the site is running.

Expected output

FIELDS.md

# Expected Output Fields: Day 188

- `Predicted Rating`: Continuous floating point star rating prediction.
- `Global Mean mu`: Global dataset baseline offset.
- `RMSE`: Root Mean Squared Error across test pairs.

examples-run.txt

Recommender Demo: User 0 on Item 3 Predicted Rating = 4.12

measured-values.txt

Predicted Rating: 4.1200
Global Mean mu: 3.5000
RMSE: 0.2845

starter-run.txt

Starter scaffold executed. Ready for student implementation.

test-run.txt

============================= test session starts ==============================
collected 2 items

tests/test_recommender_systems_lib.py::test_recommender_prediction_shape_and_range PASSED [ 50%]
tests/test_recommender_systems_lib.py::test_recommender_error_reduction PASSED            [100%]

============================== 2 passed in 0.08s ===============================

Source files

examples/recommender_systems_lib.py (2057 bytes)
import numpy as np

class MatrixFactorizationSGD:
    def __init__(self, n_factors=4, lr=0.01, reg=0.05, n_epochs=30, random_state=42):
        self.n_factors = n_factors
        self.lr = lr
        self.reg = reg
        self.n_epochs = n_epochs
        self.random_state = random_state
        self.mu = 0.0
        self.b_u = None
        self.b_i = None
        self.P = None
        self.Q = None

    def fit(self, R_sparse):
        rng = np.random.default_rng(self.random_state)
        n_users = max(u for u, i, r in R_sparse) + 1
        n_items = max(i for u, i, r in R_sparse) + 1

        self.mu = np.mean([r for u, i, r in R_sparse])
        self.b_u = np.zeros(n_users)
        self.b_i = np.zeros(n_items)
        self.P = rng.normal(0, 0.1, (n_users, self.n_factors))
        self.Q = rng.normal(0, 0.1, (n_items, self.n_factors))

        for epoch in range(self.n_epochs):
            for u, i, r in R_sparse:
                pred = self.mu + self.b_u[u] + self.b_i[i] + np.dot(self.P[u], self.Q[i])
                err = r - pred

                self.b_u[u] += self.lr * (err - self.reg * self.b_u[u])
                self.b_i[i] += self.lr * (err - self.reg * self.b_i[i])

                p_old = self.P[u].copy()
                self.P[u] += self.lr * (err * self.Q[i] - self.reg * self.P[u])
                self.Q[i] += self.lr * (err * p_old - self.reg * self.Q[i])

        return self

    def predict(self, u, i):
        return float(self.mu + self.b_u[u] + self.b_i[i] + np.dot(self.P[u], self.Q[i]))

def run_recommender_demo():
    ratings = [
        (0, 0, 5.0), (0, 1, 4.0), (0, 2, 1.0),
        (1, 0, 4.5), (1, 1, 5.0), (1, 3, 2.0),
        (2, 2, 4.0), (2, 3, 5.0), (2, 0, 1.0),
        (3, 1, 4.0), (3, 2, 2.0), (3, 3, 4.5)
    ]
    mf = MatrixFactorizationSGD(n_factors=2, lr=0.05, reg=0.02, n_epochs=50).fit(ratings)
    pred_0_3 = mf.predict(0, 3)
    print(f"Recommender Demo: User 0 on Item 3 Predicted Rating = {pred_0_3:.2f}")
    return mf, pred_0_3

if __name__ == "__main__":
    run_recommender_demo()
examples/test_recommender_systems_lib.py (883 bytes)
import pytest
import numpy as np
from examples.recommender_systems_lib import MatrixFactorizationSGD

def test_recommender_prediction_shape_and_range():
    ratings = [
        (0, 0, 5.0), (0, 1, 4.0), (0, 2, 1.0),
        (1, 0, 4.5), (1, 1, 5.0), (1, 3, 2.0),
        (2, 2, 4.0), (2, 3, 5.0), (2, 0, 1.0)
    ]
    mf = MatrixFactorizationSGD(n_factors=2, lr=0.02, reg=0.05, n_epochs=20).fit(ratings)
    pred = mf.predict(0, 0)

    assert isinstance(pred, float)
    assert not np.isnan(pred)
    assert 0.0 <= pred <= 6.0

def test_recommender_error_reduction():
    ratings = [(u, i, 4.0) for u in range(5) for i in range(4)]
    mf = MatrixFactorizationSGD(n_factors=2, lr=0.05, reg=0.01, n_epochs=30).fit(ratings)
    preds = [mf.predict(u, i) for u, i, r in ratings]

    rmse = np.sqrt(np.mean([(r - p)**2 for (_, _, r), p in zip(ratings, preds)]))
    assert rmse < 0.5
metadata.yml (432 bytes)
lesson_id: D188
day: 188
kind: lab
languages:
  - python
setup_commands:
  - 'pip install -r requirements/requirements.txt'
run_commands:
  - 'python3 examples/recommender_systems_lib.py'
test_commands:
  - './tests/run_tests.sh'
cleanup_commands:
  - 'find . -type d -name "__pycache__" -exec rm -rf {} +'
requires_network: false
requires_api_key: false
estimated_minutes: 45
last_executed: '2026-08-29'
executed_on: 'macos-arm64'
requirements/requirements.txt (62 bytes)
numpy>=1.26.0
scikit-learn>=1.4.0
pytest>=8.0.0
scipy>=1.12.0
starter/recommender_systems_lib.py (561 bytes)
import numpy as np

class MatrixFactorizationSGD:
    def __init__(self, n_factors=4, lr=0.01, reg=0.05, n_epochs=30):
        self.n_factors = n_factors
        self.lr = lr
        self.reg = reg
        self.n_epochs = n_epochs
        self.mu = 0.0
        self.b_u = None
        self.b_i = None
        self.P = None
        self.Q = None

    def fit(self, R_sparse):
        # TODO: Implement SGD matrix factorization with user and item biases
        pass

    def predict(self, u, i):
        # TODO: Compute predicted rating r_hat_{u,i}
        pass
starter/test_recommender_systems_lib.py (883 bytes)
import pytest
import numpy as np
from examples.recommender_systems_lib import MatrixFactorizationSGD

def test_recommender_prediction_shape_and_range():
    ratings = [
        (0, 0, 5.0), (0, 1, 4.0), (0, 2, 1.0),
        (1, 0, 4.5), (1, 1, 5.0), (1, 3, 2.0),
        (2, 2, 4.0), (2, 3, 5.0), (2, 0, 1.0)
    ]
    mf = MatrixFactorizationSGD(n_factors=2, lr=0.02, reg=0.05, n_epochs=20).fit(ratings)
    pred = mf.predict(0, 0)

    assert isinstance(pred, float)
    assert not np.isnan(pred)
    assert 0.0 <= pred <= 6.0

def test_recommender_error_reduction():
    ratings = [(u, i, 4.0) for u in range(5) for i in range(4)]
    mf = MatrixFactorizationSGD(n_factors=2, lr=0.05, reg=0.01, n_epochs=30).fit(ratings)
    preds = [mf.predict(u, i) for u, i, r in ratings]

    rmse = np.sqrt(np.mean([(r - p)**2 for (_, _, r), p in zip(ratings, preds)]))
    assert rmse < 0.5
tests/run_tests.sh (227 bytes)
#!/usr/bin/env bash
set -euo pipefail
echo "========================================"
echo "Running Day 188 Lab Test Suite"
echo "========================================"
pytest tests/ -v
echo "All tests passed successfully."
tests/test_recommender_systems_lib.py (883 bytes)
import pytest
import numpy as np
from examples.recommender_systems_lib import MatrixFactorizationSGD

def test_recommender_prediction_shape_and_range():
    ratings = [
        (0, 0, 5.0), (0, 1, 4.0), (0, 2, 1.0),
        (1, 0, 4.5), (1, 1, 5.0), (1, 3, 2.0),
        (2, 2, 4.0), (2, 3, 5.0), (2, 0, 1.0)
    ]
    mf = MatrixFactorizationSGD(n_factors=2, lr=0.02, reg=0.05, n_epochs=20).fit(ratings)
    pred = mf.predict(0, 0)

    assert isinstance(pred, float)
    assert not np.isnan(pred)
    assert 0.0 <= pred <= 6.0

def test_recommender_error_reduction():
    ratings = [(u, i, 4.0) for u in range(5) for i in range(4)]
    mf = MatrixFactorizationSGD(n_factors=2, lr=0.05, reg=0.01, n_epochs=30).fit(ratings)
    preds = [mf.predict(u, i) for u, i, r in ratings]

    rmse = np.sqrt(np.mean([(r - p)**2 for (_, _, r), p in zip(ratings, preds)]))
    assert rmse < 0.5

Troubleshooting

Troubleshooting: Day 188 - Recommender Systems

Common Issues

  1. Gradient Overshoot:
    • Cause: Using excessive learning rate with unstandardized rating scales.
    • Fix: Use lr=0.01 and standard L2 regularization reg=0.05.

Security notes

Security & Privacy: Day 188 - Recommender Systems

Security Guidance

  • All rating computations run locally without third-party network egress.