BookRS is a neural collaborative filtering recommender for books. It covers the full flow from raw interaction data to a trained PyTorch model, then serves recommendations through a Streamlit app and a FastAPI service.
- Data preprocessing pipeline (
src/preprocessing.py) - NCF model definition (
src/model.py) - Training script (
src/train.py) - Evaluation script (
src/evaluate.py) - Analysis and plots (
src/analyze_model.py) - User quality analysis script (
src/analyze_users.py) - Streamlit app (
src/app.py) - FastAPI backend (
src/api.py) - Custom UI styling (
src/style.css)
| Path | Purpose | Current details |
|---|---|---|
archive/interactions_large/interactions_large.csv |
Raw user-book ratings | 437.93 MB |
archive/books_metadata_large/books_metadata_large.csv |
Raw metadata used by app and API | 103.84 MB, 36,514 books |
data/processed_interactions.csv |
Encoded user, book, rating table |
2,734,350 rows, 36.54 MB |
data/user_mapping.pkl |
Original user_id to encoded user index |
377,799 users |
data/book_mapping.pkl |
Original book_id to encoded book index |
36,514 books |
models/ncf_model.pth |
Trained model weights | 101.26 MB |
models/training_history.json |
Epoch loss history | 20 epochs recorded |
results/evaluation_metrics.txt |
Saved evaluation metrics | MSE, RMSE, MAE, R2 |
results/*.png |
Analysis plots | Training loss, actual vs predicted, error distribution |
The dataset used by this project was downloaded from Kaggle and is kept local. Large data and model artifacts are intentionally not meant to be pushed to GitHub.
- Raw Kaggle files live under
archive/ - Local compressed copy is
archive.zip - Processed tables and mappings live under
data/ - Trained weights live under
models/ - Kaggle credentials (
kaggle.jsonand.kaggle/) are local-only
These paths are ignored in .gitignore.
Raw data (archive/)
-> preprocessing.py
-> processed data + ID mappings (data/)
-> train.py
-> ncf_model.pth + training_history.json (models/)
-> evaluate.py / analyze_model.py
-> metrics + plots (results/)
Serving layer:
app.py (Streamlit UI with local model inference)
api.py (FastAPI endpoints with local model inference)
Both serving paths load the same model and mapping files from disk. The Streamlit app does not call the FastAPI service right now. They run independently.
The model in src/model.py is an MLP-based NCF model:
- User embedding:
Embedding(num_users, 64) - Book embedding:
Embedding(num_books, 64) - Concatenate embeddings to 128 features
- Fully connected stack:
128 -> 128 -> 64 -> 32 -> 1 - ReLU activations
- Dropout
p=0.2after the first two hidden layers - Output is a scalar rating prediction
Training setup from src/train.py:
- Loss: MSELoss
- Training algorithm: AdamW (
lr=0.001,weight_decay=1e-5) - LR scheduler: StepLR (
step_size=5,gamma=0.5) - Batch size: 2048
- Epochs: 20
- Split: 80 percent train, 20 percent validation
- Reproducibility seed: 42 for NumPy and PyTorch
Let u be a user index and b be a book index.
p_u = E_u(u)
q_b = E_b(b)
x = [p_u ; q_b]
h1 = Dropout(ReLU(W1*x + b1))
h2 = Dropout(ReLU(W2*h1 + b2))
h3 = ReLU(W3*h2 + b3)
r_hat_ub = W4*h3 + b4
Training objective:
MSE = (1/N) * sum((r_i - r_hat_i)^2)
Evaluation metrics used in code:
RMSE = sqrt(MSE)
MAE = (1/N) * sum(|r_i - r_hat_i|)
R2 = 1 - [sum((r_i - r_hat_i)^2)] / [sum((r_i - r_mean)^2)]
Values below come from results/evaluation_metrics.txt:
| Metric | Value |
|---|---|
| MSE | 3.0186 |
| RMSE | 1.7374 |
| MAE | 1.2245 |
| R2 | 0.3302 |
Training loss in models/training_history.json goes from 4.0745 at epoch 1 to 1.9017 at epoch 20.
- Loads model, metadata, and interactions with Streamlit caching
- Uses five fixed demo profile indices:
199451, 57126, 28970, 65519, 71838 - Pages:
- Home: hero recommendation, top picks, and genre row
- Search: title search with score-ranked results
- My List: user reading history with pagination (10 books per page)
- Book detail dialog shows cover, year, average rating, genres, and description
- Match percentages are computed from predicted rating, clamped to
[0, 5], then mapped to0-100
- Startup loads model + mappings + metadata + processed interactions
- CORS middleware is enabled for all origins
- Endpoints:
GET /health and device infoGET /userstop 20 users by interaction countGET /home/{user_idx}home feed payload (hero,top_picks,trending,genre_books)GET /search?q=...title search
Python packages imported by this project:
torchpandasnumpyscikit-learnstreamlitfastapiuvicornmatplotlibseabornpydantic
Python standard library modules used:
ospicklejsonrandomasttyping
UI styling details:
- Custom CSS in
src/style.css - Google Fonts
Interimported in CSS
It is recommended to use a virtual environment:
# Create venv
python -m venv venv
# Activate venv (Windows)
.\venv\Scripts\activate
# Activate venv (Mac/Linux)
source venv/bin/activatepip install -r requirements.txtpython src/preprocessing.pyTrain model:
python src/train.pyEvaluate:
python src/evaluate.py
python src/analyze_model.py
python src/analyze_users.pyStart Streamlit app:
streamlit run src/app.pyStart API:
uvicorn src.api:app --host 0.0.0.0 --port 8000saim-rs/
src/
model.py
preprocessing.py
train.py
evaluate.py
analyze_model.py
analyze_users.py
api.py
app.py
style.css
archive/
interactions_large/interactions_large.csv
books_metadata_large/books_metadata_large.csv
interactions_small.csv
books_metadata_small.csv
data/
processed_interactions.csv
user_mapping.pkl
book_mapping.pkl
models/
ncf_model.pth
training_history.json
results/
evaluation_metrics.txt
training_loss.png
actual_vs_predicted.png
error_distribution.png
PROJECT_REPORT.md