1. 项目概述:美食推荐系统的技术实现
作为一名长期从事推荐系统开发的工程师,我最近完成了一个基于Vue+UniApp+Python技术栈的微信小程序美食推荐系统。这个项目的核心目标是通过协同过滤算法,为不同用户提供个性化的美食推荐服务。在实际开发过程中,我发现很多技术文档对这类全栈项目的实现细节描述不够完整,因此决定分享我的实战经验。
这个系统主要面向餐饮类小程序场景,特别适合外卖平台、快餐连锁等高频低决策成本的消费场景。前端采用Vue.js+UniApp实现跨平台开发,后端使用Python(Flask/Django框架)处理数据和算法逻辑,通过微信小程序作为主要用户入口。系统能够实时收集用户行为数据(浏览、评分、收藏等),基于协同过滤算法生成推荐列表,显著提升了用户粘性和转化率。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 技术架构详解
2.1 前端技术选型与实现
前端部分我们选择了Vue.js+UniApp的组合,主要基于以下几个考虑:
- 开发效率:UniApp基于Vue.js,可以使用熟悉的Vue语法快速开发,同时支持编译到多个平台(微信小程序、H5等)
- 性能优化:UniApp对小程序性能做了专门优化,解决了传统WebView渲染的性能瓶颈
- 组件生态:丰富的UniUI组件库可以快速构建美观的界面
核心页面结构如下:
- 首页:推荐菜品展示
- 详情页:菜品详细信息与用户评分
- 个人中心:用户偏好设置与历史记录
- 搜索页:支持基于关键词和分类的筛选
javascript复制// 典型页面结构示例
<template>
<view class="container">
<recommend-list :items="recommendItems" @item-click="handleItemClick"/>
<tab-bar :current="0"/>
</view>
</template>
<script>
import { getRecommendations } from '@/api/recommend'
export default {
data() {
return {
recommendItems: []
}
},
async onLoad() {
this.recommendItems = await getRecommendations()
},
methods: {
handleItemClick(item) {
uni.navigateTo({
url: `/pages/detail/detail?id=${item.id}`
})
}
}
}
</script>
注意事项:UniApp中使用Vue语法时需要注意小程序环境的限制,比如不能使用某些DOM API,样式写法也有差异。建议在开发前仔细阅读UniApp的文档说明。
2.2 后端架构设计
后端采用Python的Flask框架搭建RESTful API服务,主要模块包括:
- 用户模块:处理用户注册、登录、信息管理
- 菜品模块:管理菜品信息、分类、标签
- 行为收集模块:记录用户浏览、评分、收藏等行为
- 推荐模块:实现协同过滤算法,生成个性化推荐
数据库使用MySQL存储结构化数据,Redis作为缓存提升性能。以下是主要的数据库表设计:
用户表(users)
sql复制CREATE TABLE `users` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`openid` varchar(64) NOT NULL COMMENT '微信openid',
`nickname` varchar(64) DEFAULT NULL,
`avatar` varchar(255) DEFAULT NULL,
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `idx_openid` (`openid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
菜品表(dishes)
sql复制CREATE TABLE `dishes` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(64) NOT NULL,
`description` text,
`price` decimal(10,2) NOT NULL,
`image` varchar(255) DEFAULT NULL,
`category_id` int(11) DEFAULT NULL,
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
用户行为表(user_actions)
sql复制CREATE TABLE `user_actions` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` int(11) NOT NULL,
`dish_id` int(11) NOT NULL,
`action_type` tinyint(4) NOT NULL COMMENT '1浏览 2评分 3收藏',
`action_value` float DEFAULT NULL COMMENT '评分值',
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `idx_user_dish` (`user_id`,`dish_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
3. 协同过滤算法实现
3.1 算法选型与原理
我们采用了基于用户的协同过滤(UserCF)算法,主要基于以下考虑:
- 数据稀疏性:在初期用户量较少时,菜品-用户矩阵非常稀疏,UserCF表现更好
- 解释性强:可以告诉用户"与您口味相似的用户也喜欢..."
- 实现简单:适合作为MVP版本快速上线
算法核心步骤如下:
- 构建用户-菜品评分矩阵
- 计算用户相似度(使用余弦相似度)
- 找出最相似的K个用户
- 基于相似用户的喜好预测目标用户的评分
- 推荐评分最高的N个菜品
3.2 Python实现细节
以下是算法核心代码实现:
python复制import numpy as np
from collections import defaultdict
from sklearn.metrics.pairwise import cosine_similarity
class UserCFRecommender:
def __init__(self, k=20, n_rec=10):
self.k = k # 相似用户数
self.n_rec = n_rec # 推荐菜品数
self.user_sim = None
self.user_item_matrix = None
self.item_user_matrix = None
self.user_mean_rating = None
def fit(self, user_actions):
"""训练模型"""
# 构建用户-菜品评分矩阵
user_item = defaultdict(dict)
for action in user_actions:
if action['action_type'] == 2: # 只考虑评分行为
user_item[action['user_id']][action['dish_id']] = action['action_value']
# 转换为矩阵形式
all_users = list(user_item.keys())
all_items = list(set(dish for items in user_item.values() for dish in items))
user_index = {u:i for i,u in enumerate(all_users)}
item_index = {i:j for j,i in enumerate(all_items)}
matrix = np.zeros((len(all_users), len(all_items)))
for u, items in user_item.items():
for i, r in items.items():
matrix[user_index[u], item_index[i]] = r
# 计算用户相似度
self.user_sim = cosine_similarity(matrix)
self.user_item_matrix = matrix
self.item_user_matrix = matrix.T
self.user_mean_rating = matrix.mean(axis=1)
self.user_index = user_index
self.item_index = item_index
self.index_user = {v:k for k,v in user_index.items()}
self.index_item = {v:k for k,v in item_index.items()}
def recommend(self, user_id, n=None):
"""为用户生成推荐"""
if n is None:
n = self.n_rec
if user_id not in self.user_index:
return [] # 新用户无法推荐
uid = self.user_index[user_id]
sim_scores = list(enumerate(self.user_sim[uid]))
sim_scores = sorted(sim_scores, key=lambda x: x[1], reverse=True)
# 取最相似的k个用户(排除自己)
sim_users = [i for i,score in sim_scores[1:self.k+1]]
# 预测评分
pred_ratings = np.zeros(self.user_item_matrix.shape[1])
for i in range(len(pred_ratings)):
if self.user_item_matrix[uid, i] != 0:
continue # 已评分的菜品不推荐
# 加权平均相似用户的评分
numerator = 0
denominator = 0
for su in sim_users:
if self.user_item_matrix[su, i] == 0:
continue
numerator += self.user_sim[uid, su] * (self.user_item_matrix[su, i] - self.user_mean_rating[su])
denominator += abs(self.user_sim[uid, su])
if denominator == 0:
pred_ratings[i] = 0
else:
pred_ratings[i] = self.user_mean_rating[uid] + numerator / denominator
# 获取评分最高的n个菜品
rec_indices = np.argsort(pred_ratings)[-n:][::-1]
return [self.index_item[i] for i in rec_indices if pred_ratings[i] > 0]
实操心得:在实际应用中,我们发现纯UserCF算法存在几个问题:1)新用户冷启动问题;2)热门商品容易被过度推荐。针对这些问题,我们后续加入了基于内容的推荐作为补充,并对热门商品进行了降权处理。
4. 系统优化与实践经验
4.1 性能优化策略
-
缓存机制:
- 使用Redis缓存热门推荐结果
- 对相似度矩阵进行定期缓存(每天更新一次)
- 实现代码示例:
python复制import redis from datetime import timedelta r = redis.Redis(host='localhost', port=6379, db=0) def get_recommendations(user_id): cache_key = f"rec:{user_id}" cached = r.get(cache_key) if cached: return json.loads(cached) # 计算推荐结果 recommendations = recommender.recommend(user_id) # 缓存结果,设置过期时间 r.setex(cache_key, timedelta(hours=6), json.dumps(recommendations)) return recommendations
-
增量计算:
- 用户新行为产生后,只更新受影响的部分相似度
- 定期全量更新用户相似度矩阵(如每天凌晨)
-
降级策略:
- 当推荐系统响应超时,返回热门菜品作为兜底
- 对新用户采用基于流行度的推荐策略
4.2 常见问题与解决方案
问题1:新用户冷启动
- 解决方案:
- 引导用户选择喜欢的菜品类别
- 结合用户地理位置推荐附近热门
- 使用基于内容的推荐作为补充
问题2:推荐结果过于集中
- 解决方案:
- 对推荐结果进行多样性控制
- 引入随机扰动因子
- 混合多种推荐策略的结果
问题3:实时性要求高
- 解决方案:
- 实现近实时行为收集(使用消息队列)
- 对重要行为(如评分)立即触发推荐更新
- 采用更轻量级的相似度计算方法
4.3 部署架构
我们的生产环境部署架构如下:
-
前端:
- 微信小程序部署在微信平台
- H5版本部署在CDN上
-
后端:
- 使用Gunicorn+Flask部署推荐API
- Nginx作为反向代理和负载均衡
- MySQL主从架构保证数据可靠性
- Redis集群处理缓存和会话
-
数据处理:
- 使用Airflow调度每日离线计算任务
- Spark处理大规模数据预处理
5. 扩展与演进方向
在实际运营过程中,我们发现系统还有以下可以改进的地方:
- 算法融合:将协同过滤与深度学习模型结合,使用Wide&Deep等混合模型提升推荐效果
- 上下文感知:加入时间、地点、天气等上下文信息,实现更精准的推荐
- AB测试框架:构建完整的AB测试系统,科学评估算法改进效果
- 可视化分析:增加推荐结果的可解释性,帮助运营人员理解推荐逻辑
一个特别实用的技巧是:在推荐结果中增加推荐理由,如"因为您喜欢川菜"或"与您口味相似的用户也喜欢",可以显著提升用户的点击率和满意度。我们通过A/B测试发现,带有解释的推荐结果CTR提升了23%。
