文章目录
有需要本项目的代码或文档以及全部资源,或者部署调试可以私信博主
项目介绍
随着网络和数字媒体的发展,在线美食平台的资源日渐丰富。如何让用户在广博的美食世界里快速找到自己喜欢的美食成为困难。针对这种现象,美食推荐系统应运而生。本文对目前使用最广泛的推荐算法——协同过滤推荐算法展开研究,并实现该算法的两种模型,即基于用户的协同过滤和基于物品的协同过滤为用户提供个性化美食推荐服务,让用户方便、快速的找到自己感兴趣的美食。系统利用爬虫技术在美食天下网中获取美食相关美食数据信息,通过协同过滤算法将相关的数据生成相应的推荐结果,可以直观形象地推荐给用户。
研究背景
如今,随叫随到的外卖服务非常流行。食客可以浏览在线餐厅市场,选择餐厅并预订他们喜欢的食物或饮料。但是面对网络数据的爆炸增长,使得用户难以在海量的数据中快速地找到适合自己的美食,具有一定的盲目性。通过对现有美食App的调查发现,美食数据排行过于笼统,不能有效解决用户的个人喜好等问题。针对以上问题,现阶段的市场提供个性化推荐服务、提高检索效率、优化用户体验成了广大食客的诉求。面对这一诉求,根据数据集的用户和餐厅特征,提出基于协同过滤的美食推荐系统算法,该算法结合了基于用户的推荐算法、基于美食的推荐算法。现如今,随着我们的生活水准不断提高,对吃食也有了一定的要求,然而大多时候由于其他一些因素的限制,让大众无法了解到所喜爱的饮食习惯,所以有必要设计一个美食推荐系统,记录饮食习惯,推荐匹配度最高的美食。
研究的目的与意义
本课题主要是解决美食网站的个性化推荐功能,以便准确向用户推荐感兴趣的信息。个性化推荐的主要思想是,根据用户的历史操作行为,分析用户兴趣喜好,然后把用户比较感兴趣的同时没有操作行为的物品推荐给用户。个性化推荐也可以根据用户操作行为的变化而实时分析并推荐用户喜好程度比较高的物品,这样用户会对网站产生粘性,提高用户的忠诚度,为商家带来流量,进一步提高商家利润,具有较大的商业价值,可以达到双赢的目的。
个性化推荐的基础和核心是推荐算法,推荐算法的好坏会直接影响推荐效果。目前比较流行的推荐算法是基于用户的协同过滤推荐算法、基于物品的协同过滤推荐算法、聚类算法和混合推荐算法等,这些算法的本质都是分析用户喜好进而向用户推荐最喜欢的物品,不同的算法也会有不同的侧重点和特点。
本课题研究的推荐算法是基于用户和物品的协同过滤推荐算法,基于用户的协同过滤推荐算法通过比较用户之间的操作行为,分析与目标用户操作行为最相似的若干个用户,将若干个最相似用户的喜好程度较高的物品推荐给目标用户,基于物品的协同过滤推荐算法通过比较物品之间的相关性,为用户推荐用户已经喜欢物品的相关性较高的其他物品。
协同过滤算法
协同过滤推荐算法是目前应用最广泛的个性化推荐算法。该算法的基本思想是“物以类聚,人以群分”。对于用户A,我们可以采用两种推荐方案:一是找到和用户A品味相同的用户群,把这些用户喜欢的物品推荐给A;二是以A当前喜欢的物品为参考,把和这个物品相似的物品集推荐给A。这两种解决方案分别描述的是两种模型,一种是基于用户的协同过滤(user-based),一种是基于物品(item-based)的协同过滤。
基于用户的协同过滤算法定义
基于用户的协同过滤算法,基本思想是“人以群分”,根据目标用户对物品的喜好,找到和目标用户口味相似的“邻居”用户群,再根据邻居的历史偏好对用户进行推荐,见图1-1所示。假设用户A喜欢美食A和B,用户B喜欢美食D,用户C美食音乐A ,B 和C;从这些用户的偏好信息中,我们可以发现用户A、C的喜好相似。因为用户C喜欢美食C,我们可以推断用户A很有可能也喜欢,可以将美食C推荐给用户A。
根据用户历史行为信息构建用户-物品评分矩阵。
根据用户-物品评分矩阵计算用户之间的相似度
计算相似度常用的方法有余弦算法、修正余弦算法、皮尔森算法等等,本物品采用余弦算法。余弦算法公式如下所示:
表示:(用户u对物品i的评分用户v对物品i的评分)之和/(用户u对物品i的评分的平方之和再开方)(用户v对物品i的评分的平方之和再开方)
根据用户之间的相似度得到目标用户的最近邻居KNN
KNN的筛选常用的有两种方式,一种是设置相似度阀值,给定一个相似度的下限,大于下限的相似度为最近邻居,一种是根据与目标用户相似度的高低来选择前N个最近邻居,本次物品以前N个为例,相似度排序采用经典冒泡排序法。
预测物品评分并进行推荐
计算公式如下所示:
基于物品的协同过滤算法的定义
而基于物品的协同过滤算法(Item-basing CF)根据物品考虑问题,但算法本身并不是根据物品本身的特性来判断物品之间的相似性,而是通过用户行为来判断物品之间的相似性。如图 2 所示,用户 A 和用户 C 首选的物品中的公共物品是物品 A 和物品 D。根据算法本身的原理,可以得到 A 项与 D 项相似,因此与 A 项相似的 D 项可以在用 户 B 喜欢 A 项时相对推荐。
1.根据用户历史行为信息构建物品-用户评分矩阵。
2.根据物品-用户评分矩阵计算用户之间的相似度。
计算相似度常用的方法有余弦算法、修正余弦算法、皮尔森算法等等,系统采用余弦算法。余弦算法公式如下所示:
性能:适用于用户较少的场合,如果用户很多,计算用户相似度矩阵代价很大。
领域:时效性较强,用户个性化兴趣不太明显的领域。
实时性:用户有新行为,不一定造成推荐结果的立即变化。
冷启动:在新用户对很少的物品产生行为后,不能立即对它进行个性化推荐,因为用户相似度表示每隔一段时间离线计算的。新物品上线后一段时间,一旦有用户对物品产生行为,就可以将新物品推荐给和对它产生行为的用户兴趣相似的其他用户。
推荐理由:很难提供令用户信服的推荐解释。
2.3.2 基于物品
性能:适用于物品数明显小于用户数的场合,如果物品很多,计算物品的相似度矩阵代价很大。
领域:长尾物品丰富,用户个性化需求强烈的领域。
实时性:用户有新行为,一定会导致推荐结果的实时变化。
冷启动:新用户只要对一个物品产生行为,就可以给它推荐和该物品相关的其它物品。但没有办法在不离线更新物品相似度表的情况下将新的物品推荐给用户。
推荐理由:利用用户的历史行为给用户做推荐解释,可以令用户比较信服。
推荐算法常见问题及解决方案
冷启动问题
冷启动问题一直是协同过滤算法需要面对的问题,指的是用户刚进入系统,并没有历史行为或历史行为较少(本系统体现在对美食的评分)。根据公式我们可以看出计算出来的相似度很低,几乎没有参考价值。而推荐系统需要根据用户的历史行为和兴趣预测用户未来的行为和兴趣。
数据稀疏性问题
数据的稀疏性则是在系统搭建的初期用户很少,协同过滤的第一步是建立用户与美食的矩阵来表示。这看上去很简单,但在实际的系统中许多美食推荐系统要对巨大的信息流进行统计。而用户在系统中涉及到的物品(美食)不到系统的百分之一。因此照成形成的矩阵很稀疏。这样计算出来的相似度很难确定邻居用户集,也会极大的损耗计算性能。推荐的效果也会很低。例如用户之间的传递性缺失,如A与B用户的相似度高,B与C相似度也很高,但A与C又没有很多共同的美食评分(交集),那么判断A与C之间的关联度不高,这样会降低推荐的效果。
根据现有的技术,本系统采用了让新加入系统的用户先选择自己喜欢的分类,先根据分类的标签推荐用户,等用户具有历史操作后再给用户使用协同过滤推荐算法推荐,能解决用户的冷启动问题。
数据库设计
db_food(美食信息表)
列名 | 说明 | 数据类型 | 长度 | 主键 | 外键 | 非空 |
---|---|---|---|---|---|---|
id | 编号 | int | 20 | 是 | 是 | 是 |
foodname | 美食名称 | varchar | 20 | |||
foodtypeid | 美食类型编号 | int | 20 | 是 | 是 | |
image | 图片 | varchar | 255 | |||
material | 食材 | varchar | 255 | |||
content | 简介 | text | ||||
createtime | 添加时间 | varchar | 255 |
db_collect(美食收藏表)
列名 | 说明 | 数据类型 | 长度 | 主键 | 外键 | 非空 |
---|---|---|---|---|---|---|
id | 编号 | int | 20 | 是 | 是 | |
userid | 用户编号 | int | 20 | 是 | ||
foodid | 美食编号 | int | 20 | 是 | ||
createtime | 添加时间 | varchar | 255 |
db_comment(美食评价表)
列名 | 说明 | 数据类型 | 长度 | 主键 | 外键 | 非空 |
---|---|---|---|---|---|---|
id | 编号 | int | 20 | 是 | 是 | |
userid | 用户编号 | int | 20 | 是 | ||
foodid | 美食编号 | int | 20 | 是 | ||
content | 评论内容 | text | ||||
createtime | 添加时间 | varchar | 255 |
db_look(美食浏览记录表)
列名 | 说明 | 数据类型 | 长度 | 主键 | 外键 | 非空 |
---|---|---|---|---|---|---|
id | 编号 | int | 20 | 是 | 是 | |
userid | 用户编号 | int | 20 | 是 | ||
foodid | 美食编号 | int | 20 | 是 | ||
createtime | 添加时间 | varchar | 255 |
db_score(美食评分表)
列名 | 说明 | 数据类型 | 长度 | 主键 | 外键 | 非空 |
---|---|---|---|---|---|---|
id | 编号 | int | 20 | 是 | 是 | |
userid | 用户编号 | int | 20 | 是 | ||
foodid | 美食编号 | int | 20 | 是 | ||
score | 美食评分 | int | 20 | |||
createtime | 添加时间 | varchar | 255 |
db_type(美食类型表)
列名 | 说明 | 数据类型 | 长度 | 主键 | 外键 | 非空 |
---|---|---|---|---|---|---|
id | 编号 | int | 20 | 是 | 非空 | |
typename | 美食类型名称 | varchar | 255 |
db_user(用户信息表)
列名 | 说明 | 数据类型 | 长度 | 主键 | 外键 | 非空 |
---|---|---|---|---|---|---|
id | 编号 | int | 20 | 是 | 是 | 是 |
username | 用户名 | varchar | 20 | |||
password | 密码 | varchar | 20 | 是 | ||
header | 头像 | varchar | 255 | |||
gender | 性别 | int | 20 | |||
age | 年龄 | int | 20 | |||
phone | 电话 | varchar | 255 | |||
邮箱 | varchar | 255 | ||||
lastlogintime | 最近登录时间 | varchar | 255 | |||
state | 用户账号状态 | int | 20 | |||
createtime | 注册时间 | varchar | 255 |
db_userlabel(用户兴趣标签表)
列名 | 说明 | 数据类型 | 长度 | 主键 | 外键 | 非空 |
---|---|---|---|---|---|---|
id | 编号 | int | 20 | 是 | 是 | |
userid | 用户编号 | int | 20 | 是 | ||
foodtypeid | 美食编号 | int | 20 | 是 | ||
createtime | 添加时间 | varchar | 255 |
美食推荐系统的设计与实现
(1) PythonDjangoProject,这个是项目名称
(2) PythonDjangoProject下的PythonDjangoProject是项目全局配置文件夹,该文件夹是项目自动生成的,相当于项目容器。
init.py,这个文件是生成的空文件,用它标识一个目录为Python的标准包即模块包,如果没有那么他所在的文件夹就是个普通的文件夹不能被其他模块导入。目前我们已经在里边添加了安装mysql数据库插件模块的代码。
asgi.py,Django3.0版本新出的异步功能。
settings.py,Django 项目的配置文件,包括 Django 模块应用配置,数据库配置,模板配置等。
urls.py,Django 项目的 URL 声明。django下所有的页面都需要在该urls文件中配置一下,否则在访问的时候会找不到该文件。
wsgi.py,全称是 webserver getway interface,即Web服务器的网关接口,是python应用与Web服务器交互的接口,一般不需要做任何修改。
(3) manage.py,manage.py文件位于整个物品的最外层,是该项目的项目管理器,它提供了很多的命令用来管理该项目,在终端中可以查看它提供的所有命令,可通过输入:python manage.py 查看其命令,项目的启动其实也是通过manage.py文件的命令。
(4) MyDjango,是应用程序文件夹。
migrations是用于记录 models 中数据的变更。init.py是生成的空文件。0001_initial.py是在执行python manage.py makemigrations命令时生成的。
init.py,是生成的空文件。
admin.py,映射 models 中的数据到 Django 自带的 admin 后台。
apps.py,用于应用程序的配置。
models.py:创建应用程序数据表模型(对应数据库的相关操作)。
tests.py:创建 Django 测试。单元测试。
views.py,控制向前端显示哪些数据。
需求分析
1、游客:该类用户打开平台后,可以浏览美食信息、搜索美食,并在首页看到美食热点推荐结果(根据美食总评分和总收藏数量降序推荐),同时可以进行注册、登录操作。
2、登录用户:该类用户在浏览美食信息时,会有多个推荐方式为其进行个性化推荐美食,还可以添加兴趣标签,对美食进行收藏、评论、评分操作,并且同时查看美食的收藏、评论、评分情况,注销账号。在个人中心里,可以查看修改个人账号相关信息,管理个人的美食浏览记录,修改兴趣标签,查看并操作个人的美食收藏、评论、评分数据。
3、管理员:可以查看修改个人账号信息;查看和删除用户信息、用户选择喜好便签信息、用户浏览记录,对所有用户进行收藏管理、评论管理、评分管理,增删改美食类型信息、美食信息,同时会对数据进行统计。
4、个性化推荐功能:热点榜单(查询浏览数量最多的美食,同时不包括当前登录用户浏览过的美食);基于用户的协同过滤推荐算法(根据评分数据),如果没有推荐结果,采用热点推荐(根据登录用户兴趣标签下的美食的总评分降序推荐,同时是登录用户没有评分的);基于物品的协同过滤推荐算法(根据收藏数据),如果没有推荐结果,采用热点推荐(根据登录用户兴趣标签下的美食的收藏数量降序推荐,同时是登录用户没有收藏的)。
根据功能描述的内容,美食平台划分成以下模块:平台基本功能、个性化推荐、基本信息管理。每一个大的模块又可以划分成许多小模块
在系统功能模块的树状结构图中,每一个叶子节点都是一个最小的功能模块,每个功能模块都需要对数据库中不同的表进行操作,如查看、修改、删除、添加数据。
流程图展示了用户在使用系统时的工作过程。
说明:当浏览者进入该平台时,先以游客的身份使用平台功能。当游客要对一些受限的权限操作时。平台会提示游客是否要登录平台或注册为平台用户。当登录验证成功或注册成功后,游客就会变成平台用户并可以享有更多的权限。当平台用户退出系统后又变成了游客的身份。
主要功能用例图
爬虫代码部分展示
# 爬取美食列表
def getFoods(self, type, param):
try:
# https://home.meishichina.com/recipe/recai/ 第一页
# https://home.meishichina.com/recipe/recai/page/2/
# 分页查询当前美食类型下的美食,从第一页开始
currentPage = 0
# 每种类型下爬取的美食数量,临时变量
foodCountTemp = self.foodCount
endFlag = False # 每种类型下爬取美食结束标志
# 遍历
while True:
# 每页10条数据
currentPage = currentPage + 1
# 定义url
fullUrlTemp = None
# 如果是第一页,排序hot:最热,空:最新
if currentPage == 1:
fullUrlTemp = self.base_url + "recipe/" + param
else:
fullUrlTemp = self.base_url + "recipe/" + param + "/page/" + str(currentPage) + "/"
print("爬取美食类型:【%s" % type.get("foodtypename") + "】第【" + str(currentPage) + "】页开始")
print("链接地址:" + fullUrlTemp)
# 反爬虫技术,所以不能一直爬取,休息一会
Util().getRandomSleep()
# 爬取数据
resp = requests.get(fullUrlTemp, headers=self.headers)
resp.encoding = "utf-8"
# 使用bs4模块解析html数据
soup = BeautifulSoup(resp.text, 'lxml')
# 解析出美食列表
foodListDiv = soup.find(id="J_list")
foodList = foodListDiv.find_all("li")
# 遍历美食列表
for foodTemp in foodList:
# 定义美食对象
item = dict()
item["type"] = type
系统展示
每文一语
每一次的学习都是不断地进步