1. 项目概述
这个旅游推荐系统项目采用协同过滤算法作为核心推荐引擎,结合Django框架构建完整的Web应用。系统主要解决传统旅游平台"千人一面"的痛点,通过分析用户历史行为数据,为不同偏好的游客提供个性化的景点和路线推荐。
我在实际开发中发现,单纯的协同过滤算法在旅游领域存在几个特殊挑战:景点数据稀疏性高(用户去过的景点占总量比例极低)、季节性因素影响大(冬季滑雪场和夏季海滨的需求完全不同)、用户行为数据获取成本高(需要大量真实用户交互)。针对这些问题,系统采用了混合推荐策略,在基础协同过滤之上叠加了基于内容的过滤和简单规则引擎。
2. 技术架构设计
2.1 整体架构
系统采用经典的三层架构:
- 表现层:Django模板+ Bootstrap前端
- 业务逻辑层:Django视图+自定义推荐引擎
- 数据层:PostgreSQL + Redis缓存
选择Django主要考虑其完善的ORM系统、自带admin后台以及丰富的第三方包生态。实测在中等数据量(10万用户级别)下,Django ORM配合适当的索引设计完全能满足性能需求。
2.2 数据模型设计
核心数据表包括:
python复制class User(models.Model):
username = models.CharField(max_length=50)
# 其他用户基础字段...
class Attraction(models.Model):
name = models.CharField(max_length=100)
location = models.PointField() # 使用GeoDjango支持地理位置查询
tags = models.ManyToManyField('Tag')
# 其他景点属性...
class UserRating(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
attraction = models.ForeignKey(Attraction, on_delete=models.CASCADE)
rating = models.SmallIntegerField() # 1-5星评分
timestamp = models.DateTimeField(auto_now_add=True)
注意:实际项目中建议对UserRating表添加(user, attraction)联合唯一索引,防止重复评分
3. 推荐算法实现
3.1 协同过滤基础实现
采用基于用户的协同过滤(UserCF),核心计算步骤如下:
- 构建用户-景点评分矩阵
- 计算用户相似度(余弦相似度)
- 找出目标用户的K个最近邻
- 基于邻居的评分预测目标用户对未评分景点的偏好
关键代码片段:
python复制def user_similarity(user1, user2):
"""计算两个用户的余弦相似度"""
common_ratings = Rating.objects.filter(
user__in=[user1, user2]
).values('attraction').annotate(count=Count('attraction')).filter(count=2)
if not common_ratings.exists():
return 0
ratings1 = []
ratings2 = []
for item in common_ratings:
r1 = Rating.objects.get(user=user1, attraction=item['attraction']).rating
r2 = Rating.objects.get(user=user2, attraction=item['attraction']).rating
ratings1.append(r1)
ratings2.append(r2)
return cosine_similarity([ratings1], [ratings2])[0][0]
3.2 冷启动优化方案
新用户或新景点面临的冷启动问题通过以下策略缓解:
- 对于新用户:初始推荐热门景点和近期评价上升快的景点
- 对于新景点:采用基于内容的相似度推荐(通过标签匹配)
- 混合推荐公式:最终评分 = w1协同过滤评分 + w2内容相似度评分 + w3*热度评分
4. 系统功能实现
4.1 核心功能模块
-
用户画像构建:
- 显式数据:评分、收藏、浏览时长
- 隐式数据:点击流、搜索关键词、路线规划偏好
-
推荐展示:
- 景点卡片式布局
- 推荐理由展示("因为您喜欢X,所以推荐Y")
- 多样化推荐列表(热门、小众、季节限定等)
-
路线规划:
- 基于地理位置的景点聚类
- 交通时间估算(调用地图API)
- 个性化路线评分(根据用户历史偏好调整权重)
4.2 Django后台关键配置
python复制# settings.py 关键配置
CACHES = {
'default': {
'BACKEND': 'django_redis.cache.RedisCache',
'LOCATION': 'redis://127.0.0.1:6379/1',
'OPTIONS': {
'CLIENT_CLASS': 'django_redis.client.DefaultClient',
}
}
}
# 推荐结果缓存1小时
RECOMMENDATION_CACHE_TIMEOUT = 3600
5. 性能优化实践
5.1 推荐计算优化
- 增量计算:用户新行为触发局部更新而非全量重算
- 夜间批量作业:使用Celery定时更新用户相似度矩阵
- 缓存策略:
- 一级缓存:Redis存储热门推荐结果
- 二级缓存:预生成并持久化用户个性化推荐列表
5.2 数据库优化
sql复制-- 为常用查询添加索引
CREATE INDEX idx_user_rating_user ON user_rating(user_id);
CREATE INDEX idx_user_rating_attraction ON user_rating(attraction_id);
CREATE INDEX idx_user_rating_composite ON user_rating(user_id, attraction_id);
-- 物化视图用于加速统计查询
CREATE MATERIALIZED VIEW attraction_stats AS
SELECT
attraction_id,
AVG(rating) as avg_rating,
COUNT(*) as rating_count
FROM user_rating
GROUP BY attraction_id;
6. 部署实践
6.1 生产环境部署
推荐使用Docker Compose编排服务:
yaml复制version: '3'
services:
web:
build: .
command: gunicorn project.wsgi:application --bind 0.0.0.0:8000
volumes:
- .:/code
ports:
- "8000:8000"
depends_on:
- redis
- db
redis:
image: redis:alpine
db:
image: postgres:13
environment:
POSTGRES_PASSWORD: yourpassword
volumes:
- postgres_data:/var/lib/postgresql/data/
volumes:
postgres_data:
6.2 性能监控
- Django调试工具栏:开发环境分析查询性能
- Prometheus + Grafana:生产环境监控
- 关键指标:推荐响应时间、缓存命中率、数据库查询耗时
- 日志分析:ELK栈收集和分析用户行为日志
7. 常见问题与解决方案
7.1 推荐质量相关问题
问题1:推荐的景点过于集中
- 原因:地理位置权重过高
- 解决:在相似度计算中降低距离因素的权重系数
问题2:新用户推荐不准确
- 原因:冷启动数据不足
- 解决:增加引导问卷,收集初始偏好
7.2 技术实现问题
问题3:推荐计算耗时过长
- 优化方案:
python复制# 使用django-bulk-update批量操作 from django_bulk_update.helper import bulk_update def batch_update_similarity(): users = User.objects.all() similarity_updates = [] for user in users: # 计算逻辑... similarity_updates.append(user) bulk_update(similarity_updates, update_fields=['similarity_json'])
问题4:高并发下响应慢
- 解决方案:
- 推荐结果静态化
- 使用Nginx缓存高频访问的推荐页
- 读写分离配置
8. 扩展优化方向
- 实时推荐:接入Kafka处理用户实时行为事件
- 多模态推荐:结合景点图片的CNN特征分析
- 社交化推荐:引入好友关系网络增强推荐
- 可解释性增强:使用SHAP值解释推荐决策
我在实际部署中发现,对于中小型旅游平台,先做好基于协同过滤的基础推荐再逐步叠加复杂算法是最稳妥的方案。初期可以设置一个简单的AB测试框架,对比不同算法在实际业务中的转化率,用数据驱动算法迭代。
