This project is a content-based movie recommendation system built on the TMDB 5000 dataset. It suggests movies similar to a user-selected title by comparing textual features such as genres, keywords, cast, crew, and plot overview.
The workflow is split into two parts:
project.ipynb— loads and preprocesses movie data, builds a similarity matrix, and serializes the artifacts.app.py— a Streamlit web application that loads the saved artifacts and displays the top five recommendations with posters from The Movie Database (TMDB) API.
The repository also contains main.cpp, a standalone C++ program unrelated to the recommender pipeline.
- Content-based recommendations using movie metadata (genres, keywords, cast, crew, overview)
- Interactive Streamlit UI with a searchable movie dropdown
- Top-five similar movie suggestions per selection
- Movie poster images fetched from the TMDB API
- Reproducible offline model build via Jupyter notebook
- Serialized model artifacts (
movies.pkl,similarity.pkl) for fast app startup
| Category | Technologies |
|---|---|
| Language | Python 3, C++ |
| Web UI | Streamlit |
| Data processing | pandas, NumPy |
| NLP / ML | scikit-learn (CountVectorizer, cosine_similarity), NLTK (PorterStemmer) |
| Visualization (notebook) | matplotlib, seaborn |
| HTTP | requests |
| Serialization | pickle |
| External API | TMDB API (posters) |
| Development | Jupyter Notebook |
MLBook/
├── app.py # Streamlit movie recommender application
├── project.ipynb # Data preprocessing, similarity matrix, artifact export
├── main.cpp # Standalone C++ hello-world program (not used by the app)
├── README.md
├── .gitignore
├── tmdb_5000_movies.csv # Required dataset (not included in repo)
├── tmdb_5000_credits.csv # Required dataset (not included in repo)
├── movies.pkl # Generated movie metadata (gitignored)
└── similarity.pkl # Generated cosine similarity matrix (gitignored)
| File / folder | Description |
|---|---|
project.ipynb |
End-to-end notebook: EDA, feature engineering, vectorization, similarity computation, and pickle export |
app.py |
Loads pickles, accepts user input, runs recommendation logic, fetches posters |
movies.pkl |
Pickled DataFrame with id, title, and processed recommendKeyWords |
similarity.pkl |
Pickled cosine similarity matrix (4806 × 4806 after preprocessing) |
.gitignore |
Excludes *.pkl, *.zip, __pycache__/, .env/, .venv/ |
- Python 3.8+
- pip
- A C++ compiler (only if building
main.cpp) - TMDB 5000 CSV files placed in the project root
git clone <repository-url>
cd MLBookPlace the following files in the project root (they are referenced by project.ipynb):
tmdb_5000_movies.csvtmdb_5000_credits.csv
These files are commonly available as the TMDB 5000 Movie Dataset on Kaggle.
No requirements.txt is included. Install the packages used in the project:
pip install streamlit pandas numpy scikit-learn nltk requests matplotlib seaborn jupyterDownload NLTK data if needed (the notebook uses the Porter stemmer from nltk.stem.porter):
python -c "import nltk; nltk.download('punkt')"Run all cells in project.ipynb. This creates:
movies.pklsimilarity.pkl
Both files must exist before starting the Streamlit app.
streamlit run app.pyOpen the URL shown in the terminal (typically http://localhost:8501).
jupyter notebook project.ipynbExecute cells sequentially to regenerate the pickle files.
g++ main.cpp -o main
./main # Linux / macOS
main.exe # WindowsPrints navya to stdout. This program is not part of the recommender system.
- Ensure
movies.pklandsimilarity.pklare in the project root. - Start the app with
streamlit run app.py. - Select a movie from the Select the movie dropdown.
- Click Recommend.
- The app displays the top five similar movies with titles and poster images.
Recommendations are based on cosine similarity of bag-of-words vectors derived from each movie's combined metadata text.
flowchart LR
A[TMDB CSV files] --> B[project.ipynb]
B --> C[movies.pkl]
B --> D[similarity.pkl]
C --> E[app.py]
D --> E
F[User selects movie] --> E
E --> G[Cosine similarity lookup]
G --> H[Top 5 titles]
E --> I[TMDB API]
I --> J[Poster images]
H --> K[Streamlit UI]
J --> K
- Data merge —
tmdb_5000_movies.csvandtmdb_5000_credits.csvare merged ontitle. - Cleaning — Missing values are dropped; duplicate rows are removed (4,806 movies remain).
- Feature text — Genres, keywords, top-three cast names, crew names (where
job == "Director of Photography"), and tokenized overview are combined into a singlerecommendKeyWordsstring per movie. - Vectorization —
CountVectorizer(max_features=5000,stop_words="english") converts text to sparse count vectors. - Similarity — Pairwise cosine similarity is computed across all movies.
- Persistence — The processed DataFrame and similarity matrix are saved as pickle files.
- Inference —
app.pyloads the pickles, finds the selected movie's index, sorts similarity scores, and returns the top five matches (excluding the query movie itself). - Posters — For each recommended movie
id, the app calls the TMDB movie details endpoint and displays the poster URL.
| File | Rows (raw) | Description |
|---|---|---|
tmdb_5000_movies.csv |
4,803 | Movie metadata: budget, genres, keywords, overview, popularity, etc. |
tmdb_5000_credits.csv |
4,804 | Cast and crew JSON for each movie |
After merge, drop-null, and deduplication: 4,806 movies.
- Merge movies and credits on
title - Retain columns:
genres,id,keywords,overview,popularity,title,cast,vote_average,vote_count,crew - Drop rows with missing values (
overviewhad 3 nulls) - Remove duplicate rows
- Parse JSON-like string fields with
ast.literal_eval - Extract top 3 cast member names per movie
- Extract crew members with job "Director of Photography"
- Tokenize
overviewby splitting on whitespace - Remove spaces inside multi-word genre/cast/crew tokens (e.g.,
Science Fiction→ScienceFiction)
All signals are concatenated into one text field per movie:
recommendKeyWords = genres + keywords + cast + crew + overview
Additional steps:
- Join token lists into a single space-separated string
- Convert to lowercase
- Apply Porter stemming via NLTK
This is not a neural network. The "model" is:
- Bag-of-words representation via
CountVectorizer(vocabulary capped at 5,000 features) - Cosine similarity matrix over all movie vectors (shape: 4,806 × 4,806)
There is no supervised train/test split. The notebook:
- Fits
CountVectorizeron allrecommendKeyWordsstrings - Transforms text to a document-term matrix
- Computes the full cosine similarity matrix once
- Serializes results to disk
Re-running the notebook overwrites movies.pkl and similarity.pkl.
No formal evaluation (accuracy, precision, recall, RMSE, etc.) is implemented in the codebase. Recommendation quality is assessed informally through manual inspection in the notebook (e.g., testing with Pirates of the Caribbean: At World's End).
Example output from the notebook's recommend() function for Pirates of the Caribbean: At World's End:
| Rank | Recommended title |
|---|---|
| 1 | Pirates of the Caribbean: Dead Man's Chest |
| 2 | Pirates of the Caribbean: The Curse of the Black Pearl |
| 3 | Pirates of the Caribbean: On Stranger Tides |
| 4 | The Indian in the Cupboard |
| 5 | Life of Pi |
The first three results are other films in the same franchise, which indicates the content-based approach captures shared cast, genre, and keyword patterns.
- Content-based only — no collaborative filtering or user rating history
- No held-out evaluation — similarity is computed on the full catalog with no metric reporting
- Title-based merge — merging on
titlecan misalign rows when titles collide - Crew feature scope — only "Director of Photography" credits are used, not directors or other roles
- Cold start — new movies not in the dataset cannot be recommended
- Hardcoded API key — TMDB key is embedded in
app.py - Missing artifacts in repo — CSV and pickle files must be obtained or generated locally
- UI layout —
app.pyassigns all five recommendation columns tocol1, so posters may not render in separate columns as intended
| Item | Location | Notes |
|---|---|---|
| TMDB API key | app.py (fetchPoster) |
Used to fetch poster paths; currently hardcoded |
| Pickle artifacts | Project root | movies.pkl, similarity.pkl required by the app |
| Dataset files | Project root | tmdb_5000_movies.csv, tmdb_5000_credits.csv required by the notebook |
| Vectorizer settings | project.ipynb |
max_features=5000, stop_words="english" |
| Recommendation count | app.py, notebook |
Top 5 similar movies (index 0 excluded as the query movie) |
For production use, move the TMDB API key to an environment variable and load it in app.py instead of hardcoding it.
- Add a
requirements.txt(orpyproject.toml) with pinned dependency versions - Externalize the TMDB API key via environment variables (
.env) - Fix Streamlit column assignment so each recommendation uses
col1–col5 - Expand crew extraction to include directors and other key roles
- Add collaborative filtering or hybrid recommendations using
vote_average/vote_count - Implement evaluation metrics (e.g., precision@k on a labeled or manual test set)
- Handle duplicate movie titles during the merge step
- Add dataset download instructions or a setup script
- Remove or relocate
main.cppif it is not part of the project scope - Add error handling for missing posters or TMDB API failures
- Fork the repository and create a feature branch from
main. - Set up a virtual environment and install the dependencies listed in Installation.
- Place the TMDB 5000 CSV files in the project root and run
project.ipynbto generate artifacts. - Make focused changes with clear commit messages.
- Test the notebook pipeline and
streamlit run app.pybefore opening a pull request. - Do not commit
*.pkl, API keys, or local virtual environment directories.
Pull requests that include reproducible setup steps and avoid hardcoded secrets are especially welcome.