一个可本地完整运行的电影推荐项目:使用双塔模型召回候选影片,使用 DeepFM 精排,通过类型多样性重排生成最终片单。后端与算法按 online / offline / nearline / common 边界拆分,在线推理和离线训练严格分离。
- 自动下载和解析 MovieLens 1M
- 用户最后一次正反馈时间切分,避免随机切分造成数据泄漏
- 双塔模型学习用户和电影向量
- DeepFM 融合用户、电影、类型、年份和召回分数
- 热门电影冷启动、已看过滤、类型多样性重排
- Recall@K、HitRate@K、NDCG@K 离线评估
- 个性化推荐、相似电影、片库检索、反馈、模型状态 API
- 电影资料馆风格的响应式 React 前端
- SQLite 反馈持久化,模型产物版本化存储
- Docker Compose 声明 Redis、Milvus、PostgreSQL、MinIO、etcd 等真实组件
- 后端/前端测试和 Docker Compose
backend/
├── online/ 在线 API 请求链路:pipeline + plugin
├── offline/ 离线批处理训练链路:pipeline + stages
├── nearline/ 准实时画像、热榜、特征更新边界
├── common/ 版本、schema、storage key、manifest 等公共协议
└── app/ FastAPI 入口和历史 import 兼容层
在线 Pipeline:
UnifiedRecall → DeepFMRank → DiversityRerank → BuildResponse
│
├─ Two-Tower Recall
└─ Popular Recall
离线 Pipeline:
LoadMovieLens → PrepareTrainingSamples → TrainTwoTower
→ TrainDeepFM → EvaluateRetrieval → PublishArtifacts
详细职责见 backend/app/README.md。
MovieLens 1M
│
├─ 解析、正反馈筛选、时间切分、负采样
│
├─ 双塔训练 ──> 全量电影向量 ──> Top-N 召回
│
└─ DeepFM 训练 ──────────────> 候选精排
│
已看过滤 + 多样性重排
│
FastAPI + React
│
SQLite 用户反馈
backend/online/
pipeline/recommend.py 在线编排
plugin/ 召回、排序、重排、响应插件
backend/offline/
pipeline/batch_train.py 离线训练编排
stages/ 数据、训练、评估、发布阶段
backend/nearline/ 准实时更新边界
backend/common/ 公共协议、storage key、release manifest
backend/app/ FastAPI、模型定义、兼容 wrapper
backend/tests/ 后端测试
scripts/ 下载和训练命令
frontend/ React + Vite 前端
要求 Python 3.11+、Node.js 22+。
cd movielens_recsys
make setup
make download
make train分别启动服务:
make api
# 新终端
make web打开 http://localhost:5173,API 文档位于 http://localhost:8000/docs。
本地端到端验收:
make build
make api
# 新终端
cd frontend/dist && python3 -m http.server 8080
# 新终端
make verify-demo验收通过会输出推荐电影标题,证明页面入口、模型加载和推荐 API 都可用。
训练默认执行 3 个 epoch。快速验证可执行:
PYTHONPATH=backend .venv/bin/python scripts/train.py \
--epochs 1 --batch-size 8192 --embedding-dim 16启动在线服务和真实基础组件:
make docker-demo脚本按工业运行边界分阶段执行:先启动 Redis/Postgres/etcd/MinIO/Milvus,再运行一次性 offline-bootstrap,最后启动 online-api 和 frontend 并执行端到端验收。
offline-bootstrap 会检查 data/ml-1m 和 artifacts/latest。如果缺数据会下载 MovieLens;如果缺模型会跑一轮离线训练并发布到在线存储,然后再启动 online-api。
脚本会先用 alpine:3.20 做新容器启动预检。如果 Docker daemon 能响应但新容器无法启动,会快速失败并提示重启 Docker Desktop:
DOCKER_START_TIMEOUT_SECONDS=10 make docker-demo如果 Docker Hub 拉取基础镜像很慢,可以临时调短诊断超时:
DOCKER_PULL_TIMEOUT_SECONDS=60 make docker-demo脚本会逐个拉取 Redis/Postgres/etcd/MinIO/Milvus,超时会打印当前卡住的服务和关键日志。
也可以先单独检查 Docker daemon 是否能拉外部镜像:
make docker-pull-check如果 docker pull alpine:3.20 超时,问题在 Docker daemon 的 registry/proxy/mirror 链路;如果 docker run --rm alpine:3.20 true 超时,问题在 Docker Desktop/daemon 的容器启动路径。两者都不是本项目 Compose 配置或推荐算法代码问题。
如果需要一次性查看 daemon、pull、start-path 和当前 Compose 状态:
make docker-doctor启动后可以执行端到端验收:
make docker-verify该命令会检查:
- 前端页面可访问;
- API
/api/health模型 ready; /api/recommendations能返回真实推荐结果。
离线训练也可以作为独立 runner 在 Compose 里执行:
docker compose --profile offline run --rm offline-runnerCompose 服务边界:
online-api:FastAPI 推荐服务。offline-runner:离线训练批处理。redis:在线画像、热门召回、物料特征和版本指针。milvus:向量召回索引,在线召回优先使用,失败降级到本地模型向量。postgres:版本、运行记录、反馈等元数据。minio:模型、特征、报告对象存储。etcd:Milvus 元数据依赖。
curl -X POST http://localhost:8000/api/recommendations \
-H 'Content-Type: application/json' \
-d '{"user_id": 1, "limit": 10}'
curl 'http://localhost:8000/api/movies/search?q=matrix'
curl -X POST http://localhost:8000/api/feedback \
-H 'Content-Type: application/json' \
-d '{"user_id": 1, "movie_id": 1196, "rating": 5}'主要接口:
| 方法 | 路径 | 用途 |
|---|---|---|
| GET | /api/health |
服务和模型状态 |
| GET | /api/users |
演示用户列表 |
| POST | /api/recommendations |
个性化/冷启动推荐 |
| GET | /api/movies/search |
按片名或类型检索 |
| GET | /api/movies/{id}/similar |
相似电影 |
| POST | /api/feedback |
写入用户评分 |
| GET | /api/feedback/{user_id} |
用户反馈记录 |
| GET | /api/model/status |
模型版本和离线指标 |
make test
make build后端测试使用小型合成数据覆盖数据处理、模型张量、训练收敛、评估、产物往返、召回排序和 API;不需要下载 MovieLens 数据。前端测试覆盖用户选择和推荐结果渲染。
- MovieLens 1M 不包含海报 URL,前端使用算法生成的电影卡片封面。
- 反馈当前仍写入 SQLite,准工业级目标应迁移到 PostgreSQL。
- 当前代码仍可用本地 artifact 跑通;Compose 环境下离线会发布到 Redis/Milvus/Postgres/MinIO,在线召回会优先读取 Redis/Milvus,异常时降级到本地 artifact。
- 这是准工业级工程骨架,不等同于生产环境中的特征平台、实时流处理和多机训练系统。