这个基于Django框架的美食推荐与可视化平台,本质上是一个融合了协同过滤算法和大数据分析技术的智能推荐系统。作为一名做过多个推荐系统项目的开发者,我发现美食领域特别适合作为毕业设计选题——它既有足够的数据维度供算法发挥,又能通过可视化让非技术用户直观感受到推荐系统的价值。
这个项目的核心在于三个技术点的有机结合:Django框架提供的Web开发基础、协同过滤算法实现的个性化推荐、以及可视化技术带来的数据呈现。不同于简单的菜品展示网站,它能根据用户历史行为数据(如评分、浏览记录等)自动挖掘潜在饮食偏好,实现"千人千面"的美食推荐。
提示:选择美食领域的一个优势是数据获取相对容易,既可以通过公开数据集(如Yelp的餐厅数据),也能自己构建小型数据集进行原型验证。
Django作为Python生态中最成熟的Web框架之一,其"开箱即用"的特性特别适合毕业设计类项目。我在实际开发中发现几个显著优势:
python复制class Dish(models.Model):
name = models.CharField(max_length=100)
cuisine_type = models.CharField(max_length=50)
ingredients = models.TextField()
average_rating = models.FloatField(default=0)
/admin界面管理数据,省去前端开发时间。只需几行代码就能启用:python复制from django.contrib import admin
admin.site.register(Dish)
html复制{% for dish in recommended_dishes %}
<div class="dish-card">
<h3>{{ dish.name }}</h3>
<p>推荐指数: {{ dish.predicted_rating|floatformat:1 }}</p>
</div>
{% endfor %}
协同过滤(Collaborative Filtering)是推荐系统的经典算法,主要分为两类:
基于用户的协同过滤(User-based CF):
python复制from sklearn.metrics.pairwise import cosine_similarity
user_similarity = cosine_similarity(user_rating_matrix)
基于物品的协同过滤(Item-based CF):
我在项目中实际采用的是改进版的SVD(奇异值分解)算法,通过surprise库实现:
python复制from surprise import SVD, Dataset, accuracy
from surprise.model_selection import train_test_split
data = Dataset.load_builtin('ml-100k')
trainset, testset = train_test_split(data, test_size=0.25)
algo = SVD(n_factors=100, n_epochs=20, lr_all=0.005, reg_all=0.02)
algo.fit(trainset)
predictions = algo.test(testset)
accuracy.rmse(predictions)
注意:实际部署时要考虑冷启动问题。我的解决方案是对新用户先采用基于内容的推荐(如根据用户注册时选择的饮食偏好),积累一定数据后再切换到协同过滤。
使用ECharts.js实现动态可视化是项目的亮点之一。主要包含以下几个视图:
javascript复制option = {
radar: {
indicator: [
{ name: '川菜', max: 5 },
{ name: '粤菜', max: 5 },
{ name: '西餐', max: 5 }
]
},
series: [{
type: 'radar',
data: [{
value: [4.2, 3.5, 2.8],
name: '用户A'
}]
}]
};
javascript复制option = {
series: [{
type: 'sankey',
data: [
{name: '用户A'},
{name: '用户B(相似度87%)'},
{name: '水煮鱼'}
],
links: [
{source: '用户A', target: '用户B(相似度87%)', value: 0.87},
{source: '用户B(相似度87%)', target: '水煮鱼', value: 4.8}
]
}]
};
使用Heatmap.js实现用户-菜品评分矩阵的可视化,可以直观发现数据稀疏性问题:
python复制import seaborn as sns
import matplotlib.pyplot as plt
plt.figure(figsize=(12,8))
sns.heatmap(rating_matrix, cmap="YlGnBu")
plt.title("User-Dish Rating Matrix")
plt.xlabel("Dishes")
plt.ylabel("Users")
plt.show()
美食推荐系统的数据质量直接影响推荐效果。我通常从以下渠道获取数据:
公开数据集:
数据清洗要点:
python复制# 评分矩阵填充示例
user_mean = ratings.groupby('user_id')['rating'].mean()
dish_mean = ratings.groupby('dish_id')['rating'].mean()
rating_matrix = ratings.pivot(index='user_id', columns='dish_id', values='rating')
rating_matrix = rating_matrix.fillna(rating_matrix.mean(axis=0)) # 用菜品平均分填充
完整的推荐流程包括:
离线训练:
python复制import joblib
joblib.dump(algo, 'recommend_model.pkl')
在线推荐:
python复制from django.core.cache import cache
def get_recommendations(user_id):
cache_key = f"recs_{user_id}"
if cache.get(cache_key):
return cache.get(cache_key)
model = joblib.load('recommend_model.pkl')
dishes = Dish.objects.all()
predictions = [model.predict(user_id, dish.id) for dish in dishes]
recommendations = sorted(predictions, key=lambda x: x.est, reverse=True)[:10]
cache.set(cache_key, recommendations, timeout=3600)
return recommendations
在实际部署中发现几个性能瓶颈及解决方案:
相似度计算加速:
缓存策略:
数据库优化:
python复制Dish.objects.select_related('restaurant').filter(...)
现象:新用户或新菜品缺乏历史数据,难以产生推荐
解决方案:
新用户:
新菜品:
python复制def content_based_recommend(dish_id, top_n=5):
target = Dish.objects.get(id=dish_id)
dishes = Dish.objects.exclude(id=dish_id)
similarities = []
for dish in dishes:
sim = calculate_similarity(target.tags, dish.tags)
similarities.append((dish.id, sim))
return sorted(similarities, key=lambda x: x[1], reverse=True)[:top_n]
现象:用户只评价了少量菜品,导致评分矩阵非常稀疏
解决方案:
矩阵填充技术:
混合推荐策略:
python复制def hybrid_recommend(user_id, alpha=0.7):
cf_recs = collaborative_filtering(user_id) # 协同过滤结果
cb_recs = content_based(user_id) # 基于内容结果
# 加权融合
recommendations = []
for dish_id in set(cf_recs.keys()).union(cb_recs.keys()):
score = alpha*cf_recs.get(dish_id,0) + (1-alpha)*cb_recs.get(dish_id,0)
recommendations.append((dish_id, score))
return sorted(recommendations, key=lambda x: x[1], reverse=True)
现象:用户最新行为无法立即影响推荐结果
解决方案:
短期兴趣记录:
在线学习:
python复制from surprise import BaselineOnly
algo = BaselineOnly()
algo.fit(trainset) # 初始训练
# 当有新评分时
new_rating = (user_id, dish_id, rating)
algo.train_update([new_rating]) # 增量更新
在实际开发过程中,我发现这个基础框架可以进一步扩展:
多模态推荐:
情境感知推荐:
社交化推荐:
python复制def social_recommend(user_id):
friends = get_friends(user_id)
friend_ratings = Rating.objects.filter(user__in=friends)
popular_dishes = friend_ratings.values('dish').annotate(
avg_rating=Avg('rating'),
rating_count=Count('dish')
).order_by('-avg_rating')[:10]
return [item['dish'] for item in popular_dishes]
这个项目我前后迭代了三个版本,最大的体会是:推荐系统不是算法越复杂越好,而是要找到业务需求与技术方案的平衡点。对于美食推荐场景,简单但解释性强的算法(如改进的Item-based CF)往往比复杂的深度学习模型更实用。