1. 项目概述:构建一个基于协同过滤的零食推荐系统
最近完成了一个很有意思的电商推荐系统项目,使用Python+Django+Vue3技术栈实现了一个智能零食商城。核心亮点在于采用了协同过滤推荐算法,能够根据用户的历史行为和偏好,个性化推荐他们可能喜欢的零食。这个系统上线后,商城的用户点击率和购买转化率都有了显著提升。
作为一个全栈项目,前端采用Vue3实现响应式布局和流畅的用户交互体验,后端使用Django框架提供稳定的API接口,数据库选用MySQL存储用户信息、商品数据和交互记录。整个系统采用微服务架构设计,各组件松耦合,便于后期维护和扩展。
2. 技术选型与架构设计
2.1 前端技术栈选择
选择Vue3作为前端框架主要基于以下几个考虑:
- 响应式系统更高效:Vue3使用Proxy替代Object.defineProperty,性能更好
- Composition API更灵活:代码组织更清晰,逻辑复用更方便
- 体积更小:相比Vue2,打包体积减少了约40%
- 更好的TypeScript支持:对于大型项目更友好
前端项目结构如下:
code复制src/
├── assets/ # 静态资源
├── components/ # 公共组件
├── composables/ # 组合式函数
├── router/ # 路由配置
├── stores/ # Pinia状态管理
├── views/ # 页面组件
├── App.vue # 根组件
└── main.js # 入口文件
2.2 后端技术栈选择
Django作为后端框架的优势:
- 自带ORM:简化数据库操作
- 完善的Admin后台:快速构建管理系统
- 强大的中间件支持:方便实现权限控制、日志记录等功能
- 丰富的生态:有大量成熟的第三方包可用
后端项目结构:
code复制project/
├── apps/
│ ├── users/ # 用户模块
│ ├── products/ # 商品模块
│ ├── orders/ # 订单模块
│ └── recommend/ # 推荐模块
├── config/ # 项目配置
├── static/ # 静态文件
├── templates/ # 模板文件
└── manage.py # 管理脚本
2.3 数据库设计
MySQL数据库主要表结构设计:
- 用户表(users):
sql复制CREATE TABLE `users` (
`id` bigint NOT NULL AUTO_INCREMENT,
`username` varchar(50) NOT NULL,
`password` varchar(128) NOT NULL,
`email` varchar(100) NOT NULL,
`created_at` datetime NOT NULL,
`updated_at` datetime NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `username` (`username`),
UNIQUE KEY `email` (`email`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
- 商品表(products):
sql复制CREATE TABLE `products` (
`id` bigint NOT NULL AUTO_INCREMENT,
`name` varchar(100) NOT NULL,
`description` text,
`price` decimal(10,2) NOT NULL,
`category_id` bigint NOT NULL,
`image_url` varchar(255),
`created_at` datetime NOT NULL,
`updated_at` datetime NOT NULL,
PRIMARY KEY (`id`),
KEY `category_id` (`category_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
- 用户行为表(user_actions):
sql复制CREATE TABLE `user_actions` (
`id` bigint NOT NULL AUTO_INCREMENT,
`user_id` bigint NOT NULL,
`product_id` bigint NOT NULL,
`action_type` tinyint NOT NULL COMMENT '1:浏览 2:收藏 3:加购 4:购买',
`action_time` datetime NOT NULL,
PRIMARY KEY (`id`),
KEY `user_id` (`user_id`),
KEY `product_id` (`product_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
3. 协同过滤推荐算法实现
3.1 基于用户的协同过滤(UserCF)
UserCF的核心思想是找到与目标用户兴趣相似的其他用户,然后推荐这些相似用户喜欢的商品。
实现步骤:
- 构建用户-物品评分矩阵
- 计算用户相似度
- 生成推荐列表
关键代码实现:
python复制import numpy as np
from sklearn.metrics.pairwise import cosine_similarity
class UserCFRecommender:
def __init__(self):
self.user_item_matrix = None
self.user_similarity = None
def fit(self, user_actions):
# 构建用户-物品矩阵
users = user_actions['user_id'].unique()
products = user_actions['product_id'].unique()
self.user_item_matrix = np.zeros((len(users), len(products)))
user_id_map = {user_id: idx for idx, user_id in enumerate(users)}
product_id_map = {product_id: idx for idx, product_id in enumerate(products)}
for _, row in user_actions.iterrows():
user_idx = user_id_map[row['user_id']]
product_idx = product_id_map[row['product_id']]
# 根据行为类型赋予不同权重
if row['action_type'] == 1: # 浏览
self.user_item_matrix[user_idx][product_idx] += 1
elif row['action_type'] == 2: # 收藏
self.user_item_matrix[user_idx][product_idx] += 3
elif row['action_type'] == 3: # 加购
self.user_item_matrix[user_idx][product_idx] += 5
elif row['action_type'] == 4: # 购买
self.user_item_matrix[user_idx][product_idx] += 8
# 计算用户相似度矩阵
self.user_similarity = cosine_similarity(self.user_item_matrix)
self.user_id_map = user_id_map
self.product_id_map = product_id_map
self.reverse_product_map = {v: k for k, v in product_id_map.items()}
def recommend(self, user_id, top_n=10):
user_idx = self.user_id_map[user_id]
# 获取相似用户
sim_scores = list(enumerate(self.user_similarity[user_idx]))
sim_scores = sorted(sim_scores, key=lambda x: x[1], reverse=True)
# 取前20个相似用户(排除自己)
sim_users = [i for i, score in sim_scores[1:21]]
# 计算推荐分数
recommendation_scores = np.zeros(len(self.product_id_map))
for product_idx in range(len(self.product_id_map)):
if self.user_item_matrix[user_idx][product_idx] == 0: # 只推荐用户没接触过的商品
for sim_user in sim_users:
recommendation_scores[product_idx] += (
self.user_similarity[user_idx][sim_user] *
self.user_item_matrix[sim_user][product_idx]
)
# 获取topN推荐
top_indices = recommendation_scores.argsort()[::-1][:top_n]
return [self.reverse_product_map[i] for i in top_indices]
3.2 基于物品的协同过滤(ItemCF)
ItemCF的核心思想是计算物品之间的相似度,然后推荐与用户历史偏好物品相似的其他物品。
实现步骤:
- 构建物品-用户矩阵
- 计算物品相似度
- 生成推荐列表
关键代码实现:
python复制class ItemCFRecommender:
def __init__(self):
self.item_user_matrix = None
self.item_similarity = None
def fit(self, user_actions):
# 构建物品-用户矩阵
products = user_actions['product_id'].unique()
users = user_actions['user_id'].unique()
self.item_user_matrix = np.zeros((len(products), len(users)))
product_id_map = {product_id: idx for idx, product_id in enumerate(products)}
user_id_map = {user_id: idx for idx, user_id in enumerate(users)}
for _, row in user_actions.iterrows():
product_idx = product_id_map[row['product_id']]
user_idx = user_id_map[row['user_id']]
# 根据行为类型赋予不同权重
if row['action_type'] == 1: # 浏览
self.item_user_matrix[product_idx][user_idx] += 1
elif row['action_type'] == 2: # 收藏
self.item_user_matrix[product_idx][user_idx] += 3
elif row['action_type'] == 3: # 加购
self.item_user_matrix[product_idx][user_idx] += 5
elif row['action_type'] == 4: # 购买
self.item_user_matrix[product_idx][user_idx] += 8
# 计算物品相似度矩阵
self.item_similarity = cosine_similarity(self.item_user_matrix)
self.product_id_map = product_id_map
self.user_id_map = user_id_map
self.reverse_product_map = {v: k for k, v in product_id_map.items()}
def recommend(self, user_id, top_n=10):
user_idx = self.user_id_map[user_id]
# 获取用户历史行为物品
user_history = [
product_id for product_id, actions in user_actions.groupby('product_id')
if user_id in actions['user_id'].values
]
# 计算推荐分数
recommendation_scores = np.zeros(len(self.product_id_map))
for product_idx in range(len(self.product_id_map)):
if product_idx not in user_history: # 只推荐用户没接触过的商品
for hist_product in user_history:
hist_idx = self.product_id_map[hist_product]
recommendation_scores[product_idx] += (
self.item_similarity[product_idx][hist_idx] *
self.item_user_matrix[hist_idx][user_idx]
)
# 获取topN推荐
top_indices = recommendation_scores.argsort()[::-1][:top_n]
return [self.reverse_product_map[i] for i in top_indices]
3.3 混合推荐策略
为了结合UserCF和ItemCF的优势,我们实现了一个混合推荐器:
python复制class HybridRecommender:
def __init__(self, user_cf_weight=0.6, item_cf_weight=0.4):
self.user_cf = UserCFRecommender()
self.item_cf = ItemCFRecommender()
self.user_cf_weight = user_cf_weight
self.item_cf_weight = item_cf_weight
def fit(self, user_actions):
self.user_cf.fit(user_actions)
self.item_cf.fit(user_actions)
def recommend(self, user_id, top_n=10):
# 获取两种推荐结果
user_cf_recs = self.user_cf.recommend(user_id, top_n*2)
item_cf_recs = self.item_cf.recommend(user_id, top_n*2)
# 合并结果并加权
recommendations = {}
for i, product_id in enumerate(user_cf_recs):
recommendations[product_id] = recommendations.get(product_id, 0) + (len(user_cf_recs)-i) * self.user_cf_weight
for i, product_id in enumerate(item_cf_recs):
recommendations[product_id] = recommendations.get(product_id, 0) + (len(item_cf_recs)-i) * self.item_cf_weight
# 排序并返回topN
sorted_recs = sorted(recommendations.items(), key=lambda x: x[1], reverse=True)
return [product_id for product_id, score in sorted_recs[:top_n]]
4. 系统实现与优化
4.1 推荐系统架构
整个推荐系统采用离线计算和实时推荐相结合的架构:
-
离线计算层:
- 每天凌晨计算用户相似度和物品相似度矩阵
- 预生成部分推荐结果存入Redis缓存
- 使用Celery异步任务处理计算密集型任务
-
实时推荐层:
- 用户实时行为(浏览、收藏等)触发实时推荐更新
- 结合离线计算结果和实时行为数据生成最终推荐
- 使用Redis存储用户最近行为,提高响应速度
4.2 性能优化
-
数据稀疏性问题处理:
- 采用矩阵分解技术降维
- 实现基于物品的协同过滤比基于用户的更高效
- 对长尾物品进行适当降权
-
冷启动问题解决方案:
- 新用户:基于热门商品和品类推荐
- 新商品:基于内容相似度推荐给可能感兴趣的用户
- 实现混合推荐策略,结合协同过滤和基于内容的推荐
-
实时性优化:
- 使用Redis存储用户最近行为
- 实现增量更新算法,避免全量计算
- 采用微批处理方式平衡实时性和性能
4.3 推荐效果评估
我们设计了以下指标评估推荐效果:
-
点击率(CTR):
python复制def calculate_ctr(recommendations, user_clicks): total_recommends = len(recommendations) total_clicks = sum(1 for product in recommendations if product in user_clicks) return total_clicks / total_recommends if total_recommends > 0 else 0 -
转化率(Conversion Rate):
python复制def calculate_conversion(recommendations, user_purchases): total_recommends = len(recommendations) total_purchases = sum(1 for product in recommendations if product in user_purchases) return total_purchases / total_recommends if total_recommends > 0 else 0 -
覆盖率(Coverage):
python复制def calculate_coverage(recommendations, total_products): unique_recommended = len(set(recommendations)) return unique_recommended / total_products -
多样性(Diversity):
python复制from sklearn.metrics.pairwise import cosine_similarity def calculate_diversity(recommendations, product_features): feature_vectors = [product_features[pid] for pid in recommendations] similarity_matrix = cosine_similarity(feature_vectors) upper_triangle = similarity_matrix[np.triu_indices(len(recommendations), k=1)] return 1 - np.mean(upper_triangle)
5. 系统部署与运维
5.1 生产环境部署
我们使用Docker容器化部署整个系统,主要包含以下服务:
- Web服务:Gunicorn + Nginx
- 数据库:MySQL主从复制
- 缓存:Redis集群
- 异步任务:Celery + RabbitMQ
- 监控:Prometheus + Grafana
docker-compose.yml关键配置:
yaml复制version: '3'
services:
web:
build: .
command: gunicorn config.wsgi:application --bind 0.0.0.0:8000 --workers 4
volumes:
- .:/code
ports:
- "8000:8000"
depends_on:
- redis
- db
db:
image: mysql:8.0
environment:
MYSQL_DATABASE: snackshop
MYSQL_ROOT_PASSWORD: yourpassword
volumes:
- db_data:/var/lib/mysql
ports:
- "3306:3306"
redis:
image: redis:6
ports:
- "6379:6379"
volumes:
- redis_data:/data
celery:
build: .
command: celery -A config worker -l info
volumes:
- .:/code
depends_on:
- redis
- db
volumes:
db_data:
redis_data:
5.2 性能监控与调优
-
Django性能监控:
- 使用django-debug-toolbar分析SQL查询
- 配置Django日志记录慢查询
- 使用caching框架减少数据库访问
-
推荐算法优化:
- 实现基于时间的衰减因子,更重视近期行为
- 添加多样性控制,避免推荐结果过于集中
- 实现AB测试框架,对比不同算法效果
-
数据库优化:
- 为常用查询添加适当索引
- 定期优化表结构
- 使用读写分离减轻主库压力
6. 项目总结与经验分享
在开发这个推荐系统的过程中,积累了一些有价值的经验:
-
数据质量至关重要:
- 实际开发中发现原始用户行为数据存在大量噪声
- 实现了一套数据清洗流程,过滤无效和异常数据
- 建立了数据质量监控机制
-
算法不是越复杂越好:
- 尝试过更复杂的矩阵分解算法,但实际效果提升有限
- 最终选择了更简单但更稳定的协同过滤算法
- 在准确性和性能之间找到了平衡点
-
实时性带来的挑战:
- 完全实时的推荐系统实现成本很高
- 采用近实时策略,用户行为延迟5-10分钟反映在推荐中
- 这种折中方案在业务可接受范围内大幅降低了系统复杂度
-
系统可解释性的重要性:
- 初期用户对推荐结果信任度不高
- 增加了"为什么推荐这个"的解释功能
- 显著提高了用户对推荐结果的接受度
这个项目从技术选型到算法实现再到系统优化,每一个环节都遇到了各种挑战,但最终都找到了合适的解决方案。推荐系统是一个需要持续迭代优化的领域,后续计划引入深度学习模型进一步提升推荐效果。