Dự án xây dựng một hệ thống tìm kiếm ảnh tương tự trên bộ dữ liệu CIFAR-10 (60.000 ảnh), với hai cách tìm:
- Tìm bằng ảnh — đặc trưng sâu từ ResNet-18 (pretrained ImageNet) + FAISS.
- Tìm bằng mô tả văn bản — nhúng ảnh & chữ chung một không gian bằng CLIP (vd gõ "a red truck", "con mèo").
Hệ thống hỗ trợ:
- Trợ lý chat (một khung duy nhất): đính kèm ảnh để tìm ảnh giống, hoặc chỉ gõ mô tả — chạy offline, không cần API key
- Tìm bằng mô tả tự nhiên (CLIP), hỗ trợ cả tiếng Việt (vd "tìm 5 ảnh con chó", "máy bay trên bầu trời")
- Nói kèm số lượng K và lọc theo lớp ngay trong câu chat (vd "tìm 20 ảnh con mèo")
- Hiển thị nhãn lớp + % độ tương đồng cho từng kết quả
- Khi tìm bằng ảnh mẫu (có nhãn): hiện Precision@K, Recall@K, Average Precision ngay trên web
- Tìm kiếm vector siêu nhanh bằng FAISS (Facebook AI Similarity Search)
Image-Retrieval-System/
├── Data/ # Dữ liệu CIFAR-10 (tự tải, đã gitignore)
│ └── cifar-10-batches-py/
├── features/ # Kết quả trích xuất (đã gitignore)
│ ├── features.npy # Đặc trưng ResNet-18 (60000 × 512) — tìm bằng ảnh
│ ├── features_clip.npy # Đặc trưng CLIP (60000 × 512) — tìm bằng mô tả
│ ├── labels.npy # Nhãn lớp của từng ảnh
│ └── image_list.txt # Ảnh Base64 (64×64) để hiển thị giao diện
├── src/
│ ├── feature_extractor.py # Trích đặc trưng ResNet-18 -> features.npy
│ ├── clip_extractor.py # Trích đặc trưng CLIP -> features_clip.npy
│ ├── clip_model.py # Load CLIP + encode ảnh/chữ (kèm dịch Việt→Anh)
│ ├── chat_agent.py # Parser câu lệnh chat (rule-based, offline)
│ └── main.py # Backend Flask + API (FAISS, search, /chat)
├── static/
│ ├── css/
│ │ └── style.css
│ └── js/
│ ├── app.js # Click ảnh mẫu + hiển thị kết quả
│ └── chat.js # Logic khung chat
├── templates/
│ └── index.html
├── kaggle_extract_clip.py # Trích đặc trưng CLIP trên Kaggle GPU (nhanh)
├── BAO_CAO_DANH_GIA.md # Báo cáo đáp ứng tiêu chí đánh giá đồ án
├── README.md
├── pyproject.toml # Khai báo dependencies (thay cho requirements.txt)
├── uv.lock # Phiên bản thư viện đã khoá (commit lên Git)
├── Dockerfile # Định nghĩa image Docker
├── .dockerignore # File loại trừ khi build image
└── docker-compose.yml # Cấu hình chạy bằng Docker Compose
- Trích xuất deep features bằng ResNet-18 (tìm bằng ảnh)
- Tìm bằng văn bản (CLIP): nhúng ảnh & chữ chung không gian → gõ mô tả ra ảnh, hỗ trợ tiếng Việt
- Tìm kiếm vector siêu nhanh bằng FAISS (Facebook AI Similarity Search) — IndexFlatIP + cosine, exact search
- Trợ lý chat một khung: nhận cả ảnh lẫn mô tả, hiểu số lượng K & lớp từ ngôn ngữ tự nhiên
- Đánh giá khoa học bằng:
- Tốc độ tìm kiếm (ms/query)
- Recall@K, Precision@K, AP (Average Precision) – chất lượng retrieval
- Giao diện web đẹp, responsive, hỗ trợ upload + preview ảnh
- Hoạt động hoàn toàn offline, không cần Internet sau khi trích xuất dữ liệu
| Công nghệ | Mục đích sử dụng |
|---|---|
| PyTorch + TorchVision | Trích xuất đặc trưng bằng ResNet-18 pretrained |
| OpenCLIP (open-clip-torch) | Tìm ảnh bằng mô tả văn bản (ảnh & chữ cùng không gian) |
| NumPy | Xử lý, chuẩn hóa L2 & lưu trữ vector đặc trưng |
| FAISS | Tìm kiếm vector siêu nhanh (chính thức dùng) |
| Flask | Backend web |
| HTML/CSS/JS | Giao diện người dùng đẹp, mượt mà |
Dự án dùng uv để quản lý môi trường và thư viện (qua pyproject.toml + uv.lock).
# Cài uv (nếu chưa có)
curl -LsSf https://astral.sh/uv/install.sh | sh
# Tạo môi trường ảo + cài đúng phiên bản thư viện đã khoá trong uv.lock
# (uv tự tải Python 3.12, dùng wheel PyTorch CPU-only)
uv sync
⚠️ Lưu ý về file dữ liệu: khi mớigit clone, repo chưa có thư mụcData/(bộ CIFAR-10) lẫnfeatures/(vector đặc trưng) vì chúng rất nặng nên bị.gitignore. Cả hai sẽ được tự tạo ở Bước 2 dưới đây.
Chạy script này một lần duy nhất. Nó sẽ tự động:
- Tải bộ CIFAR-10 (~170MB) về
Data/nếu chưa có (cần Internet ở lần đầu). - Trích đặc trưng bằng ResNet-18 và ghi 3 file vào
features/.
uv run python src/feature_extractor.pyOutput sinh ra trong features/:
| File | Mô tả |
|---|---|
features.npy |
Ma trận (60000 × 512) chứa embedding của mỗi ảnh |
labels.npy |
Nhãn lớp của từng ảnh |
image_list.txt |
Danh sách ảnh mã hoá Base64 (64×64) phục vụ frontend |
💡 Lần đầu chạy sẽ mất vài phút (tải dữ liệu + tải trọng số ResNet-18 từ Internet). Những lần sau đã có sẵn
Data/vàfeatures/nên không cần chạy lại.
Muốn dùng tính năng gõ mô tả ra ảnh, cần thêm file features/features_clip.npy:
uv run python src/clip_extractor.py⚙️ Bước này trên CPU rất chậm (~1 giờ cho 60k ảnh). Khuyến nghị trích nhanh trên Kaggle GPU (~1–2 phút) bằng
kaggle_extract_clip.pyrồi tảifeatures_clip.npyvề bỏ vàofeatures/. Nếu thiếu file này, app vẫn chạy bình thường nhưng chỉ tìm bằng ảnh; phần tìm bằng mô tả sẽ tự tắt.
uv run python src/main.pySau đó mở trình duyệt tại:
http://localhost:5000
Nếu không muốn cài uv/Python trực tiếp lên máy, có thể chạy toàn bộ hệ thống bằng Docker.
| File | Vai trò |
|---|---|
Dockerfile |
Định nghĩa cách đóng gói ứng dụng thành image: cài thư viện theo uv.lock, copy mã nguồn, tải sẵn trọng số ResNet-18. |
.dockerignore |
Liệt kê thứ không đưa vào image (.venv, Data/, features/, .git…) để build nhẹ & nhanh. |
docker-compose.yml |
Cấu hình chạy: map cổng 5000, mount Data/ và features/ từ máy host vào container. |
📌 Vì sao mount volume? Bộ CIFAR-10 và file đặc trưng rất nặng nên không nhúng vào image. Thay vào đó chúng được gắn (mount) từ thư mục trên máy host lúc chạy — file sinh ra trong container vẫn được lưu lại trên máy bạn.
docker compose buildLệnh dưới chạy script trích xuất bên trong container; nhờ mount volume, kết quả
(Data/ và features/) được ghi ra thư mục dự án trên máy host:
docker compose run --rm web uv run python src/feature_extractor.pydocker compose upMở trình duyệt tại http://localhost:5000. Nhấn Ctrl+C để dừng, hoặc chạy nền bằng
docker compose up -d và dừng bằng docker compose down.
⚠️ Phải chạy Bước 2 trước. Nếufeatures/còn trống, server sẽ báo lỗiFileNotFoundErrorvì chưa có dữ liệu đặc trưng để tìm kiếm.
# Build
docker build -t image-retrieval-system .
# Trích xuất đặc trưng
docker run --rm -v "$PWD/Data:/app/Data" -v "$PWD/features:/app/features" \
image-retrieval-system uv run python src/feature_extractor.py
# Chạy server
docker run --rm -p 5000:5000 -v "$PWD/features:/app/features" \
image-retrieval-systemHệ thống dùng FAISS (IndexFlatIP + cosine similarity, sau khi L2-normalize) làm phương pháp tìm kiếm chính thức vì:
- Tốc độ: truy vấn thường chỉ 1–3 ms trên 60.000 vector 512 chiều
- Độ chính xác: IndexFlatIP là exact search (không nén vector) nên luôn trả về đúng top-K theo cosine
- Khả năng mở rộng: dễ dàng nâng lên index gần đúng (IVF, PQ, HNSW) khi xử lý hàng triệu–tỷ vector
Mỗi lần tìm kiếm bằng ảnh mẫu (có nhãn), terminal in ra thời gian và các chỉ số chất lượng:
======================================================================
BÁO CÁO TÌM KIẾM (k=10, lớp lọc=tất cả)
→ Thời gian tìm kiếm : 1.8700 ms
→ Precision@10: 0.7 | Recall@10: 0.0012 | AP: 0.83
======================================================================Ngoài tìm bằng ảnh, hệ thống còn dùng CLIP để tìm ảnh từ mô tả văn bản:
- CLIP nhúng ảnh và chữ vào cùng một không gian vector → có thể so khớp câu mô tả với ảnh trong kho.
- Toàn bộ kho ảnh được mã hoá sẵn thành
features/features_clip.npy(xem Bước 2b). - Khi gõ mô tả, câu chữ được CLIP mã hoá rồi tìm bằng FAISS giống như tìm bằng ảnh.
- Hỗ trợ tiếng Việt qua bước dịch nhanh Việt→Anh trong src/clip_model.py (CLIP gốc là tiếng Anh).
- Chạy offline, không cần API key.
Ví dụ gõ trong khung chat: "a red truck", "con mèo", "máy bay trên bầu trời", "tìm 5 ảnh con chó".
Các tiêu chí đánh giá đồ án (xác định vấn đề & chiến lược, chỉ số đo lường, cải tiến thuật toán, đánh giá chất lượng mô hình, thảo luận kết quả, hướng cải thiện, tóm tắt giải pháp, điểm thú vị/khó) được trình bày chi tiết trong:
- Dự án hoạt động tốt trên CPU, nhưng GPU sẽ nhanh hơn nhiều.
- Có thể mở rộng dataset khác hoặc model mạnh hơn (ResNet50, ViT…).
- Có thể mở rộng bằng:
- Model mạnh hơn (ResNet-50, EfficientNet, ViT)
- Dataset lớn hơn (ImageNet, LAION)
- Chỉ mục FAISS nâng cao (IVF, PQ, HNSW)
Giang Lê Hoàng