M3-ID (Multi-Modal Movie Identification) is a Python-based microservice project designed to build an information retrieval system capable of identifying movies from diverse, multi-modal user queries. Users can search for a movie using a line of dialogue (text), a memorable scene (image), or a piece of music (audio).
The system uses a state-of-the-art hybrid retrieval strategy, combining dense vector (semantic) search with sparse vector (keyword) search in a Qdrant vector database.
- Project Goal & Description
- System Architecture & Design
- Methodology
- Technology Stack
- Data Source
- System Requirements
- Project Deliverables
- References
- Project Plan & Backlog (Sprint Stories)
The primary goal of this project is to design, implement, and evaluate a robust, multi-modal information retrieval system. This project aims to demonstrate mastery of modern IR concepts, including:
- Multi-modal feature extraction (text, audio, vision).
- Vector embeddings and indexing (via Qdrant).
- Advanced hybrid retrieval models (sparse + dense search).
- Microservice architecture (FastAPI + gRPC).
- Quantitative system evaluation (mAP, R@K).
Identifying a specific movie from a vague memory is a common user problem. Traditional search engines struggle with multi-modal queries (e.g., "What's that movie that looks like this [image] and has this sound in it [audio]?").
M3-ID bridges this gap. It's an end-to-end system that allows users to submit text, image, or audio "clues." These clues are converted into vector embeddings by a dedicated machine-learning service. A retrieval service then queries a vector database using a novel hybrid search, which combines the "vibe" (semantic meaning) of the clues with specific keywords (like transcribed dialogue or names) to provide highly accurate and robust results.
The system is designed as a containerized set of Python microservices, promoting scalability and separation of concerns.
+------------------------------------------------+
| USER (e.g., Postman) |
+------------------------------------------------+
|
| (1) REST API Request (JSON + Files)
v
+-----------------------------------------------------------------------------------+
| (Docker Network) |
| |
| +-------------------------+ (2) gRPC Request +---------------------+
| | API & Retrieval | ---------------------------> | Feature Extractor |
| | Service (FastAPI) | (Raw Data) | Service (gRPC) |
| | | <--------------------------- | (Holds all ML Models)|
| | - Public /search API | (3) gRPC Response +---------------------+
| | - gRPC Client | (Query Vectors) |
| | - Qdrant Client | | (Offline Ingestion)
| | - Orchestrates flow | |
| +-------------------------+ |
| | ^ v
| | | (5) Results +--------------------------------+
| | | | scripts/ingest.py |
| | (4) Hybrid Search Query | (Offline Script) |
| | | +--------------------------------+
| v |
| +-------------------------+
| | Vector Database |
| | (Qdrant) |
| | |
| | - Stores Dense Vectors |
| | - Stores Sparse Vectors |
| +-------------------------+
| |
+-----------------------------------------------------------------------------------+
- Request: A User sends a
POST /searchrequest to the API Service (FastAPI), containing any combination of text, image files, or audio files. - Vectorize: The API Service acts as a gRPC client, sending the raw data to the Feature Extractor Service (gRPC).
- Return Vectors: The Feature Extractor uses its loaded ML models (CLIP, AST, Whisper, etc.) to process the data and returns the resulting dense and sparse query vectors.
- Search: The API Service sends the hybrid search query (with both vectors) to the Qdrant Database.
- Response: Qdrant returns a ranked list of results, which the API Service formats as JSON and sends back to the user.
- An offline script (
scripts/ingest.py) is run once. - It iterates through the entire MSR-VTT dataset (videos, captions).
- For each video, it calls the Feature Extractor Service to get all embeddings.
- It uploads the fused dense vector, sparse vector, and metadata for each video to the Qdrant Database.
This project's core novelty is its hybrid retrieval.
- Dense Search (via HNSW index) captures semantic meaning or "vibe." This is crucial for matching a scene's description or an image's visual content.
- Sparse Search (via inverted index, e.g., TF-IDF/BM25) captures exact keywords. This is critical for matching specific lines of dialogue, actor names, or titles.
-
Fusion: We use Qdrant's native hybrid search to fuse the scores:
$Score = \alpha \cdot Score_{dense} + (1 - \alpha) \cdot Score_{sparse}$ The weight$\alpha$ will be empirically tuned during the evaluation phase to maximize mAP.
We employ Late Fusion. Each modality (text, image, audio) is first processed by its own "expert" model to create an embedding. These embeddings are then combined (e.g., via simple concatenation) after extraction.
- Why? This is a practical and flexible approach. It allows us to use the best available pre-trained models for each modality (e.g., CLIP for vision, AST for audio) and simplifies handling partial queries (e.g., a user only provides text).
- Alternative (Rejected): Early Fusion, which involves building a single, complex transformer to process all raw modalities simultaneously. This is difficult to train and less flexible.
- Backend & API: Python 3.10+, FastAPI
- Microservice Communication: gRPC (
grpcio,grpcio-tools) - Vector Database: Qdrant (
qdrant-client) - Feature Extraction (ML/IR):
- Vision:
transformers(e.g.,openai/clip-vit-base-patch32) - Audio (Semantic):
transformers(e.g.,MIT/ast-finetuned-audioset) - Audio (ASR):
openai-whisper(for transcribing dialogue) - Text (Dense):
sentence-transformers(e.g.,all-MiniLM-L6-v2) - Text (Sparse):
scikit-learn(forTfidfVectorizer) or a sparse model (e.g., SPLADE).
- Vision:
- Containerization & Tooling: Docker, Docker Compose
- Dataset: MSR-VTT (A Large Video Description Dataset for Bridging Video and Language)
- Description: A large-scale benchmark dataset containing 10,000 video clips (totaling 41.2 hours) and 200,000 descriptive sentences (20 per clip).
- Project Usage:
- The video clips will be treated as the "movie" documents to be retrieved.
- The text descriptions will serve as the ground-truth queries for evaluation.
-
FR1: Multi-modal Query Input
- The system shall provide a REST API endpoint (
POST /search). - The endpoint shall accept text queries.
- The endpoint shall accept audio file uploads (e.g.,
.wav,.mp3). - The endpoint shall accept image file uploads (e.g.,
.png,.jpg). - The system shall gracefully handle queries with any combination of modalities.
- The system shall provide a REST API endpoint (
-
FR2: Feature Extraction
- The system shall transcribe uploaded audio to text (for sparse search).
- The system shall generate dense vector embeddings for text, audio, and image inputs.
- The system shall generate sparse vector embeddings for text inputs.
- The system shall fuse dense vectors from different modalities into a single query vector.
-
FR3: Data Ingestion
- The system shall provide an offline script to process and index the entire MSR-VTT dataset into the Qdrant database.
-
FR4: Retrieval
- The system shall query Qdrant using a hybrid (sparse + dense) search.
- The system shall combine the scores from both searches using a weighted fusion.
- The system shall return a ranked list of relevant video clips as a JSON response.
- NFR1: Performance (Latency)
- The end-to-end p95 search latency (API request to response) should be under 3 seconds.
- NFR2: Accuracy
- Retrieval performance must be measured using Mean Average Precision (mAP) and Recall@K (R@K).
- The final hybrid model must demonstrate a quantitative performance improvement over sparse-only and dense-only baselines.
- NFR3: Technology Stack
- The system must be implemented exclusively in Python, using FastAPI, gRPC, and Qdrant.
- NFR4: Scalability
- The
FeatureExtractorgRPC service must be stateless and horizontally scalable.
- The
- NFR5: Maintainability & Deployment
- The entire application stack must be containerized via
docker-compose.ymlfor one-command setup.
- The entire application stack must be containerized via
- Source Code: A complete GitHub repository containing all Python code for the FastAPI API, gRPC service, and ingestion/evaluation scripts.
- Containerized Application: A
docker-compose.ymlfile that builds and launches the entire M3-ID system (API, Feature Extractor, Qdrant). - Evaluation Harness: A standalone
evaluate.pyscript to run the test queries against the API and calculate mAP/R@K. - Technical Report: A final
REPORT.pdf(in IEEE format) detailing the system design, architecture, methodology, experiments, and results (including performance charts). - Final Presentation: A
PRESENTATION.pdfsummarizing the project and its findings.
- Bose, D. et al. (2022). MovieCLIP: Visual Scene Recognition in Movies. arXiv preprint arXiv:2202.01692.
- Gabeur, V. et al. (2020). Multi-modal Transformer for Video Retrieval. ECCV 2020.
- Gong, Y. et al. (2021). AST: Audio Spectrogram Transformer. INTERSPEECH 2021.
- Mandikal, V. et al. (2024). Sparse Meets Dense: A Hybrid Approach to Enhance Scientific Document Retrieval. AAAI-SDU 2024.
- OpenAI. (2022). Robust Speech Recognition via Large-Scale Weak Supervision (Whisper). arXiv preprint arXiv:2212.04356.
- Qdrant. (n.d.). Qdrant Vector Database Documentation. Retrieved from https://qdrant.tech/documentation/
- Radford, A. et al. (2021). Learning Transferable Visual Models From Natural Language Supervision (CLIP). ICML 2021.
- Robertson, S., & Zaragoza, H. (2009). The Probabilistic Relevance Framework: BM25 and Beyond. Foundations and Trends® in Information Retrieval.
- Xu, J. et al. (2016). MSR-VTT: A Large Video Description Dataset for Bridging Video and Language. CVPR 2016.
Sprint Goal: Establish the foundational architecture. By the end of this week, the Qdrant database and the gRPC FeatureExtractor service will be operational and a debug dataset will be ingested.
-
As the System Admin, I want to provision the Qdrant database service.
- Functional Requirements: (NFR) This is setup for FR3.
- Expected Procedures:
- Add the
qdrant/qdrantimage todocker-compose.yml. - Run
docker-compose upand verify the container is running and the web UI is accessible. - Run a Python script using
qdrant-clientto create the "msr-vtt" collection. - Verify the collection is configured with two vector fields:
dense_fused(HNSW index) andsparse_text(inverted index).
- Add the
-
As the Developer, I want to implement the gRPC
FeatureExtractorservice.- Functional Requirements: FR2 (Feature Extraction - the service itself).
- Expected Procedures:
- Create the
feature_extractor/server.pyfile. - Implement the gRPC server boilerplate (e.g., using
grpcio-tools). - Add the new service to
docker-compose.yml.
- Create the
-
As the ML Engineer, I want to load all pre-trained models into the
FeatureExtractorservice.- Functional Requirements: FR2 (all sub-points about generating embeddings/transcriptions).
- Expected Procedures:
- Add
transformers,sentence-transformers,openai-whisper, etc. torequirements.txt. - Write code (e.g., a singleton class) to load all models (CLIP, AST, Whisper, MiniLM, TF-IDF) into memory when the gRPC server starts.
- Add
-
As the Developer, I want to define the
.protocontract for multi-modal feature extraction.- Functional Requirements: FR1 (Query Input - defining the contract for it).
- Expected Procedures:
- Create a
features.protofile. - Define
QueryRequest(with fields liketext_query,image_bytes,audio_bytes). - Define
QueryResponse(with fields for the resulting dense and sparse vectors). - Generate the Python gRPC code from the
.protofile and integrate it into the server.
- Create a
-
As the Data Engineer, I want to create an ingestion script that processes a 100-video debug subset and populates Qdrant.
- Functional Requirements: FR3 (Data Ingestion), FR2 (Feature Extraction).
- Expected Procedures:
- Create the
scripts/ingest.pyscript. - The script will act as a gRPC client to the
FeatureExtractorservice. - It will loop through 100 videos from MSR-VTT, extract data (frames, audio), and send it to the gRPC service to get vectors.
- It will then use the
qdrant-clientto upload the vectors and metadata to the database. - Verify the 100 items are visible and searchable in the Qdrant UI.
- Create the
Sprint Goal: Implement the user-facing API and orchestrate the full retrieval pipeline. By the end of this week, a user can send a multi-modal query to the API and receive a ranked list of results.
-
As the Developer, I want to create the FastAPI API service with a
/searchendpoint.- Functional Requirements: FR1 (API endpoint).
- Expected Procedures:
- Create the
api/main.pyFastAPI application. - Add this new service to
docker-compose.ymlso it runs alongside the other services. - Verify the API runs and the
/docspage is accessible.
- Create the
-
As a User, I want the
/searchendpoint to accept text, image, and audio queries.- Functional Requirements: FR1 (all sub-points about accepting inputs).
- Expected Procedures:
- Implement the
POST /searchendpoint. - Define the endpoint to accept
Formdata fortext_query(FR1-text). - Define the endpoint to accept
UploadFileforimage_query(FR1-image).
- Implement the
- Define the endpoint to accept
UploadFileforaudio_query(FR1-audio).
-
As the System, I want the FastAPI service to orchestrate the query-to-vector pipeline.
- Functional Requirements: FR1, FR2 (Orchestration).
- Expected Procedures:
- Implement the gRPC client logic inside the FastAPI app.
- In the
/searchendpoint, add the logic to: a. Read raw data from the request. b. Send the data to theFeatureExtractorservice via gRPC. c. Receive the dense and sparse query vectors back.
-
As the System, I want to execute a hybrid (sparse + dense) search against Qdrant.
- Functional Requirements: FR4 (Hybrid retrieval, score combination).
- Expected Procedures:
- Implement the
qdrant-clientlogic inside the FastAPI app. - Using the vectors from the previous step, build a hybrid Qdrant search query.
- Implement a simple weighted score fusion (e.g.,
α=0.5to start).
- Implement the
-
As a User, I want to receive a ranked JSON list of movie results.
- Functional Requirements: FR4 (Ranked list).
- Expected Procedures:
- The
/searchendpoint must return a 200 OK with a JSON list of ranked results. - Each result should include its ID, score, and relevant metadata.
- Test the full E2E flow using Postman (send text, get JSON back).
- The
Sprint Goal: Scale the system to the full dataset, quantitatively evaluate its performance against baselines, and document all findings.
-
As the Data Engineer, I want to run the ingestion script on the full MSR-VTT dataset.
- Functional Requirements: FR3 (Data Ingestion).
- Expected Procedures:
- Run the
scripts/ingest.pyscript and monitor it until it successfully processes all 10,000 videos. - Verify the Qdrant collection count matches the full dataset size.
- Run the
-
As the Developer, I want to build an
evaluate.pyscript to measure mAP and R@K.- FunctionalRequirements: NFR2 (Accuracy).
- Expected Procedures:
- Create the
scripts/evaluate.pyscript. - The script must load the MS-VTT test set (queries + ground truth answers).
- The script must programmatically call the live
POST /searchAPI. - Implement logic to compare API results to the ground truth and calculate
mAPandR@K.
- Create the
-
As the Researcher, I want to run the evaluation harness for all baselines.
- Functional Requirements: NFR2 (Accuracy).
- Expected Procedures:
- Modify the evaluation script (or API) to support search modes.
- Run the
evaluate.pyscript in "sparse-only" mode and save the metrics. - Run the
evaluate.pyscript in "dense-only" mode and save the metrics. - Run the
evaluate.pyscript in the default "hybrid" mode and save the metrics.
-
As the Researcher, I want to tune the hybrid search
α(alpha) weight to find the optimal mAP.- Functional Requirements: NFR2 (Accuracy).
- Expected Procedures:
- Parameterize the
α(alpha) weight in the API's search logic. - Run the
evaluate.pyscript in a loop with differentαvalues (e.g., 0.25, 0.5, 0.75). - Identify and record the
αvalue that produces the highest mAP.
- Parameterize the
-
As the Author, I want to write the final technical report and presentation.
- Functional Requirements: (Project Completion).
- Expected Procedures:
- Generate plots (bar charts) comparing the mAP/R@K of the baselines vs. the tuned hybrid model.
- Write the final
REPORT.pdf(e.g., in IEEE format), detailing the project architecture, methodology, and results. - Create the final
PRESENTATION.pdfsummarizing the project.